Tur*_*bar 2 c++ inheritance unique-ptr
我有一个基类的 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*/
}
Run Code Online (Sandbox Code Playgroud)
现在,Base 本身有一个指向 Base 的原始指针的成员向量:
struct Base
{
...
std::vector<Base*> providers;
...
}
Run Code Online (Sandbox Code Playgroud)
组成 Base::providers 的指针都是通过从 global_vec 调用 unique_ptr::get() 获得的:
void Base::subscribe_to(Base* src)
{
providers.push_back(src);
}
Run Code Online (Sandbox Code Playgroud)
Base 有一个与这些订阅者一起工作的成员函数,并在工作之前检查 nullptr:
void Base::do_work()
{
...
for(Base* ptr : providers)
{
if(ptr != nullptr)
{
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,在我的代码中的其他地方,我可以删除 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);
Run Code Online (Sandbox Code Playgroud)
然而,在擦除一个元素之后, Base::suscribers 仍然会持有一个指向对象的有效指针。也就是说,当在 Base::do_work() 中遍历 Base::providers 时,没有 Base* 将等于 std::nullptr。
我希望从 global_vec 中删除 unique_ptr 会调用 Base::~Base(),从而将 Base::providers 中的指针呈现为 std::nullptr。Base 的析构函数被调用,但指针是有效的(您甚至可以从它们访问数据成员)。
Base 确实有一个虚拟析构函数。
为什么 Base::providers 中的指针仍然有效?
我希望擦除一个
unique_ptrfromglobal_vec会调用Base::~Base()
是的,它确实。
从而将指针呈现
Base::providers为std::nullptr。
这就是您的期望失败的地方。有没有办法为原料指向对象的指针设置为nullptr当自动对象被销毁。这是你的责任,以手工处理,在自己的代码。在相应的对象被销毁之前/时,您需要Base*从providers向量中删除一个指针。编译器无法为您做到这一点。
您可能会考虑vector<Base*>在您的Base类中使用两个,一个用于跟踪this已订阅的对象,另一个用于跟踪已订阅的对象this。然后,~Base()可以取消订阅this活动订阅,并通知this即将离开的活动订阅者。例如:
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;
Run Code Online (Sandbox Code Playgroud)
否则,请考虑在全局向量中使用std::shared_ptr而不是std::unique_ptr,然后您可以在其他向量中存储std::weak_ptr<Base>对象而不是原始Base*指针。当您访问 a 时std::weak_ptr,您可以在使用指针之前查询它以确保关联的对象指针仍然有效:
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;
Run Code Online (Sandbox Code Playgroud)
调用了 Base 的析构函数,但指针有效
不,它们无效,因为指向的对象已被销毁。指针只是悬空,指向旧内存,并不nullptr像您期望的那样。
你甚至可以从他们那里访问数据成员
在对象被销毁后访问其成员是未定义的行为。
| 归档时间: |
|
| 查看次数: |
683 次 |
| 最近记录: |