摘要:多态性提供一组统一的调用接口函数,依据这些条用接口函数具体对象的不同,同一名字的函数会有不同的行为。
1、重载与隐藏
(1)、对同一作用域中的同名函数,如果它们的函数特征标不同,那么它们就形成一种重载关系。
(2)、基类与派生类中非虚同名函数,不管它们的参数特征标是否相同,它们都形成隐藏关系,即派生类对象隐藏基类中的同名函数。
1 #include2 3 using namespace std; 4 5 class Animal 6 { 7 public: 8 void walk() 9 {10 cout << "Animal walk.\n";11 }12 void walk(int walkWay)13 {14 switch(walkWay)15 {16 case 0:17 cout << "Animal jump...\n";18 break;19 case 1:20 cout << "Animal fly...\n";21 break;22 default:23 cout << "Animal scrawling...\n";24 }25 }26 void sleep()27 {28 cout << "Animal sleeping..." << endl;29 }30 };31 class Human:public Animal32 {33 public:34 void sleep()35 {36 cout << "Human sleep, lying...\n";37 }38 };39 40 int main()41 {42 Animal *pAnimal = NULL;43 Animal animal;44 Human human;45 46 pAnimal = &animal;47 pAnimal->sleep();48 49 pAnimal = &human;50 pAnimal->sleep();51 52 Human *pHuman = &human;53 pHuman->sleep();54 55 pHuman->walk(1);56 pHuman->walk();57 58 int c;59 cin >> c;60 return 0;61 }
(3)、在此示例代码中,同一类中的成员函数walk()与walk(int walkWay)形成重载。
(4)、分别位于基类Animal与派生类Human中的成员函数sleep()形成隐藏关系,即派生类Human的sleep()隐藏了基类的Animal中成员函数sleep()。
(5)、依据基类和派生类的兼容性原则,可以将派生类Human对象human的地址赋值给基类Animal的指针pAnimal。不过这时pAnimal指向的函数永远是基类的成员函数。
(6)、由于派生类Human的sleep()隐藏了基类Animal中的成员函数sleep(),所以pHuman->sleep()调用派生类Human的sleep()。
(7)、注意:基类指针永远无法调用该基类派生的同名函数。
2、多态性
(1)、虚函数:若类的成员函数用关键字virtual修饰,则称该函数为该类的虚函数,其声明语法如下:
virtual 返回值类型 函数名(形参列表);
例如:
class Animal
{
public:
virtual void sleep()
{
cout << "Animal sleep." << endl;
}
};
(2)、函数覆盖:若位于基类和派生类中的两个同名虚函数的声明完全相同(包括返回值类型),则这样的两个函数形成覆盖关系。
例如:
class Pet
{
virtual void sleep()
{
cout << "Pet sleep." << endl;
}
};
class Fish:public Pet
{
virtual viod sleep()
{
cout << "Fish sleep." << endl;
}
};
(3)、多态性实现步骤:
1)、在基类中说明成员函数为virtual函数,在直接派生类或间接派生类中根据实际应用重写这些virtual函数。
2)、在直接派生类或间接派生类中覆盖那些virtual函数,它们的声明必须和基类对应的虚函数声明严格一致。
3)、声明基类指针或者基类引用;给基类指针或者引用赋值,此时对象地址既可以是基类对象地址,也可以是派生类对象地址;有指针或者引用去调用相应的virtual函数,
根据对象的不同,真正被调用的函数可以是基类同名函数,也可以是派生类同名函数。
1 #include2 3 using namespace std; 4 5 class Animal 6 { 7 public: 8 virtual void walk() 9 {10 cout << "Animal walk." << endl;11 }12 void walk(int walkWay)13 {14 switch(walkWay)15 {16 case 0:17 cout << "Animal jump..." << endl;18 break;19 case 1:20 cout << "Animal fly..." << endl;21 break;22 default:23 cout << "Animal scraling..." << endl;24 }25 }26 virtual void sleep()27 {28 cout << "Animal sleeping..." << endl;29 }30 private:31 };32 33 class Human:public Animal34 {35 public:36 virtual void walk()37 {38 cout << "Human walk by feet." << endl;39 }40 virtual void sleep()41 {42 cout << "Human sleep, lying..." << endl;43 }44 virtual void talk()45 {46 cout << "Human talking..." << endl;47 }48 private:49 };50 int main()51 {52 Animal *pAnimalArray[] = {53 new Animal, new Human54 };55 for(int i = 0;i < 2;i++)56 {57 pAnimalArray[i]->walk();58 pAnimalArray[i]->sleep();59 delete pAnimalArray[i];60 }61 int c;62 cin >> c;63 return 0;64 }
(4)、注意:若在类中只声明虚函数原型,则在类外部定义虚函数时不必加virtual关键字;若派生类覆盖基类某个虚函数,则派生类中对应的虚函数可以加virtual也可以
加;一个虚函数,他在直接派生类和间接派生类中依然保持虚函数的特性;虚函数必须是成员函数;只有通过指针或引用方式访问虚函数,才能获得虚函数的动态特性。
3、virtual析构函数
(1)、通常,我们会才用基类指针或引用来指向派生类对象,此时,使用delete释放堆中的对象时,只会释放其基类子对象部分而不会自动释放派生类子对象部分。解决
办法是把基类和派生类的析构函数都声明为virtual类型。
1 #include2 3 using namespace std; 4 5 class Vehicle 6 { 7 public: 8 Vehicle(int power) 9 {10 this->power = power;11 }12 virtual ~Vehicle()13 {14 cout << "deleting Vehicle sub object..." << endl;15 }16 public:17 virtual void run()18 {19 cout << "Vehicle running..." << endl;20 }21 private:22 int power;23 };24 class Plane:public Vehicle25 {26 public:27 Plane(int power, double height):Vehicle(power)28 {29 this->height = height;30 }31 virtual ~Plane()32 {33 cout << "deleting Plane sub object..." << endl;34 }35 public:36 void run()37 {38 cout << "Plane fly..." << endl;39 }40 private:41 double height;42 };43 int main()44 {45 {46 Vehicle *pVehicle = new Plane(30, 3005);47 delete pVehicle;48 }49 int c;50 cin >> c;51 return 0;52 }
4、纯虚函数和抽象类
(1)、纯虚函数:类中的virtual成员函数,如果没有实现体(即定义实现体为0),则称这样的函数为纯虚函数。
例如:
virtual void add()=0;
(2)、抽象类:当一个类含有一个或者多个纯虚函数时,称这样的类为抽象类。
注意:抽象类的纯虚函数可以是我们后续加入的或者从基类继承的,当然如果一个类继承一个或多个纯虚函数之后再一一实现这些纯虚函数,那么这样的类不再是抽象类;
抽象类不能用于创建对象,但允许定义抽象对象指针。
1 #include2 3 using namespace std; 4 5 const double PI = 3.14159; 6 7 class Shape 8 { 9 public:10 virtual void show() = 0;11 virtual double area() = 0;12 virtual ~Shape()13 {}14 };15 class Shape2D:public Shape16 {17 public:18 Shape2D(double x, double y)19 {20 this->x = x;21 this->y = y;22 }23 protected:24 double x;25 double y;26 };27 class Shape3D:public Shape28 {29 public:30 31 protected:32 double x;33 double y;34 double z;35 };36 class Circle:public Shape2D37 {38 public:39 Circle(double x, double y, double radius):Shape2D(x, y)40 {41 this->radius = radius;42 }43 virtual void show()44 {45 cout << "Circle center: (" << x << ", " << y << ")" << endl;46 }47 virtual double area()48 {49 return PI*radius*radius;50 }51 private:52 double radius;53 };54 class Sphere:public Shape3D55 {56 public:57 Sphere(double x, double y, double z, double radius)58 {59 this->x = x;60 this->y = y;61 this->z = z;62 this->radius = radius;63 }64 void show()65 {66 cout << "Sphere center:(" << x << ", " << y << ", " << z << ")" << endl;67 }68 double area()69 {70 return PI*radius*radius*radius;71 }72 private:73 double radius;74 };75 int main()76 {77 {78 Shape *pShape = NULL;79 Shape *pShapeArray[] = {80 new Circle(1.0, 2.0, 3.0),81 new Sphere(1.0, 2.0, 3.0, 4.0)82 };83 for(int i = 0;i < 2;i++)84 {85 pShapeArray[i]->show();86 cout << "\t area = " << pShapeArray[i]->area() << endl;87 delete pShapeArray[i];88 }89 }90 int c;91 cin >> c;92 return 0;93 }