1. 多态的两种机制
1.1机制1
派生类的指针可以赋给基类的指针
通过基类的指针调用基类和派生类中的同名虚函数(一种特殊的成员函数)时:
(1):若该指针指向一个基类的对象,那么就调用基类的虚函数
(2):若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数
1.2机制2
派生类的对象可以赋给基类的引用
通过该基类的引用调用基类和派生类的同名虚函数时:
(1):若该引用引用的是一个基类的对象,那么调用的是基类的虚函数
(2):如果引用的是一个派生类的对象,那么调用的是派生类的虚函数
1.3实例
#include <iostream>
using namespace std;
class Shape{
public:
Shape() {
cout << "constructor function:" << endl;
cout << "excute Shape()" << endl;
}
virtual void print() { cout << "excute virtual print() at class Shape" << endl; }
};
class Point :public Shape {
public:
Point() {
cout << "constructor function:" << endl;
cout << "excute Point()" << endl;
}
virtual void print() { cout << "excute virtual print() at class Point" << endl; }
};
class Circle :public Point {
public:
Circle() {
cout << "constructor function:" << endl;
cout << "excute Circle()" << endl;
}
virtual void print() { cout << "excute virtual print() at class Circle" << endl; }
};
int main(){
Shape shape1;
Point point1;
Circle circle1;
Shape *ptshape1=&shape1;
Point *ptpoint1 = &point1;
Circle *ptcircle1 = &circle1;
ptshape1->print();//调用shape1.print()
ptshape1 = ptpoint1;//派生类指针赋给基类指针
ptshape1->print();//调用point1.print()
ptshape1 = ptcircle1;//派生类指针赋给基类指针
ptshape1->print();//调用circle1.print()
return 0;
}
2.虚函数与动态联编
2.1虚函数
虚函数是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。
2.2动态联编
一条函数调用语句在编译时无法确定调用哪个函数,运行到该语句时才确定调用哪个函数,这种机制称之为动态联编(或动态关联)
2.3实例
#include <iostream>
using namespace std;
class Shape {
public:
virtual float area() const { return 0.0; } //虚函数
virtual float volume() const { return 0.0; }//虚函数
virtual void shapeName() const = 0;//纯虚函数
};
class Point :public Shape {
protected:
float x, y;
public:
Point(float = 0, float = 0);//声明构造函数
void SetPoint(float, float);
float getX() const { return x; }
float getY() const { return y; }
virtual void shapeName() const { cout << "Point: "; }//重新定义虚函数 virtual可有可无
//函数声明的const,表示该函数不会修改该类的任何成员数据的值,称为常量成员函数
friend ostream & operator <<(ostream &, const Point &);//运算符重载
};
Point::Point(float a, float b) {
x = a;
y = b;
}
void Point::SetPoint(float a, float b) {
x = a;
y = b;
}
ostream & operator<<(ostream &output, const Point &p) {
output << "[" << p.x << "," << p.y << "]";
return output;
}
class Circle :public Point {
public:
Circle(float x = 0, float y = 0, float r = 0);//声明构造函数
void setRadius(float); //set R
float getRadius() const;//get the value of Radius
virtual float area() const; //对虚函数进行再定义
virtual void shapeName() const { cout << "Circle: "; }//对虚函数进行再定义
protected:
float radius;
};
//定义Circle类的函数
Circle::Circle(float a, float b, float r) :Point(a, b) { radius = r; } //定义构造函数
void Circle::setRadius(float r) { radius = r; }
float Circle::getRadius() const {
return radius;
}
float Circle::area() const {
return 3.14*radius*radius;
}
ostream &operator<<(ostream &output, const Circle &c) {
output << "[" << c.getX() << "," << c.getY() << "],r=" << c.getRadius();
return output;
}
void myfunc(Shape *pt) {
pt->shapeName();
//编译时无法确定调用的是Shape.shapeName()、Point.shapeName()还是Circle.shapeName()
//因为编译时无法确定形参会对应哪个类的对象
}
int main()
{
Point point(2.1, 3.4); //new Point类对象
Circle circle(2.3, 4.5, 5.0); //new Circle类对象
point.shapeName(); //用对象名建立静态关联
cout << point << endl;//输出点数据
circle.shapeName(); 用对象名建立静态关联
cout << circle << endl;//输出圆数据
Shape *pshape;//定义基类指针
pshape = &point; //指向Point类的对象point
cout << "\n\n" << endl;
pshape->shapeName(); //建立动态关联
myfunc(pshape);
cout << "x=" << point.getX() << " , y=" << point.getY() << "\n area=" << pshape->area()
<< "\nvolume=" << pshape->volume() << "\n\n"; //output information about point
pshape = &circle; //指向Circle类的对象circle
cout << "\n\n" << endl;
pshape->shapeName(); //建立动态关联
myfunc(pshape);
cout << "x=" << circle.getX() << " , y=" << circle.getY() << ",r=" << circle.getRadius() << "\n area=" << pshape->area()
<< "\nvolume=" << pshape->volume() << "\n\n"; //output information about point
cout << "hello word" << endl;
return 0;
}
运行结果:
3.多态的作用
多态的本质
(1):父类定义共同接口,子类不同实现
(2):通过父类以相同的方式操作不同子类的行为
在面向对象的程序设计中使用多态
(1):能够增强程序的可扩充性
(2):程序需要修改或增加功能的时候,需要改动或增加的代码较少
4.覆盖与虚函数访问机制对比
5.多态的实现:虚函数表
(I):每一个有虚函数的类(或有虚函数的类的派生类都有一个虚函数表,该类的任何对象中都存放着虚函数表的指针。
(II):虚函数表中列出该类的虚函数地址。多出来的4个字节就是用来存放虚函数表的地址。
5.1实例
#include <iostream>
using namespace std;
class Shape {
public:
int a=9;
virtual void print() { cout << "excute virtual print() at class Shape" << endl; }
};
class Point :public Shape {
public:
int b = 10;
virtual void print() { cout << "excute virtual print() at class Point" << endl; }
};
int main() {
Shape shape1;
Point point1;
cout << "sizeof(int)=" << sizeof(int) << endl;
cout << "sizeof(Shape)=" << sizeof(Shape) << endl;
cout << "sizeof(Point)=" << sizeof(Point) << endl;
return 0;
}
6.构造函数与析构函数中调用虚函数
(I)在构造函数中和析构函数中调用虚函数时:他们调用的函数是自己类或基类(如果自己类中没有定义该虚函数)定义的函数,不会等到运行时才决定调用自己的还是派生类的同名虚函数。
(II)在普通成员函数中调用虚函数,则是动态联编,是多态
6.1实例
#include <iostream>
using namespace std;
class Shape {
public:
Shape() {
cout << "excute constructor function: Shape()" << endl;
}
virtual void print() { cout << "excute virtual Shape.print() at class Shape" << endl; }
};
class Point :public Shape {
public:
Point() {
cout << "excute constructor function: Point()" << endl;
print();
}
virtual void print() { cout << "excute virtual Point.print() at class Point" << endl; }
};
class Circle :public Point {
public:
Circle() {
cout << "excute constructor function: excute Circle()" << endl;
print();
}
virtual void print() { cout << "excute virtual Circle.print() at class Circle" << endl; }
};
int main() {
cout << "new Point class : point1" << endl << endl;
Point point1;
cout << endl << "new Circle class : circle1" << endl << endl;
Circle circle1;
Point *ptpoint1 = &point1;
ptpoint1->print();// excute point1.print();
ptpoint1 = &circle1;
ptpoint1->print();// excute circle1.print();
return 0;
}
运行结果: