在头文件中分配一个新对象

Allocating a new object in a header file

本文关键字:一个 新对象 对象 文件 分配      更新时间:2023-10-16

我在调试空指针问题时遇到了这种模式:

#include <iostream>                                   
class Foo {                                           
  public:                                             
    Foo() {                                           
      std::cout << "In Foo constructor." << std::endl;
    };                                                
};                                                    
static const Foo* DEFAULT_FOO(new Foo);               

我突然想到这可能是个坏主意,把这个常量初始化到一个cpp文件而不是头文件中解决了我的问题。

我的问题是,这里到底发生了什么,为什么它首先被允许?

在我的理解中,这在调用main函数之前在堆上分配了一个对象,并且从构造函数内部回溯显示了一堆看起来很可怕的内部东西。更糟糕的是,当有多个编译单元时,每个编译单元都有自己的这个头文件的副本,因此也有自己的DEFAULT_FOO版本;在main的任何输出之前,我在程序输出中看到了许多"In Foo constructor"的副本。

不知何故,在我的代码库中,一个对象的DEFAULT_FOO指针的副本显示为空,并将DEFAULT_FOO的初始化移动到cpp文件中解决了这个问题。到底发生了什么事?

你的倒数第二段很好地概括了为什么这是一个坏主意。

你的最后一段,我不知道。由于指针值本身不是常量,因此它可能会被其他代码中的任何内容弄乱。

你错过了一件事——对象也被泄露了。虽然这不是什么大问题,因为它只需要在程序关闭时删除,如果任何重要的东西需要在析构函数中调用,它将被跳过(除非有外部代码删除指针,这更混乱)。

因此,你应该把它包装在一个智能指针中,或者只是让它成为一个常规对象,而不是动态分配的,或者检查单例模式

你是对的,这段代码不好。首先,您不应该在头文件中声明静态变量——这没有任何意义,因为头文件包含在每个cpp文件中!所以你最终得到的是不同DEFAULT_FOO的多个副本!(由于每个CPP文件都将其视为静态的,因此它正在创建自己的局部符号)。这就是为什么你没有看到任何链接错误。

所以在头文件中声明变量(使用extern),在cpp文件中定义它们,保持冷静,继续学习

当编译单元#include是文件时,它基本上只是将头文件中的文本插入到编译单元中。因此,在原始版本中,每个编译单元将包含以下行:

static const Foo* DEFAULT_FOO(new Foo);

因此每个都将分配/构造一个Foo对象。

将这一行移动到cpp文件中意味着只有一个编译单元会实际创建该对象。