我可以为您提供非拥有的共享指针吗?

6

介绍

这个问题起源于需要一种条件接口。我可能陷入了XY问题,但(底线是)我最终需要一个共享指针,它将根据运行时选择管理或不管理(拥有或不拥有)资源。

目前的工作

以下是关于非拥有共享指针的一些想法

  1. Using placement new, eg:

    struct MyStruct {}; 
    MyStruct ms1; 
    std::shared_ptr<MyStruct> sp(new(&ms1) MyStruct); 
    
  2. Using a dummy deleter

    std::shared_ptr<MyStruct> spn(new MyStruct, [](MyStruct*){}); 
    

问题

  • 是否有标准的建议方式?
  • 是否有“不要这样做”的规则?
  • 是否至少有更好的方法?

备注

我使用的类布局(其中将使用非拥有共享指针)如下所示:

template<typename T>
struct blah
{
    shared_ptr<T> _m;
};

现在,_m 成员基于运行时的选择可能拥有或不拥有资源。我之所以不使用weak_ptr,是因为_m 实际上可能是一个拥有指针。

10
为什么不直接使用 weak_ptr,这就是它的设计目的,或者我有什么地方理解错了吗? - EdChum
使用 std::weak_ptr,然后可以通过 lock() 提取一个 shared_ptr 作为本地变量来使用资源。 - Niall
@EdChum 智能指针将成为一个类成员。我试图避免使用两个成员(一个weak和一个shared指针)再加上一个标志来知道哪一个是有效的。是否有一种方法始终只有 一个 成员? - Nikos Athanasiou
@Niall 这不是一段有效/可工作的代码,只是一个编译片段,用来推测使用放置 new 来拥有非拥有智能指针的可能性。问题特别问是否有效或者是否有有效的方法来实现它。 - Nikos Athanasiou
1
如果两个成员属于同一类(对象),我认为没有必要有两个成员。拥有指针(因此指向父对象)拥有它,此时不需要额外的非拥有成员。@NikosAthanasiou - Niall
我认为weak_ptr唯一有效的选择。shared_ptr被设计为拥有所有权,如果你不关心对象的管理,那么维护引用计数就没有意义了。使用weak_ptr只需要一个成员变量。当你实际使用对象时,只需使用本地变量锁定它,不需要标志,weak_ptr::lock已经表明是否成功。这既容易又直接,并且安全。 - Damon
2个回答

11

placement new 明显是未定义行为,因为在你的代码片段中它会试图删除堆栈上的某些内容。空 deleter 版本可以工作,但会分配一个引用计数块。

诀窍是使用 shared_ptr 的疯狂(好吧,别名)构造函数:

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, T *ptr );

它构造了一个shared_ptr,拥有r所拥有的内容,但指向ptr所指向的内容,即:

std::shared_ptr<MyStruct> sp(std::shared_ptr<MyStruct>(), p);

这是标准保证的 noexcept,不会分配任何内存。标准甚至有一条注释说:

[:此构造函数允许创建一个空的非空存储指针的shared_ptr实例。—末尾注释]


7

使用placement new进行构造,析构时会导致未定义行为(可能会崩溃),因为它将在没有使用new创建的对象上立即调用delete。

我建议使用空操作删除器。这种设计可能看起来奇怪,但如果您需要这样的行为(并足够记录文档),它将有效。我曾经在一个项目中使用过类似的方法一段时间,但后来摆脱了对它的需求。


2
我猜摆脱对它的需求是个大赌注。 - Nikos Athanasiou
2
绝对是no-op删除器;这就是为什么boost有boost::null_deleter,它曾经被埋在序列化代码中,但现在已经在核心中。当你不得不诉诸于此时,几乎总是不幸的,但如果你不能改变API,你就不能......(编辑)如果你所做的只是尝试拥有一个可能非拥有指针,那么shared_ptr<>就过度了;自己编写方便类,或者使用unique_ptr和保留布尔值的删除器来告诉它是否需要释放。如果在拥有情况下需要共享所有权,则使用shared_ptr即可。 - Stefan Atev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接