C++ weak_ptr.lock() 段错误
C++ weak_ptr.lock() segfault
所以我有这个函数来添加侦听器,它转换 class 的共享指针,以便我稍后在收到通知时可以调用它。
void registerListener(std::shared_ptr<T> listener)
{
if (!listener) {
qCWarning(OBSERVER_LOGGER) << "Attempted to register a null observer.";
return;
}
// TODO make a foreach function that removes dead listeners to get rid of this code dupe
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
if (listener == shared) {
return;
}
iter++;
} else {
iter = listeners.erase(iter);
}
}
auto weak = std::weak_ptr<T>(listener);
listeners.push_back(weak);
}
void notify(std::function<void(std::shared_ptr<T>)> onNotify)
{
// TODO make a foreach function that removes dead listeners to get rid of this code dupe
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
onNotify(shared);
iter++;
} else {
iter = listeners.erase(iter);
}
}
}
private:
std::vector<std::weak_ptr<T>> listeners;
并且出于某种原因,"iter->lock()" 段错误。我会说这是一个 Qt 应用程序,但我故意没有创建任何线程(据我所知)所以我非常困惑我做错了什么让这些 weak_ptrs 中断。所以如果我在 gdb 中 运行 它,它工作得很好。但是如果我设置 "set disable-randomization off" 那么我会得到错误。所以我觉得这是一个奇怪的问题,其中有未初始化的变量。如果有帮助,这是我在 gdb 中崩溃时的堆栈。
#0 0x00007f856bd8beec in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_get_use_count() const ()
#1 0x00007f856bd844a8 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_lock_nothrow() ()
#2 0x00007f856bd9cd7d in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count(std::__weak_count<(__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) ()
#3 0x00007f856bda9948 in std::__shared_ptr<IEntityListener<Assignment>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__weak_ptr<IEntityListener<Assignment>, (__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) ()
#4 0x00007f856bda8a62 in std::shared_ptr<IEntityListener<Assignment> >::shared_ptr(std::weak_ptr<IEntityListener<Assignment> > const&, std::nothrow_t) ()
#5 0x00007f856bda701a in std::weak_ptr<IEntityListener<Assignment> >::lock() const ()
#6 0x00007f856bda5624 in Observer<IEntityListener<Assignment> >::notify(std::function<void (std::shared_ptr<IEntityListener<Assignment> >)>) ()
#7 0x00007f856bda3a1a in EntityObserver<Assignment>::notifyCreated(std::shared_ptr<Assignment>) ()
编辑:Michael Burr 发布了在添加新听众的同时注册听众的可能性,这完全有可能发生。这将导致迭代器无效,并且当我去调用 weak_ptr.lock() 上不是 weak_ptr 的内存部分时,BOOM。我认为这里有一个寓意,我必须找到它。
我在您显示的代码中找不到明显的问题。所以我认为,问题出在您没有显示的代码中。请记住,Q_OBJECT 装饰对象也有一个与 qt 相关的生命周期管理。可能有些干扰...
如果您将您的代码与我 post 此处的代码进行比较,您可能会很快找到关键的区别。
#include <vector>
#include <algorithm>
#include <memory>
template <typename _Observable>
class IObserver
{
public:
virtual ~IObserver() {}
virtual void OnChange(const _Observable* what) = 0;
};
template <class T>
class CObservable
: public IObserver<T> // Make sure this class breaks once IObserver<> changes.
{
public:
typedef IObserver<T> Observer_t;
typedef std::weak_ptr<Observer_t> Observer_reference;
typedef std::shared_ptr<Observer_t> Observer_strong_reference;
typedef T Class_t;
typedef std::vector<Observer_reference> ObserverRefCollection;
private:
ObserverRefCollection m_observers;
void CleanupZombies()
{
m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(),
[this](Observer_reference iter) -> bool
{
Observer_strong_reference o = iter.lock();
return !o;
}
), m_observers.end());
}
public:
void RegisterObserver(Observer_strong_reference& observer)
{
if (!observer)
return;
for (auto& iter : m_observers)
{
if (observer == iter.lock())
return;
}
m_observers.push_back(Observer_reference(observer));
}
/*virtual*/ void OnChange(const Class_t* what)
{
bool hasZombies = false;
for (auto& iter : m_observers)
{
Observer_strong_reference o = iter.lock();
if (o)
{
o->OnChange(what);
}
else
{
hasZombies = true;
}
}
if (hasZombies)
CleanupZombies();
}
};
class CObservableUint32
: public CObservable<CObservableUint32>
{
uint32_t m_value;
public:
void Set(uint32_t newValue)
{
bool changed = newValue != m_value;
m_value = newValue;
if (changed)
{
OnChange(this);
}
}
uint32_t Get() const
{
return m_value;
}
};
class CSomeObserver
: public IObserver < CObservableUint32 >
{
public:
CSomeObserver()
: IObserver<CObservableUint32>()
{
}
virtual ~CSomeObserver()
{
}
virtual void OnChange(const CObservableUint32* what)
{
}
};
其他地方...
CObservableUint32 observable;
{
std::shared_ptr<IObserver<CObservableUint32> > observer = std::make_shared<CSomeObserver>();
observable.RegisterObserver(observer);
observable.Set(42UL);
}
observable.Set(100);
当调用 notify()
时,通过 onNotify()
函数对象调用的函数是否可能导致 registerListener()
被间接调用(或其他一些可以添加或删除的代码listeners
集合中的条目)?
如果是这样,那么在 notify()
for
循环中使用的 iter
可以无效。您可能希望将 notify()
更改为如下所示,它将所有 shared_ptr
对象排队等待通知,这样 listeners
集合是否在任何期间被修改都无关紧要onNotify()
回调:
#include <queue>
void notify(std::function<void(std::shared_ptr<T>)> onNotify)
{
std::queue<std::shared_ptr<T>> notify_targets;
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
notify_targets.push(shared);
iter++;
} else {
iter = listeners.erase(iter);
}
}
while (!notify_targets.empty()) {
onNotify(notify_targets.front());
notify_targets.pop();
}
}
所以我有这个函数来添加侦听器,它转换 class 的共享指针,以便我稍后在收到通知时可以调用它。
void registerListener(std::shared_ptr<T> listener)
{
if (!listener) {
qCWarning(OBSERVER_LOGGER) << "Attempted to register a null observer.";
return;
}
// TODO make a foreach function that removes dead listeners to get rid of this code dupe
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
if (listener == shared) {
return;
}
iter++;
} else {
iter = listeners.erase(iter);
}
}
auto weak = std::weak_ptr<T>(listener);
listeners.push_back(weak);
}
void notify(std::function<void(std::shared_ptr<T>)> onNotify)
{
// TODO make a foreach function that removes dead listeners to get rid of this code dupe
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
onNotify(shared);
iter++;
} else {
iter = listeners.erase(iter);
}
}
}
private:
std::vector<std::weak_ptr<T>> listeners;
并且出于某种原因,"iter->lock()" 段错误。我会说这是一个 Qt 应用程序,但我故意没有创建任何线程(据我所知)所以我非常困惑我做错了什么让这些 weak_ptrs 中断。所以如果我在 gdb 中 运行 它,它工作得很好。但是如果我设置 "set disable-randomization off" 那么我会得到错误。所以我觉得这是一个奇怪的问题,其中有未初始化的变量。如果有帮助,这是我在 gdb 中崩溃时的堆栈。
#0 0x00007f856bd8beec in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_get_use_count() const ()
#1 0x00007f856bd844a8 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_lock_nothrow() ()
#2 0x00007f856bd9cd7d in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count(std::__weak_count<(__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) ()
#3 0x00007f856bda9948 in std::__shared_ptr<IEntityListener<Assignment>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__weak_ptr<IEntityListener<Assignment>, (__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) ()
#4 0x00007f856bda8a62 in std::shared_ptr<IEntityListener<Assignment> >::shared_ptr(std::weak_ptr<IEntityListener<Assignment> > const&, std::nothrow_t) ()
#5 0x00007f856bda701a in std::weak_ptr<IEntityListener<Assignment> >::lock() const ()
#6 0x00007f856bda5624 in Observer<IEntityListener<Assignment> >::notify(std::function<void (std::shared_ptr<IEntityListener<Assignment> >)>) ()
#7 0x00007f856bda3a1a in EntityObserver<Assignment>::notifyCreated(std::shared_ptr<Assignment>) ()
编辑:Michael Burr 发布了在添加新听众的同时注册听众的可能性,这完全有可能发生。这将导致迭代器无效,并且当我去调用 weak_ptr.lock() 上不是 weak_ptr 的内存部分时,BOOM。我认为这里有一个寓意,我必须找到它。
我在您显示的代码中找不到明显的问题。所以我认为,问题出在您没有显示的代码中。请记住,Q_OBJECT 装饰对象也有一个与 qt 相关的生命周期管理。可能有些干扰...
如果您将您的代码与我 post 此处的代码进行比较,您可能会很快找到关键的区别。
#include <vector>
#include <algorithm>
#include <memory>
template <typename _Observable>
class IObserver
{
public:
virtual ~IObserver() {}
virtual void OnChange(const _Observable* what) = 0;
};
template <class T>
class CObservable
: public IObserver<T> // Make sure this class breaks once IObserver<> changes.
{
public:
typedef IObserver<T> Observer_t;
typedef std::weak_ptr<Observer_t> Observer_reference;
typedef std::shared_ptr<Observer_t> Observer_strong_reference;
typedef T Class_t;
typedef std::vector<Observer_reference> ObserverRefCollection;
private:
ObserverRefCollection m_observers;
void CleanupZombies()
{
m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(),
[this](Observer_reference iter) -> bool
{
Observer_strong_reference o = iter.lock();
return !o;
}
), m_observers.end());
}
public:
void RegisterObserver(Observer_strong_reference& observer)
{
if (!observer)
return;
for (auto& iter : m_observers)
{
if (observer == iter.lock())
return;
}
m_observers.push_back(Observer_reference(observer));
}
/*virtual*/ void OnChange(const Class_t* what)
{
bool hasZombies = false;
for (auto& iter : m_observers)
{
Observer_strong_reference o = iter.lock();
if (o)
{
o->OnChange(what);
}
else
{
hasZombies = true;
}
}
if (hasZombies)
CleanupZombies();
}
};
class CObservableUint32
: public CObservable<CObservableUint32>
{
uint32_t m_value;
public:
void Set(uint32_t newValue)
{
bool changed = newValue != m_value;
m_value = newValue;
if (changed)
{
OnChange(this);
}
}
uint32_t Get() const
{
return m_value;
}
};
class CSomeObserver
: public IObserver < CObservableUint32 >
{
public:
CSomeObserver()
: IObserver<CObservableUint32>()
{
}
virtual ~CSomeObserver()
{
}
virtual void OnChange(const CObservableUint32* what)
{
}
};
其他地方...
CObservableUint32 observable;
{
std::shared_ptr<IObserver<CObservableUint32> > observer = std::make_shared<CSomeObserver>();
observable.RegisterObserver(observer);
observable.Set(42UL);
}
observable.Set(100);
当调用 notify()
时,通过 onNotify()
函数对象调用的函数是否可能导致 registerListener()
被间接调用(或其他一些可以添加或删除的代码listeners
集合中的条目)?
如果是这样,那么在 notify()
for
循环中使用的 iter
可以无效。您可能希望将 notify()
更改为如下所示,它将所有 shared_ptr
对象排队等待通知,这样 listeners
集合是否在任何期间被修改都无关紧要onNotify()
回调:
#include <queue>
void notify(std::function<void(std::shared_ptr<T>)> onNotify)
{
std::queue<std::shared_ptr<T>> notify_targets;
for (auto iter=listeners.begin(); iter != listeners.end(); ) {
if (auto shared = iter->lock()) {
notify_targets.push(shared);
iter++;
} else {
iter = listeners.erase(iter);
}
}
while (!notify_targets.empty()) {
onNotify(notify_targets.front());
notify_targets.pop();
}
}