我正在为一个CS项目开发一个Matrix类,我正在努力研究构造函数。该项目需要两个不同的构造函数,一个只调用行数和列数,并将它们全部设为0,另一个使用初始化列表来分配值。到目前为止,头文件是:
typedef unsigned int uint;
typedef std::initializer_list<std::initializer_list<double>> i_list;
class Matrix {
public:
double ** arr;
uint mainRows;
uint mainCols;
Matrix(uint rows, uint cols);
Matrix(const i_list & list);
Matrix(const Matrix & m);
~Matrix();
};
某些测试用例要求您定义行并使用初始化列表,例如:
Matrix d(2,2);
d = {{1,2},{3,4}};
但是我注意到每次尝试运行这种代码时,析构函数都会立即删除双** arr,这是存储Matrix的值的地方。以下是构造函数的代码:
Matrix::Matrix(uint rows, uint cols)
{
mainRows = rows;
mainCols = cols;
arr = new double*[rows];
for (int i = 0; i < mainRows; i++) {
arr[i] = new double[cols];
}
for (int i = 0; i < mainRows; i++) {
for (int j = 0; j < mainCols; j++) {
arr[i][j] = 0;
}
}
}
Matrix::Matrix(const i_list & list)
{
int i = 0, j = 0;
mainRows = list.size();
mainCols = (*list.begin()).size();
arr = new double*[mainRows];
for (std::initializer_list<double> I : list) {
j = 0;
arr[i] = new double[mainCols];
for (double d : I) {
arr[i][j] = d;
j++;
}
i++;
}
}
Matrix::Matrix(const Matrix & m)
{
this->arr = m.arr;
this->mainRows = m.mainRows;
this->mainCols = m.mainCols;
for (uint i = 0; i < mainRows; i++) {
for (uint j = 0; j < mainCols; j++) {
this->arr[i][j] = m.arr[i][j];
}
}
}
Matrix::~Matrix()
{
for (uint i = 0; i < mainRows; i++) {
delete[] arr[i];
}
delete[] arr;
}
我猜是因为它为同一个对象调用了两次构造函数,它创建了两个双**并且这就是为什么析构函数想要删除原始对象,但是我可以#&# 39; t调用其他函数的值。我可以帮助我解决我做错的事吗?
答案 0 :(得分:3)
问题是你的拷贝构造函数只复制源对象的指针,而不是分配新的内存。
这是有问题的,因为
d = {{1,2},{3,4}};
从{{1,2},{3,4}}
创建一个临时对象。你的陈述实际上等于
d = Matrix({{1,2},{3,4}});
等于
d.operator=(Matrix({{1,2},{3,4}}));
完成赋值后,您有两个对象指向arr
的相同内存。然后,临时对象被破坏,导致arr
内的d
无效,因为它不再指向已分配的内存。
天真的解决方案很简单:为arr
分配内存以指向复制构造函数。 更好的解决方案是停止使用指针和动态分配,而是使用std::vector
,并使用the rule of zero生活,您不需要任何复制构造函数,也不需要复制 - 赋值运算符,没有析构函数。
答案 1 :(得分:1)
这是错误的:
Matrix::Matrix(const Matrix & m)
{
this->arr = m.arr;
this->mainRows = m.mainRows;
this->mainCols = m.mainCols;
for (uint i = 0; i < mainRows; i++) {
for (uint j = 0; j < mainCols; j++) {
this->arr[i][j] = m.arr[i][j];
}
}
}
注意yuo不要在这里创建实际的副本。 this->arr = m.arr;
指向指向内存的相同部分的两个指针,因此Matrix
的新旧实例共享此内存。所以流动的for
循环什么都不做。
当一个实例被销毁时,另一个实例指向被释放的内存。