c++ base class contain instance of a derived class

2019-07-26 06:09发布

问题:

I was wondering if someone could explain to me how I might be able to implement something similar to this:

namespace advanced_cpp_oop
{
  class A
  {
    B b;
  };

  class B : public A
  {
  };
}

int main()
{
}

Where an instance of a base class can contain an instance of a derived class? When the above code is compiled the following error is generated:

g++ advanced_cpp_oop.cpp 
advanced_cpp_oop.cpp:8:5: error: ‘B’ does not name a type

The (almost) equivalent Java code which does compile is:

public class AdvancedCppOop
{
  public static void main(String[] args)
  {
    A a;
  }
}

class A
{
  B b;
}

class B extends A
{
}

Thanks

回答1:

You need to a pointer and a forward declaration:

namespace advanced_cpp_oop
{
  class B;

  class A
  {
    B* b;
  };

  class B : public A
  {
  };
}

In your C++ code, you are creating an instance of your class B inside class A, which is not possible since the compiler does not yet know anything (especially not the size) of class B.

With the code from my answer, you need to dynamically allocate an instance of class B and assign it to the b pointer somewhere else in your code.

On a side note, from design perspective, this does not really make sense since a parent class should not depend on a sub class.



回答2:

You have to do it with some type of pointer, such as unique_ptr, and a forward declaration:

  class B;

  class A
  {
    std::unique_ptr<B> b;
  };

  class B : public A
  {
  };

This is silly though and you should probably rethink your design.



回答3:

There is one very important difference between C++ and Java. C++ is a language with value semantics, while Java is a language with reference semantics. When in Java you create a variable of anything other than a primitive type, you are not creating an object of that type, but a reference to such an object. On the contrary, in C++ the same construct refers to an actual object.

If you keep this in mind, it is simple to understand why the following cannot possibly work:

class Base {
   Derived d;
};
class Derived : Base {};

The first definition in C++ means that the object Base contains internally (not by reference) an object of type Derived. At the same time, Derived contains by means of inheritance a subobject of type Base.

That means that Derived contains a Base that contains a Derived containing a Base... What would be the size of Base or Derived?

In a language with reference semantics, or in C++ if you use pointers, that is not a problem. The Base object contains a reference/pointer to Derived. Derived contains a Base subobject by means of inheritance. The size of Base is well known: all the other fields plus the size of a reference/pointer. The size of Derived is whatever the size of Base is plus any extra members that are added.



回答4:

Andreas beat me to the correct answer, but I'll just add that the Java code works only because Java objects are implicitly held by pointers (hence the B b = new B(...); statements sprinkled throughout Java code) even if it doesn't look like it. Your original C++ code doesn't work (even with a forward declaration of class B added) because the compiler doesn't know how big a B object is, and thus doesn't know how big an A object that contains it will be. On the other hand, all pointers have the same size (regardless of pointed-to type), so the compiler has no such problems when you replace a B object with a pointer to a B object in class A.