18- C++ 强制类型转换-6 (C++)

news2024/9/21 23:29:57

第八章 强制类型转换

c++提供了 隐式类型转换,所谓隐式类型转换,是指不需要用户干预,编译器默认进行的类型转换行为(很多时候用户可能都不知道到底进行了哪些转换)。例如:

int nValue = 8;

double dValue = 10.7;

// nValue会被自动转换为double类型,用转换的结果再与dValue相加

double dSum = nValue + dValue;

但是很多时候我们希望在表达式中明确指定将一种类型转换为另一种类型,这种转换方式我们称之为显示类型转换。隐式类型转换一般是由编译器进行转换操作,显示类型转换是由程序员写明要转换成的目标类型。显示类型转换又被称为强制类型转换。

c++提供了四种强制类型转换操作符:static_cast、const_cast、dynamic_cast、reinterpret_cast

8.1 const_cast

const_cast是一种C++运算符,主要是用来去除复合类型中const和volatile属性(没有真正去除)

变量本身的const属性是不能去除的,要想修改变量的值,一般是去除指针(或引用)的const属性,再进行间接修改。

用法:const_cast <type>(expression)

通过const_cast运算符,也只能将 const type *转换为type *, 将const type&转换为type&

也就是说源类型和目标类型除了const属性不同,其他地方完全相同。

我们先来看这样一段代码:

#include <iostream>
using namespace std;

int main()
{
    const int data = 100;
    int *pp = (int *)&data;
    *pp = 300;
    cout << "data = " << data << "\t address: " << &data << endl << endl;
    cout << " pp = " << *pp << "\t address: " << pp << endl << endl;

    int *p = const_cast<int*>(&data);
    cout << "data = " << data << "\t address: " << &data << endl << endl;
    cout << "p = " << *p << "\t address: " << p << endl << endl;

    *p = 200;

    cout << "data = " << data << "\t address: " << &data << endl << endl;
    cout << "p = " << *p << "\t address: " << p << endl << endl;
    return 0;
}

 

data 的地址是 0x6ffdfc, p 指向的地址也是 0x6ffdfc, 但是修改 p 之后, 同一个地址上的内容却不相同。

可能是 const 的问题? const 的机制,就是在编译期间,用一个常量代替了 data。这种方式叫做常量折叠

常量折叠与编译器的工作原理有关,是编译器的一种编译优化。在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。所以在上面的例子中,编译器在优化的过程中,会把碰到的data(为const常量)全部以内容100替换掉,跟宏的替换有点类似。

常量折叠只对原生类型起作用,对我们自定义的类型,是不会起作用的

#include <iostream>
#include <stdio.h>
 
using namespace std;
 
class Test {
public:
    int a;
    Test() {
        a = 10;
    };
};
 
int main()
{
    const Test b;
    Test *b1 = const_cast<Test *>(&b);
    cout << "b = " << b.a << endl;
    b1->a = 100;
    cout << "b = " << b.a << ", *b1 = " << b1->a << endl;
 
    return 0;
}

函数的形参为非const,当我们传递一个const修饰的变量时可以通过const_cast去除该变量的const属性。

#include <iostream>
using namespace std;
 
const int *f(int *t)
{
	int *p = new int;
	*p = 100;
	return p;
}
 
int main () {
	int x = 1;
	int *p1 = const_cast<int *>(f(&x));
	*p1 = 1000;

	const int *y = new int(10);
	//int *p2 = const_cast<int *>(f(y));//[Note] in passing argument 1 of 'const int* f(int&)'
	int *p2 = const_cast<int *>(f(const_cast<int *>(y)));
	cout << *y <<endl;
//	*y = 100; //[Error] assignment of read-only location '* y'
}

const_cast也可以去除函数返回值的const属性

8.2 static_cast

1、static_cast 主要用于内置数据类型之间的相互转换

double dValue = 12.12;
float fValue = 3.14; //从“double”到“float”截断
int nDValue = static_cast<int>(dValue); // 12
int nFValue = static_cast<int>(fValue); // 3

2、也可以转换自定义类型。如果涉及到类,static_cast只能在有相互联系(继承)的类型间进行转换,且不一定包含虚函数

class A
{};
class B : public A
{
    public:
        void f();
};
class C
{};

int main()
{
    A *pA = new A;
    B *pB = static_cast<B*>(pA); // 编译不会报错, B类继承于A类
    pB = new B;
    pA = static_cast<A*>(pB); // 编译不会报错, B类继承于A类
    // 编译报错, C类与A类没有任何关系。error C2440: “static_cast”: 无法从“A *”转换为“C *”
    C *pC = static_cast<C*>(pA); 
    A *p1;
    B b2;
    p1 = &b2;
    static_cast<B *>(p1)->f();
}

8.3 dynamic_cast

dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用。

dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行“安全的向下转型(safe downcasting)”,也即是基类对象的指针或引用转换为同一继承层次的其他指针或引用。

至于“向上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的。

“向下转型”的前提条件:被转换对象必须是多态类型(必须公有继承自其他类,或者有虚函数)

#include <iostream>
using namespace std;

class Base
{
public:
    Base(){};
    virtual void Show(){cout<<"This is Base calss";}
};
class Derived:public Base
{
public:
    Derived(){};
    void Show(){cout<<"This is Derived class";}
};

int main()
{
    Base *base ;
    Derived *der = new Derived;
    //base = dynamic_cast<Base*>(der); //正确,但不必要。
    base = der; //先上转换总是安全的
    base->Show();
} 

“向下转型” 有两种情况。

  • 一种是基类指针所指对象是派生类类型的,这种转换是安全的;

  • 另一种是基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为0;

#include <iostream>
using namespace std;

class Base
{
public:
    Base(){};
    virtual void Show(){cout<<"This is Base calss" << endl;}
};
class Derived:public Base
{
public:
    Derived(){};
    void Show(){cout<<"This is Derived class" << endl;}
	void d_f()
	{cout << "d_f" << endl;}
};

class Derived1:public Base
{
public:
    Derived1(){};
    void Show(){cout<<"This is Derived1 class" << endl;}
	void d_f()
	{cout << "d1_f" << endl;}
};

void f(Base *p)
{
    Derived *d;
    if (d = dynamic_cast<Derived *>(p))
	{
		d->d_f();
		d->Show();
	}
}

int main()
{
	Derived d1;
	Derived1 d2;
	f(&d2);

	Base *p = new Derived;
	f(p);

	Base *p1 = new Base;
	f(p1);
	#if 0
	Derived d;
	Base *p = new Derived;

	//Derived *pd = static_cast<Derived *>(p);
	Derived *pd = dynamic_cast<Derived *>(p);
	pd->Show(); //派生类中的函数

	Base *p1 = new Base;
	//pd = dynamic_cast<Derived *>(p1); //运行返回值为0
	pd = static_cast<Derived *>(p1);
	pd->Show();  //基类中的函数
	pd->d_f();
	#endif
}

8.4 reinterpret_cast

1、reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。

2、这种转换提供了很强的灵活性,但转换的安全性只能由程序员的细心来保证了。例如,程序员执意要把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,程序员也只能自行承担查找错误的烦琐工作:(C++ 标准不允许将函数指针转换成对象指针,但有些编译器,如 Visual Studio 2010,则支持这种转换)

#include <iostream>
using namespace std;
class A
{
public:
    int i;
    int j;
    A(int n):i(n),j(n) { }
};
int main()
{
    A a(100);
    int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
    r = 200;  //把 a.i 变成了 200
    cout << a.i << "," << a.j << endl;  // 输出 200,100
    int n = 300;
    A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
    pa->i = 400;  // n 变成 400
    pa->j = 500;  //此条语句不安全,很可能导致程序崩溃
    cout << n << endl;  // 输出 400
    long long la = 0x12345678abcdLL;
    pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
    unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u
    cout << hex << u << endl;  //输出 5678abcd
    typedef void (* PF1) (int);
    typedef int (* PF2) (int,char *);
    PF1 pf1;  PF2 pf2;
    pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
}  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

评估修改后的YOLOv8模型的参数量和速度

YOLOv8公布了自己每个模型的速度和参数量 那么如果我们自己对YOLOv8做了一些修改&#xff0c;又怎么样自己写代码统计一下修改后的模型的参数量和速度呢&#xff1f; 其实评估这些东西&#xff0c;大多数情况下不需要我们从头自己写一个函数来评估 一般来说&#xff0c;只要…

【云存储】使用OSS快速搭建个人网盘教程(阿里云)

使用OSS快速搭建个人网盘 一、基础概要1. 主要的存储类型1.1 块存储1.2 文件存储1.3 对象存储 2. 对象存储OSS2.1 存储空间2.2 地域2.3 对象2.4 读写权限2.5 访问域名&#xff08;Endpoint&#xff09;2.6 访问密钥2.7 常用功能&#xff08;1&#xff09;创建存储空间&#xff…

HCIP-datacom-831题库

考取HCIP数通证书可以胜任中到大型企业网络工程师岗位&#xff0c;需要掌握中到大型网络的特点和通用技术&#xff0c;具备使用华为数通设备进行中到大型企业网络的规划设计、部署运维、故障定位的能力&#xff0c;并能针对网络应用设计出较高安全性、可用性和可靠性的解决方案…

RedisJava的Java客户端

目录 1.Jedis的使用 前置工作-ssh进行端口转发 JedisAPI的使用 Jedis连接池 2.SpringDataRedis的使用 1.创建项目 2.配置文件 3.注入RedisTemplate对象 4.编写代码 3.SpringRedisTemplate 哈希结构用法 ​总结 1.Jedis的使用 Jedis&#xff1a;以Redis命令作为方法…

途乐证券:沪指强势拉升涨0.63%,券商等板块走强,传媒板块活跃

31日早盘&#xff0c;两市股指全线走高&#xff0c;沪指一度涨超1%收复3300点&#xff0c;上证50指数盘中涨逾2%&#xff1b;随后涨幅有所收窄&#xff1b;两市成交额显着放大&#xff0c;北向资金净买入超90亿元。 到午间收盘&#xff0c;沪指涨0.63%报3296.58点&#xff0c;深…

Python多线程与GIL锁

Python多线程与GIL锁 python多线程 Python的多线程编程可以在单个进程内创建多个线程来同时执行多个任务&#xff0c;从而提高程序的效率和性能。Python的多线程实现依赖于操作系统的线程调度器&#xff0c;并且受到全局解释器锁&#xff08;GIL&#xff09;的限制&#xff0c…

如何在 Ubuntu 22.04 下编译 StoneDB for MySQL 8.0 | StoneDB 使用教程 #1

作者&#xff1a;双飞&#xff08;花名&#xff1a;小鱼&#xff09; 杭州电子科技大学在读硕士 StoneDB 内核研发实习生 ❝ 大家好&#xff0c;我是 StoneDB 的实习生小鱼&#xff0c;目前正在做 StoneDB 8.0 内核升级相关的一些事情。刚开始接触数据库开发没多久&#xff0c…

第55步 深度学习图像识别:CNN特征层和卷积核可视化(TensorFlow)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;CNN可视化 在理解和解释卷积神经网络&#xff08;CNN&#xff09;的行为方面&#xff0c;可视化工具起着重要的作用。以下是一些可以用于可视化的内容&#xff1a; &#xff08;a&#xff09;激活映射&…

多目标关联(分配)最近邻法

多目标关联&#xff08;分配&#xff09;最近邻法 最近邻数据关联 适用于两帧图片的中多目标位置关联&#xff0c;目标轨迹与新目标之间的关联、固定位置下的动目标跟踪关联等问题。 新目标与被跟踪目标的预测位置“最邻近”的观测点作为与航迹相关联的观测。 如有三批目标T…

活字格性能优化技巧-如何在大规模数据量的场景下提升数据访问效率

在上节内容中我们介绍了如何利用数据库主键提升访问性能&#xff0c;本节内容我们继续为大家介绍如何在大规模数据量的场景下提升数据访问效率。 在开始之前先做个小小的实验&#xff1a; 1. 准备一张数据表&#xff0c;内置1000W行记录。 2. 直观感受一下这个表的规模。使用…

数据结构入门指南:单链表(附源码)

目录 前言 尾删 头删 查找 位置前插入 位置后插入 位置删除 位置后删除 链表销毁 总结 前言 前边关于链表的基础如果已经理解透彻&#xff0c;那么接下来就是对链表各功能的实现&#xff0c;同时也希望大家能把这部分内容熟练于心&#xff0c;这部分内容对有关链表部分的…

CustomeG6-canvas

目录 简介 scss 快速上手 语雀 简介 antv/g6是一款基于JavaScript的图形可视化引擎&#xff0c;由阿里巴巴的AntV团队开发。 创建各种类型的图形&#xff0c;如流程图、关系图、树形图等。 G6采用了自己的绘图模型和渲染引擎&#xff0c;使其具备高性能的图形渲染能力。…

npm更新和管理已发布的包

目录 1、更改包的可见性 1.1 将公共包设为私有 ​编辑 使用网站 使用命令行 1.2 将私有包公开 使用网站 使用命令行 2、将协作者添加到用户帐户拥有的私有包 2.1 授予对Web上私有用户包的访问权限 2.2 从命令行界面授予私有包访问权限 2.3 授予对私有组织包的访问权限…

InfiniBand,到底是个啥?

对于InfiniBand&#xff0c;很多搞数通的同学肯定不会陌生。 进入21世纪以来&#xff0c;随着云计算、大数据的不断普及&#xff0c;数据中心获得了高速发展。而InfiniBand&#xff0c;就是数据中心里的一项关键技术&#xff0c;地位极为重要。 尤其是今年以来&#xff0c;以Ch…

春秋云镜 CVE-2021-32682

春秋云镜 CVE-2021-32682 elFinder RCE 靶标介绍 elFinder是一套基于Drupal平台的、开源的AJAX文件管理器。该产品提供多文件上传、图像缩放等功能;elFinder 存在安全漏洞&#xff0c;攻击者可利用该漏洞在托管elFinder PHP连接器的服务器上执行任意代码和命令。 启动场景 漏…

金蝶管易云 X Hologres:新一代全渠道电商ERP最佳实践

业务简介 金蝶管易云是金蝶集团旗下专注提供电商企业管理软件服务的子公司&#xff0c;成立于2008年&#xff0c;是国内最早的电商ERP服务商之一&#xff0c;目前已与300主流电商平台建有合作关系&#xff0c;以企业数据为驱动&#xff0c;深度融合线上线下数据&#xff0c;为…

pytorch学习——正则化技术——丢弃法(dropout)

一、概念介绍 在多层感知机&#xff08;MLP&#xff09;中&#xff0c;丢弃法&#xff08;Dropout&#xff09;是一种常用的正则化技术&#xff0c;旨在防止过拟合。&#xff08;效果一般比前面的权重衰退好&#xff09; 在丢弃法中&#xff0c;随机选择一部分神经元并将其输出…

HCIP中期实验

1、该拓扑为公司网络&#xff0c;其中包括公司总部、公司分部以及公司骨干网&#xff0c;不包含运营商公网部分。 2、设备名称均使用拓扑上名称改名&#xff0c;并且区分大小写。 3、整张拓扑均使用私网地址进行配置。 4、整张网络中&#xff0c;运行OSPF协议或者BGP协议的设备…

Hadoop 之 Hive 4.0.0-alpha-2 搭建(八)

Hadoop 之 Hive 搭建与使用 一.Hive 简介二.Hive 搭建1.下载2.安装1.解压并配置 HIVE2.修改 hive-site.xml3.修改 hadoop 的 core-site.xml4.启动 三.Hive 测试 一.Hive 简介 Hive 是基于 Hadoop 的数据仓库工具&#xff0c;可以提供类 SQL 查询能力 二.Hive 搭建 1.下载 H…