gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 01:37:19 29 4
gpt4 key购买 nike

这是定义模板的头文件:根据一些答案,我进行了修改以使模板单线程安全。但程序仍然崩溃,所以我相信这里的重点不应该是模板是否是线程安全的。这是关于当 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/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com