在C++中,是否可以将类声明为从另一个类继承?

ano*_*non 63 c++ inheritance forward-declaration

我知道我能做到:

class Foo;
Run Code Online (Sandbox Code Playgroud)

但是我可以将一个类声明为继承自另一个类,例如:

class Bar {};

class Foo: public Bar;
Run Code Online (Sandbox Code Playgroud)

示例用例是共变量引用返回类型.

// somewhere.h
class RA {}
class RB : public RA {}
Run Code Online (Sandbox Code Playgroud)

...然后在另一个不包含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();  // 
}
Run Code Online (Sandbox Code Playgroud)

唯一的信息编译器应该需要处理的声明,RB* B:Foo()RB具有RA作为公共基类.现在显然你需要somewhere.h如果你打算从中做任何类型的反引用返回值Foo.但是,如果某些客户端从不调用Foo,那么它们没有理由包含somewhere.h,这可能会显着加快编译速度.

Joe*_*Joe 42

前向声明仅对告诉编译器具有该名称的类确实存在并将在其他地方声明和定义非常有用.在编译器需要有关类的上下文信息的任何情况下都不能使用它,编译器也没有任何用处来告诉它只有一点关于类.(通常,在没有其他上下文的情况下引用该类时,您只能使用前向声明,例如作为参数或返回值.)

因此,你不能转发任何情况下宣布吧,你再使用它来帮助申报富,而且平了没有意义有一个向前声明包含基类 - 这是什么告诉你,除了没有?

  • "除了什么都没有告诉你什么?" 呃,它告诉你类型是基类的子类.那不是什么都没有.例如,如果基类中的函数偶然发现包含子类的对象(作为"其他"对象),则它无法将该对象传递给采用基类对象的函数,因为它不知道如何将子类强制转换为基类,即使它可以访问子类的所有公共成员.我同意你的看法,这是不可能做到这个人所要求的,但我不同意你的评估,认为这样做是没用的. (51认同)
  • @codetaku - 是的!例如,您希望在智能指针上使用 `static_assert` 或 `enable_if` 以将其限制为某些类型 - 您希望在不完整的类型上使用智能指针......呸! (2认同)

Jon*_*vis 40

前向声明是声明,而不是定义.所以,任何需要声明类的东西(比如指向该类的指针)只需要前向声明.但是,任何需要定义的东西 - 即需要知道类的实际结构 - 都不适用于前向声明.

派生类肯定需要知道父类的结构,而不仅仅是父元素的存在,因此前向声明是不够的.

  • 它会限制对象的实现,但我认为应该可以进行`*D` - >`*B`转换,同时只知道继承结构.我希望它看起来像实现虚拟基类所需的结构. (5认同)
  • 您是否需要知道类的结构才能正确处理指向它的指针?只要您专门处理参考文献,您就可以了. (4认同)
  • @BCS是的.只要你处理指针,你只需要前向声明,但是当你声明一个派生类时,你需要父的定义,而不仅仅是它的声明,因为你不是只处理指针. (2认同)

小智 19

不,即使您只处理指针,也无法转发声明继承.处理指针之间的转换时,编译器有时必须知道类的详细信息才能正确进行转换.这是多重继承的情况.(特殊情况下,层次结构的某些部分仅使用单一继承,但这不是语言的一部分.)

考虑以下微不足道的案例:

#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);
}
Run Code Online (Sandbox Code Playgroud)

我收到的输出(使用32位visual studio 2010)是:

A: 0018F748, B: 0018F74C, C: 0018F748
Run Code Online (Sandbox Code Playgroud)

因此,对于多重继承,在相关指针之间进行转换时,编译器必须插入一些指针算法才能使转换正确.

这就是为什么即使你只处理指针,也不能转发声明继承.

至于为什么它会有用,当你想要使用共变量返回类型而不是使用强制转换时,它会改善编译时间.例如,这将无法编译:

class RA;
class A             { public: virtual RA *fooRet(); };
class RB;
class B : public A  { public: virtual RB *fooRet(); };
Run Code Online (Sandbox Code Playgroud)

但这会:

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(); };
Run Code Online (Sandbox Code Playgroud)

当你有B类对象(不是指针或引用)时,这很有用.在这种情况下,编译器足够智能以使用直接函数调用,并且您可以直接使用RB*的返回类型而无需强制转换.在这种情况下,通常我继续进行返回类型RA*并对返回值进行静态转换.

  • 我知道这是一个古老的答案,但IMO这个答案要比那些投票较多且被接受的答案更有帮助,因为忽略了将`Foo*`转换为'Bar*'(或引用)的事实.即使是不完整的类型也是有用的. (7认同)