【问题标题】:Boost signals during destructor causing sigabrt在析构函数期间提升信号导致 sigabrt
【发布时间】:2013-02-18 22:49:12
【问题描述】:

我开始使用 boost::signals2 跟踪对象破坏。我写了一个小测试,看看我是否仍然可以在析构函数here 中使用信号。它似乎奏效了。然后我开始使用它来跟踪引用其他对象的对象列表。我的结构或多或少是这样的:

结构简图:http://oi50.tinypic.com/16c8cwn.jpg

我有两个类 IModel 和 IBlock。 IModel 有许多 IBlock,一个 IBlock 有一个 IModel 父级。但是,有一个特殊的 IBlock,称为 IModelBlock。除了其父模块外,此模块还有一个引用的 IModel。这是 IModel 之间的“连接器”。我想让 IModel 知道哪些 IModel 正在使用它们,因此我实现了一个引用计数系统,该系统使用在销毁 IModel 和 IBlock 期间发生的信号来跟踪哪些模型正在使用另一个模型。

我有我的 IBlock 纯虚拟类(显然析构函数除外):

class IBlock
{
public:
    virtual ~IBlock() { this->sigBlockDestroying(this); }
    ...
    boost::signals2::signal<void (IBlock*)> sigBlockDestroying; //raised when the destructor is called
};

我的 IModelBlock 头(纯虚拟类):

class IModelBlock : public IBlock
{
public:
    virtual ~IModelBlock() {}
    ...
};

我的 IModel 标头(纯虚拟类):

class IModel
{
public:
    ...
    virtual void nowUsedBy(IModelBlock* block) = 0;
};

我的 IModelBlock 构造函数(ModelBlock 类)的实现通知模型它正在被使用:

ModelBlock::ModelBlock(IModel *parent, long id, boost::shared_ptr<IModel> model)
{
    //...lots of stuff happens involving the parent and model...then:

    //tell the model we see them
    model->nowUsedBy(this);
}

这就是毛茸茸的地方

我的 IModel (Model) 实现定义如下:

class Model : public IModel
{
public:
    ...
    virtual ~Model();
    ...
protected:
    ...
    void onModelDestroying(IModel* model);
    void onModelBlockDestroying(IBlock* modelBlock);
    ...
    //counts for this model being used
    std::map<IModel*, int> useCounts;
    std::map<IModel*, boost::signals2::connection> modelDestructionConnections;
    std::vector<boost::signals2::connection> modelBlockDestructionConnections;
};

Model::~Model()
{
    typedef std::pair<IModel*, boost::signals2::connection> ModelDestructionRecord;
    BOOST_FOREACH(ModelDestructionRecord record, this->modelDestructionConnections)
    {
        record.second.disconnect();
    }
    BOOST_FOREACH(boost::signals2::connection& connection, this->modelBlockDestructionConnections)
    {
        connection.disconnect();
    }
}

void Model::nowUsedBy(IModelBlock *block)
{
    if (block->isOrphan())
        return; //if the block is an orphan, there isn't actually a model using it

    if (useCounts.count(block->getParentModel()))
    {
        //increment this use count
        useCounts[block->getParentModel()]++;
    }
    else
    {
        useCounts[block->getParentModel()] = 1;
        //subscribe to the model's destruction
        modelDestructionConnections[block->getParentModel()] = block->getParentModel()->sigModelDestroying.connect(boost::bind(&Model::onModelDestroying, this, _1));
    }

    //subscribe to this modelblock's destruction. we don't need to track this because modelblocks never point to
    //other models and so for its lifetime it will point to us
    this->modelBlockDestructionConnections.push_back(block->sigBlockDestroying.connect(boost::bind(&Model::onModelBlockDestroying, this, _1)));
}

void Model::onModelDestroying(IModel *model)
{
    if (this->modelDestructionConnections.count(model))
    {
        this->modelDestructionConnections[model].disconnect();
        this->modelDestructionConnections.erase(model);
    }
}

void Model::onModelBlockDestroying(IBlock *modelBlock)
{
    if (!this->useCounts[modelBlock->getParentModel()])
        return; //we've never seen this modelblock before as far as we know

    //decrement the the model count
    //note that even if the modelblock's parent pointer is invalid, we are just using it for being a value so its ok to use here
    this->useCounts[modelBlock->getParentModel()]--;

    if (this->useCounts[modelBlock->getParentModel()] <= 0 && this->modelDestructionConnections.count(modelBlock->getParentModel()))
    {
        //we are no longer used by this model
        this->modelDestructionConnections[modelBlock->getParentModel()].disconnect();
        this->modelDestructionConnections.erase(modelBlock->getParentModel());
    }
}

会发生什么

当我使用 ModelBlocks 创建一堆包含嵌套模型的模型时,一切都很好。然而,我预料到破坏会出现一些问题,所以我为一个巨大的段错误做好了准备……这从未发生过。相反,当我让所有模型(以及它们的所有块)开始销毁阶段时,我得到了一个 sigabrt,它说它发生在 Model::onModelBlockDestroying 就在第一个 if 上。我看了看控制台,上面写着pure virtual method called。我以前从未见过这个错误,所以我不确定如何修复它。

堆栈跟踪显示它正在调用 ~IBlock 析构函数并发出 sigBlockDestroying 信号,经过 10 个函数级别后最终调用 onModelBlockDestroying 函数。现在,如果模型被销毁,它的所有信号都应该被断开(参见~Model),我会认为 sigBlockDestroying 不会调用任何东西。因此,我可以得出结论,当调用 ~IBlock 析构函数时,模型仍然存在,并且对象仍然有效。我 99.9% 肯定我在这个假设中是错误的,因为显然存在问题,但我不确定它为什么会发生或如何解决它。我知道上面有很多代码,但是有人知道我哪里出错了吗?

编辑:我感觉它与调用传递给onModelBlockDestroying 的 IBlock* 的成员函数有关,但对象还没有消失(除非,因为它已经通过析构函数实际实现只剩下纯虚拟调用)。这就是正在发生的事情吗?因为析构函数在 ~IBlock 中,所以当它在树下这么远时,它已经调用了 ~ModelBlock 的析构函数,所以所有实现的函数都不再可访问?

如果我解释得不够清楚,请告诉我,我会澄清。

【问题讨论】:

    标签: c++ reference-counting pure-virtual boost-signals boost-signals2


    【解决方案1】:

    当您在析构函数中调用虚函数时,它不会按预期工作 - 您正在调用基类虚函数(很可能是纯虚函数)而不是派生类虚函数。

    您的解释中缺少一些内容,但我怀疑这是相关的。

    【讨论】:

      猜你喜欢
      • 2017-05-10
      • 1970-01-01
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多