C++ 多态 详解

一:静态绑定与动态绑定

静态绑定

编译时就能确定一条函数调用语句要调用的函数

在程序编译时多态性体现在函数和运算符的重载上

动态绑定

运行时才能确定函数调用语句调用的函数

程序运行时的多态性通过继承和虚函数来体现

二:多态性的概念

多态性是面向对象程序设计的重要特征之一

多态性概念:具有继承关系的类,其对象对同一个函数调用可以作出不同的响应

     同一个函数调用——同一条函数调用语句

     不同的响应——执行不同的函数

三:多态性的优点

多态性有助于更好地对程序进行抽象

     控制模块能专注于一般性问题的处理

     具体的操作交给具体的对象去做

多态性有助于提高程序的可扩展性

     可以把控制模块与被操作的对象分开

     可以添加已定义类的新对象,并能管理该对象

     可以添加新类(已有类的派生类)的新对象,并能管理该对象

四:虚函数

虚函数的概念:在基类中冠以关键字 virtual 的成员函数

虚函数的定义:

     函数类型 函数名称(参数列表);

     如果virtual一个函数在基类中被声明为虚函数,则他在所有派生类中都是虚函数(包括重定义函数)

只有通过基类指针或引用调用虚函数才能引发动态绑定(用基类指针指向派生类指针)

虚函数的使用 如下

//文件base.h定义基类
#if !defined BASE_H
#define BASE_H
class Base {
public:
	virtual void show();
};
#endif
//文件base.cpp基类的实现
#include “base.h”
#include <iostream.h>
void Base::show(){
	 cout<<"I am Base's object!\n";
}
//文件derived.h定义派生类Derived
#if !defined DERIVED_H
#define DERIVED_H
#include "base.h"
class Derived :public Base{
public:
	void show();
};
#endif
#include “derived.h”
#include <iostream.h>
void Derived::show(){
	 cout<<"I am Derived's object!\n";
}
//main函数文件
//测试多重继承的类层次
#include "derived.h"
int main(){
	Base *bPtr, bObj;
	Derived dObj;
	bPtr= &bObj;	//基类指针指向基类对象
	bPtr->show();//用基类指针调用成员函数,动态绑定
	bPtr= &dObj;	//基类指针指向基类对象
	bPtr->show();//用基类指针调用成员函数,动态绑定
	return 0;
}

虚函数的注意事项

1. 在类体系中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求。当然也可以像调用普通成员函数那样利用对象名来调用一个函数。

2. 在派生类中重新定义虚函数时,必须保证该函数的值和参数与基类中的说明完全一致,否则就属于重载

3. 若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码。、

4. 虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。虚函数不得是一个静态成员。

五:虚函数和重载函数的区别

1. 成员函数被重载需要在相同范围(同一个类中),而虚函数要求在不同的范围(一个在派生类,一个在基类)

2. 重载函数要求函数有相同函数名称,并有不同的参数序列;而虚函数则要求函数名、返回值类型和参数序列完全相同

3. 重载函数可以是成员函数或友员函数,而虚函数只能是成员函数

4. 重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不同类的虚函数

5. 虚函数在运行时表现出多态功能,这是C++的精髓;而重载函数则在编译时表现出多态性

六:虚析构函数

虚析构函数

虚析构函数使用 如下

//文件employee.h,定义基类Employee
#include <iostream.h>
#if !defined EMPLOYEE_H
#define EMPLOYEE_H
class Employee{
public:
	Employee(){
		cout<<"Employee begin!"<<endl;
	}
  	virtual ~Employee()   {
		cout<<"Employee end!"<<endl;
	}
};
#endif
//文件accountant.h定义派生类Accountant
#include <iostream.h>
#include "employee.h"
class Accountant: public Employee{
public:
	Accountant(int n) {
      	cout<<"Accountant begin!“
		<<endl;
		age = n;
	}
	~Accountant() {
		cout<<"Accountant end!"<<endl;
	}
private:
	int age;
};
//文件programmer.h
//定义派生类Programmer
#ifndef PROGRAMMER_H
#define PROGRAMMER_H
#include "employee.h"
class Programmer: public Employee{
public:
	Programmer(char *str) ;
	~Programmer();
private:
	char *name;
};
#endif
//文件programmer.cpp
include “programmer.h”
#include <iostream.h>
#include <string.h>
Programmer:: ~Programmer() {
	delete []name;
     cout<<"Programmer end!“
	<<endl;
}
Programmer::Programmer(char *str){
	cout<<"Programmer begin!“
	<<endl;
	name = new char[strlen(str)+1];
	strcpy(name, str);
}
#include "accountant.h"
#include "programmer.h"
const int MAX = 100;
void main() {
	int no;
	//声明储存雇员信息的数组
	Employee *ptr[MAX], *tptr;
	int ENum = 0;
	char name[100];
	int age;
	for (int i=0; i<MAX; i++){  
		ptr[i] = NULL;  
    }
    //输入雇员信息
	cout<<"Input employees' info:“
	<<endl;
	cout<<"1 --- Programmer“
	<<endl;
	<<"2 --- Accountant"<<endl
	<<"0 --- exit"<<endl;
	cin>>no;
	while (no){
    switch (no) {
	case 1:
	//输入程序员信息
	cout<<"Please input his or her name:";
	cin>>name;
	tptr = new Programmer(name);
	ptr[ENum++] = tptr;
	break;
	case 2:
	//输入会计信息
	cout<<"Please input his or her age:";
    cin>>age;
	tptr = new Accountant(age);
	ptr[ENum++] = tptr;
	break;
	default :
	break;
    	}
	cout<<"Input another employee's info:“
	<<endl;
	cout<<"1 --- Programmer"<<endl
	<<"2 ---Accountant"<<endl
	<<"0 --- exit“<<endl;
	cin>>no;
	}
}

七:纯虚函数

实现多态性的前提

     需要有共同的基类

     需要在基类中定义共同的接口

     接口要定义为虚函数

如果基类的接口没办法实现怎么办?

     如形状类Shape

解决方法

     不实现这些接口:纯虚函数

     包含纯虚函数的类:抽象基类

1. 在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做

2. 定义纯虚函数:

  class 类名{

        virtual 返回值类型 函数名(参数表) = 0;

    };

 3. 纯虚函数不需要实现

八:抽象类

1. 如果一个类中至少有一个纯虚函数,那么这个类被成为抽象类(abstract class)

2. 抽象类存在的意义是作为其它类的基类,也叫抽象基类

3. 主要作用:取若干类的共同行为,形成更清晰的概念层次。使用抽象类符合程序设计中的单选原则(single choice principle)。

抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用

可使用指向抽象类的指针支持运行时多态性

派生类中必须重写基类中的纯虚函数,否则它仍将被看作一个抽象类

抽象类的使用 如下

//文件shape.h
//定义抽象基类Shape
#if !defined SHAPE_H
#define SHAPE_H
#include <iostream.h>
class Shape{
public:
	virtual double area() const = 0;
	virtual void show() const = 0;
};
#endif
//文件circle.h,定义派生类Circle
#if !defined CIRCLE_H
#define CIRCLE_H
#include "shape.h"
#define PI 3.1416
class Circle :public Shape {
public:
	Circle(double=0.0,double=0.0,
	double=1.0);
   	double area() const;
   	void show() const;
private:
   double x,y;
   double r;
};
#endif
//文件circle.cpp,实现类Circle
#include "circle.h"
Circle::Circle(double a, double b, double c)
{
	x = a;
	y = b;
   	r = c;
}
double Circle::area() const {
	return PI*r*r;  
}
void Circle::show() const {
	cout<<"I am a Circle: ";
}
//文件rectangle.h
//定义派生类Rectangle
#if !defined __RECTANGLE__H__
#define __RECTANGLE__H__
#include "shape.h"
class Rectangle :public Shape {
public:
   Rectangle(double = 1.0, double = 1.0);
   double area() const;
   void show() const;
private:
   double length;
   double width;
};
#endif
//文件rectangle.cpp,实现类Rectangle
#include "rectangle.h"
Rectangle::Rectangle(double a, double b){
   length = a;
   width = b;
}
double Rectangle::area() const{
   return length*width;
}
void Rectangle::show() const{
   cout<<" I am a Rectangle: ";
}
#include "circle.h"
#include "rectangle.h"
void callArea(Shape &);
int main() {
	Circle cir(0.0, 0.0, 2.5);
   	Rectangle rec(2.4, 5.3);
   	callArea(cir);
   	callArea(rec);
   	return 0;
}
void callArea(Shape &obj) {
	obj.show();
	cout<<"area = "<<obj.area()<<endl;
}

版权声明:本文为m0_56051805原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。