c++之虚函数与多态

来源:互联网 发布:js代码混淆 编辑:程序博客网 时间:2024/05/29 02:40

一、多态
多态性是指同样的消息被不同类型的对象接收时能导致完全不同的行为。
也可理解为同一个接口,使用不同的实例而执行不同的操作。
多态的实现:函数重载、运算符重载、模板、虚函数

静态绑定:(函数重载、运算符重载 、模板)
绑定过程出现在编译阶段,在编译阶段就已确定要调用的函数。
动态绑定:(虚函数)
绑定过程工作在程序运行时执行,在程序运行时才确定要调用的函数。

二、虚函数
虚函数的概念:在基类中冠以关键字 virtual 的成员函数
虚函数的定义:

virtual 函数类型 函数名称(参数列表);
如果一个函数在基类中被声明为虚函数,则他在所有派生类中都是虚函数
只有通过基类指针或引用调用虚函数才能引发动态绑定,(同时必须在基类中定义相同的接口,接口要定义为虚函数),包括通过基类指针的反引用调用虚函数,因为反引用一个指针将返回所指对象的引用。
虚函数不能声明为静态
(1)在基类中冠以关键字virtual的成员函数。虚函数不能声明为静态。
(2)如果一个函数在基类中被声明为虚函数,则它所有派生类中都是虚函数。
(3)基类的析构函数为虚函数,所有派生类的析构函数都是虚函数。
(4)构造函数不能是虚函数。

构造函数为什么不能是虚函数?
因为每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。当我们创建一个对象时,会调用构造函数来构造对象,如果构造函数是虚函数,必须通过虚函数表来找到虚构造函数,然而对象还未创建不存在虚函数表,因此构造函数不能是虚函数。

三、虚函数表指针(vptr)
虚函数的动态绑定是通过虚函数表(在静态数据区)来实现的。(虚函数表存放虚函数的函数指针)
包含虚函数的类对象头4个字节存放指向虚函数表的指针

注意:若不是虚函数,一般的函数不会出现在虚函数表,因为不用通过虚函数表指针间接去访问。

由于vptr在对象中的偏移不会随着派生层次的增加而改变,而且改写的虚函数在派生类vtable中的位置与它在基类vtable中的位置始终保持一致,有了这两条保证,再加上被改写虚函数与其基类中对应虚函数的原型和调用规范都保持一致,自然就能轻松地调用起实际所指对象的虚函数了。

四、虚析构函数
何时需要虚析构函数?
当你可能通过基类指针删除派生类对象时
如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的派生类对象是有重要的析构函数需要执行,就需要让基类的析构函数作为虚函数。

为什么基类析构函数要是虚析构函数?

c++基类采用virtual虚析构函数是为了防止内存泄露。如果在派生类中申请内存空间,并在析构函数中空间进行释放,当删除基类指针指向派生类对象时就会触发动态绑定,因而只会调用基类的析构函数。而不会调用派生类的析构函数。

#include <iostream>using namespace std;class Base{public:    virtual void Fun1()    {        cout << "Base::Fun1 ..." << endl;    }    virtual void Fun2()    {        cout << "Base::Fun2 ..." << endl;    }    void Fun3()    {        cout << "Base::Fun3 ..." << endl;    }    Base()    {        cout << "Base ..." << endl;    }    // 如果一个类要做为多态基类,要将析构函数定义成虚函数    virtual ~Base()    {        cout << "~Base ..." << endl;    }};class Derived : public Base{public:    /*virtual */    void Fun1()    {        cout << "Derived::Fun1 ..." << endl;    }    /*virtual */void Fun2()    {        cout << "Derived::Fun2 ..." << endl;    }    void Fun3()    {        cout << "Derived::Fun3 ..." << endl;    }    Derived()    {        cout << "Derived ..." << endl;    }    /*  virtual*/ ~Derived() //即使没有virtual修饰,也是虚函数    {        cout << "~Derived ..." << endl;    }};int main(void){    Base *p;    p = new Derived;    p->Fun1();    delete p; //通过基类指针删除派生类对象    return 0;}

+++++++++++++++++++++++++++++++++++++++
一、纯虚函数
虚函数是实现多态性的前提:

需要在基类中定义共同的接口
接口要定义为虚函数
如果基类的接口没办法实现怎么办?

如形状类Shape
解决方法:

将这些接口定义为纯虚函数

在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做
定义纯虚函数:
class 类名{
virtual 返回值类型 函数名(参数表) = 0;
};
纯虚函数不需要实现,纯虚函数不能实例化。

二、抽象类
作用

抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
注意

抽象类只能作为基类来使用。
不能声明抽象类的对象。
构造函数不能是虚函数,析构函数可以是虚函数

1、抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用
2、可使用指向抽象类的指针支持运行时多态性
3、派生类中必须实现基类中的纯虚函数,否则它仍将被看作一个抽象类
4、尽量将基类定义为抽象类

三、RTTI(runtime type information)

RTTI, RunTime Type Information, 运行时类型信息, 是多态的主要组成部分,

(1)dynamic_cast<>的作用
dynamic_cast<>可以检查类的类型(不同的派生类)//包含在头文件#include
dynamic_cast<>可以 使基类指针转换为派生类的指针, 通过判断指针的类型, 可以决定使用的函数.
(2)typeid();
typeid(), 可以判断类型信息, 判断指针指向位置, 在多态中, 可以判断基类还是派生类.
typeid().name();//得到类类型名。

0 0
原创粉丝点击