如何在析构函数中使用 delete[]

How to use delete[] in destructor

我有以下用于实现 Bucket class 的文件。但是,我无法在 operator+ 生成的 Bucket 的析构函数中销毁 _str 成员。我得到的错误是:

heap corruption detected after normal block...CRT detected that the application wrote to memory after end of heap buffer

问题总是在添加两个 Bucket 对象后出现,但是我在调​​试过程中检查了生成的字符串以及长度是否正确。

此外,如何访问 operator>> 函数中的 _len 属性以分配新值?

头文件:

class Bucket
{
    friend ostream& operator<<(ostream& os, const Bucket& c);
    friend istream& operator>>(istream& is, const Bucket& c);
public:
    Bucket();
    Bucket(const String& str);
    Bucket(const char* str);
    ~Bucket();
    const Bucket operator+(const Bucket& s) const;
    const Bucket operator+(const char* s) const;
    const Bucket& operator=(const char* c);
    const bool operator==(const char* str);
private:
    char* _str;
    int _len;
}; 

源文件:

Bucket::Bucket() {
    _len = 0;
    _str = new char[_len];
}

Bucket::Bucket(const Bucket& myBucket) {
    _len = myBucket._len;
    _str = new char[_len + 1];
    for (int i = 0; i < _len; i++)
        _str[i] = myBucket._str[i];
    _str[_len] = '[=11=]';
}

Bucket::Bucket(const char* str) {
    _len = 0;
    for (int i = 0; str[i] != '[=11=]'; i++) _len++;
    
    _str = new char[_len + 1];
    for (int i = 0; i < _len; i++)
        _str[i] = str[i];
    _str[_len] = '[=11=]';
}

Bucket::~Bucket() {
    if (_len > 0) delete[] _str;
}

const Bucket Bucket::operator+(const Bucket& myBucket) const {
    String tempBucket;
    tempBucket._str = strcat(this->_str, myBucket._str);
    int len = 0;
    
    while (tempBucket._str[len] != '[=11=]') len++;
    tempBucket._str[len] = '[=11=]';
    tempBucket._len = len;
    return tempBucket;
}

const Bucket Bucket::operator+(const char* str) const {
    Bucket tempBucket;
    
    int len = 0;

    tempBucket._len = this->_len;

    for (int i = 0; str[i] != '[=11=]'; i++) tempBucket._len++;

    tempBucket._str = strcat(tempBucket._str, str);

    tempBucket._str[len] = '[=11=]';
    tempBucket._len = len;
    return tempBucket;
}

const Bucket& Bucket::operator=(const char* str) {
    if (this->_str == str) {
        return *this;
    }
    
    _len = 0;
    for (int i = 0; str[i] != '[=11=]'; i++) _len++;

    _str = new char[_len + 1];
    for (int i = 0; i < _len; i++)
        _str[i] = str[i];
    _str[_len] = '[=11=]';

    return *this;
}

const bool Bucket::operator==(const char* str) {
    int comp = strcmp(this->_str, str);
    if (comp == 0) {
        return true;
    }
    else {
        return false;
    }
}

ostream& operator<<(ostream& os, const Bucket& myBucket) {
    os << myBucket._str;
    return os;
}

istream& operator>>(istream& is, const Bucket& myBucket) {
    static char buffer[40];
    is >> buffer;

    int len = 0;
    for (size_t i = 0; buffer[i] != '[=11=]'; i++) {
        myBucket._str[i] = buffer[i];
        len++;
    }
    myBucket._str[len++] = '[=11=]';

    return is;
}

主要:

int main()
{
    Bucket b1("Hello, "); // This is deleted by the destructor
    Bucket b2(b1); // This is deleted by the destructor
    cout << b1 << b2 << endl; 
    b2 = "Dear ";  // Also works fine
    Bucket b3;
    cout << "Enter a name: ";
    cin >> b3; // Can't assign the _len attribute
    cout << b2 + b3 << ",";  // not destroyed
    Bucket b4(" Please write this sentence after pressing enter:\n");
    b2 = "We believe that ";
    cout << b4 + b2 << b1 << "and " << "Goodbye "  // not destroyed
        << (b1 == "Goodbye " ? "is " : "is not ") << "the same word!\n" <<
        endl;
    return 0;
}

我发现您的 Bucket 代码的实施方式存在很多问题:

  • operator>>的第二个参数需要是对non-const对象的引用,否则运算符无法修改从 istream.

    读取数据时的对象
  • 缺少复制构造函数和复制赋值运算符,最好还有移动构造函数和移动赋值运算符,根据 Rule of 3/5/0

  • 默认构造函数使用 new[] 分配内存,但由于 _len 为 0,析构函数不会释放该内存。您需要在调用 delete[].

    时删除对 _len > 0 的检查
  • 默认构造函数不是 null-terminating 分配的数组,其他构造函数正在这样做。

  • operator+ 正在返回一个新的 Bucket 对象 按值 (应该是),因此将该对象标记为 const 是多余的。

  • booloperator==returns。但是,operator 本身应标记为 const,因为它不会修改调用它的 Bucket 对象。

  • 两个 operator+ 都错误地使用了 strcat()Bucket 重载正在修改 this->str_ 的内容,这是不应该做的,更糟糕​​的是 this->_str 还没有重新分配以增加其附加 [=34] 内容的能力=]. char* 重载与 tempBucket.str_ 犯了类似的错误。您需要为每个 tempBucket.

    分配一个全新的 char[] 数组
  • operator+Bucket 重载将 tempBucket 声明为 String 而不是 Bucket.

  • operator= 通过引用 返回修改后的 Bucket 对象 (应该是),但该对象不应该标记为 const.

  • Bucket 没有公开从外部代码访问其 _str 成员的方法,因此 if (this->_str == str) 无法签入 operator== 永远是真的。

  • operator= 不是 delete[] 在用新的 char[].

    替换旧的 _str 内存之前
  • operator>> 未对其读入的 buffer 执行任何边界检查。并且 re-allocating myBucket._str 不适合内容buffer。并且,它正在计算 _len 中的空终止符,其他 class 方法的 none 也是如此。

综上所述,试试这样的东西:

class Bucket
{
    friend ostream& operator<<(ostream& os, const Bucket& rhs);
    friend istream& operator>>(istream& is, Bucket& rhs);
public:
    Bucket(size_t len = 0);
    Bucket(const Bucket& src);
    Bucket(Bucket&& src);
    Bucket(const char* str);
    Bucket(const char* str, size_t len);
    ~Bucket();

    Bucket operator+(const Bucket& rhs) const;
    Bucket operator+(const char* str) const;

    /*
    Bucket& operator=(const Bucket& rhs);
    Bucket& operator=(Bucket&& rhs);
    Bucket& operator=(const char* str);
    */
    Bucket& operator=(Bucket rhs);

    bool operator==(const Bucket& rhs) const;
    bool operator==(const char* rhs) const;

private:
    char* _str;
    size_t _len;

    void swap(Bucket &other);
    bool equals(const char* str, size_t len) const;
    Bucket concat(const char* str, size_t len) const;
};
static size_t my_strlen(const char* str) {
    const char* start = str;
    if (str) while (*str != '[=11=]') ++str;
    return (str - start);
}

Bucket::Bucket(size_t len) {
    _len = len;
    if (len > 0) {
        _str = new char[len + 1];
        _str[len] = '[=11=]';
    }
    else {
        _str = nullptr;
    }
}

Bucket::Bucket(const Bucket& src)
    : Bucket(src._str, src._len) { }

Bucket::Bucket(Bucket&& src) : Bucket(0) {
    src.swap(*this);
}

Bucket::Bucket(const char* str)
    : Bucket(str, my_strlen(str)) { }
    
Bucket::Bucket(const char* str, size_t len) : Bucket(len) {
    if (str && len > 0) {
        for(size_t i = 0; i < len; ++i) {
            _str[i] = str[i];
        }
    }
}

Bucket::~Bucket() {
    delete[] _str;
}

void Bucket::swap(Bucket &other) {
    char *ptmp = _str;
    _str = other._str;
    other._str = ptmp;

    size_t itmp = _len;
    _len = other._len;
    other._len = itmp;
}

bool Bucket::equals(const char* str, size_t len) const {
    if (this->_len != len) return false;
    for(size_t i = 0; i < len; ++i) {
        if (this->_str[i] != str[i]) return false;
    }
    return true;
}

Bucket Bucket::concat(const char* str, size_t len) const {
    Bucket tempBucket(this->_len + len);
    for(size_t i = 0; i < this->_len; ++i) {
        tempBucket._str[i] = this->_str[i];
    }
    for(size_t i = this->_len, j = 0; j < len; ++i, ++j) {
        tempBucket._str[i] = str[j];
    }
    return tempBucket;
}

Bucket Bucket::operator+(const Bucket& rhs) const {
    return concat(rhs._str, rhs._len);
}

Bucket Bucket::operator+(const char* rhs) const {
    return concat(rhs, my_strlen(rhs));
}

/*
Bucket& Bucket::operator=(const Bucket& rhs) {
    if (this != &rhs) {
        Bucket(rhs).swap(*this);
    }
    return *this;
}

Bucket& Bucket::operator=(Bucket&& rhs) {
    Bucket(std::move(rhs)).swap(*this);
    return *this;
}

Bucket& Bucket::operator=(const char* rhs) {
    Bucket(rhs).swap(*this);
    return *this;
}
*/

Bucket& Bucket::operator=(Bucket rhs) {
    rhs.swap(*this);
    return *this;
}

bool Bucket::operator==(const Bucket& rhs) const {
    return equals(rhs._str, rhs._len);
}

bool Bucket::operator==(const char* rhs) const {
    return equals(rhs._str, my_strlen(rhs));
}

ostream& operator<<(ostream& os, const Bucket& rhs) {
    os.write(rhs._str, rhs._len);
    return os;
}

istream& operator>>(istream& is, Bucket& rhs) {
    /*
    string buffer;
    is >> buffer;
    rhs = buffer.c_str();
    return is;
    */
    char buffer[40];
    if (!is.get(buffer, 40)) buffer[0] = '[=11=]';
    rhs = buffer;
    return is;
}

也就是说,如果您可以使用标准的 C++ 功能,例如 std::string,那么代码就会变得简单得多:

#include <string>

class Bucket
{
    friend ostream& operator<<(ostream& os, const Bucket& rhs);
    friend istream& operator>>(istream& is, Bucket& rhs);
public:
    Bucket() = default;
    Bucket(const Bucket& src) = default;
    Bucket(Bucket&& src) = default;
    ~Bucket() = default;

    Bucket(const std::string& str);

    Bucket operator+(const Bucket& rhs) const;

    Bucket& operator=(const Bucket& rhs) = default;
    Bucket& operator=(Bucket&& rhs) = default;

    bool operator==(const Bucket& rhs) const;

private:
    std::string _str;
};
Bucket::Bucket(const std::string str) : _str(str) { }

Bucket Bucket::operator+(const Bucket& rhs) const {
    return Bucket(this->_str + rhs._str);
}

bool Bucket::operator==(const Bucket& rhs) const {
    return (this->_str == rhs._str);
}

ostream& operator<<(ostream& os, const Bucket& rhs) {
    os.write << rhs._str;
    return os;
}

istream& operator>>(istream& is, Bucket& rhs) {
    is >> rhs._str;
    return is;
}