假设我有一个 A 类,它拥有 B 类的一个对象。
- A 负责创建和删除 B 的该对象。
- 所有权不得转让给其他类别。
- 创建 A 的对象后,B 的对象将永远不会被重新初始化。
通常,据我所知,在现代 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;
};
有人建议我也可以使用这种方法:
// 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;
};
据我了解,主要区别在于,在变体 2 中,内存是连贯分配的,而在变体 1 中,B 对象的内存可以分配在任何地方,并且必须首先解析指针才能找到它。
当然,如果 A 的公共(public)接口(interface)的用户可以使用 B*
或 B&
,情况也会有所不同。
但在这两种情况下,我确信所有权仍属于 A,因为我需要它。
我是否总是必须使用变体 1 (unique_ptrs) 来表达所有权? 使用变体 2 而不是变体 1 的原因是什么?
最佳答案
所有权可以通过不同的方式表达。
您的B b
变体 2 是最简单的所有权形式。类的实例A
独占地拥有存储在 b
中的对象并且所有权不能转移给另一个对象或(成员)变量。
std::unique_ptr<B> b
表达对 std::unique_ptr<B>
管理的对象的唯一但可转让的所有权.
还有 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&. But in both cases, I am sure that ownership stays within A as I need it.
您始终可以创建一个返回 B*
的成员函数。无论成员(member)是否B b
,或std::unique_ptr<B> b
(或者也适用于 std::optional<B>
、 std::vector<B>
、… std::variant<B,C>
)
有趣的是这段代码:
变体 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;
};
那么问题就少了:
变体 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;
};
对于变体 2 情况,调用 A
的另一个成员函数可能会使指针无效(例如,如果该函数将另一个对象分配给 unique_ptr
)。而对于变体 1,返回的指针(或引用)的有效性由 A
实例的生命周期给出。
无论如何,你都必须处理 B *
作为非拥有原始指针,并在文档中明确说明该原始指针的有效时间。
您选择哪种所有权取决于实际用例。
大多数时候,您会尝试保持最简单的所有权B b
。如果该对象是可选的,您将使用 std::optional<B>
.
如果实现需要它,例如如果您计划使用 PImpl,如果数据结构可能阻止使用 B b
(就像树形或图形结构的情况),或者如果所有权必须可转让,您可能需要使用 std::unique_ptr<B>
.
关于c++ - 我是否总是必须使用 unique_ptr 来表达所有权?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69754776/