关于设计模式:任何人都可以在c ++中为我提供Singleton样本吗?

Can any one provide me a sample of Singleton in c++?

我用下面的方式写一个单体C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class A {
    private:
        static A* m_pA;
        A();
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL)
        m_pA = new A();
    return m_pA;
}

A::~A() {
    FreeInstance()  // Can I write this? are there any potential error?
}

void A::FreeInstance() {
    delete m_pA;
    m_pA = NULL;
}

谢谢!Evan Teran和sep61.myopenid.com的回答是对的,而且非常好!我的方法是错误的,我希望任何人写这样的代码可以避免我的愚蠢的错误。

我的项目中的singleton a有一个智能指针的向量,另一个线程也可以编辑这个向量,所以当应用程序关闭时,即使添加了大量的cmutex,它也总是变得不稳定。多线程错误+单线程错误浪费了我1天。

//——————————————————————————————————————————————————————————————————————————————————————————————————————————————-如果您认为以下示例中存在任何问题,欢迎编辑新的singleton:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class A {
    private:
        static A* m_pA;
        explicit A();
        void A(const A& a);
        void A(A &a);
        const A& operator=(const A& a);
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL){
        static A self;
        m_pA = &self;
    }
    return m_pA;
}

A::~A() {
}


为什么每个人都想返回一个单例作为指针?作为参考返回它似乎更合乎逻辑!

你不应该手动释放一个单件。你怎么知道是谁提到了单身汉?如果您不知道(或不能保证)没有人有引用(在您的情况下,通过指针),那么您就没有业务释放对象。

在函数方法中使用static。这就保证它只被创建和销毁一次。它还为您提供了免费的惰性初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
class S
{
    public:
        static S& getInstance()
        {
            static S    instance;
            return instance;
        }
    private:
        S() {}
        S(S const&);              // Don't Implement.
        void operator=(S const&); // Don't implement
 };

注意,您还需要将构造函数设置为私有的。另外,请确保重写默认的复制构造函数和赋值运算符,这样就不能复制单例(否则它就不是单例)。

还请阅读:

  • https://stackoverflow.com/a/1008289/14065
  • 单人间:怎么用
  • C++单体设计模式

以确保您使用单例计数器的原因正确。

尽管在一般情况下技术上不安全,但请参见:C++函数中静态变量的生存期是多少?

GCC有一个明确的补丁来补偿这一点:http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html


您可以使用这样的静态对象来避免删除它:

1
2
3
4
if(m_pA == 0) {
    static A static_instance;
    m_pA = &static_instance;
}


C++中的单件可以这样写:

1
2
3
4
static A* A::GetInstance() {
    static A sin;
    return &sin;
}

只是不要忘记将复制构造函数和赋值运算符设为私有。


如果你阅读"现代C++设计",你会意识到单体设计比返回静态变量要复杂得多。


经过一段时间对Meyers风格的单例(使用本地静态对象,就像前面的一些答案一样),我完全厌倦了复杂应用程序中的终身管理问题。

我倾向于发现,你最终会在应用程序初始化的早期故意引用"实例"方法,以确保它们是在你想要的时候创建的,然后因为不可预测的(或者至少非常复杂和有点隐藏的)破坏顺序而玩各种游戏。

当然,ymmv,它在某种程度上依赖于单例本身的性质,但是很多关于聪明单例(以及围绕聪明的线程/锁定问题)的胡言乱语在imo中被高估了。


我不认为有任何理由写第行。析构函数方法不是静态的,您的singleton实例不会以这种方式被破坏。我不认为析构函数是必要的,如果需要清理对象,请使用已经创建的静态方法freeinstance()。

除此之外,您的单例创建方式与我的创建方式大致相同。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.

#ifndef SINGLETON_H
#define SINGLETON_H

#include <stdexcept>

template <typename C, typename ...Args>
class singleton
{
private:
  singleton() = default;
  static C* m_instance;

public:
  singleton(const singleton&) = delete;
  singleton& operator=(const singleton&) = delete;
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;

  ~singleton()
  {
    delete m_instance;
    m_instance = nullptr;
  }

  static C& create(Args...args)
  {
    if (m_instance != nullptr)
      {
    delete m_instance;
    m_instance = nullptr;
      }
    m_instance = new C(args...);
    return *m_instance;
  }

  static C& instance()
  {
    if (m_instance == nullptr)
      throw std::logic_error(
       "singleton<>::create(...) must precede singleton<>::instance()");
    return *m_instance;
  }
};

template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;

#endif // SINGLETON_H

只要您能回答这些问题,这个实现就可以:

  • 您知道什么时候创建对象吗(如果您使用静态对象而不是新对象?你有主()吗?

  • 您的singleton是否有任何在创建时可能还没有准备好的依赖项?如果使用静态对象而不是新的,那么此时初始化了哪些库?您的对象在可能需要它们的构造函数中做什么?

  • 什么时候删除?

  • 使用new()更安全,因为您可以控制创建和删除对象的位置和时间。但是您需要显式地删除它,并且系统中可能没有人知道何时删除它。如果有意义的话,可以使用atexit()。

    在方法中使用静态对象意味着不知道何时创建或删除它。您还可以在名称空间中使用全局静态对象,并完全避免getInstance()——它不会增加太多内容。

    如果你确实使用线程,那么你就有大麻烦了。实际上,不可能在C++中创建可用的线程安全单线程,因为:

  • getInstance中的永久锁非常重-每个getInstance()都有一个完整的上下文切换
  • 由于编译器优化和缓存/弱内存模型的原因,双重检查锁失败,很难实现,而且无法测试。我不会在真正的系统中尝试这样做,除非你非常了解你的体系结构,并且希望它不可移植。
  • 这些可以很容易地在Google上搜索,但这里有一个关于弱内存模型的好链接:http://ridiculousfish.com/blog/archives/2007/02/17/barrier。

    一种解决方案是使用锁定,但要求用户缓存从getInctance()获得的指针,并准备好让getInstance()变重。

    另一个解决方案是让用户自己处理线程安全。

    另一种解决方案是使用一个带有简单锁的函数,并在调用new()后用另一个不带锁和检查的函数替换它。这是可行的,但完全实现是复杂的。


    有一个伟大的C++库,ACE,基于模式。有很多关于不同类型模式的文档,所以请看它们的工作:网址:http://www.cs.wustl.edu/~schmidt/ace.html