您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

C++ 20类型转换指南:使用场景与优秀实践

时间:2023-11-20 14:55:08  来源:微信公众号  作者:coding日记

类型转换 (Casts)

C++ 提供了五种特定的类型转换:const_cast<>()、static_cast<>()、reinterpret_cast<>()、dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()。

请注意,旧的 C 风格类型转换(如 (int)myFloat)在 C++ 中仍然有效,并在现有代码库中广泛使用。C 风格的类型转换涵盖了所有四种 C++ 类型转换,因此它们更容易出错,因为您试图实现的目的并不总是显而易见的,可能会得到意外的结果。我强烈建议您在新代码中只使用 C++ 风格的类型转换,因为它们更安全,且在代码中更加突出。

C++ 20类型转换指南:使用场景与优秀实践

虚拟基类

模糊基类出现在多个父类共有一个共同的父类时。推荐的解决方案是确保共享的父类自身不具有任何功能。这样,其方法永远不会被调用,从而避免了歧义问题。C++ 还有另一种机制,称为虚拟基类,用于解决您希望共享的父类具有自己功能的情况。

如果共享的父类是一个虚拟基类,则不会有任何歧义。以下代码在 Animal 基类中添加了一个 sleep() 方法,并修改了 Dog 和 Bird 类,使它们作为虚拟基类从 Animal 继承。如果不使用虚拟基类,对 DogBird 对象的 sleep() 调用将是模糊的,并会生成编译器错误,因为 DogBird 将具有两个 Animal 子对象,一个来自 Dog,一个来自 Bird。然而,当 Animal 被虚拟继承时,DogBird 只有一个 Animal 类的子对象,因此调用 sleep() 不会有歧义。

class Animal {
public:
    virtual void eat() = 0;
    virtual void sleep() { cout << "zzzzz...." << endl; }
};

class Dog : public virtual Animal {
public:
    virtual void bark() { cout << "Woof!" << endl; }
    void eat() override { cout << "The dog ate." << endl; }
};

class Bird : public virtual Animal {
public:
    virtual void chirp() { cout << "Chirp!" << endl; }
    void eat() override { cout << "The bird ate." << endl; }
};

class DogBird : public Dog, public Bird {
public:
    void eat() override { Dog::eat(); }
};

int mAIn() {
    DogBird myConfusedAnimal;
    myConfusedAnimal.sleep(); // 因为虚拟基类而不模糊
}

 

注意:虚拟基类是避免类层次结构中歧义的好方法。

 

类型转换 (Casts)

C++ 提供了五种特定的类型转换:const_cast<>()、static_cast<>()、reinterpret_cast<>()、dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()。第一种在第 1 章中讨论过。第 1 章还介绍了用于某些基本类型之间转换的 static_cast<>(),但在继承的上下文中还有更多内容。现在您已经熟悉编写自己的类并理解类继承,是时候更仔细地看看这些类型转换了。

请注意,旧的 C 风格类型转换(如 (int)myFloat)在 C++ 中仍然有效,并在现有代码库中广泛使用。C 风格的类型转换涵盖了所有四种 C++ 类型转换,因此它们更容易出错,因为您试图实现的目的并不总是显而易见的,可能会得到意外的结果。我强烈建议您在新代码中只使用 C++ 风格的类型转换,因为它们更安全,且在代码中更加突出。

static_cast()

(1) 使用场景

static_cast()用于执行语言直接支持的显式转换。例如,将int转换为double以避免整数除法:

int i { 3 };
int j { 4 };
double result { static_cast<double>(i) / j };

static_cast() 也可用于执行因用户定义的构造函数或转换例程而允许的显式转换。例如,如果类 A 有一个接受 B 对象的构造函数,则可以使用 static_cast() 将 B 对象转换为 A 对象。

(2) 在继承中的应用

static_cast()可用于继承层次结构中的向下转型:

class Base { /* ... */ };
class Derived : public Base { /* ... */ };

Base* b { nullptr };
Derived* d { new Derived{} };
b = d; // 向上转型,不需要转换。
d = static_cast<Derived*>(b); // 向下转型,需要转换。

(3) 注意事项

  • static_cast() 不执行运行时类型检查。可以将任何 Base 指针转换为 Derived 指针,即使 Base 实际上不是 Derived。
  • static_cast() 不是万能的,它不能将一种类型的指针转换为另一种完全无关的类型的指针,也不能在没有转换构造函数的情况下直接将一种类型的对象转换为另一种类型的对象。

reinterpret_cast()

(1) 使用场景

  • reinterpret_cast() 比 static_cast() 更强大但同时也更不安全。它用于执行 C++ 类型规则技术上不允许的某些转换。
  • 可以用来将一种类型的引用转换为另一种类型的引用,即使类型之间完全无关。
  • 常用于将指针类型转换为任何其他指针类型,包括将任何类型的指针转换为 void*。

(2) 注意事项

  • 使用 reinterpret_cast() 要格外小心,因为它允许你在不执行任何类型检查的情况下进行转换。
  • 可以用 reinterpret_cast() 将指针转换为足够大以容纳它的整型类型,反之亦然。但是,尝试将 64 位指针转换为 32 位整数会导致编译错误。

std::bit_cast()

(1) 特点

  • std::bit_cast() 是 C++20 中引入的,定义在 <bit> 头文件中。
  • 它是标准库中唯一的类型转换,其他类型转换是 C++ 语言本身的一部分。
  • bit_cast() 类似于 reinterpret_cast(),但它创建一个给定目标类型的新对象,并将源对象的位复制到这个新对象中。它有效地将源对象的位解释为目标对象的位。
  • bit_cast() 要求源对象和目标对象的大小相同且都是平凡可复制的。

(2) 示例

float asFloat { 1.23f };
auto asUint { bit_cast<unsigned int>(asFloat) };
if (bit_cast<float>(asUint) == asFloat) {
    cout << "Roundtrip success." << endl;
}

(3) 应用场景

bit_cast() 的一个用例是用于平凡可复制类型的二进制 I/O。例如,可以将这些类型的单个字节写入文件,读取文件时,可以使用 bit_cast() 正确解释从文件读取的字节。

 

平凡可复制类型通常具有以下特征:

 

 

  • 无自定义析构函数:类型没有自定义的析构函数。
  • 无自定义或虚拟构造函数:类型没有自定义的构造函数,也没有虚拟构造函数。
  • 无虚函数和虚基类:类型不包含虚函数,并且不从虚基类继承。
  • 可简单拷贝其状态:类型的所有成员可以通过简单的内存拷贝来复制,没有需要特殊处理的成员(如指针或动态分配的资源)。

dynamic_cast()

(1) 特点

  • dynamic_cast() 在继承层次结构中提供了运行时类型检查。
  • 它可以用于转换指针或引用。
  • 如果转换没有意义,dynamic_cast() 将返回空指针(对于指针版本)或抛出 std::bad_cast 异常(对于引用版本)。

(2) 示例

Base* b;
Derived* d { new Derived{} };
b = d;
d = dynamic_cast<Derived*>(b);

Base base;
Derived derived;
Base& br { base };
try {
    Derived& dr { dynamic_cast<Derived&>(br) };
} catch (const bad_cast&) {
    cout << "Bad cast!" << endl;
}

(3) 与其他类型转换的区别

  • 与 static_cast() 或 reinterpret_cast() 不同,dynamic_cast() 执行运行时(动态)类型检查,而后者即使转换错误也会执行转换。
  • 为了使用 dynamic_cast(),你的类必须至少有一个虚拟方法。如果类没有虚拟表(vtable),尝试使用 dynamic_cast() 将导致编译错误。

C++ 类型转换总结

情境

推荐的转换方法

说明

移除 const 属性

const_cast()

用于移除对象的 const 属性

语言直接支持的显式转换

static_cast()

例如,从 int 转换到 double 或 bool

用户定义的构造函数或转换支持的显式转换

static_cast()

用于用户定义的转换

一个类的对象转换为另一个(无关)类的对象

bit_cast()

用于无关类之间的对象转换

同一继承层次中的类的指针对象转换

dynamic_cast() (推荐) 或 static_cast()

用于继承层次中的指针对象转换

同一继承层次中的类的引用对象转换

dynamic_cast() (推荐) 或 static_cast()

用于继承层次中的引用对象转换

不相关类型的指针转换

reinterpret_cast()

用于完全不相关的指针类型之间的转换

不相关类型的引用转换

reinterpret_cast()

用于完全不相关的引用类型之间的转换

函数指针之间的转换

reinterpret_cast()

用于函数指针之间的转换

注意事项

  • 使用 const_cast() 应谨慎,因为它改变了对象的 const 性质。
  • static_cast() 是最常用的转换类型,适用于许多标准和用户定义的转换。
  • bit_cast() 用于位级别的类型转换,要求源和目标类型大小相同且都是平凡可复制的。
  • dynamic_cast() 在继承层次中提供运行时类型检查,但要求类至少有一个虚拟方法。
  • reinterpret_cast() 提供更广泛的转换能力,但也带来更高的风险,因为它不执行类型检查。


Tags:C++   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除。
▌相关推荐
C++ 20类型转换指南:使用场景与优秀实践
类型转换 (Casts)C++ 提供了五种特定的类型转换:const_cast<>()、static_cast<>()、reinterpret_cast<>()、dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()。请注意,旧...【详细内容】
2023-11-20  Tags: C++  点击:(0)  评论:(0)  加入收藏
C++中的内联函数:提高程序效率
一、内联函数的定义和特点内联函数是一种特殊的函数,它通过在编译时将函数调用替换为函数体中的代码,以减少函数调用的开销,从而提高程序的执行效率。内联函数通常用于那些函数...【详细内容】
2023-11-20  Tags: C++  点击:(2)  评论:(0)  加入收藏
C++中函数参数的优秀传递方式
在C++编程中,函数参数的传递方式对于函数的性能和可读性有着重要的影响。本文将探讨C++中函数参数的最佳传递方式,包括值传递、引用传递和指针传递的优缺点,并给出一些示例代码...【详细内容】
2023-11-17  Tags: C++  点击:(8)  评论:(0)  加入收藏
C++编译优化:如何优化编译器的输出代码质量
在当今的软件开发世界中,C++以其高效的性能和广泛的应用领域而受到开发者的青睐。然而,随着项目规模的不断扩大和性能需求的日益增长,如何优化编译器的输出代码质量成为了亟待...【详细内容】
2023-11-16  Tags: C++  点击:(16)  评论:(0)  加入收藏
C++异常处理:如何使用try、catch、throw
一、概述在C++中,异常处理是一种重要的编程技术,用于处理程序运行过程中可能出现的意外情况。异常处理通过使用try、catch和throw关键字来实现。这些关键字共同协作,帮助程序员...【详细内容】
2023-11-13  Tags: C++  点击:(19)  评论:(0)  加入收藏
C++初始化列表:探索多种初始化方式
C++中的初始化列表是一种方便且强大的工具,用于在创建对象时初始化成员变量。初始化列表在C++中广泛应用于各种类型的对象,包括数据成员、静态成员、常量成员等。初始化列表的...【详细内容】
2023-11-13  Tags: C++  点击:(26)  评论:(0)  加入收藏
JetBrains 推出全新 C/C++ IDE 开发工具 CLion Nova,预览版免费
IT 之家 11 月 13 日消息,JetBrains 公司推出新的 C 语言和 C++ 开发 IDE &mdash;&mdash; CLion Nova,使用 ReSharper C++/Rider C++ 语言引擎而不是原版 CLion“旧版”引擎开...【详细内容】
2023-11-13  Tags: C++  点击:(16)  评论:(0)  加入收藏
C++中线程编程的应用,注意点,源代码解析
线程编程在C++中的应用线程编程是一种允许程序并发执行多个任务的技术。在C++中,线程编程可以通过使用C++11标准库中的头文件来实现。线程编程的应用非常广泛,可以用于提高程...【详细内容】
2023-11-06  Tags: C++  点击:(29)  评论:(0)  加入收藏
C++实现数独求解器:解密数独的算法之美
数独是一种经典的逻辑推理游戏,通过填充9x9方格中的数字,使得每一行、每一列和每一个3x3的小方格内都包含了1到9的数字,且不重复。本文将介绍如何使用C++编写一个数独求解器,通...【详细内容】
2023-11-06  Tags: C++  点击:(18)  评论:(0)  加入收藏
C++与设计模式有什么关系?
引言C++是一种强大的编程语言,它提供了许多工具和库以支持面向对象编程和通用编程。设计模式是一种用于解决特定问题的经过验证的解决方案。C++的灵活性和设计模式的普遍性相...【详细内容】
2023-11-02  Tags: C++  点击:(25)  评论:(0)  加入收藏
▌简易百科推荐
C++ 20类型转换指南:使用场景与优秀实践
类型转换 (Casts)C++ 提供了五种特定的类型转换:const_cast<>()、static_cast<>()、reinterpret_cast<>()、dynamic_cast<>() 和 C++20 引入的 std::bit_cast<>()。请注意,旧...【详细内容】
2023-11-20  coding日记  微信公众号  Tags:C++   点击:(0)  评论:(0)  加入收藏
C++中的内联函数:提高程序效率
一、内联函数的定义和特点内联函数是一种特殊的函数,它通过在编译时将函数调用替换为函数体中的代码,以减少函数调用的开销,从而提高程序的执行效率。内联函数通常用于那些函数...【详细内容】
2023-11-20  鲨鱼编程  微信公众号  Tags:C++   点击:(2)  评论:(0)  加入收藏
C++中函数参数的优秀传递方式
在C++编程中,函数参数的传递方式对于函数的性能和可读性有着重要的影响。本文将探讨C++中函数参数的最佳传递方式,包括值传递、引用传递和指针传递的优缺点,并给出一些示例代码...【详细内容】
2023-11-17  鲨鱼编程  微信公众号  Tags:C++   点击:(8)  评论:(0)  加入收藏
C++编译优化:如何优化编译器的输出代码质量
在当今的软件开发世界中,C++以其高效的性能和广泛的应用领域而受到开发者的青睐。然而,随着项目规模的不断扩大和性能需求的日益增长,如何优化编译器的输出代码质量成为了亟待...【详细内容】
2023-11-16  鲨鱼编程  微信公众号  Tags:C++   点击:(16)  评论:(0)  加入收藏
C 语言编译器(IDE)初学者指南:选择适合你的工具
一、前言在当今的软件开发世界中,C语言仍然是一种非常重要的编程语言,被广泛用于系统编程,游戏开发,嵌入式系统等领域。对于C语言的初学者来说,选择一款合适的编译器(IDE)是他们学...【详细内容】
2023-11-14  鲨鱼编程  微信公众号  Tags:C 语言   点击:(18)  评论:(0)  加入收藏
拿捏C语言,就看这一篇!
嵌入式系统是我们日常生活中无处不在的一部分。从智能手机到家用电器,从汽车到医疗设备,嵌入式系统的应用范围广泛且不断增长。C语言是一种高效、简洁、灵活的编程语言,是嵌入...【详细内容】
2023-11-14  华清远见西安中心    Tags:C语言   点击:(13)  评论:(0)  加入收藏
C++异常处理:如何使用try、catch、throw
一、概述在C++中,异常处理是一种重要的编程技术,用于处理程序运行过程中可能出现的意外情况。异常处理通过使用try、catch和throw关键字来实现。这些关键字共同协作,帮助程序员...【详细内容】
2023-11-13  鲨鱼编程  微信公众号  Tags:C++   点击:(19)  评论:(0)  加入收藏
C++初始化列表:探索多种初始化方式
C++中的初始化列表是一种方便且强大的工具,用于在创建对象时初始化成员变量。初始化列表在C++中广泛应用于各种类型的对象,包括数据成员、静态成员、常量成员等。初始化列表的...【详细内容】
2023-11-13  鲨鱼编程    Tags:C++   点击:(26)  评论:(0)  加入收藏
C#参数传递
前几天一个学员在学习C#与参数传递交互时,也不知道参数传递可以用来做什么 。下面我们就详细讲讲C# 和参数传递交互的相关知识。C#是一种面向对象的编程语言,支持多种参数传...【详细内容】
2023-11-11  华山自控编程    Tags:C#   点击:(7)  评论:(0)  加入收藏
C#与高级控件
前几天一个学员在学习C#与高级控件交互时,也不知道高级控件可以用来做什么 。下面我们就详细讲讲C# 和高级控件交互的相关知识。C#是一种功能丰富的面向对象编程语言,它包含...【详细内容】
2023-11-10  华山自控编程    Tags:C#   点击:(9)  评论:(0)  加入收藏
站内最新
站内热门
站内头条