我尽可能使用std::string
而不是char*
,但我担心我的性能可能会降低太多。这是返回字符串的好方法(为简洁起见没有错误检查)吗?
std::string linux_settings_provider::get_home_folder() {
return std::string(getenv("HOME"));
}
另外,一个相关问题:当接受字符串作为参数时,我应该将其作为const std::string&
或const char*
收到吗?
感谢。
答案 0 :(得分:63)
返回字符串。
我认为更好的抽象是值得的。在你能衡量一个有意义的表现差异之前,我认为这是一种微观优化,只存在于你的想象中。
在C ++中获得良好的字符串抽象花了很多年。我不相信Bjarne Stroustroup,因其保守的“只为你使用的东西买单”而闻名,可能会让这种语言成为明显的表演杀手。更高的抽象是好的。
答案 1 :(得分:14)
像所有人一样回复字符串。
接受字符串作为参数时,我应该将其作为const std::string&
或const char*
收到吗?
我会说通过引用获取任何const参数,除非它们的重量轻,足以按值获取,或者在极少数情况下需要空指针作为有效输入,意味着“以上都不是”。此政策并非特定于字符串。
非const引用参数是有争议的,因为从调用代码(没有好的IDE),您无法立即看到它们是通过值还是通过引用传递,并且差异很重要。所以代码可能不清楚。对于const params,这不适用。阅读调用代码的人通常可以假设这不是他们的问题,所以他们只是偶尔需要检查签名。
如果您要在函数中获取参数的副本,则一般策略应该是按值获取参数。然后你已经有了一个可以使用的副本,如果你将它复制到一个特定的位置(比如数据成员)那么你可以移动它(在C ++ 11中)或交换它(在C ++ 03中)到把它弄到那里。这为编译器提供了优化调用者传递临时对象的最佳机会。
特别针对string
,这涵盖了您的函数按值std::string
获取的情况,并且调用者将参数表达式指定为字符串文字或指向a的char*
以空字符结尾的字符串。如果您使用const std::string&
并将其复制到函数中,则会导致构造两个字符串。
答案 2 :(得分:12)
按值复制字符串的成本因您正在使用的STL实现而异:
MSVC下的std :: string使用短字符串优化,因此短字符串(< 16个字符iirc)不需要任何内存分配(它们存储在std :: string本身中),每次复制字符串时,较长的需要堆分配。
GCC下的std :: string使用引用计数实现:从char *构造std :: string时,每次都进行堆分配,但是当通过值传递给函数时,引用计数只是递增,避免内存分配。
一般来说,你最好忘记上面的内容并按值返回std :: strings,除非你每秒都做数千次。
re:参数传递,请记住,从char * - > std :: string开始会有成本,但不会从std :: string-> char *开始。一般来说,这意味着你最好接受对std :: string的const引用。但是,接受const std :: string&的最佳理由是作为一个参数是,然后被调用者不必有额外的代码用于检查与null。
答案 3 :(得分:10)
似乎是一个好主意。
如果这不是实时软件(如游戏)的一部分,而是常规应用程序,那么你应该会更好。
答案 4 :(得分:6)
担心性能是人的本性,特别是当编程语言支持低级优化时。 作为程序员,我们不应该忘记的是,程序性能只是众多我们可以优化和欣赏的一件事。除了程序速度,我们可以在自己的表现中找到美感。我们可以尽量减少我们的努力,同时努力实现最大的视觉输出和用户界面互动性。你是否认为这可能是更长时间担心比特和周期的动机......所以是的,返回字符串:s。它们可以最大限度地减少您的代码大小和您的工作量,并减少您的工作量。
答案 5 :(得分:5)
在您的情况下,将发生返回值优化,因此不会复制std :: string。
答案 6 :(得分:4)
当你跨越模块边界时要小心。
然后最好返回原始类型,因为即使不同版本的同一编译器,C ++类型也不一定是二进制兼容的。
答案 7 :(得分:3)
我同意其他海报,你应该使用字符串。
但是要知道,根据你的编译器如何积极地优化临时值,你可能会有一些额外的开销(而不是使用动态的字符数组)。 (注意:好消息是,在C ++ 0a中,明智地使用rvalue引用不需要编译器优化来购买效率 - 程序员将能够在不依赖于代码的质量的情况下对其代码进行一些额外的性能保证。编译器。)
在您的情况下,额外的开销是否值得引入手动内存管理?大多数合理的程序员都不同意 - 但是如果你的应用程序确实最终遇到了性能问题,那么下一步就是对你的应用程序进行分析 - 因此,如果你确实引入了复杂性,那么只有在你有充分的证据证明它需要改进时才会这样做整体效率。
有人提到返回值优化(RVO)与此无关 - 我不同意。
标准文本(C ++ 03)上的内容为(12.2):
[开始标准报价]
类类型的临时数在各种上下文中创建:将rvalue绑定到引用(8.5.3),返回rvalue(6.6.3),创建rvalue的转换(4.1,5.2.9,5.2.11) ,5.4),抛出异常(15.1),进入处理程序(15.3),并在一些初始化(8.5)中。 [注意:异常对象的生命周期在15.1中描述。 ]即使在避免创建临时对象时(12.8),也都是语义 必须遵守限制,就像创建临时对象一样。 [示例:即使未调用复制构造函数,也应满足所有语义限制,例如可访问性(第11节)。 ]
[Example: struct X { X(int); X(const X&); ˜X(); }; X f(X); void g() { X a(1); X b = f(X(2)); a = f(a); }
这里,一个实现可能会使用一个临时构造X(2),然后使用X的copy-constructor将它传递给f();或者,X(2)可以在用于保存参数的空间中构造。另外,临时可能用于保存f(X(2))的结果,然后使用X的copyconstructor将其复制到b;或者,f()的结果可能在b中构造。另一方面,表达式a = f(a)要求参数a或f(a)的结果都是临时的,以避免不希望的别名 一个。 ]
[最终标准报价]
基本上,上面的文字说明你可能在初始化情况下依赖RVO,但在分配情况下则不然。原因是,当你初始化一个对象时,没有办法将你初始化它的对象本身别名(这就是为什么你永远不会在复制构造函数中进行自检),但是当你做一项任务,它可以。
您的代码没有任何内容,本质上禁止使用RVO - 但是如果确实需要,请阅读编译器文档以确保您可以真正依赖它。
答案 8 :(得分:1)
我同意duffymo。您应首先制作可理解的工作应用程序,然后在需要时进行攻击优化。正是在这一点上,您将了解主要瓶颈所在,并能够更有效地管理您制作更快的应用程序的时间。
答案 9 :(得分:1)
我同意@duffymo。在测量之前不要进行优化,这在进行微优化时保持双重。始终:在之前衡量,在之后衡量,看看您是否真的改变了一切。
答案 10 :(得分:1)
返回字符串,在性能方面没有那么大的损失,但它肯定会让你的工作变得轻松。
另外,你总是可以内联函数,但大多数优化器都会修复它。
答案 11 :(得分:1)
如果您传递一个引用的字符串并且您处理该字符串,则不需要返回任何内容。 ;)