刚学习C++,之前把 Primer 看了一遍,现在也在刷 leetcode,感觉学习编程语言光看书页刷题也是不够的,最好是能做一些实际的项目,这样要用到哪些东西时不明白再看书,就会印象深刻些,否则光看书只是走马观花,看了也就忘了。
打算自己用C++实现一个简单的矩阵 Mat 类,包括一些简单的操作就可以了,但实现起来发现也并没有那么简单,还是遇到很多问题。这个过程也还是学到了不少东西。
固定数据类型的矩阵
先从一个固定数据类型的矩阵的开始吧,以 int 为例子,那么我们需要什么呢? 首先,我们需要记录矩阵的行和列,也需要一块内存存储矩阵的数据;然后需要一些简单的构造函数和其他的成员函数,实现一些基本的操作。
废话不多说,直接上程序:
头文件 Mat.h:
#ifndef _MAT_H_
#define _MAT_H_
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <cassert>
//implement Mat class in c++
class Mat{
friend std::ostream& operator<<(std::ostream &os, const Mat &m);
friend std::istream& operator>>(std::istream &is, Mat &m);
public:
typedef int value_type;
typedef std::vector<int>::size_type size_type;
//construct
Mat();
Mat(size_t i, size_t j);
//copy constructor
Mat(const Mat& m);
//copy assignment
Mat& operator=(const Mat& m);
// +=
Mat& operator+=(const Mat& m);
// -=
Mat& operator-=(const Mat& m);
//destructor
~Mat();
//access element value
int& operator()(size_t i, size_t j);
const int& operator()(size_t i, size_t j) const;
//get row and col number
const size_t rows() const{ return row; }
const size_t cols() const{ return col; }
//resize
void resize(size_t nr, size_t nc);
private:
size_t row;
size_t col;
std::vector<std::vector<int>> data;
};
#endif
刚开始的时候,我是用 int* 来存储的数据,用int* 不方便的一个地方就是需要自己来管理内存,在实现一些 复制赋值和移动操作时都需要很小心处理,来管理内存,稍有不慎就会出问题。所以我后来改用C++标准容器 vector 了。这样就不用自己来管理内存了。
另外,像 + 、- 这样两边的数可以互换位置的操作符,重载时最好用非成员函数来重载,而且像 +=、 -= 这样的复合操作符,必须要用成员函数来重载。另外,可以用 +=、-= 来实现非成员函数的 + 、- 操作。最后要强调的是,非成员的运算符重载函数(+,-,,/)之类,返回类型应该是 const 类型,避免类似 a b = c 这样的语句通过编译。
像输出(<<)、输入(>>)运算符,需要用非成员函数来重载,一般还要声明为友元函数。不过可以通过成员函数完成输入输出操作,非成员函数来重载、调用成员函数输入输出来避免声明友元函数,后面一个例子就是这样实现的。
最后要强调的一点就是针对const对象,需要有对应的const版本的函数,具体说来,就是对一些不改变类类型的变量成员函数,尽量声明为const类型,这样const对象也能调用这些函数。在返回矩阵数据时,需要针对const 和 非const的对象,实现两个函数。
//access element value
int& operator()(size_t i, size_t j);
const int& operator()(size_t i, size_t j) const;
例如,重载 ( ) 运算符时,需要实现两个版本的函数,const 对象调用第二个版本函数,非 const 对象调用第一个版本的函数。
源文件Mat.cpp
#include <istream>
#include <sstream>
#include <algorithm>
#include "matint.h"
using std::cout;
using std::endl;
using std::istream;
using std::ostream;
using std::stringstream;
ostream& operator<<(ostream &os, const Mat&m){
for (size_t i = 0; i < m.row; i++){
for (size_t j = 0; j < m.col; j++){
os << m.data[i][j] << " ";
}
os << std::endl;
}
os << std::endl;
return os;
}
istream& operator>>(istream &is, Mat&m){
for (size_t i = 0; i < m.row; i++){
for (size_t j = 0; j < m.col; j++){
is >> m.data[i][j];
}
}
return is;
}
// +
const Mat operator+(const Mat& m1, const Mat& m2){