灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3498回复:0

[C++技术]C++类型转换之小结

楼主#
更多 发布于:2012-10-08 12:57
1.C和C++类型转换
   C风格的强制转换:


          T(exdivssion)或者(T)exdivssion


    C++的类型转换用:dynamic_cast,static_cast,reinterdivt_cast,const_cast


2.运行时类型识别(RTTI)


 (1)定义:程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。


 (2)RTTI提供了两个有用的操作符:


   typeid:返回指针或引用所指的实际类型


   dynamic_cast:用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用


 (3)typeid 函数


   该函数的主要作用就是让用户知道当前的变量是什么类型的,比如使用typid(a).name()就能知道变量a是什么类型的。因为typid()函数是一个反回类型为const typid_info;类型的函数,所以下面先对type_info类作下介绍


 (4)type_info类


   该类的具体实现方式依编译器而定,但一般都有如下的成员定义


  class type_info


  {private:


  type_info(const type_info ;);


  type_info; operator =(const type_info;); //type_info类的复制构造函数和赋值运算符是私有的。


  public:


  virtual ~type_info(); //析构函数


  bool operator = =(const type_info;) const; //在type_info类中重载了= =运算符,该运算符可以比较两个对象的类型是否相等。


  bool operator !=(const type_info;)const; //重载的!=运算符,以比较两个对象的类型是否不相等


  const char * name() const; //使用得较多的成员函数name,该函数反回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数


  bool before(const type_info;);};


  因为type_info类的复制构造函数和赋值运算符都是私有的,所以不允许用户自已创建type_info的类,比如type_info A;错误,没有默认的构造函数。唯一要使用type_info类的方法就是使用typeid函数。


   (5)typeid的使用方式


  1)使用type_info类中的name()成员函数反回对象的类型的名称。其方法为:typeid(object).name()其中object是要显示的对象的类型名,该函数反回的名字因编译器而定。这里要注意的就是使用方式一中提到的虚函数类型的问题,即如果有类A,且有虚函数,类B,C,D都是从类A派生的,且都重定义了类A中的虚函数,这时有类A的指针p,再把对象类B的对象的地址赋给指针p,则typeid(p).name()将反回的类型将是A*,因为这里的p表示的是一个指针,该指针是类型为A的指针,所以反回A*,而typeid(*p).name()将反回B,因为指针p是指向类B的对象的,而*p就表示的是类B的对象的类型,所以反回B。


  2)使用type_info类中重载的= =与!=比较两个对象的类型是否相等。使用该方法需要调用类type_info中重载的= =和!=操作符,其使用方法为typid(object1)= =typid(object2);如果两个对象的类型相等则反回1,如果不相等则为0。这种使用方法通常用于比较两个带有虚函数的类的对象是否相等,比如有类A,其中定义有虚函数,而类B,类C,类D,都是从类A派生而来的且重定义了该虚函数,这时有个类A的指针p和p1,按照虚函数的原理,基类的指针可以指向任何派生类的对象,在这时就有可能需要比较两个指针是否指向同一个对象,这时就可以这样使用typeid了,typeid(*p)= =typeid(*p1);这里要注意的是typeid(*p)与typeid(p)是指的不同的对象类型,typeid(p)表示的是p的类型,在这里p是一个指针,这个指针指向的是类A的对象,所以p的类型是A*,而typeid(*p)则不一样,*p表示的是指针p实际所指的对象的类型,比如这里的指针p指向派生类B,则typeid(*p)的类型为B。所以在测试两个指针的类型是否是相等时应使用*p,即typeid(*p)= =typeid(*p1)。如果是typeid(p)= =typeid(p1)的话,则无论指针p和p1指向的什么派生类对象,他们都是相等的,因为都是A *的类型。


3.C++的四种类型转换


 (1)dynamic_cast


     1)dynamic_cast只能将指向派生类对象的基类指针或引用转换为派生类的指针或引用,若用于其他转换则指针为空,引用则抛出异常。此为向下类型转换。


     2)dynamic_cast转换符只能用于指针或者引用。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast转换操作符在执行类型转换时首先将检查能否成功转换,如果能成功转换则转换之,如果转换失败,如果是指针则反回一个0值,如果是转换的是引用,则抛出一个bad_cast异常,所以在使用dynamic_cast转换之间应使用if语句对其转换成功与否进行测试,比如pd=dynamic_cast<D*>(pb); if(pd){…}else{…},或者try{};catch(bad_cast){}


     3)使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。(vc6.0启用方式:project->setting->c/c++->category->c++ Language 下面第二个复选框选中)。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁:


  void menu::build(const File * pfile)


  {


  //......


  else if (typeid(*pfile)==typeid(LocalizedMedia))


  {


  add_option("play");


  }


  }


  唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果。也就是说,如果该函数成功地并且是动态的将 *pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:


  void menu::build(const File * pfile)


  {


  if (dynamic_cast <MediaFile *> (pfile))


  {


  // pfile 是 MediaFile 或者是MediaFile的派生类 LocalizedMedia


  add_option("play");


  }


  else if (dynamic_cast <TextFile*> (pfile))


  {


  // pfile 是 TextFile 是TextFile的派生类


  add_option("edit");


  }


  }


  细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。


 (2)static_cast


     1)完成向上类型转换,即将指向派生类的指针或引用转换为指向同一层次中的一个基类的指针或引用。


     2) 使用例子


[cpp]
#include <iostream>  
class B  
{      
   int i;  
public:     // Conversion constructor.      
   B(int a) : i(a)     { }      
   void display()  
   {  
       std::cout << i;
   }  
};  
int main()  
{          
   B bobj1 = (B)123;     // C-style cast int to B.
   bobj1.display();
   std::cout << '/';      
   bobj2 = B(456);       // Constructor notation B
   bobj2.display();      
   std::cout << '/';          
   B bobj3 = static_cast<B>(789);     // Static_cast.  
   bobj3.display();        
   return 0;  
}  
 (3)reinterpret_cast


    reinterpret_cast操作符代替了大多数其它C风格类型转换的使用。reinterpret_cast将指针转换为其它指针类型、将数字转换为指针或将指针转换为数字。与使用C风格的类型转换一样,当使用reinterpret_cast操作符时,用户应该知道自已要干什么。有时,只有C风格的类型转换才能起作用,但这并不是说从来都不应该使用reinterpret_cast。下例展示了一个用void型指针返回100个字符的缓冲区的简单内存分配程序。Main()函数将返回结果赋值给一个字符型指针。C++的转换规则与C并不相同。在C++的转换规则中,不能隐式地将void*转换为char*,因此,需要进行类型转换。下面使用了reinterpret_cast而不是C语言风格的类型转换。


[cpp]
#include <iostream>  
#include <cstring>  
//Create a buffer. //  
void* getmem()  
{
static char buf[100];      
return buf;  
}  
int main()  
{      
char* cp = reinterpret_cast<char*>(getmem());      
strcpy(cp, "Hello, Woody");      
std::cout << cp;        
return 0;  
}
 (4)const_cast


     刚才所讨论的3种类型转换操作都没有涉及“常量属性”,即不能使用它们移去对象的常量属性。为此,我们要使用const_cast操作符。除了const和volatile关键字之外,它的类型参数必须和对象参数的类型相匹配。
     例如,考虑一个全局的计数器,它描述了对常类对象或其它类型的类对象的操作次数。下面给出了这样的一个例子


[cpp
#include <iostream>  
class A  
{      
   int val;      
   int rptct;  // Number of times the object is reported.
public:      
   A(int v) : val(v), rptct(0) { }      
   ~A()  
   {  
       cout << val << " was reported " << rptct << " times.";  
   }      
   void report() const;  
};  
void A::report() const  
{      
   const_cast<A*>(this)->rptct++;      
   cout << val << endl;  
}  
int main()  
{      
   const A a(123);      
   a.report();      
   a.report();      
   a.report();        
   return 0;  
}

喜欢0 评分0
游客

返回顶部