对象列表中的分段错误
Segmentation fault in list of objects
我没想到这会这么难,但我有以下程序:
在多线程环境中(使用 ACE-Framework 和 OpenMP)我使用观察者模式。
在 Observer-controller-thread (ActiveObject) 的初始化例程中,我创建了一个 ConcreteObservers 的向量(用于通过它们的离散作业通知它们),如下所示:
mResynthesisVec.assign(cMaxEDTDetection, ConcreteResynthesis(pClientObj));
到目前为止,我在 class 中只使用了标准的 POD 类型,因此标准的复制构造函数没有产生任何问题。
但是今天我用 std::complex 类型的向量扩展了 ConcreteObserver-class。我认为向量已经具有类型内复制构造函数的能力,但我得到的只是一个分段错误,就在创建 ConcreteObserver 之后。那为什么不行呢?
编辑:
线程 Resynthesis 是一个 ACE::ActiveObject(线程)并维护观察者。这个线程有一个 Subject mEventMonitor 作为成员变量,它控制着所有的 concrete-observers (ConcreteResynthesis),它们被创建并存储在一个 std::vector在Resynthesis的init-routine中(如上图)。一旦要执行的工作来自另一个通过 TCP 的程序,Resynthesis 会在 Subject 上注册所需数量的具体观察者,并且 "notifies" 分别为他们提供工作。
typedef std::complex<float> TComplexType;
typedef std::vector<TComplexType> TFFTContainer;
typedef TFFTContainer::iterator TFFTContIter;
typedef std::vector<float> TWindowContainer;
class ConcreteResynthesis: public Subject::Observer {
public:
ConcreteResynthesis();
ConcreteResynthesis(TCPClient * client);
virtual ~ConcreteResynthesis();
virtual void Notify(TBinPos value, int workSignal, int shotCntr, int shots); /// Function called by observed Subject
... some methods
private:
TBinPos mPos; ///< Last observed value
TCPClient * pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
};
ConcreteResynthesis 的构造函数如下所示
ConcreteResynthesis::ConcreteResynthesis(TCPClient *client) : ///<avaroa(0),
pClientObj(client) {
fftwf_init_threads(); ///< does only need to be called once, so do it here
}
而虚析构函数在ConcreteResynthesis
中为空
在Resynthesis定义工作区后,我这样通知conrete-observers:
for (int i = 0; i < iSignalCntr; ++i)
mEventMonitor.ConcreteNotify(pClientObj->GetBinPositions(i), i, sBurstCounter, sBursts);
问题是观察者是在 Resynthesis 的初始例程中创建的,所以直到我找到工作之前只有 ConcreteResynthesis[=51 的构造函数=] 被调用。但是使用 class 中的向量,我在创建第一个具体观察者
后得到了分段错误
EDIT2
按照建议,我实现了复制构造函数、析构函数和赋值运算符。但我仍然遇到段错误。在析构函数中,我将指针设置为零,因为删除是由另一个线程负责的。下面你可以看到 'the rule of 3' 实现:
赋值运算符(几乎与复制构造函数相同,但不返回 this 指针且没有 if 语句)
ConcreteResynthesis & ConcreteResynthesis::operator=(const ConcreteResynthesis & rhs) {
if (this != &rhs) {
mRange = rhs.mRange;
pClientObj = rhs.pClientObj;
pDataPool = rhs.pDataPool;
tFullSignal = rhs.tFullSignal;
mPos = rhs.mPos; ///< Last observed value
mFFTDataCont = rhs.mFFTDataCont;
mZeroBytes2Add = rhs.mZeroBytes2Add;
mNextPowOf2 = rhs.mNextPowOf2;
//mFile = rhs.mFile;
mFFTWPlan = rhs.mFFTWPlan;
mFFTW_WisdomString = 0;
//mutable ACE_Thread_Mutex mMutex; ///< A mutex to guard the value
mCurrentLength = rhs.mCurrentLength;
mSignalPos = rhs.mSignalPos;
}
return *this;
}
虚拟析构函数:
ConcreteResynthesis::~ConcreteResynthesis() {
pClientObj = 0;
pDataPool = 0;
}
这可能无法解决您的所有问题,但您应该做的一件事是让您的 TCPClient
指针成为 std::shared_ptr
而不是原始指针。
根据您的描述,由于您使用 std::vector<ConcreteResynthesis>
,您确实在实例之间共享指针。您还提到您在完成所有操作后手动释放线程管理器中的指针。这似乎是 std::shared_ptr
.
的用例
您的 ConcreteResynthesis
class 有以下成员:
TBinPos mPos; ///< Last observed value
TCPClient* pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
通过更改为:
#include <memory>
//...
TBinPos mPos; ///< Last observed value
std::shared_ptr<TCPClient> pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
当调用 ConcreteResynthesis
对象实例的副本时,共享指针将增加引用计数。向量中的每个对象都将使用相同的单个指针。当最后一个使用共享指针的实体被销毁(销毁使引用计数为 0)时,指针最终被删除。
随着 std::vector
内部的所有复制和销毁,std::shared_ptr
成员负责所有工作,您的析构函数(现在不再需要)试图在一种不优雅的方式(以一种 "poor man's move constructor" 的方式将指针设置为 0)。
除此之外,您不需要用户定义的复制构造函数、赋值运算符或析构函数,从而使代码更加简洁并且不太可能出现错误。
请注意,尽管如此,我假设基础 class Subject::Observer
也可以毫无问题地复制。
我没想到这会这么难,但我有以下程序: 在多线程环境中(使用 ACE-Framework 和 OpenMP)我使用观察者模式。
在 Observer-controller-thread (ActiveObject) 的初始化例程中,我创建了一个 ConcreteObservers 的向量(用于通过它们的离散作业通知它们),如下所示:
mResynthesisVec.assign(cMaxEDTDetection, ConcreteResynthesis(pClientObj));
到目前为止,我在 class 中只使用了标准的 POD 类型,因此标准的复制构造函数没有产生任何问题。 但是今天我用 std::complex 类型的向量扩展了 ConcreteObserver-class。我认为向量已经具有类型内复制构造函数的能力,但我得到的只是一个分段错误,就在创建 ConcreteObserver 之后。那为什么不行呢?
编辑: 线程 Resynthesis 是一个 ACE::ActiveObject(线程)并维护观察者。这个线程有一个 Subject mEventMonitor 作为成员变量,它控制着所有的 concrete-observers (ConcreteResynthesis),它们被创建并存储在一个 std::vector在Resynthesis的init-routine中(如上图)。一旦要执行的工作来自另一个通过 TCP 的程序,Resynthesis 会在 Subject 上注册所需数量的具体观察者,并且 "notifies" 分别为他们提供工作。
typedef std::complex<float> TComplexType;
typedef std::vector<TComplexType> TFFTContainer;
typedef TFFTContainer::iterator TFFTContIter;
typedef std::vector<float> TWindowContainer;
class ConcreteResynthesis: public Subject::Observer {
public:
ConcreteResynthesis();
ConcreteResynthesis(TCPClient * client);
virtual ~ConcreteResynthesis();
virtual void Notify(TBinPos value, int workSignal, int shotCntr, int shots); /// Function called by observed Subject
... some methods
private:
TBinPos mPos; ///< Last observed value
TCPClient * pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
};
ConcreteResynthesis 的构造函数如下所示
ConcreteResynthesis::ConcreteResynthesis(TCPClient *client) : ///<avaroa(0),
pClientObj(client) {
fftwf_init_threads(); ///< does only need to be called once, so do it here
}
而虚析构函数在ConcreteResynthesis
中为空在Resynthesis定义工作区后,我这样通知conrete-observers:
for (int i = 0; i < iSignalCntr; ++i)
mEventMonitor.ConcreteNotify(pClientObj->GetBinPositions(i), i, sBurstCounter, sBursts);
问题是观察者是在 Resynthesis 的初始例程中创建的,所以直到我找到工作之前只有 ConcreteResynthesis[=51 的构造函数=] 被调用。但是使用 class 中的向量,我在创建第一个具体观察者
后得到了分段错误EDIT2 按照建议,我实现了复制构造函数、析构函数和赋值运算符。但我仍然遇到段错误。在析构函数中,我将指针设置为零,因为删除是由另一个线程负责的。下面你可以看到 'the rule of 3' 实现:
赋值运算符(几乎与复制构造函数相同,但不返回 this 指针且没有 if 语句)
ConcreteResynthesis & ConcreteResynthesis::operator=(const ConcreteResynthesis & rhs) {
if (this != &rhs) {
mRange = rhs.mRange;
pClientObj = rhs.pClientObj;
pDataPool = rhs.pDataPool;
tFullSignal = rhs.tFullSignal;
mPos = rhs.mPos; ///< Last observed value
mFFTDataCont = rhs.mFFTDataCont;
mZeroBytes2Add = rhs.mZeroBytes2Add;
mNextPowOf2 = rhs.mNextPowOf2;
//mFile = rhs.mFile;
mFFTWPlan = rhs.mFFTWPlan;
mFFTW_WisdomString = 0;
//mutable ACE_Thread_Mutex mMutex; ///< A mutex to guard the value
mCurrentLength = rhs.mCurrentLength;
mSignalPos = rhs.mSignalPos;
}
return *this;
}
虚拟析构函数:
ConcreteResynthesis::~ConcreteResynthesis() {
pClientObj = 0;
pDataPool = 0;
}
这可能无法解决您的所有问题,但您应该做的一件事是让您的 TCPClient
指针成为 std::shared_ptr
而不是原始指针。
根据您的描述,由于您使用 std::vector<ConcreteResynthesis>
,您确实在实例之间共享指针。您还提到您在完成所有操作后手动释放线程管理器中的指针。这似乎是 std::shared_ptr
.
您的 ConcreteResynthesis
class 有以下成员:
TBinPos mPos; ///< Last observed value
TCPClient* pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
通过更改为:
#include <memory>
//...
TBinPos mPos; ///< Last observed value
std::shared_ptr<TCPClient> pClientObj;
TFFTContainer mFFTDataCont; //makes the problem
fftwf_plan mFFTWPlan;
unsigned int mCurrentLength;
unsigned int mSignalPos;
TFFTContainer tFullSignal; //makes the problem
当调用 ConcreteResynthesis
对象实例的副本时,共享指针将增加引用计数。向量中的每个对象都将使用相同的单个指针。当最后一个使用共享指针的实体被销毁(销毁使引用计数为 0)时,指针最终被删除。
随着 std::vector
内部的所有复制和销毁,std::shared_ptr
成员负责所有工作,您的析构函数(现在不再需要)试图在一种不优雅的方式(以一种 "poor man's move constructor" 的方式将指针设置为 0)。
除此之外,您不需要用户定义的复制构造函数、赋值运算符或析构函数,从而使代码更加简洁并且不太可能出现错误。
请注意,尽管如此,我假设基础 class Subject::Observer
也可以毫无问题地复制。