标签:member 函数 类型 space 调用 为什么 point 没有 add
double // return type (Point::* // class the function is member pmf) // name of the pointer to member (); // argument list然后能够这样定义并初始化该指针:
double (Point::*coord)() = &Point::x;也能够这样指定值:
coord = &Point::y;想调用它,能够这样做:
(origin.*coord)();或
(ptr->*coord)();这些操作会被编译器转化为:
(coord)(&origin);和
(coord)(ptr);指向member function的指针的声明语法,以及指向"member selection运算符"的指针,其作用是作为 this 指针的空间保留者.这这也就是为什么 static member function(没有 this 指针)的类型是"函数指针",而不是"指向member function的指针"的原因.
float (Point::*pmf)() = &Point::z; Point *ptr = new Point3d;pmf,一个指向member function的指针,被设值为Point::z()(一个 virtual function)的地址,ptr则被指定以一个Point3d对象,假设直接经由ptr调用z():
ptr->z();则被调用的是point3d::z(),但假设从pmf间接调用z()呢?
(ptr->pmf)();仍然是Point3d::z()被调用吗?也就是说,虚拟机制仍然可以在使用"指向member function的指针"的情况下运行吗?答案是yes,问题是怎样实现呢?
class Point { public: virtual ~Point(); float x(); float y(); virtual float z(); };然而取得destructor的地址:
&Point::~Point;得到的结果是1,取x()或y()的地址:
&Point::x(); &Point::y();得到的则是函数在内存中的地址,由于它们不是 virtual,取z()的地址:
&Point::z();得到的结果是2,通过pmf来调用z(),会被内部转化为一个编译时期的式子,一般形式例如以下:
(*ptr->vptr[(int)pmf])(ptr);对一个"指向member function的指针"评估求值(evaluted),会由于该值有两种意义而复杂化;其调用操作也将有别于常规调用操作.pmf的内部定义,也就是:
float (Point::*pmf)();必须同意该函数可以寻址出nonvirtual x()和 virtual z()两个member functions,而那两个函数有着同样的原型:
// 二者都能够被指定给pmf float Point::x() { return _x; } float Point::z() { return 0; }仅仅只是当中一个代表内存地址,还有一个代表 virtual table中的索引值.因此,编译器必须定义pmf使它能够(1)还有两种数值,(2)更重要的是其数值能够被差别代表内存地址还是 virtual table中的索引值.
// 一般结构,用以支持在多重继承下指向member functions的指针 struct __mptr { int delta; int index; union { ptrtofunc faddr; int v_offset; }; };它们要表现什么呢?index和faddr分别(不同一时候)带有 virtual table索引和nonvirtual member function地址.在该模型下,像这种调用操作:
(ptr->*pmf)();会变成:
(pmf.index < 0) ? // non-virtual invocation (*pmf.faddr)(ptr) : // virtual invocation (*ptr->vptr[pmf.index](ptr));这样的方法所受到的批评是,每个调用操作都得付出上述成本,检查其是否为 virtual 或 nonvirtual.Microsoft把这项检查拿掉,导入一个它所谓的vcall thunk.在此策略下,faddr被指定的要不就是真正的member function地址(假设函数是nonvirtual的话),要不就是vcall thunk的地址.于是 virtual 或novirtual函数调用操作透明化,vcall thunk会选出并调用相关 virtual table 中的适当slot.
extern Point3d foo(const Point3ed&, Point3d(Point3d::*)()); void bar(const Point3d& p) { Point3d pt = foo(p, &Point3d::normal); }当中&Point3d::normal的值类似这样:
{0, -1, 10727417}将须要产生一个暂时性对象,有明白的初值:
__mptr temp = {0, -1, 10727417} foo(p, temp);本节一開始的那个结构体,delta字段表示 this 指针的offset值.而v_offset字段放的是一个 virtual(或多重继承中的第二或后继的)base class 的vptr位置.假设vptr被编译器放在 class 对象的起始处,这个字段就没有必要了,代价则是C对象兼容性减少.这些字段仅仅在多重继承或虚拟继承的情况下才有其必要性,有很多编译器在自身内部依据不同的 class 特性提供多种指向member functions的指针形式,比如Microsoft就供应了三种风格:
Point3d* (*pf)(const Point3d&, const Point3d &) = cross_product; for (int iters = 0; iters < 10000000; iters++) (*pf)(pA, pB); return 0;第二个測试(指向 member function 的指针)的声明和调用操作例如以下:
Point3d* (Point3d::*pmf)(const Point3d &) const = &Point3d::cross_product; for (int iters = 0; iters < 10000000; iters++) (pA.*pmf)(pB);上述操作会被转化为下面的内部形式,于是下面的函数调用:
(pA.*pmf)(pB);会被转化为这种推断:
pmf.index < 0 ? (*pmf.faddr)(&pA + pmf.delta, pB) : (*pA.__vptr__Point3d[pmf.index].faddr) (&pA + pA.__vptr__Point3d[pmf.index].delta, pB);一个"指向member function的指针"是一个结构,内含三个字段:index,faddr和delta.index若不是内带一个相关 virtual table的索引值,就是以-1表示函数是 nonvirtual.faddr带有nonvirtual member function的地址.delta带有一个可能的 this 指针调整.
C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)
标签:member 函数 类型 space 调用 为什么 point 没有 add
原文地址:http://www.cnblogs.com/jhcelue/p/6888865.html