使用析构函数删除动态分配矩阵时触发访问违例错误
这个错误的根源其实很典型——浅拷贝导致的重复内存释放,具体来拆解下:
错误原因
你在重载operator<<的时候,参数用的是传值传递:
friend ostream& operator<<(ostream& out,Matrice<T> mat)
当你在main里执行cout << test;时,会创建一个test的临时拷贝对象mat。但C++默认的拷贝构造函数是浅拷贝,这意味着临时对象mat和原对象test会共享同一个matrice指针(也就是指向同一块动态分配的内存)。
当operator<<执行完毕,临时对象mat会被自动销毁,调用你的析构函数释放掉所有矩阵内存。这时候原对象test的matrice指针就指向了已经被释放的内存——微软调试堆会把已释放的内存标记为0xDDDDDDCD,用来提示这块内存已经无效。
等到main函数结束,test对象析构时,再次尝试释放已经被释放的内存,就触发了0xC0000005访问违规错误。
另外提一句:你的operator<<里还用了cout而不是参数里的out,虽然功能上能跑,但不符合重载运算符的规范,建议改成用out输出。
解决方法
1. 修复operator<<的参数传递(最直接的解决)
把operator<<的参数改成const引用传递,这样就不会创建临时对象,也就不会触发浅拷贝的问题:
friend ostream& operator<<(ostream& out, const Matrice<T>& mat) { for (int i = 0; i < mat.marime; i++) { out << endl; for (int j = 0; j < mat.marime; j++) { out << mat.matrice[i][j] << " "; } } return out; }
加const是因为输出操作不应该修改原对象,这也是良好的编程习惯。
2. 实现深拷贝的拷贝构造函数和赋值运算符(推荐,避免后续坑)
如果你之后需要拷贝矩阵对象(比如Matrice<int> mat2 = test;),默认的浅拷贝还是会出问题。所以遵循C++的三法则(现在是五法则,不过基础场景三法则足够),实现自己的拷贝构造函数和赋值运算符,做深拷贝:
// 拷贝构造函数 template<class T> Matrice<T>::Matrice(const Matrice<T>& other) { marime = other.marime; // 分配新的内存并拷贝数据 matrice = new T*[marime]; for(int i = 0; i < marime; i++){ matrice[i] = new T[marime]; for(int j = 0; j < marime; j++){ matrice[i][j] = other.matrice[i][j]; } } } // 赋值运算符重载 template<class T> Matrice<T>& Matrice<T>::operator=(const Matrice<T>& other) { // 防止自赋值 if(this == &other) return *this; // 先释放当前对象的旧内存 for(int i = 0; i < marime; i++){ delete[] matrice[i]; } delete[] matrice; // 深拷贝新对象的内容 marime = other.marime; matrice = new T*[marime]; for(int i = 0; i < marime; i++){ matrice[i] = new T[marime]; for(int j = 0; j < marime; j++){ matrice[i][j] = other.matrice[i][j]; } } return *this; }
这样不管是传值还是对象拷贝,都不会出现内存重复释放的问题了。
内容的提问来源于stack exchange,提问作者Lungu Dragos

