问题描述
当存在无法初始化其成员的默认构造函数时,std::make_shared 值是否会初始化成员?
我在使用默认 ctor 没有使用 RAII 样式初始化成员的类时偶然发现了这一点,并想知道为什么在其他地方使用它之前没有发出警告。在其他地方它使用 std::make_shared ,我在想为什么它之前一直在工作并且没有发出警告。
#include <iostream>
#include <memory>
class Foo
{
public:
Foo() {} // should initalize mode but does not
int mode;
bool isZero() const
{
if (mode == 0) {
return true;
}
return false;
}
};
int main(int argc,const char* argv[])
{
auto blaa = std::make_shared<Foo>();
if (blaa->isZero()) { // No warning. Always zero initialized because using make_shared?
std::cout << "blaa.mode is zero\n";
}
Foo foo;
if (foo.isZero()) { // warning: 'foo' is used uninitialized in this function - as expected
std::cout << "foo.mode is zero\n";
}
return 0;
}
解决方法
不,make_shared
与构造对象没有任何不同,与以任何其他方式构造对象时发生的情况不同。所有对象在用 C++ 构建时都遵循相同的规则。
您的构造函数无法初始化 mode
。仅仅因为它恰好为零,当它在动态范围内,使用您的编译器和您的操作系统构造时:这并不意味着任何事情,并且这并不能保证您在自动构造对象时会发生什么范围(反之亦然)。
这是未定义的行为。就编译器警告而言:编译器没有义务为未定义的行为发出警告消息。任何此类警告消息都应被视为意外的奖励和惊喜。在您的两个测试用例之一中,您的编译器没有检测到未定义的行为,而事实就是如此。
,使用make_shared,您正在创建一个共享指针,该指针通过指针保留对象的所有权。这意味着有一个内部计数器可以跟踪此所有权。这里有更详细的解释:https://en.cppreference.com/w/cpp/memory/shared_ptr。正如 Sam Varshavchik 在他的回答中提到的那样,make_shared 在初始化方面不会做任何不同的事情。
您可能想知道为什么当您使用 make shared 与仅使用简单的 Foo 声明时 mode 有不同的值,如下图所示: Locals view in Visual Studio
0xcdcdcdcd 位模式表示该内存已由内存分配器(malloc() 或 new)初始化,但尚未由您的软件(对象构造函数或本地代码)初始化。>
0xcccccccc 位模式用于初始化堆栈上数据中的内存。