【问题标题】:C++ destructor won't free properlyC++ 析构函数不能正确释放
【发布时间】:2016-10-04 23:50:06
【问题描述】:

我正在尝试正确释放一个公寓类对象,但是 valgrind 说“无效的免费,地址......在线程1的堆栈上” 这是代码: 如果您能指出我的错误,我将不胜感激。

class Apartment{
public:
enum SquareType {EMPTY, WALL, NUM_SQUARE_TYPES};
class ApartmentException : public std::exception {};
class IllegalArgException : public ApartmentException {};
class OutOfApartmentBoundsException : public ApartmentException {};

int length;
int width;
int price;
SquareType** squares;

Apartment (SquareType** squares, int length, int width, int price);
Apartment (const Apartment& apartment);
Apartment& operator=(const Apartment& apartment);
~Apartment();
};

Apartment::Apartment (SquareType** squares=NULL, int length=0, int width=0, int price=0){
    this->price=price;
    this->length=length;
    this->width=width;

    this->squares = new SquareType*[length];
    for(int i=0; i<length ; i++){
        this->squares[i]= new SquareType[width];
    }
    this->squares = squares;
    for(int i=0; i<length; i++){
        for(int j=0; j<width; j++){

            this->squares[i][j] = squares[i][j];
        }
    }
}
Apartment::Apartment (const Apartment& apartment):length(apartment.length),
                    width(apartment.width),price(apartment.price),squares(apartment.squares){

    for(int i=0; i<apartment.length; i++){
        for(int j=0; j<apartment.width; j++){
            squares[i][j] = apartment.squares[i][j];
        }
    }

}
Apartment& Apartment::operator=(const Apartment& apartment){

    if(this == &apartment){
        return *this;
    }
    for(int i=0;i<length;i++){
        delete [] squares[i];
    }
    delete [] squares;

    squares = new SquareType*[apartment.length];

    for(int i=0; i<apartment.length ; i++){
        squares[i]= new SquareType[apartment.width];
    }

    for(int i=0; i<apartment.length; i++){
        for(int j=0; j<apartment.width; j++){
            squares[i][j] = apartment.squares[i][j];
        }
    }
    price=apartment.price;
    length=apartment.length;
    width=apartment.width;
    return *this;
}
Apartment::~Apartment(){
    for(int i=0;i<length;i++){
        delete [] squares[i];
    }
    delete [] squares;
}

这是主要的:

int main(){

    Apartment::SquareType square1[5]={Apartment::WALL};
    Apartment::SquareType square2[5]={Apartment::WALL};
    Apartment::SquareType square3[5]={Apartment::WALL};
    Apartment::SquareType square4[5]={Apartment::WALL};

    Apartment::SquareType* squares[4]={square1,square2,square3,square4};
    Apartment::SquareType* Squares[3]={square1,square2,square3};

Apartment ap(squares,4,5,0);
Apartment ap2(Squares,3,5,50);

return 0;

}

这就是 valgrind 的输出:valg

【问题讨论】:

  • 您需要提供minimal reproducible example
  • 嗨,为什么不是向量的向量?在 c-tor 中,您首先创建整个 2D 区域,然后:this->squares = squares;。这肯定会导致内存泄漏

标签: c++ class valgrind destructor


【解决方案1】:

您的复制构造函数必须复制squares 的内容,就像您在赋值运算符中所做的那样,而不是仅仅分配apartment.squares 中的地址。

【讨论】:

  • 我做了,我已经复制了整个内容,在嵌套的 for 循环中
  • 不,你没有。您刚刚在同一内存区域之间执行了分配。首先为新对象创建新缓冲区。
【解决方案2】:

错误出在复制构造函数中。你(正确地)有这个:

    this->squares = new SquareType*[length];

但不久之后你就会有这个:

    this->squares = squares;

导致内存泄漏和潜在的悬空指针。

更广泛地说:

  • 您不应该在new 上进行如此繁重的交易。相反,您应该使用标准库的抽象; std::vector 似乎非常适合您的情况,实际上可以让您完全消除析构函数。
  • 你应该看看“What is the copy-and-swap idiom?”;这是一种让这些核心功能更简单、更健壮的方法。

【讨论】:

  • 这里this-&gt;squares = new SquareType*[length];不是拷贝构造函数,但是构造函数好像也错了……
  • 谢谢,但我想先学习如何使用简单的数组来做到这一点。您提到的内存泄漏的解决方案是什么?丢掉这个作业,只复制内容?
【解决方案3】:

除了Apartment 构造函数覆盖从new[] 返回的指针的问题之外,您的复制构造函数应该执行赋值运算符中的操作。相反,您只是分配指针而不是复制数据(您正在执行 复制,而不是 复制)。

Apartment::Apartment(const Apartment& rhs) : price(rhs.price), length(rhs.length), width(rhs.width), squares(new SquareType*[rhs.length])
{
    for(int i=0; i<rhs.length ; i++)
       squares[i]= new SquareType[rhs.width];

    for(int i=0; i<rhs.length; i++)
    {
        for(int j=0; j<rhs.width; j++)
            squares[i][j] = rhs.squares[i][j];
    }
}

一旦你有了这个,你的赋值运算符就不需要复制同样的代码了。你的赋值操作员能做的就是:

#include <algorithm>
//...
Apartment& Apartment::operator=(const Apartment& rhs)
{
   Apartment temp(rhs);
   std::swap(temp.price, price);
   std::swap(temp.length, length);
   std::swap(temp.width, width);
   std::swap(temp.squares, squares);
   return *this;
}

这使用copy / swap 成语。

但总的来说,即使进行了这些更改,也很容易破坏您的代码。如果在Apartment 构造函数中将NULL 作为squares 传递,并且长度和/或宽度大于0,该怎么办?您将从传入的 NULL 指针分配给您的 squares,从而导致未定义的行为。

如果您使用诸如std::vector 之类的容器会更好,这些容器会自动知道它们的大小,而客户端不必明确地向您提供这些信息。

【讨论】:

  • 我确信这有一个狡猾的原因,你为什么不按值传递rhs 并使用它而不是temp
  • 你可以这样做——只是希望赋值运算符的签名看起来更像 OP 的。
  • 老鼠。我希望我错过了一些偷偷摸摸的编译器优化。
猜你喜欢
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
  • 2014-08-19
  • 1970-01-01
  • 2023-03-10
  • 2022-01-13
  • 1970-01-01
相关资源
最近更新 更多