由编写矩阵class想到的

2024-02-04 15:18
文章标签 编写 矩阵 class 想到

本文主要是介绍由编写矩阵class想到的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

         我们今天线性代数刚好学到矩阵了,于是尝试将所学的编程知识加以实践,利用在高数上面。再加上C++如此强大的表达能力,写好了一个类之后就可以把它当做一个组件,甚至作为内置类型来用。

         当然,说起来容易做起来难。编程就是这样,萌生一个想法很容易,但是如果要把这个想法加以细化,再予以实现,需要的不仅仅是耐心,还有扎实的编程功底。笔者这个寒假看了一本叫做《Effective C++》的书。其实看之前我还是怀着将信将疑的态度的,既然是Effective那应该和我们没有太大的关系吧,反正那是以后的事?因为有牛人推荐去看一下这本书,所以我看下去了,一探究竟为何推荐这本书。现在看来,笔者认为,《Effective C++》确实是一本好书的。其一,养成良好的编程习惯是要从学生开始的;同样,我们应该要养成高效的编程习惯,有时一个小小的细节可能会对性能产生绝对的影响(比如对象的函数传递不用const引用类型)。其二,我们在学习如何高效率编程的时候其实会对语言实现的原理进行深入的了解。所以,这是一本好书,值得一读的。

顺便一提的是,一开始看的是中文版电子书,不过后面我在网上买了一本评注版的,也就是英文版,看得云里雾里,一知半解。我以前对自己的英文水平有着过分的自信,可惜面对英文版的书,还是原形毕露了。尽管也许每个词都看得懂,句子也勉强通过,文意老是捉摸不透。所以说我现在明白了,除非是有特殊的需要,尽量选择中文版的书看,不要因为图新鲜或为了证明自己的能力而去买英文版的,真的,中文版可能翻译得不好,但是我们读者可以明白得一样好。

好了,说远了,下面是我这个class的设计。

class Matrix{

private:

    int **Mtx;

    int cols;

    int rows;

    static void intMult(Matrix& mtx, intLamda);

public:

    friend ostream&operator<<(ostream& out, const Matrix& mt);

    friend istream&operator>>(istream& in, Matrix& mt);

    friend const Matrix operator*(int lamda,const Matrix& rhs);

 

    explicit Matrix(int i=0, int j=0);

    Matrix(const Matrix& rhs); //Copy fx

         void operator=(const Matrix& rhs);//copy assignment

    ~Matrix();

 

    const Matrix operator+(const Matrix&rhs) const;

    const Matrix operator-(const Matrix& rhs)const;

    const Matrix operator*(const Matrix&rhs) const;

    const Matrix operator*(int lamda) const;

 

    const int getElem(int row, int col) const;

    const int getRow() const {return rows;}

    const int getCol() const {return cols;}

    const int getMtx(int i, int j) const{return Mtx[i][j];}

};

一开始考虑该如何存放矩阵,想着简单,但却着实烦扰了我半天。我可以选择复合的vector,但是因为是练习嘛,我打算挑战自己,建立一个二位数组。考虑到需要动态存储,我将二维数组的指针存放在int **Mtx中。大家都懂得,什么程序,一旦扯上指针就是处于灾难的边缘了。更何况指针的指针。Mtx指向一组int*,是个指针的数组。而每个int*又指向一组int。于是一个二维数组就这么诞生了(想象一下一个二维表,表头指向许多行,而每一行又指向了该行的所有元素)。所以,我令Mtx指向的一组int*作为行,而int*指向的一组int作为这一行的所有列。那么,Mtx[i][j]是第i行第j列呢,还是反着说呢?我一直都没弄清楚。经过试验可以确定从左向右的原则任然成立,也就是说是i行j列。大家也可以试一试,比较下(Mtx[i])[j](i行j列)和Mtx[i][j]的效果就可以了。看这个代码:

    Ptr[1][2]=10;

    cout << Ptr[1][2] << endl;

    cout << (Ptr[1])[2];

假设Ptr是经过分配并已初始化的int**,上面的结果两个都是10,也就是说从左向右的顺序成立。

初始化代码如下:

    Mtx=new int*[rows];

    for(int i=0;i<rows;i++){

        Mtx[i]=new int[cols];

        for(int j=0;j<cols;j++)Mtx[i][j]=rhs.Mtx[i][j];

    }

         存储问题解决了,一切就可以循序渐进了。

         Explicit的构造函数是什么呢?它是为了阻止编译器在幕后进行隐式类型转换。比如说令int a=1.22f,这时候就涉及到隐式类型转换了,编译器在幕后将(float)1.22转换为了(int)1,并且赋值给a。而显示转换比如说static_cast等,在此不赘述,可以参考reference。隐式转换在有些时候会给我们带来麻烦,这是Effective C++里面提到的。于是,一开始,我遵守了约定,将Matrix::Matrix(const Matrix&)也声明为explicit,问题就出现了。比如,设在函数里有一个Matrix M,如果我要return M的话,编译器却不干了:Error: no matching function for call to'Matrix::Matrix(Matrix&)'。我猜想,return需要创建一个对象保存结果并传递,而不是直接将M的内存区域移交给函数调用者。这就涉及到一个隐式构造函数(编译器自动创建了一个临时对象),而这个行为是被explicit所禁止的。将copy构造的explicit去掉就可以通过了。所以,可以得出,对于一个copy构造函数修饰explicit是坏主意,除非你认为这确实有必要。

         笔者还遇上了一个问题,在刚才也提到过传递const引用会对性能有很大的提升,但是,这不仅仅是性能,有的时候如果忘记给引用加const结果会很糟糕。比如说如果把构造函数写为Matrix::Matrix(Matrix&),看似没什么问题,但是一旦要构造MatrixM(M1+M2);呢?编译器又不干了,因为M1+M2的结果是const的。那如果我把Matrix::operator+()的返回值修改为非const的呢?会导致一些问题,比如M1+M2=M3;这类无效的语句却会通过编译。这是不被提倡的。

         这些做完之后,对照我们线性代数的书,根据矩阵的性质一条条地编写运算律。当然,这只是一小部分,我还会在学习线性代数的同时对它加以完善。做一个Matrix类是件小事,可能标准库还带了,但是更重要的是实践。通过实践可以让我们注意到平时看书时不被注意到的一些细节。编程是要求思维严密过细的。我感觉,C++继承了C语言的一些特性,保留了指针,使得底层高效的东西得以实现。又有灵活的类型系统,让STL、TR1这些杰作方便了我们编程的过程。C++确实是一个很优秀的语言,如果我们能掌握它,它就能很好地为我们服务。

博文

2012/3/3 0:43 于宿舍

(转载请保留)

这篇关于由编写矩阵class想到的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677933

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

基于.NET编写工具类解决JSON乱码问题

《基于.NET编写工具类解决JSON乱码问题》在开发过程中,我们经常会遇到JSON数据处理的问题,尤其是在数据传输和解析过程中,很容易出现编码错误导致的乱码问题,下面我们就来编写一个.NET工具类来解... 目录问题背景核心原理工具类实现使用示例总结在开发过程中,我们经常会遇到jsON数据处理的问题,尤其是

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

使用Java编写一个文件批量重命名工具

《使用Java编写一个文件批量重命名工具》这篇文章主要为大家详细介绍了如何使用Java编写一个文件批量重命名工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景处理1. 文件夹检查与遍历2. 批量重命名3. 输出配置代码片段完整代码背景在开发移动应用时,UI设计通常会提供不

提示:Decompiled.class file,bytecode version如何解决

《提示:Decompiled.classfile,bytecodeversion如何解决》在处理Decompiled.classfile和bytecodeversion问题时,通过修改Maven配... 目录问题原因总结问题1、提示:Decompiled .class file,China编程 bytecode

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo