【转载】成员函数指针与高性能的C++委托

一般,编译器采取最差的,而且一直使用最普通的形式。比如对于下面这个结构:

// Borland (缺省设置) 和Watcom C++.

struct {

FunctionPointer m_func_address;

int m_delta;

int m_vtable_index; //如果不是虚拟继承,这个值为0。

};

// Metrowerks CodeWarrior使用了稍微有些不同的方式。

//即使在不允许多重继承的Embedded C++的模式下,它也使用这样的结构!

struct {

int m_delta;

int m_vtable_index; // 如果不是虚拟继承,这个值为-1。

FunctionPointer m_func_address;

};

// 一个早期的SunCC版本显然使用了另一种规则:

struct {

int m_vtable_index; //如果是一个非虚拟函数(non-virtual function),这个值为0。

FunctionPointer m_func_address; //如果是一个虚拟函数(virtual function),这个值为0。

int m_delta;

};

//下面是微软的编译器在未知继承类型的情况下或者使用/vmg选项时使用的方法:

struct {

FunctionPointer m_func_address;

int m_delta;

int m_vtordisp;

int m_vtable_index; // 如果不是虚拟继承,这个值为0

};

// AIX (PowerPC)上IBM的XLC编译器:

struct {

FunctionPointer m_func_address; // 对PowerPC来说是64位

int m_vtable_index;

int m_delta;

int m_vtordisp;

};

// GNU g++使用了一个机灵的方法来进行空间优化

struct {

union {

FunctionPointer m_func_address; // 其值总是4的倍数

int m_vtable_index_2; // 其值被2除的结果总是奇数

};

int m_delta;

};

对于几乎所有的编译器,delta和vindex用来调整传递给函数的this指针,比如Borland的计算方法是:

adjustedthis = *(this + vindex -1) + delta // 如果vindex!=0

adjustedthis = this + delta // 如果vindex=0

(其中,“*”是提取该地址中的数值,adjustedthis是调整后的this指针——译者注)

Borland使用了一个优化方法:如果这个类是单一继承的,编译器就会知道delta和vindex的值是0,所以它就可以跳过上面的计算方法。

GNU编译器使用了一个奇怪的优化方法。可以清楚地看到,对于多重继承来说,你必须查看vtable(虚拟函数表)以获得voffset(虚拟函数偏移地址)来计算this指针。当你做这些事情的时候,你可能也把函数指针保存在vtable中。通过这些工作,编译器将m_func_address和m_vtable_index合二为一(即放在一个union中),编译器区别这两个变量的方法是使函数指针(m_func_address)的值除以2后结果为偶数,而虚拟函数表索引(m_vtable_index_2)除以2后结果为奇数。它们的计算方法是:

adjustedthis = this + delta

if (funcadr & 1) //如果是奇数

call (* ( *delta + (vindex+1)/2) + 4)

else //如果是偶数

call funcadr

(其中, funcadr是函数地址除以2得出的结果。——译者注)

Inter的Itanium编译器(但不是它们的x86编译器)对虚拟继承(virtual inheritance)的情况也使用了unknown_inheritance结构,所以,一个虚拟继承的指针有20字节大小,而不是想象中的16字节。

// Itanium,unknown 和 virtual inheritance下的情况.

struct {

FunctionPointer m_func_address; //对Itanium来说是64位

int m_delta;

int m_vtable_index;

int m_vtordisp;

};

我不能保证Comeau C++使用的是和GNU相同的技术,也不能保证它们是否使用short代替int使这种虚拟函数指针的结构的大小缩小至8个字节。最近发布的Comeau C++版本为了兼容微软的编译器也使用了微软的编译器关键字(我想它也只是忽略这些关键字而不对它们进行实质的相关处理罢了)。

Digital Mars编译器(即最初的Zortech C++到后来的Symantec C++)使用了一种不同的优化方法。对单一继承类来说,一个成员函数指针仅仅是这个函数的地址。但涉及到更复杂的继承时,这个成员函数指针指向一个形式转换函数(thunk function),这个函数可以实现对this指针的必要调整并可用来调用实际的成员函数。每当涉及到多重继承的时候,每一个成员函数的指针都会有这样一个形式转换函数,这对函数调用来说是非常有效的。但是这意味着,当使用多重继承的时候,子类的成员函数指针向基类成员函数指针的转换就会不起作用了。可见,这种编译器对编译代码的要求比其他的编译器要严格得多。

很多嵌入式系统的编译器不允许多重继承。这样,这些编译器就避免了可能出现的问题:一个成员函数指针就是一个带有隐藏this指针参数的普通函数指针。

原文地址:https://www.cnblogs.com/eaglexmw/p/3056657.html