C++类型转换操作符

Posted 小屁孩的成长日志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++类型转换操作符相关的知识,希望对你有一定的参考价值。

要了解C++类型转换操作符,首先需要先了解一下传统C语言格式的类型转换。传统C语言提供两种形式的转型操作:

  1. (T)expression

  2. T(expression)

两种形式并无差别,只是括号位置不同。我们通常称这种转型方法称为旧式转型。这种转型方法目前依然是合法且可用的。但是它存在以下两种缺陷:

  • 允许将任何类型转换成任何其他类型。例如可以将pointer-to-const-object转型为pointer-to-non-const-object,将pointer-to-base-class-object转型为pointer-to-derived-class-object等操作。下面可以通过实例来看一下就是转型到底有多强大。

 class Base { public: explicit Base():a(0){} virtual void dummy() {} int a; };
class Derived : public Base { public: explicit Derived(): b(0) {} int b; };


示例1:将int类型隐式转换成double类型

 int num1=10, num2=3; double result = ((double)num1)/num2; std::cout << "result=" << result <<std::endl;

示例1的输出结果为

result=3.33333

示例2:将pointer-to-const-object转型为pointer-to-non-const-object

 const Base* pcBase = new Base; Base* pBase = (Base*)pcBase; pBase->a = 1; std::cout << "pcBase->a=" << pcBase->a << ", pBase->a=" << pBase->a << std::endl;

示例2的输出结果为

pcBase->a=1, pBase->a=1

示例3将pointer-to-const-base-class-object转型为pointer-to-derived-class-object

    const Base* pcBase = new Base; Derived* pDerived = (Derived*)pcBase; pDerived->a = 2;    std::cout << "pcBase->a=" << pcBase->a << ", pDerived->a=" << pDerived->a << std::endl;

示例3的输出结果为

pcBase->a=2, pDerived->a=2

示例4:将pointer-to-const-derived-class-object转型为pointer-to-base-class-object

 const Derived* pcDerived = new Derived; Base* pBase = (Base*)pcDerived; pBase->a = 2; std::cout << "pcDerived->a=" << pcDerived->a << ", pBase->a=" << pBase->a << std::endl;

示例4的输出结果为

pcDerived->a=2, pBase->a=2
  • 转型操作语法结构不明显。旧式转型操作有小括号和对象名称组成,很难与在其他代码有明显区分。


C++提供了四种C++格式的转型操作符:

  • const_cast<T>(expression)

  • dynamic_cast<T>(expression)

  • static_cast<T>(expression)

  • reinterpret_cast<T>(expression)

这些操作符的引入除了可以很好的解决了上述旧式语言操作符的缺点,还因为每个转型操作符的目标更加的窄化,使编译器可以更能诊断转型是否被错误运用。

static_cast

static_cast用来强迫隐式转换(implicit conversions)。与传统的C语言转型操作具有相同的功能和相同的限制。不同的是旧式转型操作符还可以去除变量的常量性,新式转型操作符static_cast不可以去除变量的常量性

示例5:将int类型转换为double类型

 int num1=10, num2=3; double result = (static_cast<double>(num1))/num2; std::cout << "result=" << result <<std::endl;

示例5这个操作的功效等同于示例1的。输出结果为

result=3.33333

示例6:将pointer-to-base-class-object转型为pointer-to-derived-class-object

 Base* pBase = new Base; Derived* pDerived = static_cast<Derived*>(pBase); pDerived->a = 1; pDerived->b = 2; std::cout << "pBase=" << pBase << ", pBase->a=" << pBase->a << ", pDerived=" << pDerived << ", pDerived->a=" << pDerived->a << std::endl;

示例6输出结果为

pBase=0x5572e87632c0, pBase->a=1, pDerived=0x5572e87632c0, pDerived->a=1

在大多数情况下,在使用旧式转型操作符的地方,我们都可以使用static_cast来代替

const_cast

const_cast用来改变表达式中的常量性(constness)或变易性(volatileness)。如果用于上述以外的用途,那么转型动作会被编译器拒绝。

示例7:将pointer-to-const-base-class-object转型为pointer-to-derived-class-object

 const Derived cDerived; const Derived* pcDerived = &cDerived; Derived* pDerived = const_cast<Derived*>(pcDerived); std::cout << "cDerived.a=" << cDerived.a << ", pcDerived->a=" << pcDerived->a << ", pDerived->a=" << pDerived->a << std::endl; pDerived->a = 1; std::cout << "cDerived.a=" << cDerived.a << ", pcDerived->a=" << pcDerived->a << ", pDerived->a=" << pDerived->a << std::endl;

示例7的输出结果为

cDerived.a=0, pcDerived->a=0, pDerived->a=0cDerived.a=1, pcDerived->a=1, pDerived->a=1

在这里我们看到,常量对象通过const_cast去除常量性后可以通过代码更改常量对象的值。并且根据打印的结果可以看到更改生效。但是这个更改操作的具体结果是由编译器来决定的,下面。下面可以看一下更改并不生效的示例:

示例8:

 const int num1 = 1; const int* pcNum1 = &num1; int* pNum1 = const_cast<int*>(pcNum1); std::cout << "num1=" << num1 << ", *pcNum1=" << *pcNum1 << ", *pNum1=" << *pNum1 << std::endl; *pNum1 = 7; std::cout << "num1=" << num1 << ", *pcNum1=" << *pcNum1 << ", *pNum1=" << *pNum1 << std::endl; std::cout << "&num1=" << &num1 << ", pcNum1=" << pcNum1 << ", pNum1=" << pNum1 << std::endl;

示例8输出结果为

num1=1, *pcNum1=1, *pNum1=1num1=1, *pcNum1=7, *pNum1=7&num1=0x7ffef260652c, pcNum1=0x7ffef260652c, pNum1=0x7ffef260652c

dynamic_cast

dynamic_cast用来执行继承体系中安全的向下转型操作。使用dynamic_cast指向base class objects的pointers或references转型为指向derived class objects的pointers 或 references,并且可以根据返回的结果或者是否抛出异常来确认转换是否成功。如果转换的对象是指针,转换失败的话会返回空指针。如果转换的对象是引用的话转换失败的话则会抛出异常。

示例9:

 Base* pba = new Derived; Base* pbb = new Base; Derived* pd; pd = dynamic_cast<Derived*>(pba); std::cout << "pba=" << pba << ", pd=" << pd << std::endl; pd = dynamic_cast<Derived*>(pbb); std::cout << "pbb=" << pbb << ", pd=" << pd << std::endl;

示例9中,因为pbb中实际指向是的base对象,因此转型失败。

pba=0x5570bb559eb0, pd=0x5570bb559eb0pbb=0x5570bb559ed0, pd=0

注意:

  • dynamic_cast只能在继承体系中实施转型,要求基类必须具有虚函数。

  • 由于dynamic_cast运行成本较大,需要谨慎使用。

reinterpret_cast

reinterpret_cast允许将任意类型的指针转换成其他任意类型指针,甚至是不相关的类型之间也是可以的。它也允许将整型类型转换成指针类型或者将任意的指针类型转换成整型类型。例如,可以使用reinterpret_cast将char*类型转换成int*类型。

reinterpret_cast是通过重新解释二进制表示来实现的转换操作,是低级别转型。reinterpret_cast操作符的转型结果与操作系统和编译器强相关,故它不具有移植性。

reinterpret_cast的最常用用途是转换"函数指针"类型。例如有个特定类型的函数指针数组:

typedef void (*FuncPtr)(); FuncPtr funcPtrArray[10];

如果你希望将以下函数的一个指针放进上面的数组中

int doSomething();

若没有转型,这做不到!因为doSomething的类型与funcPtrArray声明的不一样。该数组含的函数指针返回值为void,这个却返回int.

funcPtrArray[0] = &doSomething; //错误funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); // OK

函数指针的转型动作并不具备移植性(C++不保证所有的函数指针都能以此方式呈现),某些情况下这样的转型可能会导致不正确的结果。所以应该尽量避免将函数指针转型



参考资料:

  • Effective C++ 条款27:尽量减少做转型动作

  • More Effective C++ 条款2:最好使用C++转型操作符

  • C++ Language Tutorial - Type Casting

以上是关于C++类型转换操作符的主要内容,如果未能解决你的问题,请参考以下文章

C++编程经验:使用C++风格的类型转换

C++类型转换操作符

C++的类型转换

c++类型转换

C++的类型转换

C++强制类型转换操作符 dynamic_cast