C++ 多态性
多态性
面向对象的多态性可被分为四类:重载多态,强制多态,包含多态和参数多态。
多态的实现可分为两种:
①编译时的多态:在编译的过程中确定了同名操作的具体操作对象。
②运行时的多态:在程序运行的过程中才动态的确定操作所针对的具体对象。
确定操作的具体对象的过程就是绑定,也就是把一条消息和一个对象的方法相结合的过程。绑定包括动态绑定和静态绑定。绑定工作在编译连接阶段完成的情况为静态绑定(也称为早期绑定或前绑定);绑定工作在程序运行阶段完成的情况称为动态绑定(也称为晚期绑定或后绑定)。包含多态操作对象的确定就是通过动态绑定完成的。
包含多态是类族中定义于不同类中的同名函数的多态行为,主要是通过虚函数实现的。
虚函数
虚函数是动态绑定的基础,虚函数必须为非静态的成员函数,虚函数经过派生之后,在类族中就可以实现运行过程中的多态。
#include<iostream>
using namespace std;
class Base1 {
public:
void display() const{
cout << "Base1::display()" << endl;
}
};
class Base2 :public Base1 {
public:
void display() const{
cout << "Base2::display()" << endl;
}
};
class Derived :public Base2 {
public:
void display()const {
cout << "Derived::display()" << endl;
}
};
void fun(Base1* ptr) {
ptr->display();
}
int main() {
Base1 base1;
Base2 base2;
Derived derived;
fun(&base1);
fun(&base2);
fun(&derived);
return 0;
}
运行结果为:
从运行结果看来无论实参类型为base2还是derived,fun函数中调用的还是Base1中的display()函数。这一问题如何解决呢?这就要应用虚函数来实现多态性。
#include<iostream>
using namespace std;
class Base1 {
public:
virtual void display() const{
cout << "Base1::display()" << endl;
}
};
class Base2 :public Base1 {
public:
void display() const{
cout << "Base2::display()" << endl;
}
};
class Derived :public Base2 {
public:
void display()const {
cout << "Derived::display()" << endl;
}
};
void fun(Base1* ptr) {
ptr->display();
}
int main() {
Base1 base1;
Base2 base2;
Derived derived;
fun(&base1);
fun(&base2);
fun(&derived);
return 0;
}
运行结果为:
根据赋值兼容性规则,可以使用派生类对象代替基类对象。如果用基类类型的指针指向派生类对象,就可以通过这个指针来访问对象,问题是访问到的只是从基类继承来的同名成员。解决这个问题的办法是:如果需要通过基类的指针指向派生类的对象,并访问某个与基类同名的成员,那么首先在基类中将这个同名函数声明为虚函数。这样,通过基类指针就可以使属于不同派生类的不同对象产生不同的行为,从而实现运行过程中的多态。
一般虚函数成员的声明为:
virtual 函数类型 函数名(参数列表)
注意:虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
运行过程中的多态需要满足三个条件:
一、类之间满足赋值兼容规则
二、要声明虚函数
三、要由成员函数来调用或者是通过指针、引用来访问虚函数。
如果派生类需要重写与基类函数同名的函数,就应该中基类中将此函数声明为虚函数,而基类中声明的非虚函数通常代表那些不需要被派生类改变的功能,也是不能实现多态的,因此一般不要重写继承而来的非虚函数,因为那会导致通过基类指针和派生类指针或对象调用同名函数时产生不同的结果从而引起混乱。
纯虚函数与抽象类
抽象类是一种特殊的类,建立抽象类就是为了通过它多态的使用其中的成员函数。一个抽象类无法实例化,只能通过继承机制生成抽象类的非抽象派生类,然后再实例化。
纯虚函数是中一个基类中声明的虚函数,它在该基类中没有定义具体的操作内容,一般声明格式为:
vitual 函数类型 函数名(形参表)=0;
带有纯虚函数的类就是抽象类,抽象类声明类一个类族派生类的共同接口,而接口的完整实现,要由派生类自己定义。
#include<iostream>
using namespace std;
#define pi 3.14
class Shape {
public:
virtual float getArea() = 0;
virtual float getPerim() = 0;
};
class Circle :public Shape {
private:
float r;
public:
Circle(float r) { this->r = r; }
float getArea() {
return (float)pi * r * r;
}
float getPerim() {
return (float)pi * 2 * r;
}
void show() {
cout << "Circle:" << endl;
cout << "r=" << r << endl;
}
};
class Rectangle :public Shape {
private:
float width, height;
public:
Rectangle(float w, float h) {
width = w;
height = h;
}
float getArea() {
return width * height;
}
float getPerim() {
return 2 * (width + height);
}
void show() {
cout << "Rectangle:" << endl;
cout << "w=" << width << " h=" << height << endl;
}
};
int main() {
Circle circle(2);
Rectangle rectangle(3, 4);
circle.show();
cout << "area = " << circle.getArea()
<< " perim = " << circle.getPerim() << endl << endl;
rectangle.show();
cout << "area = " << rectangle.getArea()
<< " perim = " << rectangle.getPerim() << endl << endl;
return 0;
}