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 对象被销毁,带走你有一个指向它的 chars 的数组。


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 对象死亡时被删除,它只是意味着你有一个指向你不再拥有的内存的指针。