首页 > 编程语言 >C++转型操作符 VS 强制类型转换:为何前者更胜一筹?

C++转型操作符 VS 强制类型转换:为何前者更胜一筹?

时间:2025-01-19 15:02:00浏览次数:3  
标签:类型转换 std 转换 Derived C++ cast VS

image.png

C++中的类型转换操作

一、C++转型操作符的种类及用途

1.1 static_cast

  • 主要用途
    • 进行隐式类型转换,如将 int 转换为 float,或指针转换为 void*
    • 调用显式或隐式的转换函数,可增加代码可读性。
    • 在继承体系中进行类型转换:
      • 向上转换(派生类到基类)通常是安全的隐式转换,无需使用 static_cast
      • 向下转换(基类到派生类)需使用 static_cast,但不能通过虚拟继承转换,且不进行运行时检查,若目标类型并非对象实际类型会导致未定义行为。

示例:

static_cast<float>(1); // 将整数 1 显式转换为浮点数 1.0

1.2 const_cast

  • 主要用途
    • 专门用于添加或移除变量的 const 属性,这是其他 C++ 类型转换操作符无法做到的,也适用于 volatile 属性。
    • 在基于 const 重载成员函数时很有用,但修改原本为 const 的值是未定义行为,除非原始变量本身不是 const

示例:

const int num = 5;
int& nonConstNum = const_cast<int&>(num); 

1.3 dynamic_cast

  • 主要用途
    • 专门处理多态,可将具有虚函数(声明或继承)的多态类型指针或引用转换为其他类类型的指针或引用。
    • 能在继承体系中向上、向下甚至横向转换,会在运行时寻找目标对象:
      • 对于指针,若找不到合适对象返回 nullptr
      • 对于引用,会抛出 std::bad_cast 异常。
    • 限制:
      • 不能在存在“钻石继承”且未使用虚拟继承的情况下工作。
      • 只能通过公共继承进行转换,无法通过受保护或私有继承进行转换。

示例:

class Base {
public:
    virtual void show() {
        std::cout << "Base class" << std::endl;
    }
};

class Derived : public Base {
public:
    void show() override {
        std::cout << "Derived class" << std::endl;
    }
};

int main() {
    Base* b = new Derived;
    Derived* d = dynamic_cast<Derived*>(b);
    if (d!= nullptr) {
        d->show();  // 转换成功,输出 "Derived class"
    } else {
        std::cout << "Conversion failed" << std::endl;
    }
    delete b;
    return 0;
}

1.4 reinterpret_cast

  • 主要用途
    • 最危险的类型转换操作符,直接将一种类型转换为另一种类型,如将一个指针的值转换为另一个指针类型,或将指针存储在 int 中。
    • 保证通常情况下将结果转换回原始类型可得到完全相同的值(前提是中间类型不小于原始类型)。
    • 常被滥用于奇怪的转换和位操作,C++20 引入的 std::bit_cast 是更好选择。

示例:

int num = 10;
double* ptr = reinterpret_cast<double*>(&num); 

1.5 C++20 中的 std::bit_cast

  • 主要用途
    • 将源对象的位和字节直接复制到目标类型的对象中,是符合标准的类型转换方式。
    • 要求源对象和目标对象必须大小相同且是平凡可复制的,声明在 <bit> 头文件中。
    • 若无法使用 C++20,可用 memcpy 实现类似功能。

示例:

#include <bit>
struct S1 {
    int a;
    float b;
};
struct S2 {
    int c;
    float d;
};
S1 s1 = {1, 2.0f};
S2 s2 = std::bit_cast<S2>(s1); 

二、C++转型操作符的优势

2.1 易于识别

  • C++的转型操作符(如 static_cast<int>(x))在代码中的可识别性高于 C 风格的强制类型转换(如 (int)x),有助于代码阅读和维护,使开发者更快理解代码意图。

2.2 安全性

  • C++的转型操作符提供更高安全性,如 dynamic_cast 在运行时检查转换有效性:
    • 对于指针,转换不合法会返回 nullptr
    • 对于引用,会抛出异常。
  • C 风格强制类型转换仅在编译时进行,不考虑转换合法性,可能导致运行时错误。

2.3 精确性

  • C++的转型操作符更精确,例如 const_cast 专门用于修改 constvolatile 属性,C 风格强制类型转换无法实现此功能,可减少类型转换错误。

三、示例对比

3.1 使用 C++转型操作符

class Base {
public:
    virtual void show() {
        std::cout << "Base class" << std::endl;
    }
};

class Derived : public Base {
public:
    void show() override {
        std::cout << "Derived class" << std::endl;
    }
};

int main() {
    Base* b = new Derived;
    Derived* d = dynamic_cast<Derived*>(b);
    if (d!= nullptr) {
        d->show();  // 转换成功,输出 "Derived class"
    } else {
        std::cout << "Conversion failed" << std::endl;
    }
    delete b;
    return 0;
}
  • 在上述示例中,使用 dynamic_cast 将基类指针 b 转换为派生类指针 d,由于 b 指向 Derived 类对象,转换成功,避免了非法访问。

3.2 使用 C 风格强制类型转换

class Base {
public:
    void show() {
        std::cout << "Base class" << std::endl;
    }
};

class Derived : public Base {
public:
    void show() {
        std::cout << "Derived class" << std::endl;
    }
};

int main() {
    Base* b = new Base;
    Derived* d = (Derived*)b;  // C 风格强制类型转换
    d->show();  // 未定义行为,可能导致程序崩溃
    delete b;
    return 0;
}
  • 这里使用 C 风格强制类型转换将 Base 类指针 b 转换为 Derived 类指针 d,但 b 实际指向 Base 类对象,会导致未定义行为和程序崩溃。

四、结论

  • C++的转型操作符在可读性、安全性和精确性方面优于 C 风格的强制类型转换,虽可能稍复杂,但可清晰表达程序员意图,减少类型转换错误,提高代码质量、可维护性,减少运行时错误,使程序更健壮,建议在 C++编程中优先使用。

五、参考资料

标签:类型转换,std,转换,Derived,C++,cast,VS
From: https://blog.csdn.net/Z_oioihoii/article/details/145233220

相关文章

  • 【华为OD-E卷 - 最长连续子序列 100分(python、java、c++、js、c)】
    【华为OD-E卷-最长连续子序列100分(python、java、c++、js、c)】题目有N个正整数组成的一个序列。给定整数sum,求长度最长的连续子序列,使他们的和等于sum,返回此子序列的长度,如果没有满足要求的序列,返回-1输入描述第一行输入是:N个正整数组成的一个序列第二行输入是:给定......
  • 【华为OD-E卷 - 找出两个整数数组中同时出现的整数 100分(python、java、c++、js、c)】
    【华为OD-E卷-找出两个整数数组中同时出现的整数100分(python、java、c++、js、c)】题目现有两个整数数组,需要你找出两个数组中同时出现的整数,并按照如下要求输出:有同时出现的整数时,先按照同时出现次数(整数在两个数组中都出现并目出现次数较少的那个)进行归类,然后按照出......
  • 备赛蓝桥杯——day4:C++篇
    第二章:C/C++输入输出(上)1.getchar和putchargetchar()和putchar()是属于C语⾔的库函数,C++是兼容C语⾔的,所以C++中只要正确包含头⽂件也可以正常使⽤这两个函数。1.1getchar函数原型:intgetchar(void);getchar()函数返回用户从键盘输入的一个字符(本质是返回他的asc码值),......
  • 【华为OD-E卷 - 计算疫情扩散时间 100分(python、java、c++、js、c)】
    【华为OD-E卷-计算疫情扩散时间100分(python、java、c++、js、c)】题目在一个地图中(地图由n*n个区域组成),有部分区域被感染病菌。感染区域每天都会把周围(上下左右)的4个区域感染。请根据给定的地图计算,多少天以后,全部区域都会被感染。如果初始地图上所有区域全部都被感......
  • 打卡信奥刷题(628)用C++信奥P8053[普及组/提高] [COCI2015-2016#4] DEATHSTAR
    [COCI2015-2016#4]DEATHSTAR题目描述你排除万难,潜入了DeathStar。要想摧毁它,你需要一个长度为nnn的数组a......
  • Windows图形界面(GUI)-QT-C/C++ - Qt QToolBox详解教程
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​链接点击跳转博客主页目录QToolBox基础概述QToolBox简介使用场景QToolBox常见样式选项卡式界面页面内容动态管理页面QToolBox属性设置添加和删除页面页面标题页面索引QToolBox内容操作添加页面插入页面删......
  • Windows图形界面(GUI)-QT-C/C++ - Qt QGroupBox详解教程
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​链接点击跳转博客主页目录QGroupBox基础概念QGroupBox简介使用场景QGroupBox常见样式框架和标题可启用/禁用扁平化样式QGroupBox属性设置标题​编辑对齐方式启用状态​编辑扁平化样式QGroupBox的内容操作......
  • C++模板--packaged_task 如何打包 lambda 和函数指针?
    从它的构造函数上看,似乎不能接受lambda和函数指针作为构造函数的参数但可以通过如下自定义推导规则来实现.这实际上是DeductionGuides技术//1template<class_Rp,class..._Args>packaged_task(_Rp(*)(_Args...))->packaged_task<_Rp(_Args...)>;//2template......
  • 《一个月教你玩转C++》系列第九章:C++中的 if 和 else(2)
    第九章:C++中的if和else(2)本章布丁将介绍elseif语句,关系运算符和逻辑运算符。我们将通过简单的例子来展示它们的用法哟!elseif语句elseif语句是if语句的扩展,它允许我们在第一个条件不满足时继续检查其他条件。基本用法:当我们需要根据多个条件执行不同的代码块......
  • 扬帆数据结构算法之雅舟航程,漫步C++幽谷——链表分类探析与双链表之定义与构筑
    人无完人,持之以恒,方能见真我!!!共同进步!!文章目录一、链表的分类二、双链表的实现1.双链表结构的定义2.双链表的初始化和销毁初始化函数1初始化函数2销毁函数3.双链表的打印以及节点的申请打印函数节点的申请4.双链表的头插和尾插头插函数尾插函数5.双链表的查找和......