C++ 在c++,为什么我们需要从子类调用grand_父构造函数?
请阅读代码以了解情况C++ 在c++,为什么我们需要从子类调用grand_父构造函数?,c++,inheritance,constructor,diamond-problem,C++,Inheritance,Constructor,Diamond Problem,请阅读代码以了解情况 #include <iostream> using namespace std; class one { protected: int x; public: one(int a) { x=a; cout << "one cons called\n"; } void display(void) { cout << "x = " <<
#include <iostream>
using namespace std;
class one
{
protected:
int x;
public:
one(int a)
{
x=a;
cout << "one cons called\n";
}
void display(void)
{
cout << "x = " << x << endl;
}
~one()
{
cout << "one destroy\n";
}
};
class two : virtual protected one
{
protected:
int y;
public:
two(int a,int b) : one(a),y(b)
{
cout << "two cons called\n";
}
void display(void)
{
one::display();
cout << "y = " << y << endl;
}
~two()
{
cout << "two destroy\n";
}
};
class three : protected virtual one
{
protected:
int z;
public:
three(int a,int b) : one(a),z(b)
{
cout << "Three cons called\n";
}
void display(void)
{
one::display();
cout << "z = " << z << endl;
}
~three()
{
cout << "three destroy\n";
}
};
class four : private two, private three
{
public:
four(int a,int b,int c) :one(a), two(a,b),three(a,c)
{
cout << " four cons called\n";
}
void display(void)
{
one::display();
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
~four()
{
cout << "four destroy\n";
}
};
int main()
{
four ob(1,2,3);
ob.display();
return 0;
}
与
错误消息如下:在我的代码块ide中没有调用“one::one()”的匹配函数
正如您所看到的,这是一个基于菱形问题的代码,其中类1是grand_父类。二、三班为家长班,四班为儿童班。所以我使用虚拟关键词来避免歧义。这里我理解的一切,除非有一件事。我知道当父类具有参数化构造函数时,我们需要从派生类向该构造函数提供参数。那么,为什么需要向构造函数1提供参数,其中类4只有两个父类,即2和3。
如果我不从类4调用构造函数1,代码将给我编译时错误。请解释我们为什么需要这样做。层次结构中的
虚拟继承通过确保one
的子类中只存储one
的一个实例来消除基类one
存在的歧义。回想一下,当继承某个类时,派生实例总是将一个基实例存储在内部的某个地方-因此virtual
继承确保one
在two
和three
中的实例在某种程度上被继承层次结构下的任何类“覆盖”
现在的问题是:谁负责初始化这一个实例?应该是两个
还是三个
?显然不是两个都有,因为只有一个例子。这里是:它总是最派生的类,负责初始化one
——这很有意义:嵌入基类副本的实例必须初始化它
这就是嵌入基类实例的类层次结构在没有four
和four
加虚拟继承的情况下的外观:
+----------+ +----------+
| one | | one |
+----+-----+ +----+-----+
| |
| |
+-------+-----------+ virtual +--------+--------+ virtual
| | | |
| | | |
+--------+-------+ +-------+-------+ +----+----+ +----+----+
| two | | three | | two | | three |
| +------------+ | | +----------+ | +----+----+ +----+----+
| | one | | | | one | | | |
| +------------+ | | +----------+ | +--------+--------+
| => must init! | | => must init! | |
+----------------+ +---------------+ +-------+--------+
| four |
| +------------+ |
| | one | |
| +------------+ |
| => must init! |
+----------------+
您可以这样来考虑这种机制:virtual
继承赋予基类实例virtual
-ness,这包括构造实例-这种责任向下传递到层次结构中。当一个类的两个超类有一个公共基类时,菱形问题就会发生。
这个问题的解决方案是“Virtual”关键字。一般情况下,不允许直接调用祖父母的构造函数,必须通过父类调用。只有当我们使用“Virtual”关键字时才允许。
因此,当我们使用“virtual”关键字时,默认情况下会调用祖辈类的默认构造函数,即使父类显式调用参数化构造函数。假设您有以下菱形:
Base
/ \
Left Right
\ /
Down
Base
类可以非常简单,它有一个由构造函数初始化的int
成员:
struct Base
{
Base(int x)
: x(x)
{}
virtual ~Base() = default;
int x;
};
由于Left
继承自Base
,因此其构造函数可以将参数传递给Base
构造函数。这里,如果您构造一个左
对象,其x
成员将是1
:
struct Left : virtual Base
{
Left() : Base(1)
{}
};
另一个类,Right
,也继承自Base
。这意味着它的构造函数也可以将参数传递给Base
构造函数。这里,它的x
成员将是2
:
struct Right : virtual Base
{
Right() : Base(2)
{}
};
现在有趣的部分来了:如果从左
和右
继承,会发生什么
// This does not compile.
struct Down : Left, Right
{
Down() : Left(), Right()
{}
};
Left
和Right
都调用Base
构造函数,但它们使用不同的参数。编译器现在应该使用Left
中的Base(1)
部分,还是应该使用Right
中的Base(2)
部分?答案很简单:两者都不用!编译器将选择权留给您,并允许您指定应使用哪个构造函数:
// Hooray, this version compiles.
struct Down : Left, Right
{
Down() : Base(42), Left(), Right()
{}
};
您显示的代码和类有一些可疑之处。例如,为什么要覆盖所有类中的非虚拟函数display
?为什么你有private
继承?通常建议不要使用私有继承来进行组合。@Someprogrammerdude。我写这段代码是为了学习,不是为了真正的项目写的。可能的重复:您是手工还是使用工具创建的图表?@0x499602D2初稿,然后在vim中进行一些视觉块编辑。
// This does not compile.
struct Down : Left, Right
{
Down() : Left(), Right()
{}
};
// Hooray, this version compiles.
struct Down : Left, Right
{
Down() : Base(42), Left(), Right()
{}
};