何时应该使用std::string而不是字符数组?

34

当我在设计类接口时,经常会思考是否应该使用const char*或者const std::string&,但最终我常常感觉两种方式差不多。

请看下面的两个函数原型:

void foo(const char* str);
void foo(std::string str);
如果 foo 函数需要存储字符串,我认为第二种选择是更好的,因为可以传递字符串并在可能的情况下利用移动语义。但是,如果 foo 只需要读取字符串,那么 const char* 的解决方案是否更好呢?
从性能角度来看,不需要创建临时的 std::string。然而,使用已经存在的字符串作为参数调用函数会显得有些突兀:foo(mystr.c_str())。更糟糕的是,如果在将来某个时间点需要在数组上执行更高级的操作,或者需要存储副本,则接口必须进行更改。
所以我的问题是:
是否有明确定义的、个人或其他的惯例来规定何时应该选择 std::string 还是 const char*?此外,在开始一个新项目时,最好保持使用一致性还是根据当前代码块选择最合适的方法?

14
一个简单的规则是:如果你不确定,最好使用std::string - Cheers and hth. - Alf
7
如果你不想创建临时字符串,可以使用void foo(const std::string &bar) - Red Alert
2
@RedAlert,调用foo("hello")会使用std::string变量创建一个临时变量,是这样吗? - vmrob
1
string是用来处理字符串的,const char *只适用于常量字符串字面值。对于字符串变量,使用char *是不可取的。99.9%的情况下速度并不重要,并且如果你的字符串有15个字符,它们必须被存储在某个地方,没有任何魔法可以帮助你节省这个存储空间。通过引用传递参数不会复制数据,如果你一直通过值传递参数,那么你做得很糟糕。 - Daniel Daranas
8
总是更喜欢使用std::string。这样可以在以后避免很多麻烦。相信我。 - lethal-guitar
显示剩余4条评论
7个回答

53

const char*是对C语言的一种遗留物。在良好的C++中,我认为它唯一有用的地方就是在extern "C" API中使用。

std::string 有许多优点:

  1. 它提供了一个常数时间的 size() 函数。发现 const char* 的长度需要花费线性时间。

  2. 它保证是有效的。必须检查 const char* 是否为null,并且很可能传递不正确的数据——缺少空终止符数据。这种情况几乎肯定会导致崩溃或更糟。

  3. 它与标准算法兼容。

如果你担心调用函数而创建std::string所带来的性能影响,请考虑采用标准库使用的方法——将函数改为接受一对迭代器。然后可以提供一种方便的重载,采用 const std::string& 委托给迭代器对应的函数。


2
这里使用string更好。比较一下myFunc(const char*)和myFunc(string)。 - DarkWanderer
2
@DarkWanderer,您是指 myFunc(std::string) 函数。 - Miles Rout
@DarkWanderer 不行。在头文件中键入 using anyNamespaceOrType 是一个非常糟糕的想法,我希望我不必解释原因。这样的建议具有误导性,尤其是带有强制性词语“必须”。实际上,许多人,包括我在内,甚至都不会在任何地方使用 using std - underscore_d
在这种情况下,“误导性”的含义是使用过于宽泛的陈述。请解释为什么“using std::string”是一个不好的选择(而不是“using namespace std”,而是确切地说“std::string”)。 - DarkWanderer
@DarkWanderer 熟悉和清晰度。这是主要原因。如果我查看代码并看到 std::string,那么它立即就很明显了。如果我看到 string,我就必须要想一下:是否有某个地方使用了 using?或者它是一个 typedef 或自定义提供的类,也许模仿了 std::string,也许没有? - Angew is no longer proud of SO
显示剩余5条评论

21

根据我个人的经验,如果你正在处理C++项目(与您添加的标签相同),请尽可能使用提供的std::string。不要试图重新发明最基本的结构——万能字符串。我见过许多项目都在重新发明基本字符串后花费数月时间来微调它。

如果你在C++项目中引入了一个char*变量,那么你将会退回到标准的C函数,例如strlen()strcpy(仅举两个例子...)。从这一点开始,你的项目将开始变得混乱,需要手动管理内存等等...

如果你需要与接受const char*作为参数的第三方库进行交互(我假设你信任这些库——即你相信它们不会用const_cast去除常量性并对你的可怜字符串进行恶意操作),你可以使用std::string::c_str()方法获取字符串的const char*

如果你需要与具有接受char*方法的库进行交互,我强烈建议你复制你的字符串的c_str(),并将其用作库的输入参数(当然,不要忘记删除额外的副本)。

除了这些额外的点,我同意Angew的回答中的三个观点。


谢谢您的建议,它真的很有帮助。 - vmrob
这也是我的答案,特别是“如果你发现自己需要C函数,就直接使用std :: string”这一部分。 - Mr Lister

6

std::string在C++中应始终是首选。为避免多余的复制开销,您几乎应该始终将参数作为引用和const引用传递,无论何时何地。

在这种情况下,您的函数签名将变成这样

void foo(std::string &str);

如果参数是const,则会变成这样

void foo(const std::string &str);

这里有关于const关键字的好处,您可以在这里查看


6
不,如果在C++11中无论如何都需要复制字符串,那么应该通过传值而不是const引用来传递。 - Siyuan Ren
有时候,对于内置函数来说,通过引用传递并不是最好的选择,请参考https://dev59.com/Wm435IYBdhLWcg3wjw3S。 - ilent2
是的,只有在可能的情况下才这样做,因为它可以提高API的可读性。 - Abhishek Bansal

4

不要使用

void foo(std::string str); 

您可以使用以下方法:
void foo(const std::string& str);

这句话的意思是“相当于”,可以表示两个事物在某个方面有相同的作用或效果。
void foo(const char* str);

就使用情况而言,传递引用时不会分配任何内存。但对于 C++ 中的字符串,我肯定会使用 std::string。对于随机数据或 C 兼容接口,我则不建议使用。


2
如果传递了字符串字面量(需要创建),则const std::string&可能需要新数据。 const char *可以避免在运行时进行此分配。 - Ben
1
我使用 const std::string& 唯一的问题是当我知道需要制作副本时。这就是选择 std::string 的理由。 - vmrob
@Ben 当使用字符串字面量调用函数时,也会创建一个 char[]。使用 char* 没有任何优势。 - Theolodis
1
@Theolodis 这样做会破坏C++11中移动语义的优势。 - vmrob
1
@Theolodis 基本上,如果你知道你要复制它,你应该使用 std::string。像 foo(some_func_that_returns_a_string()) 这样的调用会通过移动语义直接将其结果传递到下一个调用中。在许多情况下,甚至可以省略移动操作。请参见 https://dev59.com/QXA75IYBdhLWcg3wy8Qi - vmrob
显示剩余2条评论

4

在需要管理分配、释放和大小相关问题的情况下,std::string比使用原始char *更好,需要小心任何溢出、下溢和越过大小边界的问题。而通过抽象,std::string可以处理所有这些问题。


3
如果你想更好地处理你的数据(在你的情况下是字符串),我建议使用char *。它将为您提供更好的字符串访问和内存利用率。但如果你不必担心性能和内存管理问题,那么你可以轻松地使用std::string。

这对我来说是一个难以支持的立场,因为大多数情况下,性能和内存管理并不重要到足以导致混乱的代码或潜在的编程错误。 - vmrob
同意...但是如何编写代码(混乱或易于理解)取决于程序员。尽管如果我们考虑更大的图像,那么内存利用率更重要[例如,在网络上传输数据]。 - A J

1

好的,我会稍微改写一下你的问题。你应该问何时应该使用字符数组而不是std::string

因为除非不能使用string,否则应始终使用string。所以应该使用字符数组而不是string的情况有:

  1. 使用支持C的API,因此无法使用string
  2. exedll之间传递数据。不建议在exedll之间交换具有构造函数和/或析构函数的数据类型。

如果您担心性能问题,在启用所有优化后编译,string 就变得类似于字符数组,有时甚至可以在某些情况下提供更好的性能,例如如果您需要获取它包含的字符数。
此外,关于性能,您始终可以通过引用传递,正如其他答案所提到的那样,如果您必须传递指向字符数组的指针,您可以使用方法 c_str(),它返回指向第一个字符的 const 指针,如果您必须传递指针(而不是 const 指针),您可以(但实际上不应该)像这样传递: &str[0]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接