c++ - 为什么我的重载 + 运算符在返回时会产生 "Invalid address specified to RtlValidateHeap"错误?

标签 c++ dynamic-memory-allocation

我正在尝试为我的 TurtleProgram 类创建一个重载的 + 运算符,它存储一个动态分配的字符串数组。但是方法返回的时候调用了析构函数,里面的delete操作抛出异常“Invalid address specified to RtlValidateHeap”。

下面是重载运算符定义、析构函数、默认构造函数、复制构造函数和重载运算符方法中使用的调整大小方法定义。

我已经对这个错误进行了研究,但我没能找到任何与我的问题相关的问题。

// overloaded + operator
// postconditions: returns a new TurtleProgram object that is the sum of two TurtleProgam instruction arrays
TurtleProgram TurtleProgram::operator+(const TurtleProgram &that)
{
    TurtleProgram returnTP;

    returnTP.resize(this->size_ + that.size_);

    for (int i = 0; i < this->size_ + that.size_; i++)
    {
        if (i < this->size_)
        {
            returnTP.instructions_[i] = this->instructions_[i];
        }
        else
        {
            returnTP.instructions_[i] = that.instructions_[i - this->size_];
        }
    }

    return(returnTP);
}

// resize: resized the array to be of a new size
// preconditions: the new size must be a positive integer
// postconditions: the array is resized, and size is updated
void TurtleProgram::resize(int newSize)
{
    string *temp = new string[newSize];

    // iterate through, transferring the contents from the old array to the resized array, then setting any empty spaces to ""
    for (int i = 0; i < newSize; i++)
    {
        if (i < size_)
        {
            temp[i] = instructions_[i];
        }
        else
        {
            temp[i] = "";
        }
    }

    if (size_ != 0)
    {
        delete instructions_;
    }

    instructions_ = temp;

    size_ = newSize;
}

// Default constructor: initializes as an empty array
// postconditions: a new TurtleProgram object is made with an empty array and a size of 0
TurtleProgram::TurtleProgram()
{
    instructions_ = new string[0];
    size_ = 0;
}

// Default destructor: deallocates instructions_ before destroying the object
// postconditions: the memory is released, and the object destroyed
TurtleProgram::~TurtleProgram()
{
    if (size_ != 0)
    {
        delete instructions_;
    }
}

TurtleProgram& TurtleProgram::operator=(const TurtleProgram &that)
{
    resize(that.getLength());

    for (int i = 0; i < size_; i++)
    {
        this->instructions_[i] = that.instructions_[i];
    }

    return(*this);
}

任何人都可以发现我做错了什么/不理解吗?在此先感谢您的帮助,对于任何发布错误,我们深表歉意。这是我的第一个问题!

最佳答案

主要问题是缺少复制构造函数(参见 rule of three)!

TurtleProgram::TurtleProgram(TurtleProgram const& that)
    : TurtleProgram()
{
    *this = that;
}

请注意,即使在像这样的表达式中

TurtleProgram x, y;
TurtleProgram z = x + y;

将调用复制(或移动,如果提供)构造函数而不是赋值运算符!

如果没有,默认情况下只提供复制指针,这将导致双重删除,因为 operator+ 中的临时对象和目标对象都持有相同的指针。

据我所知,您只是在重新实现 std::vector 已经提供的所有功能!你真的应该考虑使用它而不是尝试(并且失败)重新发明轮子......

顺便说一下:如果您使用 std::vector,根据您目前提供的内容,您可以完全跳过构造函数、析构函数和赋值运算符。默认值将很适合,因为 std::vector 的构造函数/析构函数/赋值运算符随后将被调用,它们可以很好地完成您需要的工作。

关于效率的一些进一步问题和要点(仅供学习,如果您不切换到 std::vector):

任何分配给new的资源都必须是deleted,任何分配给new[]的资源都必须是delete[]d!如果你混合(像你一样),未定义的行为!!!

您在创建空数组时引入了内存泄漏:

instructions_ = new string[0];
size_ = 0;

但仅有条件地删除成员(已修复错误删除):

if (size_ != 0)
{
    delete[] instructions_;
}

空数组存在,也占内存,任何情况下都需要删除该成员:

//if (size_ != 0)
//{
    delete[] instructions_;
//}

更好的是:

instructions_ = nullptr;
size_ = 0;

//if (size_ != 0)
//{
    delete[] instructions_;
//}

所以你根本不会为空海龟占用任何内存;你仍然不需要检查,删除空指针是绝对合法的(同时使用 deletedelete[])...

你绝对应该提供一个移动构造函数/赋值运算符(rule of five):

TurtleProgram::TurtleProgram(TurtleProgram&& that)
    : TurtleProgram()
{
    *this = std::move(that);
}
TurtleProgram& TurtleProgram::operator=(TurtleProgram&& that)
{
    using std::swap;
    swap(size_, that.size_);
    swap(instructions_, that.instructions_);
    return *this;
}

您节省了大量的内存分配和初始化工作...

for (int i = 0; i < this->size_ + that.size_; i++)
{
    if (i < this->size_)
    {
        returnTP.instructions_[i] = this->instructions_[i];
    }
    else
    {
        returnTP.instructions_[i] = that.instructions_[i - this->size_];
    }
}

制作两个独立的循环:

for (int i = 0; i < this->size_; i++)
{
    returnTP.instructions_[i] = this->instructions_[i];
}

for (int i = this->size_; i < this->size_ + that.size_; i++)
{
    returnTP.instructions_[i] = that.instructions_[i - this->size_];
}

类似的:

for (int i = 0; i < newSize; i++)
{
    if (i < size_)
    {
        temp[i] = instructions_[i];
    }
    else
    {
        temp[i] = "";
    }
}

它将得到:

for (int i = 0; i < size_; i++)
{
    temp[i] = instructions_[i];
}

嗯,还有第二部分???不必要的,operator new[] 已经调用了任何数组元素的默认构造函数,这无论如何都会创建空字符串......不过有一点开放:对 newSize 的适当处理是小于 size_。将此留作您的练习。

关于c++ - 为什么我的重载 + 运算符在返回时会产生 "Invalid address specified to RtlValidateHeap"错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49646114/

相关文章:

c++ - 初始化元组数组,其中元组元素需要前一个元素作为 ctor arg

c++ - 使用 dOxygen 记录具有指定指针的 typedef 结构?

c++ - C++ 中带有模板参数的 Typedef

c - 尝试填充动态数组时出现访问冲突(大量项目)

c - 更安全的重新分配方式

c++ - 全局变量和链接器错误

C++ 单例/事件对象范例

分配和重新分配内存时崩溃

c - c 中的双指针,我的代码的赋值警告

c - 使用 malloc 在运行时分配内存时出现段错误