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打下了很好的前提基础。

### C++指针强制类型转换的使用方法及注意事项 #### 一、C++ 强制类型转换概述 C++ 提供了四种显式的强制类型转换操作符:`static_cast`、`dynamic_cast`、`reinterpret_cast` 和 `const_cast`。每种转换都有特定的应用场景和限制条件。 #### 二、具体用法与示例 1. **`reinterpret_cast` 的使用** - 这是一种底层的强制类型转换,允许将一种指针类型转换为另一种完全不同的指针类型,或者在指针和整数之间进行转换。 - 它主要用于简单的位级重新解释,例如将一个指针视为另一个类型的地址。 - 示例代码如下: ```cpp long p = 0x12345678; char* cp = reinterpret_cast<char*>(&p); // 将 long 类型变量的地址转换为 char* ``` 2. **`static_cast` 的使用** - 主要用于基本数据类型之间的转换以及具有继承关系的对象间的向上/向下型(前提是编译器能够静态推断安全性)。 - 对于指针而言,它可用于父子类间的安全转换。 - 示例代码如下: ```cpp class Base {}; class Derived : public Base {}; Derived d; Base* basePtr = static_cast<Base*>(&d); // 向上型 Derived* derivedPtr = static_cast<Derived*>(basePtr); // 向下型 ``` 3. **`dynamic_cast` 的使用** - 动态类型转换仅适用于多态对象(即包含虚函数的基类),并依赖运行时检查来验证转换的有效性。 - 如果尝试执行非法的动态转换,则返回空指针或抛出异常。 - 示例代码如下: ```cpp Base* b = new Derived(); Derived* d = dynamic_cast<Derived*>(b); if (d != nullptr) { // 转换成功 } ``` 4. **`const_cast` 的使用** - 此操作符专门用来移除常量属性 (`const`) 或易变属性 (`volatile`)。 - 需要注意的是,修改通过这种方式获得的原生不可变对象可能会引发未定义行为。 - 示例代码如下: ```cpp const int value = 42; int* nonConstValue = const_cast<int*>(&value); *nonConstValue = 99; // 不推荐这样做,因为会破坏原有语义[^1] ``` #### 三、注意事项 - 使用 `reinterpret_cast` 应格外小心,因为它绕过了类型系统的保护机制,可能导致难以调试的问题。除非绝对必要,否则不应随意应用此类转换[^2]。 - 当涉及复杂的继承结构时优先考虑采用 `dynamic_cast` 来代替直接运用 `static_cast` ,从而增强程序健壮性。 - 利用 `const_cast` 修改原本声明为只读的数据需谨慎行事,确保不会违反逻辑约束或触发潜在风险[^3]。 ```cpp // 综合示例展示多种 cast 方式 class Animal { virtual void sound() {} }; class Dog : public Animal { void bark() {} }; int main(){ double pi=3.14f; float fpi = static_cast<float>(pi); Animal* a=new Dog(); Dog* doggy=dynamic_cast<Dog*>(a); delete a; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值