2012-11-07 11 views
5

Per tal caso:corretta inizializzazione di puntatori Smart Array

class A 
    { 
     //implementation 
    }; 

    class B 
    { 
    public: 
     B(); 
     ~B(); 
    private: 
     std::vector<std::shared_ptr<A>> _innerArray; 
    }; 

cosa devo fare nel B() per creare un oggetto con stato valido? Devo chiamare manualmente il costruttore predefinito per ogni oggetto A nella matrice? E devo fare qualcosa di speciale in ~B()? Se la classe B è un esempio di design scadente, sentiti libero di dire come migliorarlo. Grazie.

Modifica Quindi, ecco uno schema di ciò di cui ho davvero bisogno qui.

enter image description here

valori reali Quindi memorizzati solo in array di A e tutti gli altri oggetti sono per la memorizzazione di connessioni. L'esempio più semplice: A = punto, B = Linea (o curva) che passa per punti selezionati e C = un piano descritto da linee. Spero che la domanda sia più esatta.

+1

Controllo di integrità: è necessario il puntatore qui? E se sì, dovrebbe essere un puntatore condiviso? –

+0

Si suppone che la classe B abbia accesso ma non possiede alcuni oggetti memorizzati altrove. Non vedo l'uso di unique_ptr invece di shared_ptr che ha alcun senso qui, ma sono un noob ai puntatori intelligenti. –

+0

@Pavel Hai ragione: il tuo scenario preclude l'uso di 'unique_ptr' e richiede l'uso di puntatori. Tuttavia, se non si presume che 'B' diventi proprietario, prenderei in considerazione l'utilizzo di puntatori raw invece di puntatori condivisi. Dopotutto, questi ultimi denotano * proprietà condivisa * (ma forse è ciò che si richiede dopo tutto). –

risposta

4

Per creare un oggetto B in uno stato valido non è necessario eseguire altre operazioni. Non è nemmeno necessario dichiarare e implementare il costruttore e il distruttore per B. std::vector<std::shared_ptr<A>> che è un membro di B verrà inizializzato di default nel costruttore B, il che significa che non avrà ancora elementi in un contenitore. Sarà anche correttamente cancellato in ~B grazie a std::vector e std::shared_ptr distruttori.

D'altra parte se per esempio si desidera inizializzarlo in qualche modo (ad es.3 valori) è possibile utilizzare il costruttore std::initializer_list di std::initializer_list nell'elenco di inizializzazione del costruttore di B. Per esempio:

class B 
{ 
public: 
    B(): _innerArray{ std::make_shared<A>(), 
         std::make_shared<A>(), 
         std::make_shared<A>() } {} 
    ~B() {} 
private: 
    std::vector<std::shared_ptr<A>> _innerArray; 
}; 

Ricordate che std::make_shared utilizza l'inoltro in modo perfetto si passa A argomenti 's costruttore come gli argomenti della funzione e non l'oggetto classe stessa.

Rispondere alle vostre preoccupazioni sul design Vorrei incoraggiarvi a pensare prima alla proprietà esclusiva dei membri di un vettore prima di decidere di condividerli.

Sopra l'implementazione è più efficace per molti motivi. Prima di tutto rende più chiaro il tuo progetto su chi è responsabile per la durata di A s. Avanti std::unique_ptr è più veloce perché non richiede il conteggio dei riferimenti sicuro. E, ultimo ma non meno importante, non costa alcuna memoria aggiuntiva (rispetto al normale puntatore C) mentre lo std::shared_ptr può richiedere decine di byte (24-48) per memorizzare dati di stato condivisi che sono altamente inefficaci quando si opera su classi piccole. Questo è il motivo per cui io uso sempre std::unique_ptr come il mio primo puntatore intelligente e faccio solo un ripiego a std::shared_ptr quando è davvero necessario.

EDIT:

Rispondere tua modifica vorrei creare 3 contenitori di classi A, B, C. A seconda del fatto se avete bisogno loro di essere polimorfico o no avrei memorizzare sia i valori del genere (di tipo non polimorfici):

std::deque<A> as; 
std::deque<B> bs; 
std::deque<C> cs; 

o (tipi polimorfi):

std::vector<std::unique_ptr<A>> as; 
std::vector<std::unique_ptr<B>> bs; 
std::vector<std::unique_ptr<C>> cs; 

in questo ordine (as devono vivere più a lungo di bs e bs devono vivere più a lungo di cs). Quindi avrei solo std::vector<A*> all'interno della classe B e std::vector<B*> all'interno della classe C senza alcun utilizzo intelligente di puntatori.

Spero che questo aiuti.

EDIT:

Modificato std::vector al std::deque nel primo caso che permette riferimenti/puntatori ad elementi contenitori sopravvivono contenitori estensioni con push_back(). Tuttavia non sopravviveranno cancellando elementi, ordinamento o altre cose.

+0

Ottima risposta. Perfezionare. –

2

Se lo si fa, il vettore ha una dimensione di zero elementi, cioè i contenuti sono banalmente inizializzati correttamente. Se il vettore fosse di dimensioni positive (ad esempio dopo aver chiamato resize sul vettore), ciascuno degli elementi sarebbe stato inizializzato correttamente. Poiché gli elementi sono shared_ptr, verrà chiamato il costruttore predefinito di shared_ptr, il che significa che si otterrà un vettore di puntatori vuoti.

Se si desidera copiare il contenuto da un altro contenitore, utilizzare la versione iteratore del costruttore vettore:

B (SomeContainerTypeContainingSharedPointers container) 
: _innerArray (container.begin(), container.end()) { 
} 

Se non si desidera inizializzare il vettore da un contenitore, ma da qualche altra parte (ad esempio, crea gli oggetti al volo): scrivi tu stesso un tipo di iteratore di input (ad es. un "iteratore di fabbrica").

1

Il vettore è vuoto, quindi non devi fare nulla di speciale nel costruttore predefinito. E non devi fare nulla in B(). Il conteggio dei riferimenti di shared_ptrs verrà diminuito automaticamente quando viene chiamato il distruttore del vettore.

1

Bt predefinito std::shared_ptr<A> popolerà interno ptr con NULL. Per creare l'uso puntatore intelligente std::make_shared:

_innerArray.push_back(std::make_shared<A>(/*constructor params here*/)); 

Ma nel tuo esempio vettore è vuoto.

0

Il costruttore predefinito fa già tutto il necessario. Puoi anche lasciare B() senza alcuna perdita.