【问题标题】:C++ deleting function pointer before end of scopeC ++在范围结束之前删除函数指针
【发布时间】:2019-05-16 06:47:16
【问题描述】:

如果我有一个函数:

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

是什么导致了这种行为?

感谢您的帮助。

【问题讨论】:

  • 这个关于 OpenGL 的问题怎么样?
  • "const char* vertSource = read_file(m_FragPath);"的区别在于它是无效的吗?
  • 编辑了标签和问题,但它应该是const char* vertSource = read_file(m_FragPath).c_str();

标签: c++ scope function-pointers destructor


【解决方案1】:

(注意:我在这里忽略了一些细节。如果您想知道我在这里提到的规则的例外情况,请查看返回值优化和复制省略。不要更改我在此描述的行为回答)。

当您从函数返回对象时,返回的对象在调用函数的行的末尾被销毁。这是总是的情况。通常,您会将该对象复制或移动到本地范围内的另一个对象中,就像您上次的 sn-p 一样:

std::string hello = returnString();

在这一行中,returnString 返回一个std::string 对象,hello 从返回的对象中移动构造,然后销毁原始对象。

如果你考虑稍有不同的线路,那就是问题出现的时候:

const char* hello = returnString().c_str();

在这种情况下,returnString 返回一个std::string 对象,你保存一个指向该std::string 对象拥有的char 数组的指针,然后原来的std::string 对象被销毁,取@ 的数组987654331@s 你有一个指向它的指针。


std::string 保留由c_str 返回的指针所指向的chars 数组的所有权。 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 对象时,它会分配一个chars 数组来保存字符串数据,而当std::string 对象被销毁时,它会删除该数组。

c_str 方法只返回一个指向std::string 内部char 数组的指针。仅仅因为你有一个指向该数组的指针并不意味着当 std::string 对象死亡时它不会被删除,它只是意味着你有一个指向你不再拥有的一些内存的指针。

【讨论】:

  • 很好的解释,除了对于 C++,换行符就像任何其他空格一样。预处理器只关心行,其他什么都不关心。特别是,谈论行可能会导致对for (const char* p = f().c_str(); *p, ++p ) { ... } 之类的代码的误解p 的所有用法都与临时std::string 在同一行中,甚至是同一for 语句的一部分。由于终身问题,它仍然是UB。
  • @BenVoigt 虽然使用“语句”而不是“行”会更正确,但我觉得用“什么是语句?”的解释使答案复杂化了。只会分散我试图解释的核心概念。因此我选择使用通俗的“线”来保持简单。
  • 感谢您的详尽解释。所以为了简化,如果函数完全绕过std::string并返回一个C字符串,引用不会被删除?
  • @MilesBudnek @BenVoigt 好的。因此,当对象被销毁时,由于delete ptr_std::string 的析构函数中的等效调用,该数组被删除。这不会发生在string hello = returnString() 行中,因为它会在删除之前复制对象。再次感谢。
  • @jackw11111 就是这样,是的。在某些情况下,从returnString 返回的对象可能会将chars 的同一数组的所有权转移到hello(或者它们可能根本不作为不同的对象存在,请参阅我关于复制省略的说明),但这就是您将观察到的行为。如果你没有从returnString的返回值初始化std::string对象,那么c_str返回的chars数组将在下一个;不存在。
猜你喜欢
  • 2011-08-20
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
  • 2018-02-23
  • 2011-04-20
  • 2010-11-26
  • 2014-05-31
  • 1970-01-01
相关资源
最近更新 更多