0

boost::signals2 を使用してオブジェクト破壊の追跡を開始しました。ここで、デストラクタでまだシグナルを使用できるかどうかを確認するためだけに、小さなテストを作成しました。うまくいったようです。その後、他のオブジェクトを参照するオブジェクトのリストを追跡するために使用し始めました。私の構造は多かれ少なかれそうです:

構造の簡略図: http://oi50.tinypic.com/16c8cwn.jpg

IModel と IBlock の 2 つのクラスがあります。IModel には多くの IBlock があり、IBlock には IModel の親があります。ただし、IModelBlock と呼ばれる特別な IBlock があります。このブロックには、親に加えて参照される 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 (モデル) の実装では、次のように定義されています。

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 を使用して内部にネストされたモデルを含む一連のモデルを作成すると、すべてがうまく機能します。ただし、破壊に関するいくつかの問題が予想されたので、巨大なセグメンテーション違反に備えました...決して起こりませんでした。代わりに、すべてのモデル (およびそのすべてのブロック) に破壊フェーズを開始させたときModel::onModelBlockDestroying、最初のif. コンソールを見ると、pure virtual method called. このエラーは今まで見たことがないので、修正方法がわかりません。

スタック トレースは、~IBlock デストラクタを呼び出していてsigBlockDestroying、10 関数レベルの後に最終的に関数を呼び出したシグナルを発行したことを示していonModelBlockDestroyingます。ここで、Model が破棄されていれば、そのシグナルはすべて切断されているはずであり ( を参照~Model)、sigBlockDestroying は何も呼び出さなかったはずです。したがって、~IBlock デストラクタが呼び出されたときにモデルがまだ存在していて、オブジェクトがまだ有効であると結論付けることができます。明らかに問題があるため、この仮定が間違っていることは 99.9% 確信していますが、なぜそれが起こっているのか、どのように修正するのかはわかりません。上記のコードがたくさんあることは知っていますが、どこが間違っているのか誰かわかりますか?

編集: に渡された IBlock* のメンバー関数を呼び出すことに関係しているように感じますonModelBlockDestroyingが、オブジェクトはまだ消えていません (ただし、実際の実装のデストラクタを既に通過しているため、残っているだけです)呼び出しに純粋な仮想を使用)。それが起こっていることですか?デストラクタが ~IBlock にあるため、ツリーのはるか下に到達するまでに、~ModelBlock のデストラクタが既に呼び出されているため、実装されているすべての関数にアクセスできなくなりますか?

十分に説明できなかった場合は、お知らせください。明確にします。

4

1 に答える 1

0

デストラクタで仮想関数を呼び出すと、期待どおりに機能しません。派生クラスの仮想関数ではなく、基本クラスの仮想関数 (おそらく純粋) を呼び出しています。

あなたの説明にはいくつかのビットが欠けていますが、これが関連していると思われます。

于 2013-10-27T18:48:14.103 に答える