题目
实现一个异常安全的动态矩阵类 Matrix,支持强异常保证的 resize 操作,并提供移动赋值运算符和访问元素的方法。
简介
在 C++ 中,实现一个异常安全且高效的动态矩阵类是非常重要的。本题要求你实现一个 Matrix 类,该类能够动态调整大小,并且在调整大小时保证强异常安全。此外,还需要实现移动赋值运算符和访问元素的方法。
要求
- 异常安全:确保在操作过程中即使发生异常,对象也能保持一致的状态。
- 强异常保证的
resize操作:在调整矩阵大小时,如果新矩阵的创建或数据复制失败,旧矩阵应保持不变。 - 移动赋值运算符:实现移动赋值运算符以提高性能。
- 访问元素:提供访问和修改矩阵元素的方法,并在索引越界时抛出异常。
- 输出运算符:实现格式化输出矩阵的方法。
实现工具
- 编程语言:C++
- 标准库:
<iostream>,<vector>,<memory>,<stdexcept>,<algorithm>,<utility>
代码实现
cpp
#include <iostream>
#include <vector>
#include <memory>
#include <stdexcept>
#include <algorithm>
#include <utility>
class Matrix {
public:
// 构造函数
Matrix(size_t rows, size_t cols) : rows_(rows), cols_(cols) {
data_ = std::make_unique<std::unique_ptr<double[]>[]>(rows);
for (size_t i = 0; i < rows; ++i) {
data_[i] = std::make_unique<double[]>(cols);
}
}
// 析构函数
~Matrix() = default;
// 移动构造函数
Matrix(Matrix&& other) noexcept
: data_(std::move(other.data_)), rows_(other.rows_), cols_(other.cols_) {
other.rows_ = 0;
other.cols_ = 0;
}
// 移动赋值运算符
Matrix& operator=(Matrix&& other) noexcept {
if (this != &other) {
data_ = std::move(other.data_);
rows_ = other.rows_;
cols_ = other.cols_;
other.rows_ = 0;
other.cols_ = 0;
}
return *this;
}
// 强异常保证的 resize 操作
void resize(size_t new_rows, size_t new_cols) {
if (new_rows == rows_ && new_cols == cols_) {
return; // 大小没变,直接返回
}
// 创建临时矩阵
Matrix temp(new_rows, new_cols);
// 复制尽可能多的元素
size_t min_rows = std::min(rows_, new_rows);
size_t min_cols = std::min(cols_, new_cols);
for (size_t i = 0; i < min_rows; ++i) {
for (size_t j = 0; j < min_cols; ++j) {
temp(i, j) = (*this)(i, j);
}
}
// 交换内容(不会抛出异常)
swap(temp);
}
// 交换辅助函数
void swap(Matrix& other) noexcept {
using std::swap;
swap(data_, other.data_);
swap(rows_, other.rows_);
swap(cols_, other.cols_);
}
// 访问元素
double& operator()(size_t row, size_t col) {
if (row >= rows_ || col >= cols_) {
throw std::out_of_range("Matrix indices out of range");
}
return data_[row][col];
}
const double& operator()(size_t row, size_t col) const {
if (row >= rows_ || col >= cols_) {
throw std::out_of_range("Matrix indices out of range");
}
return data_[row][col];
}
// 获取行列数
size_t rows() const { return rows_; }
size_t cols() const { return cols_; }
// 输出运算符友元声明
friend std::ostream& operator<<(std::ostream& os, const Matrix& matrix);
private:
std::unique_ptr<std::unique_ptr<double[]>[]> data_;
size_t rows_, cols_;
};
// 格式化输出运算符
std::ostream& operator<<(std::ostream& os, const Matrix& matrix) {
os << "Matrix[" << matrix.rows_ << "][" << matrix.cols_ << "]:\n";
os << std::fixed << std::setprecision(2);
for (size_t i = 0; i < matrix.rows_; ++i) {
for (size_t j = 0; j < matrix.cols_; ++j) {
os << std::setw(8) << matrix(i, j);
}
os << '\n';
}
return os;
}
int main() {
try {
Matrix m(3, 3);
m(0, 0) = 1.0;
m(0, 1) = 2.0;
m(0, 2) = 3.0;
m(1, 0) = 4.0;
m(1, 1) = 5.0;
m(1, 2) = 6.0;
m(2, 0) = 7.0;
m(2, 1) = 8.0;
m(2, 2) = 9.0;
std::cout << "Original Matrix:\n" << m << std::endl;
m.resize(4, 4);
m(3, 3) = 10.0;
std::cout << "Resized Matrix:\n" << m << std::endl;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}代码说明
构造函数:
Matrix构造函数接受行数和列数,并初始化二维动态数组data_。
析构函数:
- 默认析构函数,自动释放
data_的内存。
- 默认析构函数,自动释放
移动构造函数:
- 移动构造函数将
other的资源转移给当前对象,并将other的状态重置。
- 移动构造函数将
移动赋值运算符:
- 移动赋值运算符将
other的资源转移给当前对象,并将other的状态重置。
- 移动赋值运算符将
强异常保证的
resize操作:resize方法首先检查是否需要调整大小。- 创建一个临时矩阵
temp,并复制尽可能多的元素到temp。 - 使用
swap方法交换当前矩阵和temp的内容,确保强异常保证。
交换辅助函数:
swap方法交换两个矩阵的内容,不抛出异常。
访问元素:
operator()提供对矩阵元素的访问和修改,索引越界时抛出std::out_of_range异常。
获取行列数:
rows和cols方法分别返回矩阵的行数和列数。
输出运算符:
operator<<用于格式化输出矩阵。
测试
- 编译并运行上述代码,观察输出的原始矩阵和调整大小后的矩阵。
- 尝试不同的矩阵大小和元素值,验证
resize操作的正确性和异常安全性。 - 尝试访问越界的索引,验证异常处理是否正确。
通过这个题目,你可以深入理解 C++ 中的异常安全编程、动态内存管理以及自定义类的设计。
