C++四种case的详细介绍小结

目录
  • 一、static_case
    • 1、基本数据类型转换
    • 2、指针和void指针的转换
    • 3、父类和子类之间的转换
  • 二、dynamic_case
  • 三、const_case
    • 1、加上const
    • 2、去掉const
  • 四、reinterpret_case

在C++中,我们经常使用到类型的转换,像把一个int类型转换成char类型,一个int类型转换成double类型,这些转换属于隐式类型转换。而今天我们要来讲的是显式类型转换。C++提供了四种显式类型转换,分别是:static_cast、dynamic_cast、const_case、reinterpret_case。

一、static_case

static_case的定义为:

static_case<type_name>(expression)

type_name是转换的类型,expression是被转换的对象或者表达式。

static_case一般用于隐式转换,当type_name和express至少有一方可以隐式转换时,则可以用static进行强制类型转换。可以用于常见的int、float、double等类型转换;转换成功返回true,否则返回false(相当于C语言中的强制类型转换)。

1、基本数据类型转换

double serven_double_1 = 1.2;
std::cout<<serven_double_1<<std::endl;

int serven_int_1 = static_cast<int>(serven_double_1);
std::cout<<serven_int_1<<std::endl;

double serven_double_2 = static_cast<double>(serven_int_1);
std::cout<<serven_double_2<<std::endl;

运行结果:基本类型的转换,可以看到double类型转换成int类型后丢失了精度,这一点跟reinterpret_case不一样,reinterpret_case是底层二进制的强制拷贝和语义转换,所以不会丢失精度,后面会讲到。

2、指针和void指针的转换

int* serven_int_2 = new int(2);
void * serven_void_1 = static_cast<void*>(serven_int_2);
int *serven_int_3 = static_cast<int*>(serven_void_1);
*serven_int_2 = 6;

std::cout<<*serven_int_2<<std::endl;
std::cout<<*serven_int_3<<std::endl;
std::cout<<serven_void_1<<std::endl;
std::cout<<serven_int_2<<std::endl;
std::cout<<serven_int_3<<std::endl;

 运行结果:void指针和其他类型的指针进行转化的时候,他们都是指向同一个地址。

3、父类和子类之间的转换

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};

class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
    }
};

void main(){

    SERVEN_PARENT* ser_par = new SERVEN_PARENT();
    ser_par->Function();
    SERVEN_CHILD* ser_chi = static_cast<SERVEN_CHILD*>(ser_par);
    ser_chi->Function();

}

运行结果:在main函数第二行中定义了一个ser_chi,是一个派生类对象,然后强制将基类对象转换成子类,这种叫做下行转换,转换后打印的结果是子类的Function,使用static_case来进行向下转换是不安全的,因为当子类中定义了基类没有的变量,并且在Function函数中使用了这个变量,那么程序将会报错。

下面我们来看一下static_case不安全的例子:

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};

class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
        std::cout<<nums<<std::endl;
    }

private:
    char nums = 'g';
};

void main(){

    SERVEN_PARENT* ser_par = new SERVEN_PARENT();
    ser_par->Function();
    SERVEN_CHILD* ser_chi = static_cast<SERVEN_CHILD*>(ser_par);
    ser_chi->Function();

}

 运行结果:因为派生类对象使用了自己独有的变量,所以打印char字符的时候就出现了乱码。

二、dynamic_case

dynamic_case的定义为:

dynamic<type_name>(expression)

type_name是转换的类型,expression是被转换的对象或者表达式。

dynamic一般用于基类指向派生类时的强制转换,转换成功返回true,失败返回false。它不像static_case一样向下转换不安全,它是安全的。它的安全性体现在RTTI,那什么是RTTI呢?
RTTI是运行时类型识别。程序能够使用基类的指针或引用来检查着这些指针或引用所指的对象的实际派生类型(判断指针原型)。RTTI提供了两个非常有用的操作符:typeid和dynamic_cast。(三个最主要的东西,dynamic_cast,typeid,type_info)。typeid:typeid函数(为type_info类的友元函数,为什么要这样呢?目的是防止创建type_info对象)的主要作用就是让用户知道当前的变量是什么类型的,它可以返回一个type_info的引用,可以获取类的名称和编码typeid重载了type_info中的==和!=可以用于判断两个类型是否相等。
dynamic_case和static_case在类继承的区别就是dynamic_case向下转换是安全的。

三、const_case

const_case的定义为:

const_case<type_name>(expression)

type_name是转换的类型,expression是被转换的对象或者表达式。

const_case有两个功能,分别是去掉const和加上const,一般用于去掉const,修改被const修饰为常量的值。但是修改的这个值本身不能是const常量,而是被二次引用或者传参数时被引用为const,才能修改,否则修改失败。同时type和express两个类型要一直去掉const,修改成功返回true,否则返回false。

1、加上const

int* serven_int_4 = new int(2);
const int* serven_int_5 = const_cast<const int*>(serven_int_4);     // 转换为常量指针
*serven_int_4 = 3;
//*serven_int_5 = 4;              // 不能修改
std::cout<<*serven_int_4<<std::endl;
std::cout<<*serven_int_5<<std::endl;
std::cout<<serven_int_4<<std::endl;
std::cout<<serven_int_5<<std::endl;

2、去掉const

(1)const修饰指针,指针指向一个类对象(常量指针)

将一个常量指针转换成非常量指针。

class SERVEN_PARENT{
public:
    SERVEN_PARENT(){}
    void Function(){
        std::cout<<"PARENT"<<std::endl;
    }
};

class SERVEN_CHILD : public SERVEN_PARENT{
public:
    SERVEN_CHILD(){}
    void Function(){
        std::cout<<"CHILD"<<std::endl;
        std::cout<<nums<<std::endl;
    }

private:
    char nums = 'g';
};

void main(){
    SERVEN_PARENT ser_par;
    const SERVEN_PARENT* pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_case<SERVEN_PARENT*>(pP);    // 强制将pP转换成非const

}

(2)const修饰指针指向对象的数值(指针常量)

将指针常量转换为非指针常量。

void main(){
    SERVEN_PARENT ser_par;
    SERVEN_PARENT* const pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_case<SERVEN_PARENT*>(pP);    // 强制将pP转换成非const

}

(3)const修饰指针指向对象的数值并且修饰指针(常量指针常量)

常量指针常量可以被转换为非常量指针常量,也可以转换成指针常量或者常量指针。

void main(){
    SERVEN_PARENT ser_par;
    const SERVEN_PARENT* const pP = &ser_par;
    SERVEN_PARENT* pP_1 = const_case<SERVEN_PARENT*>(pP);    // 强制将pP转换成非const
    const SERVEN_PARENT* pP_2 = const_case<SERVEN_PARENT*>(pP);    // 强制将pP转换成常量指针
    SERVEN_PARENT* const pP_2 = const_case<SERVEN_PARENT*>(pP);    // 强制将pP转换成指针常量

}

四、reinterpret_case

reinterpret_case的定义为:

reinterpret_case<type_name>(expression)

type_name是转换的类型,expression是被转换的对象或者表达式。

reinterpret_case是一种比较粗暴的转换方式,并且是最不安全的,为什么说它粗暴呢?因为它直接去拷贝最底层的二进制,它的本质是编译器的指令,它的作用是可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。或者不同类型的指针相互转换。

double serven_double_2 = 1.20;
char* serven_char_1 = reinterpret_cast<char* >(&serven_double_2);
double* serven_double_3 = reinterpret_cast<double*>(serven_char_1);
std::cout<<*serven_double_3<<std::endl;

int* serven_int_6 = reinterpret_cast<int*>(&serven_double_2);
double* serven_double_4 = reinterpret_cast<double*>(serven_int_6);
std::cout<<*serven_double_3<<std::endl;

运行结果:我们可以看到reinterpret_case将double类型转换成int,再转换成double类型后精度不会丢失,而static_case会丢失。

到此这篇关于C++四种case的详细介绍小结的文章就介绍到这了,更多相关C++ case内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何用c++表驱动替换if/else和switch/case语句

    目录 C++的表驱动法 一.常用示例 二.表驱动法 三.C++实现注意 四.实用案例 C++的表驱动法 目的:使用表驱动法,替换复杂的if/else和switch/case语句. 一.常用示例 以switch为例,常用示例如下: Funcition() { switch (key) { case key1: statements 1; break; case key2: statements 2; break; ... case keyn: statements n; break; defaul

  • C++四种case的详细介绍小结

    目录 一.static_case 1.基本数据类型转换 2.指针和void指针的转换 3.父类和子类之间的转换 二.dynamic_case 三.const_case 1.加上const 2.去掉const 四.reinterpret_case 在C++中,我们经常使用到类型的转换,像把一个int类型转换成char类型,一个int类型转换成double类型,这些转换属于隐式类型转换.而今天我们要来讲的是显式类型转换.C++提供了四种显式类型转换,分别是:static_cast.dynamic_c

  • Activity 四种启动模式详细介绍

    Activity 四种启动模式详细介绍 在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. Android总Activity的启动模式分为四种: Activity启动模式设置: <activity android:name=".MainActivity" android:launchMode="standard" /&

  • C++四种cast使用详细介绍

    目录 一.static_cast 1.基本数据类型转换 2.指针和void指针的转换 3.父类和子类之间的转换 二.dynamic_cast 三.const_cast 1.加上const 2.去掉const 四.reinterpret_cast 在C++中,我们经常使用到类型的转换,像把一个int类型转换成char类型,一个int类型转换成double类型,这些转换属于隐式类型转换.而今天我们要来讲的是显式类型转换.C++提供了四种显式类型转换,分别是:static_cast.dynamic_c

  • Android LaunchMode四种启动模式详细介绍

    Android LaunchMode详解 越是做的时间越长,基础知识就忘的越干净,最近做一个项目中,发现启动的几个Activity居然重叠了,我ri--,再不回忆一下就要退出Android界了. 概念解释 Task Task叫做任务,这个简单,表示我们需要完成的事情,注意,这里我们说的是任务,是个名词,例如要发短信,那我们的任务就是发送一条短信,仅此而已,再例如教官说:"张三,你去吃屎!",ok,那张三的任务就是吃屎. Back Stack 我们常叫做回退栈,或者是任务栈,这个是什么意

  • Json对象和字符串互相转换json数据拼接和JSON使用方式详细介绍(小结)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C.C++.C#.Java.JavaScript.Perl.Python等).这些特性使JSON成为理想的数据交换语言. 易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率). 一.JSON字符串转换为JSON对象: eval() 和 JSON.parse eg- js

  • java线程池的四种创建方式详细分析

    目录 前言 1. 线程池 2. 创建方式 前言 在讲述线程池的前提 先补充一下连接池的定义 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用 可以看到其连接池的作用如下: 1. 线程池 线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的代价.线程池不仅能够保证内核的充分利用,还能防止过分调度 特点:

  • vue-router传参的四种方式超详细讲解

    目录 vue路由传参的四种方式 一.router-link路由导航方式传参 二.调用$router.push实现路由传参 三.通过路由属性name匹配路由,再根据params传递参数 四.通过query来传递参数 vue-router传递参数的几种方式 vue路由传参的四种方式 一.router-link路由导航方式传参 父组件:<router-link to="/跳转到的路径/传入的参数"></router-link>子组件:this.$route.param

  • C++ AVL树插入新节点后的四种调整情况梳理介绍

    AVL树是一个高度平衡的二叉搜索树 满足二叉搜索树的所有特性. 左子树和右子树的高度之差的绝对值不大于1. 此处AVL树结点的定义 template<class K, class V> struct AVLTreeNode { AVLTreeNode<K, V> _left; AVLTreeNode<K, V> _right; AVLTreeNode<K, V> _parent; pair<K, V> _kv; int _bf; //平衡因子 A

  • Linux 下的五种 IO 模型详细介绍

    概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间.针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空

  • python RabbitMQ 使用详细介绍(小结)

    上节回顾 主要讲了协程.进程.异步IO多路复用. 协程和IO多路复用都是单线程的. epoll  在linux下通过这个模块libevent.so实现 gevent  在底层也是用了libevent.so gevent可以理解为一个更上层的封装. 使用select或者selectors,每接收或发送数据一次都要select一次 twisted异步网络框架,强大又庞大,不支持python3 (代码量python中排top3).几乎把所有的网络服务都重写了一遍. 一.RabbitMQ 消息队列介绍

随机推荐