【问题标题】:Declaring an object before initializing it in c++在 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:链接会有所帮助stackoverflow.com/questions/2321511/…

标签: c++ scope declaration instantiation


【解决方案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 是函数原型,而不是变量声明。动物一个;就是你的意思(默认构造函数仍然被调用)。
      猜你喜欢
      • 2017-01-11
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      • 2016-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多