编辑
2022-06-15
个人项目
00
请注意,本文编写于 890 天前,最后修改于 44 天前,其中某些信息可能已经过时。

目录

主要功能
矩阵类技术细节
矩阵类成员变量
矩阵的构造函数
矩阵的拷贝构造(赋值)函数
矩阵的移动构造(赋值)函数
矩阵的析构函数
矩阵的运算符重载

这个矩阵是当时大一下学期暑假的时候为简单的平差而写的,虽然效率不高,但是对面向对象的编程来说,还是大有裨益的。

工程文件请见:C++矩阵类实现

主要功能

  • 基本的矩阵运算(包括运算符重载)

    • 矩阵加减法
    • 矩阵数乘
    • 矩阵乘法
    • 矩阵“除法”
    • 矩阵赋值(包括复合赋值)
    • 矩阵前后置自增自减
    • 矩阵判等
    • 矩阵广播(与数字的)
  • 矩阵处理

    • 矩阵合并
    • 矩阵分割
    • 矩阵打印输出
  • 矩阵构造

    • 零矩阵
    • 单位矩阵
    • 元素全为1的矩阵
    • 构造对角矩阵
    • 构造元素随机的矩阵
  • 矩阵操作

    • 矩阵转置
    • 矩阵求逆
    • 高斯消元法、求秩
    • 行列式、伴随矩阵
    • 矩阵元素下标取值
    • 矩阵取对角线元素
    • 化为 hessenberg 矩阵
    • QR 分解
    • 实对称矩阵求特征值

矩阵类技术细节

矩阵类成员变量

矩阵类的成员变量很少,主要是记录矩阵行列数的变量,以及一个存储矩阵数据的指向动态内存的指针。

矩阵类还包括静态成员变量,用于控制矩阵打印输入的格式。

c++
class Matrix { // ---- 成员变量 ---- private: int miRow = 0; //行数 int miCol = 0; //列数 double* mpBuf = nullptr; //存储矩阵 // ---- 静态成员变量 ---- static int iPrecise; // 控制流输出的显示精度 }

矩阵的构造函数

矩阵类的构造函数有多个,下面进行部分介绍

  • 无参数的构造函数生成未分配内存的0*0矩阵,这个空矩阵的意义,相信大家在数据结构课的学习中能感受到的。
  • std::initializer_list 为参数的构造函数,是为了实现如同 Matlab 的矩阵构造方法设计的,这里涉及到 std::initializer_list 的使用方法
c++
class Matrix { public: // ---- 构造函数 ---- /** * @brief 无参数构造函数 * * 获取一个0*0的未分配内存的空矩阵 * 不合法的计算一般返回本矩阵 */ explicit Matrix(); /** * @brief 构造函数 * 将double类型转化为1*1矩阵 * * @param num 数字 */ Matrix(double num); /** * @brief 构造函数 * 使用初始化列表构造矩阵 * * @param lst 初始化列表 */ Matrix(const MatIniLst& lst); /** * @brief 构造函数 * 使用初始化列表构造矩阵,列数为最多的一行的元素个数,可以以0补足其它行的元素 * * @param lst 初始化列表(两层) */ Matrix(const std::initializer_list<MatIniLst>& lst); /** * @brief 构造函数 * 使用初始化列表构造矩阵,根据设定的列数,可以以0补足初始化列表中没有的元素 * * @param lst 初始化列表(两层) * @param col 矩阵列数 */ explicit Matrix(const std::initializer_list<MatIniLst>& lst, int col); /** * @brief 构造函数 * 使用指针所指的数据构造矩阵,若数组长度小于需要的长度row*col,则会报错 * * @param p double型指针,是要输入的数据 * @param row 矩阵行数 * @param col 矩阵列数 * @param count 数组长度 */ explicit Matrix(double* p, int row, int col, int count); }

矩阵的拷贝构造(赋值)函数

拷贝构造(赋值)函数需要注意以下几点:

  • 由于矩阵类中有指针和动态分配的内存,拷贝时需要用深拷贝。
  • 拷贝的时候需要注意加上判断条件,拷贝的对象如果是自身,则不进行其他操作
  • 在重载拷贝赋值符的时候,要保持 = 原始的用法,即 operator= 应当返回赋值的结果
c++
class Matrix { public: /** * @brief 拷贝构造函数 * */ Matrix(const Matrix& tmp); /** * @brief 运算符重载,拷贝赋值函数 * 将矩阵赋值 * * @param tmp 待赋值的矩阵 * @return 返回赋值之后的该矩阵的引用 */ Matrix& operator=(const Matrix& tmp); }

矩阵的移动构造(赋值)函数

关于对象移动,在 C++ primer 中有详细的介绍,下面给出我的觉得重要的几点:

  • 移动主要是为了从“将亡值”中取出需要的数据(这里主要是动态内存),减少了拷贝的成本。
  • 移动构造(赋值)函数需要加上 noexcept 以支持与标准库交互
  • 在为类的移动构造和移动赋值函数指定不抛出异常时,必须在类的头文件声明中和定义中(如果定义在类外的话)都指定 noexcept
  • 移动的时候需要注意加上判断条件,移动的对象如果是自身,则不应当进行其他操作
  • 在重载移动赋值符的时候,要保持 = 原始的用法,即 operator= 应当返回赋值的结果
c++
class Matrix { public: /** * @brief 移动构造函数 * */ Matrix(Matrix&& tmp)noexcept; /** * @brief 运算符重载,移动赋值函数 * 将矩阵赋值 * * @param tmp 待赋值的矩阵 * @return 返回赋值之后的该矩阵的引用 */ Matrix& operator=(Matrix&& tmp)noexcept; }

矩阵的析构函数

析构函数没有什么特别的技术点,注意内存回收就好了

c++
class Matrix { public: /** * @brief 析构函数 */ ~Matrix(); }

矩阵的运算符重载

关于运算符重载,在 C++ primer 中也有详细的介绍。下面是我觉得需要注意的地方。

  • 运算符重载应当与其内置版本意义相似

  • 运算符要注意“对称”运算符与“非对称”运算符的处理。“对称”运算符应当为普通非成员函数,尤其是左侧运算对象不一定为类的对象时(这里使用的是友元函数);而“非对称”的运算符需要是成员函数,尤其是当其会改变类对象的状态时。

    c++
    class Matrix { public: /** * @brief 运算符重载,友元函数 * 将本矩阵与所给矩阵相加求和 * * @param lMat 左加矩阵 * @param rMat 右加矩阵 * @return 返回求和之后的该矩阵的引用 */ friend Matrix operator+(const Matrix& lMat, const Matrix& rMat); /** * @brief 运算符重载 * 复合赋值+= * * @param tmp 要加上的矩阵 * @return 返回结果矩阵的引用 */ Matrix& operator+=(const Matrix& tmp); }
  • 下标运算符通常定义两个版本:一个返回普通的引用,另一个是类的常量成员并返回常量引用。本类为了实现类的按行列的下标访问,返回类型略有不同。

    c++
    class Matrix { public: /** * @brief 运算符重载 * 取下标 * 返回下标对应的矩阵的某一行 * 一般连续使用两个[]以获取某个指针 * 不要在类的方法中使用取下标运算符!!! * 有两个版本,此为非常量版本 * * @param num 下标 * @return 返回下标对应的矩阵的某一行的第一个元素的指针 */ double* operator[](int num); /** * @brief 运算符重载 * 取下标 * 返回下标对应的矩阵的某一行 * 一般连续使用两个[]以获取某个指针 * 不要在类的方法中使用取下标运算符!!! * 有两个版本,此为常量版本 * * @param num 下标 * @return 返回下标对应的矩阵的某一行的第一个元素的指针 */ const double* operator[](int num)const; }
  • 递增和递减运算符有两种版本,分别是前置和后置版本。

    • 前置递增递减运算符应当与内置版本意义相近,返回递增递减后对象的引用
    • 前置递增递减运算符应当与内置版本意义相近,返回递增递减前对象的原值
    • 区分前置和后置运算符。后置版本接受一个额外的、并不使用的 int 类型形参
    c++
    class Matrix { public: /** * @brief 运算符重载 * 前置递增 * * @return 递增后的矩阵引用 */ Matrix& operator++(); /** * @brief 运算符重载 * 后置递增 * * @param 区分前后置的参数 * @return 递增前的矩阵 */ Matrix operator++(int); }

本文作者:Zerol Acqua

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!