我在调试空指针问题时在代码库中遇到了这种模式:
#include <iostream>
class Foo {
public:
Foo() {
std::cout << "In Foo constructor." << std::endl;
};
};
static const Foo* DEFAULT_FOO(new Foo);
我想到这可能是一个坏主意,将这个常量初始化移动到cpp文件而不是标题修复了我的问题。
我的问题是,这里到底发生了什么,为什么首先允许这样做?
据我了解,这会在调用main函数之前在堆上分配一个对象,并且该构造函数内部的回溯显示了一堆看起来很可怕的内部东西。更糟糕的是,当有多个编译单元时,每个编译单元都会获得自己的头部副本,因此它自己的版本为DEFAULT_FOO;我看到很多副本&#34; In Foo构造函数&#34;在main输出的任何输出之前的程序输出中。
在我的代码库中,DEFAULT_FOO指针的一个对象副本出现了null,并且将DEFAULT_FOO的初始化移动到cpp文件修复了这个问题。是什么给了什么?
答案 0 :(得分:1)
你的倒数第二段是现场,并且很清楚为什么这是一个坏主意。
你的最后一段,我不知道。由于指针值本身不是常量,因此可能会被其余代码中的任何内容弄乱。
你错过了一件事 - 物体也被泄露了。虽然这不是什么大问题,因为它只需要在程序关闭时删除,如果需要在析构函数中调用任何重要的东西,它将被跳过(除非有外部代码删除指针,这甚至更麻烦。)
因此,您应该将其包装在智能指针中,或者只是使其成为常规对象而不是动态分配,或者检查单例模式。
答案 1 :(得分:0)
当编译单元#include
是一个文件时,它基本上只是将标题中的文本插入到编译单元中。那么在原始版本中,每个编译单元都将包含以下行:
static const Foo* DEFAULT_FOO(new Foo);
因此每个人都会分配/构建一个Foo
对象。
将该行移动到cpp文件意味着只有一个编译单元实际创建该对象。
答案 2 :(得分:0)
这是错误的代码。首先,你不应该在头文件中声明静态变量 - 它没有任何意义,因为标头包含在每个cpp文件中!所以你最终得到的是不同DEFAULT_FOO的多个副本! (因为每个cpp文件都将其视为静态,因此它创建了自己的本地符号)。这就是您没有看到任何链接错误的原因。
因此,在头文件中声明变量(使用extern),在cpp文件中定义它们,保持冷静并继续学习。