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

我知道我能做到:

class Foo;

但是我可以将一个类前向声明为继承自另一个类吗,比如:

class Bar {};


class Foo: public Bar;

协变引用返回类型是一个示例用例。

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

... 然后在另一个标题,不包括某处

// 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()声明所需的唯一信息是 RBRA作为公共基类。现在,如果您打算对 Foo的返回值进行任何类型的解引用,那么显然您需要 someone. h。但是,如果一些客户端从来不调用 Foo,那么就没有理由包含可能大大加快编译速度的 someone. h。

48509 次浏览

A forward declaration is only really useful for telling the compiler that a class with that name does exist and will be declared and defined elsewhere. You can't use it in any case where the compiler needs contextual information about the class, nor is it of any use to the compiler to tell it only a little bit about the class. (Generally, you can only use the forward declaration when referring to that class without other context, e.g. as a parameter or return value.)

Thus, you can't forward declare Bar in any scenario where you then use it to help declare Foo, and it flat-out doesn't make sense to have a forward declaration that includes the base class -- what does that tell you besides nothing?

Forward declarations are declarations, not definitions. So, anything that requires the declaration of a class (like pointers to that class) need only the forward declaration. However, anything that would require the definition - i.e. would need to know the actual structure of the class - would not work with just the forward declaration.

Derived classes definitely need to know the structure of their parent, not just that the parent exists, so a forward declaration would be insufficient.

I don't think it's useful. Consider: you have defined a class, Bar:

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

Now you declare a class Foo:

class Foo;

All you can do with Foo is construct a pointer to it. Now, suppose you add the information that Foo is derived from Bar:

class Foo: public Bar;

What can you now do that you couldn't do before? I think that all you can do is accept a pointer to Foo and cast it into a pointer to Bar, then use that pointer.

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

However, you must have generated the pointer elsewhere, so you could have just accepted a pointer to Bar instead.

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

No it is not possible to forward declare inheritance, even if you are only dealing with pointers. When dealing with conversions between pointers, sometimes the compiler has to know the details of the class to do the conversion correctly. This is the case with multiple inheritance. (You could special case some parts parts of the hierarchy that only use single inheritance, but that isn't part of the language.)

Consider the following trivial case:

#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);
}

The output I received (using 32 bit visual studio 2010), is:

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

So for multiple inheritance, when converting between related pointers, the compiler has to insert some pointer arithmetic to get the conversions right.

This is why, even if you are dealing only with pointers, you can't forward declare inheritance.

As for why it would be useful, it would improve compile times when you do want to make use of co-variant return types instead of using casts. For example this will not compile:

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

But this will:

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(); };

This is useful when you have objects of type B (not pointers or references). In this case the compiler is smart enough to use a direct function call, and you can use the return type of RB* directly without casting. In this case, usually I go ahead and make the return type RA * and do a static cast on the return value.

All you needed to do was declare RB without the : public RA (oh, and also add ; to the end of your class definitions):

class RA;


class A {
public:
virtual RA* Foo();
};


class RB;


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


// client includes somewhere.h
class RA {};
class RB : public RA {};


int main ()
{
return 0;
}

However, this doesn't solve the specific problem described nicely in the answer by user1332054.

Some of the other answers appear to show some misconceptions that I'd like to dispel:

Forward declaring is useful even when when we know that the definition is not likely to be included. This allows us to do a lot of type-deduction in our libraries that make them compatible with many other established libraries without including them. Including libraries unnecessarily leads to too many nested includes that can explode the compile time. It's good practice to make your code compatible when appropriate, and to include as little as possible.

Typically you can define a class with pointers to classes that have only been declared and not defined. Example:

struct B;


struct A
{
B * b_;


B * foo ()
{
return b_;
}


B & foo (B * b)
{
return *b;
}
};


int main ()
{
return 0;
}

The above compiles fine, because the compiler doesn't need to know anything about B.

An example where it might be a bit harder to realise that the compiler needs more information:

struct B;


struct A
{
B * foo ()
{
return new B;
}
};

The above problem is because new B invokes the B::B() constructor which hasn't been defined yet. Also:

struct B;


struct A
{
void foo (B b) {}
};

Here foo must call the copy constructor for b, which also hasn't been defined yet. Lastly:

struct B;


struct A
{
B b;
};

Here we have implicitly defined A with the default constructor, which calls the default constructor of call its members,b, which hasn't been defined yet. I think' you get the point.

So, in reference to the more general problem, described by user1332054, I honestly don't understand why it's not possible to use pointers to undefined classed in an inherited virtual function.

More broadly though, I think that you're making it more difficult for yourself by defining your classes instead of only declaring them. Here's an example where you get to DoCleverStuff with your classes in your library before you've defined any of your classes at all:

// Just declare


class RA;
class RB;


class A;
class B;


// We'll need some type_traits, so we'll define them:


template <class T>
struct is_type_A
{
static constexpr bool value = false;
};


template <>
struct is_type_A <A>
{
static constexpr bool value = true;
};


template <class T>
struct is_type_B
{
static constexpr bool value = false;
};


template <>
struct is_type_B <B>
{
static constexpr bool value = true;
};


#include <type_traits>


// With forward declarations, templates and type_traits now we don't
// need the class definitions to prepare useful code:


template<class T>
typename std::enable_if<is_type_A<T>::value, RA *>::type
DoCleverStuff (T & t)
{
// specific to A


return t.fooRet();
}


template<class T>
typename std::enable_if<is_type_B<T>::value, RB *>::type
DoCleverStuff (T & t)
{
// specific to B


return t.fooRet();
}


// At some point the user *does* the include:


class RA
{
int x;
};


class RB : public RA
{
int y;
};


class A
{
public:
virtual RA * fooRet()
{
return new RA;
}
};


class B : public A
{
public:


virtual RB * fooRet()
{
return new RB;
}
};


int main ()
{
// example calls:


A a;


RA * ra = DoCleverStuff(a);


B b;


RB * rb = DoCleverStuff(b);


delete ra;
delete rb;


return 0;
}