#c #c 11 #c 17 #c 14 #smart-pointers
#c #c 11 #c 17 #c 14 #интеллектуальные указатели
Вопрос:
У меня есть объект с несколькими shared_ptrs
указателями на него, и его счетчик ссылок use_count
в связывающем блоке управления больше 1.
Теперь я хочу деконструировать объект, но я не знаю, где все они shared_ptrs
, поэтому я не могу найти и деконструировать их, прежде чем деконструировать объект.
Если я просто деконструирую объект, это приведет к тому, что shared_ptrs
они станут зависшими. Следовательно, в этой ситуации, как удалить объект с use_count
размером больше 1, но не иметь представления обо всех его shared_ptrs
?
Спасибо за любые предложения!
Комментарии:
1. Вам нужно перепроектировать свою программу, чтобы она не использовала объекты там, где не должна.
2. Сделайте некоторые из них
weak_ptr
s, чтобы объект мог умереть до того, как они это сделают?3. Устраните реальную проблему, у вас в основном проблема с управлением ресурсами. Попытка принудительного удаления просто закрывает глаза на тот факт, что у вас проблема с дизайном. знайте жизненный цикл своих объектов, понимайте права собственности и сведите использование shared_ptr к минимуму.
4. @sorosh_sabz Это неплохая практика, она совершенно неправильная. Эти общие указатели не будут «регистрировать» это удаление и попытаются также удалить управляемый объект, что приведет к неопределенному поведению.
5. @sorosh_sabz — Если вы собираетесь публиковать опасные, неправильные и вводящие в заблуждение «решения», пожалуйста, сделайте это в разделе ответов, где они могут получить экспертную оценку, которую они заслуживают. Не злоупотребляйте разделом комментариев.
Ответ №1:
Если у вас есть доступ к коду Object
класса и вы можете его изменить, вы можете выполнить следующие шаги (после этого вы можете сразу перейти к окончательному коду):
- Создайте специальную структуру
Fields
, содержащую все поля originalObject
. - Сохранить
Fields
как поле указателя, выделенное для кучиp_
. - В исходном
Object
классе сделайте все исходные поля ссылками, указывающими на поляFields
выделенного объекта кучи. - Добавьте
destroyed_
флаг bool, который отмечает, чтоObject
он уже был уничтожен. Этот флаг становитсяtrue
после первого вызова деструктора. - В каждом методе проверяйте, что
destroyed_
это неверно, в противном случае генерируйте исключение. Потому что ни один из методов не может быть использован, когда объект уже уничтожен. Вы также можете просто показать сообщение с некоторой ошибкой вместо того, чтобы вызывать исключение и возвращаться из метода, ничего не делая. Как справиться с этой ошибкой, зависит от вас. - Внутри деструктора при первом вызове выполните всю очистку как обычно. И отметьте
destroyed_
какtrue
. Второй вызов деструктора должен просто завершиться молча из-за того, что destroyed_ уже true . - Все конструкторы копирования и операторы присваивания должны быть реализованы как обычно. Пример в коде ниже.
Чтобы удалить объект до освобождения всех общих указателей, просто вызовите ptr->~Object();
destructor , вот ptr
любой общий указатель, или используйте удобную функцию std::destroy_at , например std::destroy_at(ptr.get());
.
В приведенном ниже коде, если last DoSomething()
не вызывается (попробуйте закомментировать его), программа завершается без исключения, хотя отображается предупреждение о повторном вызове деструктора.
#include <iostream>
#include <memory>
class Object {
public:
Object()
: p_(new Fields{}), f0_(p_->f0_), f1_(p_->f1_) {}
Object(Object const amp; o)
: p_(new Fields{}), f0_(o.f0_), f1_(o.f1_) {}
Object amp; operator = (Object const amp; o) {
f0_ = o.f0_;
f1_ = o.f1_;
return *this;
}
void DoSomething() {
if (destroyed_)
throw std::runtime_error("Object already destroyed!");
f0_ = 1;
f1_ = std::to_string(f0_) " ";
std::cout << "DoSomething: '" << f1_ << "'" << std::endl;
}
~Object() {
if (destroyed_) {
std::cout << "Called destructor of destroyed object..."
<< std::endl;
return;
}
// Process fields cleanup here...
delete p_;
p_ = nullptr;
destroyed_ = true;
}
private:
struct Fields {
int f0_ = 0;
std::string f1_;
};
Fields * p_ = nullptr;
bool destroyed_ = false;
int amp; f0_;
std::string amp; f1_;
};
int main() {
try {
std::shared_ptr<Object> o0 = std::make_shared<Object>();
{
std::shared_ptr<Object> o1 = o0;
o1->DoSomething();
o1->DoSomething();
// Call destructor when you don't need object.
// Even if some shared_ptrs still use it.
o1->~Object();
}
o0->DoSomething();
return 0;
} catch (std::exception const amp; ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Вывод:
DoSomething: '1 '
DoSomething: '1 2 '
Called destructor of destroyed object...
Exception: Object already destroyed!
Аналогичный код, приведенный выше, также может быть реализован с использованием C 17 std::optional вместо указателя кучи, это решение даже лучше, потому что оно не использует выделение кучи, все поля расположены внутри тела объекта (т. Е. Выделение стека).
#include <iostream>
#include <memory>
#include <optional>
class Object {
public:
Object()
: p_(Fields{}), f0_(p_->f0_), f1_(p_->f1_) {}
Object(Object const amp; o)
: p_(Fields{}), f0_(o.f0_), f1_(o.f1_) {}
Object amp; operator = (Object const amp; o) {
f0_ = o.f0_;
f1_ = o.f1_;
return *this;
}
void DoSomething() {
if (destroyed_)
throw std::runtime_error("Object already destroyed!");
f0_ = 1;
f1_ = std::to_string(f0_) " ";
std::cout << "DoSomething: '" << f1_ << "'" << std::endl;
}
~Object() {
if (destroyed_) {
std::cout << "Called destructor of destroyed object..."
<< std::endl;
return;
}
// Process fields cleanup here...
p_ = std::nullopt;
destroyed_ = true;
}
private:
struct Fields {
int f0_ = 0;
std::string f1_;
};
std::optional<Fields> p_;
bool destroyed_ = false;
int amp; f0_;
std::string amp; f1_;
};
int main() {
try {
std::shared_ptr<Object> o0 = std::make_shared<Object>();
{
std::shared_ptr<Object> o1 = o0;
o1->DoSomething();
o1->DoSomething();
// Call destructor when you don't need object.
// Even if some shared_ptrs still use it.
o1->~Object();
}
o0->DoSomething();
return 0;
} catch (std::exception const amp; ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Вывод:
DoSomething: '1 '
DoSomething: '1 2 '
Called destructor of destroyed object...
Exception: Object already destroyed!
Ответ №2:
Как удалить объект, для которого значение use_count больше 1?
Уменьшив количество использований до 0. Это может быть достигнуто путем уничтожения или переназначения общих указателей, которые в настоящее время указывают на объект.
но я не знаю, где все эти shared_ptrs
Затем вы должны решить проблему, которая мешает вам знать все эти указатели.
Или вы могли бы подумать, что, возможно, есть причина, по которой эти указатели существуют, и что, возможно, объект еще не должен быть удален, поскольку он, по-видимому, используется какой-то частью программы.