2013-02-19 12 views
6

Sto riscrivendo del codice per eliminare le variabili globali e ho creato un distruttore di classe per la gestione delle operazioni di pulizia di alcune risorse di libreria di terze parti, ma sono preoccupato per un codice che inizializza un membro da un altro membro nell'elenco di inizializzazione della classe.Posso usare i membri della classe C++ inizializzati nell'elenco di inizializzazione, più avanti nell'elenco?

class MyPodofoDocument { 
public: 
    // generates pdf to stream 
    MyPodofoDocument(std::stringstream *pStringStream) 
     : device(pStringStream), document(&device) 
    { 
    } 
private: 
    PoDoFo::PdfOutputDevice device; 
    PoDoFo::PdfStreamedDocument document; 
    PoDoFo::PdfPainter painter; 
}; 

Il codice che utilizza questa classe non ha bisogno di vedere tutti i dettagli che vanno in utilizzando la libreria, ma il modo in cui li nascondo rende dipendenti da utilizzare per inizializzare i membri altri membri, prima che colpisca il blocco di codice attuale del costruttore, dove ha un puntatore valido.

Funziona in uno scheletro di unità di misura, quindi la mia domanda è fondamentalmente, "Va bene, portatile e sicuro?"

risposta

8

I membri sono inizializzati nell'ordine in cui sono dichiarati, dall'alto verso il basso

PoDoFo::PdfOutputDevice device; 
PoDoFo::PdfStreamedDocument document; 
PoDoFo::PdfPainter painter; 

quindi è sicuro da usare device per inizializzare document.

+0

Inoltre, è legale ottenere l'indirizzo o associare un riferimento a un membro ancora da costruire (ovvero è possibile passare un riferimento a un membro dichiarato in seguito se il destinatario non * usa * l'oggetto, ma memorizza solo il riferimento/puntatore). –

+0

È legale passare, ma semanticamente sbagliato, come si passa un puntatore a qualcosa che non è stato ancora costruito. –

+0

@AlexChamberlain: Niente di sbagliato, necessariamente, ma probabilmente ha bisogno di un doppio controllo. – GManNickG

4

Tipo di. Le regole sono che le variabili membro vengono inizializzate nell'ordine in cui sono dichiarate nella dichiarazione della classe.

Nel tuo caso, va bene poiché device è dichiarato prima di document.

Tuttavia, nel seguente caso, abbiamo un comportamento non definito, nonostante l'ordine della lista di inizializzazione.

class A { 
public: 
    A(int i) : b(i), a(b) { } 
private: 
    int a; 
    int b; 
} 
+1

Anche se è sicuro * fino a quando le dipendenze tra le variabili sono le stesse dell'ordine di dichiarazione *, IMHO non è una buona pratica. Tale codice dovrebbe essere evitato quando possibile, solo perché è fin troppo facile modificare erroneamente l'ordine di dichiarazione durante il refactoring della classe, innescando così UB come l'esempio di Alex lo mostra. In altre parole, questo è corretto ma molto fragile. – syam

+1

@syam: non ne sono troppo sicuro ... pensavo lo stesso, ma è molto facile far sì che il compilatore ti avverta se gli inizializzatori sono fuori uso (il compilatore richiamerà la tua attenzione sul potenziale UB) e in alcuni casi potrebbe essere utile fare riferimento a un membro diverso dello stesso tipo. –

+0

Non è utile solo per cose dello stesso tipo di corso; qualsiasi cosa in cui vi è una reale dipendenza tra le variabili, ma dove è necessario memorizzare anche le variabili. –

Problemi correlati