这个问题在SO上被多次询问,但答案不适用于我的情况,AFAICT。下面一段代码在点击i != std::end(observers_);
后立即触发错误。
void VisualGeometry::SignalPopPointFlags(const Point_2r& p,
const uint32_t msec_delay) const {
for(auto i = std::begin(observers_); i != std::end(observers_); ++i)
(*i)->SlotPopPointFlags(p, msec_delay);
}
查看<vector>
,以下内容会触发错误:
void _Compat(const _Myiter& _Right) const
{ // test for compatible iterator pair
if (this->_Getcont() == 0
|| this->_Getcont() != _Right._Getcont())
{ // report error
_DEBUG_ERROR("vector iterators incompatible");
_SCL_SECURE_INVALID_ARGUMENT;
}
}
由于我没有比较来自不同容器的迭代器,似乎this->_Getcont() == 0
的第一次检查可能是问题,但我不知道该怎么说。
如果我为vec.begin()/ vec.end()换掉begin(vec)/ end(vec),就会出现同样的问题。
我对如何发生这种情况感到有些迷茫。关于如何进行调试的任何建议?
VisualGeometry类旨在将接收到的信号转发给正在观看的对象。以下是相关的代码段:
class VisualGeometry : public IGeometryObserver, public IObservableGeometry {
public:
void SlotPushSegmentFlags(const Segment_2r& s, const uint32_t flags,
const uint32_t msec_delay = 0) override;
void SlotPopSegmentFlags(const Segment_2r& s,
const uint32_t msec_delay = 0) override;
void SignalPushSegmentFlags(const Segment_2r& s, const uint32_t flags,
const uint32_t msec_delay = 0) const override;
void SignalPopSegmentFlags(const Segment_2r& s,
const uint32_t msec_delay = 0) const override;
/* snip */
private:
std::vector<IGeometryObserver*> observers_;
};
void VisualGeometry::SlotPushSegmentFlags(const Segment_2r& s,
const uint32_t flags,
const uint32_t msec_delay) {
SignalPushSegmentFlags(s, flags, msec_delay);
}
void VisualGeometry::SlotPopPointFlags(const Point_2r& p,
const uint32_t msec_delay) {
SignalPopPointFlags(p, msec_delay);
}
/* etc... */
答案 0 :(得分:7)
要检查的第一件事是看你是否正在修改迭代它时迭代的vector
。
看看这是否可以解决问题:
void VisualGeometry::SignalPopPointFlags(const Point_2r& p,
const uint32_t msec_delay) const {
auto obs = observers_;
for(auto i = std::begin(obs); i != std::end(obs); ++i)
(*i)->SlotPopPointFlags(p, msec_delay);
}
处理这类问题很棘手,通常表明你有设计问题。
通常,在调用一系列回调时,如果该回调有任何方法可以到达您正在迭代并更改它或其成员的序列,则需要添加一些生命周期管理代码,并确定它对于当一个当前被发送时要发送的另一个回调,以及在调用回调时安装或卸载回调的含义。
一个简单的规则是“如果在安装回调时安装了,则不会收到回调”,但如果卸载回调,则不会被调用。
为了产生这种效果,我的回调往往是weak_ptr
到std::function
s的容器。当您安装回调时,传入std::function
,然后我将其复制到shared_ptr
。我产生了weak_ptr
,并将其存储在我的回调容器中。
然后我将shared_ptr
返回到正在安装回调的代码。这个shared_ptr
是生命周期令牌:只要它(或它的副本)有效,我就会继续对其进行回调。
在我的广播功能中,我首先扫描我的容器以查找过时的weak_ptr
,然后将容器复制到本地std::vector<std::weak_ptr<std::function<void(message)>>>
。
然后我遍历此容器,执行.lock()
获取std::shared_ptr<std::function<void(message)>>
,然后调用它是否有效。
如果在我播放时触发广播,则会发生。 (如果这种情况经常发生,我会把我的筹码炸掉,但这是另一个问题)。如果有人添加回调,我很好。如果有人删除回调,我很好。
如果事情是多线程的,我在迭代本地std::vector
时没有被锁定,但在清除无效的weak_ptr
时被锁定,并且在克隆{的序列时被锁定{1}},或在向weak_ptr
添加/删除回调时。