C++指针的动态转换

您的一个老客户来找您,需要使用vector来管理众多具有继承关系的类。先有以下三个类。

 类图

       class Person {

       public:

              string name;

              Person() : name("Person") {}

       };

 

       class Male :  public Person {

       public:

              bool taller;

              Male() : taller(true) { name = "Male"; }

       };

 

       class Female :  public Person {

       public:

              bool taller;

              Female() : taller(false) { name = "Female"; }

       };

 

MaleFemale类均继承于Person,且与基类Person相比,多了一个taller字段。可别小看了这个简单的taller字段,正是它才会有了此段令人愉悦的经历。此是后话,下文再表。

 

暂且不管是否有这样的需要,反正客户要求将PersonMaleFemale这三个类均放在一个集合中。于是便用vector来管理。客户再提出具体要求,必须使用vector<Person>来统一管理,这样便可应用一些设计模式来简化维护。Design Pattern一书是谁写的?贻害不浅啊!

 

       vector<Person> persons;

 

       Person person;

       persons.push_back(person);

 

       Male male1;

       persons.push_back(male1);

 

       Male male2;

       persons.push_back(male2);

 

       Female female;

       persons.push_back(female);

 

客户说了,我要看这些个人中,哪些人地taller为真。这还不简单吗,直接就可告诉你,Maletaller为真。客户又说了,你说了不算,代码才是本质。客户的文化层次越高,码表劳工的饭碗就越不好端。

 

“Mmm, select * from persons where taller = ‘true’. ABC…这又不是数据库!尽管抱怨,迫于3.15的强大监督威力,您被迫妥协。

 

       for (vector<Person>::const_iterator iter = persons.begin(); iter != persons.end(); iter++) {

              if (iter->taller) {

                    

              }

       }

 

慢着,编译器说,taller不是Person的成员。我知道不是,可我也push_back(male1)push_back(female)了啊。您先搬出RTTI大法验明正身。

 

       for (vector<Person>::const_iterator iter = persons.begin(); iter != persons.end(); iter++) {

              cout << typeid(*iter).name() << endl;

       }

 

Person

Person

Person

Person

 

电脑可是一点也不含糊,可也太不通情达理了。在您push_back(male1)push_back(female)等过程中,由于vector使用了Person模板,电脑已经自动隐式地将MaleFemale类型转换为Person类型了。这种向上转换,使得MaleFemaletaller字段丢失。我把它的类型转换回MaleFeMale不就行了?

 

static_cast虽属显式转换,但其最主要的功能还是用于屏蔽编译器进行隐式转换时的警告信息;const_cast只用于加上常量特性;reinterpret_cast虽然功力强大,但它也可将狸猫解释为太子。显然这些都不适用。剩下的只有dynamic_cast了。而dynamic_cast主要是对具有上下级层次结构的指针或引用进行转换。看来这里可以适用,但需要先将vector的模板改为指向Person的指针。

 

       vector<Person*> vpPersons;

 

       Person person;

       Person* pPerson = &person;

       vpPersons.push_back(pPerson);

 

       Male male1;

       Person* pMale = &male1;

       vpPersons.push_back(pMale);

 

       Female female;

       Female* pFemale = &female;

       vpPersons.push_back(pFemale);

 

但显然,由于vpPersons初始化的代码放在一个代码块中,在堆(heap)上创建的变量会自动消失,只有保存存放于栈(stack)上变量的指针才是安全可靠的。

 

       vector<Person*> vpPersons;

 

       Person* pPerson = new Person();

       vpPersons.push_back(pPerson);

 

       Male* pMale1 = new Male();

       vpPersons.push_back(pMale1);

 

       Female* pFemale = new Female();

       vpPersons.push_back(pFemale);

 

OK,不是冤家不聚头,newdelete从来就是相儒以沫,骨肉不分离。尽管现在内存很便宜,但内存泄漏的罪名,我们是担当不起的。

 

       for (vector<Person*>::size_type i = 0; i < _vpPersons.size(); i++) {

              delete _vpPersons[i];

              _vpPersons[i] = NULL;

       }

 

戏台搭好了,好戏要上演了。程序运行得好似正常,但程序结束时电脑总是用一个很低沉的重低音“砰”来打破您美好的幻想,而这种声音无疑也必会将客户的野性完全引发出来。“Something must be wrong.

 

问题出在

 

       delete _vpPersons[i];

 

上面。

 

我们在通过删除一个指针来释放相应内存,而指针不仅仅指定特定的内存地址,它也是有范围的。指向char型与指向int型的指针虽可同时指向同一地址,但取出内存地址中的内容却是不一样。如果召唤出的是一个一米左右的人,即是小孩;如果是一个一米六七以上的人,那是成人。虽然我们使用vector<Person*>来统一管理众多指针,但C++在保存指针时,还是留了一手,悄悄保留了指针的原型,以保证在取出内容及释放空间时不出任何差错。

 

在程序中,当删除指针时,C++自动调用dynamic_cast将指针转换回其原有的类型。而对于继承关系的类,dynamic_cast的唯一要求是基类必须有一个虚函数。由于我们的Person类尚未达到此要求,因此,C++(其实是M$)用这种极其特别的方式“提醒”您。显而易见,您需要在Person类中加上一个虚函数。

 

       class Person {

       public:

              string name;

              Person() : name("Person") {}

              virtual void f() {}

       };

 

由于该虚函数只用于应付dynamic_cast的特殊要求,因此为节省空间,您将函数体也放在类内定义了。加上该函数后,M$也不再以那种可怕的声音来烦您了。

 

做了这么多的准备工作,您可以向难缠的客户交差了。

 

       for (vector<Person*>::const_iterator iter = vpPersons.begin(); iter != vpPersons.end(); iter++) {

              if (Male* pMale = dynamic_cast<Male*>(*iter)) {

                     cout << pMale->taller << endl;

              }

       }

 

前面的虚函数f()可谓是一箭双雕,为您显式地使用dynamic_cast打下了很好的前提基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值