2009-04-29 21 views
28

¿Es posible declarar una variable en C++ sin instanciarla? Quiero hacer algo como esto:Declarar un objeto antes de inicializarlo en C++

Animal a; 
if(happyDay()) 
    a("puppies"); //constructor call 
else 
    a("toads"); 

Basially, sólo quiero a declarar el exterior de la condicional por lo que recibe el alcance adecuado.

¿Hay alguna manera de hacer esto sin utilizar punteros y asignar a en el montón? Tal vez algo inteligente con referencias?

+0

ver RAII (Raii) – newacct

+1

si es un/espacio de nombres de alcance global no estático, entonces vale la pena señalar que en realidad se puede declarar sin inicializarla: Animal extern a; ... Animal a (cosas); –

+0

@newacct: Un enlace ayudaría a https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii – spinkus

Respuesta

27

No se puede hacer esto directamente en C++ ya que el objeto se construye cuando se define con el constructor por defecto.

Se podría, sin embargo, ejecutar un constructor con parámetros, para empezar:

Animal a(getAppropriateString()); 

O en realidad se podría usar algo como el ?: operator para determinar la secuencia correcta. (Actualización: @Greg dio la sintaxis para esto. Vea esa respuesta)

+2

+1. Esta es la forma general de la solución: envolverla dentro de una función. (Como dices,?: A menudo hace el trabajo y es más conveniente cuando lo hace, pero escribir una función separada * siempre * funciona.) –

+0

Sin embargo, si tu constructor necesita tomar múltiples argumentos, ¿haces varias funciones, una? para cada argumento? – newacct

+0

Hay algunos estudios que muestran que es mejor no tener constructores con múltiples argumentos, sino crear con los valores predeterminados y luego usar setters. Dicho esto, sí, harías una función por argumento, o mejor aún, tienes una estructura provisional para representar los elementos cohesivos que constituyen los parámetros, si están relacionados. – Uri

-3

Sí, se puede hacer lo siguiente:

Animal a; 
if(happyDay()) 
    a = Animal("puppies"); 
else 
    a = Animal("toads"); 

que llamará a los constructores correctamente.

EDITAR: Olvidó una cosa ... Al declarar a, tendrá que llamar a un constructor todavía, si es un constructor que no hace nada, o aún inicializa los valores a lo que sea. Por lo tanto, este método crea dos objetos, uno en la inicialización y el otro dentro de la declaración if.

Una mejor manera sería la creación de una función init() de la clase, tales como:

Animal a; 
if(happyDay()) 
    a.init("puppies"); 
else 
    a.init("toads"); 

De esta manera sería más eficiente.

+2

¿Estás seguro de esto? Creo que esto invocará al constructor predeterminado y luego a un operador de asignación, por lo que perderá el antiguo a. – Uri

+0

Sí, al principio me olvidé del constructor inicial. Es por eso que generalmente pruebo mi código antes de publicarlo ... esta vez no ... – DeadHead

+3

Sí, pero eso supone que (1) Animal tiene un constructor por defecto accesible (puede no tener sentido tener un constructor predeterminado en algunos clases), (2) Animal tiene un operador de asignación (algunas clases no pueden asignarse por diseño) y (3) la construcción y asignación de Animal tiene el mismo efecto que construirlo directamente. – newacct

34

No puede declarar una variable sin llamar a un constructor. Sin embargo, en su ejemplo, usted podría hacer lo siguiente:

Animal a(happyDay() ? "puppies" : "toads"); 
16

No puede usar referencias aquí, ya que tan pronto como salga del alcance, la referencia apuntará a un objeto que ser eliminado

Realmente, usted tiene dos opciones aquí:

1- Ir con punteros:

Animal* a; 
if(happyDay()) 
    a = new Animal("puppies"); //constructor call 
else 
    a = new Animal("toads"); 

// ... 
delete a; 

2- Añadir un método Init a Animal:

class Animal 
{ 
public: 
    Animal(){} 
    void Init(const std::string& type) 
    { 
     m_type = type; 
    } 
private: 
    std:string m_type; 
}; 

Animal a; 
if(happyDay()) 
    a.Init("puppies"); 
else 
    a.Init("toads"); 

me gustaría ir personalmente con la opción 2.

+1

Y yo iría con la opción n. ° 1. ¡Gracias! – TranslucentCloud

15

Prefiero la respuesta de Greg, pero también podría hacer esto:

char *AnimalType; 
if(happyDay()) 
    AnimalType = "puppies"; 
else 
    AnimalType = "toads"; 
Animal a(AnimalType); 

Lo sugiero porque he trabajado en lugares donde el operador condicional estaba prohibido. (¡Suspiro!) Además, esto se puede ampliar más allá de dos alternativas muy fácilmente.

5

Además de la respuesta de Greg Hewgill, hay algunas otras opciones:

Levante el cuerpo principal del código en una función:

void body(Animal & a) { 
    ... 
} 

if(happyDay()) { 
    Animal a("puppies"); 
    body(a); 
} else { 
    Animal a("toad"); 
    body(a); 
} 

(ab) uso de colocación nueva:

struct AnimalDtor { 
    void *m_a; 
    AnimalDtor(void *a) : m_a(a) {} 
    ~AnimalDtor() { static_cast<Animal*>(m_a)->~Animal(); } 
}; 

char animal_buf[sizeof(Animal)]; // still stack allocated 

if(happyDay()) 
    new (animal_buf) Animal("puppies"); 
else 
    new (animal_buf) Animal("toad"); 

AnimalDtor dtor(animal_buf); // make sure the dtor still gets called 

Animal & a(*static_cast<Animal*>(static_cast<void*>(animal_buf)); 
... // carry on 
+0

¿Sabes si hay una manera de hacer que la nueva versión de colocación garantice la alineación correcta (Pre C++ 11)? – enobayram

8

Si desea evitar la recolección de basura, podría usar un puntero inteligente.

auto_ptr<Animal> p_a; 
if (happyDay()) 
    p_a.reset(new Animal("puppies")); 
else 
    p_a.reset(new Animal("toads")); 

// do stuff with p_a-> whatever. When p_a goes out of scope, it's deleted. 

Si aún desea utilizar la. sintaxis en lugar de ->, puede hacerlo después del código anterior:

Animal& a = *p_a; 

// do stuff with a. whatever 
+0

Esto necesita ser cambiado a auto_ptr p_a (animal nuevo); de lo contrario, auto_ptr solo tiene un puntero nulo. Aunque me gusta la segunda idea, ya que no la copia, pero debes tener en cuenta que la vida está en ese ámbito. –

+2

@NathanAdams, un auto_ptr inicializado con nulo está bien aquí, será "cachorros" o "sapos" más tarde. Tener un "Animal nuevo" adicional es redundante. – fxam

0

Lo mejor que se puede hacer es utilizar el puntero.

Animal a*; 
if(happyDay()) 
    a = new Animal("puppies"); //constructor call 
else 
    a = new Animal("toads"); 
Cuestiones relacionadas