一、静态联编
(1)
联编是指一个程序模块、代码之间互相关联的过程。
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch 语句和 if 语句是动态联编的例子。
(2)
重载函数要根据类型、参数进行匹配,一般在编译阶段进行静态联编
普通成员函数重载可表达为两种形式::
a.在一个类说明中重载
例如:
void Show ( int , char ) ;
void Show ( char * , float ) ;
b.基类的成员函数在派生类重载。有 3 种编译区分方法:
(a1)根据参数的特征加以区分
例如: void Show ( int , char ); 与
void Show ( char * , float ); 不是同一函数,编译能够区分
(a2)使用“ :: ”加以区分
例如: A :: Show ( );
有别于 B :: Show ( );
(a3)根据类对象加以区分
例如: Aobj . Show ( ) 调用 A :: Show ( )
Bobj . Show ( ) 调用 B :: Show ( )
二、类指针的关系
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
直接用基类指针引用基类对象;
直接用派生类指针引用派生类对象;
用基类指针引用一个派生类对象;
用派生类指针引用一个基类对象。 例如:
class A
{
};
class B:public A
{
};
A * p ; // 指向类型 A 的对象的指针
A A_obj ; // 类型 A 的对象
B B_obj ; // 类型 B 的对象
p = & A_obj ; // p 指向类型 A 的对象
p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
利用 p,可以通过 B_obj 访问所有从 A 类继承的元素 ,但不能用 p访问 B 类自定义的元素 (除非用了显式类型转换)
例题一:使用基类指针引用派生类对象
#include<iostream>
#include<cstring>
using namespace std;
class A_class
{
char name[20];
public:
void put_name(char*s)
{
strcpy(name,s);
}
void show_name()
{
cout<<name<<"\n";
}
};
class B_class:public A_class
{
char phone_num[20];
public:
void put_phone(char*num)
{
strcpy(phone_num,num);
}
void show_phone()
{
cout<<phone_num<<"\n";
}
};
int main()
{
A_class*A_p;//基类指针
A_class A_obj;
B_class B_obj;
A_p=&A_obj;//基类指针指向基类对象
A_p->put_name("wang xiao hua");//基类指针调用基类成员函数
A_p->show_name();
A_p=&B_obj;//基类指针指向派生类对象
A_p->put_name("chen ming");//调用从基类继承的成员函数
A_p->show_name();
B_obj.put_phone("5556789");//用派生类对象调用派生类的成员函数
((B_class*)A_p)->show_phone();//用基类指针强类型转换调用派生类的成员函数
}
派生类指针只有经过强制类型转换之后,才能引用基类对象
例题二:日期时间程序。在派生类中调用基类同名成员函数
#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);
}
void SetDate(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
void Print()
{
cout<<year<<" "<<month<<" "<<day<<endl;
}
};
class DateTime:public Date
{
int hours,minutes,seconds;
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指针作类型转换调用基类成员函数
cout<<hours<<" "<<minutes<<" "<<seconds<<endl;
}
};
int main()
{
DateTime dt(2001,3,12,2,11,18);
dt.Print();
}
三、虚函数和动态联编
冠以关键字 virtual 的成员函数称为虚函数
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
(1)、基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
例题三:
//演示基类指针的移动
#include<iostream>
using namespace std;
class Base
{
public:
Base(char xx) {x=xx;}
void who() {cout<<" "<<x<<"\n";}
protected:
char x;
};
class First_d:public Base
{
public:
First_d(char xx,char yy):Base(xx){y=yy;}
void who(){cout<<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<<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();
}
输出结果:
A
T
E
T o
E N D
(2)、虚函数和基类指针注意:
一个虚函数,在派生类层面相同的重载函数都保持虚特性
虚函数必须是类的成员函数
不能将友元说明为虚函数,但虚函数可以是另一个类的友元
析构函数可以是虚函数,但构造函数不能是虚函数
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
如果仅仅返回类型不同,C++认为是错误重载
如果函数原型不同,仅函数名相同,丢失虚特性
例题四
虚函数重载特性
#include<iostream>
using namespace std;
class base
{ public :
virtual void vf1 ( ) ;
virtual void vf2 ( ) ;
virtual void vf3 ( ) ;
void f ( ) ;
} ;
class derived : public base
{ public :
void vf1 ( ) ; // 虚函数
void vf2 ( int ) ; // 重载,参数不同,虚特性丢失
// char vf3 ( ) ; // error,仅返回类型不同
void f ( ) ; // 非虚函数重载
} ;
void g ( )
{ derived d ;
base * bp = & d ; // 基类指针指向派生类对象
bp -> vf1 ( ) ; // 调用 deriver :: vf1 ( )
bp -> vf2 ( ) ; // 调用 base :: vf2 ( )
bp -> f ( ) ; // 调用 base :: f ( )
} ;
(3)、虚析构函数(构造函数不能是虚函数,析构函数可以是虚的,虚析构函数用于指引delete运算符正确析构动态对象)例题五A,普通析构函数在删除动态派生类对象的调用情况
#include<iostream>
using namespace std;
class A
{
public:
~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*bp=new B;//用派生类指针建立派生类的动态对象
cout<<"delete first object\n";
delete ap;//析构由基类指针建立的派生类对象没有调用派生类析构函数
cout<<"delete second object\n";
delete bp;//析构由派生类指针建立的派生类对象正确调用派生类析构函数
}
输出结果:
delete first object
A::~A() is called.
delete second object
B::~B() is called.
A::~A() is called.例题五B
#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*bp=new B;//用派生类指针建立派生类的动态对象
cout<<"delete first object\n";
delete ap;//析构由基类指针建立的派生类对象没有调用派生类析构函数
cout<<"delete second object\n";
delete bp;//析构由派生类指针建立的派生类对象正确调用派生类析构函数
}
输出结果:
delete first object
B::~B() is called.
A::~A() is called.
delete second object
B::~B() is called.
A::~A() is called.
四、纯虚函数和抽象类
纯虚函数说明形式:
virtual 类型 函数名(参数表)= 0 ;
一个具有纯虚函数的基类称为抽象类。
纯虚函数为各派生类提供一个公共界面
class point { /*……*/ } ;
class shape ; // 抽象类
{ point center ;
……
public :
point where ( ) { return center ; }
void move ( point p ) {center = p ; draw ( ) ; }
virtual void rotate ( int ) = 0 ; // 纯虚函数
virtual void draw ( ) = 0 ; // 纯虚函数
} ;
…...
hape x ; // error,抽象类不能建立对象
shape *p ; // ok,可以声明抽象类的指针
shape f ( ) ; // error, 抽象类不能作为函数返回类型
void g ( shape ) ; // error, 抽象类不能作为传值参数类型
shape & h ( shape &) ; // ok,可以声明抽象类的引用
五、虚函数与多态的应用
例题六计算雇员工资
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
using namespace std;
class Employee
{
public:
Employee(const long,const char*);
virtual~Employee();//虚析构函数
const char*getname() const;
const long getnumber() const;
virtual double earnings()const=0;//纯虚函数,用来计算月薪
virtual void print() const;//输出编号,姓名
protected:
long number;
char name[20];
};
Employee::Employee(const long k,const char*str)
{
number=k;
strcpy(name,str);
}
Employee::~Employee()
{
name[0]='\0';
}
const char*Employee::getname() const
{
return name;
}
const long Employee::getnumber() const
{
return number;
}
void Employee::print()const
{
cout<<number<<setw(20)<<name;
}
//管理人员领取固定月薪
class Manager:public Employee
{
public:
Manager(const long,const char*,double=0.0);
~Manager(){}
void setmonthlysalary(double);//设置月薪
virtual double earnings() const;//计算管理人月薪
virtual void print()const;
private:
double monthlysalary;//月薪
};
Manager::Manager(const long k,const char*str,double sal):Employee(k,str)
{
setmonthlysalary(sal);
}
void Manager::setmonthlysalary(double sal)
{
monthlysalary=sal>0?sal:0;
}
double Manager::earnings() const
{
return monthlysalary;
}
void Manager::print()const
{
Employee::print();
cout<<setw(16)<<"Manager"<<endl;
cout<<"\t $"<<monthlysalary<<endl;
}
//计时工人的月薪为基本工作时间的薪水加上加班费
class HourlyWorker:public Employee
{
public:
HourlyWorker(const long,const char*,double=0.0,int=0);
~HourlyWorker(){}
void setwage(double);//设置时薪
void sethours(int);//设置工时
virtual double earings()const;//计算计时工的月薪
virtual void print()const;//输出计时工月薪
private:
double wage;
double hours;
};
HourlyWorker::HourlyWorker(const long k,const char*str,double w,int h):Employee(k,str)
{
setwage(w);
sethours(h);
}
void HourlyWorker::setwage(double w)
{
wage=w>0?w:0;
}
void HourlyWorker::sethours(int h)
{
hours=h>=0&&h<=16*31?h:0;
}
double HourlyWorker::earings()const
{
if(hours<=8*22)
return wage*hours;
else
return wage*(8*22)+(hours-8*22)*wage*1.5;
}
void HourlyWorker::print()const
{
Employee::print();
cout<<setw(16)<<"Hours worker\n";
cout<<"\t wageperhours"<<wage<<"hours"<<hours;
cout<<"earned $"<<earnings()<<endl;
}
//计件工人
class PieceWorker:public Employee
{
public:
PieceWorker(const long,const char*,double=0.0,int=0);
~PieceWorker(){}
void setwage(double);//置工薪
void setquantity(int);//置工件数
virtual double earnings()const;
virtual void print()const;
private:
double wageperpiece;
int quantity;
};
PieceWorker::PieceWorker(const long k,const char*str,double w,int q):Employee(k,str)
{
setwage(w);
setquantity(q);
}
void PieceWorker::setwage(double w)
{
wageperpiece=w>0?w:0;
}
void PieceWorker::setquantity(int q)
{
quantity=q>0?q:0;
}
double PieceWorker::earnings()const
{
return quantity*wageperpiece;
}
void PieceWorker::print()const
{
Employee::print();
cout<<setw(16)<<"Piece worker\n";
cout<<"\t wageperpiece"<<wageperpiece<<"quantity"<<quantity;
cout<<"earned $"<<earnings()<<endl;
}
int main()
{
cout << setiosflags(ios::fixed|ios::showpoint) << setprecision(2) ;
Manager m1 ( 10135, "Cheng ShaoHua", 1200 ) ;
Manager m2 ( 10201, "Yan HaiFeng");
m2.setmonthlysalary ( 5300 ) ;
HourlyWorker h1 ( 30712, "Zhao XiaoMing", 5, 8*20 );//???h1,h2显示没有定义,不知道为啥
HourlyWorker h2 ( 30649, "Gao DongSheng");
h2.setwage ( 4.5 ) ;
h2.sethours ( 10*30 ) ;
PieceWorker p1 ( 20382, "Xiu LiWei", 0.5, 2850 ) ;
PieceWorker p2 ( 20496, "Huang DongLin" ) ;
p2.setwage ( 0.75 ) ;
p2.setquantity ( 1850 ) ;
Employee *basePtr;
basePtr=&m1; basePtr->print();
basePtr=&m2; basePtr->print();
basePtr=&h1; basePtr->print();
basePtr=&h2; basePtr->print();
basePtr=&p1; basePtr->print();
basePtr=&p2; basePtr->print();
}
六、异质链表
把不同类对象统一组织在一个数据结构中,可以定义抽象类指针数组或链表。
由于这种表中具有不同类类型元素(它们都有共同的基类),所以称为“异质表”。
例题(接上)
void test2()
{ Employee * employ[6] ;//数组元素是基类指针
int i;
employ[0] = new Manager( 10135, "Cheng ShaoHua", 1200 ) ;
employ[1] = new Manager( 10201, "Yan HaiFeng",5300 ) ;
employ[2] = new HourlyWorker( 30712, "Zhao XiaoMing", 5, 8*20 ) ;
employ[3] = new HourlyWorker( 30649, "Gao DongSheng", 4.5, 10*30 ) ;
employ[4] = new PieceWorker( 20382, "Xiu LiWei", 0.5, 2850 ) ;
employ[5] = new PieceWorker(20496, "Huang DongLin", 0.75, 1850) ;
cout << setiosflags(ios::fixed|ios::showPoint) << setprecision(2) ;
for( i = 0; i < 5; i ++ )
employ[i] -> print() ;
for( i = 0; i < 5; i ++ )
cout << employ[i]->getName() << " " << employ[i] -> earnings() << endl ;//利用多态性,访问不同派生类成员函数
}