2009-03-07 8 views
54

Jaka jest równoważna następującej:NULL wskaźnik z boostem :: shared_ptr?

std::vector<Foo*> vec; 
vec.push_back(NULL); 

gdy ma do czynienia z boost::shared_ptr? Czy to jest następujący kod?

std::vector< boost::shared_ptr<Foo> > vec; 
vec.push_back(boost::shared_ptr<Foo>()); 

Uwaga: mogę odepchnąć wiele takich obiektów. Czy powinienem zadeklarować gdzieś globalny statyczny obiekt nullPtr? W ten sposób musiałaby zostać zbudowana tylko jedna z nich:

boost::shared_ptr<Foo> nullPtr; 
+4

dobrą wiadomością od standardu przyszłorocznego C++: nie można napisać "vec.emplace_back();" i uzyskaj wskaźnik zerowy dopisz :) –

+7

Rozważ użycie 'boost :: ptr_vector', który wymaga mniej narzutów. – Philipp

Odpowiedz

0

Tak, zadeklaruj globalny statyczny wskaźnik zerowy.

4

Użytkownik może zadeklarować globalny numer nullPtr dla shared_ptr<Foo>. Ale jeśli zanieczyszczasz globalny obszar nazw, co nazwałbyś globalnym nullPtr dla shared_ptr<Bar>?

Zazwyczaj deklaruję wartość null ptr jako statyczną w klasie wskaźnika.

#include <boost\shared_ptr.hpp> 

class Foo; // forward decl 
typedef boost::shared_ptr<Foo> FooPtr; 
class Foo 
{ 
public: 
    static FooPtr Null; 
} 
... 
// define static in cpp file 
FooPtr Foo::Null; 
... 
// use Foo Null 
vec.push_back(Foo::Null); 

W ten sposób każda klasa ma statyczną wartość Null.

+1

Możesz uzyskać bardziej naturalną składnię, korzystając z szablonowego operatora konwersji w zmiennej globalnej. Zobacz moją odpowiedź ... :) –

52

Twoja sugestia (wywołanie konstruktora shared_ptr<T> bez argumentów) jest poprawna. (Wywołanie konstruktora o wartości 0 jest równoważne.) Nie sądzę, że byłoby to wolniejsze niż wywoływanie vec.push_back() z wcześniej istniejącym shared_ptr<T>, ponieważ konstrukcja jest wymagana w obu przypadkach (albo bezpośrednia konstrukcja, albo kopia).

Ale jeśli chcesz „ładniejszy” składni, można spróbować następujący kod:

class { 
public: 
    template<typename T> 
    operator shared_ptr<T>() { return shared_ptr<T>(); } 
} nullPtr; 

Ten deklaruje jeden globalny obiekt nullPtr, który umożliwia następujące naturalną składnię:

shared_ptr<int> pi(new int(42)); 
shared_ptr<SomeArbitraryType> psat(new SomeArbitraryType("foonly")); 

... 

pi = nullPtr; 
psat = nullPtr; 

Note jeśli użyjesz tego w wielu jednostkach tłumaczeniowych (plikach źródłowych), będziesz musiał nadać klasie nazwę (np. _shared_null_ptr_type), przenieść definicję obiektu nullPtr do oddzielnego pliku .cpp i dodać deklaracje extern wt he plik nagłówkowy, w którym zdefiniowana jest klasa.

+0

miły koleś. musiałem dać +1: p, ale zanotuj odpowiedź tutaj: http://stackoverflow.com/questions/395685/how-do-you-choose-between-a-singleton-and-an-unnamed-class/395738# 395738 –

+0

Dzięki chłopaki. :) Patrząc na post na litb sugeruje, że klasa powinna być naprawdę nazwana. –

+0

niektóre głupie nazwy: nullPtrT, NullPtrType, nullPtr_t. cokolwiek :) znalazłem, że dołączają _t, gdy istnieje tylko jedno wystąpienie czegoś (np. nothrow_t i nullptr_t). –

17

Cóż, to jest legalne:

shared_ptr<Foo> foo; /* don't assign */ 

I w tym stanie, nie wskazują na nic.Można nawet sprawdzić tę właściwość:

if (foo) { 
    // it points to something 
} else { 
    // no it doesn't 
} 

Więc dlaczego tego nie robić:

std::vector < shared_ptr<Foo> > vec; 
vec.push_back (shared_ptr<Foo>); // push an unassigned one 
1

Oto coś co moim zdaniem jest nieco prostsze i działa dobrze

(należy pamiętać, że typedef jest twój przyjaciel):

#include <cstdlib> 
#include <vector> 
#include <iostream> 
#include <boost/shared_ptr.hpp> 

typedef boost::shared_ptr< std::vector<char> > CharVecHandle; 

inline CharVecHandle newCharVec(std::vector<char>::size_type size) { 
    return CharVecHandle(new std::vector<char>(size)); 
} 

inline CharVecHandle newCharVec(void) { 
    return CharVecHandle(); 
} 

int main (void) 
{ 
    CharVecHandle cvh = newCharVec(); 

    if (cvh == NULL) 
     std::cout << "It's NULL" << std::endl; 
    else 
     std::cout << "It's not NULL" << std::endl; 

    std::vector<CharVecHandle> cvh_vec; 

    cvh_vec.push_back(newCharVec(64)); 
    cvh_vec.push_back(newCharVec()); 

    // or call the NULL constructor directly 
    cvh_vec.push_back(CharVecHandle()); 

    return EXIT_SUCCESS; 
} 
+0

Oczywiście, 'typedef' jest ładniejszy do oglądania niż' boost :: shared_ptr ', a OP wydaje się dotyczyć tylko jednego typu 'Foo', w którym to przypadku' typedef' pasuje ładnie. Jeśli chcesz uniknąć rozprzestrzeniania 'typedef's dla różnych typów inteligentnych wskaźników, moje podejście jest lepsze, myślę. Nie jesteś jednak pewien, co próbujesz zademonstrować za pomocą funkcji 'newCharVec ', czy możesz to wyjaśnić? –

+0

@j_random_hacker: Celem funkcji 'newCharVec' jest to samo co typedef, wygoda i spójność. Mniej znaków do wpisania i spójności w tworzeniu obiektów rzeczywistych i obiektów NULL. Chodzi o to, że nawet gdybym używał metody szablonowej, prawdopodobnie utworzyłbym typedef. Pochodząca z tła 'C', gdy widzę zbyt skomplikowaną deklarację typu, moim pierwszym instynktem jest wpisanie jej. Biorąc to pod uwagę, nie jestem pewien, czy istnieje naprawdę obiektywny powód do wyboru jednej lub drugiej metody, może to być kwestia stylu lub osobistych preferencji. –

+0

Cóż, jednym z powodów preferowania mojego podejścia jest to, że ponieważ moje 'nullPtr' jest agnostyczne typu, będzie działało w szablonach funkcji podczas manipulowania zmiennymi, których typ nie jest znany, a twoje nie. Na przykład. Mogę napisać szablon funkcji 'template push_a_null_ptr (wektor > & v) {v.push_back (nullPtr); } ', które będzie działać dla wszystkich typów' T'. Przyznaję, że to nie jest wielka przewaga, ale nie widzę żadnych minusów. –

9

W C++ 0x, można po prostu przekonwertować z nullptr do std::shared_ptr:

std::vector< boost::shared_ptr<Foo> > vec; 
vec.push_back(nullptr); 
Powiązane problemy