Tho*_*mas 8 c++ constructor undefined-behavior clang++
请考虑以下代码:
class A {
private:
int a;
public:
A(int a) : a(a) { }
};
class B : public A {
private:
int b;
bool init() {
b = 0;
return true;
}
public:
// init() is a hack to initialize b before A()
// B() : b(0), A(b) {} yields -Wreorder
// B() : A((b = 0)) {} no warning (but this one doesn't work so well with non-pod (pointer) types)
B() : A(init() ? b : 0) {}
};
Run Code Online (Sandbox Code Playgroud)
现在尝试使用clang编译此代码...
$ clang++ test.cpp -fsyntax-only
test.cpp:19:20: warning: field 'b' is uninitialized when used here [-Wuninitialized]
B() : A(init() ? b : 0) {}
^
1 warning generated.
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会不打印任何警告,甚至没有-Wall -Wextra和-pedantic.
这是未定义的行为.根据[class.base.init]:
在非委托构造函数中,初始化按以下顺序进行:
- 首先,仅针对派生类最多的类(1.8)的构造函数,虚拟基类...
- 然后,直接基类按声明顺序初始化出现在base-specifier-list中(无论mem-initializers的顺序如何).
- 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何).
b在初始化A基类时,不会初始化.由于b = 0同样的原因,赋值本身是未定义的行为 - b在调用时尚未初始化.它的默认构造函数仍将在A构造函数之后调用.
如果你想确保b首先初始化,典型的方法是成员基础成语:
struct B_member {
int b;
B_member() : b(0) { }
};
class B : public B_member, public A
{
public:
B() : A(b) // B_member gets initialized first, which initializes b
// then A gets initialized using 'b'. No UB here.
{ };
};
Run Code Online (Sandbox Code Playgroud)
在任何一种情况下,在初始化基类之前调用成员函数都会调用未定义的行为.§12.6.2/ 16:
可以为正在构造的对象调用成员函数(包括虚拟成员函数,10.3).类似地,正在构造的对象可以是运算
typeid符(5.2.8)或dynamic_cast(5.2.7)的操作数.但是,如果在基类的所有mem-initializer完成之前,在ctor-initializer(或直接或间接从ctor-initializer调用的函数)中执行这些操作,则操作 的结果是不确定的.[例如:Run Code Online (Sandbox Code Playgroud)class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined: calls member function // but base A not yet initialized j(f()) { } // well-defined: bases are all initialized };
然而,b对它本身的访问和赋值是很好的,因为它具有空的初始化,并且一旦为它获取存储(它在构造函数调用开始之前就已经发生),它的生命周期就开始了.于是
class B : public A {
private:
int b;
public:
B() : A(b=0) {}
};
Run Code Online (Sandbox Code Playgroud)
定义明确.
| 归档时间: |
|
| 查看次数: |
321 次 |
| 最近记录: |