As the title says i try to delete a dynamic allocated matrix using the destructor and i get the following error:
Exception thrown at 0x78D8DB1B (ucrtbased.dll) in oop.exe: 0xC0000005: Access violation reading location 0xDDDDDDCD.
Here is the code that i try to run.
#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;
}
The memory address 0xddddddcd
is a likely sign of a use-after-free bug, since Visual C++ debug builds tag all freed memory with that memory pattern. I compiled your program using ASAN on Linux (clang++ matrice.cc -g -Og -fsanitize=address
) and was able to replicate your issue with the following stacktrace:
==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
It looks like some resource is read by the destructor at line 44 col 22 (delete[] matrice[i];
) after it has already been freed by a destructor called from line 53 (cout << test
).
The reason for this is easy to miss at first. The destructor was being called twice, once after cout << test
and again at the end of main
.
The issue is as follows: the function friend istream& Matrice::operator>>
takes a parameter of type Matrice<T>&
, which is fine, while operator<<
takes just a Matrice<T>
by value. This causes your instance of test
to be copied, by the default copy constructor. This is a problem, because the default copy constructor doesn't deep-copy your arrays, but it just copies the pointers themselves.
When the private copy of test
used in operator<<
is destructed, it frees the same arrays that were used by test
; thus when the destructor of test
runs, it tries to read that already-freed array.
This hits the notion of the rule of 5/3/0: if your class requires a custom destructor, a custom copy constructor, or a custom operator=
, it almost certainly needs all three. The cleanest way to resolve this would be a copy constructor that deep-copies the contents of the matrix to a new set of arrays. You could alternatively delete the copy constructor (Matrice(Matrice<T> const&) = delete
), but this makes your class a little less flexible to use.
User contributions licensed under CC BY-SA 3.0