本节的复杂点,在于对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