c++笔记(9):联编、虚函数和多态性、异质表
来源:互联网 发布:java分解质因数 编辑:程序博客网 时间:2024/06/07 17:23
1.
联编是指一个程序模块、代码之间互相关联的过程。
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch 语句和 if 语句是动态联编的例子。
2.
静态联编:
普通成员函数重载可表达为两种形式:
<1>在一个类说明中重载
<2>基类的成员函数在派生类重载:根据参数的特征加以区分;使用“ :: ”加以区分;根据类对象加以区分
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
直接用基类指针引用基类对象;
直接用派生类指针引用派生类对象;
用基类指针引用一个派生类对象;
用派生类指针引用一个基类对象。
3.
基类指针引用派生类对象:基类指针可以通过派生类对象访问所有从基类继承的元素,但不能用基类指针访问派生类自定义的元素(除非用了显示类型转换)
派生类指针引用基类对象:
派生类指针只有经过强制类型转换之后,才能引用基类对象
冠以关键字 virtual 的成员函数称为虚函数
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
<2>虚函数必须是类的成员函数
<3>不能将友元说明为虚函数,但虚函数可以是另一个类的友元
<4>析构函数可以是虚函数,但构造函数不能是虚函数
6.
虚函数的重载特性
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
如果仅仅返回类型不同,C++认为是错误重载,如果函数原型不同,仅函数名相同,丢失虚特性
析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象
设计类层次结构时,提供一个虚析构函数,能够使派生类对象在不同状态下正确调用析构函数
8.
纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本,纯虚函数为各派生类提供一个公共界面
纯虚函数说明形式:virtual 类型 函数名(参数表)= 0 ;
一个具有纯虚函数的基类称为抽象类。
要使 ab_circle 成为非抽象类,必须作以下说明:
class ab_circle : public shape
{ int radius ;
public :
void rotate ( int ) ;
void draw ( ) ;
} ;
虚函数和多态性使成员函数根据调用对象的类型产生不同的动作
多态性特别适合于实现分层结构的软件系统,便于对问题抽象时定义共性,实现时定义区别
异质表
把不同类对象统一组织在一个数据结构中,可以定义抽象类指针数组或链表。
由于这种表中具有不同类类型元素(它们都有共同的基类),所以称为“异质表”。
联编是指一个程序模块、代码之间互相关联的过程。
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch 语句和 if 语句是动态联编的例子。
2.
静态联编:
普通成员函数重载可表达为两种形式:
<1>在一个类说明中重载
<2>基类的成员函数在派生类重载:根据参数的特征加以区分;使用“ :: ”加以区分;根据类对象加以区分
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
直接用基类指针引用基类对象;
直接用派生类指针引用派生类对象;
用基类指针引用一个派生类对象;
用派生类指针引用一个基类对象。
3.
基类指针引用派生类对象:基类指针可以通过派生类对象访问所有从基类继承的元素,但不能用基类指针访问派生类自定义的元素(除非用了显示类型转换)
#include<iostream>#include<cstring>using namespace std ;class A_class{ char name[20] ; public : void put_name( char * s ) { strcpy_s( 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_s ( 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 ( "5555_12345678" );//用派生类对象调用派生类的成员函数 ( ( B_class * ) A_p ) -> show_phone() ;//对基类指针强类型转换调用派生类的成员函数}
4.派生类指针引用基类对象:
派生类指针只有经过强制类型转换之后,才能引用基类对象
#include<iostream>#include<cstring>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 , month , 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 指针作类型转换调用基类成员函数,等价于Date :: Print(); cout << hours << ':' << minutes << ':' << seconds << '\n' ; } private: int hours , minutes , seconds ; };int main() { DateTime dt( 2009, 1, 1, 12, 30, 0 ) ; dt.Print() ; }
5.冠以关键字 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() ;//基类指针做类型转换}
#include<iostream>using namespace std ;class Base{ public : Base(char xx) { x = xx; } virtual 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() ;//由于who()的虚特性随着p指向不同对象,this指针作类型转换执行不同实现版本 p = &F_obj ; p -> who() ;//由于who()的虚特性随着p指向不同对象,this指针作类型转换执行不同实现版本 p = &S_obj ; p -> who() ;//由于who()的虚特性随着p指向不同对象,this指针作类型转换执行不同实现版本 }
<1>一个虚函数,在派生类层界面相同的重载函数都保持虚特性<2>虚函数必须是类的成员函数
<3>不能将友元说明为虚函数,但虚函数可以是另一个类的友元
<4>析构函数可以是虚函数,但构造函数不能是虚函数
6.
虚函数的重载特性
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
如果仅仅返回类型不同,C++认为是错误重载,如果函数原型不同,仅函数名相同,丢失虚特性
7.
虚析构函数
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象
#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 *Bp2 = new B ; cout << "delete first object:\n" ; delete Ap;//析构由基类指针建立的派生类对象只析构了基类没有调用派生类析构函数 cout << "delete second object:\n" ; delete Bp2 ;//析构由派生类指针建立的派生类对象正确调用派生类析构函数(先析构派生类再析构基类)}
#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 ;}
定义了基类虚析构函数,基类指针指向的派生类动态对象也可以正确地用delete析构设计类层次结构时,提供一个虚析构函数,能够使派生类对象在不同状态下正确调用析构函数
8.
纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本,纯虚函数为各派生类提供一个公共界面
纯虚函数说明形式: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 ;// 纯虚函数} ;shape x ;// error,抽象类不能建立对象shape *p ;// ok,可以声明抽象类的指针,不能建立抽象类型存储空间shape f ( ) ;// error, 抽象类不能作为函数返回类型void g ( shape ) ;// error, 抽象类不能作为传值参数类型shape & h ( shape &) ;// ok,可以声明抽象类的引用
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 ;// 纯虚函数} ;class ab_circle : public shape{ int radius ; public : void rotate ( int ) { } ;} ;
ab_circle 类仍为抽象类,ab_circle :: draw ( ) 、ab_circle :: rotate ( ) 也是纯虚函数要使 ab_circle 成为非抽象类,必须作以下说明:
class ab_circle : public shape
{ int radius ;
public :
void rotate ( int ) ;
void draw ( ) ;
} ;
并提供 ab_circle :: draw ( )和ab_circle :: rotate ( int )的定义
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"; }};#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() ; }#include<iostream>using namespace std ;#include"figure.h"int main(){ figure *p;// 声明抽象类指针 triangle t; square s; circle c; p=&t; p->set_dim(10.0,5.0); // triangle::set_dim() p->show_area(); p=&s; p->set_dim(10.0,5.0);// square::set_dim() p->show_area(); p=&c; p->set_dim(9.0);// circle::set_dim() p->show_area();}
9.#include<iostream>//使用抽象类引用using namespace std ;class Number{ public : Number (int i) { val = i ; } virtual void Show() = 0 ; protected: int val ;};class Hex_type : public Number{ public: Hex_type(int i) : Number(i) { } void Show() { cout << "Hexadecimal:" << hex << val << endl ; }};class Dec_type : public Number{ public: Dec_type(int i) : Number(i) { } void Show() { cout << "Decimal: " << dec << val << endl ; }};class Oct_type : public Number{ public: Oct_type(int i) : Number(i) { } void Show() { cout << "Octal: " << oct << val << endl ; }};?void fun( Number & n )// 抽象类的引用参数{ n.Show() ; }?int main(){ Dec_type n1(50); fun(n1);// Dec_type::Show() Hex_type n2(50); fun(n2);// Hex_type::Show() Oct_type n3(50); fun(n3);// Oct_type::Show()}
10.虚函数和多态性使成员函数根据调用对象的类型产生不同的动作
多态性特别适合于实现分层结构的软件系统,便于对问题抽象时定义共性,实现时定义区别
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;// 姓名};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 ;//私有数据,月薪};class HourlyWorker : public Employee{ public: HourlyWorker(const long, const char *, double=0.0, int =0 ); ~HourlyWorker(){} void setWage(double);// 置时薪 void setHours(int);// 置工时 virtual double earnings() const; // 计算计时工月薪 virtual void print() const;// 输出计时工月薪 private: double wage; double hours;};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;// 工件数};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();}
11.异质表
把不同类对象统一组织在一个数据结构中,可以定义抽象类指针数组或链表。
由于这种表中具有不同类类型元素(它们都有共同的基类),所以称为“异质表”。
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 ;//利用多态性访问不同派生类成员函数}
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; Employee *next ; // 增加一个指针成员 protected: long number; char * name;};void AddFront( Employee * &h, Employee * &t )// 在表头插入结点{ t->next = h ; h = t ; }?void test3()// 测试函数{ Employee * empHead = NULL , * ptr; ptr = new Manager( 10135, "Cheng ShaoHua", 1200 );// 建立第一个结点 AddFront( empHead, ptr ) ;// 插入表头 ptr = new HourlyWorker( 30712, "Zhao XiaoMing", 5, 8*20 );// 建立第二个结点 AddFront( empHead, ptr ); // 插入表头 ptr = new PieceWorker ( 20382, "Xiu LiWei", 0.5, 2850 );// 建立第三个结点 AddFront( empHead, ptr ) ;// 插入表头 ptr = empHead ; // 使用基类指针遍历链表 while( ptr ) { ptr -> print() ; ptr = ptr -> next ; } // 遍历链表,输出全部信息 ptr = empHead ; while( ptr ) // 遍历链表,输出姓名和工资 { cout << ptr -> getName() << " " << ptr -> earnings() << endl ; ptr = ptr -> next ; }}
0 0
- c++笔记(9):联编、虚函数和多态性、异质表
- 多态性之动态联编 虚函数
- c++--多态性和虚函数
- C++:多态性(虚函数)
- C++:多态性1(动态联编测试)
- 多态性和动态联编的分析
- C/C++ 初学简单笔记 —4— 多态性 虚函数和抽象类
- 动态联编学习:一、多态性和动态联编
- C/C++多态性(polymorphism)虚函数
- 多态性和虚函数
- 虚函数和多态性
- 虚函数和多态性
- 虚函数和多态性
- 多态性和虚函数
- 多态性和虚函数
- 多态性和虚函数
- 多态性和虚函数
- 多态性和虚函数
- LevelDB源码分析5-Arena.md
- 【模板】其他图论
- ubuntu14.04出现的依赖问题
- OutputCache概念学习
- c++中的4种显式类型的转换
- c++笔记(9):联编、虚函数和多态性、异质表
- winform DataGridView 属性说明
- activity的一些属性值详解
- 传统企业互联网转型升级新玩法:技术合伙
- springMVC拦截器处理ajax请求及数据返回
- mysql Slave_IO_Running:NO 或者connecting(爬坑录)
- linux安装vsftp出现本地用户无法访问
- 杭电1327 Knight Moves 马走日
- 七天使的通讯