由于构造函数初始值设定项列表而优化

发布于 2024-12-19 19:57:07 字数 143 浏览 2 评论 0原文

构造函数应该通过以下方式初始化其所有成员对象 如果可能的话,初始化列表。它比构建更有效 通过构造函数体内的赋值来构造函数。

有人可以解释一下,为什么在示例的帮助下使用初始值设定项列表更有效?

Constructors should initialize all its member objects through
initializer list if possible. It is more efficient than building the
constructors via assignment inside the constructor body.

Could someone explain, why it is more efficient to use the initializer list with the help of an example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

终陌 2024-12-26 19:57:07

考虑这个程序:

#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)


Or, consider this alternative. Suppose that we add the following member to 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)

现在不可能一般地说哪种形式更有效。在您的特定类中,默认构造函数的成本加上赋值的成本是否比非默认构造函数更昂贵?如果是这样,那么初始化列表会更有效。否则就不是。

我曾经编写过的大多数类都可以在 init 列表中更有效地初始化。但是,这是一个经验法则,可能并不适用于所有可能的情况。

Consider this program:

#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); }
}

On my system (Ubuntu 11.10, g++ 4.6.1), the program produces this output:

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)

Now, consider why it is doing that. In the first case, C1::C1(int), a must be default-constructed before C1's constructor can be invoked. Then it is must assigned to via operator=. In my trivial example, there is no int assignment operator available, so we have to construct an A out of an int. Thus, the cost of not using an initializer is: one default constructor, one int constructor, and one assignment operator.

In the second case, C2::C2(int), only the int constructor is invoked. Whatever the cost of a default A constructor might be, clearly the cost of C2:C2(int) is not greater than the cost of C1::C1(int).


Or, consider this alternative. Suppose that we add the following member to A:

void operator=(int) { std::cout << "A::operator=(int)\n"; }

Then the output would read:

How expesive is it to create a C1?
A::A()
A::operator=(int)
How expensive is it to create a C2?
A::(int)

Now is is impossible to say generally which form is more efficient. In your specific class, is the cost of a default constructor plus the cost of an assignment more expensive than a non-default constructor? If so, then the initialization list is more efficient. Otherwise it isn't.

Most classes that I've ever written would be more efficiently initialized in an init list. But, that is a rule-of-thumb, and may not be true for every possible case.

才能让你更想念 2024-12-26 19:57:07

好吧,否则您将调用默认构造函数并然后执行赋值。这会长一步,并且可能会变得非常低效,具体取决于初始化的性质。

Well, otherwise you call the default constructor and then perform an assignment. That's one step longer and may get really inefficient depending on the nature of initialization.

相对绾红妆 2024-12-26 19:57:07

因为它是直接初始化的,而不是默认初始化然后赋值。对于 POD 来说,这在性能方面可能并不重要,但如果类型构造函数正在执行繁重的工作,则会产生影响。

另外,在某些情况下,您必须使用init list,因此您应该始终这样做以保持一致性。

Because it initialises directly, instead of default initialising and then assigning. It likely won't matter performance-wise for PODs, but it will if the type constructor is doing heavy-weight work.

Also, in some cases you must use init list, so you should always do it for consistency.

や三分注定 2024-12-26 19:57:07

来自 C++FAQ

考虑以下使用初始化列表初始化成员对象 x_ 的构造函数:Fred::Fred() : x_(whatever) { }。这样做最常见的好处是提高性能。例如,如果表达式whatever 与成员变量x_ 类型相同,则whatever 表达式的结果将直接在x_ 内部构造——编译器不会创建该对象的单独副本。即使类型不同,编译器通常也能比赋值更好地完成初始化列表的工作。

构建构造函数的另一种(低效)方法是通过赋值,例如: Fred::Fred() { x_ =whatever; }。在这种情况下,表达式无论什么都会导致创建一个单独的临时对象,并且该临时对象被传递到 x_ 对象的赋值运算符中。然后该临时对象在;处被破坏。这效率很低。

好像这还不够糟糕,在构造函数中使用赋值时还有另一个效率低下的原因:成员对象将由其默认构造函数完全构造,例如,这可能会分配一些默认的内存量或打开一些默认文件。如果任何表达式和/或赋值运算符导致对象关闭该文件和/或释放该内存(例如,如果默认构造函数没有分配足够大的内存池或者如果它打开了错误的文件)。

From the C++FAQ :

Consider the following constructor that initializes member object x_ using an initialization list: Fred::Fred() : x_(whatever) { }. The most common benefit of doing this is improved performance. For example, if the expression whatever is the same type as member variable x_, the result of the whatever expression is constructed directly inside x_ — the compiler does not make a separate copy of the object. Even if the types are not the same, the compiler is usually able to do a better job with initialization lists than with assignments.

The other (inefficient) way to build constructors is via assignment, such as: Fred::Fred() { x_ = whatever; }. In this case the expression whatever causes a separate, temporary object to be created, and this temporary object is passed into the x_ object's assignment operator. Then that temporary object is destructed at the ;. That's inefficient.

As if that wasn't bad enough, there's another source of inefficiency when using assignment in a constructor: the member object will get fully constructed by its default constructor, and this might, for example, allocate some default amount of memory or open some default file. All this work could be for naught if the whatever expression and/or assignment operator causes the object to close that file and/or release that memory (e.g., if the default constructor didn't allocate a large enough pool of memory or if it opened the wrong file).

够钟 2024-12-26 19:57:07

防止双重初始化。

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;
}

To prevent double initialization.

class B
{
//whatever
};

class A
{
   B _b;
public:
   A(B& b)
};

Now the two cases:

//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;
}
来日方长 2024-12-26 19:57:07

就 POD 类型而言,初始化和赋值应该是等效的,因为如果没有显式执行初始化,它们将保持未初始化状态,因此唯一的操作仍然是赋值。

对于具有默认构造函数和赋值运算符的类来说,情况有所不同:不是直接在正确的状态下创建对象,而是首先必须调用默认构造函数,然后调用赋值运算符(在构造函数体内)。这肯定比从一开始就使用正确的构造函数初始化对象要低效(两步而不是一步,第一步 - 默认构造 - 通常完全浪费)。

直接初始化还具有一个优点,即您可以确定,如果执行到达构造函数的主体,则所有各个字段都已处于“正确”状态。

As far as POD types are concerned, the initialization and the assignment should be equivalent, since they are left uninitialized if no initialization is performed explicitly, so the only operation remains the assignment.

Things are different for classes which have default constructors and assignment operators: instead of creating the object directly in the correct state, first the default constructor has to be called, and then the assignment operator (inside the body of the constructor). This is surely more inefficient than initializing the object with the correct constructor straight from the beginning (two steps instead of one, and the first step - default construction - is usually completely wasted).

The direct initialization also yields the advantage that you can be sure that, if the execution reached the body of the constructor, all the various fields are already in their "correct" state.

情何以堪。 2024-12-26 19:57:07

假设您的类中有一个 std::string 类型的数据成员。当执行此类的构造函数时,将自动调用默认的构造函数字符串类,因为对象是在构造函数主体之前初始化的。

如果要在构造函数体内分配字符串,则会创建一个临时对象并将其提供给字符串的赋值运算符。临时对象将在赋值语句结束时被销毁。如果在初始化列表中执行此操作,则不会创建临时对象。

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.

Suppose you have a data member in your class which is std::string type. When the constructor of this class is executed, the default constructor string class would be called automatically because objects are initialized before the body of the constructor.

If you are assigning the string inside the body of the constructor, then a temporary object would be created and given to the string's assignment operator. The temporary object will be destroyed at the end of the assignment statement. If you do this in the initializer list, a temporary object will not be created.

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.
懒的傷心 2024-12-26 19:57:07

因为如果您使用初始化程序列表,您所调用的是该对象的构造函数副本。

而如果您在构造函数体内初始化对象,则您正在执行赋值操作。

例子:
这里我调用 int 的复制构造函数。


  myClass::myClass( int x ) : member(x) {}

在这里我调用operator=(const int&)。任务


    myClass::myClass( int x )
    {
         member = x;
    }

通常,赋值比简单的复制执行更多的操作。
您还必须记住临时对象!

because if you are using the inizializer list what you are calling is the constructor copy of that object .

While if you initialize objects inside the constructor body you are doing an assignment.

example:
here i m calling the copy constructor of int.


  myClass::myClass( int x ) : member(x) {}

while here i m calling the operator=(const int& ). the asignment


    myClass::myClass( int x )
    {
         member = x;
    }

usually the asignment does more operation then a simple copy.
you must keep in account also temporary object!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文