c++ - 为什么单例模板会使我的程序崩溃?

标签 c++ templates crash singleton

这是定义模板的头文件: 根据一些答案,我进行了修改以使模板单线程安全。但程序仍然崩溃,所以我相信这里的重点不应该是模板是否是线程安全的。这是关于当 C1 继承自 CBase 和 Singleton 时发生的情况。显然,当C1尝试调用其父类CBase的成员变量的函数时,程序崩溃了。在我看来,当多重继承和单一模板一起使用时,内存中的某些东西会变得困惑。

#ifndef _T_SINGLETON_HH_
#define _T_SINGLETON_HH_

template <class T>
class Singleton
{
public:
    static T*   getInstance()
    {   
        static T instance_;
        return &instance_;
    }   

    template <class A>
    static T*   getInstance(A a)
    {   
        static T instance_(a);
        return &instance_;
    }   

protected:
    Singleton() {}
    virtual ~Singleton() {}
private:
    Singleton(Singleton const&);
    Singleton& operator=(Singleton const&);
};
#endif  // _T_SINGLETON_HH_ //

以下是该模板的使用方式:

#include <iostream>

#include <unistd.h>
#include <pthread.h>

#include "singleton.hh"

using namespace std;

class X;
class X1; 
class ABase;
class BBase;
class BB; 
class CBase;

class X { 
public:
    virtual int getX() = 0;
};

class X1 : public X { 
public:
    int getX() { return 1; }
};

class Timer
{
private:
   static Timer* t_; 
   BBase* b_; 

   Timer();
   static void* go_helper(void* context);
   void go();

public:
    virtual ~Timer() {}

    static Timer* getInstance();
    void subscribe(BBase* b); 
    void unsubscribe(BBase* b) { b_ = NULL; }
};

class CBase {
public:
    CBase(BB& bb) : bb_(bb) {}
    virtual void run() = 0;
protected:
    BB& bb_;
};

class C1 : public CBase,
           public Singleton<C1>
{
public:
    C1(BB& bb) : CBase(bb) {}
    void run();
};

class BBase {
public:
    BBase(ABase& a) : a_(a) { Timer::getInstance()->subscribe(this); } 
    virtual ~BBase() { Timer::getInstance()->unsubscribe(this); }
    virtual void run() = 0;
protected:
    ABase&  a_; 
};

class BB : public BBase {
public:
    BB(ABase& a) : BBase(a) {
        c = C1::getInstance(*this);
//      c = new C1(*this);    IF WE USE THIS INSTEAD, THEN IT WILL WORK
    }
    int getX();
    void run();
private:
    CBase*  c;
};

class ABase {
public:
    ABase() {
        x = new X1;
        b = new BB(*this);
    }
    void run();
    int getX() { return x->getX(); }
private:
    X* x;
    BBase* b;
};

Timer* Timer::t_ = NULL;

Timer::Timer() : b_(NULL)
{
    pthread_t th;
    pthread_create(&th, NULL, &Timer::go_helper, this);
}

Timer*
Timer::getInstance() {
    if (t_ == NULL)
        t_ = new Timer;
    return t_;
}

void
Timer::subscribe(BBase* b) { b_ = b; }

void*
Timer::go_helper(void* context) {
    Timer *t = reinterpret_cast<Timer*>(context);
    t->go();
    return NULL;
}

void
Timer::go()
{
    while(1) {
        sleep(1);
        if (b_) b_->run();
    }
}

void ABase::run() {
    cout << __PRETTY_FUNCTION__ << getX() << endl;
    cout << __PRETTY_FUNCTION__ << x->getX() << endl;
    b->run();
    while(1)
        sleep(1);
}

int BB::getX() {
    return a_.getX();
}
void BB::run() {
    cout << __PRETTY_FUNCTION__ << endl;
    c->run();
}

void C1::run() {
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl;
}

int main()
{
    ABase*  a = new ABase;
    a->run();
}

如果您仍然认为这是一个线程安全问题,那么这里是使用模板的简化单线程版本:

#include <iostream>

#include <unistd.h>
#include <pthread.h>

#include "singleton.hh"

using namespace std;

class X;
class X1; 
class ABase;
class BBase;
class BB; 
class CBase;

class X { 
public:
    virtual int getX() = 0;
};

class X1 : public X { 
public:
    int getX() { return 1; }
};

class CBase {
public:
    CBase(BB& bb) : bb_(bb) {}
    virtual void run() = 0;
protected:
    BB& bb_;
};

class C1 : public CBase,
           public Singleton<C1>
{
public:
    C1(BB& bb) : CBase(bb) {}
    void run();
};

class BBase {
public:
    BBase(ABase& a) : a_(a) { } 
    virtual ~BBase() { } 
    virtual void run() = 0;
protected:
    ABase&  a_; 
};

class BB : public BBase {
public:
    BB(ABase& a) : BBase(a) {
        c = C1::getInstance(*this);
//      c = new C1(*this);
    }   
    int getX();
    void run();
private:
    CBase*  c;  
};

class ABase {
public:
    ABase() {
        x = new X1; 
        b = new BB(*this);
    }   
    void run();
    int getX() { return x->getX(); }
private:
    X* x;
    BBase* b;
};

void ABase::run() {
    cout << __PRETTY_FUNCTION__ << getX() << endl;
    cout << __PRETTY_FUNCTION__ << x->getX() << endl;
    b->run();
    while(1)
        sleep(1);
}

int BB::getX() {
    return a_.getX();
}
void BB::run() {
    cout << __PRETTY_FUNCTION__ << endl;
    c->run();
}

void C1::run() {
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl;
}

当我运行这个程序时,它崩溃了:

void ABase::run()1
void ABase::run()1
virtual void BB::run()
zsh: segmentation fault (core dumped)  ./a.out

这里是GDB给出的堆栈信息:

#0  0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89
89        int getX() { return x->getX(); }
[Current thread is 1 (Thread 0x7faa0f050740 (LWP 5351))]
(gdb) bt
#0  0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89
#1  0x0000000000400e0c in BB::getX (this=0x7ffed6e821f0) at tst_sigill.cc:138
#2  0x0000000000400e71 in C1::run (this=0xaeedd0) at tst_sigill.cc:146
#3  0x0000000000400e51 in BB::run (this=0xaeec60) at tst_sigill.cc:142
#4  0x0000000000400de3 in ABase::run (this=0xaeec20) at tst_sigill.cc:132
#5  0x0000000000400ed1 in main () at tst_sigill.cc:152

由于某种未知的原因,传递给 BB::getX 和 ABase::getX 的指针被搞乱了。我看不出代码有什么问题。

最佳答案

嗯,主要问题似乎是您的 getInstance() 实现不是线程安全的。

我建议忘记所有 construct()/destruct() 内容,并将其留给 ABI 提供的 CRT0 代码。

<小时/>

您应该使用 Scott Meyer's Singleton 之后的实现保证线程安全(至少对于当前标准):

template <class T>
class Singleton {
public:
    static T& getInstance() {   
        static T instance_;
        return instance_;
    }   

protected:    
    Singleton() {}
    virtual ~Singleton() {}

    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;
};

如果以后需要更改任何可以异步访问的Singleton属性,请记住还要提供线程安全操作。

您可以使用模板单例基础来支持这一点,例如:

template <class T>
class Singleton {
   // ...
protected:    
    mutable std::mutex internalDataGuard;
};

class C1 : public CBase, public Singleton<C1> {
public:
     C1(BB& bb) : CBase(bb) {}
     void run();

     void setSomeData(T data) {
         std::unique_lock(Singleton<C1>::internalDataGuard);
         // change internal data member ...
     }

     const T& getSomeData() const {
         std::unique_lock(Singleton<C1>::internalDataGuard);
         // return internal data member ...
     }
};

关于c++ - 为什么单例模板会使我的程序崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37444732/

相关文章:

下面这段代码的 C++ 等效项 (sprintf)

c++ - 写入输出迭代器的模板成员函数

c++ - 在 Vista 上调用 CoCreateInstance 时是否可以降低权限级别?

c++ - C++ 中的大值

javascript - 在 Handlebars.js 中实现循环标签

php - 在大数据集上运行whereHas laravel方法会导致mysql崩溃

C++ 函数模板,混合隐式和显式实例化?

templates - 根据模板参数选择参数类型

node.js - 根据日志,Heroku 应用程序不断崩溃......为什么?

iphone - 找不到导致 "active assertions beyond permitted time"崩溃的错误