必须使用初始化列表的情况

时间:2020-12-02 19:39:24
在学习c++时,一直对必须使用初始化列表的情况说不全,或者说不清楚。今天来总结学习一下。
先回忆一下,必须使用初始化列表的情况

1.类数据成员为const类型
2. 类数据成员为引用类型
3.类没有默认的拷贝构造
4. 派生类的构造函数初始化列表必须调用基类的构造函数
  • const 数据成员
   
   
   
  1. class test
  2. {
  3. public:
  4. test()
  5. {
  6. cout << "constructor with argument\n";
  7. }
  8. private:
  9. const int i=10;
  10. };
结果编译错误:
必须使用初始化列表的情况
原因:因为常量数据成员必须进行初始化,而不能被赋值。函数体内只能进行赋值,因此初始化列表是const数据成员初始化的唯一机会。
明确了两点:
  1. 只有静态常量成员才可以在类中初始化,如果没有在类内、类外初始化。则其会被初始化为0 或者 “空”(NULL或者string(“”))
  2. 非静态常量成员必须在初始化列表中完成
  3. 再需要注意的就是static成员可以在类中声明为任何的格式,但是数据成员定义必须在类的外部定义。与普通的数据成员不同,static成员不通过类构造函数进行初始化,而是应该在定义时进行初始化。
  • 引用类型的数据成员
 因为引用是一个对象的别名,因此必须进行初始化。而函数内部为初始化,不为赋值。因此初始化列表是其初始化的唯一机会。
    
    
    
  1. class test
  2. {
  3. public:
  4. test()
  5. {
  6. cout << "constructor with argument\n";
  7. cout << i <<endl;
  8. }
  9. private:
  10. int& i;
  11. //static int j;
这里如果没有给出初始化列表,编译失败。
必须使用初始化列表的情况
编译器提示,i必须在构造函数初始值列表中初始化。

笔者在此做了一个有趣的测试,如果将引用类型改为静态的数据成员,那么还需要进行初始化列表吗?
   
   
   
  1. class test
  2. {
  3. public:
  4. test()
  5. {
  6. cout << "constructor with argument\n";
  7. cout << i <<endl;
  8. }
  9. private:
  10. static int& i;
  11. static int j;
  12. };
  13. int test::j = 10;
  14. int& test::i = j;
结果可以看出,如果成员类型为静态的引用类型,则应该按照静态数据成员对待。需要在类定义体外进行定义并初始化。
  • 如果类没有默认构造函数
         
         
         
    1. class Base
    2. {
    3. public:
    4. Base(int a) : val(a) {}
    5. private:
    6. int val;
    7. };
    8. class A
    9. {
    10. public:
    11. A(int v) : p(v), b(v) {}
    12. void print_val() { cout << "hello:" << p << endl;}
    13. private:
    14. int p;
    15. Base b;
    16. };

因为当类数据成员没有默认的构造函数时,在另一个类中声明了该变量(Base)。如果不用初始化列表构造,则其编译失败,因为其没有参数,无法进行构造。
  • 如果存在继承关系,派生类中必须在其初始化列表中调用基类的构造函数
在看effective C++的时候, 其中有一条 复制对象时勿忘其每一个成分  Copying函数应该确保复制“对象内的所有成员变量” 及“所有base成分”
   
   
   
  1. class Base
  2. {
  3. public:
  4. Base(int a) : val(a) {}
  5. private:
  6. int val;
  7. };
  8. class A : public Base
  9. {
  10. public:
  11. A(int v) : p(v), Base(v) {}
  12. void print_val() { cout << "hello:" << p << endl;}
  13. private:
  14. int p;
  15. };
再看一个例子,关于Copy函数应注意的地方
   
   
   
  1. class Base
  2. {
  3. public:
  4. Base(int j):i(j)
  5. {
  6. cout << "constructor with argument\n";
  7. int p = 20;
  8. i = p;
  9. cout << i <<endl;
  10. }
  11. private:
  12. int i;
  13. };
  14. class Derive: public Base
  15. {
  16. public:
  17. Derive(int i ):Base(i),_i(i)
  18. {
  19. }
  20. Derive(const Derive &d):Base(d),_i(d._i)
  21. {
  22. }
  23. Derive& operator=(const Derive& d)
  24. {
  25. Base::operator=(d);
  26. _i = d._i;
  27. return *this;
  28. }
  29. private:
  30. int _i;
  31. };
在这个例子中我们可以看到,派生类中的拷贝构造函数对的初始化列表调用了基类构造函数进行初始化。在赋值构造函数中也调用了基类的赋值构造函数对基类部分进行构造。