【问题标题】:Constructor call in inherited classes继承类中的构造函数调用
【发布时间】:2013-02-12 03:05:56
【问题描述】:

考虑以下代码:

class A {
public:
    int a;
};

class B : public A {
public:
    B() { std::cout << "B[" << a << "]" << std::endl; }
};

class C : public B {
public:
    C() { std::cout << "C[" << a << "]" << std::endl; }
};

int main(int argc, char *argv[]) {

    B();
    std::cout << std::endl;
    C();

}

及其输出——用 g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 编译的程序:

B[0]

B[-2097962768]
C[-2097962768]

我发现获得第二次调用 -- C() -- 来初始化其值的唯一方法是添加对构造函数的显式调用,例如:

class B : public A {
public:
    B() : A() { std::cout << "B[" << a << "]" << std::endl; }
};

class C : public B {
public:
    C() : B() { std::cout << "C[" << a << "]" << std::endl; }
};

虽然我知道调用每个以前的类的默认构造函数会初始化这些值,但是当没有指定任何内容时,我无法看到调用的是什么。

默认构造函数不是默认调用的——因此是它的句柄?

【问题讨论】:

标签: c++ constructor initialization default default-constructor


【解决方案1】:

使用您的原始代码

class A {
public:
    int a;
};

class B : public A {
public:
    B() { std::cout << "B[" << a << "]" << std::endl; }
};

A 的成员没有被初始化,因为你没有指定任何初始化。通常,在 C++ 中,您不需要为不需要的东西付费。因此,默认情况下您不会为 POD 成员获取初始化(但是,您会为具有构造函数的成员获取它,因为那里指定了初始化)。

在您随后的“显式构造函数调用”代码中

class B : public A {
public:
    B() : A() { std::cout << "B[" << a << "]" << std::endl; }
};

您已指定A 基类子对象的值初始化。这实际上减少了零初始化。

的区别是一样的
A* p = new A;

A* p = new A();

后一个值-初始化对象。


标准……

C++11 §8.5/10:
“初始化器为空括号集的对象,即(),应进行值初始化。”

C++11 §8.5./7:
值初始化一个T类型的对象意味着:
— 如果T 是具有用户提供的构造函数 (12.1) 的(可能是 cv 限定的)类类型(第 9 条),则调用 T 的默认构造函数(如果 @987654332,则初始化格式错误@ 没有可访问的默认构造函数);
— 如果T 是一个(可能是 cv 限定的)非联合类类型,没有用户提供的构造函数,则该对象为零初始化,并且如果 T 的隐式声明的默认构造函数不平凡,则调用该构造函数.
— 如果T 是一个数组类型,那么每个元素都是值初始化的;
— 否则,对象被零初始化。
值初始化的对象被认为是已构造的,因此受制于适用于“构造的”对象、“构造函数已完成的对象”等的本国际标准的规定,即使没有为对象的构造函数调用构造函数。初始化。”

也许值得注意的是,值初始化不是原始 C++98 的一部分。它是由 Andrew Koenig(以“Koenig 查找”闻名)在 C++03 中引入的,目的是处理一些纯默认初始化的意外影响的严重问题。这就是 () 初始化器在 C++98 中给你买的东西。

【讨论】:

  • 非常完整的解释!谢谢!按照你的new 的例子,我曾经使用过A&amp; a(*(new A()));,有人说它是杀死一只猫。你明白他的意思吗?我的初始化是不是太“讨厌”了?
  • @Rubens:至少可以说,该声明有点不合常规。 :-) 为清楚起见,我将a 声明为指针。我想也许这个表达隐喻地指的是杀死一只猫before it kills you
  • 我想就是这样 ^^ 感谢您的回答——我将使用指针而不是参考神秘化! (:
【解决方案2】:

编辑我原来的答案是完全错误的,所以我做了很多修改。

如果要保证A::a在没有值初始化调用如

的情况下是零初始化的
A a1; //

B() { .... }

那么你可以给A一个构造函数并在那里进行初始化:

class A {
public:
  A() : a() {} // value initialization of a, means zero initialization here.
    int a;
};

否则,在BC 的构造函数中显式调用A() 执行值初始化。

// value initialization of A sub-object leads to zero initialization of A::a
 B() : A() {} 

这也适用于A 实例的初始化:

A a1 = A();

【讨论】:

  • 感谢您的帖子!我一直认为成员在默认初始化时被归零。
  • @Rubens 我的第一个答案是完全错误的。请忘记我所说的一切并阅读编辑后的版本!
猜你喜欢
  • 2018-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-03
相关资源
最近更新 更多