C++ 矩阵类的实现

                     

贡献者: addis

预备知识 C++ 中类的定义和继承

  

未完成:本文代码还没验证过

   容器指的就是像 std::vector 这样的可以储存多个元素的对象,下面介绍的矢量,矩阵,高维数组等都是容器。

   C/C++ 的数组是储存于内存的 stack(栈)中的,而 stack 一般只有几个 Mb,一旦数组太大就会产生 stack overflow 错误。第二是 stack 中的数据的大小都只能在编译时确定(即 constant expression,例如 literal 或者宏定义),所以我们不能在运行是确定数组的长度(例如从文件中读取,或通过运行时的计算得到)。

// 用 C/C++ array 的嵌套定义矩阵
#define N1 5
#define N2 5
double a[N1][N2]; // 定义并在栈中分配内存
// 全部元素赋值为 0
for (long i = 0; i < N1; ++i)
    for (long j = 0; j < N2; ++j)
        a[i][j] = 0;

   所以在一般来说容器都会 allocate(分配)到内存的 heap(堆)中。在 C++ 中,在 heap 中分配内存用 new,释放则用 delete

   std::vector 可以说是使用最广的矢量容器,理论上我们可以用 vectorvector 定义一个 heap 中的矩阵。

// 用 std::vector 的嵌套定义矩阵
vector<vector<double>> a;
// 分配内存
long N1 = 10, N2 = 10;
a.resize(N1);
for (long i = 0; i < N1; ++i)
    a[i].resize(N2);
// 全部元素赋值为 0
for (long i = 0; i < N1; ++i)
    for (long j = 0; j < N2; ++j)
        a[i][j] = 0;
注意由于用了多次 new,数据在内存中不一定是连续的,甚至不一定按 i 的顺序排列。这样不是很好,为了性能和语法上的考虑,一般需要给每个容器分配一块连续的内存(例如我们会想要用单个连续索引来获得矩阵元)。

   最后一种方法就是小时百科的 SLISC 库中的底层实现方法,即在堆中分配一段连续的内存,并把双索引转换乘单索引来获取矩阵元。

Long N1 = 10, N2 = 10;
double *a;
new double a[N1*N2];
// 全部元素赋值为 0
for (long i = 0; i < N1; ++i)
    for (long j = 0; j < N2; ++j)
        a[N2*i + j] = 0; // 行主序
        // a[i + N1*j] = 0; // 列主序
// 或者直接使用单索引赋值更快
for (long i = 0; i < N1*N2; ++i)
    a[i] = 1;
delete a[];
前面两个例子中双索引都是行主序的,即第二个索引增加 1,内存地址也增加 1。而这个例子中我们既可以使用行主序也可以使用列主序(第一个索引增加 1,内存地址增加 1),还可以使用单索引。可见这种方式要灵活得多。

   注意这只是一个底层的原理,我们需要把这个功能封装到一个矩阵类 Mat 中,在使用的时候可以达到例如以下效果

MatDoub a(10, 10); // constructor 会执行 new
// 全部元素赋值为 0
for (long i = 0; i < a.n1(); ++i) // n1() n2() 用于获取行数列数
    for (long j = 0; j < a.n2(); ++j)
        a(i, j) = 0;
// 使用单索引
for (long i = 0; i < a.size(); ++i) // size() 用于获取总长度
    a[i] = 1;

// 改变尺寸, 先用 delete 再用 new 重新分配, 数据丢失
a.resize(20, 20);
为了区分行主序和列主序,我们可以设计两个类似的矩阵类 CmatDoub 是列主序矩阵,MatDoub 是行主序矩阵。

   我们来看一个简化版的 CmatDoub 定义,不考虑使用 const CmatDoub

class CmatDoub
{
protected:
    int m_N1, m_N2;
    double *p;
public:
    CmatDoub(): m_N1(0), m_N2(0) {}
    CmatDoub(int N1, int N2): m_N1(N1), m_N2(N2) { new double m_p[N1*N2]; }
    Doub& operator()(int i, int j) { return p[m_N2*i + j]}
    int n1() { return m_N1; }
    int n2() { return m_N2; }
    int size() { return m_N1 * m_N2; }
    void resize(int N1, int N2)
    {
        m_N1 = N1; m_N2 = N2;
        if (N1 * N2 != m_N1 * m_N2) {
            delete p[]; new p [N1*N2];
        }
    }
    ~CmatDoub() { delete p[]; }
};
用这个矩阵类就可以实现上例中的使用方式。


致读者: 小时百科一直以来坚持所有内容免费无广告,这导致我们处于严重的亏损状态。 长此以往很可能会最终导致我们不得不选择大量广告以及内容付费等。 因此,我们请求广大读者热心打赏 ,使网站得以健康发展。 如果看到这条信息的每位读者能慷慨打赏 20 元,我们一周就能脱离亏损, 并在接下来的一年里向所有读者继续免费提供优质内容。 但遗憾的是只有不到 1% 的读者愿意捐款, 他们的付出帮助了 99% 的读者免费获取知识, 我们在此表示感谢。

                     

友情链接: 超理论坛 | ©小时科技 保留一切权利