这是什么奇怪的冒号成员(“:”)在构造函数中的语法?
最近我见过一个例子如下:
#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为int
。 int
不是一个类的types,所以它没有构造函数。 对于int
types来说,这个语法意味着简单的“用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,只能提供一个初始化列表
这是构造函数的初始化列表。 而不是默认构造x
, y
和z
,然后为它们分配参数中接收的值,那些成员将立即用这些值初始化。 这对float
似乎并不是很有用,但是对于构build起来很昂贵的自定义类来说,这可能是一个相当大的时间。
你是对的,这确实是一种初始化成员variables的方法。 我不确定这有什么好处,除了明确表示这是一个初始化。 在代码中有一个“bar = num”可能会更容易移动,删除或曲解。
在这个线程中还没有提到:自从C ++ 11以来,成员初始化列表可以使用列表初始化(即“统一初始化”,“加载初始化”):
Foo(int num): bar{num} {}
它与其他上下文中的列表初始化具有相同的语义。