std::shared_ptr 为空但不为空

std::shared_ptr which is empty but not null

http://www.cplusplus.com/reference/memory/shared_ptr/

A shared_ptr that does not own any pointer is called an empty shared_ptr. A shared_ptr that points to no object is called a null shared_ptr and shall not be dereferenced. Notice though that an empty shared_ptr is not necessarily a null shared_ptr, and a null shared_ptr is not necessarily an empty shared_ptr.

我可以创建一个空的 std::shared_ptr,即不拥有任何东西但不为空,即包含有效负载吗?

用例是将 "legacy" 代码与现代指针结合起来:鉴于 foo 的调用语法不会更改,

void foo(const MyClass* p) {
   if (p == nullptr) {
       auto defaultObject = std::make_shared<MyClass>("some", "parameters");
       defaultObject->doSomething();
       ...
       defaultObject->doSomethingElse();
       // The default object must be destroyed again
   } else {
       p->doSomething();
       ...
       p->doSomethingElse();
       // p must not be destroyed, its memory is managed somewhere else
   }
}

是否有 doNotOwnButStillPointTo() 的实现允许这样做:

void foo(const MyClass* p) {
    std::shared_ptr<MyClass> wrapper;
    if (p == nullptr) {
        // Create a default object
        wrapper = std::make_shared<MyClass>("some", "parameters");
    } else {
        wrapper = doNotOwnButStillPointTo(p);
    }

    wrapper->doSomething();
    ...
    wrapper->doSomethingElse();
    // p mus not be destroyed, the default object (if created) shall be
}

或者,为了不爱上 XY-Problem,是否有不同的智能指针可用或 none?

shared_ptr 有一个删除器。这是一个破坏底层对象的多态过程。您可能有一个空的删除器:

void foo(const MyClass* p) {
    std::shared_ptr<MyClass> wrapper;
    if (p == nullptr) {
        // Create a default object
        wrapper = std::make_shared<MyClass>("some", "parameters");
    } else {
        wrapper = std::shared_ptr<MyClass>(p, [](MyClass*){});
    }

    wrapper->doSomething();
    ...
    wrapper->doSomethingElse();
    // p mus not be destroyed, the default object (if created) shall be
}

但这会导致糟糕的设计。回到 ProblemXY:目的是什么?有人可能会向您传递一个对象作为原始指针(但可能会传递一个 nullptr)。如果提供的是 nullptr 而不是真实对象,您希望创建一个本地对象。您可能希望防止内存泄漏。好的

void foo(const MyClass* p) {
    std::shared_ptr<MyClass> local;
    if (p == nullptr) {
        // Create a default object
        local = std::make_shared<MyClass>("some", "parameters");
        p = local.get();
    }

    // p is always valid, local will always be destroyed (if exists)
    p->doSomething();
    ...
    p->doSomethingElse();
}

您正在寻找的是 shared_ptr 的自定义删除器构造器。这是为了允许共享指针接管来自其他地方的指针,当它被销毁时,它将 运行 您的自定义代码而不是调用 delete。在这种情况下,您的自定义代码将什么都不做,因为您不想删除指针。那看起来像

void foo(const MyClass* p) {
    std::shared_ptr<MyClass> wrapper;
    if (p == nullptr) {
        // Create a default object
        wrapper = std::make_shared<MyClass>("some", "parameters");
    } else {
        wrapper = std::shared_ptr(p, [](auto){ });
    }

    wrapper->doSomething();
    ...
    wrapper->doSomethingElse();
    // p mus not be destroyed, the default object (if created) shall be
}

Am I able to create an empty std::shared_ptr, i.e. one which does not own anything but which is not null, i.e. contains payload?

确实可以。链接页面的说法是正确的 empty shared_ptr is not necessary a null shared_ptr.

可以使用构造函数创建这样的共享指针

template< class Y > 
shared_ptr(const shared_ptr<Y>&, element_type*);

如果你传递一个空的空共享指针作为第一个参数,非空指针作为第二个参数,那么你将得到一个空的非空共享指针。

构造函数通常用于指向拥有对象的成员或别名,但它恰好也匹配这种极端情况。特别注意不要让这个共享指针泄漏到这个函数的范围之外。

你的情况:

wrapper = {std::shared_ptr<void>{}, p};

Or, to not fall for the XY-Problem, is there a different smart pointer available or none at all?

另一种方法是仅将共享指针用于默认对象的所有权,并通过 p 调用函数:

std::shared_ptr<MyClass> wrapper;
if (p == nullptr) {
    // Create a default object
    wrapper = std::make_shared<MyClass>("some", "parameters");
    p = wrapper.get();
}

p->doSomething();
...
p->doSomethingElse();

站点 cplusplus.com 对语言的使用往往没有那么严格,尤其是相对于标准而言。它更喜欢本地语言,因此可能会造成混淆。

这尤其与 smart_ptr 到 "own" 的含义有关。 From the standard:

A shared_­ptr is said to be empty if it does not own a pointer.

这与shared_ptr对"ownership"的想法有关。从 shared_ptr 的角度来看,它 "owns" 是一个指针,如果它是用一个指针值创建的,它将在其上调用删除函子(给定或默认)。任何不以这种方式 "own" 指针的 shared_ptr 都是空的。

然而,这与人类的想法截然不同 "ownership"。人们可能会说,如果您向 shared_ptr 提供删除器,但实际上并没有 删除 对象或以其他方式释放它,那么 shared_ptr 不会t 真的拥有指针。

但就 shared_ptr(及其各种接口)而言,它拥有该指针,即使您给定的删除器不会对它做任何事情。

至于如何处理你的代码特定代码,最好使用unique_ptr并有条件地给它一个销毁的值:

void foo(const MyClass* p) {
   unique_ptr<MyClass> u_p;
   if (p == nullptr) {
       u_p = std::make_unique<MyClass>("some", "parameters");
       p = u_p.get();
   }

   p->doSomething();
   ...
   p->doSomethingElse();
   // p must not be destroyed, its memory is managed somewhere else
}