C++ 中 的类型转换

类型转换

在 C++ 语言中,某些类型之间有关联。如果两种类型有关联,那么当程序需要其中一种类型的运算对象时,可以用另一种关联类型的对象或值来替代。

隐式转换

隐式转换是指编译器会自动地转换运算对象的类型:

        在大多数表达式中,比 int 类型小的整型值首先提升为较大的整数类型。

        在条件中,非布尔值转换成布尔值。

        初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。

        如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。

        函数调用时也会发生类型转换。

算数转换

整型提升:负责把小整数类型转换成较大的整数类型。对于 bool、char、signed char、 unsigned char、short 和 unsigned short 等类型来说,只要它们所有可能的值都能存在 int 里,它们就会提升成 int 类型。

无符号类型的运算对象:如果一个运算对象时无符号类型、另外一个运算对象时带符号类型,而且其中的无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的,

其他隐式类型转换

数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针。当数组被用作 decltype 关键字的参数,或者作为取地址符(&)、sizeof 及 typeid 等运算符的运算对象时,上述转换不会发生。同样,如果一个引用来初始化数组,上述转换也不会发生。

指针的转换:C++ 还规定了几种其他的指针转换方法,包括常量整数值 0 或者字面值 nullptr 能转换成任意指针类型;指向任意非常量的指针能转换成 void*;指向任意对象的指针能转换成 const void*。

转换成布尔类型:存在一种从算术类型或指针类型向布尔类型自动转换的机制。如果指针或算术类型的值为0,转换结果是 false;否则转换结果是 true。

转换成常量:允许将指向非常量类型的指针转换成指向相应的常量类型的指针,对于引用也是这样。也就是说,如果 T 是一种类型,我们就能将指向 T 的指针或引用分别转换成指向 const T 的指针或引用。

类类型定义的转换:类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。

C++ 中 explicit 的作用

在 C++ 中,explicit 通常用于构造函数的声明中,用于防止隐士转换。当将一个参数传递给构造函数时,如果构造函数声明中使用了 explicit 关键字,则只能使用显示转换进行转换,而不能进行隐式转换。

这种机制可以防止编译器自动执行预期外的类型转换,提高代码的安全性。

例如,考虑下面的代码:

class MyInt{
    public:
        MyInt(int n): num(n) {}
    
    private:
        int num;

}

MyInt fun(MyInt n)
{
    return n;
}

int main()
{
    MyInt a = 100;
    fun(100);
}

注意  MyInt a = 100;这段代码有两个步骤:

1. int 类型的 100 先隐式类型转换为 MyInt的一个临时对象

2. 将隐式类型转换后的临时对象再通过复制构造函数生成 a。

但这样有时候会出现问题,例如在外面是使用fun(100)调用fun函数时,不出出错,但是我们有时候不期望int类型的变量可以调用fun函数。如果我们希望只接受 MyInt 类型的参数,就可以将构造函数声明加上 explicit。

显示转换

static_cast 

用法:static_cast <new_type>(expression)

任何具有明确定义的类型转换,只要不包含底层 const,都可以使用 static_cast。

例如通过将一个运算对象强制转换成 double 类型就能使表达式执行浮点数除法:

int j;
double s;

double s = static_cast<double> (j);

 这种方法没有运行时类型检查来保证转换的安全性:

进行上转换是安全的:把派生类的指针或引用转换成基类表示

进行下行转换,由于没有动态类型检查,所以是不安全的:把基类的指针或引用转换位派生类表示。

const_cast

const_cast 只能改变运算对象的底层 const

常量指针转换为非常量指针,并且仍然指向原来的对象。常量引用被转换为非常量引用,并且仍然指向原来的对象。可以去掉类型 const 或 volatile 属性。

const char *pc;
char *p = const_cast<char *>(pc);
//正确:但通过 p 写值是未定义的行为

对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉 const 性质”。

一旦我们去掉了某个对象的 const 性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获取写权限是合法的行为。然而如果对象是一个常量,再使用 const_cast 执行写操作就会产生未定义的后果。

const_cast 常常用于有函数重载的上下文中。

reinterpret_cast

reinterpret_cast 通常为运算对象的位模式提供较低层次上的重新编译。

可以将整型转换位指针,也可以把指针转换为数组;可以在指针和引用里进行肆无忌惮的转换,但是平台移植性比较差。

int *p;
char *pc = reinterpret_cast<char *>(p);

dynamic_cast

dynamic_cast 在 C++ 中主要应用于父子类层次结构中的安全类型转换,它在运行时执行类型检查,因此相比于 static_cast,它更加安全。

在进行下行转换时,dynamic_cast 具有类型检查(信息在虚函数中)的功能,比 static_cast 更安全。

此外,转换后必须是类的指针、引用或者 void*, 基类要有虚函数,可以交叉转换。

要使用 dynamic_cast 有效,基类至少需要一个虚函数。因为 dynamic_cast 只有在基类存在虚函数表的情况下才有可能将基类指针转化为子类。

dynamic_cast 的底层原理

dynamic_cast 的底层原理依赖于运行时类型信息(RTTI,Runtime Type Information)。

C++ 编译器在编译时为支持多态的类生成 RTTI,它包含了类的类型信息和类层次信息。

我们都知道当使用虚函数时,编译器会为每个类生成一个虚函数表(vtable),并在其中存储指向虚函数的指针。伴随虚函数表的还有 RTTI,这些辅助的信息可以用来帮助我们运行时识别对象的类型信息。

每个多态对象都有一个指向其 vtable 的指针,称为vptr,vtable表中通常关联着 RTTI

 dynamic_cast 就是利用 RTTI 来指向运行时类型检查和安全类型转换,执行过程大概是:

        首先,dynamic_cast 通过查询对象的 vptr 来获取其 RTTI。

        然后,dynamic_cast 比较请求的目标类型与从 RTTI 获得的实际类型。如果目标时实际类型或其基类,则转换成功。

        如果目标类型是派生类,dynamic_cast 会检查类层次结构,以确定转换是合法的。如果在类层次结构中找到了目标类型,则转换成功;否则,转换失败。

        当转换成功时,dynamic_cast 返回转换后的指针或引用

        如果转换失败,对于指针类型,dynamic_cast 返回空指针;对于引用类型,dynamic_cast 会抛出一个 std::bad_cast 异常。

最后,因为 dynamic_cast 依赖于运行时类型信息,它的性能低于其他类型转换操作,而static_cast 时编译器静态转换,编译时期就完成了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值