2009-04-29 15 views
28

Est-il possible de déclarer une variable en C++ sans l'instancier? Je veux faire quelque chose comme ceci:Déclarer un objet avant de l'initialiser en C++

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

Basially, je veux juste déclarer en dehors du conditionnel il obtient le champ droit.

Y at-il un moyen de le faire sans utiliser de pointeurs et d'affecter a sur le tas? Peut-être quelque chose d'intelligent avec des références?

+0

voir RAII (acquisition de ressources est l'initialisation) – newacct

+1

si elle est globale/espace-champ non statique, alors il vaut la peine de noter que vous pouvez réellement déclarer sans l'initialiser: Animal extern a; ... Animal a (substance); –

+0

@newacct: Un lien aiderait https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii – spinkus

Répondre

27

Vous ne pouvez pas le faire directement en C++ puisque l'objet est construit lorsque vous définissez avec le constructeur par défaut.

Vous pouvez cependant exécuter un constructeur paramétrés pour commencer:

Animal a(getAppropriateString()); 

Ou vous pouvez réellement utiliser quelque chose comme le ?: operator pour déterminer la chaîne correcte. (Mise à jour: @Greg a donné la syntaxe pour cela.Voir cette réponse)

+2

+1. C'est la forme générale de la solution - envelopper dans une fonction. (Comme vous le dites,?: Fait souvent le travail et est plus pratique quand c'est le cas, mais écrire une fonction séparée fonctionnera * toujours *.) –

+0

Cependant, si votre constructeur a besoin de prendre plusieurs arguments, faites-vous plusieurs fonctions, une pour chaque argument? – newacct

+0

Il y a quelques études qui montrent qu'il vaut mieux ne pas avoir de constructeurs avec plusieurs arguments mais plutôt de créer avec default et ensuite utiliser des setters. Cela étant dit, oui, vous feriez une fonction par argument, ou mieux encore, vous auriez une structure intérimaire pour représenter les éléments cohésifs qui constituent les paramètres, s'ils sont liés. – Uri

-3

Oui, vous pouvez effectuer les opérations suivantes:

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

qui appellera les constructeurs correctement.

EDIT: Vous avez oublié une chose ... Lors de la déclaration d'un, vous devrez appeler un constructeur, qu'il s'agisse d'un constructeur qui ne fait rien, ou encore initialise les valeurs à tout. Cette méthode crée donc deux objets, l'un à l'initialisation et l'autre à l'intérieur de l'instruction if.

Une meilleure façon serait de créer une fonction init() de la classe, tels que:

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

De cette façon, serait plus efficace.

+2

Etes-vous sûr de cela? Je pense que cela va invoquer le constructeur par défaut, puis un opérateur d'affectation, de sorte que vous perdrez l'ancien a. – Uri

+0

Ouais, j'ai d'abord oublié le constructeur initial. C'est pourquoi j'ai l'habitude de tester mon code avant de l'afficher ... pas cette fois ... – DeadHead

+3

Oui, mais cela suppose que (1) Animal a un constructeur par défaut accessible (il peut ne pas être logique d'avoir un constructeur par défaut dans classes), (2) Animal a un opérateur d'affectation (certaines classes ne peuvent pas être assignées par conception), et (3) la construction et l'assignation d'Animal ont le même effet que la construction directe. – newacct

34

Vous ne pouvez pas déclarer une variable sans appeler un constructeur. Cependant, dans votre exemple, vous pouvez effectuer les opérations suivantes:

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

Vous ne pouvez pas utiliser de références ici, puisque dès que vous sortir de la portée, la référence pointe vers un objet qui serait être supprimé.

Vraiment, vous avez deux choix ici:

1- Aller avec des pointeurs:

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

// ... 
delete a; 

2- Ajouter une méthode Init à 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"); 

Je vais personnellement avec l'option 2.

+1

Et j'irais avec l'option # 1. Merci! – TranslucentCloud

15

Je préfère la réponse de Greg, mais vous pouvez aussi le faire:

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

Je suggère cela parce que j'ai travaillé dans des endroits où l'opérateur conditionnel était interdit. (Soupir!) En outre, cela peut être étendu au-delà de deux alternatives très facilement.

5

En plus de la réponse de Greg Hewgill, il y a quelques autres options:

Soulevez le corps principal du code en fonction:

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

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

(Ab) Utiliser le placement nouveau:

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

Savez-vous s'il existe un moyen de faire en sorte que l'emplacement de la nouvelle version garantisse un alignement correct (Pre C++ 11)? – enobayram

8

Si vous voulez éviter la récupération de place, vous pouvez utiliser un pointeur intelligent.

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 vous souhaitez toujours utiliser le. syntaxe au lieu de ->, vous pouvez le faire après le code ci-dessus:

Animal& a = *p_a; 

// do stuff with a. whatever 
+0

Ceci doit être changé en auto_ptr p_a (nouvel animal); sinon auto_ptr a juste un pointeur nul. Bien que j'aime la deuxième idée car elle ne la copie pas - mais vous devez être conscient que la vie est dans cette portée. –

+2

@NathanAdams, un auto_ptr initialisé avec null est bien ici, il sera soit "chiots" ou "crapauds" plus tard. Avoir un "nouvel animal" supplémentaire est redondant. – fxam

0

La meilleure solution consiste à utiliser un pointeur.

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