C++ - 对象模型之 模板、异常、RTTI的实现

C++对象模型目录

C++ - 对象模型之 编译器何时才会自行添加构造函数

C++ - 对象模型之 内存布局

C++ - 对象模型之 成员函数调用

C++ - 对象模型之 构造和析构函数都干了什么

C++ - 对象模型之 类对象在执行时是如何生成的

C++ - 对象模型之 模板、异常、RTTI的实现


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@@)

分析:
1. RTTI确实存在虚函数表紧邻上面一个字节;
2. 同类型的对象指向的RTTI是同一个,如child1和child2;
3. RTTI效率不高,中间经过了太多的间接取址;


对于RTTI更深入的分析就待续吧......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值