多态性、虚拟函数、虚拟析构器、纯虚数、抽象类
多态性:1.静态多态性:编译时系统确定调用的是哪一个函数,称为编译时多态性(静态多态性);
2.动态多态性:程序运行时动态地确定调用的是哪一个函数,称为运行时多态性(动态多态性);
多态:是指同一个函数同时具有多种实现形式;
同一函数:相同函数名的函数;
“静”和”动“则体现在实现形式上;
实现形式:静态多态性:函数重载实现;
动态多态性:虚函数实现;
虚函数:也就是带有virtual关键字的成员函数(且只能是成员函数),带了这个关键字,在将派生类地址赋给基类指针时才可通过指针指向派生类的成员函数和数据成员 而这是为何呢?就是因为virtual(此句中的基类指针也可为基类引用);
注:一旦声明为虚函数,继承该基类的子类的同名函数无论是否声明为虚函数都是虚函数;
virtual作用原理:有virtual的函数称为虚函数,有虚函数的类就有虚函数表,虚函数表是包含了该类的虚函数地址的表格,表格中也包含了虚函数表的地址,自然,类的 实例化对象也就有了指向虚函数表的指针,通过这个指针就能够找到并且调用虚函数了,这就是为何要声明为虚函数的原因了;
注:将派生类对象地址赋给一个指向过基类对象的指针时,基类对象的全部内容会消失,包括虚函数表;
例1:
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 5 class Shape 6 { 7 public: 8 virtual double Area() 9 { 10 return 0; 11 } 12 }; 13 class Circle : public Shape 14 { 15 public: 16 Circle(double radius=100):radius(radius){} 17 virtual double Area() 18 { 19 return (PI * radius * radius); 20 } 21 private: 22 double radius; 23 }; 24 int main() 25 { 26 Shape a; 27 Circle b(10); 28 Shape *p = NULL; 29 30 p = &a; 31 cout << "The shape area is:" << p->Area() << endl; 32 33 p = &b; 34 cout << "The Circle area is:" << p->Area() << endl; 35 36 return 0; 37 }
例2:
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 5 class Shape 6 { 7 public: 8 double Area() 9 { 10 return 0; 11 } 12 }; 13 class Circle : public Shape 14 { 15 public: 16 Circle(double radius=100):radius(radius){} 17 double Area() 18 { 19 return (PI * radius * radius); 20 } 21 private: 22 double radius; 23 }; 24 int main() 25 { 26 Shape a; 27 Circle b(10); 28 Shape *p = NULL; 29 30 p = &a; 31 cout << "The shape area is:" << p->Area() << endl; 32 33 p = &b; 34 cout << "The Circle area is:" << p->Area() << endl; 35 36 return 0; 37 }
根据例1和例2的函数Area的有无加virtual就可看出virtual的作用了。
虚析构函数:把基类的析构函数声明为虚函数;
为何:通过基类的指针删除派生类对象时,只调用基类的析构函数,原本是该先调用派生类的析构对象,再调用基类析构函数,而将基类的析构函数声明为虚函数之后 就会按原本的调用顺序调用了;
注:一旦基类的析构函数声明为虚析构函数,继承该基类的派生类的析构函数是否声明为虚析构函数,都是虚析构函数;
注:纯虚构函数需要提供函数的实现,也就是加大括号,大括号内可有内容
例3
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 5 class Shape 6 { 7 public: 8 double Area() 9 { 10 return 0; 11 } 12 virtual ~Shape() 13 { 14 cout << "executing Shape destructor" <<endl; 15 } 16 }; 17 class Circle : public Shape 18 { 19 public: 20 Circle(double radius=100):radius(radius){} 21 double Area() 22 { 23 return (PI * radius * radius); 24 } 25 virtual ~Circle() 26 { 27 cout << "executing Circle destructor" << endl; 28 } 29 private: 30 double radius; 31 }; 32 int main() 33 { 34 Shape *p = NULL; 35 p = new Circle; 36 delete p; 37 38 return 0; 39 }
例4
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 5 class Shape 6 { 7 public: 8 double Area() 9 { 10 return 0; 11 } 12 ~Shape() 13 { 14 cout << "executing Shape destructor" <<endl; 15 } 16 }; 17 class Circle : public Shape 18 { 19 public: 20 Circle(double radius=100):radius(radius){} 21 double Area() 22 { 23 return (PI * radius * radius); 24 } 25 ~Circle() 26 { 27 cout << "executing Circle destructor" << endl; 28 } 29 private: 30 double radius; 31 }; 32 int main() 33 { 34 Shape *p = NULL; 35 p = new Circle; 36 delete p; 37 38 return 0; 39 }
根据例3和例4的~Shape(),~Circle();是否为虚析构函数可看出virtual的作用,还可看出派生类的虚析构函数会自动调用基类的虚析构函数
纯虚数:没有函数体的虚函数,其实,基类的成员函数只是作为一种连接不同派生类的端口,基类成员函数的内容并不重要,在实际的程序设计中,也这么处理的
纯虚数性质:(1)纯虚数在基类声明,在派生类实现
(2)纯虚数在基类中只有函数的名称,因此不能被调用
(3)在派生类中对纯虚数定义货,才具备函数的功能,才可被调用
(4)纯虚数具有继承性
注:基类的函数声明为纯虚数,继承基类的子类若没有重新定义,也为纯虚数,当然,一般都是会重新定义的,不然这个函数就没意义了
例5
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 5 class Shape 6 { 7 public: 8 virtual double Area() = 0; 9 virtual ~Shape() 10 { 11 cout << "executing Shape destructor" <<endl; 12 } 13 }; 14 class Circle : public Shape 15 { 16 public: 17 Circle(double radius=100):radius(radius){} 18 virtual double Area() 19 { 20 return (PI * radius * radius); 21 } 22 virtual ~Circle() 23 { 24 cout << "executing Circle destructor" << endl; 25 } 26 private: 27 double radius; 28 }; 29 int main() 30 { 31 Circle b(10); 32 Shape *p = NULL; 33 34 p = &b; 35 cout << p->Area() << endl; 36 37 return 0; 38 }
可以看到我把例5的Shape类的Area函数声明为虚函数了,方便且无产生任何不良影响
抽象类:含有纯虚函数的类
抽象类作用:作为激烈使用,提供对外接口函数
抽象类性质:(1)抽象类不能定义对象
(2)抽象类可以定义指针变量(不然就无意义了)
(3)抽象类的构造函数:若抽象类中没有数据成员,可以不写;否则,需要写构造函数和析构函数
(4)抽象类的纯虚构函数:可用于没有其他纯虚函数的场景(因为构造函数不可声明为虚函数,也就没有构造函数的纯虚数了,就只剩下纯虚构函数了),
但是一般不这样做
例6
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 class Shape 5 { 6 public: 7 virtual double Area() = 0; 8 virtual double Perimeter() = 0; 9 }; 10 class Circle:public Shape 11 { 12 public: 13 Circle(double radius = 0):radius(radius){} 14 virtual double Area(); 15 virtual double Perimeter(); 16 private: 17 double radius; 18 }; 19 double Circle::Area() 20 { 21 return (PI * radius * radius); 22 } 23 double Circle::Perimeter() 24 { 25 return (2 *PI * radius); 26 } 27 class Rectangle:public Shape 28 { 29 public: 30 Rectangle(double length = 0,double width = 0):length(length),width(width){} 31 virtual double Area(); 32 virtual double Perimeter(); 33 private: 34 double length; 35 double width; 36 }; 37 double Rectangle::Area() 38 { 39 return (length*width); 40 } 41 double Rectangle::Perimeter() 42 { 43 return (2 * length * width); 44 } 45 int main() 46 { 47 Shape *p; 48 Circle a(100); 49 p = &a; 50 cout <<"The circle area is:" << p->Area() << endl; 51 cout <<"The circle Perimeter is:" << p->Perimeter() <<endl; 52 Rectangle b(100,200); 53 p = &b; 54 cout <<"The rectangle area is:" << p->Area() << endl; 55 cout << "The rectangle Perimeter is:" << p->Perimeter() <<endl; 56 return 0; 57 }
注:抽象类无构造函数,因此继承的子类中的构造函数不需要重载基类的构造函数
根据例6可得到(1)抽象类的纯虚数是提供给外界的端口
(2)当类为抽象类是使用更简便
例7
1 #include <iostream> 2 using namespace std; 3 const double PI = 3.1415926; 4 class Shape 5 { 6 public: 7 virtual double Area() = 0; 8 virtual double Perimeter() = 0; 9 }; 10 class Circle:public Shape 11 { 12 public: 13 Circle(double radius = 0):radius(radius){} 14 virtual double Area(); 15 virtual double Perimeter(); 16 private: 17 double radius; 18 }; 19 double Circle::Area() 20 { 21 return (PI * radius * radius); 22 } 23 double Circle::Perimeter() 24 { 25 return (2 *PI * radius); 26 } 27 class Rectangle:public Shape 28 { 29 public: 30 Rectangle(double length = 0,double width = 0):length(length),width(width){} 31 virtual double Area(); 32 virtual double Perimeter(); 33 private: 34 double length; 35 double width; 36 }; 37 double Rectangle::Area() 38 { 39 return (length * width); 40 } 41 double Rectangle::Perimeter() 42 { 43 return (2 * length * width); 44 } 45 int main() 46 { 47 Circle a(100); 48 Shape &p=a; 49 50 51 cout <<"The circle area is:" << p.Area() << endl; 52 cout <<"The circle Perimeter is:" << p.Perimeter() <<endl; 53 Rectangle b(100,200); 54 p = b; 55 cout <<"The rectangle area is:" << p.Area() << endl; 56 cout << "The rectangle Perimeter is:" << p.Perimeter() <<endl; 57 return 0; 58 }
根据例7可看出:基类指针+虚函数和基类引用+虚函数是在使用上出现的效果是一样(原理上有一点区别,引用是别名,这一点在这里就不细说了)
END
上一篇: 复数的概念和代数运算
下一篇: 每日打卡虚函数