提问者:小点点

构造函数中这个奇怪的冒号成员(“:”)语法是什么?


最近我看到了一个像下面这样的例子:

#include <iostream>

class Foo {
public:
  int bar;
  Foo(int num): bar(num) {};
};

int main(void) {
  std::cout << Foo(42).bar << std::endl;
  return 0;
}

这个奇怪的:bar(num)是什么意思? 它似乎以某种方式初始化了成员变量,但我以前从未见过这种语法。 它看起来像一个函数/构造函数调用,但是对于一个int? 对我来说毫无意义。 也许有人能启发我。 顺便问一下,还有没有其他像这样的深奥的语言特性,在普通的C++书中是找不到的?


共3个答案

匿名用户

这是一个成员初始化列表。 你应该在任何一本好的C++书籍中找到关于它的信息。

在大多数情况下,您应该初始化成员初始化列表中的所有成员对象(但是,请注意FAQ条目末尾列出的例外)。

FAQ条目的要点是,

在其他条件相同的情况下,如果使用初始化列表而不是赋值,代码将运行得更快。

匿名用户

Foo(int num): bar(num)    

这个构造在C++中称为成员初始值设定项列表。

简单地说,它将您的成员bar初始化为一个值num

成员初始化:

Foo(int num): bar(num) {};

成员分配:

Foo(int num)
{
   bar = num;
}

在使用成员初始值设定项列表初始化成员和在构造函数主体内部为其赋值之间存在显著差异。

当您通过成员初始值列表初始化字段时,构造函数将被调用一次,对象将在一次操作中被构造和初始化。

如果使用赋值,那么字段将首先用默认构造函数初始化,然后用实际值重新分配(通过赋值运算符)。

正如您所看到的,创建&; 后者中的分配,这对于用户定义的类来说可能是相当大的。

Cost of Member Initialization = Object Construction 
Cost of Member Assignment = Object Construction + Assignment

后者实际上相当于:

Foo(int num) : bar() {bar = num;}

而前者相当于只是:

Foo(int num): bar(num){}

对于内置(您的代码示例)或POD类成员,没有实际开销。

在下列情况下,您将不得不(相当强制地)使用成员初始值设定项列表:

  • 您的类有一个引用成员
  • 您的类具有非静态常量成员或
  • 您的类成员没有默认构造函数或
  • 用于初始化基类成员或
  • 当构造函数的参数名称与数据成员相同时(这不是必须的)
class MyClass
{
    public:
        //Reference member, has to be Initialized in Member Initializer List
        int &i;       
        int b;
        //Non static const member, must be Initialized in Member Initializer List
        const int k;  

    //Constructor’s parameter name b is same as class data member 
    //Other way is to use this->b to refer to data member
    MyClass(int a, int b, int c):i(a),b(b),k(c)
    {
         //Without Member Initializer
         //this->b = b;
    }
};

class MyClass2:public MyClass
{
    public:
        int p;
        int q;
        MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
        {
        }

};

int main()
{
    int x = 10;
    int y = 20;
    int z = 30;
    MyClass obj(x,y,z);

    int l = 40;
    int m = 50;
    MyClass2 obj2(x,y,z,l,m);

    return 0;
}
  • myclass2'没有默认构造函数,因此必须通过成员初始值设定项列表对其进行初始化。
  • 基类MyClass没有默认构造函数,因此要初始化其成员,需要使用成员初始值设定项列表。

类成员变量始终按照它们在类中声明的顺序初始化。

它们不是按照在成员初始值设定项列表中指定的顺序初始化的。
简言之,成员初始化列表不确定初始化的顺序。

鉴于上述情况,为成员初始化维护与在类定义中声明成员的顺序相同的成员顺序总是一个好的实践。 这是因为编译器不会在两个顺序不同时发出警告,但相对较新的用户可能会将成员初始值设定项列表混淆为初始化顺序,并根据该顺序编写一些代码。

匿名用户

这是构造函数初始化。 这是初始化类构造函数中成员的正确方法,因为它可以防止调用默认构造函数。

考虑以下两个例子:

// Example 1
Foo(Bar b)
{
   bar = b;
}

// Example 2
Foo(Bar b)
   : bar(b)
{
}

在实施例1中:

Bar bar;  // default constructor
bar = b;  // assignment

在例2中:

Bar bar(b) // copy constructor

这都是关于效率的。