手続き志向プログラミングになれてしまって困っています。クラスは関数、構造体とはどう違うのですか。参考書に載っている<クラスを使った例題>を、<関数&構造体>に置き換えたほうが短くなりますし、見やすく感じてしまいます。クラスの良さというものが分かりません。固くなった私の頭を納得させるような説明やその例題(実用的なもの)をお願いします。

このQ&Aに関連する最新のQ&A

A 回答 (2件)

回答ではありません。



短いプログラムだと一般的には<関数&構造体>がわかりやすいでしょう。

自分はクラスは、以下のようなものだと考えています。
  1)継承を除けば、クラスと<関数&構造体>は同じもの。
  2)doubleやintなどの型の拡張。
    クラスの例   :複素数
    オブジェクトの例:3i+1
    データ例    :実数部(1)、虚数部(3)
    操作の例    :2つの複素数の距離の計算
    
  3)ある抽象化されたモノや型のデータや操作をひとまとめにしたもの。
    クラスの例   :人間
    オブジェクトの例:花子さん、太郎さん、…。
    操作の例    :歩く
    データ例    :位置座標

クラスの利点をわかりやすく言うと
  1)データと操作を一体化させる。
  2)操作の名前を統一できる。

言語によって以下のような利点があります。
  1)継承ができる。(多重継承などがあるものもある)
    (差分プログラミング)
  2)データの隠蔽ができる。

補足事項
  1)言われているほど継承は用いられないことが多い。
  2)結局、クラスの操作をコーディングするときに手続き指向を使用する。
    問題は構造化プログラミングができるかどうかです。
    これを理解しているならば、クラスの利点も見えてきます。

継承機能を除けば、クラスとは手続き指向と互換性があります。
   例えば、B構造体にある操作は、A構造体の操作と同じ概念なので
   同じ名前にしたいとき、手続き指向で考えるなら、
   きっと似た名前にしようとしますね。
     typedef struct A{
     } A;
     typedef struct B{
     } B;
     void A_operation(A *, arguments, ...);
     void B_operation(B *, arguments, ...);
     
     void main(){
        A *a;
        B *b;
        A_operation(A, arguments, ...);
        B_operation(B, arguments, ...);
     }
   
   クラスで実現するならば、以下のようにしませんか?
     class A{
     public:
        void operation(arguments, ...);
     }
     class B{
     public:
        void operation(arguments, ...);
     }
     
     void main(){
        A *a = new A;
        B *b = new B;
        a->operation(arguments, ...);
        b->operation(arguments, ...);
        delete A;
        delete B;
     }

もし、継承、インプリメント、多重継承、などが分からない点だったら
アドバイスの意味が無かったかもしれないですね。

以上
    • good
    • 0
この回答へのお礼

2)doubleやintなどの型の拡張。
    クラスの例   :複素数
    オブジェクトの例:3i+1
    データ例    :実数部(1)、虚数部(3)
    操作の例    :2つの複素数の距離の計算
    
  3)ある抽象化されたモノや型のデータや操作をひとまとめにしたもの。
    クラスの例   :人間
    オブジェクトの例:花子さん、太郎さん、…。
    操作の例    :歩く
    データ例    :位置座標

↑ホント分かりやすいです。今からちょっとクラス設計に挑戦しようと思います。
回答していただきありがとうございました。

お礼日時:2001/10/12 19:46

はっきり言って、入門書の例題が悪いだけと言わざるを得ません。



たぶん C -> C++ だと思うので、C++ の STL を一例に挙げてみます。
配列の変わりに vector という(テンプレート)クラスがあります。配列の長さが実行時に動的に変化する場合、従来の関数&構造体では、メモリの管理に手間を取られていました。
しかし、vector クラスを使えば、サイズ変更は resize() の一行ですみ、また、解放も vector クラスが自動的に行ってくれるので、利用者は心配する必要がありません。
この様に、設計者と利用者が違う場合でもスムーズに、手間が少なく利用出来ます。
配列の大きさが欲しくなれば size() で得ることが出来ますし、それようの(何処で書き換えられるか分かったもんじゃない)グローバル変数を用意する必要はなくなります。
ソートしたくなれば、vector を使っていようが、list を使っていようが同じ sort 関数を用いればソートが出来てしまいます。

char* ではいちいち、どのくらい確保したから…というのを気にしてプログラムしなければいけませんが、stringを用いればそのような苦労も減ります。

また、シューティングゲームを作ることを考えます。
敵キャラを管理する必要がありますが、このとき、構造体&関数を使って、敵キャラそれぞれに独自の動きをつけようとすると、なかなかすっきりしたプログラムを作ることは出来ません。
しかし、CEnemyBase という基底クラスから敵キャラのクラスを派生させるような設計にすれば、管理は CEnemyBase のポインタ配列を持てば良いことになり、実装もすっきりしたものになります。
基底クラスにある描画する関数などは、派生クラスで独自のものにオーバーライド出来るので、わかりやすさを保ったまま細かく制御することが出来ます。

また、メンバ変数に直接アクセス出来ないようにするカプセル化も大きく力を発揮します。変数に代入する際、値のチェックなどを「確実に」するようにすると物凄い行数が必要となってしまいます。しかし、メンバ関数を通じてアクセスするようにすれば、安全なアクセスにすることができます。

また、ちょっと変更したいなと言うときのつぎはぎ的な作業もクラス化しておいた方が楽です。
そして、クラス単位に設計を分けることで複数人での分散作業もしやすくなります。
後、例えば、fwrite や fread の様にいちいちサイズを指定する必要がある関数よりも、クラスで型毎に動作が分かれている方が見やすいプログラムになりませんか?
そういった、面倒な部分を隠してしまうと言うのがとりあえず、わかりやすいメリットでは無いかと思います。


他にもメリットはたくさんあります。
もちろん、小規模のプログラムでも無理に使う必要はありませんが、大抵うまくクラスを使った方が見やすいです^^;
    • good
    • 0
この回答へのお礼

なるほどですね。ちょっと分かったような気がします。またクラス設計に挑戦してみようと思います。ありがとうございました。

お礼日時:2001/10/12 19:40

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q構造体の構造体の構造体をまとめて初期化したい

gnuplotをCで吐くためのプログラムを作成していますが、
構造体の構造体の構造体をまとめて初期化することができません。

以下がコードそのコードです。


#define MAX_PLOT_DATA_NUM 8
#define MAX_GRAPH_SET_NUM 35

//child --------------------------------------------------
typedef struct {
char *fileName;
int column[2];
char *caption;
} Col;

//child --------------------------------------------------
typedef struct {
char *category;
char *subCategory;
char *xLabel, *yLabel;
char *saveFileName;
Col col[MAX_PLOT_DATA_NUM];
} GraphSet;

//child --------------------------------------------------
typedef struct {
char *lineFormat;
int lineWidth;
} Common;

//mother -------------------------------------------------
typedef struct {
GraphSet gs[MAX_GRAPH_SET_NUM];
Common common;
} PlotFormats;

int main(){

PlotFormats pF = {

{"Motor angle","right","Time [s]","Angle [rad]","ang_R",
{"motor.dat",1,2,"ang #1 "},
{"motor.dat",1,3,"ang #2 "},
{"motor.dat",1,4,"ang #3 "},
{"motor.dat",1,5,"ang #4 "},
{"motor.dat",1,6,"ang #5 "},
{"motor.dat",1,7,"ang #6 "},
},

//ここから下のコメントアウトしてる部分をはずすとコンパイルが通らなくなります。
//{"Motor angle","left","Time [s]","Angle [rad]","ang_L",
//{"motor.dat",1,12,"ang #1 "},
//{"motor.dat",1,13,"ang #2 "},
//{"motor.dat",1,14,"ang #3 "},
//{"motor.dat",1,15,"ang #4 "},
//{"motor.dat",1,16,"ang #5 "},
//{"motor.dat",1,17,"ang #6 "},
//},

{"w l", 5}};

return 0;

}

エラーは
・間接参照のレベルが 'char *' と 'int' で異なっています。(複数)
・初期化子の数が多すぎます。
と出ます。

長くなりましたが、わかる方がいましたら、よろしくお願いします。

gnuplotをCで吐くためのプログラムを作成していますが、
構造体の構造体の構造体をまとめて初期化することができません。

以下がコードそのコードです。


#define MAX_PLOT_DATA_NUM 8
#define MAX_GRAPH_SET_NUM 35

//child --------------------------------------------------
typedef struct {
char *fileName;
int column[2];
char *caption;
} Col;

//child --------------------------------------------------
typedef struct {
char *category;
char *subCategory;
char *xLab...続きを読む

Aベストアンサー

こんなことかな?

#define MAX_PLOT_DATA_NUM (8)
#define MAX_GRAPH_SET_NUM (35)

//child --------------------------------------------------
typedef struct {
char *fileName;
int column[2];
char *caption;
} Col;

//child --------------------------------------------------
typedef struct {
char *category;
char *subCategory;
char *xLabel, *yLabel;
char *saveFileName;
Col col[MAX_PLOT_DATA_NUM];
} GraphSet;

//child --------------------------------------------------
typedef struct {
char *lineFormat;
int lineWidth;
} Common;

//mother -------------------------------------------------
typedef struct {
GraphSet gs[MAX_GRAPH_SET_NUM];
Common common;
} PlotFormats;

int main(void)
{
PlotFormats pF = {
{
{
"category", "subCategory", "xLabel", "yLabel", "saveFilename",
{
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
},
},
{
"category", "subCategory", "xLabel", "yLabel", "saveFilename",
{
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
},
},
{
"category", "subCategory", "xLabel", "yLabel", "saveFilename",
{
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
{ "fileName", 10, 20, "caption", },
},
},
},
"lineFormat", 30,
};
return 0;
}

こんなことかな?

#define MAX_PLOT_DATA_NUM (8)
#define MAX_GRAPH_SET_NUM (35)

//child --------------------------------------------------
typedef struct {
char *fileName;
int column[2];
char *caption;
} Col;

//child --------------------------------------------------
typedef struct {
char *category;
char *subCategory;
char *xLabel, *yLabel;
char *saveFileName;
Col col[MAX_PLOT_DATA_NUM];
} GraphSet;

//child -----------...続きを読む

Q関数への構造体の配列の渡し方

こんにちは、関数への構造体の配列の渡し方で理解できない点があるため、質問させていただきます。
以下がスクリプトになります。3人の名前と年齢をinput関数で入力し、それらのデータをoutput関数で出力するのが目的です。

#include <stdio.h>

typedef struct{
char name[64];
int age;
}property;

void input(property *data[]);
void output(property *data[]);

int main(void){
property data[3];
printf("Input data of three people.\n");
input(&data);
output(&data);
return 0;
}

void input(property *data[]){
int i;
for(i=0;i<3;i++){
printf("%d banme\n",i+1);
printf("name:");
scanf("%s",&data[i]->name);
printf(" age:");
scanf("%3d",&data[i]->age);
}
return;
}

void output(property *data[]){
int i;
for(i=0;i<3;i++){
printf("%d banme\n",i+1);
printf("name:%s\n",data[i]->name);
printf("age :%3d\n",data[i]->age);
}
return;
}


コンパイル時のエラーメッセージは以下のようになりました。(ファイル名はstructure5.c)
structure5.c: In function ‘main’:
structure5.c:14:2: warning: passing argument 1 of ‘input’ from incompatible pointer type [enabled by default]
input(&data);
^
structure5.c:8:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’
void input(property *data[]);
^
structure5.c:15:2: warning: passing argument 1 of ‘output’ from incompatible pointer type [enabled by default]
output(&data);
^
structure5.c:9:6: note: expected ‘struct property **’ but argument is of type ‘struct property (*)[3]’
void output(property *data[]);
^
structure5.c: In function ‘input’:
structure5.c:24:3: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[64]’ [-Wformat=]
scanf("%s",&data[i]->name);
^

構造体の配列をinput関数やoutput関数に渡すときにエラーが発生しているようなのですが、自分で調べても解決できなかったため、質問させて頂きます。
皆様のお知恵を貸してください。なおプログラミング言語自体初心者のため、できる限りわかりやすいお言葉でご教授願います。よろしくお願い致します。

こんにちは、関数への構造体の配列の渡し方で理解できない点があるため、質問させていただきます。
以下がスクリプトになります。3人の名前と年齢をinput関数で入力し、それらのデータをoutput関数で出力するのが目的です。

#include <stdio.h>

typedef struct{
char name[64];
int age;
}property;

void input(property *data[]);
void output(property *data[]);

int main(void){
property data[3];
printf("Input data of three people.\n");
input(&data);
output(&data);
return 0;
}

void input(proper...続きを読む

Aベストアンサー

もちろん
void input(property *data);
にして
int main(void){
 property data[3];

 input(data);
が最も普通 (まあ大きさも渡すと思うけど) です>#7.

配列の各要素は連続して配置されますから,
&data[0] = data,
&data[1] = &data[0]+1 = data+1,
&data[2] = &data[0]+2 = data+2
が (意味のある限りにおいて) 常に成り立ちます. つまり「ポインタの配列」を使う必要性はほとんどありません. しいて言えば「複数の配列のデータを扱うとき」には意味はあるけど, そのような状況があまり想像できない.

あと
void input(property (*data)[3]);
とすると「大きさ 3 の配列」は渡せるけどその他の大きさの配列は渡せなくなります. ただ, 例えば
property data[4][3];
とかいう 2次元配列を
input(data);
のように渡す (ここで &data としちゃうとさらに面倒です) と上のような形が発生します. この 2次元配列との絡みで「3」が省略できない.

もちろん
void input(property *data);
にして
int main(void){
 property data[3];

 input(data);
が最も普通 (まあ大きさも渡すと思うけど) です>#7.

配列の各要素は連続して配置されますから,
&data[0] = data,
&data[1] = &data[0]+1 = data+1,
&data[2] = &data[0]+2 = data+2
が (意味のある限りにおいて) 常に成り立ちます. つまり「ポインタの配列」を使う必要性はほとんどありません. しいて言えば「複数の配列のデータを扱うとき」には意味はあるけど, そのような状況があまり想像できない.

あと
...続きを読む

Q__gcクラスから、__nogcクラスの構造体を使おうとするとLNK2020エラーが出ます

NET 2003 C++ のマネージとアンマネージに関する質問です。

「__gc class」から、「__nogc class」の中に作った、「__nogc struct」構造体を使用しようとすると、「error LNK2020: 未解決のトークン」といったエラーになってしまい、
ビルドができません。
構造体を持つクラスの指定を「__gc class」に指定すると、ビルドが通るようになります。
これはどういった理由なのでしょうか?
マネージクラスから、アンマネージクラスの構造体は利用できないのでしょうか?

利用できるように改善する方法がございましたら、お教えいただけますと幸いです。
何卒よろしくお願いします。

Aベストアンサー

>B.hに、、__nogc struct Cの構造体の変数 *をstaticで宣言していたのですが、
>このstaticを外したところ、コンパイルが通りました。

クラスCのメンバ変数に struct Cのポインタ変数をもっていたということでしょうか。staticなメンバはコンストラクタの引数リストでなくグローバルな部分で初期化しなくてはならないのでそれが抜けていたのですかね。staticにする理由があるのであれば(異なるclass Cのインスタンス間で共通の変数を使いたい場合)その部分を見てみるのもよいかと思います。

Q派生クラスで,基本クラスのメンバ変数である構造体の型を・・・

派生クラスで,基本クラスのメンバ変数である構造体の型を戻り値の型とするメンバ関数を作りたいのですがエラーが発生してしまいます.

class A{
protected:
struct NODE{
char *name;
NODE *next;
}
NODE *node;
};

class B : public A{
public:
NODE* sort(NODE* fnode);
};

NODE* B::sort(NODE* fnode){ ←エラー
~~~~~
return ○○;
}

現在このような状態です.
よろしくお願いします.

Aベストアンサー

その位置では NODE という型が宣言されていないからエラーになっているんじゃないですか? とりあえず A::NODE にすればエラーにはならないはずです.
ちょっと気になるんですが, A::NODE を protected にしてますよね. それを返す関数を B では public で宣言してますけど, これは構成としてまずいと思いますよ. A (とその派生クラス) の外からは A::NODE が見えないような気がします.

Q'.GetSize' : 左側がクラス、構造体、共用体ではありません。

int nData[10];
と配列作ったとき、要素数が10個っていうのを取り出すには
どうすれば良いでしょう?
int x=nData.GetSize(); //タイトルのようにエラーとなります。
x=Sizeof(nData)/4; //intなので4byteで割った・・・。あはは。

基本的な事柄でスミマセンが出てきません。宜しくお願いします。

Aベストアンサー

x=Sizeof(nData)/sizeof(int);
で良いかと思います。
しかし10という値は自分で設定しているわけですから
#define ARRAY_SIZE 10
int nData[ARRAY_SIZE];
としておけば、
x = ARRAY_SIZE;
でいいはずです。

javaの配列なんかだと.lengthで要素数がとれますが
cの配列はそんなことは出来ません。


人気Q&Aランキング

おすすめ情報