简体   繁体   English

我是否总是必须使用 unique_ptr 来表示所有权?

[英]Do I always have to use a unique_ptr to express ownership?

Lets say I have a class A that owns an object of class B.假设我有一个 A 类,它拥有 B 类的对象。

  • A is responsible for creating and deleting this object of B. A 负责创建和删除 B 的这个对象。
  • The ownership must not be transferred to another class.所有权不得转让给其他类别。
  • The object of B will never be reinitialized after an object of A was created.在 A 的对象被创建后,B 的对象将永远不会被重新初始化。

Normally, as far as I know, in modern C++ we would use a unique_ptr to express that A is the owner of this object / reference:通常,据我所知,在现代 C++ 中,我们会使用 unique_ptr 来表示 A 是此对象/引用的所有者:

// variant 1 (unique pointer)
class A {
  public:
    A(int param) : b(std::make_unique<B>(param)) {}

    // give out a raw pointer, so that others can access and change the object 
    // (without transferring ownership)
    B* getB() {
      return b.get();
    }
  private:
    std::unique_ptr<B> b;
};

Someone suggested that I also may use this approach instead:有人建议我也可以使用这种方法:

// variant 2 (no pointer)
class A {
  public:
    A(int param) : b(B(param)) {}

    // give out a reference, so that others can access and change the object 
    // (without transferring ownership)
    B& getB() {
      return b;
    }
  private:
    B b;
};

As far as I understand the main differences are that in variant 2, the memory is allocated coherently, while in variant 1 the memory for the B object can be allocated anywhere and the pointer must be resolved first in order to find it.据我了解,主要区别在于,在变体 2 中,内存是一致分配的,而在变体 1 中,B 对象的内存可以分配到任何地方,并且必须首先解析指针才能找到它。 Also of course, it makes a difference if users of A's public interface can work with a B* or a B& .当然,如果 A 的公共接口的用户可以使用B*B& ,这也会有所不同。 But in both cases, I am sure that ownership stays within A as I need it.但在这两种情况下,我确信所有权在我需要时保持在 A 内。

Do I always have to use variant 1 (unique_ptrs) to express ownership?我是否总是必须使用变体 1 (unique_ptrs) 来表示所有权? What are reasons to use variant 2 over variant 1?使用变体 2 而非变体 1 的原因是什么?

You missed the point of unique_ptr .你错过了unique_ptr It was precisely created to allow ownership transfer via a simple assignation.它的创建是为了允许通过简单的分配进行所有权转移。 If ownership has never to be transfered it is much simpler to have a subobject by using containment.如果所有权永远不会被转移,那么通过使用包含来拥有一个子对象要简单得多。

Memory allocation does not really matter in C++ at least for a programmer using an object, because many objects internally use dynamic allocation whatever the duration of the object is.至少对于使用对象的程序员来说,内存分配在 C++ 中并不重要,因为无论对象的持续时间如何,许多对象内部都使用动态分配。 An example is std::string .一个例子是std::string In most implementations, an automatic std::string object will still use dynamic memory for its underlying character array.在大多数实现中,自动std::string对象仍将为其底层字符数组使用动态内存。


Of course library implementors should care for the way their object use dynamic memory.当然,库实现者应该关心他们的对象使用动态内存的方式。 Small String Optimization was invented in recent implementation of the standard library to avoid dynamic allocation for small strings (thanks to NathanOliver for its comment). Small String Optimization 是在最近的标准库实现中发明的,以避免对小字符串进行动态分配(感谢 NathanOliver 的评论)。

Ownership can be expressed in different ways.所有权可以用不同的方式表达。

Your B b of variant 2 is the simplest form of ownership.您的B b变体 2 是最简单的所有权形式。 The instance of class A exclusively owns the object stored in b and the ownership cannot be transferred to another object or (member) variable.A的实例独占存储在b的对象,并且所有权不能转移到另一个对象或(成员)变量。

std::unique_ptr<B> b expresses an unique - but transferable - ownership over the object managed by std::unique_ptr<B> . std::unique_ptr<B> b表示对std::unique_ptr<B>管理的对象的唯一但可转让的所有权。

But also std::optional<B> , std::vector<B> , … or std::variant<B,C> , expresses ownership.std::optional<B>std::vector<B> 、... 或std::variant<B,C>表示所有权。

Also of course, it makes a difference if users of A's public interface can work with a B* or a B&.当然,如果 A 的公共接口的用户可以使用 B* 或 B&,这也会有所不同。 But in both cases, I am sure that ownership stays within A as I need it.但在这两种情况下,我确信所有权在我需要时保持在 A 内。

You can always create a member function that returns B* no matter if the member is B b , or std::unique_ptr<B> b (or also for std::optional<B> , std::vector<B> , … std::variant<B,C> )无论成员是B b还是std::unique_ptr<B> b (或者也适用于std::optional<B>std::vector<B> ……),您始终可以创建一个返回B*的成员函数。 std::variant<B,C> )

Interestingly this code:有趣的是这段代码:

Variant 1变体 1

class A {
  public:
    A(int param) : b(B(param)) {}

    // give out a reference, so that others can access and change the object 
    B& getBRef() {
      return b;
    }


    B* getB() {
      return &b;
    }
  private:
    B b;
};

can be less problematic then:那么问题可能会少一些:

Variant 2变体 2

class A {
  public:
    A(int param) : b(std::make_unique<B>(param)) {}

    // give out a raw pointer, so that others can access and change the object 
    // (without transferring ownership)
    B* getB() {
      return b.get();
    }
  private:
    std::unique_ptr<B> b;
};

For the Variant 2 case, a call to another member function of A could potentially invalidate the pointer (if that function eg assigned another object to the unique_ptr ).对于变体 2 的情况,对A另一个成员函数的调用可能会使指针无效(如果该函数例如将另一个对象分配给unique_ptr )。 While for Variant 1 the validity of the returned pointer (or reference) is given by the lifetime of an instance of A而对于变体 1 ,返回的指针(或引用)的有效性由A的实例的生命周期给出

In any case, you then have to treat B * as non owning raw pointer, and make clear in the documentation, how long that raw pointer is valid.在任何情况下,您都必须将B *视为非拥有的原始指针,并在文档中明确说明该原始指针的有效期有多长。


Which kind of ownership you choose depends on the actual use case.您选择哪种所有权取决于实际用例。 Most of the time you will try to stay with the simplest ownership B b .大多数情况下,您将尝试保留最简单的所有权B b If that object should be optional you will use std::optional<B> .如果该对象应该是可选的,您将使用std::optional<B>

If the implementation requires it eg if you plan to use PImpl, if the data structure might prevent the use of B b (like in the case of tree- or graph-like structures), or if the owner-ship has to be transferable, you might want to use std::unique_ptr<B> .如果实现需要它,例如,如果您计划使用 PImpl,如果数据结构可能阻止使用B b (例如在树状或图状结构的情况下),或者所有权必须是可转让的,您可能想使用std::unique_ptr<B>

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

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