2011-07-28 6 views
9

W Scott Meyers na Efektywne C++, poz 18 Bądź interfejsy łatwy w użyciu prawidłowo i trudny w użyciu nieprawidłowo, wspomniał zerowej shared_ptr:Dlaczego potrzebna jest wartość null shared_ptr i jak można jej użyć?

std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0), getRidOfInvestment) 

i operację przypisania vogue

pInv = ...  //make retVal point to the correct object 

W takim przypadku może być konieczne utworzenie wartości null shared_ptr i wykonanie przypisania później? Dlaczego nie po prostu utworzyć shared_ptr, gdy masz zasoby (nieprzetworzony wskaźnik)?

Od Scott Meyers nie wykazują kompletny zadanie w poprzednim przykładzie, myślałem operator przypisać shared_ptr jest przeciążony, że można to zrobić:

pInv = new Investment; // pInv will take charge of the pointer 
          // but meanwhile keep the delete function it already had 

ale próbowałem z doładowania „s wdrażania go nie działa w ten sposób. Więc jaki jest sens posiadania wartości null shared_ptr?

Jestem prawie pewna, że ​​czegoś tu brakuje, ktoś mi z tego nie pomógł.

ps. więcej o inicjalizacji i przyznaniem shared_ptr

#include <boost/shared_ptr.hpp> 

int main(int argc, char *argv[]) 
{ 
    boost::shared_ptr<int> ptr1(new int); 
    boost::shared_ptr<int> ptr2; 
    ptr2.reset(new int); 
    boost::shared_ptr<int> ptr3 = new int; 

    return 0; 
} 

ten przykład nie mogą być skompilowane przez g ++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 oraz najnowszej doładowania:

sptr.cpp: In function ‘int main(int, char**)’: 
sptr.cpp:8:39: error: conversion from ‘int*’ to non-scalar type ‘boost::shared_ptr<int>’ requested 
+0

Starałem się zrozumieć to tutaj: http://ideone.com/BUjOwZ. Nie mogłem wymyślić wyjaśnienia. –

+0

Jak zauważyli inni, istnieje wiele powodów, dla których warto mieć wartość null shared_ptr, tak jak masz zerowe surowe wskazówki. Ale prawdziwą tajemnicą jest to, dlaczego Scott poczuł potrzebę posiadania niestandardowego deletera. –

Odpowiedz

19

Nie trzeba używać tego hacka, aby uzyskać wartość zerową (pustą) shared_ptr. Wystarczy użyć domyślnego konstruktora:

std::shared_ptr<Investment> pInv; // starts null 

Aby przypisać wskaźnik do shared_ptr, albo zrobić to w czasie budowy:

std::shared_ptr<Investment> pInt(new Investment); 
// not allowed due to explicit annotation on constructor: 
// std::shared_ptr<Investment> pInt = new Investment; 

lub użyj .reset() funkcję:

pInt.reset(new Investment); 

jest to możliwe że autor tego artykułu mógł mieć na celu dostarczenie niestandardowego deletera (getRidOfInvestment). Jednak funkcja deletera jest resetowana po wywołaniu .reset() lub w przeciwnym razie zmieniany jest wewnętrzny wskaźnik. Jeśli chcesz niestandardowy deleter, musisz go przekazać do .reset() po utworzeniu shared_ptr.

Jeden wzór może chcesz użyć, aby to bardziej niezawodny jest funkcja tworzenia niestandardowych:

class Investment { 
protected: 
    Investment(); 
    // ... 
public: 
    static shared_ptr<Investment> create(); 
}; 

shared_ptr<Investment> Investment::create() { 
    return shared_ptr<Investment>(new Investment, getRidOfInvestment); 
} 

Później:

shared_ptr<Investment> pInv = Investment::create(); 

Zapewnia to zawsze będziesz miał prawidłową funkcję destructor dołączony do shared_ptr s utworzone z Investment s.

+0

Przykładowe dane w kwerencie, powodem zerowego wskaźnika w wywołaniu konstruktora jest użycie niestandardowego narzędzia do usuwania. Konstruktor inteligentnego wskaźnika nie ma wersji, która pobiera tylko niestandardowy deleter, stąd potrzeba "hacka". – diverscuba23

+0

Dobra uwaga, dodałem trochę, żeby to omówić. – bdonlan

+1

"std :: shared_ptr pInt = nowa inwestycja;" nie można skompilować – zhanwu

2

Istnieje wiele powodów, dla których obiekty mogą być domyślnie skonstruowane.Przede wszystkim chciałbyś, aby wskaźnik inteligentny był jak najbardziej zbliżony do wskaźnika surowego, a ponieważ możesz powiedzieć int * p; (i uzyskać niezdefiniowany, niezainicjowany wskaźnik), możesz także powiedzieć shared_ptr<int> p; i uzyskać wskaźnik, który nie jest punkt w dowolnym miejscu (ale można go przetestować przy pomocy !).

Jednym z najbardziej istotnych powodów jest to, że możesz wykonać pojemniki z shared_ptr s, i możesz wypełnić kontenery bez przypisywania punktualnych tam i tam.

6

To jest ten sam powód, dla którego mamy zerowy surowy wskaźnik - np.

że masz:

typedef std::tr1::shared_ptr<Investment> InvestmentPtr; 
map<key,InvestmentPtr> portfolio; 
... 
get(mykey) { 
    iterator it = portfolio.find(mykey); 
    if (it == portfolio.end()) 
    return InvestmentPtr(); 
    else 
    return it->second; 
    } 
} 

Pozwala to, aby to zrobić:

InvestmentPtr p = get(key); 
if (p) ... 
Powiązane problemy