C++primer读书笔记6(一)

2014-11-24 02:00:34 · 作者: · 浏览: 2
纯虚函数
在函数后面写上=0 指定该函数为纯虚函数
含有一个或多个纯虚函数的类是抽象基类,不能创建对象。
class Base
{
public:
virtual void f() const =0;
virtual void fcn(int i)
{
cout<
}
};
int main()
{
Base *b=new Base; //失败 纯虚类无法定义对象
getchar();
}
普通函数名称的特殊处理:
编译器会把重载的两个函数在编译阶段做优化,在名称上加入参数编码,制造出独一无二的效果。
如下所示:
void x(float newx);
float x();
处理为:
Void x_5pointx(floatnewx);
Void x_5pointy();
虚拟成员函数
ptr->pay()转化为:
(*ptr->vptr[1])(ptr);
实现模型,vptr与vtbl。Vtbl是在编译时,每个类(具有虚函数:继承或者自己声明的虚函数,或者纯虚函数)都会产生的一个虚函数表。每个类可能不止一个虚函数表,比如多重继承的情况下。【注意】一个类可能具有多个虚函数表。
虚函数表中的内容包括该类自己具有的虚函数,从其他函数继承来的虚函数,纯虚函数实体,同时为了支持虚继承,某些实现会把虚基类地址也放到vtbl里。
Vtbl则是每个对象都拥有的一个指向虚函数表的指针。在对象产生时的构造函数里设置,由编译器生成设置代码。一般而言我们不知道ptr所指对象的真正类型,但是经过ptr总是可以访问对象的虚表。虽然不知道那个z()函数实体被调用,但是z()地址总是会放到相同的slot中。于是一个多态的访问Ptr->z();总是可以被转换成(*Ptr->vptr[4])(ptr),这里vptr表示编译器安插的指针,4表示z()放置的slot编号。
多重继承下的虚拟成员函数
深入浅出C++虚函数表
class A
{
public: virtual int add(int i);
};虚函数表
类A中的int add (int i)地址
。。。其他虚函数地址
如果类A的定义如下:
class A
{
public:
int i;
virtual int add(int i);
}
那么sizeof(A) == 8。所以类的大小等于vtable的size加上数据成员的size,如果他有父类,则还需要加上父类中数据成员的size。例如:
class B: public A
{
public:
int k;
}
cout<
输出12。
B的vtable (4bytes)+ B::k (4bytes) + A::i (4bytes) =12 bytes。
多态的原理
多态是如何利用这个虚函数表实现的呢?看下面的代码:
struct Super
{
int data;
virtual int add(int i){return i;};
virtual string toString()=0;
};
class Sub: public Super
{
public:
string toString()
{
return string("Sub class");
}
};
Super* s = new Sub();
cout<toString()<
delete s;
输出”Sub class”。
Super vtable
Super:: add地址
Super::toString地址
Sub vtable
Super::add地址
Sub::toString地址
int data;
编译器会把虚表编译成上面的样式,注意两件事情:
第一, 虚函数表在类所有成员的最前面。
第二, 第二,Sub因为重写了toString,Sub的虚函数表就记录者Sub的toString的地址。
上面的程序Super* s = new Sub();
s实际指向的是Sub类对象的内存区域,所以调用方法的时候会根据这片内存记录的函数地址进行调用,多态就是这么实现的。
访问虚函数表,手动调用虚函数
既然虚函数表存在于内存,那么就会有方法找到这片内存区域然后调用,下面就用程序来说明这项工作。
首先来看一项技术
struct S
{
int k;
int m;
};
S a;
int i = 5;
a.k = 5;
a.m = 4;
cout<<*(int*)&a<
输出5。
说明一下这段程序,我想就是最一行语句理解起来不太容易,把它拆开来看看会很清晰。
首先用int*型指向S类型
int* pi = (int*)&a;
然后用解引用来查看这片内存存储的int型数据,合起来就*(int*)&a是这种形式。
如此咱们就有了方法访问一个类的任意一片内存了,那么就先找到虚函数表的地址吧!
了解一个数据类型DWORD_PTR,它被定义为32位的指针(64位系统下64位),我们就用这个指针来找到虚函数表的地址。
DWORD_PTR pvTable;
pvTable = (DWORD_PTR)&Sub();
Sub()创建了一个Sub的临时对象,&Sub()表示取这个对象的地址,然后把它强制类型转换为DWORD_PTR,上文说过虚表是在类的最上面,所以pvTable就指向了这个类的虚函数表。
如下代码访问此类的虚函数:
((Sub*)pvTable)->toString()
利用这种技术可以实现动态调用,具体实现不说明了,可以参考<<精通MFC>>P19,基本思想是实现一个与被调用类匹配的结构,然后进行类型转换就可以映射到相应位置的内存。
struct CompitibleStruct