当前位置: 首页 > news >正文

C++ 类型转换

一、C语言中的类型转化

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

void foo(double d);
foo(10);  // 10被转换为double类型

int x = 3.14;  // x = 3(小数部分被截断)

int* p = &i;
// 显示的强制类型转换
int address = (int) p;
缺点:转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换。

 

二、C++中的类型转化

1.隐式类型转换

与C语言类似,编译器自动完成,但C++的隐式转换规则更严格,尤其在面向对象编程中。

1. ​基本类型的隐式转换
  • 遵循C语言的算术提升规则(如 char → int → double)。
  • 类类型可以通过构造函数或类型转换运算符隐式转换:
    class MyInt {
    public:
        MyInt(int x) {}  // 允许从int隐式转换为MyInt
    };
    MyInt a = 5;  // 隐式调用构造函数
2. ​派生类到基类的转换
  • 在继承体系中,派生类对象可以隐式转换为基类指针或引用(会切片):
    class Base {};
    class Derived : public Base {};
    Derived d;
    Base* pb = &d;  // 隐式向上转换(Upcasting)

2.显式类型转换(强制转换)

C++提供了四种类型安全的强制转换操作符,取代C风格的强制转换((type)value),以增强代码可读性和安全性。

1. ​static_cast
  • 用途:用于明确定义的、编译时已知的类型转换。
  • 示例
    int main()
    {
      double d = 12.34;
      int a = static_cast<int>(d);
      cout<<a<<endl;
      return 0;
    }
  • 限制
    • 不能用于无关类型(如指针和整数之间)。
    • 无法去除 const 或 volatile 属性。
2. ​dynamic_cast
  • 用途
    dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)。
    向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)。
    向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)。
    注意:
    1. dynamic_cast只能用于父类含有虚函数的类。
    2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。
  • 示例
    class A
    {
    public:
    	virtual void f() {}
    };
    class B : public A
    {};
    void fun(A* pa)
    {
    	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
    	B* pb1 = static_cast<B*>(pa);
    	B* pb2 = dynamic_cast<B*>(pa);
    	cout << "pb1:" << pb1 << endl;
    	cout << "pb2:" << pb2 << endl;
    }
    int main()
    {
    	A a;
    	B b;
    	fun(&a);
    	fun(&b);
    	return 0;
    }

 

3. ​const_cast
  • 用途:添加或移除 const 属性,常用于删除const属性。
  • 示例
    void Test ()
    {
      const int a = 2;
      int* p = const_cast<int*>(&a );
      *p = 3;
      cout<< a <<endl;
    }
4. ​reinterpret_cast
  • 用途:低层次的类型重新解释(如指针与整数之间的转换),不进行类型检查,用于将一种类型转换为另一种不同的类型。

  • 示例
    int main()
    {
    	double d = 12.34;
    	int a = static_cast<int>(d);
    	cout << a << endl;
    	// 这里使用static_cast会报错,应该使用reinterpret_cast
    	//int *p = static_cast<int*>(a);
    	int* p = reinterpret_cast<int*>(a);
    	return 0;
    }
  • 风险:可能导致未定义行为,应尽量避免使用。

三、运行时类型识别(RTTI)

我们每个人都有一个身份证,身份证上记录了这个人的姓名、出生日期、性别等信息。在C++中,每个对象也有一个“身份证”,这个“身份证”记录了对象的类型信息。RTTI就像是读取身份证信息的工具,它允许我们在程序运行时查看对象的类型。

1.typeid操作符

typeid操作符就像是你查看某个人的身份证,它会返回一个type_info对象,这个对象包含了类型的信息。

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {};

int main() {
    Base* basePtr = new Derived();

    std::cout << "Type of basePtr: " << typeid(*basePtr).name() << std::endl;

    delete basePtr;
    return 0;
}

 在这个例子中,typeid(*basePtr)会返回一个type_info对象,表示basePtr指向的对象的实际类型。注意,为了使typeid能够正确工作,基类通常需要至少有一个虚函数(即多态类型)。

 

2.dynamic_cast操作符

dynamic_cast操作符就像是在不同类型的证件之间进行安全转换。它主要用于将基类指针或引用转换为派生类指针或引用,并且只有在转换是安全的情况下才会成功。

#include <iostream>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    void derivedFunction() {
        std::cout << "Derived function called." << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();

    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    if (derivedPtr) {
        derivedPtr->derivedFunction();
    } else {
        std::cout << "Conversion failed." << std::endl;
    }

    delete basePtr;
    return 0;
}

在这个例子中,dynamic_cast尝试将basePtr转换为Derived*。如果basePtr实际上指向一个Derived对象,转换会成功,否则会返回nullptr

3. RTTI的缺点
  • 性能开销:RTTI会增加程序的运行时开销,特别是在频繁使用的情况下。
  • 代码复杂性:过度依赖RTTI可能会使代码变得复杂,难以维护。
4. 适用场景
  • 多态类型:RTTI主要用于处理多态类型,即至少有一个虚函数的类。
  • 调试和日志:在调试或记录日志时,RTTI可以帮助识别对象的实际类型。
  • 动态类型转换:当需要在运行时安全地转换类型时,可以使用dynamic_cast
http://www.dtcms.com/a/98459.html

相关文章:

  • java基础以及内存图
  • presto任务优化参数
  • RAG、大模型与智能体的关系
  • Binlog、Redo log、Undo log的区别
  • 【从零实现Json-Rpc框架】- 项目实现 - Dispatcher模块实现篇
  • Eigen 3
  • Jenkins 持续集成:Linux 系统 两台机器互相免密登录
  • 27_promise
  • 基于Selenium的IEEE Xplore论文数据爬取实战指南
  • 通信协议之串口
  • Java面试黄金宝典22
  • 【Basys3】外设-灯和数码管
  • 使用ANTLR4解析Yaml,JSON和Latex
  • SpringSecurity配置(自定义退出登录)
  • CubeMx——串口与 printf 打印
  • Python每日一题(9)
  • MyBatis的第一天笔记
  • 标书工具私有部署技术方案
  • springmvc redirect 使用https后跳转到了http://域名:443问题处理
  • 梯度裁剪(Gradient Clipping)
  • 【商城实战(97)】ELK日志管理系统的全面应用
  • 大模型最新面试题系列:微调篇之微调框架(三)
  • MySQL 常见面试问题总结
  • web3包含哪些关键技术栈,一些成功使用场景的分享
  • [FGPA基础学习]分秒计数器的制作
  • flutter 专题 七十 Flutter应用开发之webview_flutter插件
  • C盘清理技巧分享:PE Dism++ 空间清理篇
  • Vue学习笔记集--watch
  • 【SQL】MySQL基础2——视图,存储过程,游标,约束,触发器
  • 关于音频采样率,比特,时间轴的理解