C++ 使用成员初始值设定项列表会使初始化稍微快一点吗?
Bjarne Stroustrup在其《使用C++编程、原则和实践》一书中介绍了成员初始值设定项列表的概念,见第314-316页(§9.4.4)。他举了以下例子:C++ 使用成员初始值设定项列表会使初始化稍微快一点吗?,c++,c++11,initializer-list,C++,C++11,Initializer List,Bjarne Stroustrup在其《使用C++编程、原则和实践》一书中介绍了成员初始值设定项列表的概念,见第314-316页(§9.4.4)。他举了以下例子: // Simple Date (year, month, day) class Date { public: Date(int yy, int mm, int dd): y{yy}, m{mm}, d{dd} { //... } private: int y, m, d; }; 在
// Simple Date (year, month, day)
class Date
{
public:
Date(int yy, int mm, int dd): y{yy}, m{mm}, d{dd}
{
//...
}
private:
int y, m, d;
};
在第315页,他说:
我们本可以写:
Date::Date(int yy, int mm, int dd) // constructor
{
y = yy;
m = mm;
d = dd;
}
但是,原则上,我们将首先对成员进行默认初始化,然后为其分配值
因此,我是否可以得出这样的结论:使用成员初始值设定项列表可以使代码稍微快一点?当然,在现代PC上没有人会注意到。但是我正打算用C++来进行嵌入式开发。
编辑:我会进一步说明我的问题。“稍微快一点”实际上是指“所涉及的CPU周期更少”。
我也同意这个例子的潜在效率提高几乎是零。但是对于更大的类和结构,它可能在微控制器上变得明显。 < P> C++标准指定“默认初始化”如下: [dcl.init] 默认初始化T类型的对象意味着: -如果T是 (可能是cv限定的)类类型(第9条),默认 调用T的构造函数(12.1)(初始化为 如果T没有默认构造函数或重载解析,则格式错误 (13.3)导致歧义或功能被删除或 无法从初始化上下文访问) -如果T是一个 数组类型,每个元素默认初始化 -否则,不执行初始化 你的班级成员都是普通的、花园式的,
int
s。他们不是班级。它们不是数组。因此,在int
s的情况下,默认初始化不执行任何操作
在两个示例中,我希望大多数编译器生成相同的代码。无论如何,这没有什么区别。在第二个示例中,您没有初始化,而是为已经初始化的变量赋值。变量在进入构造函数之前被初始化(默认构造),因此实际上您要设置它们两次
int
没有任何特定的默认初始值设定项,因此您不会注意到,而是尝试使用不同的代码
所以你看,它们绝对不是等价物。实际上,例如,您不能在构造函数中用
int
s指定const
字段?没有什么区别。尝试一些重的类。如果没有副作用,这都是优化出来的。使一个成员常量。现在哪条路可行?这些决策涉及的不仅仅是绩效。始终使用成员初始值设定项列表作为默认值。您的问题有一个明显的答案。“在一个非真空阶段做某事会比两个非真空阶段快吗?”当然,如果这两个阶段在指令计数方面具有可比性,那么删除一个会节省CPU周期……在许多实际情况下,使用初始化列表(或类内初始化器)比使用构造函数体更快。在某些情况下,编译器可以将其全部优化为相同的内容(但不要指望它)。尽可能使用初始化列表。谢谢@SamVarshavchik。你能想出一个例子来说明它的重要性吗?这是引用《标准》中的话指出的。如果类成员本身是一个(另一个)类,其构造函数实际上做了一些事情,那么这很重要。谢谢@SamVarshavchik:-)使用初始值设定项列表可能仍然是一个更好的习惯。如果你研究这个主题,有些事情只能通过初始化列表来完成。哇,有趣的代码片段!因此,在您的示例中:(1)“在构造函数中赋值”导致对Foo构造函数的3次调用,(2)“在初始值设定项列表中赋值”只导致对Foo构造函数的1次调用。这3次调用Foo构造函数=>这意味着内存中某处有3个Foo对象?实际上,对于两次调用Foo
构造函数和一个复制赋值运算符,Foo()
在构造Bar
时被调用。然后调用Foo(int)
来构造一个临时Foo
,该临时Foo
通过常量引用传递给Foo
中包含的Bar
的operator=
,后者复制该值。然后,临时文件被销毁。成员初始值设定项列表避免了这一点,因为它是初始化对象的受限位置,因此编译器可以自由地执行任何他想要的操作。它们不仅仅是代码块中的赋值,就像在构造函数中一样。太棒了!我只是想知道为什么在你的例子中需要“const”。如果没有,它是否也会工作?是的,首先调用Foo(int)
,因为函数/构造函数的参数在调用Bar
的构造函数之前进行计算,该构造函数将在输入构造函数代码之前初始化所有成员(如果在成员初始值设定项列表中没有提供初始化,则将使用默认初始化)。
#include <iostream>
using namespace std;
class Foo
{
int x;
public:
Foo() : x(0) { cout << "Foo()" << endl; }
Foo(int x) : x(x) { cout << "Foo(int)" << endl; }
Foo& operator=(const Foo& o) {
cout << "Foo::operator=(const Foo&)" << endl;
this->x = o.x; return *this;
}
};
class Bar
{
Foo foo;
public:
Bar(const Foo& foo) { this->foo = foo; }
Bar(bool, const Foo& foo) : foo(foo) { }
};
int main() {
cout << "Assigned in constructor" << endl;
Bar bar = Bar(Foo(5));
cout << "Assigned in initializer list" << endl;
Bar bar2 = Bar(false, Foo(5));
}
Assigned in constructor
Foo(int)
Foo()
Foo::operator=(const Foo&)
Assigned in initializer list
Foo(int)