C + + Learning 01: how to use C + + to create a two-dimensional matrix class and overload various operators
1. Create header file
The recommended practice of C + + for creating a class and various methods is to pre define all functions and variables in the class in the header file. h, and then implement all these methods one by one in another. cpp file. Therefore, we need to define all variables of our matrix class (we will use CMatrix to represent this class later) in CMatrix.h
#ifndef CMATRIX_H #define CMATRIX_H #include<iostream> using namespace std; class CMatrix { private: // We define a two-dimensional matrix class with three variables int m_nRow;//Number of rows int m_nCol;//Number of columns double * m_pData=NULL;//Pointer used to store all value addresses in the matrix, initialized to NULL public: CMatrix(); //constructor CMatrix(int nRow, int nCol, double * pDate = NULL); //The default value cannot be placed before the variable value CMatrix(const CMatrix& m); CMatrix(const char * strPah); ~CMatrix(); bool Create(int nRow, int nCol, double* pDate); void Set(int nRow, int nCol, double dVale); void Release(); friend istream & operator >>(istream& is, CMatrix & m); //Global function //The friend keyword can access private properties and methods that cannot be accessed under normal circumstances externally. friend ostream& operator <<(ostream& os, const CMatrix &m); CMatrix& operator = (const CMatrix& m); CMatrix& operator+=(const CMatrix& m); double & operator[](int nIndex); double & operator()(int nRow,int nCol); bool operator == (const CMatrix& m); bool operator != (const CMatrix& m); operator double(); }; CMatrix operator+(const CMatrix& m1, const CMatrix& m2); inline void CMatrix::Set(int nRow, int nCol, double dVal){ m_pData[nRow*m_nCol + nCol]=dVal; } #endif // CMATRIX_H //Inline function: when an inline function needs to be called, instead of calling, insert the whole section of the function code where the inline function needs to be used, so as to save the calling process and improve the running speed. //It is not recommended to write more than ten lines of code as inline functions, nor is it recommended to write recursive functions as inline functions
After the header file is created, we begin to implement all the predefined methods one by one. First, we implement the constructor
2. Creation of various constructors
Next, we all implement these methods in the CMatrix.cpp file. First, refer to the header file
#include "cmatrix.h" #include <fstream> #include <assert.h> #include<iostream> using namespace std;
2.1 constructors without parameters
//The constructor without parameters is automatically assigned to the matrix parameter 0 rows and 0 columns by default, and the value is empty // The parameter list is used for assignment. After we apply for the parameter space, we initialize the parameter // At the same time, the order of assignment using the parameter list cannot be inconsistent with the order of initialized variables CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(NULL) { } //At the same time, some direct assignment and some call functions to assign values are also possible CMatrix::CMatrix():m_nRow(0),m_pData(NULL) { m_nCol = 0 } //The function can also be written as follows, but it is less efficient than the above CMatrix::CMatrix() { m_nCol = 0; m_nRow = 0; m_pData = NULL; }
2.2 constructor with row, column, data pointer and other parameters, and the parameters have default values;
CMatrix::CMatrix(int nRow, int nCol, double * pData=NULL){ m_pData=NULL; //First change the value of the pointer to null Create(nRow, nCol, pData); //In writing classes, it is found that assignment and class creation operations are often used, so a function is defined to enhance reusability } bool CMatrix::Create(int nRow, int nCol, double *pData){ Release(); //Before creating a new class space, release the previously occupied space to prevent bug s m_nRow = nRow; m_nCol = nCol; m_pData=new double[nRow*nCol]; //Open up a floating-point array to store all matrix values if(m_pData!=NULL){ //When there is not enough space, m_pData = NULL if(pData!=NULL){ memcpy(m_pData, pData, nRow*nCol*sizeof (double)); //Copy array values } } return true; } void CMatrix::Release() //When the class variable life cycle is up, the occupied space is released { if(m_pData) //If M_ If the pointer of pdata is not null, the value is released { delete []m_pData;//'[]' needs to be added to delete the array m_pData = NULL; } m_nRow = m_nCol=0; }
2.3 constructor with file path parameter;
In many large-scale operations on classes, we need to use file input to replace complicated manual input values, so creating class variables from file input is also very important for class integrity:
CMatrix::CMatrix(const char * strPath) { m_pData = NULL; m_nRow = m_nCol =0; ifstream cin(strPath);//Construct input stream from hard disk to file cin>>*this;//All member functions have a hidden this pointer }
However, note that the ordinary '> >' operator can only pass in some integer numbers, floating-point numbers and strings. He doesn't know how to read the class we created, so we also need to overload the operator:
friend istream & operator >>(istream& is, CMatrix & m); //Here, friend is used to represent the friend function, because this method is public, but it needs to access the private variable defined by us //So we need to use friend to tell the program that it is a friend function and that it can access private variables istream & operator>>(istream& is, CMatrix & m) //Overloaded operator '> >' { is>>m.m_nRow>>m.m_nCol; //Number of input stream assignment rows and columns m.Create(m.m_nRow, m.m_nCol,0); //Open up space for(int i=0;i<m.m_nRow*m.m_nCol;i++) { is>>m.m_pData[i];//Assign the values in the matrix one by one } return is; }
Another thing to note here is that the variables we create from the file must conform to the format. For example, first give the number of trips and columns, and write the values of the matrix one by one (an example is as follows):
2 2 1 2 3 4
2.4 copy constructor
Assign a new class by copying all the values of an existing class
CMatrix::CMatrix(const CMatrix& m):m_pData(NULL) { *this = m; //copy constructor }
One thing to note here is that the copy function we constructed shares an address with the original variables, which can only be regarded as a shallow copy, which means that when we modify the value of one variable, other variables will also change.
3. Creation of destructor
When the life cycle of a class variable comes to an end, the system automatically executes the destructor. Destructors are often used to "clean up the aftermath", that is, release memory and prevent memory leakage. When we don't write destructors, the system will automatically synthesize them for us, but such uncertainty affects our code. Therefore, we need to write a complete code ourselves:
CMatrix::~CMatrix() //Destructor overloading frees up space { Release(); //Call the function to release } void CMatrix::Release() { if(m_pData) { delete []m_pData;//'[]' needs to be added to delete the array m_pData = NULL; } m_nRow = m_nCol=0; }
4. Overloading of operators
The four operations of addition, subtraction, multiplication and division can be used freely in decimals and integers, but for the classes we create ourselves, the program does not know how to calculate addition, subtraction, multiplication and division (for example, in linear algebra, the multiplication of matrices has its own set of rules), so we also need to overload the operators to make our classes add and subtract
4.1 overloading of "+", "+ =" signs
CMatrix& CMatrix::operator+=(const CMatrix &m) //Adds its own value to another variable and returns itself { assert(m_nRow==m.m_nRow && m_nCol == m.m_nCol); // //The assertion function ensures that the two added matrices have the same shape for (int i=0; i<m_nRow*m_nCol;i++) { m_pData[i]+=m.m_pData[i]; //Add the results } return *this; } CMatrix operator+(const CMatrix& m1, const CMatrix& m2) { CMatrix m3(m1);//copy constructor m3+=m2; //Using the overload of + = symbol to express the operation of + sign quickly return m3; }
4.2 overloading of "-", "- ="
Subtraction is just the inverse operation of addition. Just modify the above function slightly:
CMatrix& CMatrix::operator-=(const CMatrix &m) { assert(m_nRow==m.m_nRow && m_nCol == m.m_nCol); for (int i=0; i<m_nRow*m_nCol;i++) { m_pData[i]-=m.m_pData[i]; } return *this; } CMatrix operator-(const CMatrix& m1, const CMatrix& m2) { CMatrix m3(m1);//copy constructor m3-=m2; return m3; }
4.3 overloading of relational operators
Overloading of '>', '<', '=' operators
Before defining the redefinition of the greater than less than symbol, we need to define a value to represent the size of the variable. Here, we use the sum of all values of the matrix to represent its size:
CMatrix::operator double() //Returns the sum of all values in a matrix { double dS=0; for(int i=0;i<m_nRow*m_nCol;i++) { dS+=m_pData[i]; } return dS; } //Size returns a Boolean value bool operator > (CMatrix& m1, CMatrix& m2) { return double(m1)>double(m2); } bool operator < (CMatrix& m1, CMatrix& m2) { return double(m1)<double(m2); } //Overload of '= =' operator //The comparison rule is to strictly compare all the values of the matrix, whether they are equal one by one, return True if they are equal, and return False if they are not equal bool CMatrix::operator==(const CMatrix &m) { if(!(m_nRow==m.m_nRow && m_nCol == m.m_nCol)) { return false; } for(int i=0;i<m_nRow*m_nCol;i++) { if(m_pData[i]!=m.m_pData[i]) { return false; } } return true; }
4.4. Overloading of subscript operators
Since it is a matrix, we need to access the functions of some specific location parameters, so we also need to overload the functions of "[]" and "()"
//The operator overload takes out the data according to the order of coordinates in the matrix. For example, (0,1) means that it is the first in order, so we use the subscript 0 double & CMatrix::operator[](int nIndex) { assert(nIndex<m_nRow*m_nCol);//Ensure that the access does not exceed the maximum limit of the array return m_pData[nIndex]; } //This operator overload obtains the value directly according to the coordinates. For example, (0,1) then we directly use (0,1) to access this location double & CMatrix::operator()(int nRow, int nCol) { assert(nRow*m_nCol+nCol < m_nRow*m_nCol); return m_pData[nRow*m_nCol + nCol]; }
4.5 overloading of assignment operators
CMatrix& CMatrix::operator=(const CMatrix &m) { if(this!=&m){ Create(m.m_nRow, m.m_nCol, m.m_pData); } //If the left and right of the equal sign are equal, we directly return the original value without any modification //If you do not operate yourself in this way, the create function will directly release the space before opening up. If you use m=m, there will be a bug, that is, all the values of M will be cleared to 0 return *this; }
5. Input and output transport symbols: <, > >
Just like the tostrifg () method of java for each class, we also need to define how to input and output the class. At the same time, because we need to access private variables, we need to define them as friend functions when defining functions
friend istream & operator >>(istream& is, CMatrix & m); //Global function //The friend keyword can access private properties and methods that cannot be accessed under normal circumstances externally. friend ostream& operator <<(ostream& os, const CMatrix &m); istream & operator>>(istream& is, CMatrix & m) //Overloaded operator '> >' { is>>m.m_nRow>>m.m_nCol; m.Create(m.m_nRow, m.m_nCol,0); for(int i=0;i<m.m_nRow*m.m_nCol;i++) { is>>m.m_pData[i]; } return is; } //Overloaded operator '< <' ostream & operator<<(ostream & os, const CMatrix &m) { os<<m.m_nRow<<" "<<m.m_nCol<<endl; //The number of rows and columns of the output matrix double *pData = m.m_pData; for(int i=0;i<m.m_nRow;i++) { for(int j=0;j<m.m_nCol;j++) { os<<*pData++<<" ";//Output the values of each variable in the matrix in turn, separated by spaces } os<<endl; } return os; }
6. Run test code
After writing the above methods, we certainly need to test the code to ensure the correctness of each code. The test code is as follows:
#include <iostream> #include "ccomplex.h" #include<stdio.h> #include "cmatrix.h" using namespace std; int main() { double pData[10] = {2, 3, 4,5 }; CMatrix m1,m2(2,5,pData),m3("D:\\1.txt"), m4(m2); //Test multiple constructor methods for correctness cin>>m1; //Test "> >" overload method m2.Set(1,3,10); cout<<m1<<m2<<m3<<m4; m4=m3; //Test '=' overload method m4[1]=m4+1; //Test '[]' overload method if(m4==m3) { cout<<"Error!!!!!"<<endl; } m4-=m3; cout<<"sum of m4 = "<<double(m4)<<endl;//Test the m4 sum after subtraction bool result = (m4>m3); cout<<"m4 > m3 the Result is "<<result<<endl;//Test ">" overload results return 0; }
Experimental results:
6. Conclusion
By creating a complete two-dimensional moment class this time, I realized the compilation of various constructor functions. I learned various code writing specifications in object-oriented programming in C + +, and the functions of various modifying nouns such as public, friend, private and inline in C + +. And learned the overloading of various operators, and learned that C + + is more free and less constrained than java language object-oriented programming, so we also need to pay more attention to details.