贡献者: addis
我们先以双精度类型 Doub
(即 double
)来举例介绍 SLISC 中的密矩阵。我们习惯上也把 1 维的矩阵称为矢量。
先来看一个基本的使用的例子
#include "SLISC/arithmetic.h"
#include "SLISC/disp.h"
int main()
{
using namespace slisc;
// ======= 矢量 ===========
VecDoub u; // 生成长度为 0 的 double 矢量
VecDoub v(3); // 生成长度为 3 的 double 矢量, 不初始化
copy(a, 1); // 全部元素赋值为 1
linspace(v, 1.1, 3.3); // 等间距赋值 v = [1.1, 2.2, 3.3]
u.resize(v.size()); // u 变为和 v 相等的长度。 注意 resize 会丢失所有数据
copy(u, 3); // 全部元素赋值为 3
copy(u, v); // 把 v 复制到 u
cout << "u = " << endl; disp(u); // 把 u 输出到命令行
assign(v, 1., 3., 2.); // 对 v 手动赋值(注意必须使用 double 类型, 不支持自动转换)
cout << "v = " << endl; disp(v);
assign2(v, 2, 2, 4., 5.); // 从 v[2] 开始的 2 个元素手动赋值
v[2] = 2*PI; // 给单个元素赋值为两倍圆周率
cout << "v[2] = " << v[2] << endl; // 获取单个元素
v.end() = v.end(2) = 10; // 给最后一个和倒数第 2 个元素赋值。
resize_cpy(v, 5); // 把 v 的长度变为 5, 原来的元素不变, 新增元素为 0
VecDoub w;
w << u; // 把 u 的数据转移给 w (转移指针地址), u 变为长度为 0 的矢量
// ========= 矩阵 ==========
CmatDoub a(2, 3); // 2x3 的列主序矩阵, 不初始化
linspace(v, 1, 6); // 等间距赋值 a(0,0) = 1, a(1, 0) = 2, ...
a(1, 2) = 7; // 双索引获取元素
a[5] = 8; // 单索引获取元素
a.end(2) = 9; // 倒数第二个元素
a.resize(3, 4); // 变为 3x4 的矩阵; 会丢失所有数据, 不初始化
copy(a, 1); // 全部元素赋值为 1
resize_cpy(a, 4, 5); // 拓展为 4x5 矩阵; 保留数据, 新增元素为 0
cout << "a = " << endl; disp(a); // 显示 a
}
所有密矩阵都继承于 VbaseDoub
,VbaseDoub
不直接使用,仅用于被继承。这里使用继承主要是为了避免在每个密矩阵中都重复定义这些基础功能。
class VbaseDoub
{
protected:
Doub *m_p; // 第一个元素的指针
Long m_N; // 元素个数
public:
VbaseDoub(); // 默认构造(不分配内存)
explicit VbaseDoub(Long_I N); // 构造:分配 N 个矩阵元
VbaseDoub(const VbaseDoub &v); // 复制构造
Doub* p(); // 返回指针 m_p
const Doub* p() const; // 返回 const 指针
Long size() const;
void resize(Long_I N); // 销毁内存并重新分配 N 个矩阵元(若当前长度不是 N)
Doub &operator[](Long_I i); // 获取矩阵元 a[i]
const Doub &operator[](Long_I i) const;
Doub& end(); // 获取最后一个矩阵元
const Doub& end() const;
Doub& end(Long_I i); // 获取 a[m_N-i]
const Doub& end(Long_I i) const;
void operator<<(VbaseDoub &rhs); // 从另一个对象转移数据
~VbaseDoub();
};
简单来说,VbaseDoub
就是一个简单的 std::vector
,负责内存管理,用方括号 [i]
获取第 i
个矩阵元,用 end()
获取最后一个元,end(i)
获取第 N-i
个元。用 .size()
获取元素长度,.resize(N)
释放内存并重新分配 N
个元素的新内存。<<
用于把一个对象所分配的内存转移到另一个对象中。所有继承 VbaseDoub
的密矩阵都具有这些功能,也会增加新的功能。
VecDoub
矢量继承 VbaseDoub
,为了避免歧义,任何两个容器之间禁止使用等号。如果要复制使用 copy(a, b)
函数把 b
复制给 a
。
class VecDoub : public VbaseDoub
{
public:
typedef VbaseDoub Base;
VecDoub() = default;
explicit VecDoub(Long_I N);
VecDoub(const VecDoub &rhs);
VecDoub &operator=(const VecDoub &rhs) = delete;
void operator<<(VecDoub &rhs);
};
列主序矩阵 CmatDoub
和行主序矩阵 MatDoub
同样继承 VbaseDoub
。在此基础上增加一个行数 m_N0
,列数 m_N1
,双索引 a(i, j)
。以 CmatDoub
定义为例:
class CmatDoub : public VbaseDoub
{
protected:
typedef VbaseDoub Base;
Long m_N0, m_N1; // 行数和列数
public:
CmatDoub(): m_N0(0), m_N1(0) {}; // 默认构造
CmatDoub(Long_I N0, Long_I N1); // 构造:分配 N0*N1 个元素
CmatDoub(const CmatDoub &rhs); // 复制构造
CmatDoub &operator=(const CmatDoub &rhs) = delete; // 不允许等号复制
void operator<<(CmatDoub &rhs); // 从另一个对象转移数据
Doub& operator()(Long_I i, Long_I j); // 双索引
const Doub& operator()(Long_I i, Long_I j) const;
Long n0() const; // 获取行数
Long n1() const; // 获取列数
void resize(Long_I N0, Long_I N1); // 重新分配
};
三维数组同样分为列主序的 Cmat3Doub
和行主序的 Mat3Doub
,功能和 CmatDoub
大同小异
class Cmat3Doub : public VbaseDoub
{
protected:
typedef VbaseDoub Base;
Long m_N0, m_N1, m_N2; // 三个维度的长度
public:
Cmat3Doub(): m_N0(0), m_N1(0), m_N2(0) {}; // 默认构造
Cmat3Doub(Long_I N0, Long_I N1, Long_I N2; // 构造: 分配 N0*N1*N2 个元素
Cmat3Doub(const Cmat3Doub &rhs); // 复制构造
Cmat3Doub &operator=(const Cmat3Doub &rhs) = delete;
void operator<<(Cmat3Doub &rhs); // 从 rhs 转移数据
void resize(Long_I N0, Long_I N1, Long_I N2); // 变形,重新分配
Doub &operator()(Long_I i, Long_I j, Long_I k); // 矩阵元 a(i,j,k)
const Doub &operator()(Long_I i, Long_I j, Long_I k) const;
Long n0() const; // 第一维长度
Long n1() const; // 第二维长度
Long n2() const; // 第三位长度
};
更高维的数组的定义也大同小异。