c++ - 是否有可能/希望创建不可复制的共享指针模拟(以启用 weak_ptr 跟踪/借用类型语义)?
问题描述
问题:Unique_ptrs 很好地表达了所有权,但无法让 weak_ptrs 跟踪它们的对象生命周期。shared_ptrs 可以被weak_ptrs 跟踪,但不能清楚地表达所有权。
建议的解决方案:派生一个新的指针类型(我将称之为strong_ptr),它只是一个shared_ptr,但删除了复制构造函数和赋值运算符,因此很难克隆它们。然后,我们创建另一个新的 borrowed_ptr 类型(不易存储)来处理 weak_ptr 访问对象时所需的临时生命周期延长,从而可以避免在任何地方显式使用 shared_ptrs。
这个问题非所有权副本 std::unique_ptr 和这个更好的 shared_ptr by distinct types for "ownership" and "reference"? 两者都相似,但在这两种情况下,选择都被简单地定义为 unique_ptr 与 shared_ptr 并且答案并没有在我的脑海中提出令人满意的解决方案。(也许我应该回答这些问题而不是问一个新问题?不确定在这种情况下正确的礼仪是什么。)
这是一个基本的刺。请注意,为了避免弱指针的用户必须转换为 shared_ptr 才能使用它,我创建了一个 borrowed_ptr 类型(感谢 rust 的名称),它包装了 shared_ptr 但使用户很难意外存储它。因此,通过使用不同的 hamstrung shared_ptr 衍生物,我们可以表达预期的所有权并引导客户端代码正确使用。
#include <memory>
template <typename T>
// This owns the memory
class strong_ptr : public std::shared_ptr<T> {
public:
strong_ptr() = default;
strong_ptr(T* t) : std::shared_ptr<T>(t) {}
strong_ptr(const strong_ptr&) = delete;
strong_ptr& operator=(const strong_ptr&) = delete;
};
template <typename T>
// This can temporarily extend the lifetime but is intentionally hard to store
class borrowed_ptr : public std::shared_ptr<T> {
public:
borrowed_ptr() = delete;
borrowed_ptr(const borrowed_ptr&) = delete;
borrowed_ptr& operator=(const borrowed_ptr&) = delete;
template <typename T>
static borrowed_ptr borrow(const std::weak_ptr<T>& wp)
{
return wp.lock();
}
private:
borrowed_ptr(std::shared_ptr<T> &sp) : std::shared_ptr<T>(sp) {}
};
这似乎相当简单,并且是对 shared_ptr 的改进,但我找不到任何关于这种技术的讨论,所以我只能想象我错过了一个明显的缺陷。
谁能给我一个具体的理由为什么这是一个坏主意?(是的,我知道这比 unique_ptr 效率低——对于 PIMPL 等,我仍然会使用 unique_ptr。)
警告:我还没有在一个基本示例中使用它,但是它可以编译并运行正常:
struct xxx
{
int yyy;
double zzz;
};
struct aaa
{
borrowed_ptr<xxx> naughty;
};
void testfun()
{
strong_ptr<xxx> stp = new xxx;
stp->yyy = 123;
stp->zzz = 0.456;
std::weak_ptr<xxx> wkp = stp;
// borrowed_ptr<xxx> shp = wkp.lock(); <-- Fails to compile as planned
// aaa badStruct { borrowed_ptr<xxx>::borrow(wkp) }; <-- Fails to compile as planned
// aaa anotherBadStruct; <-- Fails to compile as planned
borrowed_ptr<xxx> brp = borrowed_ptr<xxx>::borrow(wkp); // Only way to create the borrowed pointer
// std::cout << "wkp: " << wkp->yyy << std::endl; <-- Fails to compile as planned
std::cout << "stp: " << stp->yyy << std::endl; // ok
std::cout << "bp: " << brp->yyy << std::endl; // ok
}
解决方案
唯一所有权是唯一的,句号。一个地方拥有这个资源,并会在该代码选择时释放它。
共享所有权是共享的。多个地方可以拥有该资源,并且只有在所有地方都这样做后才能释放该资源。这是一种二元状态:要么一个地方拥有资源,要么多个地方拥有。
您的所有权语义是独一无二的......除非它们不是。以及以某种方式起作用的规则,除非它们没有问题。
现在,您的具体实现充满了漏洞。shared/weak_ptr
都是这些类型接口的显式部分,因此shared_ptr
从strong_ptr
. 如果你有一个weak_ptr
to a strong_ptr
(对于 来说是必需的borrowed_ptr::borrow
),那么你就可以lock
得到一个shared_ptr
.
但是,即使您的界面正确地隐藏了所有这些(也就是说,您创建了自己的weak_ptr
- 等效类型并停止从 继承shared_ptr
),您的 API 也无法阻止某人将其存储borrowed_ptr
在他们想要的任何地方。哦,当然,他们以后不能更改它,但是很容易在构造时将其存储在类成员中,或者堆分配一个或其他。
因此,归根结底,锁定弱指针仍然代表所有权声明。因此,指针堆栈的所有权语义仍然是共享的;只是一个 API 鼓励不要保留共享所有权太久。
unique_ptr
没有“API 鼓励”;它有 API强制执行。这就是赋予它独特所有权的原因。C++ 没有一种机制来创建您想要创建的所有权语义的类似实施。
鼓励在某种程度上可能有用,但borrowed_ptr
对于那些想要表达他们只是暂时声称拥有所有权的人来说,鼓励可能和鼓励一样有用。否则直接shared/weak_ptr
照常使用。也就是说,您的 API 应该明确地认识到它正在使用共享所有权,这样没有人会被愚弄而产生其他想法。
推荐阅读
- ios - 如何在swift 4中绘制半(半)圆并将其设置为特定角度?
- android - java.lang.IllegalArgumentException:在 bjata
- javascript - 键入 [type="text"] 后如何获取 charCode 并引发未定义的按摩?
- php - 解密php中的salesforce自定义字段
- android - 通过单击 RecyclerView 中的 ParentItem 填充 ChildItem
- json - Angular HttpClient - how to convert from Http: JSON.parse(JSON.stringify(data))._body)?
- postgresql - 如何在 postgresQL 中将时间从文本转换为时间格式?
- angular-ui-select - 刷新不能与 minimum-input-length 属性一起正常工作
- elasticsearch - Elasticsearch 使用自定义 _id 创建文档
- github - 将 Private Repository 链接到 bintray 中的 jCenter