2012-01-18 8 views
10

目前我正在读的“The C++编程语言:特别版”由Bjarne Stroustrup的和133页上它指出以下几点:建议的速度提升与价值立即定义字符串时,而不是拖延

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

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

可以很容易地超过

string s = "Voltaire"; 

慢得多,我知道它指出可以轻松,这意味着它并不一定是这样的,但是我们只能说它确实发生。

为什么会这样做潜在性能增加?

仅仅对于用户定义的类型(甚至是STL类型)或者int,float等也是如此?

+0

除了下面的答案,这可以帮助:http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.22 – dsign 2012-01-18 17:32:58

回答

9

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

这两种方法之间的区别在于:

  • 在第一版本中,一个空字符串首先构造(使用默认构造);那么赋值运算符用于有效地抛弃默认构造函数完成的工作,并将新值赋给字符串。
  • 在第二个版本中,所需值在建造时立即设置。

当然,很难先验地告诉它会有多大的性能差异。

+0

性能差异是值得注意的用户定义类型。几乎没有任何POD类型。 – 2012-01-18 17:51:08

7
  1. 这需要时间来执行默认的构造函数。在随后的中,重写初始化字符串的内容需要时间,这也是所调用的赋值运算符

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

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

+1

我认为范围问题是最重要的问题,所有其他答案都已经避开。从Stroustrup中不明显的是,在后一种情况下,您可能完全避免使用该变量。 – 2012-01-18 17:52:56

-1

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

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

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

+2

第二种情况是复制初始化,并且从不使用默认构造函数或赋值运算符。它在技术上是通过临时转换构建的,然后复制's'的构造,但通常会省略多余的副本,使其等同于直接初始化(情况3)。 – 2012-01-18 17:33:12

0

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

当创建一个对象,即

std::string s; 

它的构造函数被调用时,它可能分配一些内存,做一些其他变量的初始化,获取自身准备使用。实际上,代码中的这一点可能会执行大量的代码。

你后来做:

s = "hello world!"; 

这会导致类要扔掉的大部分东西,它的完成,并准备更换它与一个新的字符串的内容。

这实际上减少到一个单一的操作,如果你设置当变量定义的值,即:

std::string s = "Hello world"; 

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

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

我希望能够帮助我们澄清一些问题。

+1

实际上,一个好实现的'string'默认构造函数可能不一定分配内存。我确定gcc的'vector'不知道。 – 2012-01-18 17:51:38

+1

实际成本可能是分配比构造更复杂的事实,因为它必须考虑处理和/或重新使用字符串的以前状态。构造函数知道没有以前的状态。 – 2012-01-18 19:36:01

0

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

  • 默认构造函数呼吁“S”
  • 赋值运算符要求“S”

在第二种情况下,首先考虑的是用复制省略这相当于string s("Voltaire") ,从而:

  • C-字符串构造称为

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

由于成本是在默认构造函数中,所以此额外费用仅适用于用户类型。对于任何像int这样的基本类型,或者实际上任何具有简单构造函数/复制的基本类型,缺省构造函数都没有成本 - 数据不会被初始化(当在函数作用域中时)。

0

为什么这会使潜在的性能提高?

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

只有用户自定义类型(甚至是STL类型)才这样吗?或者int,float等也是这样吗?

它只对用户定义的类型是这样;然后它取决于构造函数和赋值操作符实际做了什么。对于标量类型,默认初始化什么都不做,分配和初始化的值是一样的,所以两个选项都是等价的。