C++ オブジェクトの初期化について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ホースメン

C++ オブジェクトの初期化について

#1

投稿記事 by ホースメン » 15年前

こんにちは。C++のオブジェクトの初期化について質問させて下さい。

クラスオブジェクトのメンバ変数を、コンストラクタ内で全て0の値で初期化する時、
どのようにするのが良いでしょうか?
メンバ変数は様々な型で20個以上、仮想関数なしという仮定でお願いします。

1.memset( this, 0, sizeof(CLASSNAME) ); ← サイズやアドレスに起因して問題が生じるケースは無いでしょうか?
2.メンバ一つ一つ代入していく。 ← データメンバが多い場合、出来れば避けたいです。
3.その他

また、memset()の内部アルゴリズムは、1バイトずつループで回して値をセットしていると書いてあったのですが、本当でしょうか?

その他注意点等御座いましたら、合わせてご意見頂けましたら幸いです。

よろしくお願い致します。

バグ

Re:C++ オブジェクトの初期化について

#2

投稿記事 by バグ » 15年前

初期化用のメンバ関数を作成し、メンバ変数1つ1つ初期化を行うのに1票

Tororo

Re:C++ オブジェクトの初期化について

#3

投稿記事 by Tororo » 15年前

バクさんと同じく初期化メンバ関数で一つ一つ初期化に一票

MNS

Re:C++ オブジェクトの初期化について

#4

投稿記事 by MNS » 15年前

メンバイニシャライザで初期化する分には、大した作業量にはならないと思います。

kazuoni

Re:C++ オブジェクトの初期化について

#5

投稿記事 by kazuoni » 15年前

初期化にこだわるならコンストラクタ初期化子で一つ一つ初期化に一票

1番についてはたかぎさんあたりから意見が出てくるのではないでしょか^^;
メンバ変数は様々な型っというのがミソな気がしますが。
個人的には、1は真っ当な方法ではないと思います。

#初期化用のメンバ関数を作成し、メンバ変数1つ1つ初期化
これは代入ではないのでしょか・・・?

バグ

Re:C++ オブジェクトの初期化について

#6

投稿記事 by バグ » 15年前

メンバが変数ではなくクラスや構造体の場合もありますので、ああいう書き方をしました。

kazuoni

Re:C++ オブジェクトの初期化について

#7

投稿記事 by kazuoni » 15年前

> バグさん

納得しました。
もうすこし考えてから質問すべきでした。大変失礼しました。

ホースメン

Re:C++ オブジェクトの初期化について

#8

投稿記事 by ホースメン » 15年前

バグさん、Tororoさん、MNSさん、kazuoniさん、ご意見ありがとう御座います。
メンバは自分の中では一般のデータ型を考えていたので、代入といった表現になってしまいました。
曖昧な記述をしてしまい申し訳御座いません。
みなさんの理由は、memset()を使うのはリスクがある、ということでしょうか?

「メンバが非常に多い時にmemset()で値をセット出来れば都合が良いが、問題が発生するケースは無いか?
あるとしたら、出来るだけ簡潔に値をセットする方法は無いか?」という疑問が今回質問させて頂いた経緯ですが、質問文を考えているうちに真意の伝わり難い文章になってしまったようです。

以前、microsoft(?)のサンプルコードにmemset()を使用しているものがあり、自分で使用したこともあります。その時は問題ありませんでした。

余談ですが、こんなのもあったような…なるほど、と思いつつなんか怖いな、とも思ったりした記憶があります。
RECT rect;
func((LPPOINT)&rect);// rectの左上の座標

Justy

Re:C++ オブジェクトの初期化について

#9

投稿記事 by Justy » 15年前

>どのようにするのが良いでしょうか?

 設計的にこのクラスが20以上のメンバを持つことが妥当なのか見直す必要はありそうですが、
仮に妥当だったとして。

 今はあまり思いつかないですが、希な例外はあるにしても基本は2、
コンストラクタ初期化子で初期化、ですね。

 ただ、
 
・ 複数のコンストラクタがあり、それぞれで同じような初期化を行う場合で、
 個々のコンストラクタで初期化するとメンテナンス上問題があり、
 且つ代入で初期化しても問題のないメンバ変数

・ クラスを生成後、途中で複数のメンバ変数を再初期化する必要がある場合

のどちらかの条件を満たす場合は初期化用のメンバ関数を作って、コンストラクタで呼んでも
いいでしょう。


 或いはそれらのメンバ変数が intや double、ポインタ型などのプリミティブ型であるなら
boost::value_initializedを使うとか、或いはそれらを1つのC互換構造体に纏めてしまって

struct DATA
{
...
};

class A
{
DATA data;
public:
A():data() { }
};
のようにコンストラクタ初期化子で初期化してもいいかと思います。




>memset()の内部アルゴリズムは、1バイトずつループで回して値をセットしている
 感覚的にはそんな感じですが、実際にそうなっているかどうかは処理系によります。
 アライメントとサイズによっては1バイトではなく多バイトずつ処理している処理系も
あります。


>サイズやアドレスに起因して問題が生じるケースは無いでしょうか?
 とりあえず、たかぎさんのところのページをお読み下さい。

[迷信] とりあえず memset で初期化 | 株式会社きじねこ
http://www.kijineko.co.jp/tech/supersti ... emset.html

C++における構造体の初期化 | 株式会社きじねこ
http://www.kijineko.co.jp/node/681

たかぎ

Re:C++ オブジェクトの初期化について

#10

投稿記事 by たかぎ » 15年前

クラスの初期化に関しては、一般論で議論しても決定的かつ単一の良策はありません。
C互換構造体なのか、C互換共用体なのか、C互換でない集成体クラスなのか、それ以外なのかによって方法論が変わります。
静的記憶域期間を持つかどうかによっても変わってきます。

あるいは、処理系を特定して移植性を考慮しないのであれば、memsetやZeroMemoryやbzeroなどでゼロクリアするという手もあります。

ホースメン

Re:C++ オブジェクトの初期化について

#11

投稿記事 by ホースメン » 15年前

Justyさん、アドバイスありがとうございます。

>boost::value_initializedを使う
>C互換構造体に纏めてしまって
知らなかったことなので、勉強になります。ただ、データアクセスが冗長になるんでしょうか?

>感覚的にはそんな感じですが、実際にそうなっているかどうかは処理系によります。
>アライメントとサイズによっては1バイトではなく多バイトずつ処理している処理系も
>あります。
そうですか・・・memset()って速そうなイメージあったんだけどなあ;;
魔法みたいな方法って意外に無いものなんですね。

>[迷信] とりあえず memset で初期化 | 株式会社きじねこ
http://www.kijineko.co.jp/tech/supersti ... emset.html
>C++における構造体の初期化 | 株式会社きじねこ
http://www.kijineko.co.jp/node/681
上のリンクの方は先ほどたまたま拝見させて頂いたところでした。
こちらのたかぎさんが書いてらしたと知って、びっくりしました。

コンパイラに依存するmemset()はこのケースでは避けるべき、というように理解しました。
集成体なら{}で初期化、new演算子で生成した場合等は()で初期化(C互換構造体なら問題なし?)ってことでしょうか?
しかし集成体の条件としてユーザ定義のコンストラクタが無いことや、集成体やC互換構造体の定義を正確に理解していない現在の自分にとっては、一つ一つ初期化していくのが最良に思えました。

あのサンプルコードはmicrosoftだからこそ出来た、ってことなんですかね(苦笑)。


…っと思ったら

たかぎさん、アドバイスありがとう御座います!
たかが0クリア、と思っていましたが、非常に奥が深いものなんですね。
生兵法は怪我の元、注意一秒怪我一生(違)ですね…。

サイトを拝見させて頂き、今の自分では理解出来ない部分はありましたが、とても勉強になります。

皆様、色々とありがとう御座いました。

たかぎ

Re:C++ オブジェクトの初期化について

#12

投稿記事 by たかぎ » 15年前

> しかし集成体の条件としてユーザ定義のコンストラクタが無いことや、集成体やC互換構造体の定義を正確に理解していない現在の自分にとっては、一つ一つ初期化していくのが最良に思えました。

初期化と代入は別だということも理解してくださいね。
メンバを一つ一つ初期化するには、コンストラクタ初期化子を使うか、{ } の初期化子を使う以外にはありません。

面倒なのは、newで生成する場合と一時オブジェクトなんです。
それらを考慮しないのであれば、コンストラクタがあるならコンストラクタを使って、そうでなければ { } でと覚えておけば充分です。

あと、やたら遅レスですが...

> メンバが変数ではなくクラスや構造体の場合もありますので、ああいう書き方をしました。

配列の場合は ( ) を使えばコンストラクタ初期化子でゼロクリアできます。
構造体の場合も、コンストラクタがある場合やC互換構造体なら ( ) でOKです。
それ以外は、(移植性を考えると)別途初期化済みのオブジェクトを作っておいて、それをコピーするしかなのですが、現実的ではありませんね。
GCCやC++0Xであれば、複合リテラルを使って初期化するという手もあるのでしょうが...

たかぎ

Re:C++ オブジェクトの初期化について

#13

投稿記事 by たかぎ » 15年前

> GCCやC++0Xであれば、複合リテラルを使って初期化するという手もあるのでしょうが...

書いたあとで気付きましたが、C++0xには複合リテラルはないようですね。

SCI

Re:C++ オブジェクトの初期化について

#14

投稿記事 by SCI » 15年前

>>たかぎさん

詳しくは知りませんが、C++0xには類似したものでinitializer_listというものがあるようです。
個人的には、言語のファーストクラスオブジェクトなのにライブラリ依存というのはなんか慣れませんが(笑)

たかぎ

Re:C++ オブジェクトの初期化について

#15

投稿記事 by たかぎ » 15年前

> 詳しくは知りませんが、C++0xには類似したものでinitializer_listというものがあるようです。

それはまったく別のものです。
複合リテラルというのは...

struct A
{
int a;
double b;
char *c;
};

struct A a = (struct A){ 123, 4.56, "abc" };

のようなものです。
C++でもこれが使えれば、集成体のデータメンバをコンストラクタ初期化子で初期化できるはずです(コピー可能で
あることが必須ですが...)。

SCI

Re:C++ オブジェクトの初期化について

#16

投稿記事 by SCI » 15年前

initializer_listを調べてみましたが、これはコンストラクタにリストを与えることができるということで、複合リテラルとは全然似てないですね(笑)
C++0xに関してはまだ理解が追いつかないです。

複合リテラル自体はC99だかの関連で知ってはいますが、実際に使ったことはないですねぇ・・・GCCだけですか?

たかぎ

Re:C++ オブジェクトの初期化について

#17

投稿記事 by たかぎ » 15年前

> 複合リテラル自体はC99だかの関連で知ってはいますが、実際に使ったことはないですねぇ・・・GCCだけですか?

GCCだけということはないですが、本当に複合リテラルを使いたくなるのはVC++だったりします。
例えば、

RegisterClassEx(&(WNDCLASSEX){ .cbSize = sizeof(WNDCLASSEX), .style = CS_HREDRAW | CS_VREDRAW, lpfnWndProc = &WndProc, ... });

のようなことができると、それなりに便利です。

閉鎖

“C言語何でも質問掲示板” へ戻る