尝试使用析构函数删除动态分配矩阵时出错



正如标题所说,我试图使用析构函数删除动态分配的矩阵,但我得到了以下错误:

在oop.exe中的0x78D8DB1B(基于ucrt.dll(处引发异常:0xC0000005:读取位置0xDDDDDCD时发生访问冲突。

这是我尝试运行的代码。

#include <iostream>
using namespace std;
template<class T>
class Matrice
{
private:
int marime;
T** matrice;
public:
Matrice(int marime);
~Matrice();
friend istream& operator>>(istream& in,Matrice<T>& mat) {
for (int i = 0; i < mat.marime; i++) {
for (int j = 0; j < mat.marime; j++) {
cout << "Matrice[" << i << "][" << j << "]: ";
in >> mat.matrice[i][j];
}
}
return in;
}
friend ostream& operator<<(ostream& out,Matrice<T> mat) {
for (int i = 0; i < mat.marime; i++) {
cout << endl;
for (int j = 0; j < mat.marime; j++) {
out << mat.matrice[i][j]<<" ";
}
}
return out;
}
};
template<class T>
Matrice<T>::Matrice(int marime) {
this->marime = marime;
matrice = new T * [marime];
for (int i = 0; i < marime; i++) {
matrice[i] = new T[marime];
}
}
template<class T>
Matrice<T>::~Matrice() {
for (int i = 0; i < marime; i++) {
delete[] matrice[i]; //Here is where i get the error.
}
delete[] matrice;
}
int main()
{
Matrice<int> test(3);
cin >> test;
cout << test;
}

内存地址0xddddddcd可能是释放后使用错误的标志,因为Visual C++调试使用该内存模式构建标记所有释放的内存。我在Linux(clang++ matrice.cc -g -Og -fsanitize=address(上使用ASAN编译了您的程序,并能够用以下堆栈复制您的问题:

==6670==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000010 at pc 0x0000004c9a76 bp 0x7fffdcd001b0 sp 0x7fffdcd001a8
READ of size 8 at 0x603000000010 thread T0
#0 0x4c9a75 in Matrice<int>::~Matrice() /tmp/z.cc:44:22
#1 0x4c93c9 in main /tmp/z.cc:54:1
#2 0x7f34a76370b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
#3 0x41f3cd in _start (/tmp/a.out+0x41f3cd)
0x603000000010 is located 0 bytes inside of 24-byte region [0x603000000010,0x603000000028)
freed by thread T0 here:
#0 0x4c736d in operator delete[](void*) (/tmp/a.out+0x4c736d)
#1 0x4c93c1 in main /tmp/z.cc:53:5
#2 0x7f34a76370b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
previously allocated by thread T0 here:
#0 0x4c6b1d in operator new[](unsigned long) (/tmp/a.out+0x4c6b1d)
#1 0x4c9536 in Matrice<int>::Matrice(int) /tmp/z.cc:36:19

在第53行(cout << test(调用的析构函数释放了某个资源之后,第44行第22列(delete[] matrice[i];(的析构构函数似乎读取了该资源。

这一点一开始很容易被忽略。析构函数被调用了两次,一次是在cout << test之后,一次在main结束时。

问题如下:函数friend istream& Matrice::operator>>接受类型为Matrice<T>&的参数,这很好,而operator<<只接受值为Matrice<T>的参数。这会导致默认的复制构造函数复制test的实例。这是一个问题,因为默认的复制构造函数不会深度复制数组,但它只是复制指针本身。

operator<<中使用的test的私有副本被销毁时,它释放了与test使用的相同的数组;因此,当test的析构函数运行时,它会尝试读取已释放的数组。

这符合5/3/0规则的概念:如果您的类需要一个自定义析构函数、一个自定义复制构造函数或一个自定义operator=,那么它几乎肯定需要这三个。解决这个问题的最干净的方法是使用复制构造函数,将矩阵的内容深度复制到一组新的数组中。您也可以删除复制构造函数(Matrice(Matrice<T> const&) = delete(,但这会降低类的使用灵活性。

最新更新