【问题标题】:why constructors are called if they are not inherited?如果构造函数没有被继承,为什么要调用它们?
【发布时间】:2016-12-29 16:39:00
【问题描述】:
代码正在打印所有的构造函数。我读到当我们从另一个类派生一个类时,构造函数不会被继承。那么为什么c的创建是从b和a调用构造函数
class A
{
public:
A() { cout << "A's constructor called" << endl; }
};
class B
{
public:
B() { cout << "B's constructor called" << endl; }
};
class C: public B, public A // Note the order
{
public:
C() { cout << "C's constructor called" << endl; }
};
int main()
{
C c;
return 0;
}
【问题讨论】:
标签:
c++
oop
inheritance
multiple-inheritance
【解决方案1】:
当您阅读的文档说构造函数是“未继承的”时,这意味着如果类 A 定义了构造函数 A::A(int x),那么子类 B 将不会自动具有采用 @ 的构造函数987654324@.
但是,仍然需要初始化父类的值;否则,父对象可能处于无效状态。构造函数用于初始化类,因此意味着一个父类的构造函数必须从子构造函数的初始化列表中调用。如果父类具有默认构造函数,则默认调用该构造函数。这就是您在示例中看到的。如果父级没有提供默认构造函数,则必须指定要调用的构造函数:
class A
{
public:
A(int x) { cout << "A's constructor called" << endl; }
};
class C: public A
{
public:
C()
: A(7) /* compilation will fail without this line */
{ cout << "C's constructor called" << endl; }
};
【解决方案2】:
构造函数不是传统意义上的继承。
类是继承的。
但是为了构造一个类,它的构造函数需要被调用。这就是它的工作。硬性规定,没有例外。
当你从第二个类继承一个类时,构造第一个类也需要构造第二个类。因为第一类总是包含第二类。另一个硬性规则,没有例外。这就是“继承”的意思。
因此,构造第一个类将调用其构造函数。然后,要构造第二个类,还需要调用它的构造函数(实际上是先构造第二个类,然后再构造第一个类)。
这就是为什么要使用这两个构造函数的原因。
【解决方案3】:
我读到当我们从另一个类派生一个类时,构造函数不会被继承
没错。但是,您似乎误解了它的含义。
假设你有:
struct A
{
A(int) {}
};
struct B : A
{
B() : A(0) {}
};
鉴于以上情况,您将无法使用:
B b(10);
因为A(int) 不被B 继承。
这就是你误会的症结所在。
那么为什么创建 c 是从 b 和 a 调用构造函数
但是,当您构造B 时,会调用B 的构造函数来初始化其成员。还必须调用A的构造函数,才能初始化B对应A的子对象。
有几种方法可以初始化A 的B 部分。
-
您可以使用以下语法在成员初始化列表中显式使用A 的构造函数:
B() : A(0) {}
-
将成员初始化留空,此时会调用A的默认构造函数。
B() {}
相当于:
B() : A() {}
在我提供的示例中,这将导致编译器错误,因为 A 的默认构造函数已通过提供与默认构造函数不同的另一个构造函数而被删除。
回到你对C 的默认构造函数的实现,你有:
C() { cout << "C's constructor called" << endl; }
相当于
C() : B(), A() { cout << "C's constructor called" << endl; }
B::B() 和 A::A() 在构造 C 的实例时被调用。
【解决方案4】:
构造函数在继承类时调用。继承基本上为派生类实例提供了基类的匿名成员实例等。需要构造这些实例,以便调用它们的构造函数。
【解决方案5】:
“构造函数不被继承”的意思是,类 C 应该并且将会有它自己的构造函数,尽管事实上有 B 的构造函数,它不能使用 B 的构造函数而不是 C 的构造函数。
这正是你得到的:你得到所有父类的构造函数。
当你有类的层次结构,并从一个构造对象时,他的所有父级将依次构造,从基类开始。
当你摧毁他们时,他和他所有的父母都会相继毁灭,从他开始。
按规则:先创建 -- 最后销毁。
【解决方案6】:
通过不继承,C++11 标准意味着这个
class A
{
public:
A(int x) {}
};
class B: public A
{
};
int main(void)
{
B b(5);
return 0;
}
这将无法编译,因为A(int) 未被继承。您可以定义B 以显式继承A(int) by
class B: public A
{
using A::A;
};
在您的情况下,您定义了所有默认 ctor,无论是否明确定义,仍然存在,并且由于您的 C c 声明,将作为对象初始化的一部分被调用。
【解决方案7】:
C++ 继承基本上创建了一个由其超类的部分组成的类。例如:
class A {
public:
A() {
std::cout << "Constructor A" << '\n';
}
};
class B : public A {
public:
B() {
std::cout << "Constructor B" << '\n';
}
};
class C : public B {
public:
C() {
std::cout << "Constructor C" << '\n';
}
};
C 类实际上是 C 类,具有 B 类部分和 A 类部分。因此,为了构造 C 类,我们需要通过调用这些部分的构造函数来构造它的每个部分。这些构造函数的顺序是从最基类到最派生类(在本例中为 A 到 C)。 most-base 是继承树顶部的类,most-derived 是底部的类。
同样的规则也适用于析构函数。唯一的区别是析构函数的调用顺序是从最衍生到最基(C 到 A)。