【问题标题】:Buffer Overrun with delete []删除缓冲区溢出 []
【发布时间】:2014-07-07 11:31:06
【问题描述】:

我尝试搜索相同的问题,但没有人帮助我。当我运行程序时,我收到“发生缓冲区溢出...”错误。

构造:

Player(char* n)
{
   length = strlen(n);
   name = new char[length+1];

   for(unsigned int i(0); i < length; i++)
       name[i] = n[i];

   name[length] = '\0';
}

目标:

~Player(void)
{
   delete [] name;
}

我有 NULL 终止的字符串并且没有超出范围,有什么问题?

【问题讨论】:

  • 班级是否遵循Rule of Three?如果没有,您很有可能会两次删除同一个缓冲区。使用std::string 为您正确管理动态内存,除非您特别想练习指针杂耍技巧。
  • 你怎么知道是这个特定的代码导致了问题?
  • 相关:Rule of five
  • @MikeSeymour:我还是有点慢。你当然是对的。

标签: c++ string char buffer delete-operator


【解决方案1】:

您发布的代码中没有明显的错误,但是尝试通过处理原始指针来管理动态内存几乎不可避免地会导致这样的错误。

根据Rule of Three,您可能没有正确实现或删除复制构造函数和复制赋值运算符。在这种情况下,复制一个Player 对象将给出两个具有指向同一个数组的指针的对象;他们都将尝试删除该数组,给出未定义的行为。

最简单的解决方案是使用专为管理字符串而设计的类来管理您的字符串。把name的类型改成std::string,然后构造函数就可以简单的像

explicit Player(std::string const & n) : name(n) {}

根本不需要声明析构函数(或移动/复制构造函数/赋值运算符)。

【讨论】:

    【解决方案2】:

    所以...已经提供了使用std::string 的解决方案,但让我提供另一个解决方案,保持您的成员变量不变。

    问题是这样的。假设你在某处有这段代码:

    Player p1("Bob"); // Okay
    Player p2("Annie"); // Okay
    p2 = p1; // Oops! (1)
    Player p3(p1); // Oops! (2)
    

    在 (1) 处,方法 Player&amp; Player::operator=(const Player&amp;) 被调用。由于您没有提供一个,因此编译器会为您生成一个。当它这样做时,它只是假设它可以复制所有成员变量。在这种情况下,它会复制Player::namePlayer::length。所以,我们有p1.name == p2.name。现在当p2的析构函数被调用时,p2.name指向的分配内存被删除。那么当p1的析构函数被调用时,相同的内存会被删除(因为p1.name == p2.name)!这是违法的。

    要解决这个问题,您可以自己编写一个赋值运算符。

    Player& Player::operator = (const Player& other)
    {
        // Are we the same object?
        if (this == &other) return *this;
    
        // Delete the memory. So call the destructor.
        this->~Player();
    
        // Make room for the new name.
        length = other.length;
        name = new char[length + 1];
    
        // Copy it over.
        for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
        name[length] = '\0';
    
        // All done!
        return *this;
    }
    

    在 (2) 处,会出现同样的问题。您没有复制构造函数,因此编译器会为您生成一个。它还将假设它可以复制所有成员变量,因此当调用析构函数时,它们将尝试再次删除相同的内存。要解决这个问题,还要编写一个复制构造函数:

    Player::Player(const Player& other)
    {
        if (this == &other) return;
        length = other.length;
        name = new char[length + 1];
        for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
    }
    

    在一天结束时,您应该使用std::string

    【讨论】:

    • 谢谢!我离这个问题太近了,但我没有这样做,因为 10 分钟前读过它;)突然发现我没有用“删除 []”出错。对于不正确的问题,我很抱歉,但这些答案解决了我不知道的问题。再次感谢!
    • 这不是实现复制语义的最佳示例。特别是,不要调用析构函数——它会给出未定义的行为(即使你可能会在这里侥幸逃脱)。我希望有合理的异常安全性;如果内存分配失败,这会给分配目标留下一个悬空指针。考虑 copy-and-swap idiom 使用复制构造函数进行安全分配,而不是手动编码这两个函数。无需在构造函数中检查相同的对象,因为您正在初始化一个新对象。
    猜你喜欢
    • 2015-12-16
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    • 2013-11-06
    • 2013-04-11
    • 2015-07-07
    • 2012-02-05
    • 2013-07-21
    相关资源
    最近更新 更多