为什么这个单例类代码工作正常?

时间:2013-10-31 03:30:24

标签: c++ singleton

我对此代码有疑问>任何讨论都会对理解这些事情非常有帮助:

class Singleton
{
private:
    static Singleton *single;
    Singleton() {}
    ~Singleton() {}
public:
        static Singleton* getInstance()
        {
                if (!single)
                        single = new Singleton();
                return single;
        }
        void method()
        {
                cout << "Method of the singleton class" << endl;
        }
        static void destroy()
        {
                delete single;
                single = NULL;
        }
};


Singleton* Singleton::single = NULL;

int main()
{
    Singleton *sc2;
            sc2 = Singleton::getInstance();  // sc2 is pointing to some memory location
    {
        Singleton *sc1 = Singleton::getInstance(); // sc1 and sc2 pointing to same memory location
        sc1->method();
        Singleton::destroy();   // memory location deleted.
        cout << sc1;
    }

    sc2->method();   // ??? how this is working fine??

    return 0;
}

在这个块中,我们正在删除“Singleton :: destroy()”;

中的内存
{
Singleton *sc1 = Singleton::getInstance();
    sc1->method();
Singleton::destroy();
cout << sc1;
}

然后如何调用“sc2-&gt; method();”是成功的吗?

Devesh

2 个答案:

答案 0 :(得分:2)

为什么会有效?

因为您正在调用的函数是静态定义的,所以不需要this指针来调用它,函数本身也不使用this指针。因此,该功能没有理由崩溃。

但是,正如您所提到的,该课程被滥用了。

仍允许使用destroy()函数的一种更安全的方法是对Singleton指针使用shared_ptr&lt;&gt;()。这样,破坏就变成了:

single.reset();

如果其他人仍有指针,它仍然有效。只有在再次调用getInstance()时才会出现问题。那时你有两个版本的“单身人士”。虽然你有另一种方法可以解决这个问题:一旦调用了destroy,你就可以阻止对getInstance()的任何调用。

getInstance() ... if(destroyed) throw std::runtime_error("singleton destroyed"); ...
destroy() ... destroyed = true; ...

<强>更新

根据g ++ / objdump -d有一个方法调用程序集的副本(在Linux下,虽然它也应该在cygwin中工作):

400978:       48 8b 45 f0             mov    -0x10(%rbp),%rax
40097c:       48 89 c7                mov    %rax,%rdi
40097f:       e8 ae 00 00 00          callq  400a32 <_ZN9Singleton6methodEv>

(P.S.objdump用通常的INTEL语法反转的寄存器进行反汇编。)

正如我们所看到的,编译器使用“callq”。 this指针位于%rax。 “callq”不使用%rax。就汇编代码而言,该函数目前是静态的。

method()内,未使用%rax,因此无论其价值如何都无关紧要:

0000000000400a32 <_ZN9Singleton6methodEv>:
400a32:       55                      push   %rbp
400a33:       48 89 e5                mov    %rsp,%rbp
400a36:       48 83 ec 10             sub    $0x10,%rsp
400a3a:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
400a3e:       be 44 0b 40 00          mov    $0x400b44,%esi
400a43:       bf 80 10 60 00          mov    $0x601080,%edi
400a48:       e8 b3 fd ff ff          callq  400800 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
400a4d:       be 30 08 40 00          mov    $0x400830,%esi
400a52:       48 89 c7                mov    %rax,%rdi
400a55:       e8 c6 fd ff ff          callq  400820 <_ZNSolsEPFRSoS_E@plt>
400a5a:       c9                      leaveq 
400a5b:       c3                      retq   

答案 1 :(得分:1)

简要总结Jesse的链接说明:

你很幸运。 sc2仍然指向那个旧实例。它的内存被释放,但这并不意味着它被清除了。如果访问该内存,则结果未定义。它似乎可以工作(就像它对你一样),它可能会遇到一些可能会提醒你注意问题的调试功能,或者它可能会使计算机长大并跑出门。这是未定义的,由你来确保它不会发生。