[温故而知新] 《深度探索c++对象模型》——对象方法成员

来源:互联网 发布:网络女主播大尺度直播 编辑:程序博客网 时间:2024/05/01 14:03

本节的复杂点,在于对virtual function的支持上。

先从简单的几种function入手

1.non member function
2.static member function
3.non static member function

第一种是最常见的 non member funcion:

//在一个地方定义returnValue functionName(argumentList){    //function body}//在另一个地方声明extern returnValue functionName(argumentList);

第二种static member function

class ClassName{public:    static returnValue functionName(argumentList);}//调用ClassName::functionName(...);

static member function 与 non member function非常相似,实际上,编译器就是通过类名和函数签名等把static member function 转换为 non member function。

第三种 non static member function

class ClassName{public:    returnValue functionName(argumentList);}

第三种与第二种也是非常类似,最终编译器也是把它转为一个non member function。与static member function 区别在于,non static member function 会在转换的过程中修改函数的签名,加入”this”指针。

比如:

class Point{public:    int x;    int y;    void setX(int n);};void Point::setX(int n){    x = n;}//转化为类似如下的方法,下面的函数名命名规则,各家编译器不尽相同:void Point_setX_int(Point *const this,int n){    this->x = n;}//有了上面这一步的转化,那么那些执行方法调用的地方,编译器也会进行修改:Point point;point.setX(1);   //转化为类似 Point_setX_int(&point,1);

上面三种方式,效率是一致的。

virtual member functions

2.1、单继承下的virtual member functions
class Animal{public:    virtual void say() =0; //纯虚函数};class Dog:public Animal{public:    void say();};class Cat:public Animal{public:    void say();};void Dog::say(){    cout << "wang~ wang~ wang~" << endl;}void Cat::say(){    cout << "miao~ miao~ miao~" << endl;}

画个图了解下内存布局:

这里写图片描述

注意上图种的vptr table,这些是编译器根据我们的代码生成的辅助结构,vptr table存的是一些函数指针。

这里我们假设:

//Dog::say() 编译器转化后的non member funtion为 void Dog_say_void(Dog const *thiz){    cout << "wang~ wang~ wang~" << endl;}//Cat::say() 编译器转化后的non member function为void Cat_say_void(Cat const *thiz){    cout << "miao~ miao~ miao~" << endl;}
Dog dog;Cat cat;Animal *ptr;//1.使用指针调用的虚函数ptr = &dog;ptr->say();            //wang~ wang~ wang~ptr = &cat;ptr->say();            //miao~ miao~ miao~/** ptr->say()的编译器转化(*ptr->vptr[index])(ptr)*///2.使用对象调用的虚函数dog.say();//编译器转化为 Dog_say_void(&dog);cat.say();//编译器转化为 Cat_say_void(&cat);

注意比较上面两种调用方式,使用指针调用virtual 方法,可能需要经过一层间接查表得到最终调用的方法,因为在编译期无法确定ptr指向的对象实际是哪种类型,而使用对象调用virtual 方法,则不需要中间那一层查表,因为编译器在编译期就已经知道这个对象是哪种类型。

2.2、多继承下的virtual member functions
class Base1{public:    virtual ~Base1();    virtual void speakClearly();    virtual Base1 *clone() const;protected:    float data_Base1;};class Base2{public:    virtual ~Base2();    virtual void mumble();    virtual Base2 *clone() const;protected:    float data_Base2;};class Derived :public Base1, public Base2{public:    virtual ~Derived();    virtual Derived *clone()const;protected:    float data_Derived;};

画个图了解下内存布局:

这里写图片描述

注意上图中右下角 Derived的内存布局,是优化后的,也就是把Derived 的 vptr去掉,与Base1用同一个vptr,同时修改vptr table。

同时注意到,在上图的多重继承中的Derived,有三处地方需要涉及到“this”指针的调整。
1.virtual destructor
2.多重继承中第二个或之后的Base Class 继承下来的virtual function。这里的例子为图中标红的Base2::mumble()。
3.一个语言扩充性质:允许一个virtual function的返回值类型有所变化,可能是base type,也可能是 public derived type。 这里的例子为图中标红的clone()方法。

为什么需要调整this指针呢?因为non member function的第一个参数为this指针,比如

Base2 *ptr = new Derived;delete ptr;//这里实际调用的是Derived::~Derived()而不是Base2::~Base2();//也就是实际上的调用类似 Derived_destrutor(ptr),显然这里的ptr是不对的,需要调整为Derived_destrutor(ptr-sizeof(Base1))

关于this指针的调整,书中提到微软采用了”thunk”技术,我根据书中例子画了个图,更容易理解:
这里写图片描述

上图中,红色虚框中就用到了“thunk”。

那么什么是 “thunk”技术呢?
简单地理解就是一个代码片段,这个代码片段的目的就是修改某些参数,然后通过一个jmp指令跳转到目标的代码去。
具体可以参考:
《C++ Tips: Adjustor thunk: what is it, why and how it works》
《Adjustor thunks》
《C++ 的THUNK技术》

2.3、虚拟继承下的virtual member functions

这个与多重继承下的virtual member functions类似。
书中提到一个建议:“不要在一个virtual base class 中声明nonstatic data members”。

指向 member functions 的指针

本节的复杂点,也是在于virtual member function 在多重继承和虚拟继承下的支持。
比如:

class Point{public:    float x();    virtual float z();};//Point *ptr = new Point;float (Point::*pmf)() = &Ponit::z;(ptr->*pmf)();//编译器转化为:(*ptr->vptr[(int)pmf])(ptr)pmf = &Point::x;(ptr->*pmf)();//编译器转化为:(*pmf)(ptr)

可以看到,同样是执行(ptr->*pmf)(),编译器要根据情况进行不同的转化。书中提到了一个结构用于支持member function指针:

struct _mptr{    int delta;    int index;    union{        ptrtofunc faddr;        int v_offset;    }}

书中也没详细讲解这一节,不过可以简单的理解,就是在处理上面提到的各种对于member function pointer的支持,使用这个结构中相应的字段进行标识、支持即可。

inline function

注意几个副作用:

//1.形式参数的副作用inline int min(int i,int j){    return i<j?i:j;}int minval;minval = min(foo(),bar()+1);//转化为int t1;int t2;minval = (t1 = foo(),t2 = bar()+1),t1<t2?t1:t2;//2.局部变量inline int min(int i,int j){    int minval = i<j?i:j;    return minval;}int m;m = min(val1,val2);//转化为int __min_minval;m = (__min_minval = val1<val2?val1:val2),__min_minval;

const相关小知识点

class Point{public:    int getX() const;  //限制该方法只读取而不修改Point对象相关数据    void setX(int n);private:    int x;}//限制ptr不能被修改,比如不允许 ptr = otherPtr;Point *const ptr//限制ptr指向的对象不能被修改,比如不允许调用 ptr->setX(0)const Point *ptr//限制ptr不能被修改,并且不能修改ptr指向的对象。const Point *const ptr
0 0
原创粉丝点击