在实现operator + =时使用delete [](堆损坏)

时间:2010-05-02 17:57:15

标签: c++ operator-overloading operators new-operator delete-operator

我一直试图想出这个问题好几个小时了,而且我已经结束了。如果有人能在我做错的时候告诉我,我当然会感激。

我编写了一个简单的类来模拟字符串的基本功能。该类的成员包括一个字符指针 data (指向一个动态创建的char数组)和一个整数 strSize (它包含字符串的长度,sans终止符。)< / p>

由于我使用的是删除,因此我实现了复制构造函数和析构函数。当我尝试实现运算符+ = 时出现问题。 LHS对象正确构建新字符串 - 我甚至可以使用cout打印它 - 但是当我尝试在析构函数中释放数据指针时出现问题:我在指向的内存地址处得到“正常块后检测到的堆损坏”通过 data 数组,析构函数试图解除分配。

这是我完整的课程和测试程序:

#include <iostream>

using namespace std;

// Class to emulate string
class Str {
public:

    // Default constructor
    Str(): data(0), strSize(0) { }

    // Constructor from string literal
    Str(const char* cp) {
        data = new char[strlen(cp) + 1];
        char *p = data;
        const char* q = cp;
        while (*q)
            *p++ = *q++;
        *p = '\0';
        strSize = strlen(cp);
    }

    Str& operator+=(const Str& rhs) {
        // create new dynamic memory to hold concatenated string
        char* str = new char[strSize + rhs.strSize + 1];

        char* p = str;                  // new data
        char* i = data;                 // old data
        const char* q = rhs.data;       // data to append

        // append old string to new string in new dynamic memory
        while (*p++ = *i++) ;
        p--;
        while (*p++ = *q++) ;
        *p = '\0';

        // assign new values to data and strSize
        delete[] data;
        data = str;
        strSize += rhs.strSize;
        return *this;
    }


    // Copy constructor
    Str(const Str& s)
    {
        data = new char[s.strSize + 1];
        char *p = data;
        char *q = s.data;
        while (*q)
            *p++ = *q++;
        *p = '\0';
        strSize = s.strSize;
    }

    // destructor
    ~Str() { delete[] data;  }

    const char& operator[](int i) const { return data[i]; }
    int size() const { return strSize; }

private:
    char *data;
    int strSize;
};

ostream& operator<<(ostream& os, const Str& s)
{
    for (int i = 0; i != s.size(); ++i)
        os << s[i];
    return os;
}


// Test constructor, copy constructor, and += operator
int main()
{
    Str s = "hello";        // destructor  for s works ok
    Str x = s;              // destructor for x works ok
    s += "world!";          // destructor for s gives error
    cout << s << endl;
    cout << x << endl;
    return 0;
}

编辑:加速C ++问题12-1。

4 个答案:

答案 0 :(得分:4)

以下代码块使p指向数组旁边。

while (*p++ = *q++) ;
*p = '\0';

您在复制构造函数中使用的更好(和安全)的解决方案:

while (*q)
    *p++ = *q++;
*p = '\0';

答案 1 :(得分:3)

这里已经有很多好的答案,但值得插入Valgrind作为解决这类问题的工具。如果您可以访问* nix框,Valgrind工具可以成为真正的救星。

只是为了告诉你,这是我在编译和运行你的程序时得到的:

% g++ -g -o test test.cpp
% valgrind ./test
==2293== Memcheck, a memory error detector
==2293== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2293== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
==2293== Command: ./test
==2293==
==2293== Invalid write of size 1
==2293==    at 0x8048A9A: Str::operator+=(Str const&) (test.cpp:36)
==2293==    by 0x8048882: main (test.cpp:82)
==2293==  Address 0x42bc0dc is 0 bytes after a block of size 12 alloc'd
==2293==    at 0x4025024: operator new[](unsigned int) (vg_replace_malloc.c:258)
==2293==    by 0x8048A35: Str::operator+=(Str const&) (test.cpp:26)
==2293==    by 0x8048882: main (test.cpp:82)
==2293== 
helloworld!
hello
==2293== 
==2293== HEAP SUMMARY:
==2293==     in use at exit: 0 bytes in 0 blocks
==2293==   total heap usage: 4 allocs, 4 frees, 31 bytes allocated
==2293== 
==2293== All heap blocks were freed -- no leaks are possible
==2293== 
==2293== For counts of detected and suppressed errors, rerun with: -v
==2293== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 6)
%

你可以看到它指出了另一个答案在这里指出的线条(靠近第36行)。

答案 2 :(得分:2)

while (*p++ = *i++) ; // the last iteration is when i is one past the end
// i is two past the end here -- you checked for a 0, found it, then incremented past it
p--; //here you corrected for this
while (*p++ = *q++) ;// the last iteration is when p and q are one past the end
// p and q are two past the end here
// but you didn't insert a correction here
*p = '\0';  // this write is in unallocated memory

使用类似于您在复制构造函数中使用的习惯用语:

while (*i) *p++ = *i++; //in these loops, you only increment if *i was nonzero
while (*q) *p++ = *q++;
*p = '\0'

答案 3 :(得分:1)

您已经有两个答案指向使您丢弃堆的特定错误。假设这是 家庭作业 或其他一些形式的练习(否则我们都会因为你自己写的字符串类而大喊大叫),这里还有一些要咀嚼的东西为你:

  • 如果您认为需要 注释 代码,请考虑使其 更具表现力
    例如,您可以只写char* p = str; // new data而不是char* new_data = str; 您可以只编写//do frgl,而不是do_frgl();,而不是一大块代码。如果函数是内联的,那么对于生成的代码没有任何区别,但对代码的读者来说有很多不同。
  • 包含标头在内的所有人都可以 从名称空间std转储到全局命名空间 中的所有内容。 That's not a good idea at all.我会避免像瘟疫那样包括你的标题。
  • 您的构造函数应在 初始化列表 中初始化其类成员。
  • 您的Str::Str(const char*)构造函数为同一个字符串调用std::strlen()两次 应用程序代码 尽快 库代码 < / strong>,另一方面,如果您不知道它结束的应用程序,则应尽可能 。你正在编写库代码。
  • size()会员功能是否会返回 负值 ?如果没有,为什么它是有符号整数?
  • 此代码会发生什么:Str s1, s2; s1=s2
  • 那是怎样的:Str str("abc"); std::cout<<str[1];

(如果有人遇到此问题可以考虑更多提示,请随意扩展此内容。)