web-dev-qa-db-fra.com

Quelle est la différence entre un std :: shared_ptr vide et un null en C ++?

Le cplusplus.com shared_ptr page appelle une distinction entre un vide std::shared_ptr et un null shared_ptr. La page cppreference.com n'appelle pas explicitement la distinction, mais utilise à la fois "vide" et la comparaison avec nullptr dans sa description de std::shared_ptr comportement.

Y a-t-il une différence entre un vide et un nul shared_ptr? Existe-t-il un cas d'utilisation pour de tels pointeurs à comportement mixte? Est-ce qu'un null non vide shared_ptr même logique? Y aurait-il jamais un cas d'utilisation normale (c'est-à-dire si vous n'en avez pas explicitement construit un) où vous pourriez vous retrouver avec un _ vide mais non nul shared_ptr?

Et l'une de ces réponses change-t-elle si vous utilisez la version Boost au lieu de la version C++ 11?

75
R.M.

C'est un coin étrange du comportement de shared_ptr. Il a un constructeur qui vous permet de créer un shared_ptr Qui possède quelque chose et pointe vers autre chose:

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

Le shared_ptr Construit à l'aide de ce constructeur partage la propriété avec r, mais points à tout ce que ptr pointe vers (c'est-à-dire que l'appel de get() ou operator->() renverra ptr). C'est pratique dans les cas où ptr pointe vers un sous-objet (par exemple, un membre de données) de l'objet appartenant à r.

La page que vous avez liée appelle un shared_ptr Qui ne possède rien vide , et un shared_ptr Qui ne pointe vers rien (c'est-à-dire, dont get() == nullptr) null . ( Vide est utilisé dans ce sens par la norme; null isn ' t.) Vous pouvez construire un shared_ptr nul mais non vide, mais ce ne sera pas très utile. Un shared_ptr Vide mais non nul est essentiellement un pointeur non propriétaire, qui peut être utilisé pour faire des choses étranges comme passer un pointeur à quelque chose alloué sur la pile à une fonction attendant un shared_ptr (mais je suggère de frapper celui qui a mis shared_ptr Dans l'API en premier).

boost::shared_ptr Aussi a ce constructeur , qu'ils appellent le constructeur d'alias .

77
T.C.

Y a-t-il une différence entre un shared_ptr vide et un null shared_ptr?

shared_ptr Vide n'a pas de bloc de contrôle et son compte d'utilisation est considéré comme 0. La copie de shared_ptr Vide est un autre shared_ptr Vide. Ce sont deux shared_ptr Séparés qui ne partagent pas de bloc de contrôle commun car ils ne l'ont pas. shared_ptr Vide peut être construit avec le constructeur par défaut ou avec un constructeur qui prend nullptr.

Null non vide shared_ptr A un bloc de contrôle qui peut être partagé avec d'autres shared_ptr S. La copie du null non vide shared_ptr Est shared_ptr Qui partage le même bloc de contrôle que l'original shared_ptr Donc le nombre d'utilisation n'est pas 0. Il on peut dire que toutes les copies de shared_ptr partagent le même nullptr. Un null non vide shared_ptr Peut être construit avec un pointeur null de type objet (pas nullptr)

Voici un exemple:

#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;
}

Il génère:

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

6
anton_rh