您的位置:首页 > 编程语言 > C语言/C++

C++中的显式类型转换

2016-06-22 20:39 513 查看
0. 前言

C++中类型的转换分位两种:隐式和显式转换;隐式转换发生在不经意间,比如int类型和float类型相加时,int类型会隐式转换为float类型,然后再相加运算。而今天主要介绍标准C++中的四个显式类型转换运算符:static_cast、dynamic_cast、const_cast和reinterpret_cast。本篇博文转自:果冻想,并做了细微的修改。注意:static_cast和dynamic_cast都可以对类间的类型进行转换,前者在向下转换时不在运行期进行类型检查,后者在运行期进行类型检查,较之安全,且后者可用于多态类型之间的转换,前者主要用于非多态类型间的转换。

1. static_cast

static_cast的转换格式:static_cast <new_type> (expression)

将expression转换为new_type类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。主要在以下几种场合中使用:

用于类层次结构中,基类和子类之间指针和引用的转换;

当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;

当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
把void指针转换成目标类型的指针,是及其不安全的;
注:static_cast不能转换掉expression的const、volatile和__unaligned属性。

2. dynamic_cast
dynamic_cast < new_type > (expression )

new_type - 必须是类的指针、类的引用或者void*

expression -将expression转换为new_type类型
注意:(1)如果转换成功,dynamic_cast返回new_type类型的值。

           (2)如果转换失败且new_type是指针类型,则返回对于类型的空指针。

           (3)如果转换失败且new_type是引用类型,则抛出异常std::bad_cast。

dynamic_cast主要应用于类层次之间的向上转换,向下转换或者交叉转换。向上转换时dynamic_cast和static_cast效果一样;向下转换时,dynamic_cast进行类型检查,比static_cast安全。在多态类型间转换主要使用dynamic_cast,因为多态类型提供了运行时的信息。

情形1:最常见的向上转换——B继承自A,B转换为A为向上转换,安全

#include <iostream>

class A
{

};

class B : public A
{

};

void main(){
B *b = new B();

A *a1 = b; // 隐式转换,常用
A *a2 = dynamic_cast<A*> (b);// 显示转换

std::cin.get();
}

情形2:多重继承之间的向上转换

类A派生出B,类B派生出类C,需要使用dynamic_cast

#include <iostream>

class A
{

};

class B : public A
{

};
class C : public B
{

};

void main(){
C *c = new C();

// 使用dynamic_cast——显示转换
B *_b = dynamic_cast<B*> (c);// OK
A *_a = dynamic_cast<A*> (c);// OK

// 通常情况下向上转换可使用默认的隐式转换,向下转换时需要使用显式转换关键字
// 向上转换——隐式转换(常用)
B *b = c; // OK
A *a1 = b; // OK
A *a2 = c;// OK

std::cin.get();
}

注意:向上转换默认被称为隐式转换,加不加dynamic_cast都可以,加了是显式转换;而上述两种情形的向上转换也可使用static_cast,效果是一样的,代码如下:

#include <iostream>

class A
{

};

class B : public A
{

};

void main(){
// 向上转换

// 隐式
A *a_ = new B();

// 显式
A *a = static_cast<A*>(new B());
A *_a = dynamic_cast<A*> (new B());

std::cin.get();
}

情形3:转换为void*

#include <iostream>
class A
{
public:
virtual void operation(){}
};

void main(){
A *a = new A();
void *pV = dynamic_cast<void *>(a); // pV points to an object of A
void *pV2 = static_cast<void *>(a);

std::cin.get();
}

在类A必须包含虚函数,为什么呢?因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。

情形4:向下转换

派生类赋值给基类对象的指针为向上转换,也是隐式转换,是安全的,加不加dynamic_cast或static_cast都可以;但是向下转换时,是不安全的,必须要显式转换且使用dynamic_cast。

#include <iostream>
class A{
public:
virtual void operation(){}
};

class B : public A{
public:
virtual void operation(){}

};

void main(){

A *a2A = new A();
B *b2A = dynamic_cast<B*>(a2A);// b2A points to a A not a B, now b2A is NULL

A *a2B = new B();
B *b2B = dynamic_cast<B*>(a2B);// ok: b2B actually points to a B

std::cin.get();
}

使用dynamic_cast需要注意的问题:

对于一些复杂的继承关系来说,使用dynamic_cast进行转换存在一些陷阱;比如,有如下结构的类继承关系:



D类型可以安全的转换为B和C类型,但是D类型要是直接转换为A类型呢?下面以new_type为指针类型,如果转换失败,则出现NULL;同样的如果new_type为类对象的引用类型,那么如果转换失败,则会抛出异常;这里以指针类型为例:

#include <iostream>
// 注意:当发生多态公有继承时,通常需要设置抽象基类的析构函数为virtual,即虚函数
// 这里为理解方便,省略了这些内容,只保留核心内容
class A{
public:
virtual void operation() = 0;
};

class B : public A{
public:
virtual void operation(){
std::cout << "operation in B" << std::endl; }
};

class C : public A{
public:
virtual void operation(){
std::cout << "operation in C" << std::endl; }
};

class D : public B, public C{
public:
virtual void operation(){
std::cout << "operation in D" << std::endl; }
};

void main()
{
D *d = new D();
A *a = dynamic_cast<A*> (d);// ERROR 基类A不明确
a->operation();// 程序崩溃

std::cin.get();
}如果进行上面的直接转,你将会得到一个NULL的pA指针;这是因为,B和C都继承了A,并且都实现了虚函数operation,导致在进行转换时,无法进行抉择应该向哪个A进行转换。正确的做法是:

void main()
{
D *d = new D();
B *b = dynamic_cast<B*> (d);
A *a = dynamic_cast<A*> (b);
b->operation();// operation in D
}

对于多重继承的情况,从派生类向父类进行转换时,需要特别注意;比如有如下情况:



现在,你拥有一个A类型的指针,它指向E实例,如何获得B类型的指针,指向E实例呢?如果直接进行转的话,就会出现编译器出现分歧,不知道是走E->C->B,还是走E->D->B。对于这种情况,我们就必须先将A类型的指针进行下行转换,获得E类型的指针,然后,在指定一条正确的路线进行上行转换。

上面就是对于dynamic_cast转换的一些细节知识点,特别是对于多重继承的情况,在实际项目中,很容易出现问题。

3. const_cast

转换格式为:const_cast<new_type>(expression)

cons_cast用来将类型的const,volatile和_unaligned属性移除。常量指针/常量引用分别被转换为非常量指针/非常量引用,并仍然指向或者引用原来的对象。

#include <iostream>

class CA{
public:
CA() : _iA(10){}
int _iA;
};

void main()
{
const CA *pA = new CA();
// pA->_iA = 100; // ERROR
CA *pB = const_cast<CA *>(pA);
pB->_iA = 100; // OK
// Now the pA and the pB points to the same object
std::cout
<< pA->_iA << ", "
<< pB->_iA
<< std::endl;

const CA &a = *pA;
// a._iA = 200;// error
CA &b = const_cast<CA &> (a);
b._iA = 200;
// Now the a and the b reference to the same object
std::cout
<< a._iA << ", "
<< b._iA
<< std::endl;

std::cin.get();
}

注:你不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。

4. reinterpret_cast

reinterpret_cast转换格式为:reinterpret_cast<new_type >( expression )

允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: