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

标签 c++

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

Exception thrown at 0x78D8DB1B (ucrtbased.dll) in oop.exe: 0xC0000005: Access violation reading location 0xDDDDDDCD.



这是我尝试运行的代码。
    #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 行 ( delete[] matrice[i]; ) 调用的析构函数已将其释放后,析构函数似乎在第 44 列第 22 行 ( cout << test ) 读取了某些资源。

起初很容易忽略其原因。析构函数被调用了两次,一次在 cout << test 之后。又在 main 结尾.

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

test的私有(private)拷贝用于operator<<被破坏,它释放 test 使用的相同数组;因此当 test 的析构函数运行时,它会尝试读取该已释放的数组。

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

关于c++ - 尝试使用析构函数删除动态分配的矩阵时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61698074/

相关文章:

c++ - 使用 Boost 库的错误

C++::与所有其他类共享类

c++ - 我应该删除指向静态的本地指针吗

c++ - 私有(private)枚举访问无法从嵌套类的友元函数编译

c++ - 声明类的 C 函数友元并返回 C 枚举器

c++ - C++ 的跨平台多处理和多线程库

c++ - 如何将用户代码合并到我自己的应用程序中?

c++ - 无法推断模板参数 'N'

c++ - 如果没有插入而只有 .find(),在多线程上使用 C++ STL 容器是否安全?

c++ - 使用 Poco::zip 添加新目录总是给出异常