C++基础:纯虚函数、抽象类、编程案例

2021-05-06 10:29

阅读:590

标签:this   Once   指针变量   名称   利用   子类   方式   自己的   base   

纯虚函数和抽象类

基本概念

(1)纯虚函数

纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本;

纯虚函数位各派生类提供一个公共界面(接口的封装和设计、软件的模块功能划分);

(2)抽象类

一个具有纯虚函数的基类成为抽象类

// 抽象类
class Base
{
public:
	virtual void fun1() = 0;	// 纯虚函数
	virtual void fun2() = 0;
};

void main()
{
	//Base	b; // err,抽象类不能实例化
	Base*	p;
}

抽象类案例

定义抽象类 Figure,纯虚函数 get_area 提供接口,由各个派生类自己实现函数的功能

// 抽象类 figure图形
class Figure
{
public:
	// 纯虚函数
	virtual void get_area() = 0; // 求面积
};

class Circle : public Figure
{
public:
	Circle(int r, int b)
	{
		this->r = r;
		this->b = b;
	}
	virtual void get_area()
	{
		cout h = h;
		this->d = d;
	}
	virtual void get_area()
	{
		cout h = h;
		this->d = d;
	}
	virtual void get_area()
	{
		cout get_area();	//多态
}

void main()
{
	Figure* pF = NULL;
	
	Circle		c(10, 0);
	Triangle	t(20, 30);
	Square		s(40, 50);

	// 通过父类指针指向子类对象
	area(&c);
	area(&t);
	area(&s);

	system("pause");
}

抽象类在多继承中的应用

C++中没有 Java 中的接口概念,抽象类可以模拟 Java 中的接口类。

多继承的应用场景:继承多个接口类(纯虚函数实现接口)

C++中可以使用纯虚函数实现接口;接口类中只有函数原型定义,没有任何数据的定义。

多重继承接口不会带来二义性和复杂性等问题;

接口类只是一个功能说明,而不是功能实现。

class Interface1
{
public:
	virtual int add(int a, int b) = 0;
	virtual void print() = 0;
};

class Interface2
{
public:
	virtual int mult(int a, int b) = 0;
	virtual void print() = 0;
};

class test : public Interface1, public Interface2
{
public:
	virtual void print()
	{
		cout add(1, 2);

	Interface2 *it2 = &c1;
	it2->mult(3, 4);

	system("pause");
	return;
}

抽象类知识点强化

编写一个程序,计算程序员( programmer )工资

(1)要求计算初级程序员(junior_programmer)中级程序员(mid_programmer)高级程序员(adv_programmer)的工资

(2)要求利用抽象类统一界面,方便程序的扩展

// 抽象类 programmer
class programmer
{
public:
	// 纯虚函数
	virtual void get_salary() = 0;
};

//初级程序员
class junior_programmer : public programmer
{
public:
	junior_programmer(char *name, char *job, int sal)
	{
		this->name = name;
		this->job = job;
		this->sal = sal;
	}
	virtual void get_salary()
	{
		cout name = name;
		this->job = job;
		this->sal = sal;
	}
	virtual void get_salary()
	{
		cout name = name;
		this->job = job;
		this->sal = sal;
	}
	virtual void get_salary()
	{
		cout get_salary();
}

void main()
{
	// 初始化
	junior_programmer	jp("张三", "初级工程师", 3000);
	mid_programmer		mp("李四", "中级工程师", 8000);
	adv_programmer		ap("王五", "高级工程师", 10000);

	CalProgSal(&jp);
	CalProgSal(&mp);
	CalProgSal(&ap);

	system("pause");
	return;
}

面向对象类编程思想

虚函数和多态性使成员函数根据调用对象的类型产生不同的动作

面向抽象类编程(面向接口编程)是项目开发中重要技能之一

几个重要的面向对象思想:继承-组合、注入、控制反转(IOC)、MVC、aop

案例:socket库C++模型设计与实现

需求

在系统框架中集成 Socket通信产品 和 加解密产品

Socket 通信产品:完成两点之间的通信;

加密产品:完成数据发送时加密;数据解密时解密;

设计:

CSocketProtocol :Socket接口类

CSocketImp:Socket产品类

CEncDesProtocol :des接口类

CEncDesImp:des产品类

  1. 框架(C函数方式:框架函数、C++类方式:框架类);

int SckSendAndRec_EncDec()

class SocketAndEncDec: 继承或组合

  1. 集成业务模型

系统框架 ---> 接口层

实现:

SocketProtocol.h

#pragma once

//接口层
//Socket接口类
class CSocketProtocol
{
public:
	CSocketProtocol(){;}
	// 虚析构函数
	virtual ~CSocketProtocol(){;}
	//---------------第一套api接口-----Begin---------------//
	//客户端初始化 获取handle上下
	virtual int cltSocketInit(/*out*/) = 0;
	//客户端发报文
	virtual int cltSocketSend(/*in*/ unsigned char* buf /*in*/, int buflen /*in*/) = 0;
	//客户端收报文
	virtual int cltSocketRev(/*in*/ unsigned char* buf /*in*/, int* buflen /*out*/) = 0;
	//客户端释放资源
	virtual int cltSocketDestory(/*in*/) = 0;
	//---------------第一套api接口-----End---------------//
};

CEncDesProtocol.h

#pragma once

//接口层
//加解密接口类

class CEncDesProtocol
{
public:
	CEncDesProtocol(){;}
	~CEncDesProtocol(){;}

	// 加密
	virtual int EncData(unsigned char* plain, int plainlen, unsigned char* cryptdata, int* cryptlen) = 0;
	// 解密
	virtual int DecData(unsigned char* cryptdata, int cryptlen, unsigned char* plain, int* plainlen) = 0;
};

SocketImp.h

#pragma once
#include "SocketProtocol.h"
// socket产品类
// 继承接口,实现虚函数功能
class CSocketImp : public CSocketProtocol
{
public:
	//客户端初始化 获取handle上下
	int cltSocketInit(/*out*/);
	//客户端发报文
	int cltSocketSend(/*in*/ unsigned char* buf /*in*/, int buflen /*in*/);
	//客户端收报文
	int cltSocketRev(/*in*/ unsigned char* buf /*in*/, int* buflen /*out*/);
	//客户端释放资源
	int cltSocketDestory(/*in*/);

private:
	unsigned char* buf;	// 报文
	int buflen;			// 报文长度
};

SocketImp.cpp

#include "iostream"
using namespace std;
#include "SocketImp.h"

//客户端初始化 获取handle上下
int CSocketImp::cltSocketInit(/*out*/)
{
	buf		= NULL;
	buflen	= 0;
	return 0;
}

//客户端发报文
int CSocketImp::cltSocketSend(/*in*/ unsigned char* buf /*in*/, int buflen /*in*/)
{
	int ret = 0;
	// 得到buf,分配内存
	this->buf = new unsigned char[buflen];
	if (this->buf == NULL)
	{
		ret = -1;
		cout buf, buf, buflen);
	this->buflen = buflen;

	return ret;
}

//客户端收报文
int CSocketImp::cltSocketRev(/*in*/ unsigned char* buf /*in*/, int* buflen /*out*/)
{
	int ret = 0;
	if (buf == NULL || buflen == NULL)
	{
		ret = -1;
		cout buf, this->buflen);
	*buflen = this->buflen;

	return ret;
}

//客户端释放资源
int CSocketImp::cltSocketDestory(/*in*/)
{
	if (buf != NULL)
	{
		delete buf;
		buf = NULL;
		buflen = 0;
	}
	return 0;
}

EncDesImp.h

#pragma once
#include "CEncDesProtocol.h"

// 加解密产品类
// 继承接口,实现虚函数功能
class CEncDesImp : public CEncDesProtocol
{
public:
	// 加密
	virtual int EncData(unsigned char* plain, int plainlen, unsigned char* cryptdata, int* cryptlen);

	// 解密
	virtual int DecData(unsigned char* cryptdata, int cryptlen, unsigned char* plain, int* plainlen);
};

EncDesImp.cpp

#include "iostream"
using namespace std;

#include "EncDesImp.h"
#include "des.h"

// 加密
int CEncDesImp::EncData(unsigned char* plain, int plainlen, unsigned char* cryptdata, int* cryptlen)
{
	int ret = 0;
	ret = DesEnc(plain, plainlen, cryptdata, cryptlen);
	if (ret != 0)
	{
		ret = -1;
		cout 

Test_Socket.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;

#include "SocketImp.h"
#include "SocketProtocol.h"
#include "EncDesImp.h"
#include "CEncDesProtocol.h"

//面向抽象类编程,框架 C函数实现
int SckSendAndRec_EncDec(CSocketProtocol* sp, CEncDesProtocol* ed, unsigned char* in, int inlen, unsigned char* out, int* outlen)
{
	int ret = 0;
	// 临时数据
	unsigned char data[4096];
	int datalen = 0;

	// 初始化 Socket
	ret = sp->cltSocketInit();
	if (ret != 0)
	{
		goto End;
	}

	// 加密输入的数据
	ret = ed->EncData(in, inlen, data, &datalen);
	if (ret != 0)
	{
		goto End;
	}

	// 发送加密的数据
	ret = sp->cltSocketSend(data, datalen);
	if (ret != 0)
	{
		goto End;
	}

	// 收到加密的数据
	ret = sp->cltSocketRev(data, &datalen);
	if (ret != 0)
	{
		goto End;
	}
	
	// 对加密的数据进行解密
	ret = ed->DecData(data, datalen, out, outlen);
	if (ret != 0)
	{
		goto End;
	}

End:
	ret = sp->cltSocketDestory();
	return 0;
}


//面向抽象类编程,框架 C++类实现
//继承:抽象类在多继承中的应用
/*
class SocketAndEncDec : public CSocketProtocol, public CEncDesProtocol
{
};
*/
// 组合
class SocketAndEncDec
{
public:
	SocketAndEncDec()
	{
		this->sp = NULL;
		this->ed = NULL;
	}
	SocketAndEncDec(CSocketProtocol* sp, CEncDesProtocol* ed)
	{
		this->sp = sp;
		this->ed = ed;
	}
	// 注入思想
	void setSp(CSocketProtocol* sp)
	{
		this->sp = sp;
	}
	void setEd(CEncDesProtocol* ed)
	{
		this->ed = ed;
	}

	// 框架成员函数
	int SckSendAndRec_EncDec_adv(unsigned char* in, int inlen, unsigned char* out, int* outlen)
	{
		int ret = 0;
		// 临时数据
		unsigned char data[4096];
		int datalen = 0;

		// 初始化 Socket
		ret = this->sp->cltSocketInit();
		if (ret != 0)
		{
			goto End;
		}

		// 加密输入的数据
		ret = this->ed->EncData(in, inlen, data, &datalen);
		if (ret != 0)
		{
			goto End;
		}

		// 发送加密的数据
		ret = this->sp->cltSocketSend(data, datalen);
		if (ret != 0)
		{
			goto End;
		}

		// 收到加密的数据
		ret = this->sp->cltSocketRev(data, &datalen);
		if (ret != 0)
		{
			goto End;
		}

		// 对加密的数据进行解密
		ret = this->ed->DecData(data, datalen, out, outlen);
		if (ret != 0)
		{
			goto End;
		}

	End:
		ret = this->sp->cltSocketDestory();
		return 0;
	}
private:
	CSocketProtocol* sp;
	CEncDesProtocol* ed;
};

//test
void main()
{
	int ret = 0;
	// 准备测试案例
	unsigned char in[4096];
	int inlen;
	unsigned char out[4096];
	int outlen = 0;

	strcpy((char*)in, "TestSocketAndEncDes");
	inlen = 10;

	// 框架 C函数实现
	{
		// 父类指针指向子类对象,实现多态
		CSocketProtocol* sp1 = NULL;
		CEncDesProtocol* ed1 = NULL;
		sp1 = new CSocketImp;
		ed1 = new CEncDesImp;

		// 使用接口
		ret = SckSendAndRec_EncDec(sp1, ed1, in, inlen, out, &outlen);
		if (ret != 0)
		{
			cout setEd(ed2);
		mySAED->setSp(sp2);

		ret = mySAED->SckSendAndRec_EncDec_adv(in, inlen, out, &outlen);
		if (ret != 0)
		{
			cout 

面向接口编程和C多态

函数类型

  1. 函数类型

函数三要素:名称、参数、返回值

C语言中的函数有自己特定的类型

函数类型:typedef int Func(int, int);

函数指针:函数指针用于指向一个函数,函数名是函数体的入口地址;

(1)可以通过函数类型定义函数指针:Func* pFunc;

(2)可以直接定义:int (*Func)(int, int);

  1. 函数指针

(1)数组指针(数组类型 --> 数组指针类型 --> 数组指针变量)

void main()
{
	// 定义一个数组类型 int a[10];
	{
		typedef int (myTypeArr)[10];
		myTypeArr myArr;
		myArr[0] = 10;
		printf("myArr[0]: %d\n", myArr[0]);
	}

	// 定义一个数组指针类型
	{
		int a[10] = { 0 };
		// 定义类型
		typedef int (*pTypeArr)[10];
		// 使用类型定义变量
		pTypeArr myPArr;
		// 使用变量
		myPArr = &a;
		// 使用数组指针 指向 a数组
		(*myPArr)[0] = 20;
		printf("a[0]: %d\n", a[0]);
	}
	system("pause");
	return;
}

(2)函数指针(函数类型 --> 函数指针类型 --> 函数指针变量)

int add(int a, int b)
{
	printf("func add result: %d\n", a+b);
	return a + b;
}
void main()
{
	// 定义一个函数类型  int add(int a, int b)
	{
		// 定义类型
		typedef int (myTypeFunc)(int, int);
		// 使用类型定义指针变量
		myTypeFunc* pFunc = NULL;

		// pFunc = &add; &可以省略
		pFunc = add;
		pFunc(1, 2); //间接调用	
	}

	// 定义一个函数指针类型
	{
		typedef int(*myPTypeFunc)(int, int);
		// 直接使用函数指针类型 定义 函数指针变量
		myPTypeFunc pFunc = NULL;
		pFunc = add;
		pFunc(3, 4);
	}

	// 函数指针
	{
		// 直接定义函数指针变量
		int (*myPFunc)(int, int);
		myPFunc = add;
		myPFunc(5, 6);
	}
	system("pause");
	return;
}

函数指针做函数参数(回调函数)

当函数指针操作函数的参数,传递给一个被调用函数,被调用函数就可以通过这个指针调用外部的函数,形成了回调

// 假设是不同实现者 实现的功能
int add(int a, int b)
{
	printf("func add result: %d\n", a+b);
	return a + b;
}
int sub(int a, int b)
{
	printf("func sub result: %d\n", a - b);
	return a - b;
}
int mul(int a, int b)
{
	printf("func mul result: %d\n", a * b);
	return a * b;
}

// 定义一个函数指针类型
typedef int (*myPTypeFunc)(int, int);
// 操作者,任务的编写,  函数指针做函数参数
// int Operator(int (*myPTypeFunc)(int, int))
int Operator(myPTypeFunc myFunc)
{
	int n = myFunc(1, 2);	// 间接调用
	return n;
}

//任务的调用
void main()
{
	// 使用回调函数
	Operator(add);
	Operator(sub);
	Operator(mul);

	system("pause");
	return;
}

思路:

(1)任务的实现,任务的操作框架,任务的调用有效的分离;

(2)任务的框架不用改变,去调用不同的函数,实现不同的功能 --> 通过回调函数调用不同的函数;

(3)回调函数:提前定义了一个协议(把函数参数、函数返回值提前约定),实现解耦合;

函数指针正向调用

显示调用dll中的函数:通过 LoadLibrary、GetProcAddress

TestDll

__declspec(dllexport) int myAdd(int a, int b);
__declspec(dllexport) int mySub(int a, int b);
__declspec(dllexport) int myMul(int a, int b);
__declspec(dllexport) int myDiv(int a, int b);
// 定义函数指针类型
typedef int(*myFunc)(int, int);

void main()
{
	// 用函数指针类型 定义 函数指针变量
	myFunc myAdd;
	myFunc mySub;
	myFunc myMul;
	myFunc myDiv;

	HINSTANCE hInstLibrary = LoadLibrary("c:/TestDLL.dll");
	myAdd = (myFunc)GetProcAddress(hInstLibrary, "myAdd");
	mySub = (myFunc)GetProcAddress(hInstLibrary, "mySub");
	myMul = (myFunc)GetProcAddress(hInstLibrary, "myMul");
	myDiv = (myFunc)GetProcAddress(hInstLibrary, "myDiv");

	myAdd(1, 2);
	mySub(2, 1);
	myMul(1, 2);
	myDiv(2, 1);

	system("pause");
	return;
}

案例:C动态库升级成框架

动态库:抽象一个套接口,单独封装成模块,给别人调用,无法扩展;

框架:能自由扩展,自由的集成第三方的产品;

技术图片

回调机制原理:

当具体事件发生时,调用者通过函数指针调用具体函数;

回调机制的将调用者和被调函数分开,两者互不依赖;

C++基础:纯虚函数、抽象类、编程案例

标签:this   Once   指针变量   名称   利用   子类   方式   自己的   base   

原文地址:https://www.cnblogs.com/2dx3906/p/13189739.html


评论


亲,登录后才可以留言!