C++矩阵破坏导致断点和严重错误(解决方案在问题的最后)
C++ matrix destruction causes breakpoint and critical error (solution is at the end of the question)
所以我想创建一个 4x4 矩阵 class,将其数据存储为 float** m
。
我这样初始化它:
Matrix4f::Matrix4f()
{
this->m = new float*[4];
for (int i = 0; i < 4; i++)
{
this->m[i] = new float[4];
memset(this->m[i], 0, sizeof(float) * 4);
}
}
之后,我有一个这样的析构函数:
Matrix4f::~Matrix4f()
{
for (int i = 0; i < 4; i++)
{
delete[] m[i];
}
delete[] m;
}
但有时当我使用这个 class 时,这一行:
delete[] m[i];
导致断点,并抛出:Critical error detected c0000374
这是初始化和删除 4x4 矩阵的正确方法吗?
编辑:对于未来的读者:我的问题是我没有重写矩阵的复制构造函数和赋值运算符 (void operator=(const Matrix&)。这是一个问题,因为有可能复制是默默地进行的(就像说 Matrix4f mat = getTransformationMatrix();
,然后这个矩阵在范围的末尾被删除,释放与转换矩阵相同的指针,因为复制的是指针,而不是内容。所以覆盖这两个像接受的答案中的功能一样,它将摆脱这样的问题。
如前所述,三者的规则可能是问题所在。您提供的代码在技术上是正确的,但是,如果您复制 Matrix4f,则必须重新实现复制构造函数和重载赋值构造函数:
#include <iostream>
class Matrix4f
{
public:
Matrix4f()
: m_data(new float*[4])
{
for (int i = 0; i < 4; i++)
m_data[i] = new float[4]{ 0 };
}
Matrix4f(const Matrix4f& source)
{
m_data = new float*[4];
for (int i = 0; i < 4; i++)
{
m_data[i] = new float[4]{ 0 };
for (int j = 0; j < 4; j++)
{
m_data[i][j] = source.m_data[i][j];
}
}
}
Matrix4f& operator=(const Matrix4f& source)
{
if (this == &source) return *this;
clear();
m_data = new float*[4];
for (int i = 0; i < 4; i++)
{
m_data[i] = new float[4]{ 0 };
for (int j = 0; j < 4; j++)
{
m_data[i][j] = source.m_data[i][j];
}
}
return *this;
}
~Matrix4f()
{
clear();
}
//private:
float**m_data;
void clear()
{
if (m_data != NULL)
{
for (int i = 0; i < 4; i++)
{
delete[] m_data[i];
}
delete[]m_data;
}
}
void print()const
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout << m_data[i][j] << "\t";
}
std::cout << "\n";
}
}
};
int main()
{
Matrix4f m1;
m1.m_data[0][0] = m1.m_data[1][1] = m1.m_data[2][2] = m1.m_data[3][3] = 1;
{
Matrix4f m2(m1);
Matrix4f m3 = m2;
m2.print();
m3.print();
}
m1.print();
}
但我建议你改用一维向量,它更快更安全。
正如评论中所指出的,当且仅当您正确地管理副本的情况时,您的代码才可以。
你有三个选择:
- 禁用复制
- 浅拷贝
- 深拷贝
让我们讨论这个选择:
您只需要声明复制构造函数和删除的复制赋值运算符。现在无法复制 class 的对象。这降低了矩阵的有用性-class.
这本身没有实际价值。因为您可以通过将类型 3 的对象包装到 std::shared_ptr
.
中来实现此目的
在这种情况下不需要动态内存。即使有,您也想使用 std::vector 或其他东西来管理内存。
明智的解决方案是使用 float[4][4]
作为 m
。
这会更健壮,因为您避免了内存管理。
所以我想创建一个 4x4 矩阵 class,将其数据存储为 float** m
。
我这样初始化它:
Matrix4f::Matrix4f()
{
this->m = new float*[4];
for (int i = 0; i < 4; i++)
{
this->m[i] = new float[4];
memset(this->m[i], 0, sizeof(float) * 4);
}
}
之后,我有一个这样的析构函数:
Matrix4f::~Matrix4f()
{
for (int i = 0; i < 4; i++)
{
delete[] m[i];
}
delete[] m;
}
但有时当我使用这个 class 时,这一行:
delete[] m[i];
导致断点,并抛出:Critical error detected c0000374
这是初始化和删除 4x4 矩阵的正确方法吗?
编辑:对于未来的读者:我的问题是我没有重写矩阵的复制构造函数和赋值运算符 (void operator=(const Matrix&)。这是一个问题,因为有可能复制是默默地进行的(就像说 Matrix4f mat = getTransformationMatrix();
,然后这个矩阵在范围的末尾被删除,释放与转换矩阵相同的指针,因为复制的是指针,而不是内容。所以覆盖这两个像接受的答案中的功能一样,它将摆脱这样的问题。
如前所述,三者的规则可能是问题所在。您提供的代码在技术上是正确的,但是,如果您复制 Matrix4f,则必须重新实现复制构造函数和重载赋值构造函数:
#include <iostream>
class Matrix4f
{
public:
Matrix4f()
: m_data(new float*[4])
{
for (int i = 0; i < 4; i++)
m_data[i] = new float[4]{ 0 };
}
Matrix4f(const Matrix4f& source)
{
m_data = new float*[4];
for (int i = 0; i < 4; i++)
{
m_data[i] = new float[4]{ 0 };
for (int j = 0; j < 4; j++)
{
m_data[i][j] = source.m_data[i][j];
}
}
}
Matrix4f& operator=(const Matrix4f& source)
{
if (this == &source) return *this;
clear();
m_data = new float*[4];
for (int i = 0; i < 4; i++)
{
m_data[i] = new float[4]{ 0 };
for (int j = 0; j < 4; j++)
{
m_data[i][j] = source.m_data[i][j];
}
}
return *this;
}
~Matrix4f()
{
clear();
}
//private:
float**m_data;
void clear()
{
if (m_data != NULL)
{
for (int i = 0; i < 4; i++)
{
delete[] m_data[i];
}
delete[]m_data;
}
}
void print()const
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout << m_data[i][j] << "\t";
}
std::cout << "\n";
}
}
};
int main()
{
Matrix4f m1;
m1.m_data[0][0] = m1.m_data[1][1] = m1.m_data[2][2] = m1.m_data[3][3] = 1;
{
Matrix4f m2(m1);
Matrix4f m3 = m2;
m2.print();
m3.print();
}
m1.print();
}
但我建议你改用一维向量,它更快更安全。
正如评论中所指出的,当且仅当您正确地管理副本的情况时,您的代码才可以。
你有三个选择:
- 禁用复制
- 浅拷贝
- 深拷贝
让我们讨论这个选择:
您只需要声明复制构造函数和删除的复制赋值运算符。现在无法复制 class 的对象。这降低了矩阵的有用性-class.
这本身没有实际价值。因为您可以通过将类型 3 的对象包装到
std::shared_ptr
. 中来实现此目的
在这种情况下不需要动态内存。即使有,您也想使用 std::vector 或其他东西来管理内存。 明智的解决方案是使用
float[4][4]
作为m
。 这会更健壮,因为您避免了内存管理。