美文网首页
c++四种强制类型转化

c++四种强制类型转化

作者: 王王王王王景 | 来源:发表于2019-08-01 16:00 被阅读0次

c++四种强制类型转化

https://www.cnblogs.com/Allen-rg/p/6999360.html

https://www.cnblogs.com/cauchy007/p/4968707.html

0、前言

在C语言中,有隐式类型的转换和强制类型的转换. 隐式类型的转换:相关类型的转换,即意义相似的类型. 强制类型的转换:不类型类型的转换,比如:指针和整型.
例如:

int i = 20;
double d = i;//隐式类型的转换.
int* p = (int*)i;//强制类型的转换.

1、四种强制类型转化的简单介绍

一、static_cast转换

  • 1.基本用法:static_cast<type-id> expression

  • 2.使用场景:

a、用于类层次结构中基类和派生类之间指针或引用的转换

上行转换(派生类---->基类)是安全的;

下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的。

b、用于基本数据类型之间的转换,如把int转换为char,这种带来安全性问题由程序员来保证

c、把空指针转换成目标类型的空指针

d、把任何类型的表达式转为void类型

  • 3.使用特点:

a、主要执行非多态的转换操作,用于代替C中通常的转换操作

b、隐式转换都建议使用static_cast进行标明和替换

二、dynamic_cast转换

  • 1.基本用法:dynamic_cast<type-id> expression

  • 2.使用场景:只有在派生类之间转换时才使用dynamic_cast,type-id必须是类指针,类引用或者void*。

  • 3.使用特点:

a、基类必须要有虚函数,因为dynamic_cast是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(如果一个类没有虚函数,那么一般意义上,这个类的设计者也不想它成为一个基类)。

b、对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)

c、dynamic_cast还可以进行交叉转换

三、const_cast转换

  • 1.基本用法:const_cast<type-id>expression

  • 2.使用场景:

a、常量指针转换为非常量指针,并且仍然指向原来的对象

b、常量引用被转换为非常量引用,并且仍然指向原来的对象

  • 3.使用特点:

a、cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符

b、去除常量性是一个危险的动作,尽量避免使用。一个特定的场景是:类通过const提供重载时,一般都是非常量函数调用const_cast<const T>将参数转换为常量,然后调用常量函数,然后得到结果再调用const_cast <T>去除常量性。

四、reinterpret_cast转换

  • 1.基本用法:reinterpret_cast<type-id>expression

  • 2.使用场景:不到万不得已,不用使用这个转换符,高危操作

  • 3.使用特点:

a、reinterpret_cast是从底层对数据进行重新解释,依赖具体的平台,可移植性差

b、reinterpret_cast可以将整型转换为指针,也可以把指针转换为数组

c、reinterpret_cast可以在指针和引用里进行肆无忌惮的转换

3、各种转化之间的区别

3.1 static_cast和dynamic_cast

class Base
{
    public:
        Base(int c = 2):_c(c){}
    public:
        int _c;
};
class Derived:public Base
{
    public:
        int _d;
        int _e;
};
int main(void)
{
    int tempA = 2;
    int tempB = 3;
    Base base;
    
    /*1.无编译告警,但是危险操作,譬如说调用drvPtrA->_d会造成不可预知的后果*/
    Derived *drvPtrA = static_cast<Derived *>(&base);  // 父类-->子类
    
    drvPtrA->_d = 4;
    drvPtrA->_e = 5;
    /*2.输出:tempA为5,tempB为4,踩内存了(机器信息:32位ubuntu,编译器clang++)*/
    cout<<tempA<<endl;
    cout<<tempB<<endl;
    
    /*3.Base中没有虚函数,无法查看运行时信息,编译不过*/
    Derived *drvPtrB = dynamic_cast<Derived *>(&base);

    return 0;
}

在基类派生类互相转换时,虽然static_cast是在编译期完成,效率更高,但是不安全,上例中就示范了一个踩内存的例子。相比之下因为dynamic_cast可以查看运行时信息,上例如果Base含有虚函数,那么drvPtrB就是一个空指针(这可比踩内存什么的好多了),不能操作Derived中_d的数据从而保证安全性,所以应该优先使用dynamic_cast。

3.2 static_cast和reinterpret_cast

class BaseA
{
    public:
        BaseA(int c = 2):_c(c){}
        int _c;
};
class BaseB
{
    public:
        BaseB(int d = 3):_d(d){}
        int _d;
};
int main(void)
{
    BaseA baseA;
    /*1.编译不过*/
    BaseB *baseB = static_cast<BaseB *>(&baseA);
    /*2.无任何编译告警,编译通过,正常运行*/
    BaseB *baseC = reinterpret_cast<BaseB *>(&baseA);
    cout<<baseC->_d<<endl;

    return 0;
}

static_cast虽然也不是一种绝对安全的转换,但是它在转换时,还是会进行必要的检测(诸如指针越界计算,类型检查)。reinterpret_cast完全是肆无忌惮,直接从二进制开始重新映射解释,是极度不安全的,再次提醒,不到万不得已,不要使用。

3.3 const_cast使用

错误的例子:

const int a = 10;
const int * p = &a;
*p = 20;                  //compile error
int b = const_cast<int>(a);  //compile error

在本例中出现了两个编译错误,第一个编译错误是*p因为具有常量性,其值是不能被修改的;另一处错误是const_cast强制转换对象必须为指针或引用,而例3中为一个变量,这是不允许的!

const_cast正确使用:

#include<iostream>
using namespace std;
 
int main()
{
    const int a = 10;
    const int * p = &a;
    int *q;
    q = const_cast<int *>(p);
    *q = 20;    //fine
    cout <<a<<" "<<*p<<" "<<*q<<endl;
        cout <<&a<<" "<<p<<" "<<q<<endl;
    return 0;
}

在本例中,我们将变量a声明为常量变量,同时声明了一个const指针指向该变量(此时如果声明一个普通指针指向该常量变量的话是不允许的,Visual Studio 2010编译器会报错)。

之后我们定义了一个普通的指针*q。将p指针通过const_cast去掉其常量性,并赋给q指针。之后我再修改q指针所指地址的值时,这是不会有问题的。

最后将结果打印出来,运行结果如下:
10 20 20
002CFAF4 002CFAF4 002CFAF4

查看运行结果,问题来了,指针p和指针q都是指向a变量的,指向地址相同,而且经过调试发现002CFAF4地址内的值确实由10被修改成了20,这是怎么一回事呢?为什么a的值打印出来还是10呢?

其实这是一件好事,我们要庆幸a变量最终的值没有变成20!变量a一开始就被声明为一个常量变量,不管后面的程序怎么处理,它就是一个常量,就是不会变化的。试想一下如果这个变量a最终变成了20会有什么后果呢?对于这些简短的程序而言,如果最后a变成了20,我们会一眼看出是q指针修改了,但是一旦一个项目工程非常庞大的时候,在程序某个地方出现了一个q这样的指针,它可以修改常量a,这是一件很可怕的事情的,可以说是一个程序的漏洞,毕竟将变量a声明为常量就是不希望修改它,如果后面能修改,这就太恐怖了。

在例4中我们称“*q=20”语句为 未定义行为语句 ,所谓的未定义行为是指在标准的C++规范中并没有明确规定这种语句的具体行为,该语句的具体行为由编译器来自行决定如何处理。对于这种未定义行为的语句我们应该尽量予以避免!

相关文章

  • c++四种强制类型转化

    c++四种强制类型转化 https://www.cnblogs.com/Allen-rg/p/6999360.ht...

  • C++类型转换

    C++类型转换 C++为了规范C中的类型转换,加强类型转换的可视性,引入了四种强制类型转换操作符:static_c...

  • [转]C++强制类型转换运算符(static_cast、rein

    将类型名作为强制类型转换运算符的做法是C语言的老式做法,C++ 为保持兼容而予以保留。 C++ 引入了四种功能不同...

  • php第三天--运算符和表达式

    1、强制类型转化1、类型转化函数 intval floatval strval boolval【注】转化的...

  • 2018-03-30

    让我们继续咯, 今天要总结的是C++中的四种强制类型转换符, 这四种转换符算不上难点, 不过也还是需要我们注意总结...

  • R语言强制数据类型转换

    1.as.character(x) 强制转化 对象X为 字符类型 对象 2.as.numeric(x) 强制转化 ...

  • C基础 循环 三大结构

    1.类型转化(1)隐式转化:注意:当赋值号;两边类型不同时,自动转化位精度较高的类型。(2)隐式转化:强制类型转化...

  • 笔记

    1.类型转化隐式转化 5/3=1.00000 当赋值号两边类型不同时,自动转化精度较高的类型显式转化 强制类型...

  • JS 09-19 or 20

    数据类型转换 强制类型转化 ParseInt 将数字或者字符转化整形 Parsefloat 将数字或者字符转化为浮...

  • Flutter 手动json转化之后强制类型转化

    GitHub地址 手动json转化之后强制类型转化 转化为Map数组 转化为List遍历

网友评论

      本文标题:c++四种强制类型转化

      本文链接:https://www.haomeiwen.com/subject/unotrctx.html