C+的堆/动态与静态内存分配+;单例类实例 我的具体问题是,在实现C++时,下面两个代码在性能、侧重点或某些方面存在实质性差异: 类单例 { // ... 静态单例&getInstance() { //在堆上分配 静态单例*pInstance=新单例(); 返回*pInstance; } // ... };

C+的堆/动态与静态内存分配+;单例类实例 我的具体问题是,在实现C++时,下面两个代码在性能、侧重点或某些方面存在实质性差异: 类单例 { // ... 静态单例&getInstance() { //在堆上分配 静态单例*pInstance=新单例(); 返回*pInstance; } // ... };,c++,memory,static,singleton,dynamic-memory-allocation,C++,Memory,Static,Singleton,Dynamic Memory Allocation,这是: 类单例 { // ... 静态单例&getInstance() { //使用静态变量 静态单例实例; 返回实例; } // ... }; (请注意,在基于堆的实现中取消引用不应影响性能,因为AFAIK没有为取消引用生成额外的机器代码。这似乎只是区分指针的语法问题。) 更新: 我有一些有趣的答案和评论,我试着在这里总结一下。(建议有兴趣的人阅读详细答案。)‎: 在使用静态局部变量的singleton中,类析构函数在进程终止时自动调用,而在动态分配情况下,您必须在某个时候以某种方式管理

这是:

类单例
{
// ...
静态单例&getInstance()
{
//使用静态变量
静态单例实例;
返回实例;
}
// ...
};

(请注意,在基于堆的实现中取消引用不应影响性能,因为AFAIK没有为取消引用生成额外的机器代码。这似乎只是区分指针的语法问题。)

更新:

我有一些有趣的答案和评论,我试着在这里总结一下。(建议有兴趣的人阅读详细答案。)‎:

  • 在使用静态局部变量的singleton中,类析构函数在进程终止时自动调用,而在动态分配情况下,您必须在某个时候以某种方式管理对象销毁,例如使用智能指针:
static singleton&getInstance(){
静态std::auto_ptr实例(新的singleton());
return*instance.get();
}
  • 使用动态分配的singleton比静态singleton变量“更懒”,在后一种情况下,singleton对象所需的内存(始终?)在进程启动时保留(作为加载程序所需的整个内存的一部分),并且只有singleton构造函数的调用延迟到
    getInstance()
    通话时间。当
    sizeof(singleton)
    较大时,这可能很重要

  • 在C++11中,两者都是线程安全的。但C++的早期版本是特定于实现的。

  • 动态分配案例使用一级间接寻址来访问单例对象,而在静态单例对象案例中,对象的直接地址在编译时确定并硬编码



注意:根据@TonyD的回答,我已经更正了我在原始帖子中使用的术语。

主要区别在于,使用本地

静态
关闭程序时,对象将被销毁,而堆分配的对象将被放弃而不被销毁


<>注意,在C++中,如果在函数内声明静态变量,则在第一次进入作用域时将初始化,而不是在程序开始时(如它发生而不是全局静态持续时间变量)。 总的来说,这些年来,我从使用惰性初始化切换到显式控制初始化,因为程序启动和关闭是一个微妙的阶段,很难调试。如果你的类没有做任何复杂的事情,只是不能失败(例如,它只是一个注册表),那么即使是延迟初始化也可以。。。否则,控制局面将为你省去很多麻烦

在进入
main
的第一条指令之前或在执行
main
的最后一条指令之后崩溃的程序更难调试

使用单例的惰性构造的另一个问题是,如果代码是多线程的,则必须注意并发线程同时初始化单例的风险。在单线程上下文中进行初始化和关闭更简单

从C++11开始,多线程代码中函数级静态实例的初始化过程中可能出现的争用问题已经得到解决,当时该语言添加了官方的多线程支持:在正常情况下,编译器会自动添加适当的同步保护,因此在C++11或更高版本的代码中,这不是一个问题。但是,如果函数
a
中的静态初始化调用函数
b
,反之亦然,如果两个函数第一次由不同的线程同时调用,则可能会出现死锁的风险(仅当编译器对所有静态使用单个互斥锁时,这不是问题)。还请注意,不允许从静态对象的初始化代码中递归调用包含静态对象的函数。

  • 新版本显然需要在运行时分配内存,而非指针版本在编译时分配内存(但两者都需要进行相同的构造)

  • new
    版本不会在程序终止时调用对象的析构函数,但非
    new
    版本会:您可以使用智能指针来更正此问题

    • 您需要注意的是,某些静态/命名空间范围对象的析构函数在其静态本地实例的析构函数运行后不会调用您的singleton。。。如果你关心这个问题,你也许应该多读一些关于单例生命周期和管理它们的方法的书。Andrei Alexandrescu的现代C++设计有一个非常可读的处理。
  • 在C++03下,它的实现定义了两者是否是线程安全的。(我相信GCC倾向于这样,而Visual Studio倾向于不这样-欢迎评论以确认/更正。)

  • 在C++11下,它是安全的:6.7.4“如果控件在初始化变量时并发输入声明,则并发执行应等待初始化完成。”(sans递归)

讨论重新编译时与运行时分配和初始化

从您编写总结和一些评论的方式来看,我怀疑您没有完全理解静态变量的分配和初始化的一个微妙方面

假设你的程序有3个本地