复制构造函数内存泄漏
Copy constructor memory leaks
我正在尝试学习编写自定义构造函数的基础知识,但我无法弄清楚自己做错了什么。我知道就我的目的而言,让编译器完成它的工作就足够了,但我很好奇我如何修复我的定义。
#include <iostream>
#include <stdexcept>
class Matrix {
public:
Matrix(int rows, int cols); //my custom constructor
~Matrix(); //my custom destructor
Matrix(const Matrix& m); //my custom copy constructor
Matrix& operator= (const Matrix& m); //my custom assignment operator
private:
int rows_, cols_;
double* data_;
};
Matrix::Matrix(int rows, int cols): rows_ (rows), cols_ (cols){
if (rows == 0 || cols == 0)
throw std::out_of_range("Matrix constructor has 0 size");
data_ = new double[rows * cols];
}
Matrix::~Matrix()
{
delete[] data_;
}
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
data_=m.data_;
}
Matrix& Matrix::operator=(const Matrix& m){
if(this != &m){
double* newdata_=new double[m.cols_*m.rows_];
*newdata_=*data_;
delete[] data_;
data_=newdata_;
rows_=m.rows_;
cols_=m.cols_;
}
return *this;
}
然后在程序的主体部分:
int main(){
Matrix m1(2,2);//creating a matrix of size 2x2
Matrix m2=m1; //this doesn't work
Matrix m3(m1); //nor this
return 0;
}
运行 可执行文件的错误是:
free():在 tcache 2
中检测到双重释放
我认为复制构造函数和赋值运算符都不会导致析构函数被调用,我的想法是否正确?这是为什么?
基本问题是在复制构造函数中分配内存后,您没有复制 数据。
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
data_ = m.data_; // <-- This is wrong
}
带有 // <-- This is wrong
注释的行不仅删除了前面的行(分配内存的地方),它也没有复制任何实际数据。它所做的只是复制指针值。所以你现在有 data_
和 m.data_
指向相同的内存,因此内存泄漏和双删除错误。
修复方法是将数据实际复制到新分配的内存中。
另一个不容易发现的潜在错误是您未能初始化所有数据。即使我们修复了这个问题来进行复制,您也会 运行 进入未定义的行为。
以下是解决这两个问题的方法:
#include <algorithm>
//...
Matrix::Matrix(int rows, int cols): rows_ (rows), cols_ (cols)
{
if (rows == 0 || cols == 0)
throw std::out_of_range("Matrix constructor has 0 size");
data_ = new double[rows * cols](); // <-- Note the () to zero-initialize the data
}
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
std::copy_n(m.data_, m.rows_ * m.cols_, data_);
}
还有一个错误,那就是在赋值运算符中。您犯了同样的错误,错误地使用 =
进行复制,而不是在两个缓冲区之间进行数据复制所需的函数。
Matrix& Matrix::operator=(const Matrix& m)
{
if(this != &m)
{
double* newdata_=new double[m.cols_*m.rows_];
*newdata_=*data_; // <-- This does not copy the data
//
此修复类似于使用 std::copy_n
在复制构造函数中完成的修复。但它是如此相似,以至于您实际上可以使用复制构造函数来完成所有这些工作,而不是使用重复代码。这种在赋值运算符中使用复制构造函数的技术称为 copy / swap idiom.
Matrix& Matrix::operator=(const Matrix& m)
{
if(this != &m)
{
Matrix temp(m); // <-- Copy is made
std::swap(temp.data_, data_);
std::swap(temp.rows_, rows_);
std::swap(temp.cols_, cols_);
}
return *this;
}
基本上是制作了一个副本,然后我们只是用副本的数据换出当前对象的数据。然后副本与旧数据一起消失。
我正在尝试学习编写自定义构造函数的基础知识,但我无法弄清楚自己做错了什么。我知道就我的目的而言,让编译器完成它的工作就足够了,但我很好奇我如何修复我的定义。
#include <iostream>
#include <stdexcept>
class Matrix {
public:
Matrix(int rows, int cols); //my custom constructor
~Matrix(); //my custom destructor
Matrix(const Matrix& m); //my custom copy constructor
Matrix& operator= (const Matrix& m); //my custom assignment operator
private:
int rows_, cols_;
double* data_;
};
Matrix::Matrix(int rows, int cols): rows_ (rows), cols_ (cols){
if (rows == 0 || cols == 0)
throw std::out_of_range("Matrix constructor has 0 size");
data_ = new double[rows * cols];
}
Matrix::~Matrix()
{
delete[] data_;
}
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
data_=m.data_;
}
Matrix& Matrix::operator=(const Matrix& m){
if(this != &m){
double* newdata_=new double[m.cols_*m.rows_];
*newdata_=*data_;
delete[] data_;
data_=newdata_;
rows_=m.rows_;
cols_=m.cols_;
}
return *this;
}
然后在程序的主体部分:
int main(){
Matrix m1(2,2);//creating a matrix of size 2x2
Matrix m2=m1; //this doesn't work
Matrix m3(m1); //nor this
return 0;
}
运行 可执行文件的错误是: free():在 tcache 2
中检测到双重释放我认为复制构造函数和赋值运算符都不会导致析构函数被调用,我的想法是否正确?这是为什么?
基本问题是在复制构造函数中分配内存后,您没有复制 数据。
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
data_ = m.data_; // <-- This is wrong
}
带有 // <-- This is wrong
注释的行不仅删除了前面的行(分配内存的地方),它也没有复制任何实际数据。它所做的只是复制指针值。所以你现在有 data_
和 m.data_
指向相同的内存,因此内存泄漏和双删除错误。
修复方法是将数据实际复制到新分配的内存中。
另一个不容易发现的潜在错误是您未能初始化所有数据。即使我们修复了这个问题来进行复制,您也会 运行 进入未定义的行为。
以下是解决这两个问题的方法:
#include <algorithm>
//...
Matrix::Matrix(int rows, int cols): rows_ (rows), cols_ (cols)
{
if (rows == 0 || cols == 0)
throw std::out_of_range("Matrix constructor has 0 size");
data_ = new double[rows * cols](); // <-- Note the () to zero-initialize the data
}
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
data_ = new double[rows_ * cols_];
std::copy_n(m.data_, m.rows_ * m.cols_, data_);
}
还有一个错误,那就是在赋值运算符中。您犯了同样的错误,错误地使用 =
进行复制,而不是在两个缓冲区之间进行数据复制所需的函数。
Matrix& Matrix::operator=(const Matrix& m)
{
if(this != &m)
{
double* newdata_=new double[m.cols_*m.rows_];
*newdata_=*data_; // <-- This does not copy the data
//
此修复类似于使用 std::copy_n
在复制构造函数中完成的修复。但它是如此相似,以至于您实际上可以使用复制构造函数来完成所有这些工作,而不是使用重复代码。这种在赋值运算符中使用复制构造函数的技术称为 copy / swap idiom.
Matrix& Matrix::operator=(const Matrix& m)
{
if(this != &m)
{
Matrix temp(m); // <-- Copy is made
std::swap(temp.data_, data_);
std::swap(temp.rows_, rows_);
std::swap(temp.cols_, cols_);
}
return *this;
}
基本上是制作了一个副本,然后我们只是用副本的数据换出当前对象的数据。然后副本与旧数据一起消失。