浅谈C++多态
动态、静态联编
静态联编
静态联编是程序的匹配、连接在编译阶段实现,也称为早期匹配。比如,重载函数使用静态联编。
普通成员函数重载可表达为两种形式:
-
在一个类说明中重载
-
基类的成员函数在派生类重载。有 3 种编译区分方法:
(1)根据参数的特征加以区分
(2)使用“ :: ”加以区分
(3)根据类对象加以区分
类指针的关系
1.基类指针可以获取派生类对象地址,却只能访问派生类从基类继承的成员!
使用基类指针引用派生类对象会调用从基类继承的成员函数,若派生类中有函数和基类中重名,则不会调用派生类的函数。
2.派生类指针只有经过强制类型转换之后,才能引用基类对象 。
使用基类指针引用派s生类对象、派生类指针引用基类对象
方法:
①若使用指针,必须强制类型转换 使得指针类型发生变化
((要强制转换的类型 *)基类指针 )->调用的函数
②不用指针,通过对象调用函数
//使用基类指针引用派生类对象
#include<iostream>
#include<cstring>
using namespace std;
class A{
protected:
char name[20];
public:
void get(char *n){
strcpy(name,n);
}
void p(){
cout<<"A::name :"<<name<<endl;
}
} ;
class B:public A{
char ph[12];
public:
void get(char *n){
strcpy(ph,n);
}
void show_phone(){
cout<<"B::phone : "<<ph<<endl;
}
};
int main(){
A *p; //基类指针
A aa;
p=&aa; //引用基类对象
p->get("aaa");
p->p();
B bb;
p=&bb; //引用派生类对象
( ( B * ) p ) -> get("110"); //还可以直接调用bb对象完成传值 bb.get()
( ( B * ) p ) -> show_phone(); //强制类型转换,使指针指向派生类
return 0;
}
//派生类指针引用基类对象
//要调用基类函数,必须使用强制类型转换
#include<iostream>
#include<cstring>
using namespace std;
class DATE{
protected:
int year,month,day;
public:
DATE(int y,int m,int d){
setdate(y,m,d);
}
~DATE(){}
void setdate(int y,int m,int d){
year=y;
month=m;
day=d;
}
void print(){
cout<<"现在时间为:"<<year<<"年"<<month<<"月"<<day<<"日";
}
};
class DateTime : public DATE{
private:
int hour,minn,sec;
public:
DateTime(int y,int m,int d,int hh,int mm,int ss):DATE(y,m,d){
gettime(hh,mm,ss);
}
~DateTime(){}
void gettime(int hh,int mm,int ss){
hour=hh;
minn=mm;
sec=ss;
}
void print(){
//在此处打出日期和时间
((DATE *)this)->print();
//强制类型转换使得指针指向基类对象——与 DATE::print()等效!
cout<<hour<<"时"<<minn<<"分"<<sec<<"秒"<<endl;}
};
int main(){
DATE dd(2021,5,16);
DateTime dt(2021,5,16,10,45,45);
DateTime *p; //派生类指针
p=&dt;
p->print(); //指针指向派生类对象
return 0 ;
}
虚函数和动态联编
虚函数
冠以关键字 virtual 的成员函数称为虚函数。
虚函数必须是类的成员函数,在派生类层面相同的重载函数都保持虚特性
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同!
如果仅仅返回类型不同,C++认为是错误重载
如果函数原型不同,仅函数名相同,丢失虚特性
析构函数可以是虚函数,构造函数不能是虚函数!
不能将友元说明为虚函数,但虚函数可以是另一个类的友元。
基类有虚函数,派生类继承了函数——虚继承
实现虚继承,应具备两点:
①类与类之间具备继承关系
②具有虚函数 (冠以关键字 virtual 的成员函数)
实现运行时多态的方法
①有基类、派生类 (存在继承关系)
②说明虚函数
③用基类指针调用派生类的不同实现版本
为了实现基类指针的灵活调用,将要灵活使用的函数设为虚函数,以使得this指针根据虚特性作类型转换执行不同实现版本 。
//基类指针在运行时,调用不同版本的成员函数
#include<iostream>
using namespace std ;
class Base
{
public :
Base(char xx) { x = xx; }
virtual void who(){ cout << "Base class: " << x << "\n" ; }
//以关键字 virtual 声明虚函数 以使基类指针可以调用同形函数 (派生类中的 virtual 可省略)
protected:
char x;
} ;
class First_d : public Base
{
public :
First_d(char xx, char yy):Base(xx) { y = yy; }
virtual void who(){ cout << "First 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; }
virtual void who() { cout << "Second class: "<< x << ", " << y << ", " << z << "\n" ; }
protected:
char z;
} ;
int main(){
Base bb('x');
First_d fd('a','b');
Second_d sd('c','d','e');
Base *p;
p=&bb; p->who();
p=&fd; p->who();
p=&sd; p->who();
return 0;
}
虚析构函数
虚析构函数可指引 delete 运算符正确析构动态对象
普通析构函数在删除动态派生类对象时,会使得派生类中继承基类的部分内存资源无法回收,使用虚析构函数即可解决这个问题.
方法:在原有析构函数前加关键字 virtual
设计类层次结构时,提供一个虚析构函数,能够使派生类对象在不同状态下正确调用析构函数
动态联编
程序联编推迟到运行时进行,所以又称为晚期联编。switch 语句和 if 语句是动态联编的例子。
纯虚函数和抽象类
1.纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,其功能无法实现。
纯虚函数为各派生类提供一个公共界面.一个具有纯虚函数的基类称为抽象类。
说明形式:
virtual 类型 函数名(参数表)= 0 ;
【要使抽象类变为非抽象类,需要在派生类中将基类中的纯虚函数的功能实现】
若不对抽象类的功能进行实现,编译将不通过!
2.含有纯虚函数的类称为基类称为“抽象类”
不能建立抽象类型存储空间
①抽象类不能建立对象(无法进行内存分配)
②可以声明抽象类的指针
③可以声明抽象类的引用(因为引用不分配内存)