7

Why do std::string::data and std::string::c_str() return pointers to const chars, while std::string::operator[] returns references to mutable chars?

std::string string("eightfold is the greatest");

auto s = string.data();
*s = 'r'; // illegal

auto t = &string[0];
*t = 'r'; // totally fine

auto& c = string[0];
c = 'r'; // totally fine

Why don’t std::string::data() and std::string::c_str() return char*, or why doesn’t std::string::operator[] return char const&?

What is the rationale behind this?

4

2 回答 2

14

operator []使您可以直接访问受控的std::string对象序列。c_str()原来没有。

在存储序列的原始规范中std::string,不需要以零结尾的字符串。这意味着在一般情况下c_str()无法返回指向存储序列的直接指针。它必须返回一个指向完全独立、单独分配的受控序列临时副本的指针(添加了零终止符)。出于这个原因,尝试修改返回的 C 字符串c_str()完全没有意义。应用于该单独 C 字符串的任何修改都不会传播到实际的受控序列。(事实上​​,规范明确禁止任何修改尝试。例如,对于空std::string的实现可以简单地返回指向字符串文字的指针"",这当然是不可修改的,并且可以很容易地在所有对象之间共享。)因此,返回std::string是非常有意义的。c_str()const char *

C++11 更改了c_str()使其返回指向实际受控序列的直接指针的内部规范。但外部规范c_str()保持不变,以使其与旧规范保持一致。

于 2013-11-03T19:58:25.497 回答
3

由于历史原因,C++ 及其标准库支持 C-strings(字符数组),并且许多 C++ 代码使用 C-strings 进行输入和输出。

您还可以想象 std::string 的一种可能实现,它将其数据保存在字符数组中。这通常是一个完全私有的实现细节,不会通过类的公共接口公开。

编辑:明确地说,一个类通常不会公开其私有数据的非常量视图。要了解为什么这会是一个问题,请想象以下代码:

std::string s("abc");  
char* ps = s.c_str();  //  ps[0] == 'a' and ps[3] == '\0'
ps[3] = 'd';  // string is not null terminated
printf("%s", s.c_str());  // printing non-terminated string.

这样的更改将允许类的用户以破坏不变量的方式更改其私有数据,即以下不变量:“用于存储的字符缓冲区将以空值终止。”

的部分约定operator[]是调用者不得提供大于或等于字符串长度的参数。at(size_t pos)成员函数通过抛出异常来强制进行边界检查。std::string::operator[]仍然可以不安全地使用,但至少可以记录一个 contract,这与ps[3].

编辑结束

但为了支持与需要const char*C 字符串的函数的互操作性,std::string公开此字符缓冲区。

当然,与 一样std::vector,用户可能想要修改字符串中的单个元素(字符),这就是字符串提供operator[].

(实际上,string实现通常有一个固定长度的字符缓冲区,它们在内部保留,然后如果字符串的内容超过该固定长度,则在堆上“重新分配”。这称为“小字符串优化”。)

data()你可能会问,当有一个完全可用的成员函数时,为什么会有一个成员c_str()函数?我认为这是为了简化通用编程: std::array并且std::vector还具有data()成员函数,并且std::strings 旨在像容器一样工作。

于 2013-11-03T19:49:17.180 回答