std::string
提供const char* c_str ( ) const:
获取C字符串等效项
生成以字符串对象为内容的以null结尾的字符序列(c-string),并将其作为指向字符数组的指针返回。
将自动附加终止空字符。
返回的数组指向一个内部位置,该位置具有此字符序列所需的存储空间及其终止的空字符,但此数组中的值不应在程序中修改,并且只有在下次调用时才会保持不变。字符串对象的非常量成员函数。
他们为什么不定义operator const char*() const {return c_str();}
?
从C ++编程语言20.3.7(强调我的):
转换为C风格的字符串可能是由运算符const char *()而不是c_str()提供的。在这种转换出乎意料的情况下,这将提供隐式转换的便利,但会以惊喜为代价。
我看到隐式转换至少有两个问题:
c_str()
提供的显式转换也足够危险。我已经看到很多情况下指针存储在原始字符串对象的生命周期结束后使用(或者对象被修改从而使指针无效)。通过明确调用c_str()
,您希望了解这些问题。但是通过隐式转换,很容易导致未定义的行为,如:
const char *filename = string("/tmp/") + name;
ofstream tmpfile(filename); // UB
string name;
if (name) // always true
;
name-2; // pointer arithmetic + UB
这些可以通过某种方式避免,但为什么要首先陷入这个麻烦?Josuttis的书中说了以下内容:
这是出于安全原因,以防止导致奇怪行为的意外类型转换(类型
char *
经常具有奇怪的行为)和歧义(例如,在组合string
和C字符串的表达式中,可以将string
转换为char *
反之亦然)。
因为隐式转换几乎从不像您期望的那样表现。它们可以在重载决策中给出令人惊讶的结果,因此通常更好的是提供std :: string的显式转换。
除了规范中提供的基本原理(意外的惊喜)之外,如果你将C API调用与std :: string混合,你真的需要养成使用:: c_ctr()方法的习惯。如果你曾经调用过一个需要const char *的varargs函数(例如:printf或者等价函数),并且你直接传递了一个std :: string(没有调用提取方法),你就不会遇到编译错误(没有类型)检查varargs函数),但是你会得到一个运行时错误(类布局不是与const char *二进制相同)。
顺便说一句,CString(在MFC中)采用相反的方法:它具有隐式转换,并且类布局与const char *(或const w_char *,如果编译宽字符串,即:“Unicode”)二进制兼容。
这可能是因为这种转换会产生令人惊讶和特殊的语义。特别是你引用的第四段。
另一个原因是有一个隐式转换const char* -> string
,这将只是反过来,这将意味着重载决议的奇怪行为(你不应该进行隐式转换A->B
和B->A
)。
因为C风格的字符串是错误的来源和许多安全问题,所以明确地进行转换会更好。