IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C++实验一:CMatrix类设计 -> 正文阅读

[数据结构与算法]C++实验一:CMatrix类设计

一、CMatrix类的代码实现

1. main.cpp

#include <iostream>
#include "CComplex.h"
#include <stdio.h>
#include "CMatrix.h"
using namespace std;
int main(int argc, char** argv) {
	//初始化,调用CMatrix()
	CMatrix m;
	cin >> m;
	cout << "m = " << m << endl;
	double pData[10] = { 1,2,3,4,5 };
	CMatrix m1(2, 5, pData);
	CMatrix m2("1.txt");
	cout << "m2 = \n" << m2 << endl;
	CMatrix m3(m1);
	CMatrix m4;
	//指向等一个地方
	m4 = m1;
	//第0行第1列改为20
	m1.Set(0, 1, 20);
	//此处m1,m4相同
	cout << "m3 = \n" << m3 << endl;
	cout << "m1 = \n" << m1 << endl;
	cout << "m4 = \n" << m4 << endl;
	m4 = m4;
	cout << "new m4 = \n" << m4 << endl;
	//[]重载,[0,1]不允许带多个参数
	cout << "m4[8] = " << m4[8] << endl;
	//()重载,()允许带多个参数
	m4(1, 1) = 10;
	cout << "m4(1,1) = " << m4(1, 1) << endl;
	if (m4 == m1) {
		cout << "Error!" << endl;
	}
	//+重载
	CMatrix  m5;
	m5 = m1 + m4;
	cout << "m5 = \n" << m5 << endl;
	//double重载
	double d = m5;
	cout << "sum of m5 = " << d << endl;

	return 0;
}

2. CMatrix.h

#pragma once
#ifndef CMATRIX_H
#define CMATRIX_H
#include <iostream>
using namespace std;

class CMatrix {
public:
	//不带参数的构造函数
	CMatrix();

	//如果nRow=0有默认值则之后的都得有默认值
	//若都默认值为0,相当于无参数,则会与上一个构造函数CMatrix();产生歧义,不知道调用哪一个
	//带行、列及数据指针等参数的构造函数, 并且参数带默认值
	CMatrix(int nRow, int nCol, double* pData = NULL);

	//拷贝构造函数
	CMatrix(const CMatrix& m);
	//带文件路径参数的构造函数
	CMatrix(const char* strPath);
	//析构函数名应与类名相同,只是在函数名前面加一个位取反符,以区别于构造函数。
	//它不能带任何参数,也没有返回值(包括void类型)。由于没有函数参数,因此它不能被重载,只能有一个析构函数。
	//如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显示的析构函数。
	//当对象的生命周期结束时,撤销类对象时候会自动调用析构函数。
	//1.对象在生命期结束,撤销类对象时会自动调用析构函数。
	//2.如果用new运算动态地建立一个对象,那用delete运算符释放该对象时,才会调用析构函数。
	~CMatrix();
	bool Create(int nRow, int nCol, double* pData = NULL);
	void Set(int nRow, int nCol, double dVal);
	//隐式内联函数,不声明
	//void CMatrix::Set(int nRow, int nCol, double dVal) {
	//	m_pData[nRow * m_nCol + nCol] = dVal;
	//}
	void Release();

	//赋予特权,特殊声明,friend加了之后为全局函数不是成员函数,类里面定义的所有对象都可以调用,所以可以访问下面的private私有变量
	//输出输入流运算符重载,原本输出输入流只能针对基础的数据类型,比如char,int,double,指针,重载后使其能够读取自己定义的类
	friend istream& operator>>(istream& is, CMatrix& m);
	friend ostream& operator<<(ostream& os, const CMatrix& m);

	CMatrix& operator=(const CMatrix& m);
	CMatrix& operator+=(const CMatrix& m);
	//  CMatrix& operator+(const CMatrix& m);
	//  CMatrix operator+(const CMatrix& m1,const CMatrix& m2);
	double& operator[](int nIndex);
	double& operator()(int nRow, int nCol);
	bool operator ==(const CMatrix& m);
	bool operator !=(const CMatrix& m);
	operator double();
//不允许外部访问
private:
	//行
	int m_nRow;
	//列
	int m_nCol;
	//存储地址
	double* m_pData;
};
//全局函数
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

3. CMatrix.cpp

#include "CMatrix.h"
#include <fstream>
#include <assert.h>
//这种初始化方式提高效率,顺序与成员变量原来的顺序一样,可同时与下面的初始化方式共用
CMatrix::CMatrix() :m_nRow(0), m_nCol(0), m_pData(NULL){
	//先申请空间完之后才赋值
	//m_nRow = 0;
	//m_nCol = 0;
	//m_pData = NULL;
}
CMatrix::CMatrix(int nRow, int nCol, double* pData) : m_pData(NULL) {
	//因为Create中有Release(),如果不初始化会因为不是NULL直接delete
	Create(nRow, nCol, pData);
}
//拷贝构造函数
CMatrix::CMatrix(const CMatrix& m) : m_pData(NULL) {
	*this = m;
}
//方法1:
//CMatrix::CMatrix(const CMatrix& m) :  {
//	m_nRow = 0;
//	m_nCol = 0;
//	m_pData = NULL;
//	Create(m_nRow, m_nCol, m_pData);
//}
//方法2:构造该函数,用另一个构造函数去构造
//CMatrix::CMatrix(const CMatrix& m) : CMatrix(m.m_nRow,m.m_nCol,m.m_pData) {
//	
//}
CMatrix::CMatrix(const char* strPath) {
	m_pData = NULL;
	m_nRow = m_nCol = 0;
	ifstream is(strPath);
	is >> *this;
}

CMatrix::~CMatrix() {
	Release();
}
bool CMatrix::Create(int nRow, int nCol, double* pData) {
	Release();
	m_nRow = nRow;
	m_nCol = nCol;
	//新建空间存储数据
	m_pData = new double[nRow * nCol];
	//当nRow*nCol数据过大时,m_pData分配空间不足,此时m_pData就会为空
	if (m_pData != NULL) {
		if (pData!=NULL) {
			//void *memcpy(void *destin, void *source, unsigned n);
			//作用是:以source指向的地址为起点,将连续的n个字节数据,复制到以destin指向的地址为起点的内存中。
			//函数有三个参数,第一个是目标地址,第二个是源地址,第三个是数据长度。
				memcpy(m_pData, pData, nRow * nCol * sizeof(double));
			}
	}
	else {
		return false;
	}
	return true;
}
//delete之后赋值为空,delete指针之后只是释放了空间,指针依旧存在且指向原来的地址,此时对该指针的操作都将十分容易报错
void CMatrix::Release() {
	if (m_pData!=NULL) {
		delete []m_pData;
		m_pData = NULL;
	}
	m_nRow = m_nCol = 0;
}
CMatrix& CMatrix::operator=(const CMatrix& m) {
	//判断如果等号左右相同,直接返回,否则会是空,因为Create开头是Release
	//等号运算符重载该有的判断
	if (this != &m) {
		Create(m.m_nRow, m.m_nCol, m.m_pData);
	}
	return *this;
}
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);
	m3 += m2;
	return m3;
}

//可以写成内联函数
double& CMatrix ::operator[](int nIndex) {
	assert(nIndex < m_nRow* m_nCol);
	return m_pData[nIndex];
}

double& CMatrix ::operator()(int nRow,int nCol) {
	assert(nRow*m_nCol+nCol < m_nRow* m_nCol);
	return m_pData[nRow * m_nCol + nCol];
}
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;
}
bool CMatrix::operator!=(const CMatrix& m) {
	return !((*this) == m);
}
//前面无double但是有返回值,因为这是强制类型转换,已经知道返回值就是double
CMatrix::operator double() {
	double dS = 0;
	for (int i = 0; i < m_nRow * m_nCol; i++) {
		dS += m_pData[i];
	}
	return dS;
}

istream& operator>>(istream& is, CMatrix& m) {
	//m.Release();后面Create开始就有 此处可以省略
	is >> m.m_nRow >> m.m_nCol;
	m.Create(m.m_nRow, m.m_nCol);
	for (int i = 0; i < m.m_nRow*m.m_nCol; i++) {
		is >> m.m_pData[i];
	}
	return is;
}
ostream& operator<<(ostream& os, const CMatrix& m) {
	os << m.m_nRow << " " << m.m_nCol << endl;
	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++<<" ";
		}
		os << endl;
	}
	return os;
	//上面的方法效率更高
	//os << m.m_nRow << "" << m.m_nCol << endl;
	//for (int i = 0; i < m.m_nRow; i++) {
	//	for (int j = 0; j < m.m_nCol; j++) {
	//		os << m.m_pData[i * m.m_nCol + j] << " ";
	//	}
	//	os << endl;
	//}
	//return os;
}



二、运行结果

在这里插入图片描述

三、函数

1. 构造函数

//不带参数的构造函数
CMatrix();
//如果nRow=0有默认值则之后的都得有默认值
//若都默认值为0,相当于无参数,则会与上一个构造函数CMatrix();产生歧义,不知道调用哪一个
//带行、列及数据指针等参数的构造函数, 并且参数带默认值
CMatrix(int nRow, int nCol, double* pData = NULL);
//拷贝构造函数
CMatrix(const CMatrix& m);
//带文件路径参数的构造函数
CMatrix(const char* strPath);

(1)分类

  • 不带参数的构造函数
  • 带有参数的构造函数
  • 拷贝构造函数
  • 带文件路径参数的构造函数
  • (2)特点

    构造函数具有如下几个特点:

  • 名字与类名相同,可以有参数,但是不能有返回值(void也不行);
  • 作用是对对象进行初始化工作,如给成员变量赋值等;
  • 如果定义类时没有写构造函数,系统会生成一个默认的无参构造函数,默认构造函数没有参数,不做任何工作;
  • 如果定义了构造函数,系统不再生成默认的无参构造函数;
  • 对象生成时构造函数自动调用,对象一旦生成,不能在其上再次执行构造函数
  • 一个类可以有多个构造函数,为重载关系。
  • 2. 析构函数

     //析构函数名应与类名相同,只是在函数名前面加一个位取反符,以区别于构造函数。
    //它不能带任何参数,也没有返回值(包括void类型)。由于没有函数参数,因此它不能被重载,只能有一个析构函数。
    //如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显示的析构函数。
    //当对象的生命周期结束时,撤销类对象时候会自动调用析构函数。
    //1.对象在生命期结束,撤销类对象时会自动调用析构函数。
    //2.如果用new运算动态地建立一个对象,那用delete运算符释放该对象时,才会调用析构函数。
    ~CMatrix();
    
  • 格式:在类名前加上~;且与构造函数不同的是,析构函数从来没有参数。
  • 作用:当对象的生命周期结束时,撤销类对象时候会自动调用析构函数。 1.对象在生命期结束,撤销类对象时会自动调用析构函数。 2.如果用new运算动态地建立一个对象,那用delete运算符释放该对象时,才会调用析构函数。
  • 3. 运算符重载

    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);
    

    C++预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型(类)是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需要我们对这么运算符进行重新定义,赋予其新的功能,以满足自身的需求。

    4. 友元函数

    //赋予特权,特殊声明,friend加了之后为全局函数不是成员函数,类里面定义的所有对象都可以调用,所以可以访问下面的private私有变量
    //输出输入流运算符重载,原本输出输入流只能针对基础的数据类型,比如char,int,double,指针,重载后使其能够读取自己定义的类
    friend istream& operator>>(istream& is, CMatrix& m);
    friend ostream& operator<<(ostream& os, const CMatrix& m);
    

    在实现类之间数据共享时,减少系统开销,提高效率。

    5. 内联函数

    以牺牲代码段空间为代价,提高程序的运行时间的效率(入栈与出栈操作)。

    (1)显示

  • 放在类外
  • //显式内联函数,好处相当于直接把函数放过去没有调用函数,调用函数还需要出栈入栈
    //十几行较多行的代码,递归,循环不建议写成内联函数
    inline void CMatrix::Set(int nRow, int nCol, double dVal) {
    	m_pData[nRow * m_nCol + nCol] = dVal;
    }
    

    (2)隐式

  • 放在类内
  • void CMatrix::Set(int nRow, int nCol, double dVal) {
    	m_pData[nRow * m_nCol + nCol] = dVal;
    }
    
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-09 16:31:37  更:2021-10-09 16:32:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 13:52:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码