我做了一个矩阵课。这是其中的一部分。
Math::Matrix::Matrix(int row, int col, double * mat)
{
this->mat = new double[(this->row = row) * (this->col = col)];
for (int i = 0; i < row * col; i++)
{
this->mat[i] = mat[i];
}
}
Math::Matrix::~Matrix()
{
if (this->mat != nullptr)
{
delete[] this->mat;
}
}
const Math::Matrix Math::Matrix::multiply(Matrix & A) const
{
if (!this->is_multipliable(A))
{
throw new std::exception("Math::Matrix::multiply : cannot multiply!");
}
Matrix B = Matrix(this->row, A.col);
for (int k = 0; k < this->col; k++)
{
for (int i = 0; i < this->row; i++)
{
for (int j = 0; j < A.col; j++)
{
B.mat[i * A.col + j] = this->mat[i * this->col + k] * A.mat[k * A.col + j];
}
}
}
return B;
}
std::ostream & Math::operator<<(std::ostream & os, const Matrix & m)
{
for (int i = 0; i < m.row; i++)
{
for (int j = 0; j < m.col; j++)
{
os << m.mat[i*m.col + j] << ' ';
}
os << '\n';
}
return os;
}
这是我写的矩阵类的一部分,当我尝试
时int main()
{
A = Math::Matrix(2, 3, new double[6]{ 1, 2, 3, 4, 5, 6 });
B = Math::Matrix(3, 4, new double[12]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 });
std::cout << A << std::endl;
std::cout << B << std::endl;
std::cout << A.multiply(B) << std::endl;
}
在main中,程序将输出一些愚蠢的数字,如
-1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 1.07458e-255 3.02386e-294 1.41763e-311
2.122e-314 -7.84591e+298 -1.45684e+144 1.87482e-310
2.9803e-294 -1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
(不调试)
或
1 2 3
4 5 6
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
3.67842e-293 8.81477e-310 3.6647e-293 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
-1.45682e+144 -1.45682e+144 -1.45682e+144 -1.45682e+144
(调试)
然后抛出异常。
在调试模式下,它表示&#34; wntdll.pdb包含查找模块ntdll.dll&#34;的源代码所需的调试信息。
我正在使用2017年的视觉工作室。
这段代码似乎有什么问题?
答案 0 :(得分:3)
让我们猜测你如何定义Matrix
,然后重新将其重写为默认正确。
namespace Math {
class Matrix {
double * mat; // Bad. Owning raw pointer
int col, row; // Questionable. std::size_t is the normal type for an index
public:
Matrix(int row, int col, double * mat); // Bad. Owning raw pointer
~Matrix(); // Will be un-necessary under the rule of 0
const Matrix multiply(Matrix & A) const; // this is usually named "operator *"
friend std::ostream & operator<<(std::ostream & os, const Matrix & m); // Good
};
}
这就是我们整理它的方式
namespace Math {
class Matrix {
std::vector<double> mat;
std::size_t col, row;
public:
Matrix(std::size_t row, std::size_t col)
: row(row), col(col), mat(row * col, 0)
{}
template <typename InputIterator>
Matrix(std::size_t row, std::size_t col, InputIterator it) // copy from any sequence of doubles
: Matrix(row, col) // delegate to 2-arg constructor to initialises fields
{
std::copy_n(it, row * col, mat.begin());
}
double & data(std::size_t c, std::size_t r) // Convinience
{ return mat[(r * col) + c]; }
double data(std::size_t c, std::size_t r) const // Overload for const
{ return mat[(r * col) + c]; }
friend Matrix operator*(const Matrix & lhs, const Matrix & rhs)
{
if (!lhs.is_multipliable(rhs))
{
// MSVC includes this as a non-standard extension
// std::runtime_error is a portable replacement
throw new std::exception("Math::Matrix::multiply : cannot multiply!");
}
Matrix result = Matrix(lhs.row, rhs.col);
for (int k = 0; k < lhs.col; k++)
{
for (int i = 0; i < lhs.row; i++)
{
for (int j = 0; j < rhs.col; j++)
{
result.data(i, j) += lhs.data(i, k) * rhs.data(k, j);
}
}
}
return result;
}
friend std::ostream & operator<<(std::ostream & os, const Matrix & m);
friend std::istream & operator>>(std::istream & is, Matrix & m);
};
}
请注意,std::exception
未定义为const char *
accepting constructor
答案 1 :(得分:2)
这条线是个坏主意
A = Math::Matrix(2, 3, new double[6]{ 1, 2, 3, 4, 5, 6 });
您为初始化数组分配内存,但是您无法释放该内存,因为未保存指向数组的指针。
您没有复制构造函数和赋值,因此在销毁时间矩阵后,A将保留具有无效值的指针,因为其字段是浅层复制的。
至少定义复制构造函数和赋值运算符,建议添加移动构造函数和移动赋值和默认构造函数。可以使用std::initializer_list
答案 2 :(得分:2)
使用原始指针时,您需要了解它的工作原理。要遵循的最重要的规则之一 - rule of three,你的班级肯定违反了这一规则。所以这些代码行有问题:
A = Math::Matrix(2, 3, new double[6]{ 1, 2, 3, 4, 5, 6 });
B = Math::Matrix(3, 4, new double[12]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 });
Matrix B = Matrix(this->row, A.col);
你的对象在临时被摧毁的情况下持有悬空指针。当您将指针传递给动态分配的内存然后丢弃它时,您也会遇到内存泄漏问题。您最好在类中使用std::vector<double>
,它将为您完成所有分配,并且已经为您提供了正确的复制/移动构造函数和赋值运算符以及析构函数。您根本不需要实现析构函数并使用默认值。
答案 3 :(得分:1)
好的问题可能是您没有显示的复制/移动操作符。
只要你有一个保存动态内存的原始指针,你就应该实现复制和移动构造函数并复制和移动分配操作符或明确删除它们,因为它们隐含定义的版本无法正确处理它。
所以这是可以接受的(我说可接受,不好,因为除非你实现一个通用容器,否则你应该避免原始动态内存分配,只依赖于标准容器):
#include <iostream>
namespace Math {
class Matrix {
public:
int row;
int col;
double *mat; // beware rule of 3/5
Matrix(int row, int col, double *mat);
~Matrix();
Matrix(const Matrix&) = delete; // explicitely delete what we don't define
Matrix(Matrix&&) = delete;
Matrix operator = (const Matrix&) = delete;
Matrix operator = (Matrix&&) = delete;
};
}
Math::Matrix::Matrix(int row, int col, double * mat)
{
this->mat = new double[(this->row = row) * (this->col = col)];
for (int i = 0; i < row * col; i++) {
this->mat[i] = mat[i];
}
}
Math::Matrix::~Matrix()
{
if (this->mat != nullptr)
{
delete[] this->mat;
}
}
std::ostream & operator<<(std::ostream & os, const Math::Matrix & m)
{
for (int i = 0; i < m.row; i++)
{
for (int j = 0; j < m.col; j++)
{
os << m.mat[i*m.col + j] << ' ';
}
os << '\n';
}
return os;
}
int main(){
double *m = new double[6]{1,2,3,4,5,6}; // store the automatic memory pointer ...
Math::Matrix mat(2, 3, m); // only uses the direct ctor
delete[] m; // to be able to delete it!
std::cout << mat;
return 0;
}
但是当你写:
A = Math::Matrix(2, 3, new double[6]{ 1, 2, 3, 4, 5, 6 });
默认移动运算符将将<{1}}成员从临时矩阵复制到A,然后临时矩阵将被销毁,释放mat
的内存指针会员 - &gt;晃来晃去的指针保证
悬空指针是未定义行为和奇怪输出的原因。
BTW我的版本与已删除的赋值运算符将在该行引发编译错误,这是红灯警告:注意在这一点附近很快就会破坏...
TL / DR:如果可以,请避免直接管理已分配的内存,如果无法避免,请实现复制/移动构造函数和赋值运算符,或明确删除那些未定义的内存。