如果我有功能:
std::string returnString() {
return "Hello, World!";
}
通话:
std::string hello = returnString();
std::cout << hello << std::endl;
产生Hello, World!
。
但是,如果我尝试:
const char* hello = returnString().c_str();
,然后尝试使用:
for (const char* p = hello; *p; ++p ) {
std::cout << *p;
}
std::cout << std::endl;
给我一个错误,说Invalid read of size 1
,这意味着p是NULL
。
是什么原因导致这种行为?
感谢您的帮助。
答案 0 :(得分:6)
(注意:我在这里掩盖了一些细节。如果您想知道我在这里提到的规则的例外情况,请查找“返回值优化”和“复制省略”。不要更改我在此描述的行为虽然回答)。
从函数返回对象时,返回的对象将在调用该函数的行的末尾销毁。情况总是如此。通常,您将像上一片段一样将该对象复制或移动到本地范围内的另一个对象中:
std::string hello = returnString();
在这一行中,returnString
返回一个std::string
对象,hello
从返回的对象中进行移动构造,然后销毁原始对象。
如果您考虑稍微不同的一行,那就是出现问题了:
const char* hello = returnString().c_str();
在这种情况下,returnString
返回一个std::string
对象,您保存了一个指向该char
对象所拥有的std::string
数组的指针,然后是原始{{1} }对象被销毁,将您拥有一个指针的std::string
数组作为对象。
char
保留std::string
返回的指针指向的char
数组的所有权。 c_str
会在其超出范围时删除其拥有的数组,这意味着指向的数组的生存期与std::string
对象的生存期相关。
您可以认为std::string
看起来像这样:
std::string
实际的class string
{
public:
string(const char* str)
: ptr_(new char[strlen(str) + 1])
{
strcpy(ptr_, str);
}
~string()
{
delete[] ptr_;
}
const char* c_str()
{
return ptr_;
}
// other members
private:
const char* ptr_;
};
有点复杂,但是基本思想是相同的。构造std::string
对象时,它将分配一个std::string
数组来保存字符串数据,而当char
对象被破坏时,它将删除该数组。
std::string
方法仅返回指向c_str
内部std::string
数组的指针。仅仅因为您有指向该数组的指针并不意味着在char
对象死亡时也不会删除该数组,仅意味着您有一个指向不再拥有的内存的指针。