为什么 C++ 成员函数指针是 16 字节宽的

当提及指针时,我们通常认为它是可以用void * 指针表示的在x86_64架构上占用8字节的东西。例如, 维基百科有一篇 关于x86_64的文章 中这样写道:

Pushes and pops on the stack are always in 8-byte strides, and pointers are 8 bytes wide .

从CPU的角度来看,指针就只是一个内存地址,并且x86_64中的所有内存地址用64位表示,所以8字节的假设是成立的。其实可以简单地通过打印不同类型的指针大小来得到这个结论。

#include <iostream>

int main() {

std::cout 《

"sizeof(int*)      == " 《 sizeof(int*) 《 "\n"

"sizeof(double*)   == " 《 sizeof(double*) 《 "\n"

"sizeof(void(*)()) == " 《 sizeof(void(*)()) 《 std::endl;

}

编译并运行这个程序,结果明确地说明了所有指针是8字节的:

$ uname -i

x86_64

$ g++ -Wall ./example.cc

$ ./a.out

sizeof(int*)      == 8

sizeof(double*)   == 8

sizeof(void(*)()) == 8

但是在 C++ 里就有这么一个例外 —— 指向成员函数的指针。

更有趣的是,成员函数指针的大小正好是其他指针大小的两倍。通过下面的简单的程序就可以验证这一点,它会打印 “16”:

#include <iostream>

struct Foo {

void bar() const { }

};

int main() {

std::cout 《 sizeof(&Foo::bar) 《 std::endl;

}

难道是 Wikipedia 错了么?当然不是。对于所有硬件来说,所有指针依然还是 8 个字节的宽度。那成员函数指针到底是什么呢?它其实是 C++ 语言的一个特性,是一个不能与硬件(物理)地址一一对应的虚拟出来的地址。由于它是由 C++ 编译器在运行时来实现(把成员函数指针转换成实际的虚拟内存地址,还伴随其他的一些相关工作),这一特性会带来轻微的运行时开销从而导致性能损失。C++ 规范并不关心具体的语言实现,所以它对该类指针并未做过多说明。幸运的是 Itanium C++ ABI specification (安腾 C++ 应用二进制接口规范,致力于标准化 C++ 运行时的实现)除了对 virtual table(虚表),RTTI(运行时类型识别)和 exceptions(异常)的实现做了说明外,还在 §2.3 节对成员函数指针做了如下的说明:

每一个指向成员函数的指针都是有如下两部分成:

ptr:

如果指针指向一个非虚成员函数,该字段就是一个简单的函数指针。曼哈顿娱乐城如果该指针指向的是一个虚函数成员,那么该字段的值是该虚函数成员在其虚表中位移值加 1,在 C++ 中用 ptrdiff_t 类型表示。0 值表示 NULL 指针,与下面的调整字段值无关。

adj:

当成员函数被调用时,this 指针所必须做的位置调整(译者注:这与 C++ 的对象内存模型有关,确保每个成员函数正确的访问其函数体内引用的各种函数成员,下面会有进一步的解释),在 C++ 中用 ptrdiff_t 类型表示。

一个成员函数指针是 16 位的,因为除了需要 8 位字节来存储函数的地址外,还需要一个地址大小(8 字节)的字段来存储 this 指针位置如何调整的信息(常识: 每当一个非静态的成员函数被调用时,this 指针都会被编译器暗中传递给该函数,以便于在函数体内部通过该指针正确的访问调用对象的各类成员)。上面的 ABI 规范没有说清楚的是为什么以及什么时候需要对 this 指针的位置做调整。

推荐阅读http://www.yupoo.com/photos/summo2/93898660/ 
http://www.cnblogs.com/huangdj/p/4142633.html

时间: 2024-10-07 01:28:28

为什么 C++ 成员函数指针是 16 字节宽的的相关文章

为什么 C++ 中成员函数指针是 16 字节?

当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes and pops on the stack are always in 8-byte strides, and pointers are 8 bytes wide. 从 CPU 的角度来看,指针无非就是内存的地址,所有的内存地址在 x86_64 平台下都是由 64 位来表示,所以假设它是 8 个字节是

为什么c++中函数指针是16字节

原文链接 当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes and pops on the stack are always in 8-byte strides, and pointers are 8 bytes wide. 从 CPU 的角度来看,指针无非就是内存的地址,所有的内存地址在 x86_64 平台下都是由 64 位来表示,所以假设它是 8

C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论

今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程中执行.最终目标是,继承的业务逻辑类外部调用时有两个接口可选,调用syncRun同步执行:调用由引擎自动生成的asyncRun就异步执行.最终自动生成asyncRun的模板基类没能实现,主要原因是mingw对this处理的太有问题了!!原本以为编译器问题,后来才知道成员函数指针和this指针如此特殊

自制反汇编工具使用实例 其二(使用xmm寄存器初始化对象,以及空的成员函数指针)

在反汇编代码中,当看到xmm寄存器,第一反应是将要进行浮点操作或访问,但是更加多的情况是在使用xmm寄存器初始化局部对象. 下面是自制反汇编工具翻译出来的代码: // -[CALayer setAllowsEdgeAntialiasing:] void CALayer20_setAllowsEdgeAntialiasing3A_(id self, SEL sel, uintptr_t setAllowsEdgeAntialiasing) { // 0 pushq %rbp // 1 rbp =

类的成员函数指针(比較深入)

From:http://blog.csdn.net/hairetz/archive/2009/05/06/4153252.aspx 个人感觉对于类的成员函数指针这块解说的比較深入具体 推荐阅读 ///////////////////////////////////////////////// 先看这样一段代码 class test {    public:       test(int i){ m_i=i;}       test(){} void hello()       {        

成员函数指针与高性能的C++委托

成员函数指针与高性能的C++委托(上篇) 撰文:Don Clugston 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做"闭包(closure)"或"委托 (delegate)")在一些语言中已经证明了它宝贵的价值.在Delphi (Object Pascal)中,面向对象的函数指针是Borland可视化组建库(VCL,Visual Component Library)的基础.而在目前,C#使"委托&

C++的类成员和类成员函数指针

类成员函数指针: 用于访问类成员函数,和一般函数指针有区别. 类成员函数处理的是类数据成员,声明类成员函数指针的同时,还要指出具体是哪个类的函数指针才可以.调用时也要通过对象调用. 而对于类的静态成员函数,它是类对象共享的,且只能处理静态数据成员,所以它的函数指针可以类似一般函数指针一样使用. 1 class Test 2 { 3 public: 4 void fun(int); 5 void fun(int) const; //重载函数,加一个const限制 6 static void fun

C/C++ 类成员函数指针 类成员数据指针

普通函数指针:  "return_type (*ptr_name)(para_types) " 类成员函数指针: "return_type (class_name::*ptr_name)(para_types)" 类数据成员指针: "type class_name::* ptr_name"; C/C++: 1 class Demo 2 { 3 public: 4 Demo():data(100) 5 { 6 7 } 8 int data; 9 i

VB6/VBA中跟踪鼠标移出窗体控件事件(类模块成员函数指针CHooker类应用)

前几天发了一篇博文,是关于获取VB类模块成员函数指针的内容(http://www.cnblogs.com/alexywt/p/5880993.html):今天我就发一下我的应用实例. VB中默认是没有鼠标移出事件响应的,而这个事件其实在项目开发中,实用性很强,很多时候需要在鼠标移出窗体或控件时做些事情:没有这个事件会感觉很费力: 今天我所说的实际案例就是,在窗体上,设计一个SplitterBar控件,窗体的最终用户使用这个控件可以在运行程序时任意调整其内部控件大小. 我在第二篇参考博文作者开发的