C++对象模型目录
C++ - 对象模型之 模板、异常、RTTI的实现
模板
注意以下几点:
1. 只有声明了某个类型的class,该类型的class才会生成;
2. 只声明了该类型class的指针,不会使该类型的class生成,因为指针只是代表一个地址,不代表一个具体的对象;
3. 只声明了该类型class的引用,并设置该引用为0,也会使该类型的class生成,因为引用代表的是对象;
4. Member functions只有用到时才会被具现,这样可以节省空间;
异常
待续...
RTTI
基本用法
Runtime type Identification:运行阶段类型识别。类型转换,详见《C++ Primer Plus》15.4章。
Dynamic_cast
基类向继承类转换;
Class parent;有私有speek()函数
Class child,child新增 say()函数
Parent* parentobj;
Child * childobj;
Childobj = dynamic_cast< Child*>parentobj;
如果parentobj在new时是一个child 类,则返回child指针,否则返回NULL。
由此可见,dynamic_cast和static_cast相仿,但是比static_cast更安全。
Const_cast
去除const修饰符,用于对const变量进行修改。
Static_cast
相关类之间的转换,不相关类之间转换出错。
Class parent;有私有speek()函数
Class child,child新增 say()函数
用于从基类向继承类转换:不安全,可能parentobj对象没有say函数。
Parent* parentobj;
Child * childobj = static_cast<child*>parentobj;
Childobj->say();
用于从继承类向基类转换:安全
Child * childobj;
Parent * parentobj = static_cast<child*>parentobj;
Parentobj-> speek();
Reinterpret_cast
用于不相关类型转换,用的最多的是由void * 转为某类指针。
一般void * 鉴于要保存不同类型的数据,所以为void *,但是对于每种类,都有自己独有的方法,所以必须转为某种类后再执行。
这种转换很容易出错。
获得对象的类型
可以通过typeid操作符获得对象类型,代码如下:
class Father
{
public:
Father(){who();}
~Father(){who();}
virtual void who(){printf("I am Father\n");}
};
class Child : public Father
{
public:
Child(){who();}
virtual void who(){printf("I am child\n");}
virtual ~Child(){who();}
private:
int Age;
};
int main(){
Child child;
Father* father = &child;
printf("child(%s) father(%s)\n", typeid(child).name(), typeid(*father).name());
}
结果:
child(class Child) father(class Child)
分析:
虽然father是一个Father指针,但是typeid还是可以得到father指针实际指向对象的类型。
注意:
如果,将Child和Father的所有虚函数全部改为非虚函数,那么结果是:
child(class Child) father(class Father),无法判别出father指针真正指向对象的类型。
也就是,RTTI的底层机制,是通过和虚函数相关的技术来实现的。
底层机制
很感谢该博文:《
浅议 Dynamic_cast 和 RTTI》http://www.cnblogs.com/zhyg6516/archive/2011/03/07/1971898.html
该文章里面一张图片说明了一切:
可见,RTTI信息保存在了虚函数表上面一个字节的位置,这和《C++对象模型》说明的差不多,只是vptr没有直接指向RTTI。根据该文章的指点,如代码:
#include <stdio.h>
typedef void(*Fun)(void);
class GrandFather{
public:
GrandFather():age(60){}
virtual void who(){ printf("I am GrandFather\n");}
virtual void fishing(){ printf("GrandFather go to fishing...\n");}
virtual void hungry(){ printf("GrandFather is hungry\n");}
int age;
};
class Father:public GrandFather{
public:
Father():age(36){}
virtual void who(){ printf("I am Father\n");}
virtual void cutting(){ printf("Father go to cutting...\n");}
virtual void hungry(){ printf("Father is hungry\n");}
int age;
};
class Child : public Father{
public:
Child():age(8){}
virtual void who(){ printf("I am Child\n");}
virtual void studying(){ printf("Child go to studying...\n");}
virtual void hungry(){ printf("Child is hungry\n");}
int age;
};
int main(){
Child child1, child2;
Father father;
GrandFather gfather;
int * child1_vtbl = (int *)(*((int*)&child1));
int * child1_class_name = (int*)(*((int*)(*(child1_vtbl - 1))+3))+2;
int * child2_vtbl = (int *)(*((int*)&child2));
int * child2_class_name = (int*)(*((int*)(*(child2_vtbl - 1))+3))+2;
int * father_vtbl = (int *)(*((int*)&father));
int * father_class_name = (int*)(*((int*)(*(father_vtbl - 1))+3))+2;
int * gfather_vtbl = (int *)(*((int*)&gfather));
int * gfather_class_name = (int*)(*((int*)(*(gfather_vtbl - 1))+3))+2;
printf("child1 rtti(%p) class_name(%p)(%s)\n", child1_vtbl - 1, child1_class_name, child1_class_name);
printf("child2 rtti(%p) class_name(%p)(%s)\n", child2_vtbl - 1, child2_class_name, child2_class_name);
printf("father rtti(%p) class_name(%p)(%s)\n", father_vtbl - 1, father_class_name, father_class_name);
printf("gfather rtti(%p) class_name(%p)(%s)\n", gfather_vtbl - 1, gfather_class_name, gfather_class_name);
}
结果:
child1 rtti(00415890) class_name(0041803C)(.?AVChild@@)
child2 rtti(00415890) class_name(0041803C)(.?AVChild@@)
father rtti(00415870) class_name(00418020)(.?AVFather@@)
gfather rtti(00415BE8) class_name(004180B0)(.?AVGrandFather@@)
child2 rtti(00415890) class_name(0041803C)(.?AVChild@@)
father rtti(00415870) class_name(00418020)(.?AVFather@@)
gfather rtti(00415BE8) class_name(004180B0)(.?AVGrandFather@@)
分析:
1. RTTI确实存在虚函数表紧邻上面一个字节;
2. 同类型的对象指向的RTTI是同一个,如child1和child2;
3. RTTI效率不高,中间经过了太多的间接取址;
对于RTTI更深入的分析就待续吧......