У чому різниця між порожнім та нульовим std :: shared_ptr у C ++?


80

Cplusplus.com shared_ptrсторінка волає відмінність між порожнім std::shared_ptr і в нулі shared_ptr . Сторінка cppreference.com явно не називає відмінності, але використовує як "порожній", так і порівняння для nullptrопису std::shared_ptrповедінки.

Чи існує різниця між порожнім і нульовим значенням shared_ptr? Чи існує варіант використання таких покажчиків із змішаною поведінкою? Чи shared_ptrмає сенс не пуста нуль ? Чи коли-небудь був би випадок у звичайному використанні (тобто, якщо ви не створили його явно), коли ви могли б отримати в результаті порожній, але не нульовий shared_ptr?

І чи змінюється будь-яка з цих відповідей, якщо ви використовуєте версію Boost замість версії C ++ 11?

Відповіді:


80

Це дивний куточок shared_ptrповедінки. Він має конструктор, який дозволяє вам створити, shared_ptrщо є власником чогось і вказує на щось інше:

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

shared_ptrПобудовано з допомогою цього конструктора акцій в власності з r, але указует на будь-які ptrточки до (тобто, покликання get()або operator->()повернуся ptr). Це зручно для випадків, коли ptrвказує на суб'єкт (наприклад, елемент даних) об'єкта, що належить r.

На сторінці ви пов'язані дзвінків в shared_ptrтому , що не володіє нічого порожнім , і shared_ptrщо вказує на нічого (тобто, чий get() == nullptr) нуль . ( Порожній використовується в цьому сенсі стандартом; null - ні.) Ви можете створити null-but-not-empty shared_ptr, але це не буде дуже корисним. Порожній, але не нульовий shared_ptr, по суті, не є власником покажчика, який може використовуватися для здійснення деяких дивних речей, таких як передача вказівника на щось, виділене в стеці, до функції, яка очікуєshared_ptr (але я пропоную пробити того, хто вклав shared_ptrвсередину API).

boost::shared_ptrтакож має цей конструктор , який вони називають конструктором згладжування .


8
Варто зазначити: C ++ 11 § 20.7.2.2.1 (p16) "Примітка. Цей конструктор дозволяє створювати порожній shared_ptrекземпляр із збереженим показником, що не є НУЛЬНИМ." Також варто згадати попередню примітку (p15), "Щоб уникнути можливості звисання вказівника, користувач цього конструктора повинен забезпечити збереження pчинності принаймні до тих пір, поки група власника не rбуде знищена." Дійсно рідко використовувана конструкція.
WhozCraig

@Cubbi shared_ptrякого get()повертається nullptr робить порівняння рівним nullptrнезалежно від того, чи має воно що - небудь.
TC

3
Нульові, але непусті shared_ptrs можуть бути корисними, щоб переконатися, що якась функція виконується, коли всі власні вказівники вичерпані (навіть у випадку винятку!). Не впевнений, чи існує зараз спеціальний клас для цього.
виправлення

@coldfix Що може зробити нуль-але- shared_ptrне-порожній, а що не-нуль-і-не-порожній shared_ptrне може?
TC

2
Конструктор псевдонімів бере свій початок від Bloomberg і був запропонований для стандарту ще до його реалізації в Boost (див. N1851 ). Я віддаю перевагу терміну стандарту "володіє правами власності r" перед фразою "володіє тим, що rволодіє"
Джонатан Уейклі

9

Чи існує різниця між порожнім та нульовим shared_ptr?

Empty shared_ptrне має блоку управління, і його кількість вважається рівною 0. Копія empty shared_ptr- це інша порожняshared_ptr . Вони обидва є окремими shared_ptr, які не мають спільного блоку керування, оскільки їх немає. Порожній shared_ptrможе бути побудований за допомогою конструктора за замовчуванням або з конструктором, який бере nullptr.

Непусте значення null shared_ptrмає блок управління, яким можна поділитися з іншими shared_ptrs. Копія не пустого нуля shared_ptrполягає shared_ptrв shared_ptrтому, що вона використовує той самий блок керування, що і оригінал, тому кількість використання не дорівнює 0. Можна сказати, що всі копії shared_ptrспільно використовують однаковіnullptr . Непорожній нуль shared_ptrможе бути побудований за допомогою нульового вказівника типу об'єкта (не nullptr)

Ось приклад:

#include <iostream>
#include <memory>

int main()
{
    std::cout << "std::shared_ptr<int> ptr1:" << std::endl;
    {
        std::shared_ptr<int> ptr1;
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    std::cout << "std::shared_ptr<int> ptr1(nullptr):" << std::endl;
    {
        std::shared_ptr<int> ptr1(nullptr);
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    std::cout << "std::shared_ptr<int> ptr1(static_cast<int*>(nullptr))" << std::endl;
    {
        std::shared_ptr<int> ptr1(static_cast<int*>(nullptr));
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    return 0;
}

Виводить:

std::shared_ptr<int> ptr1:
    use count before copying ptr: 0
    use count  after copying ptr: 0
    ptr1 is null

std::shared_ptr<int> ptr1(nullptr):
    use count before copying ptr: 0
    use count  after copying ptr: 0
    ptr1 is null

std::shared_ptr<int> ptr1(static_cast<int*>(nullptr))
    use count before copying ptr: 1
    use count  after copying ptr: 2
    ptr1 is null

http://coliru.stacked-crooked.com/a/54f59730905ed2ff


1
Я думаю, що це кращі відповіді, чому нам доводиться перевіряти наявність null у користувацькому видалювачі shared_ptr. Чи є сенс перевіряти наявність nullptr у користувацькому видалювачі shared_ptr?
David Lee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.