How to call copy constructor of all base classes for copying most derived class object in diamond inheritance in C++?

狂风中的少年 提交于 2019-12-07 03:54:08

问题


Consider the below code:

#include<iostream>
using namespace std;
class A
{
public:
     A() {cout << "1";}
     A(const A &obj) {cout << "2";}
};

class B: virtual A
{
public:
    B() {cout << "3";}
    B(const B & obj) {cout<< "4";}
};

class C: virtual A
{
public:
   C() {cout << "5";}
   C(const C & obj) {cout << "6";}
};

class D:B,C
{
public:
    D()  {cout << "7";}
    D(const D & obj) {cout << "8";}
};

int main()
{
   D d1;
   cout << "\n";
   D d(d1);
}

The output of the program is below:

1357
1358

So, for line D d(d1) the copy constructor of D class is bein called. During inheritance we need to explicitly call copy constructor of base class otherwise only default constructor of base class is called. I understood till here.

My Problem:

Now I want to call copy constructor of all base classes during D d(d1) execution. For that if I try below D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";} Then I get this error: error: 'class A A::A' is inaccessible within this context

How to resolve the issue. I want copy constructor of A, B and C when copy constructor of D gets called. It might be very small change but I am not getting.


回答1:


First, lets change your inheritance as currently it is private:

class B : virtual protected A {...};
class C : virtual protected A {...};

Now, in your copy constructor, explicitly specify that the copy constructors of A and B and C should be called:

class D : protected B, protected C {
    D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};

And the output will be as desired (2468).

Why?

When we have virtual base classes, they must be initialized by the most derived class, otherwise there would be ambiguity concerning whether B or C for example is responsible for the construction of A.

§12.6.2, (13.1):

In a non-delegating constructor, initialization proceeds in the following order:

  • First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

In particular, if you define a copy constructor, and omit the list of copy constructors it should call, then the default constructors will be used.




回答2:


The way you have inherited your classes, all of them use private inheritance.

By changing the inheritance of B from A and C from A to be protected or public, you can resolve the problem.

class B : protected virtual A
{
   ...
}

class C : protected virtual A
{
   ...
}

or

class B : public virtual A
{
   ...
}

class C : public virtual A
{
   ...
}

and then update D's copy constructor to:

D(const D & obj) : A(obj), B(obj), C(obj) {cout <<"8";}

PS It's baffling to me that the default constructor works even with private inheritance.




回答3:


Alternative solution which doesn't require changing the inheritance modifiers of class B or C:

class A
{
public:
     A() {cout << "1";}
     A(const A &obj) {cout << "2";}
};

class B: virtual A
{
public:
    B() {cout << "3";}
    B(const B & obj) {cout<< "4";}
};

class C: virtual A
{
public:
   C() {cout << "5";}
   C(const C & obj) {cout << "6";}
};

class D:B,C,virtual A
{
public:
    D() {cout << "7";}
    D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};



回答4:


Regarding access checking for constructors: from [class.access]/6

All access controls in Clause [class.access] affect the ability to access a class member name from the declaration of a particular entity ... [ Note : This access also applies to implicit references to constructors, conversion functions, and destructors. — end note]

similarly, [class.access]/4

Special member functions obey the usual access rules. [ Example: Declaring a constructor protected ensures that only derived classes and friends can create objects using it. — end example ]

Regarding base class subobject initialization: from [class.base.init]/9

In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then ... otherwise, the entity is default-initialized

The lack of any ctor-initializer for a base class subobject means that the subobject is default-initialized; from [dcl.init]/7

To default-initialize an object of type T means: ... The constructor thus selected is called, with an empty argument list, to initialize the object.

So the lack of any base in a ctor-initializer is a request for default-initialization for that base, which means calling the default constructor.

The lack of mention of a base class makes no difference; in any case, a constructor has no name and is not named in the ctor-initializer, it is referenced either explicitly or implicitly. There is nothing in the standard suggesting that access control should not be performed in such case.

It seems like the constructor, being from an inaccessible base class, cannot be called either way, so your program should not compile.

In any case, you can change the inheritance from private to protected, even add a path to the virtual base class:

class D: B, C, virtual A
{

This way, the virtual base class A is still private, but is accessible to D.



来源:https://stackoverflow.com/questions/43703229/how-to-call-copy-constructor-of-all-base-classes-for-copying-most-derived-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!