2011-09-26 7 views
33

En C++, quiero definir un objeto como un miembro de una clase como esta:Definición de un objeto sin llamar a su constructor en C++

Object myObject; 

Sin embargo hacer esto, intentará llamar a su constructor sin parámetros, que doesn existo Sin embargo, necesito que se llame al constructor después de que la clase contenedora haya realizado algunas inicializaciones. Algo como esto.

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

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

} 
+4

¿Por qué no utilizar la inicialización dinámica? auto_ptr/shared_ptr? – qehgt

+1

Alcance global o miembro de una clase? Tu código no coincide con tu pregunta. –

+0

¿Qué tiene de malo llamar al constructor predeterminado y después de la inicialización configurarlo en el objeto que le interesa, exactamente como lo hace su código? O simplemente conviértalo en un puntero: 'Object * myObj;' – Chad

Respuesta

20

tienda un puntero a un Object en lugar de un verdadero Object

así:

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

} 

No se olvide de delete en el destructor. El C++ moderno lo ayuda allí, en el sentido de que podría usar auto_ptr shared_ptr en lugar de un puntero de memoria sin formato.

+4

Si crea un destructor, debe obedecer la regla de tres. – Sardathrion

+0

¿por qué no 'std :: unique_ptr' en lugar de compartir? –

+0

@RomanKruglov • a 'std :: unique_ptr' está bien, siempre y cuando' 'delete' el constructor de copias y el operador de asignación, o los implemente para hacer lo correcto. – Eljay

1

Puede usar un puntero (o un puntero inteligente) para hacerlo. Si no usa un puntero inteligente, asegúrese de que su código libere memoria cuando se elimine el objeto. Si usa un puntero inteligente, no se preocupe.

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

Otros han publicado las soluciones utilizando punteros primas, sino un puntero inteligente sería una mejor idea:

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 
}; 

Esto evita la necesidad de añadir un destructor, e implícitamente prohíbe el copiado (a menos que usted escribe su . operator= propia y MyClass(const MyClass &)

Si se quiere evitar una asignación del montón por separado, esto se puede hacer con aligned_storage de impulso y la colocación de nuevo no probado:.

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, si Object es copiable (o móvil), puede utilizar boost::optional:

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

Usando su primera sugerencia, obtengo 'El texto"> "es inesperado. Es posible que este token haya sido diseñado como un terminador de la lista de argumentos de la plantilla, pero se desconoce que el nombre sea una plantilla. – Anonymous

5

Si usted tiene acceso a impulsar, no es un objeto útil que se proporciona llama boost::optional<> - esto evita el necesidad de asignación dinámica, por ejemplo

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

También puede ser capaz de volver a escribir el código para utilizar la lista de inicialización del constructor, si se puede mover fuera de la otra inicialización en constructores:

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*/) 
     { 
     ... 
     } 
    }; 

Necesita ser consciente de que como consecuencia de una El patrón te llevará a un camino en el que harás mucho trabajo en constructores, lo que a su vez lleva a necesitar comprender el manejo y la seguridad de excepciones (ya que la forma canónica de devolver un error a un constructor es lanzar una excepción).

Cuestiones relacionadas