ChatGPT解决这个技术问题 Extra ChatGPT

C++ 单例设计模式

最近,我遇到了 C++ 单例设计模式的实现/实现。它看起来像这样(我从现实生活的例子中采用了它):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

从这个声明中,我可以推断出实例字段是在堆上启动的。这意味着有内存分配。对我来说完全不清楚的是何时释放内存?还是有错误和内存泄漏?执行起来好像有问题。

我的主要问题是,如何以正确的方式实施它?

您将在本文中找到关于如何实现单例以及 C++ 中的线程安全的精彩讨论。 aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf
@sbi - 只有西斯绝对交易。如果没有 Singletons,绝大多数问题都可以解决吗?绝对地。单身人士会导致自己的问题吗?是的。但是,我不能老实说它们很糟糕,因为设计就是要考虑权衡和理解方法的细微差别。
@derekerdmann:我并不是说你永远不需要全局变量(当你需要一个时,单例有时会更好)。我说的是应该尽可能少地使用它们。将 Singleton 美化为一种有价值的设计模式给人的印象是使用它很好,而不是认为它是一种 hack,使代码难以理解、难以维护和难以测试。这就是我发表评论的原因。到目前为止,您所说的没有一个与此相矛盾。
@sbi:你说的是“不要使用它们”。不是你后来改为更合理的“它们应该尽可能少地使用”——你肯定看到了不同之处。

S
ShadowRanger

2008 年,我提供了单例设计模式的 C++98 实现,它是惰性求值、保证破坏、非技术线程安全的:
Can any one provide me a sample of Singleton in c++?

这是单例设计模式的更新 C++11 实现,它是惰性求值、正确销毁和 thread-safe

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are inaccessible(especially from outside), otherwise, you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

请参阅这篇关于何时使用单例的文章:(不经常)
Singleton: How should it be used

请参阅这两篇关于初始化顺序以及如何应对的文章:
Static variables initialisation order
Finding C++ static initialization order problems

请参阅这篇描述生命周期的文章:
What is the lifetime of a static variable in a C++ function?

请参阅这篇讨论对单例的一些线程影响的文章:
Singleton instance declared as static variable of GetInstance method, is it thread-safe?

请参阅这篇解释为什么双重检查锁定在 C++ 上不起作用的文章:
What are all the common undefined behaviours that a C++ programmer should know about?
Dr Dobbs: C++ and The Perils of Double-Checked Locking: Part I


好答案。但应该注意这不是线程安全的stackoverflow.com/questions/1661529/…
@zourtney:很多人没有意识到你刚刚做了什么:)
@MaximYegorushkin:当它被破坏时,定义非常明确(没有歧义)。请参阅:stackoverflow.com/questions/246564/…
What irks me most though is the run-time check of the hidden boolean in getInstance() 这是对实现技术的假设。不需要假设它还活着。请参阅 stackoverflow.com/a/335746/14065 您可以强制一种情况使其始终处于活动状态(开销低于 Schwarz counter)。全局变量在初始化顺序(跨编译单元)方面存在更多问题,因为您不强制执行顺序。该模型的优点是 1) 延迟初始化。 2)执行命令的能力(施瓦茨有帮助,但更丑陋)。是的get_instance()更丑陋。
C
Cătălin Pitiș

您可以避免内存分配。有很多变体,在多线程环境中都有问题。

我更喜欢这种实现方式(实际上,我更喜欢这种说法并不正确,因为我尽可能避免使用单例):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

它没有动态内存分配。


在某些情况下,这种延迟初始化并不是要遵循的理想模式。一个例子是,如果单例的构造函数从堆中分配内存,并且您希望该分配是可预测的,例如在嵌入式系统或其他严格控制的环境中。当单例模式是最好的模式时,我更喜欢将实例创建为类的静态成员。
对于许多较大的程序,尤其是那些具有动态库的程序。由于库卸载时的破坏顺序问题,任何非原始的全局或静态对象都可能在许多平台上导致程序退出时出现段错误/崩溃。这是许多编码约定(包括 Google 的)禁止使用重要的静态和全局对象的原因之一。
似乎这种实现中的静态实例具有内部链接,并且在不同的翻译单元中将具有唯一且独立的副本,这将导致混乱和错误的行为。但是我看到了很多这样的实现,我错过了什么吗?
是什么阻止用户将其分配给幕后编译器使用自己的复制构造函数的多个对象?
@FaceBro 这里有两个关键字 static 实例,但从链接的角度来看,它们都没有问题。 static 的第一次出现是关于静态成员函数的,它与链接无关。 static 的第二次出现是关于 INSTANCE 的存储时长。该对象将在程序运行期间一直存在于内存中,您不能在 TU 之外通过名称访问它并不重要,因为您可以通过具有外部链接的成员函数 instance 访问它。
R
Reed Copsey

作为一个单例,你通常不希望它被破坏。

当程序终止时,它将被拆除并释放,这是单例的正常行为。如果您希望能够显式清理它,可以很容易地向该类添加一个静态方法,该方法允许您将其恢复到干净状态,并在下次使用时重新分配它,但这超出了 a 的范围“经典”单身人士。


它不再是内存泄漏,而是全局变量的简单声明。
直截了当地说……“内存泄漏”对单例的担忧完全无关紧要。如果您有解构顺序很重要的有状态资源,那么单例可能很危险;但是所有的内存在程序终止时都被操作系统干净地重新获得了……在 99.9% 的情况下,这完全是学术点。如果您想来回争论什么是“内存泄漏”和不是“内存泄漏”的语法,那很好,但要意识到这会分散实际设计决策的注意力。
@jkerian:C++ 上下文中的内存泄漏和破坏实际上与内存泄漏无关。真的是关于资源控制。如果您泄漏内存,则不会调用析构函数,因此与对象关联的任何资源都不会正确释放。内存只是我们在教授编程时使用的简单示例,但还有更复杂的资源。
@Martin 我完全同意你的看法。即使唯一的资源是内存,如果您必须遍历泄漏列表,过滤掉“无关紧要”的泄漏,您仍然会在试图在程序中找到真正的泄漏时遇到麻烦。最好将这些都清理干净,这样任何报告泄漏的工具都只会报告有问题的事情。
隐约值得考虑的是,存在 C++ 实现(甚至可能是托管的),其中“操作系统”在程序退出时不会恢复所有资源,但确实有一些“再次运行程序”的概念,这给了你一个全新的集合全局变量和静态局部变量。在这样的系统上,未释放的单例在任何合理的定义下都是真正的泄漏:如果您的程序运行足够多的时间,它将关闭系统。您是否关心此类系统的可移植性是另一回事——只要您不编写库,您几乎肯定不会。
C
Community

@Loki Astari's answer 非常好。

但是,有时对于多个静态对象,您需要能够保证在使用单例的所有静态对象不再需要它之前,不会破坏单例。

在这种情况下,std::shared_ptr 可用于为所有用户保持 singleton 活动,即使在程序结束时调用静态析构函数:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

你能用 = delete 解释这两行吗,作为一个 C# 程序员,这个语法对我来说有点奇怪。或者您能否提供一个链接,我可以在其中阅读有关此确切语法的信息?
@MohammedNoureldin 默认情况下 C++ 将自动生成函数来复制对象。如果您想防止您的对象被复制,您可以“删除”这些功能。所以 = delete 告诉编译器不要生成它们。
这是否实现了未完成的常见问题 isocpp.org/wiki/faq/ctors#nifty-counter-idiom 中提到的 Nifty Counter 模式?
@RexYuan 是的,我相信是的。它将确保您的 singleton 对象 在需要它的最后一个组件首先被销毁之前不会被销毁。但是您需要确保单例本身在其销毁期间不需要任何全局静态对象,并且只要您没有做任何愚蠢的事情,例如在 {1 之外保留原始指针或对其目标对象的原始引用}。
J
James Hopkin

另一种非分配替代方案:根据需要创建一个单例,例如 C 类:

singleton<C>()

使用

template <class X>
X& singleton()
{
    static X x;
    return x;
}

这和 Cătălin 的答案在当前 C++ 中都不是自动线程安全的,但会在 C++0x 中。


目前在 gcc 下它是线程安全的(并且已经有一段时间了)。
这种设计的问题在于,如果跨多个库使用。每个库都有该库使用的单例的自己的副本。所以它不再是单例了。
Y
Yuriy

我没有在答案中找到 CRTP 实现,所以这里是:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

要使用继承您的类,例如:class Test : public Singleton<Test>


在我将默认构造函数设置为受保护和“=默认值”之前,无法使其与 C++17 一起使用。
T
Tony Bai

我们最近在我的 EECS 课上讨论了这个话题。如果您想详细查看讲义,请访问 http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf。这些笔记(以及我在此答案中给出的引文)由我的教授 David Kieras 创建。

我知道有两种方法可以正确创建 Singleton 类。

第一种方式:

以类似于您在示例中的方式实现它。至于销毁,“单例通常会在程序运行期间持续存在;大多数操作系统会在程序终止时恢复内存和大多数其他资源,因此有理由不担心这一点。”

但是,最好在程序终止时进行清理。因此,您可以使用辅助静态 SingletonDestructor 类来执行此操作,并将其声明为 Singleton 中的朋友。

class Singleton {
public:
  static Singleton* get_instance();
  
  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

// somewhere in code (Singleton.cpp is probably the best place) 
// create a global static Singleton_destroyer object
Singleton_destoyer the_destroyer;

Singleton_destroyer 将在程序启动时创建,并且“当程序终止时,所有全局/静态对象都被运行时库关闭代码(由链接器插入)销毁,因此 the_destroyer 将被销毁;它的析构函数将删除 Singleton,运行它的破坏者。”

第二种方式

这称为 Meyers Singleton,由 C++ 向导 Scott Meyers 创建。只需以不同的方式定义 get_instance() 即可。现在您还可以摆脱指针成员变量。

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

这很简洁,因为返回的值是引用的,您可以使用 . 语法而不是 -> 来访问成员变量。

“编译器会自动构建第一次通过声明创建's'的代码,而不是此后,然后在程序终止时删除静态对象。”

还要注意,使用 Meyers Singleton,“如果对象在终止时相互依赖,您可能会遇到非常困难的情况——Singleton 相对于其他对象什么时候消失?但对于简单的应用程序,这很好用。”


L
LastBlow

这是一个简单的实现。

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

每次只创建一个对象,并且每次都返回此对象引用。

SingletonClass instance created!
00915CB8
00915CB8

这里 00915CB8 是单例对象的内存位置,在程序运行期间相同,但(通常!)每次程序运行时都不同。

注意这不是线程安全的。你必须确保线程安全。


H
HugoTeixeira

已接受答案中的解决方案有一个明显的缺点 - 在控件离开 main() 函数后调用单例的析构函数。当在 main 中分配一些依赖对象时,确实可能存在问题。

我在尝试在 Qt 应用程序中引入 Singleton 时遇到了这个问题。我决定,我所有的设置对话框都必须是单例,并采用了上面的模式。不幸的是,Qt 的主类 QApplication 是在 main 函数的堆栈上分配的,当没有可用的应用程序对象时,Qt 禁止创建/销毁对话框。

这就是为什么我更喜欢堆分配的单例。我为所有单例提供了明确的 init()term() 方法并在 main 中调用它们。因此,我可以完全控制单例创建/销毁的顺序,并且我保证无论是否有人调用 getInstance(),都会创建单例。


如果您指的是当前接受的答案,那么您的第一个陈述是错误的。在销毁所有静态存储持续时间对象之前,不会调用析构函数。
R
Red.Wave

有人提到 std::call_oncestd::once_flag 吗?大多数其他方法——包括双重检查锁定——都被破坏了。

单例模式实现中的一个主要问题是安全初始化。唯一安全的方法是使用同步屏障保护初始化序列。但这些障碍本身需要安全启动。 std::once_flag 是确保安全初始化的机制。


N
Nicolas Holthaus

如果要在堆中分配对象,为什么不使用唯一指针。由于我们使用的是唯一指针,因此内存也将被释放。

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

在 c++11 中已弃用。建议使用 unique_ptr。 cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
这不是线程安全的。最好将 m_s 设为 getInstance() 的本地 static 并立即初始化它而无需测试。
比较 m_s.get()nullptr 会比比较 0 更好。
M
Milind Deore

C++11 线程安全实现:

 #include <iostream>
 #include <thread>


 class Singleton
 {
     private:
         static Singleton * _instance;
         static std::mutex mutex_;

     protected:
         Singleton(const std::string value): value_(value)
         {
         }
         ~Singleton() {}
         std::string value_;

     public:
         /**
          * Singletons should not be cloneable.
          */
         Singleton(Singleton &other) = delete;
         /**
          * Singletons should not be assignable.
          */
         void operator=(const Singleton &) = delete;

         //static Singleton *GetInstance(const std::string& value);
         static Singleton *GetInstance(const std::string& value)
         {
             if (_instance == nullptr)
             {
                 std::lock_guard<std::mutex> lock(mutex_);
                 if (_instance == nullptr)
                 {
                     _instance = new Singleton(value);
                 }
             }
             return _instance;
         }

         std::string value() const{
             return value_;
         }
 };

 /**
  * Static methods should be defined outside the class.
  */
 Singleton* Singleton::_instance = nullptr;
 std::mutex Singleton::mutex_;


 void ThreadFoo(){
     std::this_thread::sleep_for(std::chrono::milliseconds(10));
     Singleton* singleton = Singleton::GetInstance("FOO");
     std::cout << singleton->value() << "\n";
 }

 void ThreadBar(){
     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
     Singleton* singleton = Singleton::GetInstance("BAR");
     std::cout << singleton->value() << "\n";
 }

 int main()
 {
     std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
                 "If you see different values, then 2 singletons were created (booo!!)\n\n" <<
                 "RESULT:\n";
     std::thread t1(ThreadFoo);
     std::thread t2(ThreadBar);
     t1.join();
     t2.join();
     std::cout << "Complete!" << std::endl;

     return 0;
 }

如果您的 C++ 编译器符合标准,则简单的局部静态函数变量是线程安全的。不需要所有的互斥魔法。这确实意味着静态初始化程序可能会导致死锁,因此需要小心,但您在此处建议的代码也是如此。
T
T.E.D.

它确实可能是从堆中分配的,但是没有来源就无法知道。

典型的实现(取自我已经在 emacs 中的一些代码)是:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

...然后依靠超出范围的程序进行清理。

如果您在必须手动完成清理的平台上工作,我可能会添加手动清理例程。

这样做的另一个问题是它不是线程安全的。在多线程环境中,两个线程可以在任何一个有机会分配新实例之前通过“if”(所以两者都会)。如果您仍然依赖程序终止来清理,这仍然不是什么大不了的事。


您可以推断,因为您可以看到实例变量是指向类实例的指针。
无需动态分配单例。事实上,这是一个坏主意,因为没有办法使用上述设计自动取消分配。让它超出范围是不调用析构函数,只是懒惰。
您可以使用 atexit 函数自动解除分配。这就是我们所做的(不是说这是个好主意)
d
dan-man

除了此处的其他讨论之外,可能值得注意的是,您可以拥有全局性,而无需将使用限制在一个实例中。例如,考虑引用计数的情况......

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

现在,您可以在函数(例如 main)中的某个位置执行以下操作:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

refs 不需要将指针存储回它们各自的 Store,因为该信息是在编译时提供的。您也不必担心 Store 的生命周期,因为编译器要求它是全局的。如果确实只有一个 Store 实例,那么这种方法没有开销;对于不止一个实例,编译器在代码生成方面是否聪明。如有必要,甚至可以将 ItemRef 类设为 Storefriend(您可以有模板化的朋友!)。

如果 Store 本身是一个模板类,那么事情就会变得更加混乱,但仍然可以使用此方法,也许通过实现一个具有以下签名的帮助类:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

用户现在可以为每个全局 Store 实例创建一个 StoreWrapper 类型(和全局实例),并始终通过其包装器实例访问存储(因此忘记了使用 Store 所需的模板参数的血腥细节) .


r
ricab

这是使用 CRTPmockable singleton。它依赖 a little helper 在任何时候(最多)强制执行单个对象。要在程序执行上强制执行单个对象,请删除重置(我们发现这对测试很有用)。

ConcreteSinleton 可以这样实现:

class ConcreteSingleton : public Singleton<ConcreteSingleton>
{
public:
  ConcreteSingleton(const Singleton<ConcreteSingleton>::PrivatePass&)
      : Singleton<StandardPaths>::Singleton{pass}
  {}
  
  // ... concrete interface
  int f() const {return 42;}

};

然后与

ConcreteSingleton::instance().f();

b
baris.aydinoz

这是关于对象生命周期管理的。假设您的软件中有多个单例。他们依赖于 Logger 单例。在应用程序销毁期间,假设另一个单例对象使用 Logger 记录其销毁步骤。你必须保证 Logger 应该最后被清理。因此,还请查看这篇论文:http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


K
Kevin Marshall

我的实现类似于 Galik 的。不同之处在于我的实现允许共享指针清理分配的内存,而不是保留内存直到应用程序退出并清理静态指针。

#pragma once

#include <memory>

template<typename T>
class Singleton
{
private:
  static std::weak_ptr<T> _singleton;
public:
  static std::shared_ptr<T> singleton()
  {
    std::shared_ptr<T> singleton = _singleton.lock();
    if (!singleton) 
    {
      singleton.reset(new T());
      _singleton = singleton;
    }

    return singleton;
  }
};

template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;

A
Ali Sajjad

您的代码是正确的,只是您没有在类之外声明实例指针。静态变量的内部类声明在 C++ 中不被视为声明,但是在 C# 或 Java 等其他语言中允许这样做。

class Singleton
{
   public:
       static Singleton* getInstance( );
   private:
       Singleton( );
       static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global

你要知道,Singleton 实例不需要我们手动删除。我们在整个程序中都需要它的一个对象,所以在程序执行结束时,它会被自动释放。


u
uuu777

这是我对如何做正确的单例(和其他重要的静态对象)的看法:https://github.com/alex4747-pub/proper_singleton

概括:

使用静态初始化列表在正确的时间实例化单例:在进入 main 之后和启用多线程之前添加小的改进以使其对单元测试友好。


A
Andrushenko Alexander

我想在这里展示另一个 C++ 中的单例示例。使用模板编程是有意义的。此外,从不可复制且不可移动的类派生单例类是有意义的。下面是它在代码中的样子:

#include<iostream>
#include<string>

class DoNotCopy
{
protected:
    DoNotCopy(void) = default;
    DoNotCopy(const DoNotCopy&) = delete;
    DoNotCopy& operator=(const DoNotCopy&) = delete;
};

class DoNotMove
{
protected:
    DoNotMove(void) = default;
    DoNotMove(DoNotMove&&) = delete;
    DoNotMove& operator=(DoNotMove&&) = delete;
};

class DoNotCopyMove : public DoNotCopy,
    public DoNotMove
{
protected:
    DoNotCopyMove(void) = default;
};

template<class T>
class Singleton : public DoNotCopyMove
{
public:
    static T& Instance(void)
    {
        static T instance;
        return instance;
    }

protected:
    Singleton(void) = default;
};

class Logger final: public Singleton<Logger>
{
public:
    void log(const std::string& str) { std::cout << str << std::endl; }
};



int main()
{
    Logger::Instance().log("xx");
}

拆分为 NotCopyable 和 NotMovable 类允许您更具体地定义单例(有时您想要移动单个实例)。


S
Sarath Govind

它将类的实例化限制为一个对象。当需要一个对象来协调整个系统的动作时,这很有用

class Singleton {
private:
    int data;
    static Singleton* instance;
    Singleton();
public:
    static Singleton* getInstance();
};
Singleton* Singleton::instance = 0;
Singleton::Singleton()
{
    this->data = 0;
    cout << "constructor called.." << endl;
}

 

Singleton* Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
        return instance;
    }
}
int main() {
    Singleton *s = s->getInstance();
    Singleton *s1 =s1->getInstance();
    }

这有两个问题。 (1) getInstance() 不是线程安全的:如果多个线程同时调用 getInstance() 则可以构造多个 Singleton 实例,这意味着您有内存泄漏。 (2) 如果实例已经存在, getInstance() 没有返回值,所以你有未定义的行为。
s
sdfsdaf

上面链接的论文描述了双重检查锁定的缺点是编译器可能会在调用对象的构造函数之前为对象分配内存并设置指向分配内存地址的指针。在 c++ 中很容易使用分配器手动分配内存,然后使用构造调用来初始化内存。使用这种方法,双重检查锁定工作得很好。


不幸的是没有。一些最优秀的 C++ 开发人员已经深入讨论了这一点。双重检查锁定在 C++03 中被破坏。
A
Ali Khazaee

简单的单例类,这一定是你的头类文件

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

像这样访问你的单例:

sSingletonClass->Relocate(1, 2, 5);

G
Gank
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

例子:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);