- ベストアンサー
コンストラクタの順序でプログラムがフリーズする問題
- ソースコードにおいて、クラスBのm_listiが初期化される前に使用されてしまい、プログラムがフリーズしてしまう問題が発生しています。
- この問題を解決するためには、クラスAのコンストラクタ内でクラスBの関数を呼び出すように修正する必要があります。
- また、クラスBのメンバ変数m_listiの初期化をコンストラクタで行うことで、問題を回避することができます。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
環境依存になりますが、グローバルコンストラクタの実行順を制御する事は可能です。 GCCなら__attribute__((init_priority(N))) VCなら#pragma init_seg(~)が用意されています。 http://support.microsoft.com/kb/104248/ja http://d.hatena.ne.jp/yupo5656/20070203/p1 自作のアロケーターを作るときに使った覚えはありますが、勿論回避可能なら別の方法を検討した方がよいです。 またinit_priorityの100以下の値やinit_seg(compiler)の場合は Cランタイムの初期化も終わってない段階なので、標準関数は基本的に使えないと思ってください。(WinAPI等は可)
その他の回答 (4)
- maru_yoshi_
- ベストアンサー率39% (17/43)
C/C++ではグローバル変数の初期化順序を制御することはできない。 Aクラスのインスタンスを管理したければ、管理処理をAクラスに閉じ込めることを考える。 今回の場合、管理データを静的なポインタ変数にすればよいと思われる。 私なら以下の通り。 /* library.h */ #include <list> class A { public: A(); ~A(); private: static std::list<A*>* m_listlpC; void Insert(); void Delete(); }; /* library.cpp */ #include "library.h" #include <iostream> std::list<A*>* A::m_listlpC = 0; A::A() { if (m_listlpC == 0) { m_listlpC = new std::list<A*>; } Insert(); } A::~A() { Delete(); } void A::Insert() { std::cout << "APtrListのInsert" << std::endl; m_listlpC->push_back(this); } void A::Delete() { std::cout << "APtrListのDelete" << std::endl; m_listlpC->remove(this); } 管理クラスBのメンバー関数相当はクラスAの静的メンバー関数にすればよい。
お礼
ありがとうございます。 参考にさせていただきます。
- tsuduki123
- ベストアンサー率32% (21/65)
コンストラクタに処理を書くのはやめましょうという話に帰結しそうな気がしないでもありませんが、 僕ならg_CBをポインタにしてしまいますかねぇ。。 そもそも、ほんとにインスタンス生成前だからという原因調査あっているのかが怪しい感じ。 生成前ならフリーズじゃなくてアクセス違反とか異常終了するはずなんですがね? ちなみに、例外をちゃんと処理しようと思うのなら コンストラクタの中で例外が発生しうる処理を書いたらいけません。
お礼
ご回答ご指摘ありがとうございます。 確かに原因も間違っているかもしれません。
補足
質問が分かりにくく、申し訳ありませんでした。 変数名等を分かりやすくして質問し直したいと思います。 /* library.h */ #pragma comment(lib, "library.lib") class A { public: A(); }; /* library.cpp */ #include "library.h" #include <list> #include <iostream> class APtrList { private: std::list<A*> m_listlpC; public: APtrList() { std::cout << "APtrListのコンストラクタ" << std::endl; } void Insert(A* lpC) { std::cout << "APtrListのInsert" << std::endl; m_listlpC.push_back(lpC); //フリーズ return; } } g_CAPtrList; A::A() { g_CAPtrList.Insert(this); return; } /* test.cpp */ #include "library.h" A CA1; A CA2; A CA3; int main() { return 0; } g_CAPtrListでAのインスタンスを管理したいと考えています。 そこでAのインスタンスが作られるごとに自らを登録させるようにしています。 しかしいろいろ検証した結果、どうやらCA1のコンストラクタが作られた時点で、 g_CAPtrList自体は作られているが、そのメンバ変数は初期化されていない状態だと思います。(もしかしたら違うかもしれない) ちなみに「APtrListのInsert」と1つ表示された状態でフリーズします。(⇔ g_CAPtrList のコンストラクタも呼ばれていない) akrさんのご指摘通り、もう少し条件を追加すると、 基本的には、変えたくないのは、g_CAPtrListでのインスタンスの管理という基本理念と test.cpp の中で A のインスタンスをグローバルに定義したいという事だけです。 (もしこれをメイン関数の中に書けばフリーズは起りません。) 再度長々と申し訳ありません。。。
- qwertfk
- ベストアンサー率67% (55/81)
グローバル変数の初期化順序ははっきりしないので、グローバル変数のコンストラクタで他のオブジェクトとの相互関係があるような構造はやめたほうが良いのですが、たとえば、 /* library.cpp */ #include "library.h" #include <list> class B { private: std::list<int> m_listi; public: void Func() { m_listi.push_back(0); } static B& instance() { static B obj; return obj; } }; A::A() { B::instance().Func(); } のようにすると、回避は可能です。
お礼
ご回答ありがとうございます。 参考にしたいと思います。
- akr
- ベストアンサー率18% (32/173)
CA若しくはg_CB若しくはB::m_listを静的に生成するのを止めて、動的に生成するようなロジックにすれば、インスタンスの生成順が制御できるので、初期化の問題は回避出来ると思います。 と、ここまで回答を書きましたが、どこまでの改変を許可しているのかによって回答が変わってくると思いませんか? 例えば上記の回答にある、静的生成を止めるってのが、このプログラムの要件に対して許可されていなければ、この回答は駄目ですよね。 条件次第では出来る事と出来無い事が変わってきますから、もう少し条件を追加した質問にしたほうが良いかと思います。
お礼
早速のご回答ありがとうございます。 ご指摘通り、もう少し条件を追加したいと思います。
お礼
そのようなpragmaがあったとは驚きです! これを使えば即解決ですね。 今回は他の方法で頑張ろうと思いますが、 ぜひまたの機会に活用したいと思います!