本文对四种标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast进行了介绍,通过本文应当能够理解这四个类型转换操作符的含义。 在学习转型之前,文章Effective C++ —— 实现(五)中条款27将告诉你应该慎重并少用转型。

内容简介: 
  有四种标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。 
  dynamic_cast:动态类型转换,一般用在父类和子类指针或应用的互相转化; 
  static_cast:静态类型转换,一般是普通数据类型转换(如int m=static_cast<int>(3.14)); 
  reinterpret_cast:重新解释类型转换,很像c的一般类型转换操作; 
  const_cast:常量类型转换,是把cosnt或volatile属性去掉。 
下面将依次对它们进行相对详细地介绍。

主要内容: 
  一、static_cast 
  二、dynamic_cast 
  三、reinterpret_cast 
  四、const_cast 
  五、其它

一、static_cast 
=================================================== 
[用法]:static_cast < type-id > ( expression ) 
[功能] 
  该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。 
[描述]

  支持子类指针到父类指针的转换,并根据实际情况调整指针的值,反过来也支持,但会给出编译警告,它作用最类似C风格的“强制转换”,一般来说可认为它是安全的。
主要有如下几种用法: 
  (a)用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。 
    进行上行转换(把派生类的指针或引用转换成基类表示)是安全的; 
    进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。 
  (b)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。 
  (c)把空指针转换成目标类型的空指针。 
  (d)把任何类型的表达式转换成void类型。 
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。 
[举例] 
  这里,关于static_cast的使用举例,通过与reinterpret_cast的例子进行对比,容易理解,所以参见后面reinterpret_cast的使用举例部分中对static_cast的使用方法。

二、dynamic_cast 
=========================================== 
[用法]:dynamic_cast < type-id > ( expression ) 
[功能] 
  该运算符把expression转换成type-id类型的对象,Type-id必须是类的指针、类的引用或者void *。 
[描述] 
  支持子类指针到父类指针的转换,并根据实际情况调整指针的值,和static_cast不同,反过来它就不支持了,会导致编译错误,这种转换是最安全的转换。 
  (a).如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。 
  (b).dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。 
  (c).在类层次间进行上行(派生类向基类转型)转换时,dynamic_cast和static_cast的效果是一样的; 
  (d).在进行下行(基类向派生类转型)转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。 
[举例] 
  1)在类层次间进行转换 代码如下:

  1. class B{
  2. public:
  3. int m_iNum;
  4. virtual void foo();
  5. };
  6.  
  7. class D:public B{
  8. public:
  9. char *m_szName[];
  10. };
  11.  
  12. void func(B *pb){
  13. D *pd1 = static_cast<D *>(pb);
  14. D *pd2 = dynamic_cast<D *>(pb);
  15. }

这里可见,使用dynamic_cast进行转换,如果出现了把指向父类对象的指针,转换成了子类的指针的时候(安全下行转型),就会返回空值。

  在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针。 
  另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。 

  2)类之间的交叉转换 代码如下:

  1. class A{
  2. public:
  3. int m_iNum;
  4. virtual void f(){}
  5. };
  6.  
  7. class B:public A{
  8. };
  9.  
  10. class D:public A{
  11. };
  12.  
  13. void foo(){
  14. B *pb = new B;
  15. pb->m_iNum = ;
  16.  
  17. D *pd1 = static_cast<D *>(pb); //compile error???实践好象没有编译错误
  18. D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
  19. delete pb;
  20. }

这里,可见,如果出现了交叉转换的情况那么dynamic_cast将会返回空值。

  在函数foo中,使用static_cast进行类交叉转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。

dynamic_cast例子如下:
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class Base
  5. {
  6. public:
  7. virtual ~Base() {}
  8. };
  9. class Derived:public Base
  10. {
  11. public:
  12. void blink() { cout << "Derived blink." << endl; }
  13. };
  14. class Derived1
  15. {
  16. public:
  17. void blink() { cout << "Derived1 blink." << endl; }
  18. };
  19.  
  20. int main()
  21. {
  22. // 使用dynamic_cast,Base 转 Derived 返回null,基类向派生类的安全向下转型
  23. Base* b1 = new Base();
  24. Derived* d = dynamic_cast<Derived*>(b1); // 直接 “Derived* d = b1;” 会报错
  25. if (d == NULL)
  26. cout << "d is nullptr." << endl; //被执行
  27. else
  28. cout << "d is val = " << d << endl;
  29. d->blink(); // 同样被执行,虽然d == NULL,但同时会执行其类型Derived类中的相应函数
  30.  
  31. // 使用static_cast,Base 转 Derived 返回 一个指针值,检测不合法的转型也不会将指针置为NULL
  32. Derived* dd = static_cast<Derived*>(b1);
  33. if (dd == NULL)
  34. cout << "dd is nullptr." << endl;
  35. else
  36. cout << "dd is val = " << dd << endl; //被执行
  37. dd->blink(); //被执行
  38.  
  39. // Derived 转 Base,自然语法下就能完成派生类向基类转型
  40. Derived* ddd = new Derived();
  41. Base* bb = ddd; // 不用使用转换
  42. if (bb == NULL)
  43. cout << "bb is nullptr." << bb << endl;
  44. else
  45. cout << "bb is val = " << bb << endl;
  46.      // bb->blink(); 基类没有此方法,报错
  47.  
  48. // cross跨 类体系 的转型
  49. Derived1* d1 = dynamic_cast<Derived1*>(b1);
  50. if (d1 == NULL)
  51. cout << "d1 is nullptr." << d1 << endl;
  52. else
  53. cout << "d1 is val = " << d1 << endl;
  54. d1->blink();
  55. return ;
  56. }
  57.  
  58. //output ----------------------------------------------------------------------------------------------
  59. d is nullptr.
  60. Derived blink.
  61. dd is val = 0x361708
  62. Derived blink.
  63. bb is val = 0x3622e8
  64. d1 is nullptr.
  65. Derived1 blink.
  66. 请按任意键继续. . .

三、reinterpret_cast 
===================== ====================================
[用法]:reinterpret_cast<type-id> (expression) 
[功能] 
  它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。 
[描述] 
  reinterpret_cast是C++里的强制类型转换符,支持任何转换,但仅仅是如它的名字所描述的“重解释”而已。也就是说:操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。例如:

  1. int *n= new int;
  2. double *d=reinterpret_cast<double*> (n);

在进行计算以后, d包含无用值.这是因为reinterpret_cast仅仅是复制n的比特位到d, 没有进行必要的分析。 
  reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的(C++编程思想中的原话)。将static_cast和reinterpret_cast对比一下进行解释,比较容易理解:static_cast 和 reinterpret_cast 操作符修改了操作数类型,但是reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。例如:

  1. int n=;
  2. double d=static_cast<double>(n);

  上面的例子中, 我们将一个变量从int转换到double。这些类型的二进制表达式是不同的,所以将整数9转换到双精度整数9,static_cast需要正确地为双精度整数d补足比特位。其结果为 9.0。而reinterpret_cast 的行为却不同:

  1. int n=;
  2. double d=reinterpret_cast<double & >(n);

  这里, 和static_cast不同,在进行计算以后, d包含无用值。这是因为reinterpret_cast仅仅是复制n的比特位到d, 没有进行必要的分析. 因此, 需要谨慎使用 reinterpret_cast。 
[举例] 
  这个例子,将static_cast和reinterpret_cast对比进行测试,具体的输出参见其中的注释。

  1. #include <iostream>
  2. using std::cout;
  3. using std::endl;
  4. class CBaseX
  5. {
  6. public:
  7. int x;
  8. CBaseX() { x = ; }
  9. void foo() { printf("CBaseX::foo() x=%d/n", x); }
  10. };
  11. class CBaseY
  12. {
  13. public:
  14. int y;
  15. int* py;
  16. CBaseY() { y = ; py = &y; }
  17. void bar() { printf("CBaseY::bar() y=%d, *py=%d/n", y, *py);}
  18. };
  19. class CDerived : public CBaseX, public CBaseY
  20. {
  21. public:
  22. int z;
  23. };
  24.  
  25. int main(int argc, char *argv[])
  26. {
  27. float f = 12.3;
  28. float* pf = &f;
  29.  
  30. //基本类型的转换
  31. cout<<"=================Basic Cast================="<<endl;
  32. //======static cast<>的使用:
  33. int n = static_cast<int>(f); //成功编译
  34. cout<<"n is :"<<n<<endl;//n = 12
  35. //int* pn = static_cast<int*>(pf);//编译错误,指向的类型是无关的,不能将指针指向无关的类型
  36. void* pv = static_cast<void*>(pf);//编译成功
  37. int* pn2 = static_cast<int*>(pv);//成功编译, 但是 *pn2是无意义的内存(rubbish)
  38. cout<<"pf is:"<<pf<<",pv is:"<<pv<<",pn2 is:"<<pn2<<endl;//三者值一样
  39. cout<<"*pf is:"<<*pf<<",*pn2 is:"<<*pn2<<endl;//pf=12.3,pn2是无用值,注意无法使用"*pv"因为编译错。
  40.  
  41. //======reinterpret_cast<>的使用:
  42. //int i = reinterpret_cast<int>(f);//编译错误,类型‘float’到类型‘int’的转换无效.
  43. //成功编译, 但是 *pn 实际上是无意义的内存,和 *pn2一样
  44. int* pi = reinterpret_cast<int*>(pf);
  45. cout<<"pf is:"<<pf<<",pi is:"<<pi<<endl;//值一样
  46. cout<<"*pf is:"<<*pf<<",*pi is:"<<*pi<<endl;//pi是无用值,和pn2一样。
  47.  
  48. //对象类型的转换
  49. cout<<"=================Class Cast================="<<endl;
  50. CBaseX cx;
  51. CBaseY cy;
  52. CDerived cd;
  53.  
  54. CDerived* pD = &cd;
  55. CBaseX *pX = &cx;
  56. CBaseY *pY = &cy;
  57. cout<<"CDerived* pD = "<<pD<<endl;
  58.  
  59. //======static_cast<>的使用:
  60. CBaseY* pY1 = pD; //隐式static_cast转换
  61. //不一样是因为多继承,pD还要前移动以便也指向CBaseX.
  62. cout<<"CDerived* pD = "<<pD<<",CBaseY* pY1 = "<<pY1<<endl;//pY1=pD+4!!!!!!
  63.  
  64. //CDerived* pD1 = pY1;//编译错误,类型 ‘CBaseY*’ 到类型 ‘CDerived*’ 的转换无效
  65. CDerived* pD1 = static_cast<CDerived*>(pY1);//成功编译
  66. cout<<"CDerived* pD1 = "<<pD1<<endl;//现在 pD1 = pD
  67.  
  68. //pX = static_cast<CBaseX*>(pY);//编译错误,从类型 ‘CBaseY*’ 到类型 ‘CBaseX*’ 中的 static_cast 无效。
  69. pD1 = static_cast<CDerived*>(pY);//竟然可以编译通过!!!!!!
  70. cout<<"CDerived* pD1 = "<<pD1<<",CBaseY *pY = "<<pY<<endl;//现在 pD1 = pY-4
  71. //======reinterpret_cast<>的使用:
  72. CBaseY* pY2 = reinterpret_cast<CBaseY*>(pD);// 成功编译, 但是 pY2 不是 CBaseY*
  73. cout<<"CDerived* pD = "<<pD<<",CBaseY* pY2 = "<<pY2<<endl;//pY2=pD!!!!!!
  74.  
  75. //======通过void的转换注意:
  76. CBaseY* ppY = pD;
  77. cout<<"CDerived* pD = "<<pD<<",CBaseY* ppY = "<<ppY<<endl;//ppY = pD + 4
  78.  
  79. void* ppV1 = ppY; //成功编译
  80. cout<<"CBaseY* ppY = "<<ppY<<",void* ppV1 = "<<ppV1<<endl;//ppV1 = ppY
  81.  
  82. //CDerived* ppD2 = ppV1;//编译错误,类型‘void*’ 到类型 ‘CDerived*’的转换无效
  83. CDerived* ppD2 = static_cast<CDerived*>(ppV1);
  84. cout<<"CDerived* ppD2 = "<<ppD2<<endl;//ppD2 = ppY, 但是我们预期 ppD2 = ppY - 4 = pD
  85. //ppD2->bar();//系统崩溃,段错误
  86. return ;
  87. }

这里,需要注意的地方是:

  *第63行中基类指针pY1被赋予子类指针pD后,pY1=pD+4而不是pD,因为pD是多继承,pD还要前移动以便也指向CBaseX.内存布局大致如下: 
       +CDerived------------------+ 
       |   +CBase X--------+      |\ 
       |   |  int x        |      | 4 bytes 
       |   +---------------+      |/ 
       |                          | 
       |   +CBase Y--------+      | 
       |   |  int y,*py    |      | 
       |   +---------------+      | 
       +--------------------------+ 
  *第69行和70行的可以将父类指针用static_cast强制转换成子类指针,但是两个无关的类的指针之间却不能转换。 
  *第74行中使用reinterpret_cast将子类指针强制转换赋给父类指针后,却没有像static_cast那样将父类指针位置调整以指向正确的对象位置,这样导致虽然两者值是一样的,但是父指针所指向的内容却不是父对象了。 
  *第76行之后使用void将子类转换成父类再转回子类,却无法使用了。 
  因为任何指针可以被转换到void*,而void*可以被向后转换到任何指针(对于static_cast<> 和 reinterpret_cast<>转换都可以这样做),如果没有小心处理的话错误可能发生。一旦我们已经转换指针为void*,我们就不能轻易将其转换回原类所以使用void转换的时候一定要小心。在上面的例子中,从一个void* 返回CDerived*的唯一方法是将其转换为CBaseY*然后再转换为CDerived*。但是如果我们不能确定它是CBaseY* 还是 CDerived*,这时我们不得不用dynamic_cast<> 或typeid[2](dynamic_cast<>需要类成为多态,即包括“虚”函数,并因此而不能成为void*)。 
[其它] 
  dynamic_cast<>,从另一方面来说,可以防止一个泛型CBaseY* 被转换到CDerived*。

四、const_cast 
====================================================== 
[用法]:const_cast<type_id> (expression) 
[功能] 
  该运算符用来修改类型的const或volatile属性。除了const或volatile修饰之外,type_id和expression的类型是一样的。 
[描述] 
  const_cast剥离一个对象的const属性,允许对常量进行修改。 
  (a).常量指针被转化成非常量指针,并且仍然指向原来的对象; 
  (b).常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。 
  Voiatile和const类似。参见后面的例子可以了解更多信息。 
[举例] 

  给出的源代码如下:

  1. #include <iostream>
  2. using std::cout;
  3. using std::endl;
  4.  
  5. class CTest
  6. {
  7. public:
  8. CTest(int i){m_val = i;cout<<"construction"<<m_val<<endl;}
  9. ~CTest(){cout<<"destructionn"<<endl;}
  10. void SelfAdd(){m_val++;};
  11. int m_val;
  12. };
  13.  
  14. int main(int argc, char *argv[])
  15. {
  16. const int ic = ;
  17. //int cc = const_cast<int>(ic);//编译错误
  18. int cc = const_cast<int&>(ic);
  19. cout<<cc<<endl;//输出100
  20. //const_cast<int &>(&ic)=200;//编译错误,从类型 ‘const int*’ 到类型 ‘int&’ 中的 const_cast 无效
  21. const_cast<int &>(ic)=;
  22. cout<<ic<<endl;//输出100
  23. cout<<*(&ic)<<endl;//输出100
  24. //int *pc = &ic;//编译错误,从类型 ‘const int*’ 到类型 ‘int*’ 的转换无效
  25. const int *pc=&ic;
  26. //const_cast<int &>(pc)=200;//编译错误,从类型 ‘const int**’ 到类型 ‘int*’ 中的 const_cast 无效
  27. const_cast<int &>(ic)=;
  28. //printf("%d,%d/n", ic, *pc);
  29. cout<<ic<<','<<*pc<<endl;//100,200
  30. //int *ppc = const_cast<int*>(ic);//编译错误
  31. int *ppc = const_cast<int*>(&ic);
  32. *ppc = ;
  33. cout<<ic<<','<<*ppc<<endl;//100,300
  34.  
  35. const CTest test();
  36. CTest test2();
  37. //test = test2;//编译错误,无法给常量赋值
  38. const_cast<CTest &>(test)= test2;
  39. cout<<test.m_val<<endl;//输出1050
  40. }

  这里,结果输出参见每行代码相应的注释。根据结果可知:凡是对结构体或类进行这个转换,都是成功的,但对char,short等基本类型的转换,通过直接打印变量显示其值都是不成功的,但是通过指针却能显示出修改之后的值。

  通过对代码进行反汇编,可知,虽然本身我们没有使用优化,但系统还是对ic这个const进行了预编译般的替换,将它替换成“64h”(十六进制的64就是十进制的100),这肯定不是一般用户想要的结果,如果它不是一个C++的规范,应该算是个C++的bug吧。 
[其他] 注意, 
  (1)操作对象 
  const_cast操作的对象必须是pointer, reference, nor a pointer-to-data-member type,如下代码是错误的:

  1. const int a = ;
  2. int aa = const_cast<int>(a);

  而使用引用的方式,如下却是正确的:

  1. const int a = ;
  2. int aa = const_cast<int&>(a);

  (2)可能的误解 
  可能上面的描述有误解的地方,根据参考资料中的一个评论,说:const_cast只能修改变量的常引用的const属性,和变量的常指针的const属性,还有对象的const属性。要想改变常量本身的值是不可能的,也就是说,你改变的是引用的const属性,而不是常量本身的const属性。估计 const int ic = 100; 定义的时候就已经将这个基础类型对象放入常量符号表里面了,永远不会改变它的值。

五、其它 
===================== ===========================
做为一个对前面所说的四种类型转换操作符的补充,对它们之间的区别大致进行说明一下,如下: 
1,static_cast和dynamic_cast的对比: 
  1)static_cast在编译期间发现错误。 
对于基本类型,它不允许将一种类型的指针指向另一种类型,所以如下代码是错误的:

  1. float f = 12.3;
  2. float* pf = &f;
  3. int* pn = static_cast<int*>(pf);//编译错误,指向的类型是无关的,不能将指针指向无关的类型

对于复合类型(例如类),它允许转换子对象地址赋值给父指针,也允许转换父对象地址赋值给子指针,但是不允许两个无关的类之间的转换,所以如下是错误的:

  1. CBaseX *pX = &cx;
  2. CBaseY *pY = &cy;
  3. pX = static_cast<CBaseX*>(pY);//编译错误,从类型 ‘CBaseY*’ 到类型 ‘CBaseX*’ 中的 static_cast 无效。

  2)dynamic_cast在运行期间发生错误,它只允许它允许转换子对象地址赋值给父指针,其它情况都返回空。 如:

  1. B *pb = new B;
  2. D *pd = dynamic_cast<D *>(pb); //pd is NULL
  3. delete pb;

2,static_cast,dynamic_cast和reinterpret_cast之间的对比: 
  1)static_cast和dynamic_cast可以执行指针到指针的转换,或实例本身到实例本身的转换,但不能在实例和指针之间转换。static_cast只能提供编译时的类型安全,而dynamic_cast可以提供运行时类型安全。举个例子:

  1. class A { ... };
  2. class B:public A { ... };
  3. class C { ... };
  4.  
  5. //上面三个类A是基类,B继承A,C和A,B没有关系
  6. //假设有一个函数
  7. void function(A& a);
  8. //现在有一个对象是B的实例b,一个C的实例c
  9. function(static_cast<A&>(b); //通过编译
  10. function(static_cast<A&>(c)) //不能通过编译,因为在编译的时候编译器已经知道C和A的类型不符,因此static_cast可以保证安全。

  2)reinterpret_cast可以转换任意一个32bit整数,包括所有的指针和整数。可以把任何整数转成指针,也可以把任何指针转成整数,以及把指针转化为任意类型的指针,威力最为强大!但不能将非32bit的实例转成指针。总之,只要是32bit的东东,怎么转都行!对于刚刚说的例子,我们修改如下:

  1. //先把C转成类型B
  2. B& ref_b = reinterpret_cast<B&> c;
  3. function(static_cast<A&>(ref_b)) //通过编译
  4. //因为从编译器的角度来看,在编译时并不能知道ref_b实际上是c!
  5. //function(dynamic_cast<A&>(ref_b))编译时能过,但在运行时就失败了,因为dynamic_cast在运行时检查了ref_b的实际类型,这样怎么也骗不过去了。

  在应用多态编程时,当我们无法确定传过来的对象的实际类型时使用dynamic_cast,如果能保证对象的实际类型,用static_cast就可以了。至于reinterpret_cast很象c语言那样的暴力转换。

参考资料: 
  http://zhidao.baidu.com/question/81318972.html 
  http://blog.csdn.net/guogangj/article/details/1545119 
  http://zhidao.baidu.com/question/212970514.html 
  http://blog.csdn.net/deyili/article/details/5354242

以上是从网上搜集的,以及根据自己的理解对C++中四种操作符号的总结,如有不准确的地方,感谢读者的告知。^_^ 
作者:QuietHeart 
Email:quiet_heart000@126.com 
日期:2011年7月12日

C++新式转型的更多相关文章

  1. C++ 之 新式转型操作符

    四种新式转型: const_cast.dynamic_cast.reinterpret_cast.static_cast!! 1.const_cast  :  去除常量性 2.dynamic_cast ...

  2. 新式转型操作符[条款9] --《C++必知必会》

    在旧式转型(cast)下面隐藏着一些见不得人的.鬼鬼祟祟的东西.他们的语法形式使其在一段代码中通常很难引起人们的注意,但它们可能会搞一些可怕的破坏活动,就好比你冷不丁被一个恶棍猛击一拳似的.让我们阐明 ...

  3. C++中的四种转型操作符

    在具体介绍C++中的四种转型操作符之前,我们先来说说旧式转型的缺点: ①它差点儿同意将不论什么类型转换为不论什么其它类型,这是十分拙劣的.假设每次转型都可以更精确地指明意图,则更好. ②旧式转型难以辨 ...

  4. C++提供的四种新式转换--const_cast dynamic_cast reinterpret_cast static_cast

    关于强制类型转换的问题,许多书都讨论过,写的最具体的是C++之父的<C++的设计和演化>. 最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_c ...

  5. C++ 转型动作 尽量避免 以及 那些意想不到的威胁

    看完EffectiveC++的关于转型的章节,顿时认为周围的代码都处在悬崖边上~~ C的旧式转型:inta = 10; double b = (double)a; 对于C++的四种转型函数, cons ...

  6. 读书笔记_Effective_C++_条款二十七:尽量少做转型动作

    有关转型的几种做法,已经在早些的博客中写过了.这里先简单回顾一下,再讲一讲effective中对之更深入的阐述. 转型可以按风格可以分成C风格转型和C++风格转型两大类,C风格转型很容易看到,因为我们 ...

  7. Effective C++ Item 27 少做转型操作

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie todo Item34 旧式转型 (T) expression 或 T (expressio ...

  8. 《C++必知必会》学习笔记

    转载:http://dsqiu.iteye.com/blog/1734640 条款一 数据抽象 抽象数据设计遵循步骤:(1)为类型取一个描述性的名字.(2)列出类型所能执行的操作,不要忘了初始化(构造 ...

  9. Effective C++笔记:实现

    条款26:尽可能延后变量定义式的出现时间 博客地址:http://www.cnblogs.com/ronny/ 转载请注明出处! 有些对象,你可能过早的定义它,而在代码执行的过程中发生了导常,造成了开 ...

随机推荐

  1. Android问题-“signaturs do not match the previously installed version”

    问题现象:电脑上的XE10.2中写代码,F9后,提示“signaturs do not match the previously installed version;” 问题原因:签名与以前安装的版本 ...

  2. 应聘Java笔试时可能出现问题及其答案

    有感:应聘Java笔试时可能出现问题及其答案 huij 前段时间因为要参加一个笔试,在准备期间在网上找到了两条关于笔试题目的文章,其中一篇为<<有感:应聘Java笔试时可能出现问题> ...

  3. 利用jdbc连接池(利用jdni)

    简介 前段时间用jdbc连接池,在这里记录下 1.利用jdni配置数据源 在Web项目的META-INF文件夹中新建context.xml文件,内容为: <?xml version=" ...

  4. 【C#】Event事件的订阅和发布

    学习笔记:学习了委托和事件的用法.事件是一种特殊的委托,记录下事件从订阅到发布的流程. 学习资料源于视频:http://www.maiziedu.com/course/510-6891/ 新建一个De ...

  5. Userdata

    我们首先关心的是如何在Lua中表示数组的值.Lua为这种情况提供专门提供一个基本的类型:userdata.一个userdatum提供了一个在Lua中没有预定义操作的raw内存区域. Lua API提供 ...

  6. kettle的报错解决机制

    在kettle执行的过程中,如果遇到错误,kettle会停止运行.在某些时候,并不希望kettle停止运行,这时候可以使用错误处理(Step Error Handling).错误处理允许你配置一个步骤 ...

  7. 事件总线demo

    经过几天的努力拜读大牛高手文章,终于对事件总线有所了解,特此记录下来,以免忘记 1.定义相关的接口: A  事件接口 public interface IDomainEvent { DateTime ...

  8. 【Java面试题】51 什么时候用assert。

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制. 在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保 ...

  9. CentOS7忘记root密码的解决方法

    开机启动centos 7.0,看到如下画面,选择下图选单,按"e"键 在下图linux16行中,将ro这两个字母修改为rw init=/sysroot/bin/sh 修改结果如下图 ...

  10. 数据结构 http://www.cnblogs.com/sun-haiyu/p/7704654.html

    数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图“最好情况”所示,此时树 ...