为什么data()和c_str()返回char const *,而operator []返回char&?

时间:2013-11-03 19:42:30

标签: c++ string const

为什么std::string::datastd::string::c_str()会返回指向 const 字符的指针,而std::string::operator[]会返回对 mutable 字符的引用?

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

为什么std::string::data()std::string::c_str()不返回char*,或为什么std::string::operator[]不返回char const&

这背后的理由是什么?

2 个答案:

答案 0 :(得分:14)

operator []可让您直接访问std::string对象的受控序列。 c_str()最初没有。

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

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

答案 1 :(得分:2)

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

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

编辑:显而易见,类通常不会公开其私有数据的非const视图。要了解这可能是一个问题,请想象以下代码:

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) member function通过抛出异常来强制执行边界检查。 std::string::operator[]仍然可以不安全地使用,至少可以document a contract,与ps[3]中的指针解引用运算符不同。

编辑结束

但是为了支持与期望const char* C字符串的函数的互操作性,std::string公开了这个字符缓冲区。

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

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

当有完全可维护的data()成员函数时,为什么会有c_str()成员函数?我认为这是为了简化通用编程:std::arraystd::vector也有data()个成员函数,而std::string被设计成像容器一样。