【问题标题】:Referring to a "std::unique_ptr" that you don't own (use a raw pointer?)引用您不拥有的“std::unique_ptr”(使用原始指针?)
【发布时间】:2019-07-28 10:31:46
【问题描述】:

通常,如果您使用std::shared_ptr 指向一个对象,并且您想创建另一个指向该对象但不共享所有权的指针,您将创建一个std::weak_ptr

// Create a shared pointer to own the object
std::shared_ptr<int> p = std::make_shared<int>(42);

// Create a weak pointer (that does not own the object)
std::weak_ptr<int> q(p);

// Use the weak pointer some time later
if (std::shared_ptr ptr = q.lock()) {
  // use *ptr
}

我的问题是,当涉及到std::unique_ptr 时,你是如何做到这一点的?

使用唯一指针可确保当前资源由std::unique_ptr 本身独占。但是,如果我想创建一个指向不拥有该资源的同一资源的指针怎么办?我不能使用std::weak_ptr,因为弱指针旨在处理来自std::shared_ptr 的引用计数。我会在这里使用原始指针吗?还是有更好的选择?

// Create a unique pointer to own the object
std::unique_ptr<int> p = std::make_unique<int>(42);

// Create a non-owning pointer to the same object
// Is this really the best way?
int* q = p.get();

// Use the pointer some time later
if (q != nullptr) {

  // Imagine this may be multithreaded...
  // what happens if p.reset() is called by another thread while the current thread is RIGHT HERE.

  // use *q
}

我能想到的创建指向std::unique_ptr 拥有的对象的非拥有指针的唯一方法是使用原始指针,但正如您从上面的代码中看到的那样,这可能会导致线程应用程序出现问题。有没有更好的方法来实现相同的目标?

【问题讨论】:

  • 是的,只使用原始指针,或者,C++ 核心指南对非拥有指针有一些特殊类型,我想不起来了。
  • 视情况而定。 weak_ptr 提供了在对象锁定时延长对象生命周期的语义。您是否希望在 unique_ptr 的情况下使用它,或者您是否可以在使用对象时删除它?
  • @SergeyA 是 gsl::observer&lt;T&gt;,iirc 是 T* 的别名
  • “使用原始指针可能会导致多线程问题”。使用几乎任何东西,包括智能指针,都可能导致多线程问题
  • 我会为std::unique_ptrstd::shared_ptr 使用原始指针,除非应用程序需要std::weak_ptr。为不使用的东西付费是没有意义的。

标签: c++ c++11 shared-ptr unique-ptr weak-ptr


【解决方案1】:

根据您的上一个示例,这是一个应该使用std::shared_ptrstd::weak_ptr 的场景。

std::unique_ptr 和非拥有的原始指针应该用于您可以保证智能指针比原始指针寿命更长的情况。

class A {
    std::unique_ptr<int> ptr = std::make_unique<int>(5);
public:
    int* get_ptr() const{return ptr.get();}
};

class B {
    A a;
public:
    void do_something() {
        //int * ptr = a.get_ptr();//Valid, but not advised
        int & ref = *a.get_ptr();//Preferred
        ref++;
    }
};

如果你能保证,你应该使用std::unique_ptr 和一个原始指针来表示这个对象。这在意识形态上是正确的。

但是,如果您无法保证在需要操作对象时的生命周期,则应由std::weak_ptr 提供引用,这些引用用于获取所有权(即使只是暂时的!)以进行更改。

class A {
    std::shared_ptr<int> ptr = std::make_shared<int>(5);
public:
    std::weak_ptr<int> get_ptr() const {
        return ptr;//Implicitly converts
    }
    void destroy() {
        ptr.reset();
    }
};

class B {
    std::weak_ptr<int> ptr;
public:
    B(std::weak_ptr<int> ptr) : ptr(ptr) {}
    void do_something() {
        if(auto owned_ptr = ptr.lock()) {//owned_ptr will be deduced to be of type std::shared_ptr<int>
            *owned_ptr++; //Guaranteed to only execute if the pointer is still valid
        }
    }
};

int main() {
    A a;
    B b(a.get_ptr());
    if(get_random_value() > 10)
        a.destroy();
    b.do_something();
}

【讨论】:

  • //int * ptr = a.get_ptr();//Valid, but not advised int &amp; ref = *a.get_ptr();//Preferred 为什么?两种情况都做同样的事情。如果指针从您下方删除,那么您将留下一个悬空引用,这与悬空指针一样糟糕。
  • @NathanOliver 该示例的重点是B 拥有自己对A 的引用,因此能够管理指针的生命周期。所以没有指针超出范围的风险[在维护其不变量的正确记录的代码中]。因此,在不考虑生命周期的情况下选择引用或指针,更喜欢引用。
【解决方案2】:

这取决于额外指针是否可能比unique_ptr 寿命更长。

  • 如果非拥有指针绝对不能超过unique_ptr:使其成为普通指针。获得这种指针的人可以假定它是有效的,但不能假定拥有它指向的内存。
  • 如果非拥有指针可能比unique_ptr 寿命更长:您的原始指针并不是真正的“唯一”;并且您应该将 unique_ptr 替换为 shared_ptr 并将 weak_ptr 作为非拥有副本传递。

我刚刚注意到这个答案基本上是对Xirema's answer的简要总结。请投票/接受该答案!

【讨论】:

    猜你喜欢
    • 2022-01-17
    • 1970-01-01
    • 2013-08-18
    • 1970-01-01
    • 2012-01-10
    • 2012-06-26
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多