コンストラクタの順序でプログラムがフリーズする問題

このQ&Aのポイント
  • ソースコードにおいて、クラスBのm_listiが初期化される前に使用されてしまい、プログラムがフリーズしてしまう問題が発生しています。
  • この問題を解決するためには、クラスAのコンストラクタ内でクラスBの関数を呼び出すように修正する必要があります。
  • また、クラスBのメンバ変数m_listiの初期化をコンストラクタで行うことで、問題を回避することができます。
回答を見る
  • ベストアンサー

コンストラクタの順序?

以下のソースに関して質問です。 /* library.h */ #pragma comment(lib, "library.lib") class A { public: A(); }; /* library.cpp */ #include "library.h" #include <list> class B { private: std::list<int> m_listi; public: void Func() { m_listi.push_back(0); //フリーズ return; } } g_CB; A::A() { g_CB.Func(); return; } /* test.cpp */ #include "library.h" A CA; int main() { return 0; } library.cppからlibrary.libをつくり、それをtest.cppで使用したところ、 ソースに示したところでプログラムがフリーズしてしまいます。 恐らくクラスBのm_listiが初期化される前にそれを使おうとしたためだと思います。 どうすればそれを避けられるでしょうか。。。

質問者が選んだベストアンサー

  • ベストアンサー
  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.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等は可)

oasobi034
質問者

お礼

そのようなpragmaがあったとは驚きです! これを使えば即解決ですね。 今回は他の方法で頑張ろうと思いますが、 ぜひまたの機会に活用したいと思います!

その他の回答 (4)

回答No.4

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の静的メンバー関数にすればよい。

oasobi034
質問者

お礼

ありがとうございます。 参考にさせていただきます。

回答No.3

コンストラクタに処理を書くのはやめましょうという話に帰結しそうな気がしないでもありませんが、 僕ならg_CBをポインタにしてしまいますかねぇ。。 そもそも、ほんとにインスタンス生成前だからという原因調査あっているのかが怪しい感じ。 生成前ならフリーズじゃなくてアクセス違反とか異常終了するはずなんですがね? ちなみに、例外をちゃんと処理しようと思うのなら コンストラクタの中で例外が発生しうる処理を書いたらいけません。

oasobi034
質問者

お礼

ご回答ご指摘ありがとうございます。 確かに原因も間違っているかもしれません。

oasobi034
質問者

補足

質問が分かりにくく、申し訳ありませんでした。 変数名等を分かりやすくして質問し直したいと思います。 /* 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)
回答No.2

グローバル変数の初期化順序ははっきりしないので、グローバル変数のコンストラクタで他のオブジェクトとの相互関係があるような構造はやめたほうが良いのですが、たとえば、 /* 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(); } のようにすると、回避は可能です。

oasobi034
質問者

お礼

ご回答ありがとうございます。 参考にしたいと思います。

  • akr
  • ベストアンサー率18% (32/173)
回答No.1

CA若しくはg_CB若しくはB::m_listを静的に生成するのを止めて、動的に生成するようなロジックにすれば、インスタンスの生成順が制御できるので、初期化の問題は回避出来ると思います。 と、ここまで回答を書きましたが、どこまでの改変を許可しているのかによって回答が変わってくると思いませんか? 例えば上記の回答にある、静的生成を止めるってのが、このプログラムの要件に対して許可されていなければ、この回答は駄目ですよね。 条件次第では出来る事と出来無い事が変わってきますから、もう少し条件を追加した質問にしたほうが良いかと思います。

oasobi034
質問者

お礼

早速のご回答ありがとうございます。 ご指摘通り、もう少し条件を追加したいと思います。

関連するQ&A

  • ヘッダのインクルード時のエラー

    初歩的な質問で恐縮です。 以下のようなファイルの構成でc++のプログラムを組んでいます。 <header.h> class H { public: virtual void func() = 0; }; <fileA.cpp> #include "header.h" class A : public H { public: void func() {} }; <fileB.cpp> #include "header.h" class B : public H { public: void func() {} }; <main.cpp> #include "fileA.cpp" #include "fileB.cpp" int main() { A a; B b; a.func(); b.func(); return 0; } これをビルドしようとすると、「クラス"H"を再定義しようとしています」とエラーが出てコンパイルができません。理屈は分かるのですが、これの対処方法が分かりません。この場合、header.hはJAVAにおけるInterfaceのような役割としてそれを使うfileA.cpp、fileB.cppの両者にインクルードしておきたいのです。これはどのようにして対処すべきなのでしょうか?

  • コンストラクタについて

    クラスを学んだため面積と体積を計算するプログラムを試しに作ってみました。面積を計算するクラスとその派生の体積です。見ずらいでしょうが以下プログラムを載せます。 この時 「エラー E2251 class.cpp 40: 基本クラス 'area' を初期化するデフォルトコンストラクタが見つからない(関数 volume::volume(int) )」とエラーが出ます。 何が原因でしょうか? その他プログラムについてのコメントをお願いします。 #include <iostream.h> class area{ protected: int x,y; public: area (int i, int e); int thearea(); int area::set (int i, int e); }; area::area(int i, int e) { cout <<"construct for area is running"<<endl; set (i, e); } int area::set (int i, int e) { x=i, y=e; return 0; } int area::thearea (){ return x*y; } class volume:public area{ int z; public: volume (int d); thevolume (); }; volume::volume(int d) {cout <<"construct for volume is running"<<endl; z=d; } int volume::thevolume (){ return area::thearea()*z; } int main() { int a, b,d; cout<<"a"; cin>>a; cout<<"b"; cin>>b; area c(a, b); cout << "the area" <<c.thearea(); cout<<"next d"; cin>>d; cout<<"volume"; volume t (d); cout << t.thevolume(); return 0; }

  • extern記憶クラス指定子を使う事について

    prg1.cpp----------------------------- #include<stdio.h> void func(void); int gg=5678; int main(void) { printf("main gg=%d\n",gg); func(); return 0; } ---------------------------------- prg2.cpp-------------------------- #include<stdio.h> extern int gg; void func(void) { printf("func gg=%d\n",gg); } ---------------------------------- 以上「prg1.cpp」と「prg2.cpp」という名前のふたつのファイルを作成し、「prg1.cpp」で宣言したグローバル変数を「prg2.cpp」で利用可能にしたいと思っています。 それには、「prg1.cppをコンパイルし、prg2.cppもコンパイルして、両者のオブジェクト(コンパイル後のファイル)をリンクする」と参考書には書いてあったのですが、どのようにしたらリンクされるのかわかりません。 prg.1cpp---------------------- #include<stdio.h> void func(void); int gg=5678; int main(void) { printf("main gg=%d\n",gg); func(); return 0; } #include<stdio.h> extern int gg; void func(void) { printf("func gg=%d\n",gg); } ------------------------------- 以上のように、「prg1.cpp」のファイルに1つにまとめれば、なぜかよくわかりませんが実行できました。 しかし「prg1.cpp」と、「prg2.cpp」をリンクさせてみたいので、教えていただけると嬉しいです。

  • コンストラクタ について

    以下のように、String.h にクラスが定義されています。 class String { public:   String(); private:   char* str; }; それから String.cpp にて、以下のように関数があります。 #include "String.h" String::String() : str(NULL) {   str = new char[15]; } これを、main() から呼ぶにはどのようにしたらいいのでしょうか。 初心者の質問で具にもつかない質問かもしれませんが、一般的な例を教えてください。

  • *.libを作成する方法

    自作のC++のライブラリを使おうとしましたが、 ある環境で、Cしかコンパイルできない環境だったので、 C関数でワンクッションおいた、Cのライブラリを作りたいのです。 しかし、libファイルの作り方がわかりません。 makeとかで作る方法が知りたいです。 C++のライブラリは、統合環境で作成したライブラリです。 そのライブラリ名はMyCPP.libとし、ヘッダはMyCPP.hppとします。 ワンクッション用のファイルは one.cppとし、ヘッダはone.hとします。 *** one.cpp int c_add(int a, int b) { MyCPP x return x.Add(a, b); } *** one.h extern "C" { int c_add(int a, int b); } cl one.cppとすると、エントリポイントがありませんと表示されます。 目的は one.libを作成したいです。

  • 【C++】関数ポインタの使い方

    関数ポインタの使い方で悩んでいます。 下記の (1)のようにグローバルメソッドとして定義したメソッドを関数ポインタに代入することは出来るのですが、 (2)のようにクラスのメンバメソッドとして定義したメソッドは関数ポインタに代入することは出来ませんでした。 Error:バインドされた関数へのポインターは関数の呼び出しにのみ使用できます。 というエラーが発生します。 関数ポインタに外部参照でメソッドを代入することは出来ないのでしょうか? -----(1)------------------------------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; int f(int a, int b){ return a * b; } int _tmain(int argc, _TCHAR* argv[]) { typedef int (* FUNC_POINTER)(int, int); FUNC_POINTER fp; fp = f; cout << fp(1,2) <<endl; getchar(); return 0; } ------------------------------------------------------------------------- -----(2)------------------------------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; class MPointerList{ public: int f(int a, int b){ return a * b; } }; int _tmain(int argc, _TCHAR* argv[]) { typedef int (* FUNC_POINTER)(int, int); FUNC_POINTER fp; //fp = f; MPointerList mP; fp = mP.f; cout << fp(1,2) <<endl; getchar(); return 0; } -------------------------------------------------------------------------

  • COMプログラミング教えてください。

    //COMプログラミングVC->VB6.0 Visual C++で、 main.cpp , NumberClass.h , NumberClass.cpp , NumberIDL.idl ファイルを以下のコードで作成し、 実行しましたら、正常にコンソールアプリケーションが動作しました。 次に、 VisualBasic6.0から参照設定で、Number.tlbというタイプライブラリを読み込ませて実行させたところ、「Functionまたは変数が必要です。」とエラーが表示されました。 どうすれば解決できますか?よろしくお願いします。 VB6.0のコード Private Sub Command1_Click() Dim a As NumberLibrary.NumberClass Dim b As Long b = a.Add(15, 35) End Sub //main.cpp #include<stdio.h> #include<string.h> #include"NumberClass.h" int main() { NumberClass Number; printf("%d",Number.Add(10,90)); getchar(); return 0; } //NumberClass.h class NumberClass { private: int num; public: int Add(int a,int b); void Save(int a); void Load(int *a); }; //NumberClass.cpp #include"NumberClass.h" int NumberClass::Add(int a,int b) { return a+b; } void NumberClass::Save(int a) { num=a; return; } void NumberClass::Load(int *a) { *a=num; return; } //NumberIDL.idl import "oaidl.idl"; import "ocidl.idl"; [uuid(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx),version(1.0)] library NumberLibrary { importlib("stdole2.tlb"); [uuid(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx), object] interface INumber : IUnknown { HRESULT Add([in]int a,[in]int b); HRESULT Save([in]int a); HRESULT Load([out]int *a); }; [uuid(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)] coclass NumberClass { interface INumber; } };

  • const の使い方

    お世話になります。 VC5.0からVC2005.NETにソフトの移植をしているのですが、 その際、const の使い方で混乱しております。 環境:WinXP、VC2005.NET 以下のコードをビルドするとコンパイルエラーが出てしまいます。 ネットで検索しても、混乱中の上自分のケースに当てはまっているのか どうかさえも怪しい状態でして・・・ --コード-- #include "stdafx.h" class A { public: void func( const int*& a ); }; void A::func( const int*& a ) { return; } int _tmain(int argc, _TCHAR* argv[]) { int *a; A CA; CA.func( a ); ←※ここでエラー return 0; } --ここまで-- --エラー-- error C2664: 'A::func' : 1 番目の引数を 'int *' から 'const int *&' に変換できません。(新しい機能 ; ヘルプを参照) --ここまで-- 基本的なことですがご教授願います。

  • 変数の値がおかしくなる

    以下のようなプログラム(DLLとEXE)を書いたのですが、変数の値がおかしくなる(●参照)箇所があります。原因がお分かりになりましたら、ご回答をよろしくお願い致します。 ●mkdll.cppの、sub1()の(※1)までは、input[]が正しい値で入っているが、(※2)で値がおかしくなる。(※1)から(※2)までで、input[]は参照するだけです。 ---test.cpp(EXE)--- … main(){ int input[10],output[10]; int err; CDLL DLL; err = DLL.func(input,output); … return(0); } ---test.cpp End--- ---mkdll.h(DLL)--- … class __declspec(dllexport) CDLL{ public: int func(int *input, int *output); private: int sub1(int *input, int *output); int sub2(int in, int out); … }; ---mkdll.h End--- ---mkdll.cpp(DLL)--- #include "mkdll.h" … int CDLL::func(int *input, int*output){ int i; int error; error = sub1(input, output); return(error); } int CDLL::sub1(int *input, int *outout){ int i; int in1, in2, out1, out2; int err; //(※1) for(i=0; i<5; i++){ err = 0; in1 = input[i*2]; in2 = input[i*2+1]; //(※2) err = sub2(in1, out1); if(err != 0) return(err); … } } … ---mkdll.cpp End---

  • bccでエラー:メンバー関数は呼び出すかそのアドレスを~

    クラスのメンバ関数を、ある関数(実は qsort)の引数として渡したい と思っています。環境は OS:Windows XP コンパイラ:C++Builder6 に付属の bcc32.exe です。 下記のコード(t.cpp)をコマンドプロンプトで bcc32 t としてコンパイルすると「※1」の行で下のエラーになります。 「エラー E2235 t.cpp 29: メンバー関数は呼び出すか  そのアドレスをとらなければならない(関数 main() )」 どのように直せば良いでしょうか? -------------------------------------------------------- #include <stdio.h> #include <stdlib.h> class Aclass { public: void f(int); }; void Aclass::f(int x) { printf("classfunc %d\n", x); } void sub1(int x) { printf("sub-1 %d\n", x); } void call_func(void(*func)(int), int x) { func(2*x + 1); } void main(void) { call_func((void(*)(int))sub1, 11); Aclass *a = new Aclass; call_func((void(*)(int))a->f, -7); //※1 delete a; } -------------------------------------------------------- この掲示板、行頭の空白が削除されてしまいますねー。

専門家に質問してみよう