在 C++ 中初始化对象之前声明一个对象
Posted
技术标签:
【中文标题】在 C++ 中初始化对象之前声明一个对象【英文标题】:Declaring an object before initializing it in c++ 【发布时间】:2010-10-22 11:18:05 【问题描述】:是否可以在 C++ 中声明一个变量而不实例化它?我想做这样的事情:
Animal a;
if( happyDay() )
a( "puppies" ); //constructor call
else
a( "toads" );
基本上,我只想在条件之外声明 a ,以便获得正确的范围。
有什么方法可以在不使用指针和在堆上分配a
的情况下做到这一点?也许引用的一些聪明的东西?
【问题讨论】:
见 RAII(资源获取是初始化) 如果它是一个非静态的全局/命名空间范围,那么值得注意的是你可以在不初始化的情况下实际声明:extern Animal a; ... 动物 a(stuff); @newacct:链接会有所帮助***.com/questions/2321511/… 【参考方案1】:你不能在不调用构造函数的情况下声明一个变量。但是,在您的示例中,您可以执行以下操作:
Animal a(happyDay() ? "puppies" : "toads");
【讨论】:
【参考方案2】:您不能在此处使用引用,因为一旦超出范围,引用就会指向将被删除的对象。
真的,你有两个选择:
1- 使用指针:
Animal* a;
if( happyDay() )
a = new Animal( "puppies" ); //constructor call
else
a = new Animal( "toads" );
// ...
delete a;
或使用智能指针
#include <memory>
std::unique_ptr<Animal> a;
if( happyDay() )
a = std::make_unique<Animal>( "puppies" );
else
a = std::make_unique<Animal>( "toads" );
2- 给Animal
添加一个Init方法:
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" );
我个人会选择选项 2。
【讨论】:
我会选择选项#1。谢谢! 如果你选择选项 1,你应该使用unique_ptr
。
我喜欢 2 选项。感谢日志:)【参考方案3】:
您不能直接在 C++ 中执行此操作,因为对象是在您使用默认构造函数定义时构造的。
但是,您可以先运行参数化构造函数:
Animal a(getAppropriateString());
或者您实际上可以使用?: operator
之类的东西来确定正确的字符串。
(更新:@Greg 给出了这个语法。见那个答案)
【讨论】:
+1。这是解决方案的一般形式——将其包装在一个函数中。 (正如你所说,?: 经常完成这项工作,而且这样做更方便,但编写一个单独的函数将总是工作。) 但是,如果您的构造函数需要接受多个参数,您是否会创建多个函数,每个参数一个? 有一些研究表明最好不要使用具有多个参数的构造函数,而是使用默认值创建然后使用 setter。话虽这么说,是的,你会为每个参数做一个函数,或者更好的是,有一个临时结构来表示构成参数的内聚元素,如果它们是相关的。【参考方案4】:我更喜欢 Greg 的回答,但你也可以这样做:
char *AnimalType;
if( happyDay() )
AnimalType = "puppies";
else
AnimalType = "toads";
Animal a(AnimalType);
我建议这样做是因为我曾在禁止条件运算符的地方工作过。 (叹气!)此外,这可以很容易地扩展到两个替代方案之外。
【讨论】:
【参考方案5】:如果您想避免垃圾回收 - 您可以使用智能指针。
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.
如果您仍想使用 .语法而不是->,你可以在上面的代码之后这样做:
Animal& a = *p_a;
// do stuff with a. whatever
【讨论】:
这个需要改成auto_ptr p_a(new Animal);否则 auto_ptr 只有一个空指针。虽然我喜欢第二个想法,因为它不会复制它 - 但你必须注意生活在那个范围内。 @NathanAdams,用 null 初始化的 auto_ptr 在这里很好,稍后它将是“小狗”或“蟾蜍”。拥有一个额外的“新动物”是多余的。【参考方案6】:除了 Greg Hewgill 的回答,还有其他几个选择:
将代码主体提炼成函数:
void body(Animal & a)
...
if( happyDay() )
Animal a("puppies");
body( a );
else
Animal a("toad");
body( a );
(Ab)使用新的展示位置:
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
【讨论】:
你知道有没有办法让placement新版本保证正确对齐(Pre c++11)?【参考方案7】:最好的解决方法是使用指针。
Animal a*;
if( happyDay() )
a = new Animal( "puppies" ); //constructor call
else
a = new Animal( "toads" );
【讨论】:
使用new
时,变量存放在堆中,以后应该删除【参考方案8】:
从 c++17 开始,现在有一种无需开销的方法:std::optional
。这种情况下的代码是:
#include <optional>
std::optional<Animal> a;
if(happyDay())
a.emplace("puppies");
else
a.emplace("toads");
【讨论】:
这可能是一个很好的解决方案。这不是 optional 的预期用途(标记可能会或可能不会初始化的值),但它确实避免调用a
的默认构造函数。
请注意,optional
在存储 bool(我的系统上为 8 个字节)和初始化自身方面确实有微小量的开销。
探索这个(和其他)解决方案的要点:gist.github.com/sbliven/359d180753febc4777ac79bb97685b5b
@Quantum7 一个好的编译器会完全优化掉可选的:godbolt.org/z/x9gncT【参考方案9】:
你也可以使用 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);
...
【讨论】:
std::move
绝对有可能在这里使用,但是这段代码没有显示如何解决问题中的双重初始化。您能否重写您的答案以更密切地关注我的问题(例如实现Animal(char *)
构造函数)?您还应该声明这需要 C++11。【参考方案10】:
是的,您可以执行以下操作:
Animal a;
if( happyDay() )
a = Animal( "puppies" );
else
a = Animal( "toads" );
这将正确调用构造函数。
编辑:忘了一件事...... 声明 a 时,您仍然必须调用构造函数,无论它是什么都不做的构造函数,还是仍将值初始化为任何值。因此,此方法会创建两个对象,一个在初始化时创建,另一个在 if 语句中。
更好的方法是创建类的 init() 函数,例如:
Animal a;
if( happyDay() )
a.init( "puppies" );
else
a.init( "toads" );
这样会更有效率。
【讨论】:
你确定吗?我认为这将调用默认构造函数,然后调用赋值运算符,所以你会失去旧的 a. 是的,一开始我忘记了初始构造函数。这就是为什么我通常会在发布代码之前对其进行测试......这次没有...... 是的,但是假设 (1) Animal 具有可访问的默认构造函数(在某些类中具有默认构造函数可能没有意义),(2) Animal 具有赋值运算符(某些类不能被设计赋值),并且(3)构造和赋值Animal与直接构造它的效果是一样的。 使用 init() 方法可能是个坏主意,因为这意味着对象在构造函数完成后无效。 @DeadHead:小心你的语法。你的动物 a(); line 是函数原型,而不是变量声明。动物一个;就是你的意思(默认构造函数仍然被调用)。以上是关于在 C++ 中初始化对象之前声明一个对象的主要内容,如果未能解决你的问题,请参考以下文章