虚函数与多态

多态性是指一个名字,多种语义;或界面相同,多种实现。
重载函数是多态性的一种简单形式。
多态的实现:
多态性的实现和联编这一概念有关。联编(绑定)就是把函数名与函数体的程序代码连接(联系)在一起的过程。
联编分成两大类:静态联编动态联编(虚函数允许函数调用与函数体的联系在运行时才进行)。
静态联编优点:调用速度快,效率高,但缺乏灵活性;
动态联编优点:运行效率低,但增强了程序灵活性。
多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。
编译时的多态是通过静态联编来实现的。静态联编就是在编译阶
段完成的联编。编译时多态性主要是通过函数重载和运算符重载实现的。
运行时的多态是用动态联编实现的。动态联编是运行阶段完成的
联编。运行时多态性主要是通过虚函数来实现的。
联编是指一个程序模块、代码之间互相关联的过程。
静态联编是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。
普通成员函数重载可表达为两种形式:
1.在一个类说明中重载,例如:
void Show(int,char);
void Show( char *,float ) ;
2.基类的成员函数在派生类重载。有3种编译区分方法:
(1)根据参数的特征加以区分
例如:void Show ( int,char );与void Show ( char *,float );不是同一函数,编译能够区分。
(2)使用::加以区分:
例如:A::show();有别于 B::show();
(3)根据类对象加以区分:
例如:Aobj.show()调用A::show()
Bobj.show()调用B::show()
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
1.直接用基类指针引用基类对象;
2.直接用派生类指针引用派生类对象;
3.用基类指针引用一个派生类对象;
4.用派生类指针引用一个基类对象。
例如:
A *p; //指向类型A的对象的指针
A A_obj;//类型A的对象
B B_obj; //类型B的对象
p=&A_obj; //p措拘类型A的对象
p=&B_obj; //p指向类型B的对象,它是A的派生类
注意:派生类指针只有经过强制类型转换后,才能引用基类对象。

#include<iostream>
using namespace std;
class Date{
public:
Date(int y,int m,int d){ 
SetDate(y,m,d);
}
void SetDate(int y,int m,int d){
year=y;
month=m; 
day=d;
}
void Print(){ 
cout<<year<<"/"<<month<<"/"<<day<<";";
}
protected:
int year;
int month;
int day;
};
class DateTime:public Date{
public :
DateTime(int y,int m,int d,int h,int mi,int s): Date( y, m, d){ SetTime(h,mi,s);
}
void SetTime(int h,int mi,int s){
 hours=h; 
 minutes=mi;
seconds=s; 
}
void Print(){
((Date*)this)->Print();//对this指针类型转换调用基类成员函数
}
private: 
int hours;
int minutes;
int seconds;
};
int main(){
cout<<hours<<":"<<minutes<<":"<<seconds<<'\n';
DateTime dt(2009,1,1,12,30,0);
dt.Print();
}

在这里插入图片描述
根据赋值兼容,用基类类型的指针指向派生类,就可以通过这个指针来使用类(基类或派生类)的成员函数。
如果这个函数是
普通
的成员函数,通过基类类型的指针访问到的只能是基类的同名成员
而如果将它设置为虚函数,则可以使用基类类型的指针访问到指针正在指向的派生类的同名函数。从而实现运行过程的多态
实现动态联编方式的前提
1.先要声明虚函数
2.类之间满足赋值兼容规则
3.通过指针引用来调用虚函数。
冠以关键字virtual的成员函数称为虚函数
基类指针只能访问派生类从基类继承的成员

#include<iostream>
using namespace std ;
class Base{
public:
Base(char xx){x=XX;}
void who(){ 
cout<<"Base class: "<<x<<"\n";
 }
protected:
char x;
};
class First_d:public Base{
public:
First_d(char XX,char yy):Base(xx){y=yy;}
void who(){
cout<<"First derived class: "<<X <<","<<y<<"\n"; 
}
protected:
char y;
};
class Second_ _d : public First _d
{ public :
Second_ d( char XX, char yy, char zz): First_ d( XX, yy){z=zz; }
void who() { cout << "Second derived class: "<<x<<","<<y<<","<<z<< "\n" ;}
protected: char z;
};
int main(){
Base B_obj('A');
 First_d F_obj('T','O'); Second_d S_obj('E','N','D');
Base *p;
p=&B_obj;
p->who();
p=&F_obj ;
p>who();
p=&S_obj ;
p->who();
F_obj.who();
(( Second_d*)p)->who();
}


注意:
1.一个虛函数,在派生类层界面相同的重载函数都保持虚特性
2.虚函数必须是类的成员函数
3.不能将友元说明为虚函数,但虚函数可以是另一个类的友元
4.析构函数可以是虚函数,但构造函数不能是虚函数
虚函数的重载特性
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同。
构造函数不能是虚函数,析构函数可以是虚的。
1.派生类应该从它的基类公有派生。
2.必须首先在基类中定义虚函数。
3.派生类对基类中声明虚函数重新定义时,关键字virtual可以不写。
4.一般通过基类指针访问虚函数时才能体现多态性。
5.一个虚函数无论被继承多少次,保持其虚函数特性
6.虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数。
7.构造函数、内联成员函数、静态成员函数不能是虚函数。(虚函数不能以内联的方式进行处理)
8.析构函数可以是虚函数,通常声明为虚函数。
纯虚函数的作用
在许多情况下,在基类中不能对虛函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
纯虚函数说明形式:virtual 类型 函数名(参数名)=0;
一个具有纯虚函数的基类称为抽象类。
注意:
抽象类不能建立对象,可以声明为抽象类的指针。
抽象类不能作为函数返回类型,不能作为传值参数类型,可以声明抽象类的引用。

class figure{
 protected:
  double x;
  double y;
public:
 void set_ dim(double i,double j=0){
 	x=i; 
	y=j;
}
virtual void show_area()= 0 ;
};
class trian{
virtual void show_area()=0;//纯虚函数
public:
void show_area(){ 
cout<<"Triangle with high "<<x<<" and base "<<y <<" has an area of"<<x*0.5*y<<"\n";
 }
};
class square:public figure{ 
public:
void show_area(){
 cout<<"Square with dimension"<<x<<"*"<<y <<" has an area of"<<x*y<<"\n";
  }
};
class circle:public figure{ 
public:
void show_area(){ 
cout<<"Circle with radius "<<x;
cout<<" has an area of"<<3.14*x*x<<"\n";
 }
};
#include<iostream>
using namespace std
#include"figure.h" 
int main(){
triangle t; //派生类对象
square s;
circle c;
t.set_dim(10.0,5.0);
t.show_area();
s.set_dim(10.0,5.0);
s.show_area();
c.set_dim(9.0);
c.show_area();
}

在这里插入图片描述

感想:
上学期通过Java学习了继承和多态还有抽象类以及接口等等,在学习过程中由于之前没有接触过遇到了很多的问题,甚至一开始连类怎么定义都不清楚,但通过网课,书本等方式渐渐了解了一些,学完Java之后再学C++感觉自己对类有了更深的认识,看到C++中类的介绍时也会想到学习过的Java的知识,而且在C++中学习了一些新的知识,比如多继承一个类可以直接继承多个基类;赋值兼容规则基类对象可以用公有派生类的对象来替代,不需要为每一个类设计单独的模块,从而大大提高了代码的效率等等,这些新的知识也为我们提供了更多实现类的方法。类的三大特性为封装,继承和多态。虽然类的知识已经讲完了,但是还需要进行深入的研究,把学过的知识真正变为自己的东西,才能在以后编写过程中更好的利用,写出更好的程序。通过编写图书管理系统的几个版本的编写也有了更深的认识,实现了对类中的运算符重载,进行文件读写,对图书的增删查找以及借书、还书和续借的操作等等,虽然写的程序不怎么样,很多功能存在问题等等,但是我也从中学到了很多的东西,加深了对知识的理解和使用,在课程设计中肯定能得到更好的利用。
接下来谈一下这段时间的感受吧!
到现在为止已经是十三周了,有好多课都已经结课了,程序设计B也在上周结课,在家学习的效率明显不如在学校的效率高,甚至有时候会偷懒,放松,抱有侥幸的心理认为开学日期还有很久不必着急,没意识到时间过得这么快转眼已经第十三周了,真的应该努力了,虽然一开始会抱怨作业的难度,自己完成的也并没有那么好,但是真的学到了东西,觉得作业难也只是因为自己能力的限制,只能怪自己没有达到预期的水平。而且感觉没有一开始上网课那么紧张了,还有几周基本上全部的课程就结束了,现在应该努力了,再不努力真的来不及了,想起一句话:“且将新火试新茶,诗酒趁年华。”趁着年轻多拼一下才会不留遗憾,以后的每一天都要充实的过,回想起来不会留遗憾。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值