矩阵构造后析构函数抛出异常,求故障原因
矩阵析构函数抛出访问违规异常排查
问题描述
用双指针实现矩阵类时,程序在析构函数调用阶段抛出读取访问违规异常,错误信息如下:
Exception thrown: read access violation.
this->array was 0x111011101110112.
怀疑问题出在移动/拷贝构造函数或初始化构造函数中,多次调试仍无法定位,附上代码请求排查。
代码片段
Matrix.h
#pragma once #include <iostream> class Matrix { size_t rows; size_t cols; int** array; public: ~Matrix(); Matrix(size_t, size_t); Matrix(const Matrix&); Matrix(Matrix&&); Matrix(size_t, size_t, std::initializer_list<std::initializer_list<int>>); };
Matrix.cpp
#include "Matrix.h" Matrix::~Matrix() { for (size_t i = 0; i < rows; i++) { delete[] array[i]; } delete[] array; } Matrix::Matrix(size_t rows, size_t cols) : rows(rows), cols(cols), array(new int* [rows]) { for (size_t i = 0; i < rows; i++) { array[i] = new int[cols]; } for (size_t i = 0; i < rows; i++) { for (size_t j = 0; j < cols; j++) { array[i][j] = 0; } } std::cout << "CONSTRUCTOR" << std::endl; } Matrix::Matrix(size_t rows, size_t cols, std::initializer_list<std::initializer_list<int>> list) : rows(rows), cols(cols), array(new int* [rows]) { for (size_t i = 0; i < rows; i++) { array[i] = new int[cols]; } size_t i = 0; for (auto row : list) { size_t j = 0; for (auto elem : row) { array[i][j] = elem; j++; } i++; } } Matrix::Matrix(const Matrix& other) : rows(other.rows), cols(other.cols), array(new int* [other.rows]) { if (other.array != nullptr) { for (size_t i = 0; i < other.rows; i++) { array[i] = new int[other.cols]; } for (size_t i = 0; i < other.rows; i++) { for (size_t j = 0; j < other.cols; j++) { array[i][j] = other.array[i][j]; } } } else { delete[] array; array = nullptr; } std::cout << "COPY" << std::endl; } Matrix::Matrix(Matrix&& other) : rows(other.rows), cols(other.cols), array(other.array) { other.array = nullptr; std::cout << "MOVE" << std::endl; }
main.cpp
#include "Matrix.h" Matrix f(Matrix m) { return m; } int main() { Matrix m(2, 2, { {1,2},{2,3} }); Matrix k(f(m)); }
问题根源及修复方案
1. 移动构造函数未重置原对象的行数和列数
移动构造函数仅将原对象的array置为nullptr,但保留了rows和cols的原值。当原对象(比如函数f的形参m)被析构时,会循环rows次尝试访问array[i],此时array已经是空指针,直接触发读取访问违规。
修复代码:
Matrix::Matrix(Matrix&& other) : rows(other.rows), cols(other.cols), array(other.array) { // 重置原对象所有成员,避免析构时出错 other.array = nullptr; other.rows = 0; other.cols = 0; std::cout << "MOVE" << std::endl; }
2. 析构函数未做空指针检查
当array为nullptr时,直接循环访问array[i]会导致非法内存访问,需要先判断array是否有效。
修复代码:
Matrix::~Matrix() { if (array != nullptr) { for (size_t i = 0; i < rows; i++) { delete[] array[i]; } delete[] array; } }
3. 拷贝构造函数冗余逻辑优化(可选)
原拷贝构造函数中other.array不可能为nullptr——所有构造函数都会初始化array为有效指针,移动后的对象rows为0,拷贝时不会进入else分支。可以简化逻辑,同时用std::copy减少循环次数:
Matrix::Matrix(const Matrix& other) : rows(other.rows), cols(other.cols), array(new int* [other.rows]) { for (size_t i = 0; i < other.rows; i++) { array[i] = new int[other.cols]; std::copy(other.array[i], other.array[i] + other.cols, array[i]); } std::cout << "COPY" << std::endl; }
4. 初始化列表构造函数越界风险修复(可选)
如果传入的初始化列表行数/列数与指定的rows/cols不匹配,会导致数组越界,可添加参数合法性检查:
#include <stdexcept> // 需要包含头文件 Matrix::Matrix(size_t rows, size_t cols, std::initializer_list<std::initializer_list<int>> list) : rows(rows), cols(cols), array(new int* [rows]) { if (list.size() != rows) { throw std::invalid_argument("初始化列表行数与指定行数不匹配"); } // 先初始化所有元素为默认值 for (size_t i = 0; i < rows; i++) { array[i] = new int[cols]; std::fill(array[i], array[i] + cols, 0); } size_t i = 0; for (auto row : list) { if (row.size() > cols) { throw std::invalid_argument("初始化列表列数超过指定列数"); } size_t j = 0; for (auto elem : row) { array[i][j] = elem; j++; } i++; } }
内容的提问来源于stack exchange,提问作者Edmond Dantes
相关产品推荐
相关产品推荐

