2009-03-07 11 views
54

Qual è l'equivalente al seguente:Puntatore NULL con boost :: shared_ptr?

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

quando si tratta di boost::shared_ptr? È il seguente codice?

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

Nota: è possibile respingere un sacco di tali oggetti. Devo dichiarare un oggetto statico globale nullPtr da qualche parte? In questo modo solo uno di essi dovrebbe essere costruito:

boost::shared_ptr<Foo> nullPtr; 
+4

buone notizie dallo standard della prossima C++: lì, si può scrivere "vec.emplace_back();" e ottieni un puntatore nullo append :) –

+7

Considera l'uso di 'boost :: ptr_vector' che richiede meno overhead. – Philipp

risposta

0

Sì, dichiarare un puntatore nullo statico globale.

4

È possibile dichiarare un valore globale nullPtr per shared_ptr<Foo>. Ma se inquini lo spazio dei nomi globale, come chiameresti lo nullPtr globale per shared_ptr<Bar>?

In genere dichiaro il ptr null come statico nella classe del puntatore.

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

In questo modo ogni classe ha un valore Null statico.

+1

È possibile ottenere una sintassi più naturale utilizzando un operatore di conversione basato su modelli su una variabile globale. Vedi la mia risposta ... :) –

52

Il tuo suggerimento (chiamare il costruttore shared_ptr<T> senza argomenti) è corretto. (Chiamare il costruttore con il valore 0 è equivalente.) Non penso che sarebbe più lento di chiamare vec.push_back() con un valore preesistente shared_ptr<T>, poiché la costruzione è necessaria in entrambi i casi (costruzione diretta o copia-costruzione).

Ma se si vuole sintassi "più bello", si potrebbe provare il seguente codice:

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

Questo dichiara un singolo oggetto globale nullPtr, che consente la seguente sintassi naturale:

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

... 

pi = nullPtr; 
psat = nullPtr; 

Nota che se lo si utilizza in più unità di traduzione (file di origine), è necessario assegnare un nome alla classe (ad esempio _shared_null_ptr_type), spostare la definizione dell'oggetto nullPtr in un file .cpp separato e aggiungere le dichiarazioni extern in t lui file di intestazione in cui è definita la classe.

+0

simpatica idea. ho dovuto fare +1 su questo: p ma nota la risposta qui http://stackoverflow.com/questions/395685/how-do-you-choose-between-a-singleton-and-an-unnamed-class/395738# 395738 –

+0

Grazie ragazzi. :) Guardare il post di litb suggerisce che la classe dovrebbe essere davvero intitolata. –

+0

alcuni nomi sciocchi: nullPtrT, NullPtrType, nullPtr_t. qualunque cosa :) ho trovato che aggiungono _t quando c'è solo una istanza di qualcosa (come, nothrow_t e nullptr_t). –

17

Beh, questo è legale:

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

E in questo stato, non punta a nulla.Si può anche testare questa proprietà:

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

Quindi, perché non fare questo:

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

Ecco qualcosa che credo sia un po 'più semplice e funziona bene

(ricordiamo che typedef è la vostra amico):

#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

Certo, un 'typedef' è più bello da vedere di' boost :: shared_ptr ', e l'OP sembra riguardare solo un singolo tipo 'Foo', nel qual caso' typedef' si adatta bene. Se vuoi evitare una proliferazione di 'typedef's per diversi tipi di puntatori intelligenti, il mio approccio è migliore, penso. Non sei sicuro di cosa stai provando a dimostrare con le tue funzioni 'newCharVec()', puoi spiegare? –

+0

@j_random_hacker: Lo scopo delle funzioni 'newCharVec' è lo stesso di typedef, convenienza e coerenza. Meno caratteri da digitare e coerenza nella creazione di oggetti reali e oggetti NULL. Il fatto è che, anche se stavo usando il metodo template, probabilmente creerei ancora un typedef. Provenendo da uno sfondo 'C' ogni volta che vedo una dichiarazione di tipo eccessivamente complicata, il mio primo istinto è quello di digitarlo. Tenendo conto di ciò, non sono sicuro che ci sia davvero una ragione oggettiva per scegliere un metodo o l'altro, potrebbe essere una questione di stile o preferenza personale. –

+0

Beh, uno dei motivi per preferire il mio approccio è che, poiché il mio 'nullPtr' è di tipo agnostico, funzionerà nei template di funzioni quando manipolerà variabili il cui tipo è sconosciuto, mentre il tuo no. Per esempio. Posso scrivere un modello di funzione 'modello push_a_null_ptr (vector > & v) {v.push_back (nullPtr); } ', che funzionerà per tutti i tipi' T'. Ammetto che non è un enorme vantaggio, ma non vedo nessun aspetto negativo. –

9

In C++ 0x, è sufficiente convertire da nullptr-std::shared_ptr:

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