【问题标题】:Use unique_ptr for ownership and raw pointer otherwise?否则使用 unique_ptr 作为所有权和原始指针?
【发布时间】:2012-09-15 08:19:17
【问题描述】:

我正在使用 C++11 编写一些代码。我有

class X { /* */ };

class A {
    std::vector<X*> va_x;
};

class B {
    std::vector<X*> vb_x;
    std::vector<A> vb_a;
};

A 类中“va_x”的 X*s 指向 B 类中“vb_x”的 X*s 也指向的对象。

现在我想使用智能指针。对我来说,显然 B 类拥有 X* 指向的对象的所有权(特别是因为我的 A 实例属于 B)

所以我应该在 B 中为 X 使用 unique_ptr:

class B {
    std::vector<unique_ptr<X>> vb_x;
    std::vector<A> vb_a;
};

我的问题是,我应该为 A 班做些什么?我应该保留原始指针吗?通过这样做,在我的单元测试中,我必须承认它会导致一些尴尬的事情(imo),例如(不要担心封装,这不是重点):

unique_ptr<X> x(new X());
A a;
a.va_x.push_back(&(*x)); //awkward, but what else can I do?

A.vb_a.push_back(a); //ok
B.vb_x.push_back(move(x)); //ok

【问题讨论】:

  • 对于未来有关 C++11 的问题,请同时使用 C++ 标记它们。 :)
  • 你之前不是问过一个几乎相同的问题吗?
  • @DeadMG 好吧,前一个也是关于 unique_ptr 但我认为这不是一回事
  • 您可能一开始就不应该在向量中存储原始指针!为什么不将唯一指针放入向量中?
  • @BérengerBerthoul:在这种情况下,将引用包装器存储在A 中,并构造您的代码,以便B 对象必须首先存在。

标签: c++ c++11 unique-ptr ownership


【解决方案1】:

正如 Xeo 在他的回答中所说,解决方案通常是使用 x.get()

有一个FILE 的示例,它在需要访问时使用x.get()

void file_deleter(FILE *f)
{
    fclose(f);
}

[...]

{
    std::unique_ptr<FILE, decltype(&file_deleter)>
                       f(fopen("/tmp/test.tmp", "rw"), &file_deleter);

    // read/write use f.get() as in:
    fread(buf, 1, sizeof(buf), f.get());
    fwrite(buf, 1, sizeof(buf), f.get());

    // fclose() gets called in the deleter, no need to do anything
}

但是,在您的情况下,您需要使用x.release()

A a;
{
    unique_ptr<X> x(new X());
    a.va_x.push_back(&(*x)); // this is wrong
}
// here a[0] is a dangling pointer

&amp;(*x) 将获取原始指针并在a 向量中进行复制。但是,当unique_ptr&lt;&gt; 超出范围时,该指针将被删除。所以在} 之后,a 向量中的指针不再好(虽然它可能会工作一段时间。)

传递裸指针的正确方法是使用release()函数,如:

    a.va_x.push_back(x.release()); // this works

在那一行之后,指针只在a 向量中。它是从唯一指针中释放出来的,其想法是调用者现在负责管理该资源。

重要提示:push_back() 可能会抛出 (bad_alloc),如果发生这种情况,资源就会丢失。为避免该问题(如果您的软件捕获 bad_alloc 并继续运行),您需要首先在向量中保留空间,如下所示:

    a.va_x.reserve(a.va_x.size() + 1);  // malloc() happens here
    a.va_x.push_back(x.release());      // no `bad_alloc` possible here

这样,bad_alloc 将在该语句上发生,而资源仍附加到 unique_ptr,并且在发生异常时不会泄漏它。

综上所述,您可能需要shared_ptr。这些可以毫无困难地复制。 unique_ptr 更多的是用于分配一次资源然后忘记函数返回或删除对象的资源。当涉及(许多)副本时,shared_ptr 更有意义。

class X
{
    typedef std::shared_ptr<X> pointer_t;
    [...]
}

class A
{
    std::vector<X::pointer_t> va_x;
}

X::pointer_t x(new X());
A a;
a.va_x.push_back(x); // much cleaner and the pointer is still managed

【讨论】:

  • “但是,在你的情况下,你需要使用 x.release()”。不,这完全违背了使用唯一指针的意义,因为我现在负责删除指针。
  • @Bérenger 好吧,如果你不这样做,那么你必须确保向量在 unique_ptr 实例之前超出范围。如果你可以在你的特定代码中做到这一点,那么你确实很好。
【解决方案2】:

您可以使用x.get(),它将返回内部指针。

除此之外,是的,使用原始指针来处理非拥有引用是可行的方法,另请参阅this question

【讨论】:

  • 好的,谢谢。但这不是更好吗?所以我还是应该使用原始指针?
  • @BérengerBerthoul:只要它们不拥有任何东西,使用原始指针没有任何问题。顾名思义,他们应该只是指向事物,一切都很好。
  • 猜猜它解决了它。我对这些新功能非常谨慎,因为我没有看到任何真正的代码。当我到处听到“用智能指针替换你的原始指针”时,我开始认为不再需要原始指针(我承认这不是很聪明......)谢谢
  • @Xeo:我不同意原始指针没有任何问题。原始指针是一个概念上的噩梦,因为它们没有任何关联的所有权语义。对所有权使用唯一指针,或者对环境拥有的对象使用引用(包装器)。
猜你喜欢
  • 2012-02-01
  • 1970-01-01
  • 2015-10-01
  • 2014-10-25
  • 2021-03-18
  • 2019-11-07
  • 2019-12-13
  • 2019-07-28
  • 1970-01-01
相关资源
最近更新 更多