【问题标题】:C++ singleton with private constructor带有私有构造函数的 C++ 单例
【发布时间】:2013-09-04 12:18:08
【问题描述】:

我需要具有应用程序生命周期、保证创建/销毁和静态访问的单例。

#include <iostream>
#include <cstdlib>

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&);             \
    void operator=(const TypeName&)

#define M() C::sM()
#define M2() C::sM2()

using namespace std;

class C {
  private:
    static C* s;

    ~C() { cout << "~C()" << endl; }
    static C* instance() { 
        if (s==NULL) { s=new C(); }
        cout << "instance()=" << s << endl; return s; 
    }
    static void cleanUp() { delete s; }
    void m() { cout << "m()" << endl; }
    void m2() { cout << "m2()" << endl; }
    DISALLOW_COPY_AND_ASSIGN(C);
  public:
    C() { 
        cout << "C()" << endl; if (s==NULL) { 
            s=this; atexit(&cleanUp); 
            cout << "cleanUp is installed" << endl; 
        } else { 
            cout << "cleanUp is not installed" << endl; 
        } 
    }
    void* operator new(size_t sz) { 
        void* p=NULL; if (s==NULL) { p=new char[sz]; } else { p=s; }
        cout << "new(" << sz << ")=" << p << endl;
        return p;
    }
    void operator delete(void* p, size_t sz) { 
        cout << "delete(" << sz << "," << p << ")" << endl;
        if (p) delete[] static_cast<char*>(p); s=NULL; 
    }
    void static sM() { cout << "sM()" << endl; instance()->m(); }
    void static sM2() { cout << "sM2()" << endl; instance()->m2(); }
};

C* C::s = NULL;

int main() {
    M();
    M2();
    C* p1 = new C();
    C* p2 = new C();
}  

但我不知道如何摆脱 g++ 警告:

test.cpp: In static member function 'static void C::operator delete(void*, size_t)':
test.cpp:22: warning: deleting 'void*' is undefined

如果我写 C* 而不是 void*,析构函数开始在无限循环中调用自己。任何人都可以帮助我在没有警告的情况下获得干净的代码吗?当然是 C++98。

【问题讨论】:

  • 你为什么要超载newdelete
  • 你认为你为什么想要一个单身人士?
  • 因为我只需要调用一次默认构造函数。
  • 当然可以。它用于记录、配置和其他应用程序的单实例对象。
  • 您的 new 运算符正在泄漏内存,因为您从不返回指针。

标签: c++ constructor new-operator delete-operator


【解决方案1】:

我习惯于编写单例(当我真正需要时)的方式是:

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

private:
     Singleton()
     {
     }
};

无需处理重载newdelete

【讨论】:

  • 我认为 OP 使操作员超负荷,因为他想记录对 newdelete 的每次调用。
  • @Snps 将其记录在 con/destructor 中会是一个更好的选择。
  • @Dukeling 同意,内置类型除外。
  • @Dmitry 那应该什么时候调用? _atexit() ?只需注册一个朋友功能或某事。像这样。实例的生命周期管理将取决于 crt0 的实际实现方式(某些实现可能根本不会为单例调用 delete
  • @g-makulik 我已经阅读了 A. Alexandrescu 的“现代 C++ 设计”中关于 C++ 中单例的一章,除了 _atexit 破坏单例的选项之外,我还没有看到过。
【解决方案2】:

new()delete() 都不需要重载。 而且您可能不需要向您的客户分发指针。参考就行了。

单例的构造和销毁将在您的instance() 中完成,如下所示:

static C& instance() {
  static C _instance;
  cout << "instance()" << endl; 
  return _instance; 
}

这保证了构造和销毁,因为C的构造函数在第一个用户调用instance()时被调用,并且只在第一次调用时被调用。 销毁将在您的程序结束时发生。

【讨论】:

  • 它实际上只保证构造(不一定是破坏)AFAIK。
  • 它保证构造和销毁(C++ 编程语言,Stroustrup,2000)。
  • @g_makulik 我很确定静态对象的销毁肯定会在main() 结束后开始,按照与构造相反的顺序。如果他必须在 d'tor 做事,这可能有点晚了。
【解决方案3】:

要删除的类型是char*:

void operator delete(void* p, size_t sz) { 
    cout << "delete(" << sz << "," << p << ")" << endl;
    if (p) delete (char*) p;
}

您的新版本中有一个错误:What's the difference between new char[10] and new char(10)

也许应该是:

p=new char[sz];

然后

delete[] (char*)p ;

----已编辑:

纯粹主义者的删除,牺牲了人们学习的清晰度;P:

delete[] static_cast<char*>(p) ;

(*其实我很欣赏关于演员阵容的说明)

【讨论】:

    【解决方案4】:

    在我看来,如果您仅删除 newdelete 重载,您的代码应该可以工作(如果您想记录每个 con/destruction,我真的不明白这一点,请在构造函数/析构函数),但下面的内容应该可以解决您的错误。

    由于您将char* 分配给p,因此在删除中将其转换为char* 应该可以工作,即delete (char*)p;

    但是,我相信p=new char(sz); 正在制作一个值为sz 的字符,而不是一个大小为sz 的字符数组。对于后者,您可能需要p=new char[sz];,然后是delete[] pdelete[] (char*)p;

    【讨论】:

    • @Dmitry 你不应该用答案中建议的所有修复来编辑你的问题。这会使所有这些答案无效(这可能导致它们被否决或删除),并且如果其中一个修复程序解决了您的问题,它也会使您的问题无效。除了(通常)格式化、缩进和可能给出一个更短的示例来重现您的问题之外,您通常应该对您的代码进行最少的更改甚至不更改,尤其是当您询问错误时。您应该 accept 选择您认为最能回答您的问题的答案。
    • 抱歉,忘记了。那么问题本身就是无效的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 2013-11-15
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    相关资源
    最近更新 更多