栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

静态联编、动态联编以及虚函数的工作原理

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

静态联编、动态联编以及虚函数的工作原理

1.什么是静态联编和动态联编?

        程序调用函数时,要是用哪个可执行代码块呢?编译器负责完成这个问题,在编译阶段编译器会将函数调用解释为去到某个地址执行代码块,这称为函数名联编。

        在C++中,由于存在重载,编译器需要查看特征标来确认到底是用哪个函数,C++的编译器也可以在编译阶段完成这项工作,这称为静态联编。

        而虚函数的存在使得C++编译器无法在编译阶段完成这项工作,比如之前的例子中的基类指针数组,根据数组中具体存放的是基类对象还是派生类对象,才能确定调用哪个虚函数,这在编译阶段是不确定的,因此C++编译器只能在运行时选择正确的虚函数代码块执行,这称为动态联编。

2.虚函数的工作原理

        编译器处理虚函数的方法是:给每个对象添加一个隐藏成员,该隐藏成员中保存了一个指向函数地址数组的指针,该数组称为虚函数表(virtual function table,vtbl)。

        例如,基类对象包含一个指针,该指针指向基类中所有虚函数的地址表,即虚函数表;

        当派生类对象被创建时,也会有一个指针,指向的时派生类对象的虚函数表;

        若该派生类没有对基类中的虚函数进行重写,则派生类对象的虚函数表中,该函数的地址将与基类中的一致;

        若派生类有对基类虚函数重写,则派生类对象的虚函数表中,该函数的地址就与基类中的不一致;

        除此以外,若派生类自己新增了一个虚函数,该虚函数的地址会被增加到派生类对象的虚函数表中。

        在调用函数时,编译器首先会查看虚函数表,如果该函数在虚函数表中,则由地址进入到该虚函数的代码块处执行,由此保证调用的正确性。

        虚函数的使用会消耗一定的内存和降低执行速度,具体表现如下:
        1.每个对象都会增大,以用于存储虚函数表
        2.对于每个类,编译器都创建一个虚函数地址表
        3.对于每次函数调用,都需要执行额外操作,即查找虚函数表。
        4.虚函数数量的多少不会影响隐藏指针的大小(虚函数表本质是数组,隐藏指针只指向虚函数表的首位),只会增加虚函数表的长度

3.虚函数的几个使用要点

        3.1 构造函数不需要定义为虚函数。因为派生类的构造函数不可能与基类的构造函数同名,且在派生类中有可能定义了与基类同名的虚函数,如果把基类的构造函数定义成了虚函数,在调用的派生类虚函数的时候可能发生错误。

        3.2 在任何情况下都应该将析构函数定义为虚函数。

        3.3 友元函数不可能是虚函数。因为虚函数必须是成员函数。

        3.4 派生类处在派生链中的时候,将使用最新的虚函数版本。

        3.5 重新定义将隐藏方法,例子:

        class A
        {
            virtual void fun(int n);
        }
        class B
        {
            virtual void fun();
        }

        派生类B重写了A的虚函数fun,但是特征标不同,此时编译器可能会出现警告,且当出现以下调用时:

        B b;
        b.fun();//valid
        b.fun(5);//invalid

        即基类的虚函数被隐藏了,这里体现了继承与重载的不同之处。由这个要点可以引出两个经验规则:

如果重写虚函数,应保证原型完全相同,但返回类型可以不同。如果基类虚函数被重载了,应在派生类中重写所有基类虚函数重载版本。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/784001.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号