详解C++类型转换特性(代码+详解)

news2024/10/5 13:58:14

0

C++类型转换

  • 引言
    • 1. C语言中的类型转换
    • 2. 为什么C++需要四种类型转换
  • C++强制类型转换
    • 1.static_cast
      • 补充
    • 2.dynamic_cast
    • 3.const_cast
    • 4.reinterpret_cast
  • RTTI

引言

1. C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值
类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理
  3. 缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

2. 为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰
    因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

C++强制类型转换

1.static_cast

static_cast 是 C++ 中的一种类型转换操作符,用于在编译时进行静态类型转换。它提供了一种类型安全的转换方式,并在编译时进行类型检查,以确保转换的安全性。

static_cast 可以用于以下几个方面:

  • 类型转换:用于在具有相关类型之间进行显式转换,例如将整数类型转换为浮点类型、将指针类型转换为整数类型等。

  • 类层次转换:用于在类之间进行向上转型(基类指针或引用指向派生类对象)和向下转型(派生类指针或引用转换为基类指针或引用)。

  • 避免隐式类型转换:可以使用 static_cast 来明确指定想要的转换,而不依赖于隐式转换的规则。

  • 指针类型转换:可以用于将指针类型进行转换,但需要注意确保转换的安全性,因为 static_cast 在指针转换时无法进行动态类型检查。

应用场景包括但不限于:

  1. 在基本数据类型之间进行转换,例如将 int 转换为 float。
  2. 在类层次结构中进行向上转型和向下转型。
  3. 避免隐式类型转换,明确指定所需的转换方式。
  • 在指针之间进行转换,但需要谨慎处理,确保转换的安全性。
  • 需要注意的是,static_cast 并不适用于所有类型转换的情况,某些情况下可能需要使用其他类型转换操作符,如 dynamic_cast、reinterpret_cast 或 const_cast,具体选择取决于需要实现的转换类型和转换的安全性。在进行类型转换时,应仔细考虑转换的语义和安全性,并确保不违反类型系统的规则。
  • 测试代码
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
#include<iostream>
/*int main()
{
	enum Week
	{
		Monday = 1,
		Tuesday,
		Wednesday,
		Thursday,
		Friday,
		Saturday,
		Sunday
	};
	Week noday1 = static_cast<Week>(7); //sucess
	Week noday2 = static_cast<Week>(8); //error

		int i1 = 10;
	char c = static_cast<char>(i1);//正确,int转换为char 只是编译通过 无意义
	cout << "C:"<<c << endl;

	int* pI = NULL;
	//char* cI = static_cast<void*>(pI);//将int类型空指针转换为char空指针

	int i2 = 10;
	const int j = static_cast<const int>(i2);//正确,将int型数据转换成const int型数据
	//j = 230;  报错 
	const int i3 = 20;
	int ii = static_cast<int>(i3);//编译通过 ,static_cast不能转换掉i的const属性
	ii = 211; //这里跟编译器的处理机制有关系 在vs2019中可以通过 换到linux下编译就报错了
	 //C+规定static_cast 是不可以换掉变量的const属性的
	cout << "ii:" << ii << endl;




}*/

#include <iostream>

class Shape {
public:
	virtual void print() const {
		std::cout << "Shape" << std::endl;
	}
};

class Circle : public Shape {
public:
	void print() const override {
		std::cout << "Circle" << std::endl;
	}

	void circleSpecificFunction() {
		std::cout << "Circle-specific function" << std::endl;
	}
};

int main() {
	Circle circle;

	// 向上转型:派生类指针转换为基类指针
	Shape* shapePtr = static_cast<Shape*>(&circle);
	shapePtr->print();  // 输出 "Circle",调用的是派生类的 print() 函数

	// 向下转型:基类指针转换为派生类指针
	Circle* circlePtr = static_cast<Circle*>(shapePtr);
	circlePtr->print();  // 输出 "Circle",调用的是派生类的 print() 函数
	circlePtr->circleSpecificFunction();  // 调用派生类特有的函数

	return 0;
}
/*在上述代码中,Shape 是基类,Circle 是派生类,它们之间存在继承关系。
通过将派生类指针 &circle 使用 static_cast 转换为基类指针 Shape*,
可以进行向上转型,将派生类对象视为基类对象。同样地,
通过将基类指针 shapePtr 使用 static_cast 转换回派生类指针 Circle*,
可以进行向下转型,将基类对象还原为派生类对象。在向下转型后,
可以调用派生类特有的函数 circleSpecificFunction()。

需要注意的是,在进行向下转型时,需要确保基类指针指向的实际对象是派生类对象,
否则可能导致未定义的行为。因此,在实际应用中,通常会在进行向下转型之前使用 
dynamic_cast 进行类型检查,以确保安全转型。*/

补充

关于static_cast不能去除变量const特性这点 ,有些编译器可能可以编译通过,这取决于编译器自身特殊的处理机制,但是C+规定是不允许的。
p1
如上面,在我的vs2019中编译就可以通过 。
相同的代码我放在Linux下编译就会报错:
p2

报错:
p3

2.dynamic_cast

dynamic_cast 是 C++ 中的一种类型转换运算符,用于在类层次结构中进行安全的向上转型和向下转型,并进行运行时类型检查。与 static_cast 不同,dynamic_cast 具有一定的安全性检查机制,可以在转型过程中检查类型的有效性,避免出现错误的转型。

下面是一个示例代码,演示了 dynamic_cast 的用法和应用场景:

#include <iostream>

class Animal {
public:
    virtual void makeSound() const {
        std::cout << "Animal sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() const override {
        std::cout << "Dog barking" << std::endl;
    }

    void dogSpecificFunction() {
        std::cout << "Dog-specific function" << std::endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() const override {
        std::cout << "Cat meowing" << std::endl;
    }

    void catSpecificFunction() {
        std::cout << "Cat-specific function" << std::endl;
    }
};

int main() {
    Animal* animalPtr = new Dog();

    // 向下转型前先进行类型检查
    if (Dog* dogPtr = dynamic_cast<Dog*>(animalPtr)) {
        dogPtr->makeSound();  // 输出 "Dog barking"
        dogPtr->dogSpecificFunction();  // 调用 Dog 特有的函数
    } else {
        std::cout << "Invalid downcast" << std::endl;
    }

    delete animalPtr;

    return 0;
}

  • 在上述代码中,Animal 是基类,Dog 和 Cat 是派生类,它们之间存在继承关系。通过创建一个指向 Dog 对象的 Animal* 指针 animalPtr,然后使用 dynamic_cast 进行向下转型,将基类指针转换为派生类指针。在转型之前,使用 dynamic_cast 对转型结果进行检查,确保转型是有效的。如果转型成功,即指针指向的对象是 Dog 类型,就可以安全地调用 Dog 类的成员函数。如果转型失败,即指针指向的对象不是 Dog 类型,可以根据需要进行错误处理。

  • 需要注意的是,dynamic_cast 只能用于具有多态性的类层次结构,即基类必须至少定义一个虚函数。此外,dynamic_cast 只能在指针或引用之间进行转型,不能用于转换普通的数值类型。

  • 通过使用 dynamic_cast,可以在进行类层次结构中的向下转型时进行类型检查,避免转型错误,提高程序的安全性

3.const_cast

const_cast 是 C++ 中的一种类型转换运算符,用于在特定情况下去除常量性 (constness)。它主要用于将对象的常量性转换为非常量性,从而可以修改原本被声明为常量的对象。

下面是一个示例代码,演示了 const_cast 的用法和应用场景:

#include <iostream>

void modifyValue(int& value) {
    value = 42;
}

int main() {
    const int constantValue = 10;

    // 错误示例:无法将常量对象传递给非常量引用参数
    // modifyValue(constantValue);  // 编译错误

    // 正确示例:使用 const_cast 去除常量性
    modifyValue(const_cast<int&>(constantValue));  // OK

    std::cout << "Modified value: " << constantValue << std::endl;  // 输出 "Modified value: 42"

    return 0;
}

  • 在上述代码中,constantValue 被声明为常量,因此无法直接传递给一个非常量引用参数。这是因为常量对象是不可修改的。但是,如果我们确实需要在某种情况下修改该对象,可以使用 const_cast 去除其常量性。通过将 constantValue 强制转换为非常量引用,我们可以将其传递给 modifyValue 函数,修改其值。注意,这种转换只能用于移除常量性,而不能用于将非常量对象转换为常量对象。

  • 需要注意的是,对于本身是常量的对象使用 const_cast 进行转换并尝试修改其值是一种不安全的操作,可能会导致未定义行为。因此,在使用 const_cast 时要小心,并确保不会导致程序的逻辑错误或潜在的错误。

  • 总之,const_cast 主要用于去除常量性,以便在特定情况下修改被声明为常量的对象。然而,应该谨慎使用 const_cast,并且只在确实需要修改常量对象的情况下使用它。

4.reinterpret_cast

reinterpret_cast 是 C++ 中的一种类型转换运算符,用于在不同类型之间进行强制类型转换。它提供了一种低级别的类型转换,可以将一个指针或引用转换为不同类型的指针或引用,甚至可以将一个整数类型转换为一个指针类型,反之亦然。

  • reinterpret_cast 的使用非常谨慎,因为它执行的是一种底层的、不安全的类型转换。它不会进行任何类型检查或转换操作,仅仅是将一个类型的位模式重新解释为另一个类型的位模式。因此,使用 reinterpret_cast 进行类型转换需要确保转换是有意义和合法的,否则可能导致未定义行为。

下面是一些示例代码,演示了 reinterpret_cast 的用法和应用场景

#include <iostream>

struct A {
    int x;
};

struct B {
    double y;
};

int main() {
    A a;
    a.x = 42;

    // 将 A 对象的地址转换为 B 对象的地址
    B* b = reinterpret_cast<B*>(&a);

    // 修改 B 对象的成员
    b->y = 3.14;

    // 输出 A 对象的成员,将会看到修改后的值
    std::cout << "a.x: " << a.x << std::endl;  // 输出 "a.x: 1078530011"

    return 0;
}

在上述代码中,我们将一个 A 对象的地址强制转换为一个 B 对象的地址,并通过这个指针修改了 B 对象的成员。由于 A 和 B 是不同的类型,这种转换是不安全的,会导致对象被重新解释为不同类型。在这个例子中,我们修改了 B 对象的成员 y,但实际上这个对象应该被看作是 A 类型的对象,因此 a.x 的值也会受到影响。

需要注意的是,reinterpret_cast 可能导致类型安全性问题和不可移植性问题。因此,应该避免在正常情况下使用 reinterpret_cast 进行类型转换,除非有明确的理由和合理的解释。一般而言,使用 reinterpret_cast 应该限制在与底层内存表示相关的特定情况下,如与位模式操作、底层编程或与外部系统的交互等。

总之,reinterpret_cast 提供了一种低级别的类型转换,可以在不同类型之间进行强制转换。然而,由于其不安全性和潜在的不可移植性,应该谨慎使用,并确保转换是有意义和合法的。

RTTI

Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符

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

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

相关文章

虚拟机Centos7环境下如何安装wget

一、wget简介 wget 是一个从网络上自动下载文件的自由工具&#xff0c;支持通过 HTTP、HTTPS、FTP 三个最常见的 TCP/IP协议 下载&#xff0c;并可以使用 HTTP 代理。“wget” 这个名称来源于 “World Wide Web” 与 “get” 的结合。所谓自动下载&#xff0c;是指 wget 可以在…

JVM oop内存模型

一、oop模型 1、非数组对象 InstaceOopDesc 2、数组对象 arrayOopDesc 2.1 基本数据类型数组 typeArrayOopDesc 2.2 引用类型数组 objArrayOopDesc 3、MarkOopDesc 存放锁信息、分代年龄等 二、对象的内存结构 对象内存结构分成三大部分 对象头 &#xff08;64位操作系统&a…

软考02原码反码和补码

文章目录 前言一、原码二、反码三、补码总结 前言 机器是通过二进制来存储数据的&#xff0c;最好是在学习了软考01进制转换基础上开始学习原码反码和补码。 一、原码 原码通常以固定位数表示,不足补0&#xff0c;由于需要区分正负数所以&#xff0c;最高位为符号位(0为正&…

Electron中启动node服务

记一次遇到的问题&#xff0c;我们知道Electron 中主进程是在node环境中&#xff0c;所以打算在node环境中再启动一个node服务。但是直接使用exec命令启动就会卡主。对应的代码如下 // 启动Node server const startServer async () > {try {console.log(开始启动node serv…

React | 再战Redux

✨ 个人主页&#xff1a;CoderHing &#x1f5a5;️ React.js专栏&#xff1a;React.js 再战Redux &#x1f64b;‍♂️ 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f4ab; 系列专栏&#xff1a;吊打面试官系列 16天学会Vue 7天学会微信小程序 Node专栏…

chatgpt赋能python:下载Python的方法及使用指南

下载Python的方法及使用指南 Python是一种高级编程语言&#xff0c;被广泛应用于各种领域。如果你是一名程序员或者对编程有兴趣&#xff0c;那么学习Python会是一个不错的选择。本文将介绍Python的下载方法&#xff0c;并提供使用Python的基础指南。 Python的下载方法 Pyth…

Istio与Mcp Server服务器讲解与搭建演示

01Istio与外部注册中心 Istio为何需要对接外部注册中心 Istio 对 Kubernetes 具有较强的依赖性&#xff1a; 1.服务发现就是基于 Kubernetes 实现的&#xff0c;如果要使用 Istio&#xff0c;首先需要迁移到 Kubernetes 上&#xff0c;并使用 Kubernetes 的服务注册发现机制…

【数据挖掘】时间序列教程【二】

2.4 示例&#xff1a;颗粒物浓度 在本章中&#xff0c;我们将使用美国环境保护署的一些空气污染数据作为运行样本。该数据集由 2 年和 5 年空气动力学直径小于或等于 3.2017 \&#xff08;mu\&#xff09;g/m\&#xff08;^2018\&#xff09; 的颗粒物组成。 我们将特别关注来自…

认识GCC

GNU GNU是Linux系统下的一些工具包&#xff0c;GNU是GNU is Not Unix的缩写&#xff0c;因为当年Unix收费后&#xff0c;理查德马修斯托曼打算做一套GNU操作系统&#xff0c;当时GNU的工具包已经写好&#xff0c;就差内核即可组装成一个完整的操作系统&#xff0c;正好Linux写…

跨链 vs 多链

跨链 dApp 可以在部署在多个不同区块链上的多个不同智能合约上运行&#xff0c;而多链 dApp 则可以在不同网络上以多个单独的版本部署。 由于对区块空间的需求不断增加&#xff0c;Web3 应用层现在存在于数百个不同的区块链、二层网络和应用链上。这种现实催生了两个新术语——…

【教程】解决php微擎中的goto加密解密,一键解密工具

今天&#xff0c;我将向大家揭秘一款神奇的工具——goto解密工具&#xff0c;轻松解密这个看似棘手的问题。 无数开发者都曾因为php中的goto功能而头疼不已。goto解密工具其中之一就是解密goto代码。通过精妙的算法和强大的解析能力&#xff0c;它能够解密被goto加密的代码段&…

Vue项目设置网站小徽标

一、预期效果 自定义Vue项目的网站小徽标&#xff0c;用于显示网站的logo&#xff0c;效果大致如下 二、制作 .ico文件 2.1 打开比特虫官网 比特虫官网&#xff1a;https://www.bitbug.net/ 2.2 操作步骤如图 三、引入Vue项目 3.1 将生成的 .ico文件放入我们的 Vue 项目 3.…

servlet+JSP与SpringBoot+Vue项目交互——servlet请求SpringBoot接口

问题 servletJSP与SpringBootVue项目交互——servlet请求SpringBoot接口 详细问题 笔者前一段时间开发一个项目&#xff0c;使用的技术框架是servletJSP&#xff0c;现阶段开发的项目技术框架为SpringBootVue&#xff0c;笔者现在需要输入servletJSP请求SpringBoot接口&…

C语言编程—递归

递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a;从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&…

2024考研408-计算机组成原理第六章-总线学习笔记

文章目录 前言初识总线一、总线概述1.1、总线的概述1.1.1、认识总线1.1.2、设计总线需要的特性1.1.3、总线的分类①按照数据传输格式分&#xff08;串行、并行&#xff09;②按照总线功能连接的总线&#xff08;片内总线、系统总线、通信总线&#xff09;③按照时序控制方式&am…

css新特性(五)

css基础&#xff08;一&#xff09;css基础&#xff08;一&#xff09;_上半场结束&#xff0c;中场已休息&#xff0c;下半场ing的博客-CSDN博客Emmet语法Emmet语法_上半场结束&#xff0c;中场已休息&#xff0c;下半场ing的博客-CSDN博客css基础&#xff08;二&#xff09;c…

Retrofit注解

1. 注解类型 Retrofit路径结合的规则 2. 网络请求方法 2.1 Get请求 完整地址&#xff1a;http://mock-api.com/2vKVbXK8.mock/getUserInfo?iduserid 2.1.1 Query 创建Retrofit实例必须传入baseurl(http://mock-api.com/2vKVbXK8.mock/)&#xff0c;在GET("getUserIn…

Android跨平台语言分析

跨平台技术发展的三个阶段 第一阶段是混合开发的web容器时代 为了解决原生开发的高成本、低效率&#xff0c;出现了Hybrid混合开发原生中嵌入依托于浏览器的WebViewWeb浏览器中可以实现的需求在WebView中基本都可以实现但是Web最大的问题是&#xff0c;它的性能和体验与原生开发…

Mybatis面试题--MyBatis执行流程

首先我们知道Mybatis是目前最流行的持久层框架&#xff0c;当我们了解了执行流程&#xff0c;可以让我们理解各个组件的关系&#xff0c;以及Sql的执行过程&#xff08;参数映射、sql解析、执行和结果处理&#xff09; 1首先我们需要读取框架的核心配置文件 2接下来我们就要去操…

chatgpt赋能python:Python编程的好玩儿之处:介绍几个有趣的Python程序

Python编程的好玩儿之处&#xff1a;介绍几个有趣的Python程序 Python已经成为了一种非常受欢迎的编程语言&#xff0c;除了其在Web开发和数据科学领域的应用&#xff0c;它还能够完成很多好玩儿的事情&#xff01;接下来&#xff0c;我将介绍几个有趣的Python程序&#xff0c…