C++一些注意点之型别转换

       看了一些网上的博客,整理了一些前辈们的思路,I am a burglar!端午节就这么过了,多年以后再回首吧。

       命名的强制类型转换符号的一般形式如下:

                   cast_name<type>(expression);

其中, cast_name可以为dynamic_cast、static_cast、const_cast和reinterpret_cast的一种,type为转换的目标类型,而expression则是被强制转换的值。下面分别讲一下他们的具体含义。

1、const_cast

      设定或去除型别的常数性,亦可去除volatile饰词,除此之外不允许任何转换:

           (1)常量指针被转化成非常量指针,并且仍然指向原来的对象;

        (2)常量引用被转换成非常量引用,并且仍然指向原来的对象;

      (3)常量对象被转换成非常量对象。

#include<iostream>

using namespace std;

int main()
{
     int num = 10;
     const int* ptr = &num;
     //int* ptr1 = ptr;               //A
     int *ptr1 = const_cast<int*>(ptr)
     *ptr1 = 15; 
     cout<<*ptr<<endl;;
     return 0;
}
     A行的赋值将会产生编译错误,不能将const int*转换为int*

2、static_cast

     (1)static_cast用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。static_cast只能提供编译时的安全进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。(如果原先基类指针指向的是基类,那么这么做就会出错)。static_cast不能在兄弟之间进行转换。

     (2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

     (3)把空指针转换成目标类型的空指针;把任何类型的表达式转换成void类型。

范例1:

//class Der:public class Base
 
//当pb确实指向一个Der,则这是使用两种转换方式都没问题
Base *pb = new Der();
Der *pd1 =static_cast<Der*>(pb);
Der *pd2 =dynamic_cast<Der *>(pb);
 
//当pb指向一个Base,则就有问题
Base *pb = newBase();
Der *pd1 =static_cast<Der*>(pb);  //(1)
Der *pd2 =dynamic_cast<Der *>(pb);//(2)
//(1)此时pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的。
//(2)pd2将是一个空指针,即NULL指针。

范例2:

//class Der:public class Base  
//class Der1:public class Base  

//static是无法在兄弟类之间完成转换的
Base *pb = new Der();
Der1 *pd1 = static_cast<Der1 *>(pb);//编译错误

Der1 *pd2 = reinterpret_cast<Der1 *>(pb);//可以完成

Der1 *pd3 = dynamic_cast<Der1 *>(pb);//可以完成

//pd2和pd3并不是真正转到相应的类型了

范例3:

#include<iostream>
#include<cstdio>
using namespace std;
 
class CBaseX{
public:
   int x;
   CBaseX() { x = 10; }
   void foo() { printf("CBaseX::foo()x=%d\n", x); }
};
 
class CBaseY{
public:
   int y;
   int* py;
   CBaseY() { y = 20; py = &y; }
   void bar() { printf("CBaseY::bar()y=%d, *py=%d\n", y, *py);}
};
 
class CDerived :public CBaseX, public CBaseY{
public:
    int z;
};
 
int main()
{
    CDerived *pd = new CDerived();                    //1
    printf("CDerived* pd = %x\n",(int)pd);
 
    CBaseY *pb = pd;                                  //2
    printf("CBaseX* pb = %x\n",(int)pb);
 
    CDerived* pd1 =static_cast<CDerived*>(pb);        //3
    printf("CDerived* pd1 = %x\n",(int)pd1);
 
    CBaseY* pb2 =reinterpret_cast<CBaseY*>(pd);       //4
    printf("CBaseY* pb2 = %x\n",(int)pb2);
 
    CBaseY* pb3 = new CBaseY();                       //5
    printf("CBaseY* pb3 = %x\n",(int)pb3);
 
    CDerived* pd3 =static_cast<CDerived*>(pb3);       //6
    printf("CDerived* pd3 = %x\n",(int)pd3);
 
    CDerived* pd4 =reinterpret_cast<CDerived*>(pb3);  //7
    printf("CDerived* pd4 = %x\n", (int)pd4);
    return 0;
}

运行结果:

C++一些注意点之型别转换_第1张图片

针对上面的结果有如下分析:

        //1:声明了一个指向派生类的指针

        //2:一个简单的赋值,此时注意pb指向基类的指针,如果将派生类的指针赋给给它,此时pb只指向了派生类中基类对象的部分。

         //3:此时将指向基类的指针转换为指向派生类的指针,注意它将pb的指针想前面移了4个位置,表示指向派生类。

        //4:和第三行有同样的结果

        //5:定义了一个指向基类的指针。

        //6:这时还用static_cast转换,将指针往前面挪4个位置,但是由于本身就不存在派生类对象,这样访问前面的地址空间是不安全的。

         //7:未做修改还是指向了原来的空间。

3、reinterpret_cast

      C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。比如:

        int i;

        char *p = "This is aexample.";

        i = reinterpret_cast<int>(p);

  此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,//i 也是指针,整型指针;一个明显的现象是在转换前后没有数位损失。

范例:

int n = 9;
double d = reinterpert_cast<dboule&>(n);
cout<<n<<" "<<d;
//输出:9   5.28421e-308
//因为reinterpert_cast仅仅是复制n的比特位到d,没有进行必要的分析。

4、dynamic_cast

      如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。

       如果是指针转换失败将返回NULL,如果是引用转换失败将抛出bad_cast.

       dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。

       dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_caststatic_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

注:dynamic_cast要有虚函数,否则会编译出错;static_cast则没有这个限制。

注:dynamic_cast不支持反向转换。

 范例1:

#include<iostream>  
  
using namespace std;  
  
class A{  
public:  
   int m_nA;  
};  
  
class B{  
public:  
   int m_nB;  
};  
  
class C:public A,public B{  
public:  
   int m_nC;  
};  
  
int main()  
{  
    C *pC = new C;  
    cout<<"pC= "<<(int) pC<<endl;
    B *pB = dynamic_cast<B*>(pC);   //a
    B *pB1 = static_cast<B*>(pC);   //b
    B *pB2 = pC;                    //c
    cout<<"pB= "<<(int) pB<<endl;
    cout<<"pB1="<<(int) pB1<<endl;
    cout<<"pB2="<<(int) pB2<<endl;
    return 0;  
}  

运行结果:

C++一些注意点之型别转换_第2张图片

分析:这是一个派生类指针向基类指针转换的例子,这种转换只是将派生类指针向下挪几个位置(挪多少与具体成员有关),即指向到基类位置的地方。因为在派生类中既包含基类成员又包含派生类成员。

范例2:

//一个常用的用法
void fun(Base* pB)
{
     Der* pd = dynamic_cast<Der*>(pb);
     if(pd){......}
}

范例3:

#include <iostream>
using namespace std;

class Base
{
public:
	virtual void f() { cout << "Base::f" << endl; }
	void f1(){cout << "Base::f1" << endl;}
private:
	double x;
	double y;
};
class Derived : public Base
{
public:
	virtual void f(){cout << "Derived::f" << endl; }
	virtual void k(){cout << "Derived::k" << endl; }
private:
    double z;
};
class Base1
{
public:
	virtual void g(){ cout << "Base1::g" << endl;}
	void g1(){cout << "Base1::g1" << endl;}
};
class Derived1 : public Base,public Base1
{
public:
	virtual void f(){ cout << "Derived1::f" << endl;}
	virtual void h(){ cout << "Derived1::h" << endl;}
};
void Test1()
{
	// 对于单继承,
	// 如果pD真的指向Derived,用dynamic_cast和static_cast效果相同
	Base *pD = new Derived;
	Derived *pD1 = dynamic_cast<Derived*>(pD);
	pD1->f();
	pD1->k();
	pD1->f1();
	Derived *pD2 = static_cast<Derived*>(pD);
	pD2->f();
	pD2->k();
	pD2->f1();
	// 但是如果pB不是真的指向Derived,则用dynamic_cast则返回NULL,能够更早的禁止error的发生,
	// 如果用static_cast虽然返回的不为NULL,但是运行时可能抛出exception。
	/**///// Error code
	//Base *pB = new Base();
	//Derived *pD3 = static_cast<Derived*>(pB);
	//pD3->f();
	//pD3->k();
	//pD3->f1();
	//Derived *pD4 = dynamic_cast<Derived*>(pB);
	//pD4->f();
	//pD4->k();
	//pD4->f1();
}
void Test2()
{
	// 对于多重继承,
	// 如果pD真的指向的是Derived1,使用dynamic_cast和static_cast都可以转化为Derived1,
	// 但是如果要转化为Base的兄弟类Base1,必须使用dynamic_cast,使用static_cast不能编译。
	Base *pD = new Derived1;
	Derived1 *pD1 = dynamic_cast<Derived1*>(pD);
	pD1->f();
	pD1->h();
	pD1->f1();
	Base1 *pB1 = dynamic_cast<Base1*>(pD);
	pB1->g();
	Derived1 *pD2 = static_cast<Derived1*>(pD);
	pD2->f();
	pD1->h();
	pD2->f1();
	/**///// error can not compiler
	//Base1 *pB2 = static_cast<Base1*>(pD);
	//pB2->g();
	// 当然对于pB不是真的指向Derived1,想要转化为Derived1或Base的兄弟类Base1,情况与Test1中的error情况相同。
}
int main(int argc, _TCHAR* argv[])
{
	Test1();
	Test2();
	return 0;
}



你可能感兴趣的:(C++一些注意点之型别转换)