虚函数与多态性
一.静态联编
1.联编是指一个程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系; 就是把函数调用与函 数的程序代码连接(联系)在一起的过程。
2.联编分为两大类
1)静态联编
调用速度快,效率高,但缺乏灵活性。
C++为了兼容C语言仍然是编译型的,采用静态联编。
2)动态联编
运行效率低,但增强了程序灵活性。
为了实现多态性,利用虚函数机制,可部分地采用动态联编。
二.类指针的关系
基类指针和派生类指针与基类对象和派生类对象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 的派生类
(二)派生类指针引用基类对象
1.不能将一个声明为指向派生类对象的指针指向其基类的对象。
2.派生类指针只有经过强制类型转换之后,才能引用基类对象
三.虚函数与动态联编
(一)实现动态联编方式的前提
1.先要声明虚函数
2.类之间满足赋值兼容规则:派生类公有继承基类
3.通过指针与引用来调用虚函数。
(二)虚函数
- 冠以关键字 virtual 的成员函数称为虚函数
- 实现运行时多态的关键首先是要说明虚函数
- 必须用基类指针调用派生类的不同实现版本
虚函数的重载特性
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同。
注意:
1.构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。
2.析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象。
3.虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数。
4.构造函数、内联成员函数、静态成员函数不能是虚函数。(虚函数不能以内联的方式进行处理)
(三)虚析构函数
1.构造函数不能是虚函数。
2.析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象。
例,普通析构函数在删除动态派生类对象的调用情况
#include<iostream>
using namespace std ;
class A { public:
virtual~A()
{ cout << "A::~A() is called.\n" ; }
} ;
class B : public A { public:
~B()
{ cout << "B::~B() is called.\n" ; }
} ;
int main() { A *Ap = new B ; B *Bp2 = new B ;
cout << "delete first object:\n" ;
delete Ap;
cout << "delete second object:\n" ;
delete Bp2 ;
}
四.纯虚函数和抽象类
1.纯虚函数是一种特殊的虚函数,是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本
2.纯虚函数为各派生类提供一个公共界面
3. 纯虚函数说明形式:
virtual 类型 函数名(参数表)= 0 ;
4. 一个具有纯虚函数的基类称为抽象类。
5.纯虚函数的作用:
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
例.简单图形类
#include<iostream>
using namespace std ;
#include"figure.h"
class figure
{ protected : double x,y;
public: void set_dim(double i, double j=0) { x = i ; y = j ; }
virtual void show_area() = 0 ; };
class triangle : public figure
{ 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"; } };
virtual void show_area() = 0 ; //纯虚函数
void show_area()
{ cout<<"Triangle with high "<<x<<" and base"
<<y <<" has an area of "<<x*0.5*y<<"\n"; }
void show_area()
{ cout<<"Square with dimension "<<x<<"*"<<y
<<" has an area of "<<x*y<<"\n"; }
void show_area()
{ cout<<"Circle with radius "<<x;
cout<<" has an area of "<<3.14*x*x<<"\n";}};
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() ;
}
五.虚函数与多态的应用
#include<iostream>
#include<cstring>
#include<iomanip>
using namespace std ;
class Employee
{
public:
Employee(const long k,const char* str )
{
number = k;
strcpy(name,str);
}
virtual ~Employee()
{
name[0] = '\0';
}
const char * getName() const
{
return name;
}
const long getNumber() const
{
return number;
}
virtual double earnings() const=0;
virtual void print() const
{
cout <<number<<setw(20)<<name;
}
protected:
long number;
char name[20];
};
class Manager : public Employee
{
public:
Manager(const long k,const char * str, double salary=0.0):
Employee(k,str)
{
setMonthlySalary(salary);
}
~Manager() { }
void setMonthlySalary(double salary)
{
monthlySalary = salary;
}
virtual double earnings() const
{
return monthlySalary;
}
virtual void print() const
{
Employee::print();
cout<<setw(16)<<"Manager\n";
cout<<" earned $"<<earnings()<<endl;
}
private:
double monthlySalary;
};
class HourlyWorker : public Employee
{
public:
HourlyWorker(const long k,const char * str,double w=0.0,int h=0): Employee(k,str)
{
setWage(w);
setHours(h);
}
~HourlyWorker() {}
void setWage(double w)
{
wage = w;
}
void setHours(int h)
{
hours = h;
}
virtual double earnings() const
{
return wage * hours;
}
virtual void print() const
{
Employee::print();
cout<<setw(16)<<"Hours Worker\n"<<"\t\twageperhour "<<wage<<" Hours "<<hours;
cout<<" earned $"<<earnings()<<endl;
}
private:
double wage;
double hours;
};
class PieceWorker : public Employee
{
public:
PieceWorker(const long k,const char *str,double wage=0.0,int quantity=0):
Employee(k,str)
{
setWage(wage);
setQuantity(quantity);
}
~PieceWorker() { }
void setWage ( double wage )
{
wagePerPiece = wage;
}
void setQuantity ( int q)
{
quantity = q;
}
virtual double earnings() const
{
return wagePerPiece * quantity;
}
virtual void print() const
{
Employee::print();
cout<<setw(16) <<"Piece Worker\n";
cout<<"\t\twagePerPiece "<< wagePerPiece<<" quantity "<<quantity;
cout<<" eared $"<< earnings() <<endl;
}
private:
double wagePerPiece;
int quantity;
};
void test1()
{
cout << setiosflags(ios::fixed|ios::showpoint) << setprecision(2) ;
Manager m1 ( 10135, "Cheng ShaoHua", 1200 ) ;
Manager m2 ( 10201, "Yan HaiFeng");
m2.setMonthlySalary ( 5300 ) ;
HourlyWorker hw1 ( 30712, "Zhao XiaoMing", 5, 8*20 ) ;
HourlyWorker hw2 ( 30649, "Gao DongSheng" ) ;
hw2.setWage ( 4.5 ) ;
hw2.setHours ( 10*30 ) ;
PieceWorker pw1 ( 20382, "Xiu LiWei", 0.5, 2850 ) ;
PieceWorker pw2 ( 20496, "Huang DongLin" ) ;
pw2.setWage ( 0.75 ) ;
pw2.setQuantity ( 1850 ) ;
Employee *basePtr;
basePtr=&m1;
basePtr->print();
basePtr=&m2;
basePtr->print();
basePtr=&hw1;
basePtr->print();
basePtr=&hw2;
basePtr->print();
basePtr=&pw1;
basePtr->print();
basePtr=&pw2;
basePtr->print();
}
int main()
{
test1();
return 0;
}
六.异质链表
1.把不同类对象统一组织在一个数据结构中,可以定义抽象类指针数组或链表。
2.由于这种表中的具有不同类类型元素(它们都有共同的基类),所以称为“异质表”。
上一篇: 23. 多态性(虚函数)
下一篇: 实验六 多态性与虚函数