views:

807

answers:

7

Is it possible to declare a variable in c++ without instantiating it? I want to do something like this:

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

Basially, I just want to declare a outside of the conditional so it gets the right scope.

Is there any way to do this without using pointers and allocating a on the heap? Maybe something clever with references?

A: 

Yes, you can do do the following:

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

That will call the constructors properly.

EDIT: Forgot one thing... When declaring a, you'll have to call a constructor still, whether it be a constructor that does nothing, or still initializes the values to whatever. This method therefore creates two objects, one at initialization and the one inside the if statement.

A better way would be to create an init() function of the class, such as:

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

This way would be more efficient.

DeadHead
Are you sure about this? I think this will invoke default constructor and then an assignment operator, so you'll lose the old a.
Uri
Yeah, I forgot about the initial constructor at first. That's why I usually test my code before posting it... didn't this time...
DeadHead
Yea, but that assumes that (1) Animal has an accessible default constructor (it may not make sense to have a default constructor in some classes), (2) Animal has an assignment operator (some classes can't be assigned by design), and (3) constructing and assigning Animal has the same effect as constructing it directly.
newacct
Using an init() method is probably a bad idea as that implies the object is not valid after the constructor is finished.
Martin York
@Martin, I don't see what's wrong with that... as it is in the OP's example, the object shouldn't be used until a value is set to it, through whatever method he is going to use.
DeadHead
@DeadHead: Careful with your syntax. Your Animal a(); line is a function prototype, not a variable declaration. Animal a; is what you mean (default constructor is still invoked).
Drew Hall
+24  A: 

You can't declare a variable without calling a constructor. However, in your example you could do the following:

Animal a(happyDay() ? "puppies" : "toads");
Greg Hewgill
+11  A: 

You can't do this directly in C++ since the object is constructed when you define it with the default constructor.

You could, however, run a parameterized constructor to begin with:

Animal a(getAppropriateString());

Or you could actually use something like the ?: operator to determine the correct string. (Update: Greg gave the syntax for this, see above)

Uri
+1. This is the general form of the solution -- wrap it inside a function. (As you say, ?: often does the job and is more convenient when it does, but writing a separate function will *always* work.)
j_random_hacker
However, if your constructor needs to take multiple arguments, do you make multiple functions, one for each argument?
newacct
There are some studies that show that it is better to not have constructors with multiple arguments but rather to create with default and then use setters. That being said, yes, you'd do a function per argument, or even better, have an interim struct to represent the cohesive elements that constitute the parameters, if they are related.
Uri
+1  A: 

You can't use references here, since as soon as you'd get out of the scope, the reference would point to a object that would be deleted.

Really, you have two choices here:

1- Go with pointers:

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

// ...
delete a;

2- Add an Init method to 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" );

I'd personally go with option 2.

Joce
+10  A: 

I prefer Greg's answer, but you could also do this:

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

I suggest this because I've worked places where the conditional operator was forbidden. (Sigh!) Also, this can be expanded beyond two alternatives very easily.

Steve Fallows
+1  A: 

In addition to Greg Hewgill's answer, there are a few other options:

Lift out the main body of the code into a function:

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

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

(Ab)Use placement new:

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
Logan Capaldo
+6  A: 

If you want to avoid garbage collection - you could use a smart pointer.

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.

If you still want to use the . syntax instead of ->, you can do this after the code above:

Animal& a = *p_a;

// do stuff with a. whatever
paquetp