C++中的类型转化

目录

1.C语言中的类型转化

2.C++强制类型转换

2.1、static_cast

2.2、reinterpret_cast

2.3、const_cast

2.4、dynamic_cast


1.C语言中的类型转化

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
2. 显式类型转化:需要用户自己处理

看下面这段代码来理解:

void Test()
{
    int i = 1;
    // 隐式类型转换(意义相近的类型)
    double d = i;
    printf("%d, %.2f\n", i, d);
    int* p = &i;
    // 显式的强制类型转换(意义相差比较大的类型),此时隐式便会报错
    int address = (int)p;
    printf("%x, %d\n", p, address);
}

int和double意义比较接近,所以可以隐式类型转化,将int升为double类型

而address时整型,p是指针,再隐式类型转化会出错,所以此时必须显式强制转化.

 

 缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换.

下面这种场景也是让人非常难受的.

void insert(size_t pos, char ch)
{
    size_t _size = 5;
    int end = _size - 1;
    while (end >= pos)
    {
        --end;
    }
}

当pos=0时,end会--直到0,最后一次进去end会变成-1,然后再次与pos进行比较,此时-1< 0不符合条件应该跳出,然而这里却发生了一个隐式类型转化,将end从int(有符号)提升为size_t(无符号),这样-1就成了2^32-1,便造成了死循环.

所以C++为了解决这些问题,便提出了这四种强制类型转换.

2.C++强制类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast.

2.1、static_cast

static_cast用于非多态类型的转换(静态转换),编译器执行的任何隐式类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换

int main()
{
    double d = 12.34;
    int a = static_cast<int>(d);
    cout << a << endl;

    int* p = &a;
    //不支持不相关类型转化
    int address = static_cast<int>(p);
}

只要记住,static_cast多用于隐式类型转化.

2.2、reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型.

可以理解为可强转为不相关的类型.

比如刚才上面的例子,static_cast不支持不相关类型转化,即不能将指针p转为int整型.我们改用reinterpret_cast即可.

    int* p = &a;
    //支持不相近类型转化
    int address = reinterpret_cast<int>(p);

这样我们看到static_cast就知道这是相近类型转化. reinterpret_cast就知道是不相近类型转化.

2.3、const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值.

int main()
{
    const int a = 2;
    //错误的,由于a具有常性
    //int* p = reinterpret_cast<int*>(&a);
    int* p = const_cast<int*>(&a);
    *p = 3;
}

利用const_cast,此时便成功把常量a的地址给了p,然后并且可以修改了.

但有一个奇怪的现象:

我们输出下a和*p的值,由于p的地址是也是a,对p修改也会影响a。

所以我们预期的结果应该都是3.我们输出一下:

    cout << a << endl;
    cout << *p << endl;

答案是2和3.为什么a没有被修改呢?我们调试观察一下:

 

 奇怪的现象又出现了,内存中显示a也已经被修改为3了,怎么输出出来还是2呢

这源于编译器的优化,编译器认为a是const已经不能被改了,那么我每次从内存中取多慢,编译器便直接把a加载到寄存器里面去,每次访问直接从寄存器读,效率便会提高。因为是const所以不会写,这才敢放到寄存器.所以每次读取的都是一开始的2.

至于监视窗口,也是另外一个单独的进程,没有优化,而是实时检测的,它这个时候便去内存中取,显示的便是3了.

那么我们如果不想让编译器优化,每次就要从内存中取,这时就要在前面加上volatile关键字.

此时便得到了正确的结果.

下面一种转化是C++独有的一种转化.

2.4、dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用 -> 父类指针/引用(不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回nullptr。

看这段代码:

class A
{
public:
    int _a = 0;
};
class B : public A
{
public:
    int _b = 1;
};
int main()
{
    B bb;
    //子类->父类
    A aa1 = bb;
    A& ra1 = bb;
}

以上是可以正常转化的,子类转成父类,会有切片操作,这我们可以理解的.

但是向下转型,父类转化成子类时,可以正常转吗?

首先要知道的是,父类对象是无论如何都不可以转化成子类对象的.

但是利用dynamic_cast可以把父类对象的指针或引用转化成子类的指针或引用.

class A
{
public:
    virtual void f() {}
};
class B : public A
{};
//A*指针有可能指向父类,有可能指向子类
void fun(A* pa)
{
    // dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
    //如果pa是指向子类,那么可以转化,转化表达式返回正确的地址
    //如果pa是指向父类,那么不可以转化,转化表达式返回nullptr
    B* pb = dynamic_cast<B*>(pa);//安全的
    //B* pb = (B*)pa;//不安全的,都会转化成功
    if (pb)
    {
        cout << "转化成功" << endl;
    }
    else
    {
        cout << "转化失败" << endl;
    }
}
int main()
{
    A a;
    B b;
    fun(&a);
    fun(&b);
    return 0;
}

总的来说:dynamic_cast真正的功能是区分指针是指向父类还是子类.

如果"父类"指针是父类指针,dynamic_cast转化成子类会出现错误.

如果"父类"指针是子类指针,dynamic_cast转化成子类会成功.

所以说dynamic_cast向下转型是安全的.

这样c++的四种类型转化也就全部讲完了.

猜你喜欢

转载自blog.csdn.net/weixin_47257473/article/details/131666528
今日推荐