虚函数 订阅
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。 展开全文
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。
信息
关    键
用指向基类的指针或引用操作对象
形象解释
求同存异
定    义
被virtual关键字修饰的成员函数
作    用
实现多态性
中文名
虚函数
声    明
virtual
外文名
virtual function
虚函数定义
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。下面来看一段简单的代码。
收起全文
精华内容
下载资源
问答
  • 虚函数

    万次阅读 2018-07-21 12:37:58
    要成为虚函数必须满足两点,一就是这个函数依赖于对象调用,因为虚函数就是依赖于对象调用,因为虚函数是存在于虚函数表中,有一个虚函数指针指向这个虚表,所以要调用虚函数,必须通过虚函数指针,而虚函数指针是...

    拥有 Virtual 关键字的函数称之为虚函数

    虚函数的作用是实现动态绑定的,也就是说程序在运行的时候动态的的选择合适的成员函数

    那么,有哪些函数可以成为虚函数?那些函数不可以成为虚函数?

    要成为虚函数必须满足两点,一就是这个函数依赖于对象调用,因为虚函数就是依赖于对象调用,因为虚函数是存在于虚函数表中,有一个虚函数指针指向这个虚表,所以要调用虚函数,必须通过虚函数指针,而虚函数指针是存在于对象中的。二就是这个函数必须可以取地址,因为我们的虚函数表中存放的是虚函数函数入口地址,如果函数不能寻址,就不能成为虚函数。

    所以呢,这些函数不能成为虚函数;

    1.内联函数:我们都知道内联函数只是在函数调用点将其展开,它不能产生函数符号,所以不能往虚表中存放,自然就不能成为虚函数。

    2.静态函数:定义为静态函数的函数,这个函数只和类有关系,它不完全依赖于对象调用,所以也不能成为虚函数。

    3.构造函数:都知道只有当调用了构造函数,这个对象才能产生,如果把构造函数写成虚函数,这时候我们的对象就没有办法生    成。更别说用对象去调用了。所以构造函数不能成为虚函数。

    那些函数可以成为虚函数呢?

    普通的成员方法是可以成为虚函数的

    还有析构函数,因为析构函数是为了释放对象的,所以之前我们的对象已经生成,而且析构函数可以取地址,所以可以成为虚函数。

    那么,什么情况下,析构函数必须定义为虚函数。

    当我们定义了一个基类指针,然后在堆上new了一个派生类的对象,让这个指针指向堆上开辟的这块内存。

    Base *p = new Derive(10);
    delete p;

    如果基类的析构函数没有写成虚函数,delete这个基类指针,就不能释放掉堆上的派生类对象。因为delete p会调用基类的析构,你觉得调用基类的析构函数会释放掉派生类的对象吗?当然是不可能的。所以我们就要把基类的析构函数写成虚函数。写成虚函数后,当delete的时候,先会去基类调用析构函数,一看基类的析构函数是虚函数,就会自动的到派生类中调用派生类的析构函数。这时候派生类的对象就能释放了。

    展开全文
  • C++虚函数表剖析

    万次阅读 多人点赞 2016-02-18 20:28:23
    关键词:虚函数,虚表,虚表指针,动态绑定,多态一、概述为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。二、类的虚表每个...

    关键词:虚函数,虚表,虚表指针,动态绑定,多态

    一、概述

    为了实现 C++ 的多态,C++ 使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。

    二、类的虚表

    每个包含了虚函数的类都包含一个虚表。

    我们知道,当一个类(A)继承另一个类(B)时,类 A 会继承类 B 的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

    我们来看以下的代码。类 A 包含虚函数vfunc1vfunc2,由于类 A 包含虚函数,故类 A 拥有一个虚表。

    class A {
    public:
        virtual void vfunc1();
        virtual void vfunc2();
        void func1();
        void func2();
    private:
        int m_data1, m_data2;
    };
    

    类 A 的虚表如图 1 所示。

    在这里插入图片描述

    图 1:类 A 的虚表示意图

    虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。

    虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了。

    三、虚表指针

    虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。

    为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

    在这里插入图片描述

    图 2:对象与它的虚表

    上面指出,一个继承类的基类如果包含虚函数,那个这个继承类也有拥有自己的虚表,故这个继承类的对象也包含一个虚表指针,用来指向它的虚表。

    四、动态绑定

    说到这里,大家一定会好奇 C++ 是如何利用虚表和虚表指针来实现动态绑定的。我们先看下面的代码。

    class A {
    public:
        virtual void vfunc1();
        virtual void vfunc2();
        void func1();
        void func2();
    private:
        int m_data1, m_data2;
    };
    
    class B : public A {
    public:
        virtual void vfunc1();
        void func1();
    private:
        int m_data3;
    };
    
    class C: public B {
    public:
        virtual void vfunc2();
        void func2();
    private:
        int m_data1, m_data4;
    };
    

    类 A 是基类,类 B 继承类 A,类 C 又继承类 B。类 A,类 B,类 C,其对象模型如下图 3 所示。

    在这里插入图片描述

    图 3:类 A,类 B,类 C 的对象模型

    由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类 A 的虚表(A vtbl),类 B 的虚表(B vtbl),类 C 的虚表(C vtbl)。类 A,类 B,类 C 的对象都拥有一个虚表指针,*__vptr,用来指向自己所属类的虚表。

    类 A 包括两个虚函数,故 A vtbl 包含两个指针,分别指向A::vfunc1()A::vfunc2()
    类 B 继承于类 A,故类 B 可以调用类 A 的函数,但由于类 B 重写了B::vfunc1()函数,故 B vtbl 的两个指针分别指向B::vfunc1()A::vfunc2()
    类 C 继承于类 B,故类 C 可以调用类 B 的函数,但由于类 C 重写了C::vfunc2()函数,故 C vtbl 的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()

    虽然图 3 看起来有点复杂,但是只要抓住“对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数”这个特点,便可以快速将这几个类的对象模型在自己的脑海中描绘出来。

    非虚函数的调用不用经过虚表,故不需要虚表中的指针指向这些函数。

    假设我们定义一个类 B 的对象。由于 bObject是类 B 的一个对象,故bObject包含一个虚表指针,指向类 B 的虚表。

    int main() 
    {
        B bObject;
    }
    

    现在,我们声明一个类 A 的指针p来指向对象bObject。虽然p是基类的指针只能指向基类的部分,但是虚表指针亦属于基类部分,所以p可以访问到对象bObject的虚表指针。bObject的虚表指针指向类 B 的虚表,所以p可以访问到 B vtbl。如图 3 所示。

    int main() 
    {
        B bObject;
        A *p = & bObject;
    }
    

    当我们使用p来调用vfunc1()函数时,会发生什么现象?

    int main() 
    {
        B bObject;
        A *p = & bObject;
        p->vfunc1();
    }
    

    程序在执行p->vfunc1()时,会发现p是个指针,且调用的函数是虚函数,接下来便会进行以下的步骤。

    首先,根据虚表指针p->__vptr来访问对象bObject对应的虚表。虽然指针p是基类A*类型,但是*__vptr也是基类的一部分,所以可以通过p->__vptr可以访问到对象对应的虚表。

    然后,在虚表中查找所调用的函数对应的条目。由于虚表在编译阶段就可以构造出来了,所以可以根据所调用的函数定位到虚表中的对应条目。对于 p->vfunc1()的调用,B vtbl 的第一项即是vfunc1对应的条目。

    最后,根据虚表中找到的函数指针,调用函数。从图 3 可以看到,B vtbl 的第一项指向B::vfunc1(),所以 p->vfunc1()实质会调用B::vfunc1() 函数。

    如果p指向类 A 的对象,情况又是怎么样?

    int main() 
    {
        A aObject;
        A *p = &aObject;
        p->vfunc1();
    }
    

    aObject在创建时,它的虚表指针__vptr已设置为指向 A vtbl,这样p->__vptr就指向 A vtbl。vfunc1在 A vtbl 对应在条目指向了A::vfunc1()函数,所以 p->vfunc1()实质会调用A::vfunc1()函数。

    可以把以上三个调用函数的步骤用以下表达式来表示:

    (*(p->__vptr)[n])(p)
    

    可以看到,通过使用这些虚函数表,即使使用的是基类的指针来调用函数,也可以达到正确调用运行中实际对象的虚函数。

    我们把经过虚表调用虚函数的过程称为动态绑定,其表现出来的现象称为运行时多态。动态绑定区别于传统的函数调用,传统的函数调用我们称之为静态绑定,即函数的调用在编译阶段就可以确定下来了。

    那么,什么时候会执行函数的动态绑定?这需要符合以下三个条件。

    • 通过指针来调用函数
    • 指针 upcast 向上转型(继承类向基类的转换称为 upcast,关于什么是 upcast,可以参考本文的参考资料)
    • 调用的是虚函数

    如果一个函数调用符合以上三个条件,编译器就会把该函数调用编译成动态绑定,其函数的调用过程走的是上述通过虚表的机制。

    五、总结

    封装,继承,多态是面向对象设计的三个特征,而多态可以说是面向对象设计的关键。C++ 通过虚函数表,实现了虚函数与对象的动态绑定,从而构建了 C++ 面向对象程序设计的基石。

    参考资料

    • 《C++ Primer》第三版,中文版,潘爱民等译
    • http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
    • 侯捷《C++最佳编程实践》视频,极客班,2015
    • Upcasting and Downcasting, http://www.bogotobogo.com/cplusplus/upcasting_downcasting.php

    附:源代码

    https://github.com/haozlee/vtable/blob/master/main.cpp

    展开全文
  • 虚函数表详解

    万次阅读 多人点赞 2018-06-22 17:29:10
    本文转自:...这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。二、类的虚表每个包含了虚函数的类都包含一个虚表。我们知道,当一个类(A)继承另一个类(B)时...

    关键词:虚函数,虚表,虚表指针,动态绑定,多态

    一、概述

    为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。

    二、类的虚表

    每个包含了虚函数的类都包含一个虚表。 
    我们知道,当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

    我们来看以下的代码。类A包含虚函数vfunc1,vfunc2,由于类A包含虚函数,故类A拥有一个虚表。

    class A {
    public:
        virtual void vfunc1();
        virtual void vfunc2();
        void func1();
        void func2();
    private:
        int m_data1, m_data2;
    };
    •  

    类A的虚表如图1所示。 
    这里写图片描述 
    图1:类A的虚表示意图

    虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。 
    虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了。

    三、虚表指针

    虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。 
    为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

    这里写图片描述
    图2:对象与它的虚表

    上面指出,一个继承类的基类如果包含虚函数,那个这个继承类也有拥有自己的虚表,故这个继承类的对象也包含一个虚表指针,用来指向它的虚表。

    四、动态绑定

    说到这里,大家一定会好奇C++是如何利用虚表和虚表指针来实现动态绑定的。我们先看下面的代码。

    class A {
    public:
        virtual void vfunc1();
        virtual void vfunc2();
        void func1();
        void func2();
    private:
        int m_data1, m_data2;
    };
    
    class B : public A {
    public:
        virtual void vfunc1();
        void func1();
    private:
        int m_data3;
    };
    
    class C: public B {
    public:
        virtual void vfunc2();
        void func2();
    private:
        int m_data1, m_data4;
    };
    •  

    类A是基类,类B继承类A,类C又继承类B。类A,类B,类C,其对象模型如下图3所示。

    这里写图片描述
    图3:类A,类B,类C的对象模型

    由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类A的虚表(A vtbl),类B的虚表(B vtbl),类C的虚表(C vtbl)。类A,类B,类C的对象都拥有一个虚表指针,*__vptr,用来指向自己所属类的虚表。 
    类A包括两个虚函数,故A vtbl包含两个指针,分别指向A::vfunc1()和A::vfunc2()。 
    类B继承于类A,故类B可以调用类A的函数,但由于类B重写了B::vfunc1()函数,故B vtbl的两个指针分别指向B::vfunc1()和A::vfunc2()。 
    类C继承于类B,故类C可以调用类B的函数,但由于类C重写了C::vfunc2()函数,故C vtbl的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()。 
    虽然图3看起来有点复杂,但是只要抓住“对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数”这个特点,便可以快速将这几个类的对象模型在自己的脑海中描绘出来。

    非虚函数的调用不用经过虚表,故不需要虚表中的指针指向这些函数。

    假设我们定义一个类B的对象。由于bObject是类B的一个对象,故bObject包含一个虚表指针,指向类B的虚表。

    int main() 
    {
        B bObject;
    }
    • 现在,我们声明一个类A的指针p来指向对象bObject。虽然p是基类的指针只能指向基类的部分,但是虚表指针亦属于基类部分,所以p可以访问到对象bObject的虚表指针。bObject的虚表指针指向类B的虚表,所以p可以访问到B vtbl。如图3所示。
    int main() 
    {
        B bObject;
        A *p = & bObject;
    }
    • 当我们使用p来调用vfunc1()函数时,会发生什么现象?
    int main() 
    {
        B bObject;
        A *p = & bObject;
        p->vfunc1();
    }
    •  

    程序在执行p->vfunc1()时,会发现p是个指针,且调用的函数是虚函数,接下来便会进行以下的步骤。 
    首先,根据虚表指针p->__vptr来访问对象bObject对应的虚表。虽然指针p是基类A*类型,但是*__vptr也是基类的一部分,所以可以通过p->__vptr可以访问到对象对应的虚表。 
    然后,在虚表中查找所调用的函数对应的条目。由于虚表在编译阶段就可以构造出来了,所以可以根据所调用的函数定位到虚表中的对应条目。对于 p->vfunc1()的调用,B vtbl的第一项即是vfunc1对应的条目。 
    最后,根据虚表中找到的函数指针,调用函数。从图3可以看到,B vtbl的第一项指向B::vfunc1(),所以 p->vfunc1()实质会调用B::vfunc1()函数。

    如果p指向类A的对象,情况又是怎么样?

    int main() 
    {
        A aObject;
        A *p = &aObject;
        p->vfunc1();
    }
    •  

    当aObject在创建时,它的虚表指针__vptr已设置为指向A vtbl,这样p->__vptr就指向A vtbl。vfunc1在A vtbl对应在条目指向了A::vfunc1()函数,所以 p->vfunc1()实质会调用A::vfunc1()函数。

    可以把以上三个调用函数的步骤用以下表达式来表示:

    (*(p->__vptr)[n])(p)

    可以看到,通过使用这些虚函数表,即使使用的是基类的指针来调用函数,也可以达到正确调用运行中实际对象的虚函数。 
    我们把经过虚表调用虚函数的过程称为动态绑定,其表现出来的现象称为运行时多态。动态绑定区别于传统的函数调用,传统的函数调用我们称之为静态绑定,即函数的调用在编译阶段就可以确定下来了。

    那么,什么时候会执行函数的动态绑定?这需要符合以下三个条件。

    • 通过指针来调用函数
    • 指针upcast向上转型(继承类向基类的转换称为upcast,关于什么是upcast,可以参考本文的参考资料)
    • 调用的是虚函数

    如果一个函数调用符合以上三个条件,编译器就会把该函数调用编译成动态绑定,其函数的调用过程走的是上述通过虚表的机制。

    五、总结

    封装,继承,多态是面向对象设计的三个特征,而多态可以说是面向对象设计的关键。C++通过虚函数表,实现了虚函数与对象的动态绑定,从而构建了C++面向对象程序设计的基石。

    参考资料

    附录

    示例代码

     
    展开全文
  • 虚函数 虚函数

    千次阅读 2020-03-09 20:52:33
    虚函数是面向对象编程函数的一种特定形态,是C++用于实现多态的一种有效机制。C++的多态可以分为静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,而通过虚函数可以实现动态多态。实现函数的动态联...

    虚函数是面向对象编程函数的一种特定形态,是C++用于实现多态的一种有效机制。C++的多态可以分为静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,而通过虚函数可以实现动态多态。实现函数的动态联编其本质核心则是虚表指针与虚函数表。

    虚函数的本质就是通过基类访问派生类定义的函数。每一个含有虚函数的类,其实例对象内部都有一个虚函数表指针,该虚函数表指针被初始化为本类的虚函数表的内存地址。所以在程序中,不管对象类型如何转换,但该对象内部的虚函数表指针是固定的,这样才能实现动态地对对象函数进行调用,这就是C++多态性的原理。

    使用虚函数注意事项:

    • 只需在声明函数的类体中使用关键字virtual将函数声明为虚函数,在定义函数时不需要。
    • 将基类中某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。
    • 如果类(基类和派生类)中声明了某成员函数为虚函数,则类中不能再出现与之相同的非虚函数。

    当父类有实现名为虚函数fucnA ,而子类没有实现虚函数funcA,那么子类将继承该虚函数,该虚函数将存在子类的虚函数表中。

    子类继承父类,并且子类的虚函数覆盖父类的同名虚函数的时候,子类的虚函数表将存放着子类的虚函数及没有被覆盖的同名虚函数。

    虚函数工作的原理:

    编译器处理虚函数的方法是:给每个对象添加一个隐藏成员,隐藏成员中包含了一个指向函数地址数组的指针。这种数组称为虚函数表。虚函数表中存储了为类对象进行声明的虚函数地址。

    基类对象包含一个指针,该指针指向基类中所有函数的地址表。派生类对象将包含一个指向独立指针表的指针,如果派生类提供了函数的新定义,该函数表将保存新的函数的地址,如果派生类没有重新定义函数,则虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,该函数的地址也将被添加到虚函数表中。

    调用虚函数时,程序将查看存储在对象中的虚函数表的地址。然后转向相应的函数地址表。使用虚函数时,在内存和执行速度方面有一定的成本,包括:

    • 每个对象都将增大,增大量为存储地址的空间;
    • 对于每个类,编译器都创建一个虚函数地址表( 数组);
    • 对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址。

    虚函数,虚函数表里面内存如何分配?

    编译时若基类中有虚函数,编译器为该的类创建一个一维数组的虚表,存放是每个虚函数的地址。基类和派生类都包含虚函数时,这两个类都建立一个虚表。构造函数中进行虚表的创建和虚表指针的初始化。在构造子类对象时,要先调用父类的构造函数,初始化父类对象的虚表指针,该虚表指针指向父类的虚表。执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。每一个类都有虚表。虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。当用一个指针/引用调用一个函数的时候,被调用的函数是取决于这个指针/引用的类型。即如果这个指针/引用是基类对象的指针/引用就调用基类的方法;如果指针/引用是派生类对象的指针/引用就调用派生类的方法,当然如果派生类中没有此方法,就会向上到基类里面去寻找相应的方法。这些调用在编译阶段就确定了。当涉及到多态性的时候,采用了虚函数和动态绑定,此时的调用就不会在编译时候确定而是在运行时确定。不在单独考虑指针/引用的类型而是看指针/引用的对象的类型来判断函数的调用,根据对象中虚指针指向的虚表中的函数的地址来确定调用哪个函数。

    纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。纯虚函数是虚函数再加上= 0。virtual void fun ()=0。当类中声明有纯虚函数时,则不能创建该类的对象。

    虚函数的使用时注意:

    1.构造函数不能是虚函数??

    虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。

    2.析构函数

    析构函数应当是虚函数,除非类不用做基类。例如;假设Employee是基类,Singer 是派生类,并添加一个char *成员,该成员指向由new分配的内存。当Singer对象过期时,必须调用~Singer( )析构函数来释放内存。看下面的代码:

    Employee *pe=new Singer ;

    delete pe;

    如果使用默认的静态联编,delete 语句将调用~ Employee( )析构函数。这将释放由Singer对象中的Employee部分指向的内存,但不会释放新的类成员指向的内存。但如果析构函数是虚的,则上述代码将先调用~ Singer析构函数释放由Singer组件指向的内存,然后,调用~Employee( )析构函数来释放由Employee组件指向的内存。
    这意味着,即使基类不需要显式析构函数提供服务,也不应依赖于默认构造函数,而应提供虚析构函数,即使它不执行任何操作:
    virtual ~BaseClass() 
    顺便说一句,给类定义一个虚析构函数并非错误,即使这个类不用做基类;这只是一个效率方面的问题。

    3.友元函数能不能是虚函数??

    友元不能是虚函数,因为友元不是类的成员,而只有成员才能是虚函数。

     


     

    展开全文
  • C++虚函数虚函数表原理

    万次阅读 多人点赞 2018-07-26 19:49:54
    虚函数的地址存放于虚函数表之中。运行期多态就是通过虚函数虚函数表实现的。 类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数虚函数的调用会被编译器转换为对虚函数表的访问: ptr-...
  • c++虚函数详解(你肯定懂了)

    万次阅读 多人点赞 2018-08-01 11:02:13
    转自:c++虚函数 大牛的文章,就是通俗易懂,言简意赅。 前言 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数...
  • 多态的实现,是基于虚函数的继承,虚函数则是基于虚函数表和虚函数表指针。用一个示例介绍虚函数表指针访问虚函数,更易于方便理解虚函数,程序中也有注释// virtual-Table.cpp #include <iostream> ...
  • 要成为虚函数必须满足两点,一就是这个函数依赖于对象调用,因为虚函数就是依赖于对象调用,因为虚函数是存在于虚函数表中,有一个虚函数指针指向这个虚表,所以要调用虚函数,必须通过虚函数指针,而虚函数指针是...
  • C++虚函数详解

    万次阅读 多人点赞 2019-04-08 19:31:35
    C++虚函数详解 前言 C++的特性使得我们可以使用函数继承的方法快速实现开发,而为了满足多态与泛型编程这一性质,C++允许用户使用虚函数**(virtual function)来完成运行时决议这一操作,这与一般的编译时决定**...
  • 虚函数和纯虚函数的区别

    万次阅读 多人点赞 2012-05-11 22:35:51
    定义一个函数为虚函数,不代表函数为不被实现的函数。 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 定义一个函数为纯虚函数,才代表函数没有被实现。 定义纯虚函数是为了实现一个接口,起到一个...
  • 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。 C++默认的***析构函数不是虚函数是因为虚函数需要...
  • 虚函数 2 之虚函数的定义

    千次阅读 2019-01-24 23:20:34
    1、虚函数的定义 虚函数就是在基类中被关键字 virtual 说明,并在派生类中重新定义的函数。 虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,593
精华内容 15,837
关键字:

虚函数