如果可能的话,构造函数应通过初始化列表初始化其所有成员对象。这比在构造函数体内通过赋值构建构造函数更有效率。
有人能否通过示例来解释为什么使用初始化列表更有效率?
如果可能的话,构造函数应通过初始化列表初始化其所有成员对象。这比在构造函数体内通过赋值构建构造函数更有效率。
有人能否通过示例来解释为什么使用初始化列表更有效率?
考虑以下程序:
#include <iostream>
struct A {
A() { std::cout << "A::A()\n"; }
A(int) { std::cout << "A::(int)\n"; }
void operator=(const A&) { std::cout << "A::operator=(const A&)\n"; }
};
struct C1 {
A a;
C1(int i) {
a = i;
}
};
struct C2 {
A a;
C2(int i) : a(i) {}
};
int main() {
std::cout << "How expesive is it to create a C1?\n";
{ C1 c1(7); }
std::cout << "How expensive is it to create a C2?\n";
{ C2 c2(7); }
}
在我的系统上(Ubuntu 11.10,g++ 4.6.1),该程序输出如下:
How expesive is it to create a C1?
A::A()
A::(int)
A::operator=(const A&)
How expensive is it to create a C2?
A::(int)
现在,考虑为什么会这样。在第一种情况下,C1::C1(int)
中,必须先对a
进行默认构造,然后才能调用C1
的构造函数。然后它通过operator=
进行赋值。在我的简单示例中,没有可用的int
赋值运算符,因此我们必须从int构造出一个A
。因此,不使用初始化程序的代价是:一个默认构造函数,一个int
构造函数和一个赋值运算符。
在第二种情况下,C2::C2(int)
只调用了int
构造函数。无论默认A
构造函数的成本是多少,显然C2:C2(int)
的成本都不会大于C1::C1(int)
的成本。
A
添加以下成员:void operator=(int) { std::cout << "A::operator=(int)\n"; }
那么输出将会是:
How expesive is it to create a C1?
A::A()
A::operator=(int)
How expensive is it to create a C2?
A::(int)
现在无法一般性地说哪种形式更有效率。在你具体的类中,一个默认构造函数加上一个赋值操作的成本是否比一个非默认构造函数更昂贵?如果是这样,那么初始化列表更有效率。否则就不是。
我编写的大多数类都可以更有效地在初始化列表中初始化。但是,这只是一个经验规则,并不适用于每种可能的情况。
C1 c1(7)
的情况下应该调用int
构造函数。只需要调用默认构造函数和赋值运算符就可以了,对吗? - nitin_cherianA::operator=(const A&)
。如果 A
更完整 -- 如果它包括 A::operator=(int)
-- 那么默认构造函数就不会被调用。 - RobᵩA
为输入,因此语句 a=7
首先会使用 A::A(int)
创建一个临时 A
对象,然后再调用赋值运算符。字面值 7
会被转换成 A
类型。 - Luc Tourailleint
构造函数来创建...然后int
构造函数将不会...”。 - Robᵩ否则,您将调用默认构造函数,然后执行赋值操作。这是一步更长的过程,根据初始化的性质可能会变得非常低效。
就POD类型而言,初始值和赋值应该是等价的,因为如果没有显式执行初始化,则它们将被保留未初始化,因此唯一的操作是赋值。
对于具有默认构造函数和赋值运算符的类来说,情况就不同了:必须首先调用默认构造函数,然后调用赋值运算符(在构造函数体内)。这肯定比直接使用正确的构造函数初始化对象更加低效(需要两个步骤而不是一个步骤,并且第一个步骤 - 默认构造 - 通常完全浪费)。
直接初始化还具有一个优点,就是您可以确信,如果执行到构造函数的主体,所有各种字段已经处于其“正确”的状态。
class B
{
//whatever
};
class A
{
B _b;
public:
A(B& b)
};
现在是两种情况:
//only initializes _b once via a copy constructor
A::A(B& b) : _b(b)
{
}
//initializes _b once before the constructor body, and then copies the new value
A::A(B& b)
{
//_b is already initialized here
//....
//one extra instruction:
_b = b;
}
class Person
{
public:
Person(string s);
~Person();
private:
string name;
};
// case 1
Person::Person(string s)
{
name = s; // creates a temporary object
}
// case 2
Person::Person(string s):name(s) {} // whereas this will not create a temporary object.
因为如果您正在使用初始化器列表,则调用的是该对象的构造函数副本。
而如果您在构造函数体内初始化对象,则正在执行赋值操作。
例如:在这里我调用了int的复制构造函数。
<code>myClass::myClass( int x ) : member(x) {}</code>
while here i m calling the operator=(const int& ). the asignment
<code>myClass::myClass( int x )
{
member = x;
}</code>
通常赋值操作比简单的复制要执行更多的操作。你必须考虑到临时对象!