我在基类上有一个unique_ptrs的全局向量,并向其派生类中添加了unique_ptrs:
std::vector<std::unique_ptr<Base>> global_vec;
template<typename T>
Base* create_object()
{
std::unique_ptr<T> uptr = std::make_unique<T>(/* ... */);
Base* last_ptr = uptr.get();
global_vec.emplace_back(std::move(uptr));
return last_ptr; /*this is a bit irrelevant, but is for the caller*/
}
现在,Base本身具有一个指向Base的原始指针的成员向量:
struct Base
{
...
std::vector<Base*> providers;
...
}
组成Base :: providers的指针都是通过从global_vec:调用unique_ptr :: get()获得的。
void Base::subscribe_to(Base* src)
{
providers.push_back(src);
}
Base具有一个与这些订阅者一起使用的成员函数,并在工作前检查nullptr:
void Base::do_work()
{
...
for(Base* ptr : providers)
{
if(ptr != nullptr)
{
...
}
}
}
现在,在代码的其他位置,我可以删除global_vec中的unique_ptrs:
auto itr = std::find_if(global_vec.begin(), global_vec.end(), [&](std::unique_ptr<Base> const& n)
{ return n.get() == ptr_selected; }); //ptr_selected points to a widget selected by the user
global_vec.erase(itr);
但是,在擦除元素之后,Base :: suscribers仍将持有指向对象的有效指针。也就是说,当遍历Base :: do_work()中的Base :: providers时,没有Base *会等于std :: nullptr。
我希望从global_vec删除unique_ptr会调用Base ::〜Base(),从而将Base :: providers中的指针呈现为std :: nullptr。调用了Base的析构函数,但是指针是有效的(您甚至可以从它们访问数据成员)。
Base确实具有虚拟析构函数。
为什么Base :: providers中的指针仍然有效?
答案 0 :(得分:3)
我希望从
unique_ptr
删除global_vec
会调用Base::~Base()
是的。
因此将
Base::providers
中的指针显示为std::nullptr
。
这是您的期望失败的地方。销毁对象时,原始指针无法自动设置为nullptr
。用您自己的代码手动处理它是您的责任。在销毁相应对象之前/之时,需要从Base*
向量中删除providers
指针。编译器无法为您做到这一点。
您可能考虑在您的vector<Base*>
类中有两个Base
,一个用于跟踪this
已订阅的对象,另一个用于跟踪已订阅{ {1}}。然后,this
可以取消订阅~Base()
的活动订阅,并通知活动订阅者this
即将消失。例如:
this
否则,请考虑在全局向量中使用std::shared_ptr
而不是struct Base
{
...
protected:
std::vector<Base*> providers;
std::vector<Base*> subscribers;
...
public:
~Base();
...
void subscribe_to(Base* src);
void unsubscribe_from(Base* src);
...
};
Base::~Base()
{
std::vector<Base*> temp;
temp = std::move(providers);
for(Base* ptr : temp) {
unsubscribe_from(ptr);
}
temp = std::move(subscribers);
for(Base* ptr : temp) {
ptr->unsubscribe_from(this);
}
}
void Base::subscribe_to(Base* src)
{
if (src) {
providers.push_back(src);
src->subscribers.push_back(this);
}
}
void Base::unsubscribe_from(Base* src)
{
if (src) {
std::remove(providers.begin(), providers.end(), src);
std::remove(src->subscribers.begin(), src->subscribers.end(), this);
}
}
void Base::do_work()
{
...
for(Base* ptr : providers) {
...
}
...
}
...
std::vector<std::unique_ptr<Base>> global_vec;
,然后可以在其他向量中存储std::unique_ptr
对象,而不是原始的std::weak_ptr<Base>
指针。访问std::weak_ptr
时,可以查询它以确保关联的对象指针在使用该指针之前仍然有效:
Base*
Base的析构函数被调用,但指针有效
否,它们不是有效,因为指向的对象已被破坏。指针只是悬空而指向旧的内存,并且不像您期望的那样struct Base
{
...
protected:
std::vector<std::weak_ptr<Base>> providers;
...
public:
...
void subscribe_to(std::shared_ptr<Base> &src);
...
};
void Base::subscribe_to(std::shared_ptr<Base> &src)
{
if (src) {
providers.push_back(src);
}
}
void Base::do_work()
{
...
for(std::weak_ptr<Base> &wp : providers) {
std::shared_ptr<Base> ptr = wp.lock();
if (ptr) {
...
}
}
...
}
...
std::vector<std::shared_ptr<Base>> global_vec;
。
您甚至可以从他们那里访问数据成员
在对象被破坏后访问其成员是未定义的行为。