В чем разница между пустым и нулевым std:: shared_ptr в С++?

Страница cplusplus.com shared_ptr вызывает различие между пустым std::shared_ptr и нулевым shared_ptr. На странице cppreference.com явно не указано различие, но в описании поведения std::shared_ptr используется как "пустое", так и сравнение с nullptr.

Есть ли разница между пустым и нулевым shared_ptr? Есть ли какой-либо прецедент для таких указателей со смешанным поведением? Имеет ли смысл не пустой номер null shared_ptr? Был бы когда-нибудь случай при обычном использовании (т.е. Если вы явно не построили его), где вы могли бы получить пустой, но не нулевой shared_ptr?

И если любой из этих ответов изменится, если вы используете версию Boost вместо версии С++ 11?

Ответ 1

Это странный угол поведения 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 - нет.) Вы можете построить пустой, но не пустой shared_ptr, но это будет не очень полезно. Пустой, но не нулевой shared_ptr по существу является не владеющим указателем, который может использоваться для выполнения некоторых странных вещей, таких как передавая указатель на что-то, выделенное в стеке, на функция, ожидающая shared_ptr (но я бы предложил перфорировать того, кто сначала помещает shared_ptr в API).

boost::shared_ptr также имеет этот конструктор, который они называют конструктором псевдонимов.

Ответ 2

Есть ли разница между пустым и null shared_ptr?

Пусто shared_ptr не имеет контрольного блока, а его счет использования считается равным 0. Копия пустого shared_ptr - еще одна пустая shared_ptr. Они оба являются отдельными shared_ptr, которые не имеют общего блока управления, потому что у него его нет. Пустой shared_ptr можно построить с помощью конструктора по умолчанию или с конструктором, который принимает nullptr.

Непустой null shared_ptr имеет блок управления, который может использоваться совместно с другими shared_ptr s. Копия непустого null shared_ptr - это shared_ptr, который имеет тот же блок управления, что и исходный shared_ptr, поэтому использование count не равно 0. Можно сказать, что все копии shared_ptr имеют один и тот же nullptr. Непустой null 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