Усилить сигналы во время деструктора, вызывающего sigabrt

Я начал отслеживать разрушение объекта, используя boost :: signal2. Я написал небольшой тест, чтобы посмотреть, смогу ли я использовать сигналы в деструкторах. Вот. Казалось, работает. Затем я начал использовать его для отслеживания списков объектов, которые ссылаются на другие. Моя структура более или менее так:

Для сокращенной схемы структуры: http://oi50.tinypic.com/16c8cwn.jpg

У меня есть два класса IModel и IBlock. У IModel есть много IBlocks, и у IBlock есть родитель IModel. Тем не менее, существует специальный IBlock, называемый IModelBlock. Этот блок имеет ссылочную IModel в дополнение к его родителю. Это что-то вроде «соединителя» между IModels. Я хотел, чтобы IModels знал, какие IModels используют их, поэтому я внедрил систему подсчета ссылок, которая использовала сигналы, которые происходят во время разрушения 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% уверен, что ошибаюсь в этом предположении, поскольку очевидно, что есть проблема, но я не уверен, почему это происходит или как это исправить. Я знаю много кода выше, но кто-нибудь видит, где я иду не так?

РЕДАКТИРОВАТЬ: У меня есть ощущение, что это связано с вызовом функции-члена IBlock * передается в onModelBlockDestroying, но объект еще не ушел (если только из-за того, что он уже прошел деструктор для фактической реализации, ему оставлен только виртуальный вызов). Это то, что происходит? Поскольку деструктор находится в ~ IBlock, к тому времени, когда он заходит так далеко вниз по дереву, он уже называется деструктором для ~ ModelBlock, и поэтому все реализованные функции уже недоступны?

Если я не смог объяснить достаточно хорошо, просто дайте мне знать, и я уточню.

0

Решение

Когда вы вызываете виртуальную функцию в деструкторе, она не будет работать должным образом — вы вызываете виртуальную функцию базового класса (которая, скорее всего, является чистой) вместо виртуальной функции производного класса.

В вашем объяснении отсутствуют некоторые фрагменты, но я подозреваю, что это связано.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]