请考虑以下代码:
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; }
};
虽然我明白调用每个前一个类的默认构造函数会初始化值,但是当没有指定任何内容时,我无法看到被调用的内容。
默认情况下不是默认构造函数 - 因此它的句柄?
答案 0 :(得分:6)
使用原始代码
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();
后一个值 - 初始化对象。
... Standardese
C ++11§8.5/ 10 :
“一个对象,其初始化程序是一组空的括号,即()
,应进行值初始化。”
C ++11§8.5。/ 7 :
“要值初始化类型T
的对象意味着:
- 如果T
是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T
的默认构造函数(并且初始化是错误的 - 如果T
没有可访问的默认构造函数,则形成;) - 如果T
是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,那么构造函数被称为。
- 如果T
是数组类型,则每个元素都是值初始化的;
- 否则,对象被零初始化 值初始化的对象被视为构造,因此受本国际标准的规定适用于“构造”对象,“构造函数已完成的对象”等,即使没有为该对象调用构造函数也是如此。初始化“。
值得注意的是,值初始化不是原始C ++ 98的一部分。它是在C ++ 03中由Andrew Koenig(“Koenig查找”成名)引入的,以便处理纯默认初始化的意外影响的一些严重问题。这是()
初始化程序在C ++ 98中为您提供的内容。
答案 1 :(得分:3)
编辑我的原始答案非常错误,因此我对其进行了大量编辑。
如果您想保证在没有值初始化调用(例如
)的情况下A::a
被零初始化
A a1; //
或
B() { .... }
然后你可以给A
一个构造函数并在那里进行初始化:
class A {
public:
A() : a() {} // value initialization of a, means zero initialization here.
int a;
};
否则,在A()
和B
的构造函数中显式调用C
会执行值初始化。
// value initialization of A sub-object leads to zero initialization of A::a
B() : A() {}
这也适用于A
实例的初始化:
A a1 = A();