2011-09-26 6 views
33

In C++, voglio definire un oggetto come un membro di una classe come questa:Definizione di un oggetto senza chiamare il suo costruttore in C++

Object myObject; 

Tuttavia facendo questo proverà a chiamare è costruttore senza parametri, che doesn esiste Comunque ho bisogno che il costruttore venga chiamato dopo che la classe contenente ha fatto dell'inizializzazione. Qualcosa come questo.

class Program 
{ 
public: 
    Object myObject; //Should not try to call the constructor or do any initializing 
    Program() 
    { 
     ... 

     //Now call the constructor 
     myObject = Object(...); 
    } 

} 
+4

Perché non utilizzare l'inizializzazione dinamica? auto_ptr/shared_ptr? – qehgt

+1

Ambito globale o membro di una classe? Il tuo codice non corrisponde alla tua domanda. –

+0

Cosa c'è di sbagliato chiamandolo costruttore predefinito e dopo l'inizializzazione lo hai impostato sul tuo oggetto che ti interessa, esattamente come fa il tuo codice? Oppure basta renderlo un puntatore: 'Object * myObj;' – Chad

risposta

20

Conservare un puntatore ad un Object piuttosto che un vero e proprio Object

così:

class Program 
{ 
public: 
    Object* myObject; // Will not try to call the constructor or do any initializing 
    Program() 
    { 
     //Do initialization 
     myObject = new Object(...); // Initialised now 
    } 

} 

Non dimenticare di delete nel distruttore. Il C++ moderno ti aiuta lì, in quanto potresti utilizzare un auto_ptr shared_ptr piuttosto che un puntatore di memoria non elaborato.

+4

Se si crea un distruttore, si dovrebbe obbedire alla regola del tre. – Sardathrion

+0

perché non 'std :: unique_ptr' anziché condiviso? –

+0

@RomanKruglov • un 'std :: unique_ptr' va bene, purché si anche' = DELETE' il costruttore di copia e l'operatore di assegnazione, o implementare loro di fare la cosa giusta. – Eljay

1

È possibile utilizzare un puntatore (o un puntatore intelligente) per farlo. Se non si utilizza un puntatore intelligente, assicurarsi che il codice venga liberato dalla memoria quando l'oggetto viene eliminato. Se si utilizza un puntatore intelligente, non preoccuparti.

class Program 
{ 
public: 
    Object * myObject; 
    Program(): 
     myObject(new Object()) 
    { 
    } 
    ~Program() 
    { 
     delete myObject; 
    } 
    // WARNING: Create copy constructor and = operator to obey rule of three. 
} 
16

Altri hanno inviato soluzioni utilizzando i puntatori prime, ma un puntatore intelligente sarebbe un'idea migliore:

class MyClass { 
    std::unique_ptr<Object> pObj; 
    // use boost::scoped_ptr for older compilers; std::unique_ptr is a C++0x feature 
public: 
    MyClass() { 
    // ... 
    pObj.reset(new Object(...)); 
    pObj->foo(); 
    } 
    // Don't need a destructor 
}; 

Questo evita la necessità di aggiungere un distruttore, e vieta implicitamente la copia (a meno che non si scrive il . operator= proprio e MyClass(const MyClass &)

Se si vuole evitare una dotazione mucchio a parte, questo può essere fatto con del aligned_storage spinta e nuova collocazione testato:.

template<typename T> 
class DelayedAlloc : boost::noncopyable { 
    boost::aligned_storage<sizeof(T)> storage; 
    bool valid; 
public: 
    T &get() { assert(valid); return *(T *)storage.address(); } 
    const T &get() const { assert(valid); return *(const T *)storage.address(); } 

    DelayedAlloc() { valid = false; } 

    // Note: Variadic templates require C++0x support 
    template<typename Args...> 
    void construct(Args&&... args) 
    { 
    assert(!valid); 
    new(storage.address()) T(std::forward<Args>(args)...); 
    valid = true; 
    } 

    void destruct() { 
    assert(valid); 
    valid = false; 
    get().~T(); 
    } 

    ~DelayedAlloc() { if (valid) destruct(); } 
}; 

class MyClass { 
    DelayedAlloc<Object> obj; 
public: 
    MyClass() { 
    // ... 
    obj.construct(...); 
    obj.get().foo(); 
    } 
} 

O, se Object è copiabile (o mobile), è possibile utilizzare boost::optional:

class MyClass { 
    boost::optional<Object> obj; 
public: 
    MyClass() { 
    // ... 
    obj = Object(...); 
    obj->foo(); 
    } 
}; 
+0

Utilizzando il primo suggerimento, ho 'Il testo ">" è inaspettato. Può darsi che questo token fosse inteso come terminatore di un elenco di argomenti modello ma il nome non è noto per essere un modello – Anonymous

5

Se si ha accesso per aumentare, v'è un oggetto a portata di mano che viene fornito chiamato boost::optional<> - questo evita il bisogno di allocazione dinamica, ad es

class foo 
{ 
    foo() // default std::string ctor is not called.. 
    { 
    bar = boost::in_place<std::string>("foo"); // using in place construction (avoid temporary) 
    } 
private: 
    boost::optional<std::string> bar; 
}; 
2

Si può anche essere in grado di riscrivere il codice per utilizzare l'elenco costruttore di inizializzazione, se è possibile spostare fuori l'altro di inizializzazione in costruttori:

class MyClass 
    { 
    MyObject myObject; // MyObject doesn't have a default constructor 
    public: 
    MyClass() 
     : /* Make sure that any other initialization needed goes before myObject in other initializers*/ 
     , myObject(/*non-default parameters go here*/) 
     { 
     ... 
     } 
    }; 

Devi essere consapevole del fatto che in seguito a tale pattern ti condurrà a un percorso in cui lavori molto nei costruttori, il che a sua volta porta a dover cogliere la gestione e la sicurezza delle eccezioni (dato che il modo canonico di restituire un errore da un costruttore è di generare un'eccezione).

Problemi correlati