C++ 是一门弱类型的语言, 提供了许多复杂和灵巧类型转换的方式. 笔者之前写的 Python 与 Go 都是强类型的语言, 对这种弱类型的设计实在是接受无力啊~~ ( 生活所迫, 工作还得写 C++ 啊~~)C++ 语言提供了四种类型转换的操作: static_cast,dynamic_cast,reinterpret_cast,const_cast, 今天就来聊一聊, 在 C++ 之中应该如何来使用这些类型转换的.
1. 旧式类型转换
开门见山, 先聊聊笔者对类型转换的看法吧. 从设计上看, 一门面向对象的语言是不一样提供类型转换的, 这种方式破坏了类型系统. C++ 为了兼容 C 也不得不吞下这个苦果, 在实际进行编程工作的过程之中, 并不太推荐大家使用类型转换.(Java 在这里就做了一些妥协, 在基本类型之中提供了类型转换. 对于对象类型则不提供类型转换这种黑魔法)
C++ 之中提供了两种类型转换的方式, 第一种方式沿用了 C 语言之中的类型转换, 称之为旧式类型转换. 说起来也很简单, 举个栗子:
- char x = 'c';
- int y = (int) x;
这是最简单的一个旧式类型转换, 一个 char 类型被装换为一个 int 类型. 但是这种旧式的类型转换是存在问题的: 过于粗暴且极易失控, 所以 C++ 新提供了四种新式的类型转换来替代旧式的类型转换, 这四种类型转换分别用于不用的转换场景, 接下来笔者来一一梳理一下它们的用法.
2. 新式的类型转换
C++ 语言提供了四种新式类型转换的操作:
static_cast,dynamic_cast,reinterpret_cast,const_cast, 这些操作都依托了 C++ 的模板来使用, 标准的用法是
xxx_cast < 转换类型>(转换参数)
这种新式转换优于旧式的转换就在于: 编译器可以在转换期间进行更多的检查, 对于一些不符合转换逻辑的转换进行一些纠错. 而某些类型转换操作可以利用 RTTI(运行时类型信息)来确保类型转换的合理, 这是旧式的类型转换无法达成的效果.
const_cast
从名字上就可以看出来, 这厮是用来对 const 属性进行类型转换的. 这个名字取得有些偏颇, 它同样适用于 volatile 属性. 它可以为变量添加或接触上述属性, 它也是新式转换之中唯一具有这个能力的转换方式, 没有什么额外的坑, 用户体验良好:(但是偶尔对于 const 属性的转换需要执行多步, 先通过 const_cast 转换, 再借助其他转换)
- // 函数需要传递 const 属性的变量, 如 atoi
- atoi(const_cast<const char*>(char_ptr))
- static_cast
static_cast 是静态的转换形式, 不通过运行时类型检查来保证转换的安全性. 它主要用于如下场合:
用于基本数据类型之间的转换, 如把 long 转换成 char, 把 int 转换成 char.
用于面向对象编程之中基类与派生类之间的指针或引用转换. 它分为两种
上行转换 (把派生类的指针或引用转换成基类) 是安全的;
下行转换(把基类指针或引用转换成派生类), 由于没有运行时的动态类型检查, 所以是不安全的.
把非 const 属性的类型转换为 const 类型, 但是不能将 const 类型转换为非 const 类型, 这个得借助前文的 const_cast.
void 的空指针转换成其他类型的的空指针.
上面的几种概念的比较好理解, 这里笔者着重聊聊上下行转换: 不啰嗦, 看代码:
- class Bird {
- public:
- virtual void fly() {
- cout <<"I can fly." << endl;
- }
- };
- class Penguin:public Bird {
- public:
- void fly() {
- cout << "I can't fly." << endl;
- }
- };
上述代码我们定义了两个类 Bird 与 Penguin:
- int main() {
- Penguin* p = new (std::nothrow) Penguin;
- Bird* b = static_cast<Bird *>(p);
- b->fly();
- return 0;
- }
上行转换, 将派生类转换为基类的指针, 合法.
- int main() {
- Bird* b = new (std::nothrow) Bird;
- Penguin* p = static_cast<Penguin *>(b);
- if (p != nullptr) {
- p->fly();
- } else {
- }
- return 0;
- }
下行转换, 将基类转换为派生类的指针, 此时程序的行为是不确定的. 并且编译期间并没有警告, 这是一种十分危险的用法, 所以使用时一定要谨小慎微. 所以接下来就要请出下一种转换 dynamic_cast, 这是在对象基类和派生类之间转换推荐的一种方式.
dynamic_cast
dynamic_cast 主要用于在类层次间进行上下行转换时, 它与 static_cast 的最大的区别就在于 dynamic_cast 能够在运行时进行类型检查的功能, 所以做起类型转换比 static_cast 更安全, 但是 dynamic_cast 会耗费更多的系统资源. dynamic_cast 是无法通过旧式类型转换完成的类型转换.
- int main() {
- Bird* b = new (std::nothrow) Bird;
- Penguin* p = dynamic_cast<Penguin *>(b);
- if (p != nullptr) {
- p->fly();
- } else {
- cout <<"cast failed" << endl;
- }
- return 0;
- }
dynamic_cast 对于非法的下行转换会返回空指针, 所以可以在一定程度上避免不安全的类型转换.
reinterpret_cast
reinterpret_cast 主要用于指针类型之间的转换, 和对象到指针类型之间的转换. reinterpret 就是对数据的比特位重新解释转换为我们需要转换的对象. 其与 static_cast 的区别在于其能处理有继承关系类的指针和内置数据类型的转换. 而 reinterpret_cast 能够处理所有指针 (引用) 之间的转换
- int main() {
- Bird* b = new (std::nothrow) Bird;
- Penguin* p = reinterpret_cast<Penguin *>(b);
- if (p != nullptr) {
- p->fly();
- } else {
- cout << "cast failed" << endl;
- }
- return 0;
- }
上述代码依旧可以转换成功, 结果不可控.
3. 小结
梳理完 C++ 新引进的四种类型转换符之后, 想必大家在实践之中可以很好的运用好这些 C++ 的类型转换. 后续笔者还会继续深入的探讨有关 C++ 之中类型系统相关的内容, 欢迎大家多多指教.
来源: https://www.cnblogs.com/happenlee/p/9722458.html