在 c + + 中初始化对象之前声明一个对象

有没有可能在 c + + 中声明一个变量而不实例化它:

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

基本上,我只是想声明一个条件的外部,这样它就得到了正确的作用域。

有没有办法不使用指针并在堆上分配 a来实现这一点?也许是一些聪明的推荐信?

60645 次浏览

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.

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

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

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)

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.

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.

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

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

The best work around is to use pointer.

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

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

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");