std :: shared_ptr, который является пустым, но не нулевым - PullRequest
2 голосов
/ 28 апреля 2020

http://www.cplusplus.com/reference/memory/shared_ptr/ говорит

shared_ptr, которому не принадлежит ни один указатель, называется пустым shared_ptr. Shared_ptr, который указывает на отсутствие объекта, называется нулевым shared_ptr и не имеет разыменования. Обратите внимание, что пустой shared_ptr не обязательно является пустым shared_ptr, а нулевой shared_ptr не обязательно является пустым shared_ptr.

Могу ли я создать пустой std :: shared_ptr, то есть тот, который не владеть чем-либо, но не равным NULL, то есть содержит полезную нагрузку?

Вариант использования состоит в объединении «устаревшего» кода с современными указателями: учитывая, что синтаксис вызова 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 , есть другой умный указатель доступно или вообще отсутствует?

  • Однако я хочу добавить, что строка std::make_shared<MyClass>("some", "parameters") на самом деле является вызовом более сложной функции, которая создает shared_ptr. Я хотел бы сохранить эту функцию и использовать ее результат

Ответы [ 4 ]

3 голосов
/ 28 апреля 2020

То, что вы ищете, это пользовательский конструктор удаления 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
}
3 голосов
/ 28 апреля 2020

shared_ptr имеет средство удаления . Это полиморфная c процедура уничтожения базового объекта. У вас может быть пустое средство удаления:

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
}

Это, однако, приводит к плохому дизайну. Возвращаясь к проблемеXY: какова цель? Кто-то может передать вам объект в виде необработанного указателя (но может передать nullptr). Вы можете sh создать локальный файл на случай, если вместо реального объекта предоставлен nullptr. И вы, вероятно, wi sh, чтобы предотвратить утечку памяти. ОК.

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();
}
2 голосов
/ 28 апреля 2020

Могу ли я создать пустой std :: shared_ptr, то есть тот, который не владеет ничем, но не является нулевым, то есть содержит полезную нагрузку?

Это действительно возможно. Ссылка на странице правильна: пустой shared_ptr не обязательно равен null shared_ptr .

Такой общий указатель можно создать с помощью конструктора

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

Если вы передадите пустой нулевой общий указатель в качестве первого параметра и ненулевой указатель в качестве второго, затем вы получите пустой ненулевой общий указатель.

Конструктор обычно используется для указания на член или псевдоним принадлежащего объекта, но это также соответствует этому угловому случаю. Будьте особенно осторожны, чтобы не дать этому общему указателю просочиться за пределы области действия этой функции.

В вашем случае:

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

Или не упасть для проблемы XY, есть ли другой интеллектуальный указатель или нет вообще?

Другой подход - использовать общий указатель только для владения объектом по умолчанию и вызывать функции через * 1024. * вместо:

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();
1 голос
/ 28 апреля 2020

Сайт cplusplus.com, как правило, не так строго относится к использованию языка, особенно по отношению к стандарту. Он предпочитает более местный язык и поэтому может сбивать с толку.

Это особенно относится к тому, что означает для smart_ptr «владеть» чем-то. Из стандарта :

A shared_­ptr называется пустым, если у него нет указателя.

Это относится к shared_ptr идея "собственности". С точки зрения shared_ptr, он «владеет» указателем, если он создан со значением указателя, на которое он вызовет функтор удаления (заданный или по умолчанию). Любой shared_ptr, который не «владеет» указателем таким образом, пуст.

Однако это отличается от того, что человек считает «собственностью». Человек может сказать, что если вы предоставите удалитель для shared_ptr, который на самом деле не удалит объекта или не освободит его иным образом, то shared_ptr не действительно владеть указателем.

Но что касается shared_ptr (и его различных интерфейсов), он владеет этим указателем, даже если ваш удалитель не собирается ничего с ним делать.

Что касается обработки вашего кода, заданного c, было бы лучше использовать 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
}
...