在 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++ 中初始化对象之前声明一个对象的主要内容,如果未能解决你的问题,请参考以下文章

C++ (P70—)

C++类和对象-2

如何在构造函数中初始化 C++ 对象成员变量?

在 C++ 中删除未初始化的对象 [重复]

在对象初始化之前访问 C++ std::vector 对象成员

C++类和对象下