c ++ singleton implementation:pimpl成语单身,优点和缺点

时间:2010-10-15 08:33:14

标签: c++ oop singleton

在c ++中实现单例时,我看到了两种存储实现数据的方法:

(A)将所有实施数据放在私有部分并像往常一样实施类 (B)“用于单身人士的pimpl习语”:通过将实现数据放置在'Impl'结构中来隐藏实现数据,该结构可以在实现文件中定义。专用部分仅包含对实现结构的引用。

这是一个概念代码,用于阐明(A)和(B)实施选项的含义:

(A)SingletonClassMembers.hpp:


// a lot of includes required by private section
#include "HelperClass1.hpp"
#include "HelperClass2.hpp"
// some includes required by public section
// ...
class SingletonClassMembers {
public:
    static SingletonClassMembers& getInstance();
    // public methods
private:
   SingletonClassMembers ();
   ~SingletonClassMembers();
   SingletonClassMembers (const SingletonClassMembers&); //not implemented
   SingletonClassMembers& operator=(const SingletonClassMembers&); //not implemented
   HelperClass1 mMember1;
   HelperClass2 mMember2; //and so on
(A)SingletonClassMembers.cpp:

#include "SingletonClassMembers.hpp"
SingletonClassMembers& getInstance() { 
    static SingletonClassMembers sImpl; 
    return sImpl; 
}
(B)SingletonHiddenImpl.hpp:

// some includes required by public section
// ...
class SingletonHiddenImpl {
public:
    static SingletonHiddenImpl& getInstance();
    // public methods
private:
   SingletonHiddenImpl ();
   ~SingletonHiddenImpl ();
   SingletonHiddenImpl (const SingletonHiddenImpl&); //not implemented
   SingletonHiddenImpl& operator=(const SingletonHiddenImpl&); //not implemented
   struct Impl;
   Impl& mImpl;
};
(B)SingletonHiddenImpl.cpp:

#include "SingletonHiddenImpl.hpp"
#include "HelperClass1.hpp"
#include "HelperClass2.hpp"

struct SingletonHiddenImpl::Impl { HelperClass1 member1; HelperClass2 member2; }; static inline SingletonHiddenImpl::Impl& getImpl () { static Impl sImpl; return sImpl; } SingletonHiddenImpl::SingletonHiddenImpl () : mImpl (getImpl()) { }

因此,使用(B)方法,您可以更好地隐藏实现细节(与普通类的pimpl习惯用法不同),没有性能损失。我无法想象(A)方法更合适的条件

问题是,将实施数据存储为班级成员(A)有什么好处?

谢谢

3 个答案:

答案 0 :(得分:1)

使用案例A有以下好处:

  1. 您减少了类SingletonClassMembers和SingletonHiddenImpl之间的依赖关系。
  2. 如果尝试通过依赖注入避免对(1)的限制,则不需要在类SingletonClassMembers中创建配置器模式
  3. 这种情况很弱,但无论如何:维护单一类
  4. 很简单
  5. 在多线程环境中,您需要同时支持类同步机制,而在单个类中只需要单个锁。

答案 1 :(得分:1)

由于您只有一个单例实例,您实际上可以将助手移动到实现类中作为“静态”,而不要求它们在标题内是私有的。当然你不想初始化它们,直到你开始你的类,所以你会使用某种智能指针,可能是auto_ptr here或boost :: scoped_ptr,或指针有boost :: once初始化(更多线程安全) )在你的单身人士的析构函数中删除。

您可以将此模型称为C,并且在完全隐藏您的实现时可能具有两全其美的效果。

与任何单身人士的情况一样,你需要格外小心,不要乱扔你的构造函数。

答案 2 :(得分:1)

在考虑pimpl的效率时,不是导致开销的堆,而是间接(通过委托完成)。这个委托通常没有优化(至少在我考虑这个时没有;-)),所以除了创建impl的启动(1次)惩罚之外没有大的收益。 (顺便说一句,我在你的例子中没有看到任何委托功能)

所以我认为在正常班级或单身人士中使用pimpl并没有太大区别。我认为在这两种情况下,对于界面有限且实现繁重的类使用pimpl,这是有道理的。