C++ 在范围结束前删除函数指针
C++ deleting function pointer before end of scope
如果我有一个函数:
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
。
是什么导致了这种行为?
感谢您的帮助。
(注意:我在这里掩盖了一些细节。如果您想了解我在这里提到的规则的例外情况,请查看 Return 价值优化和复制省略。不要改变我在这个答案中描述的行为)。
当您从函数中 return 一个对象时,returned 对象在调用该函数的行的末尾被销毁。这是总是的情况。通常,您将将该对象复制或移动到本地范围内的另一个对象,如上一个片段所示:
std::string hello = returnString();
在这一行中,returnString
returns 一个 std::string
对象,hello
是从 returned 对象移动构造的,然后是原始对象被销毁。
如果你考虑一条稍微不同的线,那就是问题出现的时候:
const char* hello = returnString().c_str();
在这种情况下,returnString
return 是一个 std::string
对象,您将指针保存到该 std::string
对象拥有的 char
数组,然后原始的 std::string
对象被销毁,带走你有一个指向它的 char
s 的数组。
std::string
保留 c_str
指针 return 指向的 char
数组的所有权。 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
方法只是 return 指向 std::string
的内部 char
数组的指针。仅仅因为你有一个指向那个数组的指针并不意味着它不会在 std::string
对象死亡时被删除,它只是意味着你有一个指向你不再拥有的内存的指针。
如果我有一个函数:
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
。
是什么导致了这种行为?
感谢您的帮助。
(注意:我在这里掩盖了一些细节。如果您想了解我在这里提到的规则的例外情况,请查看 Return 价值优化和复制省略。不要改变我在这个答案中描述的行为)。
当您从函数中 return 一个对象时,returned 对象在调用该函数的行的末尾被销毁。这是总是的情况。通常,您将将该对象复制或移动到本地范围内的另一个对象,如上一个片段所示:
std::string hello = returnString();
在这一行中,returnString
returns 一个 std::string
对象,hello
是从 returned 对象移动构造的,然后是原始对象被销毁。
如果你考虑一条稍微不同的线,那就是问题出现的时候:
const char* hello = returnString().c_str();
在这种情况下,returnString
return 是一个 std::string
对象,您将指针保存到该 std::string
对象拥有的 char
数组,然后原始的 std::string
对象被销毁,带走你有一个指向它的 char
s 的数组。
std::string
保留 c_str
指针 return 指向的 char
数组的所有权。 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
方法只是 return 指向 std::string
的内部 char
数组的指针。仅仅因为你有一个指向那个数组的指针并不意味着它不会在 std::string
对象死亡时被删除,它只是意味着你有一个指向你不再拥有的内存的指针。