質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
コンストラクタ

オブジェクト指向言語において、オブジェクトを生成時に呼び出され、データの初期化などを行なう関数・メソッドのことである。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1524閲覧

C++でメンバ変数の初期化順序の正しさを静的に検知して適宜エラーを出したい

foolslab

総合スコア5

コンストラクタ

オブジェクト指向言語において、オブジェクトを生成時に呼び出され、データの初期化などを行なう関数・メソッドのことである。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/01/13 17:58

あるモジュール内のサブモジュール群が依存性で絡んでいたりするときに、正しくない初期化順序を検知できると安心できると考えているのですが、静的に行う良い方法はありますでしょうか。

以下のような場合を考えています。(疑似コードですいません)

C++

1 2class SubModule1 {...}; 3class SubModule2 {...}; // SubModule1に依存 4class SubModule3 {...}; // SubModule1・SubModule2に依存 5 6class MyClass{ 7 SubModule1 sub1; 8 SubModule2 sub2; 9 SubModule3 sub3; 10public: 11 MyClass(){ 12 sub1.init(); 13 sub2.init(sub1); // sub1を用いて初期化 14 sub3.init(sub1, sub2); // sub1とsub2を用いて初期化 15 } 16}; 17

例えば上記のようなコードがあったときに、うっかりMyClassのコンストラクタを

C++

1sub2.init(sub1); // sub1を用いて初期化(アウト、sub1はまだ初期化されていない) 2sub1.init(); 3sub3.init(sub1, sub2); // sub1とsub2を用いて初期化

という風に書いてしまったとき、これをエラーにしたいのです。

すぐに思いつく方法としては、各SubModuleに初期化フラグを持たせ、依存先のモジュールを利用する時は初期化フラグを確認するといった方法が考えられますが、これは動的な検知方法になります。

C++

1 2class SubModule1 {...}; 3 4class SubModule2 { 5 void init(SubModule1& sub1){ 6 if(!sub1.isInit()){ 7 throw exception("sub1 not initialized"); 8 } 9 ... //初期化処理 10 } 11 ... 12}; 13 14class SubModule3 { 15 void init(SubModule1& sub1, SubModule2& sub1){ 16 if(!sub1.isInit() || !sub2.isInit()){ 17 throw exception("sub1 or sub2 not initialized"); 18 } 19 ... //初期化処理 20 } 21 ... 22};

初期化の順序はMyClassのコンストラクタのコードという静的な要素のみで決まるはずなので、だったら静的に判断してstatic_assertなどでエラーにできたら「コンパイルが通る=正しいコード」という安心感が得られる上、実行時の初期化確認のコストもゼロになってとても便利だと思うのですが、そういういい方法はC++には無いものでしょうか。
上記のような動的な検知をデバッグビルドで行い、リリースビルドで削除するような手が最善なのでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

インスタンスの初期化を、init()に分けてするのではなく、コンストラクタで全部行い、sub1; sub2; sub3; の初期化はMyClassのコンストラクタのメンバ初期化子リストで行うのではいけないのでしょうか。

C++

1class SubModule1 {}; 2 3class SubModule2 { 4public: 5 SubModule2(const SubModule1 & sub1) {} 6}; 7 8class SubModule3 { 9public: 10 SubModule3(const SubModule1 & sub1, const SubModule2 & sub2) {} 11}; 12 13class MyClass { 14 SubModule1 sub1; 15 SubModule2 sub2; 16 SubModule3 sub3; 17 18public: 19 MyClass() 20 : sub1(), sub2(sub1), sub3(sub1, sub2) 21 { 22 } 23};

SubModule2のコンストラクタの引数にSubModule1を渡すようにすれば、そもそもSubModule1なしにはSubModule2のインスタンスを作れないので、依存関係が保証できます。

コンストラクタの実行が済んだのに、インスタンスが初期化されていないというのは、好ましくないと思います。初期化部分を、init()という文法上は、ただのメンバ関数に切り出してしまったために、依存関係がコンパイル時にチェックできなくなってしまったわけですし。

まぁ、いつもいつも、理想通りにはいかないのですが。

ちなみに、メンバ変数の初期化は、コンストラクタのメンバ初期化子リストの順番ではなく、変数の宣言を書いた順番で行われるので注意してください。

投稿2020/01/13 22:15

編集2020/01/13 22:20
Bearded-Ockham

総合スコア430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

foolslab

2020/01/14 06:36 編集

ありがとうございます。おかげさまでVC++及びGCCでコンパイル時に警告が出てくれました。 >ちなみに、メンバ変数の初期化は、コンストラクタのメンバ初期化子リストの順番ではなく、変数の宣言を書いた順番で行われるので注意してください。 これは知らなかったです。コロンの後に書いた順番通りに初期化されるものとばかり思っていました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問