Declaring an object before initializing it in c++

C++ScopeDeclarationInstantiation

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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;

or with a smart pointer

#include <memory>

std::unique_ptr<Animal> a;
if( happyDay() ) 
    a = std::make_unique<Animal>( "puppies" );
else
    a = std::make_unique<Animal>( "toads" );

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.

Solution 2 - C++

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

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

Solution 3 - C++

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 that answer)

Solution 4 - C++

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.

Solution 5 - C++

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

Solution 6 - C++

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

Solution 7 - C++

The best work around is to use pointer.

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

Solution 8 - C++

Since c++17, there is now an overhead-free way to do this: std::optional. The code in this case would be:

#include <optional>

std::optional<Animal> a;
if(happyDay()) 
    a.emplace("puppies");
else
    a.emplace("toads");

Solution 9 - C++

You can also use std::move:

class Ball {
private:
        // This is initialized, but not as needed
        sf::Sprite ball;
public:
        Ball() {
                texture.loadFromFile("ball.png");
                // This is a local object, not the same as the class member.
                sf::Sprite ball2(texture);
                // move it
                this->ball=std::move(ball2);
        }
...

Solution 10 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionQuantum7View Question on Stackoverflow
Solution 1 - C++joceView Answer on Stackoverflow
Solution 2 - C++Greg HewgillView Answer on Stackoverflow
Solution 3 - C++UriView Answer on Stackoverflow
Solution 4 - C++Steve FallowsView Answer on Stackoverflow
Solution 5 - C++paquetpView Answer on Stackoverflow
Solution 6 - C++Logan CapaldoView Answer on Stackoverflow
Solution 7 - C++rkadeFRView Answer on Stackoverflow
Solution 8 - C++ChronialView Answer on Stackoverflow
Solution 9 - C++RodolfoAPView Answer on Stackoverflow
Solution 10 - C++DeadHeadView Answer on Stackoverflow