最近我见过如下例子:
#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 ++书中找到它吗?
答案 0 :(得分:290)
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
没有默认构造函数,因此要初始化其成员,需要使用成员初始化列表。类成员变量总是按照它们在类中声明的顺序进行初始化。
它们不按照在成员初始化列表中指定的顺序进行初始化。
简而言之,成员初始化列表不确定初始化的顺序。
鉴于上述情况,保持成员初始化的成员顺序与在类定义中声明它们的顺序始终是一个好习惯。这是因为如果两个订单不同,编译器不会发出警告,但是相对较新的用户可能会将成员初始化程序列表混淆为初始化顺序,并编写一些依赖于此的代码。
答案 1 :(得分:188)
这是成员初始化列表。您应该在任何good C++ book中找到有关它的信息。
You should, in most cases, initialize all member objects in the member initialization list(但是,请注意FAQ条目末尾列出的例外情况)。
FAQ条目的要点是,
在所有其他条件相同的情况下,如果使用初始化列表而不是赋值,则代码将运行得更快。
答案 2 :(得分:14)
这是构造函数的初始化。这是在类构造函数中初始化成员的正确方法,因为它会阻止调用默认构造函数。
考虑以下两个例子:
// 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
这完全取决于效率。
答案 3 :(得分:12)
这称为初始化列表。它是初始化类成员的另一种方法。使用它有一些好处,而不是简单地为构造函数体中的成员分配新值,但如果你有常量或引用的类成员,那么它们是必须进行初始化。
答案 4 :(得分:9)
这不是模糊的,而是C++ initialization list syntax
基本上,在您的情况下,x
将使用_x
初始化,y
合并_y
,z
合并_z
。
答案 5 :(得分:8)
另一个已经向您解释过,您观察到的语法称为“构造函数初始化列表”。此语法允许您自定义初始化类的基础子对象和成员子对象(而不是允许它们默认初始化或保持未初始化)。
我只想注意,正如你所说,“看起来像构造函数调用”的语法不一定是构造函数调用。在C ++语言中,()
语法只是初始化语法的一种标准形式。对于不同类型,它的解释不同。对于具有用户定义的构造函数的类类型,它意味着一件事(它确实是构造函数调用),对于没有用户定义的构造函数的类类型,它意味着另一件事(所谓的value initialization)为空()
)对于非类类型,它再次表示不同的东西(因为非类类型没有构造函数)。
在您的情况下,数据成员的类型为int
。 int
不是类类型,因此它没有构造函数。对于类型int
,此语法仅表示“使用bar
”初始化num
,就是这样。它就像那样直接完成,没有涉及构造函数,因为int
不再是类类型,因此它不能有任何构造函数。
答案 6 :(得分:7)
我不知道你怎么会错过这个,这是非常基本的。这是初始化成员变量或基类构造函数的语法。它适用于普通的旧数据类型以及类对象。
答案 7 :(得分:6)
这是一个初始化列表。它将在构造函数体运行之前初始化成员。 考虑
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 = p; 时,将在'str'上调用
在第二个例子中,str将直接初始化 调用它的构造函数
string( const string& s );
以'p'作为参数。
答案 8 :(得分:5)
你是对的,这确实是一种初始化成员变量的方法。除了清楚地表明它是初始化之外,我不确定这有什么好处。在代码中包含“bar = num”可以更容易地移动,删除或误解。
答案 9 :(得分:5)
还有另一个“好处”
如果成员变量类型不支持空初始化或者它的引用(不能为null初始化)那么你别无选择,只能提供初始化列表
答案 10 :(得分:5)
这是构造函数的初始化列表。而不是默认构建x
,y
和z
,然后将它们分配给参数中接收的值,这些成员将立即使用这些值进行初始化。这对于float
来说似乎并不是非常有用,但是对于构造起来很昂贵的自定义类来说可能要相当长。
答案 11 :(得分:1)
此线程尚未提及:自C ++ 11以来,成员初始化列表可以使用列表初始化(又称。&#34;统一初始化&#34;,&#34;支持初始化&#34;):< / p>
Foo(int num): bar{num} {}
与其他上下文中的list-initialization具有相同的语义。