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

最近我见过一个例子如下:

#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)是什么意思? 它似乎似乎初始化成员variables,但我从来没有见过这种语法。 它看起来像一个函数/构造函数调用,但为int ? 对我来说没有意义。 也许有人可以启发我。 顺便说一下,还有没有其他的深奥的语言function,你永远不会find一个普通的C + +书?

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

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

常见问题条目的关键点在于,

所有其他的事情是平等的,如果你使用初始化列表而不是赋值,你的代码将运行得更快。

 Foo(int num): bar(num) 

这个构造在C ++中被称为成员初始化列表

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


构造函数中的Initializing和Assignment有什么区别?

成员初始化:

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

成员作业:

 Foo(int num) { bar = num; } 

使用成员初始值设定项列表初始化一个成员,并在构造函数体内为其赋值。

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

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

正如你所看到的那样,在后者中还有额外的创build和分配开销,这对于用户定义的类可能是相当可观的。

 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类的成员,没有实际的开销。


你什么时候需要使用成员初始化列表?

如果出现以下情况,您将( 有)强制使用成员初始值设定项列表:

  • 你的课有一个参考成员
  • 你的类有一个非静态的const成员或
  • 您的类成员没有默认的构造函数或
  • 对于基类成员的初始化
  • 当构造函数的参数名称与数据成员相同时(这不是必须的)

一个代码示例:

 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没有默认构造函数,所以初始化它的成员将需要使用成员初始化列表。

在线版本的代码示例


使用成员初始值列表时要注意的要点:

类成员variables总是按照在类中声明的顺序进行初始化。

它们没有按照成员启动程序列表中指定的顺序进行初始化。
总之,成员初始化列表并不决定初始化的顺序。

鉴于上述情况,始终保持成员初始化顺序与其在类定义中声明的顺序始终相同。 这是因为编译器不会警告如果两个命令是不同的,但相对较新的用户可能会混淆成员初始化列表作为初始化的顺序,并编写一些依赖于它的代码。

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

考虑这两个例子:

 // 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 

这完全是关于效率。

这被称为初始化列表。 这是初始化类成员的一种替代方法。 使用它有好处,而不是简单地将新值分配给构造函数体中的成员,但是如果你有常量引用的类成员,它们必须被初始化。

这不是晦涩的,它是C ++初始化列表语法

基本上,在你的情况下, x将用_x初始化, y_y初始化, z_z初始化。

另一个已经向你解释说,你观察到的语法被称为“构造函数初始化列表”。 这个语法可以让你自定义初始化类的基类子对象和成员子对象(而不是允许它们默认初始化或保持未初始化)。

我只想指出,正如你所说,“看起来像一个构造函数调用”的语法不一定是构造函数调用。 在C ++语言中, ()语法只是初始化语法的一种标准forms。 对不同types的解释是不同的。 对于具有用户定义的构造函数的类types来说,这意味着一件事(确实是一个构造函数调用),对于没有用户定义的构造函数的类types,这意味着对于empty () )和对于非类types它再次意味着不同的东西(因为非类types没有构造函数)。

在你的情况下,数据成员的types为intint不是一个类的types,所以它没有构造函数。 对于inttypes来说,这个语法意味着简单的“用num的值初始化bar ”,就是这样。 它是这样做的,直接地,没有涉及的构造函数,因为int再次不是一个类types,因此它不能有任何构造函数。

我不知道你怎么会错过这个,这是非常基本的。 这是初始化成员variables或基类构造函数的语法。 它适用于普通的旧数据types以及类对象。

这是一个初始化列表。 它将在构造函数体运行之前初始化成员。 考虑

 class Foo { public: string str; Foo(string &p) { str = p; }; }; 

VS

 class Foo { public: string str; Foo(string &p): str(p) {}; }; 

在第一个例子中,str将被它的无参数构造函数初始化

 string(); 

在Foo构造函数的主体之前。 在foo的构造函数里面,

 string& operator=( const string& s ); 

将会像str一样在'str'上调用str = p;

在第二个例子中,str将通过调用其构造函数直接初始化

 string( const string& s ); 

以“p”作为参数。

还有另一个“好处”

如果成员variablestypes不支持空初始化,或者如果它的引用(不能被空初始化),那么你别无select,只能提供一个初始化列表

这是构造函数的初始化列表。 而不是默认构造xyz ,然后为它们分配参数中接收的值,那些成员将立即用这些值初始化。 这对float似乎并不是很有用,但是对于构build起来很昂贵的自定义类来说,这可能是一个相当大的时间。

你是对的,这确实是一种初始化成员variables的方法。 我不确定这有什么好处,除了明确表示这是一个初始化。 在代码中有一个“bar = num”可能会更容易移动,删除或曲解。

在这个线程中还没有提到:自从C ++ 11以来,成员初始化列表可以使用列表初始化(即“统一初始化”,“加载初始化”):

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

它与其他上下文中的列表初始化具有相同的语义。