0

我对这段代码有一些疑问>任何讨论都会对理解这些事情很有帮助:

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->method();” 成功了吗??

德韦什

4

2 回答 2

2

为什么它有效?

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

但是,正如您提到的那样,该类被滥用了。

仍然允许使用 destroy() 函数的一种更安全的方法是将 shared_ptr<>() 用于 Singleton 指针。这样,破坏就变成了:

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>

(PS objdump 使用与通常的 INTEL 语法相反的寄存器进行反汇编。)

正如我们所见,编译器使用了“ callq”。this指针在%rax. " callq" 不使用%rax. 就汇编代码而言,该函数当前是静态的。

Inside 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   
于 2013-10-31T03:38:42.227 回答
1

简要总结一下 Jesse 的链接所说的内容:

你会走运的。sc2仍然指向那个旧实例。它的内存被释放了,但这并不意味着它被清除了。如果您访问该内存,则结果是未定义的。它可能看起来有效(就像它为您所做的那样),它可能会遇到一些可能会提醒您问题的调试功能,或者它可能会使计算机长腿并跑出门外。它是未定义的,您可以确保它不会发生。

于 2013-10-31T03:38:18.960 回答