类与对象学习总结3

(类与对象的最后一篇了)

目录

三、多态

1.多态分为两类

2.多态的优点

3.纯虚函数和抽象类

4.多态原理剖析

5.最后通过一个案例


三、多态

多态:面向对象的三大特征之一。多态,顾名思义就是多种形态。下面是我自己的理解,

对于一个类,可能有很多个子类,子类会继承父类的成员,而当通过父类指针创造子类对象时,会导致无法调用子类成员函数,这时就需要用到多态。

1.多态分为两类

1.静态多态:函数重载和运算符重载都属于静态多态,复用函数名
2.动态多态:派生类和虚函数实现运行时多态

特征
静态多态地址早绑定,即在编译阶段已绑定;动态多态地址晚绑定,即在运行阶段绑定,而后者就是我们接下来要讲的,通过动态多态实现地址晚绑定,在执行时才确定调用的是哪个对象的成员函数。

我们会使用虚函数,将父类的成员函数设置为虚函数,让函数地址不在编译阶段绑定,而在运行阶段绑定具体子类对象。语法:在父类成员函数前+virtual, 下面示例代码

#include<iostream>
using namespace std;
class Animal{
	public:
		//虚函数 想让猫说话,就得让函数地址晚绑定,在运行阶段绑定 ,称为动态联编 
		virtual void speak(){
			cout<<"动物在说话"<<endl; 
		}
}; 
class Cat:public Animal{
	public:
		void speak(){
			cout<<"小猫在说话"<<endl;
		}
};
class Dog:public Animal{
	public:
		void speak(){
			cout<<"小狗在说话"<<endl;
		}
};
void doSpeak(Animal &animal){ 
	animal.speak();
}
int main(){
	Cat cat;
	doSpeak(cat);//Animal &animal=cat
	Dog dog;
	doSpeak(dog); 
	return 0;
}

从这个案例我们可以总结一下,多态的使用场景,多态一般满足以下条件:
1.有继承关系
2.子类重写了父类的虚函数
多态使用
·用父类的指针或引用指向子类对象
·重写函数的返回值,函数名,参数列表完全一样(区别于函数重载)

2.多态的优点

没接触面向对象时,我们写的程序是没有经过封装的,对于临时的使用确实不成问题,但是如果是一个长期使用的项目,你可能需要对原先的代码进行修改、拓展,又或需要与别人合作,这时需要与别人共享代码。这时多态的优势会体现出来,多态的使用会使代码

1.代码组织结构清晰
2.可读性强
3.利于前期和后期的扩展和维护

下面通过一个计算器的实现作为例子

#include<iostream>
using namespace std;
//下面利用多态写一个计算器
//计算器抽象类(虚基类)
class counter{
	public:
		int a;
		int b;
		virtual int getResult(){
			return 0;
		}
		virtual ~counter(){
		}
}; 
//加法计算器
class Addcounter:public counter{
	public:
		int getResult(){
			return a+b;
		}
}; 
//减法计算器
class Subcounter:public counter{
	public:
		int getResult(){
			return a-b;
		}
}; 
//乘法计算器
class mulcounter:public counter{
	public:
		int getResult(){
			return a*b;
		}
};  
//不同计算写成不同的类,结构清晰,可读性强 
void func(){
	/*多态使用的条件
	有继承关系;
	父类指针指向子类对象*/
	counter *c=new Addcounter;//堆区开辟 
	c->a=10;
	c->b=20;
	cout<<c->a<<"+"<<c->b<<"="<<c->getResult()<<endl;
	delete c;//用完记得手动销毁 
	c=new Subcounter;
	c->a=10;
	c->b=20;
	cout<<c->a<<"-"<<c->b<<"="<<c->getResult()<<endl;
	delete c;
	c=new mulcounter;
	c->a=10;
	c->b=20;
	cout<<c->a<<"*"<<c->b<<"="<<c->getResult()<<endl;
	delete c;
}
int main(){
	func();
	return 0;
}

如果未使用多态,我们通常会使用条件语句来做计算器,但是当你需要修改计算器,或者添加功能时你就需要去修改源代码。而使用多态,若需要修改只需修改有错误的类,其他的不用动,如果需要扩展功能也不需要修改源码,只需添加子类即可。

3.纯虚函数和抽象类

上文我们讲到了在父类中写虚函数来实现多态,这时我们会发现,父类的虚函数内容失去了实际意义,这时候可以把它写成纯虚函数。

语法:virtual 返回值类型 函数名(参数列表)=0;

一个类只要写了一个纯虚函数,就成为抽象类,有以下两个特点
1.抽象类无法实例化对象
2.抽象类的子类如果不重写纯虚函数,则也为抽象类

#include<iostream>
using namespace std;
class father{
	public:
		virtual void func()=0;//纯虚函数 
		virtual ~father(){};//为了防止释放堆区数据时的警告 
}; 
class son1:public father{
	public:
		void func(){
			cout<<"son1-func()调用"<<endl; 
		}
}; 
class son2:public father{
	public:
		void func(){
			cout<<"son2-func()调用"<<endl; 
		}
}; 
int main(){
	//father f;不可实例化对象
	father *f=new son1;
	f->func();
	delete f;
	f=new son2;
	f->func();
	delete f; 
	return 0;
}

注意:子类一定要重写父类纯虚函数,否则无法实例化

下面通过一个实例进行演示,制作饮品,大致流程:煮水->冲泡->倒入杯中->加入佐料,实现不同饮品的制作

#include<iostream>
using namespace std;
class AbstractDrinking{
	public:
		virtual void boil()=0;
		virtual void brew()=0;
		virtual void Pour()=0;
		virtual void addsomething()=0;
		virtual ~AbstractDrinking(){}
		void makeDrinking(){
			boil();
			brew();
			Pour();
			addsomething();
		}
};
class coffee:public AbstractDrinking{
	public:
		void boil(){
			cout<<"煮个水先"<<endl;
		}
		void brew(){
			cout<<"冲泡咖啡"<<endl; 
		}
		void Pour(){
			cout<<"倒入咖啡杯"<<endl;
		}
		void addsomething(){
			cout<<"加入糖和牛奶"<<endl;
		}
};
class tea:public AbstractDrinking{
	public:
		void boil(){
			cout<<"先煮个水"<<endl;
		}
		void brew(){
			cout<<"冲泡茶叶"<<endl; 
		}
		void Pour(){
			cout<<"倒入茶杯"<<endl;
		}
		void addsomething(){
			cout<<"加入枸杞"<<endl;
		}
};
void doDrinking(AbstractDrinking *abs){
	abs->makeDrinking();
	delete abs;
}
int main(){
	doDrinking(new coffee);
	cout<<"******************************"<<endl;
	doDrinking(new tea);
	return 0;
}

4.多态原理剖析

父类中的函数成为虚函数时,其内部结构是一个虚函数指针指向一个虚函数表,虚函数表内部写记录了父类定义域下的函数地址;而子类继承父类后,其内部结构同父类

图片来自B站黑马程序员《黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难》

而当子类重写父类虚函数时,其虚函数表内部会被替换成子类虚函数地址

而当我们通过父类指针创建子类对象时,即发生多态,调用函数,就会指向对应子类的虚函数表中的地址,这个过程实在运行阶段实现的,所以这时函数实现了晚绑定

图片来自B站黑马程序员《黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难》

5.最后通过一个案例

实现多台电脑组装,具体实现就是结合上述知识,示例代码

#include<iostream>
using namespace std;
class Cpu{
	public:
		//抽象计算函数 
		virtual void calculate()=0;
		virtual ~Cpu(){
//			cout<<"Cpu的析构函数调用"<<endl; 
		} 
}; 
class Gpu{
	public:
		//抽象显示函数 
		virtual void display()=0;
		virtual ~Gpu(){
//			cout<<"Gpu的析构函数调用"<<endl;
		}
}; 
class Memory{
	public:
		//抽象存储函数 
		virtual void storge()=0;
		virtual ~Memory(){
//			cout<<"Memory的析构函数调用"<<endl;
		} 
};
class Computer{
	public:
		Computer(Cpu *cpu,Gpu *gpu,Memory *memory){
			this->cpu=cpu;
			this->gpu=gpu;
			this->memory=memory;
		}
		void dowork(){
			cpu->calculate();
			gpu->display();
			memory->storge();
		}
		~Computer(){
			if(cpu!=NULL){
				delete cpu;
				cpu=NULL;
			}
			if(gpu!=NULL){
				delete gpu;
				gpu=NULL;
			}
			if(memory!=NULL){
				delete memory;
				memory=NULL;
			}
		}
	private:
		Cpu *cpu;
		Gpu *gpu;
		Memory *memory;
}; 
class intelCpu:public Cpu{
	void calculate(){
		cout<<"intel的cpu正在计算"<<endl;
	}
	~intelCpu(){
//		cout<<"intelCpu的析构函数调用"<<endl;
	}
};
class intelGpu:public Gpu{
	void display(){
		cout<<"intel的Gpu正在显示"<<endl; 
	} 
	~intelGpu(){
//		cout<<"intelGpu的析构函数调用"<<endl; 
	}
}; 
class intelMemory:public Memory{
	void storge(){
		cout<<"intel的内存条正在储存"<<endl;
	}
	~intelMemory(){
//		cout<<"intelMemory的析构函数调用"<<endl;
	}
};
class lenovoCpu:public Cpu{
	void calculate(){
		cout<<"lenovo的cpu正在计算"<<endl;
	}
	~lenovoCpu(){
//		cout<<"lenovoCpu的析构函数调用"<<endl;
	}
};
class lenovoGpu:public Gpu{
	void display(){
		cout<<"lenovo的Gpu正在显示"<<endl; 
	} 
	~lenovoGpu(){
//		cout<<"lenovoGpu的析构函数调用"<<endl;
	} 
}; 
class lenovoMemory:public Memory{
	void storge(){
		cout<<"lenovo的内存条正在储存"<<endl;
	}
	~lenovoMemory(){
//		cout<<"lenovoMemory的析构函数调用"<<endl;
	}
};
int main(){
	Computer *c1=new Computer(new intelCpu,new intelGpu,new intelMemory);
	c1->dowork();
	delete c1;
	cout<<"*******************************************************"<<endl;
	c1=new Computer(new lenovoCpu,new lenovoGpu,new lenovoMemory);
	c1->dowork();
	delete c1;
	cout<<"*******************************************************"<<endl;
	c1=new Computer(new intelCpu,new lenovoGpu,new intelMemory);
	c1->dowork();
	delete c1;
	return 0;
}
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值