简体   繁体   English

由unique_ptr返回的指针 <T> 原始的unique_ptr被销毁后::: get()不为nullptr

[英]Pointer returned by unique_ptr<T>::get() is not nullptr after original unique_ptr is destroyed

I have a global vector of unique_ptrs to a base class, to which I append unique_ptrs to a derived class: 我在基类上有一个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*/
}

Now, Base itself has a member vector of raw pointers to Base: 现在,Base本身具有一个指向Base的原始指针的成员向量:

struct Base
{
  ...
  std::vector<Base*> providers;
  ...
}

The pointers that make up Base::providers are all obtained via calling unique_ptr::get() from global_vec: 组成Base :: providers的指针都是通过从global_vec调用unique_ptr :: get()获得的:

void Base::subscribe_to(Base* src)
{
  providers.push_back(src);
}

Base has a member function that does work with these subscribers, and checks for nullptr before doing work: Base有一个与这些订阅者一起使用的成员函数,并在工作之前检查nullptr:

void Base::do_work()
{
  ...
  for(Base* ptr : providers)
  {
    if(ptr != nullptr)
    {
      ...
    }
  }
}

Now, somewhere else in my code, I can erase the unique_ptrs in global_vec: 现在,在代码的其他地方,我可以删除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);

However, after erasing an element, Base::suscribers will still hold a valid pointer to an object. 但是,在删除元素后,Base :: suscribers仍将持有指向对象的有效指针。 That is, when iterating through Base::providers in Base::do_work(), no Base* will equal std::nullptr. 也就是说,当遍历Base :: do_work()中的Base :: providers时,没有Base *会等于std :: nullptr。

I would expect that erasing a unique_ptr from global_vec would invoke Base::~Base(), thus rendering the pointers in Base::providers as std::nullptr. 我希望从global_vec删除unique_ptr会调用Base ::〜Base(),从而将Base :: providers中的指针呈现为std :: nullptr。 Base's destructor is invoked, but the pointers are valid (you can even get access to data members from them). Base的析构函数被调用,但是指针是有效的(您甚至可以从它们访问数据成员)。

Base does have a virtual destructor. Base确实具有虚拟析构函数。

Why are the pointers in Base::providers still valid? 为什么Base :: providers中的指针仍然有效?

I would expect that erasing a unique_ptr from global_vec would invoke Base::~Base() 我希望从global_vec删除unique_ptr会调用Base::~Base() global_vec Base::~Base()

Yes, it does. 是的,它确实。

thus rendering the pointers in Base::providers as std::nullptr . 因此,将Base::providers的指针呈现为std::nullptr

This is where your expectation fails. 这是您的期望失败的地方。 There is no way for raw pointers to an object to be set to nullptr automatically when the object is destroyed. 销毁对象时,无法自动将指向该对象的原始指针设置为nullptr It is YOUR responsibility to handle that manually in your own code. 您有责任以自己的代码手动处理。 You need to remove a Base* pointer from the providers vector before/when the corresponding object is destroyed. 在销毁相应对象之前/之时,您需要从providers向量中删除Base*指针。 The compiler cannot do that for you. 编译器无法为您做到这一点。

You might consider having two vector<Base*> in your Base class, one to keep track of objects that this has subscribed to, and one to keep track of objects that have subscribed to this . 你可能会考虑有两个vector<Base*>在你的Base班,一个跟踪的对象的this已经订阅,以及一个跟踪已订阅对象的this Then, ~Base() can unsubscribe this from active subscriptions, and notify active subscribers that this is going away. 然后, ~Base()可以取消this从活动的订阅,并通知活跃用户认为this是怎么回事了。 For example: 例如:

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;

Otherwise, consider using std::shared_ptr instead of std::unique_ptr in your global vector, and then you can store std::weak_ptr<Base> objects instead of raw Base* pointers in your other vectors. 否则,请考虑在全局向量中使用std::shared_ptr而不是std::unique_ptr ,然后可以在其他向量中存储std::weak_ptr<Base>对象而不是原始Base*指针。 When you access a std::weak_ptr , you can query it to make sure the associated object pointer is still valid before using the pointer: 当您访问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;

Base's destructor is invoked, but the pointers are valid Base的析构函数被调用,但指针有效

No, they are not valid , since the object being pointed to was destroyed. 不,它们无效 ,因为指向的对象已损坏。 The pointers are simply left dangling, pointing to the old memory, and are not nullptr like you are expecting. 指针只是悬空,指向旧内存,并且不像您期望的那样为nullptr

you can even get access to data members from them 您甚至可以从他们那里访问数据成员

It is undefined behavior to access members of an object after it has been destroyed. 销毁对象后,访问对象成员是不确定的行为

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM