79

私はできることを知っています:

class Foo;

しかし、次のように、クラスを別のクラスから継承するものとして前方宣言できますか?

class Bar {};

class Foo: public Bar;

ユースケースの例は、共変参照の戻り値の型です。

// somewhere.h
class RA {}
class RB : public RA {}

...そして、somewhere.hを含まない別のヘッダーで

// other.h
class RA;

class A {
 public:
  virtual RA* Foo();  // this only needs the forward deceleration
}

class RB : public RA; // invalid but...

class B {
 public:
  virtual RB* Foo();  // 
}

コンパイラが宣言を処理するために必要な唯一の情報は、パブリック基本クラスとして持っているRB* B:Foo()ものです。からの戻り値の何らかの種類の逆参照を行う場合は、明らかにどこか.h が必要になります。ただし、一部のクライアントが をまったく呼び出さない場合、コンパイルを大幅に高速化する可能性がある where.h をインクルードする理由はありません。RBRAFooFoo

4

5 に答える 5

50

前方宣言は、その名前のクラスが存在し、別の場所で宣言および定義されることをコンパイラに伝える場合にのみ役立ちます。コンパイラがクラスに関するコンテキスト情報を必要とする場合には使用できません。また、クラスについて少しだけ伝えるためにコンパイラに使用することもできません。(通常、パラメータや戻り値など、他のコンテキストなしでそのクラスを参照する場合にのみ、前方宣言を使用できます。)

したがって、Bar を使用して Foo の宣言を支援するシナリオでは、Bar を前方宣言することはできません。基本クラスを含む前方宣言を行うことはまったく意味がありません。なし?

于 2010-01-29T01:56:50.333 に答える
40

前方宣言は宣言であり、定義ではありません。したがって、クラスの宣言を必要とするもの (そのクラスへのポインターなど) は、前方宣言のみが必要です。ただし、定義を必要とするもの、つまりクラスの実際の構造を知る必要があるものは、前方宣言だけでは機能しません。

派生クラスは、親が存在するだけでなく、親の構造を確実に知る必要があるため、前方宣言では不十分です。

于 2010-01-29T02:06:31.813 に答える
23

いいえ、ポインターのみを扱っている場合でも、継承を前方宣言することはできません。ポインター間の変換を処理する場合、コンパイラーは変換を正しく行うためにクラスの詳細を知る必要がある場合があります。これは多重継承の場合です。(単一の継承のみを使用する階層の一部を特別なケースにすることもできますが、それは言語の一部ではありません。)

次の簡単なケースを考えてみましょう:

#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{ 
    C c; A *pa = &c; B *pb = &c; C *pc = &c; 
    printf("A: %p, B: %p, C: %p\n", pa, pb, pc);
}

私が受け取った出力 (32 ビットの Visual Studio 2010 を使用) は次のとおりです。

A: 0018F748, B: 0018F74C, C: 0018F748

したがって、多重継承の場合、関連するポインター間で変換する場合、コンパイラーはポインター演算を挿入して変換を正しく行う必要があります。

これが、ポインタだけを扱っている場合でも、継承を前方宣言できない理由です。

なぜ便利なのかというと、キャストを使用する代わりに共変の戻り値の型を使用したい場合、コンパイル時間が改善されます。たとえば、これはコンパイルされません:

class RA;
class A             { public: virtual RA *fooRet(); };
class RB;
class B : public A  { public: virtual RB *fooRet(); };

しかし、これは:

class RA;
class A             { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A  { public: virtual RB *fooRet(); };

これは、タイプ B (ポインタや参照ではない) のオブジェクトがある場合に便利です。この場合、コンパイラは直接関数呼び出しを使用できるほどスマートであり、キャストせずに RB* の戻り値の型を直接使用できます。この場合、通常は先に進み、戻り値の型を RA * にし、戻り値に対して静的キャストを行います。

于 2012-04-13T17:15:11.103 に答える
-1

役に立たないと思います。考えてみてください: クラス、Bar を定義しました:

class Bar {
public:
    void frob();
};

次に、クラス Foo を宣言します。

class Foo;

Foo でできることは、それへのポインターを構築することだけです。Fooここで、から派生した情報を追加するとしますBar

class Foo: public Bar;

今までできなかったことができるようになったことは何ですか?あなたができることは、へのポインターを受け入れてFooへのポインターにキャストし、Barそのポインターを使用することだけだと思います。

void frob(Foo* f) {
    Bar *b = (Bar)f;
    b->frob();
}

ただし、別の場所でポインターを生成している必要があるため、Bar代わりにポインターを受け入れることができます。

void frob(Bar* b) {
    b->frob();
}
于 2010-02-02T11:22:54.497 に答える