[解決済み] クラス間の循環的な依存関係によるビルドエラーを解決する
質問
私はよく、C++プロジェクトで複数のコンパイル/リンカエラーに直面する状況に陥ります。これは、(他の誰かによって作られた)いくつかの間違った設計上の決定によって、異なるヘッダーファイル内のC++クラス間の循環依存関係が発生するためです。 (同じファイル内でも起こりうる) . しかし、幸いなことに(?)このようなことはあまり起こらないので、次にまた同じことが起こったときのために、この問題の解決策を覚えておくことにしました。
そこで、今後思い出しやすくするために、代表的な問題とその解決策を一緒に掲載することにしました。もちろん、より良い解決策を歓迎します。
-
A.h
class B; class A { int _val; B *_b; public: A(int val) :_val(val) { } void SetB(B *b) { _b = b; _b->Print(); // COMPILER ERROR: C2027: use of undefined type 'B' } void Print() { cout<<"Type:A val="<<_val<<endl; } };
-
B.h
#include "A.h" class B { double _val; A* _a; public: B(double val) :_val(val) { } void SetA(A *a) { _a = a; _a->Print(); } void Print() { cout<<"Type:B val="<<_val<<endl; } };
-
main.cpp
#include "B.h" #include <iostream> int main(int argc, char* argv[]) { A a(10); B b(3.14); a.Print(); a.SetB(&b); b.Print(); b.SetA(&a); return 0; }
解決方法は?
これを考える方法は、"コンパイラのように考えること"です。
あなたがコンパイラを書いていると想像してください。そして、このようなコードを見てください。
// file: A.h
class A {
B _b;
};
// file: B.h
class B {
A _a;
};
// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
A a;
}
コンパイル時に
.cc
ファイルを作成します(このとき
.cc
であって
.h
がコンパイルの単位である場合)、オブジェクト
A
. では、まあ、どれくらいのスペースが必要なのでしょうか?を格納するのに十分な容量です。
B
! の大きさは?
B
では を格納するのに十分な
A
! おっとっと。
明らかに循環参照を断ち切らなければならない。
これを破るには、代わりにコンパイラが前もって知っている限りの領域を確保するようにします。たとえばポインタや参照は常に32ビットか64ビット(アーキテクチャによる)なので、(どちらかを)ポインタか参照に置き換えれば、物事はうまくいくでしょう。で置き換えるとしましょう。
A
:
// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
今は良くなっています。多少は。
main()
はまだ言っている。
// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
にコピーするだけで、(プリプロセッサを除けば)どこから見ても
.cc
. そのため、実際には
.cc
のように見えます。
// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
コンパイラがこれを処理できない理由はおわかりでしょう。
B
は、そのシンボルを見たこともないのです。
そこで、コンパイラに
B
. これは
前方宣言
で詳しく説明しています。
この回答
.
// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
これは 作品 . これは すごい . しかし、この時点で、循環参照問題と、その修正が悪いとはいえ、私たちが"fix"するために何をしたかについて、理解していただく必要があります。
この修正が悪いのは、次の人が
#include "A.h"
を宣言する必要があります。
B
を使用することができず、ひどい
#include
というエラーが発生します。そこで、この宣言を
A.h
そのものです。
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
そして、その中に
B.h
この時点では
#include "A.h"
を直接入力します。
// file: B.h
#include "A.h"
class B {
// note that this is cool because the compiler knows by this time
// how much space A will need.
A _a;
}
HTH
関連
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】コンストラクターでのエラー:識別子を期待されますか?
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】変数 '' を抽象型 '' と宣言できない。
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み】IntelliSense:オブジェクトに、メンバー関数と互換性のない型修飾子がある
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】'cout'は型名ではない
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】文字列関数で'char const*'のインスタンスを投げた後に呼び出されるterminate [閉店].
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】Visual Studioのデバッガーエラー。プログラムを開始できません 指定されたファイルが見つかりません
-
[解決済み】コンパイラーエラー C4430: 型指定子がない - int を想定 [重複].