使用类成员初始化特性简化构造代码

来源:互联网 发布:淘宝外观专利侵权 知乎 编辑:程序博客网 时间:2024/05/29 03:34

一个新的C++0x特性称为类成员初始化,它允许您在声明数据成员时单一地初始化数据。了解如何使用此功能,以简化您的代码,减少潜在的错误,使你的代码更易读。

一般来说,初始化成员数据的工作是在构造函数里完成的。如果类有多个构造函数并且每个构造函数都分别初始化个别成员时,往往会导致重复的初始化代码分散在各个构造函数里。使用委托构造函数能在一定程序上解决了这个问题,不过有时仍然需要这样的重复初始化,从而使用代码维护困难并损害可读性。

当前现状

您的类有多个构造函数,迫使你在每一个构造函数中重复数据成员的初始化代码。

解决方案

使用类成员初始化来一次性初始化数据会员。

初始化问题

在当代C + +中,只有整数类型的静态成员或者枚举常量才可以直接声明初始数据。例如:

  1. class A
  2. {
  3. static const int MAX=1024;
  4. public:
  5.   A();
  6. //..
  7. };

然而,下面这个类是不允许的,因为b既不是一个整数类型的常量静态成员也不是一个常量枚举类型:

  1. class C
  2. {
  3. int b=1024; //error
  4. bool f;
  5. double d;
  6. //..
  7. };

由于这一限制,我们不得不在构造C类时重复初始化成员b:

  1. class C
  2. {
  3. int b;
  4. bool f;
  5. double d;
  6. public:
  7.     C(): b(1024), f(false), d(0.0) {}
  8.     C(bool flag) : b(1024), f(flag), d(0.0) {}
  9.     C(bool flag, double dbl): b(1024), f(flag), d(dbl){}
  10. };

这种重复既费力而又容易出错。为什么不一次指定b的初始值?一个新的C++0x建议允许我们做,甚至更多。

类成员初始化

在解释什么是类成员初始化之前,我不得不澄清一下术语,以避免混淆。

成员初始化列表,它出现在构造函数的参数列表之后,以冒号开头:

  1. class A
  2. {
  3. int x;
  4. int y;
  5. public:
  6.   A(): x(2), y(3){}
  7. };

initializer_list是C++09的一个类模板,定义于头文件之中 。构造函数使用initializer_list把一块初始数据转换成范围,从而能让我们得到数目不等的初始化对象。

  1. template
  2. class vector
  3. {
  4. public:
  5.     vector(std::initializer_list s);
  6. };
  7. vector vi={1,8,102,99};

类成员初始化是一个新的C++0x提案,不要和上述两个初始化结构混淆了。

类成员初始化语法看起来像这样:

  1. class D
  2. {
  3. public:
  4. int x=5; //类成员初始化, C++0x only
  5. int y(6); //另一种形式的初始化
  6. };

编译器会按概念把类成员初始化转换成类成员初始化列表。因此,上面声明的D类在编译时会转换成下面这个样子:

  1. class D
  2. {
  3. public:
  4. int x;
  5.     D(): x(5), y(6) {}
  6. };

从编程者的角度来看,类成员初始化功能似乎并不像是一个显着的改善(除了语法更直观一点) 。然而,当你的类有多个构造函数时,它的优势就会变得明显起来。这里是一个使用类成员初始化的C类:

  1. class C
  2. {
  3. int b=1024;//c类成员初始化, C++0x
  4. bool f;
  5. double d;
  6. public:
  7.     C(): f(false), d(0.0) {}
  8.     C(bool flag) : f(flag), d(0.0) {}
  9.     C(bool flag, double dbl): f(flag), d(dbl){}
  10. };

数据成员f和d出现在每一个构造函数的成员初始化列表中,在这之前,b只需要初始化一次,而不用构造函数操心。

默认和覆盖

类成员初始化能够做的比您刚才看到的更多。在前面的版本的C类里,前两个构造函数把d初始化为0.0,只有第三个构造函数把d初始化成了其它值。在这种情况下,您可以用类成员初始化指定默认值,并在必要时覆盖的初始化值:

  1. class C
  2. {
  3. int b=1024;
  4. bool f;
  5. double d=0.0; //d的默认值
  6. public:
  7.     C(): f(false) {}//b 初始化为 1024, d 为 0.0
  8.     C(bool flag) : f(flag) {} // 同上
  9.     C(bool flag, double dbl): f(flag), d(dbl){} // 覆盖d的默认值
  10. };

构造函数的成员初始化列表中不包含成员d时就会使用默认的初始化-类成员初始化。因此,在前面的示例中,前两个构造函数将d初始化为0.0,第三个初始化为dbl。

你可以进一步简化构造函数:

  1. class C
  2. {
  3. int b=1024;
  4. bool f=false;
  5. double d=0.0;
  6. public:
  7.     C(){} C(bool flag): f(flag) {}
  8.     C(bool flag, double dbl): f(flag), d(dbl){}
  9. };

总之,只有需要覆盖默认初始化值时才应在构造函数的初始化列表中明确指出。请注意,没有初始化的数据成员将有一个不确定的值,因此:

  1. class G
  2. {
  3. public:
  4.     std::string x("TEST"); //class member init for string
  5. int y;
  6. int z;
  7.     G(): z(0){}//initialize x and z, y has an undefined value
  8. };

在本文写作之时,类成员初始化的提案有很大的机会被纳入到C++0x标准中,您可以在这里获得最新的各种支持C++0x的编译器。

原创粉丝点击