使用派生类对象通过成员函数指针调用基类虚函数之不可能性的证明

本贴最后更新于 2228 天前,其中的信息可能已经东海扬尘

希望大家没有被这么拗口的标题吓到:). 本文源于论坛上的这个问题:

struct base
{ 
	void foo() 
	{ 
		cout << "base::foo" << endl; 
	} 
	virtual void bar() 
	{ 
		cout << "base::bar" << endl; 
	} 
}; 
struct derived : base 
{ 
	virtual void bar() 
	{ 
		cout << "derived::bar" << endl; 
	} 
}; 
int __cdecl _tmain( int argc, _TCHAR* argv[] ) 
{ 
	void (base::*pfn)() = &base::bar; 
	derived d; 
	d.base::bar(); // 1 
	(d.base::*pfn)(); // 2 想实现和上一行一样的输出, 但是编译失败 
	return 0; 
}

很明显, 标 2 的那一行是想使用派生类对象通过成员函数指针调用基类虚函数, 以实现与标 1 的那行相同的输出, 但却无法编译通过. 这是个语法错误, 因为 :: 运算符的优先级高于 ., 所以那一行会先计算 base::*pfn, 然而 *pfn 并不是 base 的成员, 故有错误时很自然的. 那么是否可以通过修改那条语句来达到目的呢? 分析了成员函数指针的实现后, 我发现, 至少在 vc7.1 和 vc8 上, 这是不可能的. 由于 vc 的标准兼容性已经非常高,所以我怀疑 c++ 标准就不支持这种调用, 但没有证实.

在上面的例子中, pfn 指向的是虚函数 bar, 但它也必须能指向普通成员函数 foo. 当指向 foo 时, 它保存的就是 foo 的入口地址; 然而当指向 bar 时, 直接保存这个地址就不行了,因为对 basederived 来说, 这个地址并不相同. vc 对此的解决方法是由编译器加入了一系列的内部函数 vcall. 一个类中的每个虚函数都有一个唯一与之对应的 vcall 函数, 但在不同类之间, 这些 vcall 实际上是公用的. pfn 指向的就是这些 vcall 中的一个.

我们知道, 调用成员函数时要传递 this 指针. 一般情况下, 它是通过 ecx 寄存器传递的, 所以 vcall 的实现如下所示:

    mov eax, dword ptr[ecx]; 
    jmp dword ptr [eax+xx] 

第一句中, 由于 ecxthis 指针, 而一般 vfptr 是类的第一个成员, 所以它是把 vfptr, 也就是 vtable 的地址存到了 eax 中. 第二句里面的 xx, 在 32 位计算机上, 是 4 的整数倍, 所以这一句的意思是: 跳转到 vtable 的第 xx/4 项所指的地址上, 这个地址就是最终要调用的函数的入口.

明白了虚成员函数指针的实现, 就可以看那种调用为什么不能实现了. 让我们用反证法, 如果它能实现, 为讨论方便就假设标 2 的那一句是正确的吧, 在进行调用时, 编译器首先要保证传递的是指向 dthis 指针, 然后还要保证 this 所指向的 vfptr 所指的是 basevtable. 编译器能做到吗? 当然能, 它可以在调用前偷偷修改 vfptr 使其指向 basevtable, 并在调用返回后再把它恢复过来, 但想想这样做在多线程环境中的后果吧. 所以编译器是不可能这样做的, 也就是说"使用派生类对象通过成员函数指针调用基类虚函数是不可能的".

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1083 引用 • 3461 回帖 • 286 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖
  • 技术

    到底什么才是技术呢?

    88 引用 • 179 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 5 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    396 引用 • 3416 回帖
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 548 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 24 关注
  • 倾城之链
    23 引用 • 66 回帖 • 100 关注
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 370 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 180 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 47 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 18 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    330 引用 • 614 回帖
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 559 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 71 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 291 关注
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    53 引用 • 85 回帖
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 350 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 1 关注
  • 反馈

    Communication channel for makers and users.

    123 引用 • 906 回帖 • 192 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 685 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 345 关注
  • gRpc
    10 引用 • 8 回帖 • 54 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 27 关注
  • 笔记

    好记性不如烂笔头。

    303 引用 • 777 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 60 回帖 • 467 关注
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    5 引用 • 15 回帖 • 222 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 232 回帖 • 6 关注