我同意这样的共识,即通常最好在成员初始化列表中初始化C ++数据成员而不是构造函数的主体,但我对this explanation
持怀疑态度构建构造函数的另一种(低效)方法是通过赋值,例如:
Fred::Fred() { x_ = whatever; }.
在这种情况下,表达式会导致创建单独的临时对象,并且此临时对象将传递给x_对象赋值运算符。然后在;
处破坏该临时对象。那效率很低。
这实际上是对的吗?我原以为编译器会忽略默认构造的临时对象,该对象会立即被正文中的赋值所取代。我不知道为什么我会这么想,但在阅读了上述说法之后,我想我已经悄悄地假设了多年。
成员初始化列表实际上更有效吗?如果是这样,是因为这个原因吗?
答案 0 :(得分:8)
用Alexandrescu & Sutter(第9项)的话来说,不要过早地悲观
避免过早优化并不意味着无偿伤害 效率。过早的悲观化意味着写下这样的无偿 潜在的低效率:
•在传递引用时定义值传递值参数 适当。 (见第25项。)
•当前缀版本同样好时使用postfix + +。 (看到 项目28。)
•在构造函数内部使用赋值而不是初始化程序 名单。 (见第48项。)
每当你在构造函数中编写赋值时,你的代码审阅者都会处于警戒状态:这是一件特别的事吗?他真的想要一些特殊的两阶段初始化(因为无论如何都会生成成员的隐式默认构造!)。不要无缘无故地给你的代码读者带来惊喜。
请注意Alexandrescu& Sutter继续讨论潜在的低效率,但不要声称在实际优化代码中存在实际低效率的任何地方。这也是关于表达意图并避免风险效率低下的问题。
答案 1 :(得分:7)
使用成员初始化列表
#include <string>
struct Fred {
Fred() : x_("hello") { }
std::string x_;
};
int main() {
Fred fred;
}
Clang 3.9.1和gcc 6.3使用-O3 -fno-exceptions
(Compiler Explorer)生成以下内容:
main: # @main
xor eax, eax
ret
如果我们在体内做了一项任务:
#include <string>
struct Fred {
Fred() { x_ = "hello"; }
std::string x_;
};
int main() {
Fred fred;
}
都会生成更多代码,例如Clang 3.9.1输出:
main: # @main
push rbx
sub rsp, 32
lea rbx, [rsp + 16]
mov qword ptr [rsp], rbx
mov qword ptr [rsp + 8], 0
mov byte ptr [rsp + 16], 0
lea rdi, [rsp]
xor esi, esi
xor edx, edx
mov ecx, .L.str
mov r8d, 5
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
mov rdi, qword ptr [rsp]
cmp rdi, rbx
je .LBB0_2
call operator delete(void*)
.LBB0_2:
xor eax, eax
add rsp, 32
pop rbx
ret
.L.str:
.asciz "hello"
所以似乎成员初始化列表确实更有效,至少在某些情况下,即使使用现代编译器也是如此。
答案 2 :(得分:3)
成员初始化列表实际上更有效吗?如果是这样,是因为这个原因吗?
一般是的。使用成员初始化,您可以将值直接传递给构造函数,否则将创建默认构造的对象,然后调用赋值运算符。请注意,这不是关于&#34;临时&#34;在你提供的引文中提到,这是关于字段本身。
你可以看到它here
class Verbose {
public:
Verbose() { std::cout << "Verbose::Verbose()" << std::endl; }
Verbose( int ) { std::cout << "Verbose::Verbose(int)" << std::endl; }
Verbose &operator=( int ) { std::cout << "Verbose::operator=(int)" << std::endl; }
};
class A {
public:
A() : v( 0 ) {}
A(int) { v = 0; }
private:
Verbose v;
};
int main() {
std::cout << "case 1 --------------------" << std::endl;
A a1;
std::cout << "case 2 --------------------" << std::endl;
A a2( 0 );
// your code goes here
return 0;
}
输出:
case 1 --------------------
Verbose::Verbose(int)
case 2 --------------------
Verbose::Verbose()
Verbose::operator=(int)