10

我目前正在阅读 Bjarne Stroustrup 的“C++ 编程语言:特别版”,在第 133 页上它说明了以下内容:

对于用户定义的类型,将变量的定义推迟到合适的初始化程序可用也可以带来更好的性能。例如:

string s;  /* .... */ s = "The best is the enemy of the good.";

很容易比

string s = "Voltaire";

我知道它可以很容易地说明它,这意味着它不一定会如此,但是我们只是说它确实发生了。

为什么这会带来潜在的性能提升?

是只有用户定义的类型(甚至 STL 类型)如此int,还是float, 等也是如此?

4

7 回答 7

9

我想说这主要是关于具有非平凡默认构造函数的类型,至少就性能而言。

这两种方法的区别在于:

  • 在第一个版本中,首先构造一个空字符串(使用默认构造函数);然后赋值运算符用于有效地丢弃默认构造函数所做的工作,并将新值分配给字符串。
  • 在第二个版本中,在构造点立即设置所需的值。

当然,很难先验地判断这会产生多大的性能差异。

于 2012-01-18T17:29:45.117 回答
7
  1. 执行默认构造函数需要时间。在随后调用的赋值运算符中覆盖它初始化字符串的内容也需要时间。

  2. 当函数(由于return语句或异常)留在默认构造函数和赋值运算符的调用之间时,执行可能永远不会到达赋值。在这种情况下,对象被不必要地默认初始化

  3. 如果抛出异常,实现可能会浪费性能以确保调用对象的析构函数。如果对象在从未到达的后续范围中初始化,则也不需要。

于 2012-01-18T17:29:47.577 回答
1

因为:

string s;  /* .... */ s = "The best is the enemy of the good.";    

涉及两个操作:构造和赋值

尽管:

string s = "Voltaire";   

只涉及建筑。

这相当于在 Constructor body 中选择 Member Initializer 列表而不是 Assignment

于 2012-01-18T17:30:48.063 回答
0

这是个好问题。你是对的,这只发生在复杂类型上。即类和结构,std::string 就是这样一个对象。这里涉及的真正问题与构造函数有关。

当一个对象被创建时,即

std::string s;

它的构造函数被调用,它可能分配一些内存,进行一些其他变量初始化,准备好使用。事实上,在代码中的这一点上可以执行大量代码。

稍后你会这样做:

s = "hello world!";

这导致该类不得不丢弃它所做的大部分工作,并准备用新字符串替换它的内容。

如果您在定义变量时设置值,这实际上简化为单个操作,即:

std::string s = "Hello world";

实际上,如果您在调试器中查看代码,则执行一次不同的构造函数,而不是构造对象,然后分别设置一个值。实际上,前面的代码与以下代码相同:

std::string s("Hello world");

我希望这有助于澄清一些事情。

于 2012-01-18T17:31:05.907 回答
0

考虑在这两种情况下会发生什么。在第一种情况下:

  • 为“s”调用的默认构造函数
  • 赋值运算符调用“s”

在第二种情况下,首先考虑使用复制省略,这等价于string s("Voltaire"),因此:

  • 调用的 c 字符串构造函数

从逻辑上讲,第一种方法需要抽象机器做更多的工作。这是否真正转化为更真实的代码取决于实际类型以及优化器可以做多少。尽管请注意,对于除了微不足道的用户类型之外的所有用户类型,优化器可能不得不假设默认构造函数具有副作用,因此不能简单地删除它。

此额外成本应仅适用于用户类型,因为成本在默认构造函数中。对于任何像 int 这样的原始类型,或者实际上任何带有微不足道的构造函数/副本的类型,默认构造函数都没有成本——数据根本不会被初始化(在函数范围内)。

于 2012-01-18T17:31:44.270 回答
0

为什么这会带来潜在的性能提升?

第一种情况涉及默认初始化,然后是赋值;第二个涉及从值初始化。默认初始化可能会做一些稍后必须通过分配重做(甚至撤消)的工作,因此第一种情况可能比第二种情况涉及更多的工作。

是只有用户定义的类型(甚至 STL 类型)如此,还是 int、float 等也是如此?

只有用户定义的类型才如此;然后它取决于构造函数和赋值运算符的实际作用。对于标量类型,默认初始化什么都不做,而赋值与从值初始化的作用相同,因此两种选择都是等价的。

于 2012-01-18T17:37:25.320 回答
-1

该类有三种初始化字符串的方法:

string  s;         // Default constructor
string  s = "..."; // Default constructor followed by operator assignment
string  s("...");  // Constructor with parameters passed in

字符串类需要分配内存。一旦知道它需要多少内存,最好分配它。

于 2012-01-18T17:30:54.910 回答