C++(四十一) — 多态、虚函数、虚析构函数、纯虚函数

 一、多态ios

     面向对象程序设计中,多态性表现为:安全

  (1)重载多态:函数重载、运算符重载;ide

  (2)运行多态:经过基类的指针(或引用)调用不一样派生类的同名函数,表现出不一样的行为;函数

  (3)模板多态:参数多态,经过一个模板获得不一样的函数或不一样的类,具备不一样的特性和不一样的行为;测试

二、同名覆盖与重载编码

 (1)override(同名覆盖)spa

  在类的继承中才会出现,多个函数的原型是相同的。设计

 (2)overload(重载)指针

   在同一做用域范围内,由参数个数或类型不一样的多个同名函数构成。code

三、虚函数

   缘由:经过指针调用成员函数时,只能访问到基类的同名成员函数。在同名覆盖现象中,经过某个类的对象(指针及引用)调用同名函数,编译器会将该调用静态联编到该类的同名函数,也就是说,经过基类对象指针是没法访问派生类的同名函数的,即便这个指针是用派生类对象来初始化的。

  虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是经过基类访问派生类定义的函数。

  虚函数给基类指针访问派生类同名函数的一个机会

  指向基类的指针在操做它的多态类对象时,会根据不一样的类对象,调用其相应的函数,这个函数就是虚函数。

  能够说,基类声明的虚函数,在派生类中也是虚函数,即便再也不使用virtual关键字。

  虚函数实现多态的原理:

  1. 当类中声明虚函数时,编译器会在类中生成一个虚函数表,用于存储类成员函数的指针,由编译器自动生成和维护;
  2. 存在虚函数时,每一个对象都有一个指向虚函数表的指针(对于派生类的虚函数表,先放父类,后方子类,函数覆盖时就用子类的同名函数代替父类的);
  3. 编译器肯定是否为虚函数,如是则根据对象的指针,找到所指虚函数表查找函数并调用。

  动态联编:一个类函数的调用并非在编译时刻被肯定的,而是在运行时刻被肯定的。因为编写代码的时候并不能肯定被调用的是基类的函数仍是哪一个派生类的函数,因此被成为“虚”函数。

   虚函数只能借助于指针或者引用来达到多态的效果。但会为程序引入较大的开销,实际中应尽可能避免。

  注意:

  虚函数在基类中声明,构造函数、静态成员函数不能够为虚函数,但析构函数能够

  使用角度:虚函数经过父类指针调用子类的成员函数,而构造函数在建立时自动调用,无需父类指针;

  存储角度:虚函数对应一个指向虚函数表的指针,虚函数表经过构造函数初始化,若构造函数为虚,则须要经过虚函数表来找到虚构造函数的入口地址,而此时无虚函数表,因此构造函数不能为虚函数。

四、虚析构函数

   只有虚析构函数,没有虚构造函数。

  建立派生类对象时,调用基类构造->派生类构造->派生类析构->基类析构。

  若是用new运算符动态建立派生类对象,并以此对象地址初始化基类指针,构造没问题,但用delete运算符删除派生类对象时,因为指针是指向基类的,经过静态联编,调用基类析构函数,不调用派生类析构函数,使得派生类没法执行某些清理工做,例如:派生类中申请的内存没机会还给系统。

  虚析构函数:基类设置虚析构函数,派生类都是。此时使用基类对象指针销毁派生类对象时,会经过动态联编调用派生类析构函数,完成派生类的清理工做。

五、纯虚函数

  以下声明表示一个函数为纯虚函数:

class A
{
public:
    virtual void foo()=0;   // =0 标志一个虚函数为纯虚函数,没有函数体,不可实例化,不可被调用。
};

  一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,个人派生类都会有这个函数。

  纯虚函数的引入,是出于两个目的:
  一、为了安全,由于避免任何须要明确可是由于不当心而致使的未知的结果,提醒子类去作应作的实现。其实是限制了派生类的功能,规范接口,把实现留给子类。
  二、为了效率,不是程序执行的效率,而是为了编码的效率。

 六、运行时多态的应用实例

 (1)头文件 shape.h

#pragma once

//#ifdef SHAPE_H
 #include <iostream>
using namespace std; class Shape { public: virtual double getArea() const = 0;//纯虚函数
    void print() const; virtual ~Shape() {}                //虚析构函数
}; class Circle :public Shape { public: Circle(int xv= 0, int yv= 0, double rv= 0.0); double getArea() const; void print() const; protected: int x, y; double r; }; class Rectangle :public Shape { public: Rectangle(int av= 0, int bv = 0); double getArea() const; void print() const; protected: int a, b; }; //#endif // DEBUG

 

(2)函数定义 shape.c

#include <iostream>
using namespace std; #include "shape.h"

void Shape::print() const { cout << "Base class Object" << endl; } Circle::Circle(int xv, int yv, double rv) { x = xv; y = yv; r = rv; } double Circle::getArea() const { return 3.14*r*r; } void Circle::print() const { cout << "center is" << x << "  " << y << endl; } Rectangle::Rectangle(int av, int bv) { a = av; b = bv; } double Rectangle::getArea() const { return a*b; } void Rectangle::print() const { cout << "h is " << a << "  " << b << endl; }

 

三、测试文件

#include <iostream>
using namespace std; #include "shape.h"

void creat_object(Shape **ptr); void display_area(Shape *ptr); void delete_object(Shape *ptr); void main() { Shape *shape_ptr; creat_object(&shape_ptr); display_area(shape_ptr); delete_object(shape_ptr); system("pause"); } void creat_object(Shape **ptr) { char type; *ptr = nullptr; do { cout << "建立对象:" << endl; cin >> type; switch (type) { case 'c': { int xx, yy; double rr; cout << "请输入圆心及半径:"; cin >> xx >> yy >> rr; *ptr = new Circle(xx, yy, rr); break; } case 'r': { int aa, bb; cout << "请输入矩形的长和宽:"; cin >> aa >> bb; *ptr = new Rectangle(aa,bb); break; } default:cout << "请从新选择\n" << endl; } } while (*ptr == nullptr); } void display_area(Shape *ptr) { cout << ptr->getArea() << endl; } void delete_object(Shape *ptr) { delete(ptr); }
相关文章
相关标签/搜索