C++中 static_cast,dynamic_cast,const_cast的区别

文章参考了这篇博文

C++中的类型转换分为:隐式类型转换和显式类型转换

隐式类型转换:

又称为“标准转换”,包括以下几种情况:

(1)算术转换(Arithmetic conversion): 在混合类型的算术表达式中,最宽的数据类型成为目标转换类型。

int iVal = 3;
double dval = 3.14159;

ival+dval;   //ival被提升为double类型

(2)一种类型的表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型

   

int *pi = 0;  // 0被转化为int *类型
ival = dval;  // double -> int

   (3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型

    

double sqrt(double);

cout<<"The square root of 2 is"<

   (4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标类型转化为函数的返回类型

    

double difference(int ival1,int ival2)
{
    return ival1-ival2;
    //返回值被提升为double类型
}

显式类型转换

又被称为“强制类型转换”(cast)

C 风格: (type-id)

C++风格:static_cast、dynamic_cast、reinterpret_cast和const_cast.

关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++之父的《C++的设计和演化》。最好的解决方法就是不要使用C风格的强制类型转化,而是使用标准C++的类型转换符。下面对他们一一进行介绍:

static_cast:

     用法:static_cast(expression)

     说明:该运算符把expression转化为type-id类型,但没有运行时类型检查来保证转换的安全性。

   为什么需要static_cast强制转换?

    情况1:void指针 -> 其他类型指针

    情况2:改变通常的标准转换

    情况3:避免出现可能多种转换的歧义

 它主要有如下几种用法:

    (1) 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换是安全的(把子类的指针或引用转换为基类表示);进行下行转换(把基类指针或引用转换为子类的),由于没有动态类型检查,所以是不安全的。

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

(3)把void类型转换为目标类型的指针(不安全

  (4)把任何类型转化为void类型

注意:static_cast不能转换掉expression的const、volitale或__unaligned属性。

dynamic_cast:

用法:danamic_cast (expression)

 说明:该运算符把expression转换为type-id的对象。type-id必须是类的指针、类的引用或者void*;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。

 

为什么要用dynamic_cast强制转换?

     简单的说,当无法使用virtual函数的时候

典型案例:

 W公司提供给我们一个类库,其中提供一个类Employee    以头文件Employee.h 和类库 .lib分发给用户,显然我们无法得到类的实现的源代码。

//Employee.h
class Employee
{
    public:
        virtual int salary();
};

class Manager: public Employee
{
   public:
        int salary();
};

class Programmer: public Employee
{
   public:
        int salary();
}

公司在开发的时候建立有如下类:

class MyCompany
{
    public:
        void payroll(Employee *pe);
    // ...
};

void MyCompany::payroll(Employee *pe)
{
    //do something
}

但是开发到后期,我们希望能增加一个bonus()的成员函数到W公司提供的类库中。

假设我们知道源代码的情况下,很简单,增加虚函数:

//Employee.h
class Employee
{
   public:
     virtual int salary();
     virtual int bonus();
};

class Manager: public Employee
{
    public:
      int salary();
};

class Programmer:public Employee
{
    public:
      int salary();
      int bonus();
 };

//Employee.cpp
...
int Programmer::bonus()
{
   //...
}

//payroll 通过多态来调用bonus()
class MyCompany
{
    public:
        void payroll(Employee *pe);
    //...
};

void MyCompany::payroll(Employee *pe)
{
    //do something
    // pe->bonus();
}

但是现在的情况是,我们并不能修改源代码,怎么办?dynamic_cast华丽登场了!

在Employee.h 中增加bonus()声明,在另一个地方定义此函数,修改调用函数payroll()    . 重新编译,OK!

//Employee.h
class Employee
{
   public:
      virtual int salary();
};

class Manager: public Employee
{
   public:
      int salary();
};

class Prigrammer: public Employee
{
    public:
       int salary();
       int bonus();  //直接在这里扩展
};

//somewhere.cpp

int Programmer::bonus()
{
    //define ...
}

class MyCompany
{
   public:
      void payroll(Employee *pe);
      //
};

void MyCompany::payroll(Employee *pe)
{
   Programmer *pm = dynamic_cast(pe);
   //如果pe实际指向一个Programmer对象,danamic_cast成功,并且开始指向Programmer对象起始处
   if(pm)
   {
         //call Programmer::bonus()
   }
// 如果pe不是实际指向Programmer对象,dynamic_cast失败,并且pm = 0
   else
   {
       //use Employee member functions
   }
}
   

dynamic_cast主要用于类层次之间的上行转换和下行转换,还可以用于类之间的交叉转换。

在类层次间进行上下转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

class Base
{
    public:
        int m_iNum;
        virtual void foo();
};

class Derived:public Base
{
  public:
       char *m_szName[100];
};

void func(Base *pb)
{
    Derived *pd1 = static_cast(pb);
    Derived *pd2 = dynamic_cast (pb);
}

在上面的代码段中,如果pb实际指向一个Derived类型的对象,pd1和pd2是一样的,并且对这两个指针执行Derived类型的任何操作都是安全的;如果pd实际指向一个Base类型的对象,那么pd1将是一个指向该对象的指针,对它进行Derived类型的操作将是不安全的,而pd2将是一个空指针(因为dynamic_cast失败)

另外要注意:Base要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有定义了虚函数的类才有虚函数,没有定义虚函数的类是没有虚函数表的。

你可能感兴趣的:(c++)