【问题标题】:C++ initialization list vs assigning valuesC++ 初始化列表与赋值
【发布时间】:2020-05-11 03:17:19
【问题描述】:

在 C++ 中,初始化列表和在构造函数中赋值而不是每个方法的外观有什么区别? 我的意思是使用一个而不是另一个的优点是什么,为什么在幻灯片(下)的给定示例中仅适用于初始化? (我没找到,希望能加点资源)

Click here to view slide: uploaded on imgur

【问题讨论】:

    标签: c++ class initialization


    【解决方案1】:

    在构造函数中使用初始化列表是一步过程,即它在声明对象的那一刻初始化对象。它调用复制构造函数。

    而使用分配是两步过程,即定义对象然后分配它。定义对象调用默认构造函数,然后赋值调用赋值运算符。因此,昂贵的操作。

    在C++中,类的常量或引用数据成员变量只能在初始化列表中初始化,不能在构造函数体中使用赋值。

    常量和引用数据成员变量都具有必须在声明时初始化的属性。因此,只有在构造函数中使用初始化列表,因为初始化列表在声明时初始化类成员变量,而如果构造函数主体在声明后初始化数据成员,则赋值。

    在某些情况下,构造函数内的数据成员初始化不起作用,必须使用初始化列表。以下是此类情况。

    1. 用于初始化非静态 const 数据成员。
    #include<iostream> 
    using namespace std; 
    
    class Test { 
        const int t; 
    public: 
        Test(int t):t(t) {}  //Initializer list must be used 
        int getT() { return t; } 
    }; 
    
    int main() { 
        Test t1(10); 
        cout<<t1.getT(); 
        return 0; 
    }
    
    1. 用于引用成员的初始化。
    #include<iostream> 
    using namespace std; 
    
    class Test { 
        int &t; 
    public: 
        Test(int &t):t(t) {}  //Initializer list must be used 
        int getT() { return t; } 
    }; 
    
    int main() { 
        int x = 20; 
        Test t1(x); 
        cout<<t1.getT()<<endl; 
        x = 30; 
        cout<<t1.getT()<<endl; 
        return 0; 
    }
    
    1. 用于初始化没有默认构造函数的成员对象。 (在您的情况下 Array digits 没有默认构造函数)
    #include <iostream>
    using namespace std;
    class A { 
        int i; 
    public: 
        A(int ); 
    }; 
    
    A::A(int arg) { 
        i = arg; 
        cout << "A's Constructor called: Value of i: " << i << endl; 
    } 
    
    // Class B contains object of A 
    class B { 
        A a; 
    public: 
        B(int ); 
    }; 
    
    B::B(int x):a(x) {  //Initializer list must be used 
        cout << "B's Constructor called"; 
    } 
    
    int main() { 
        B obj(10); 
        return 0; 
    }
    
    1. 用于基类成员的初始化。
    2. 当构造函数的参数名称与数据成员相同时。
    3. 出于性能原因。

    【讨论】:

      【解决方案2】:

      如果不使用初始化列表,则在到达构造函数主体之前,将默认构造类的数据成员:

      
      class Foo{
          private:
              int bar;
      
          public:
              Foo(int _bar){//bar is default constructed here
                  bar = _bar; //bar is assigned a new value here
              }
      };
      

      这对于基本类型(如int)来说不是什么大问题,因为默认构造函数并不昂贵。但是,如果数据成员没有默认构造函数,或者默认构造后赋值比直接构造更昂贵,则可能会成为问题:

      //Bar does not have a default constructor, only a copy constructor
      class Bar{
          public:
              Bar() = delete; //no default constructor
              Bar(const Bar& bar); //copy constructor only
      };
      
      class Foo{
          private:
              Bar bar;
      
          public:
              Foo(const Bar& _bar){
                  //this will not compile, bar does not have a default constructor
                  //  or a copy assignment operator
                  bar = _bar;
              }
      
              Foo(const Bar& _bar) : bar(_bar){//this will compile, copy constructor for bar called
              }
      };
      

      一般来说,使用初始化列表可以获得更高效的代码。

      【讨论】:

      • 嗨,为什么“Bar 没有默认构造函数”我在读到,当我们不编写构造函数时,编译器会为我们提供默认构造函数,除非我们声明其他任何构造函数......另外,可以举个例子详细说明一下:“但是,如果数据成员没有默认构造函数,或者默认构造后赋值比直接构造更昂贵,则可能会成为问题:”
      • @clark_smith1 如果您查看第二个代码 sn-p,我已经使用 Bar()=delete 显式删除了 Bars 默认构造函数。这可以防止编译器构造一个。因此,Bar 没有任何构造函数。
      猜你喜欢
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多