关于 C++ 中的强制转换 - 基础篇
引言
假设有基类 A
,包含了虚函数 func1
,以及有派生类 B
,继承于类 A
,派生类 B
中实现了函数 func1
。此时可以用 A
类型的指针指向 B
类型的对象,并用 A
类型的指针调用 B
类型对象中的函数 func1
。这时,就形成了多态。包含虚函数的类 A
,我们也称为多态类。
由于派生类 B
完整包含了 基类 A
的所有定义,将 B
类型的指针转换为 A
类型的指针总是安全的。
而将 A
类型的指针强制转换为 B
类型的指针时,如果 A
类型指针指向的对象确实为 B
类型的对象,那么转换也是安全的。此时,该 B
类型对象被称为完整对象(complete object)。
强制转换有哪些类型?
C++ 包含了以下几种强制转换运算符,这些运算符用于消除老式 C 语言转换中的存在的歧义和隐患:
dynamic_cast
static_cast
const_cast
reinterpret_cast
safe_cast
本文会着重介绍如何使用 dynamic_cast
和 static_cast
。
提醒:
除非必须,不要使用
const_cast
和reinterpret_cast
,因为它们存在一些老式 C 语言转换中的隐患。
dynamic_cast
运算符
语法:
dynamic_cast <type-id> (expression)
type-id
必须是一个指针或者引用,指向/引用已定义的类类型或者 void。如果type-id
是指针,则 expression
必须也为指针类型,如果 type-id
是引用,expression
必须为左值类型。
如果 type-id
是 void*,那么在运行时将检测 expression
的实际类型。其结果返回 expression
指向的完整对象。
如非需要,现代 C++ 中应该避免使用 void
指针,因为容易出错。
下面看些示例,了解 dynamic_cast
的使用方式。
示例1:
class Root { };
class Base : public Root { };
class Derived : public Base { };
void f(Derived* pd) {
Base* pb = dynamic_cast<Base*>(pd); // ok: Base is a direct base class
// pb points to Base subobject of pd
Root* pr = dynamic_cast<Root*>(pd); // ok: Root is an indirect base class
// pr points to Root subobject of pd
}
示例1 中提到了子对象(subobject)的概念,注意与子类型进行区分:
Root
类型包含子类型Base
,Base
类型包含子类型Derived
。Derived
对象包含了Base
类型的子对象,Base
类型的子对象又包含了Root
类型的子对象。
再联系下前面说的:派生类完整包含了基类的所有定义。
示例2:
class B {virtual void f();};
class D : public B {virtual void f();};
void f() {
B* pb = new D; // unclear but ok
B* pb2 = new B;
D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a D
D* pd2 = dynamic_cast<D*>(pb2); // pb2 was nullptr.
}
示例2 中 通过 dynamic_cast
转换为 pd2
时,不会报错,返回 nullptr
。但如果同样地情况转换的对象是引用类型,那么运行时会抛出 std::bad_cast
异常。如果 pb2
指向/引用的对象无效,同样也会抛出异常。
示例3:
#include <stdio.h>
#include <iostream>
struct A {
virtual void test() {
printf_s("in A\n");
}
};
struct B : A {
virtual void test() {
printf_s("in B\n");
}
void test2() {
printf_s("test2 in B\n");
}
};
struct C : B {
virtual void test() {
printf_s("in C\n");
}
void test2() {
printf_s("test2 in C\n");
}
};
void Globaltest(A& a) {
try {
C &c = dynamic_cast<C&>(a);
printf_s("in GlobalTest\n");
}
catch(std::bad_cast) {
printf_s("Can't cast to C\n");
}
}
int main() {
A *pa = new C;
A *pa2 = new B;
pa->test();
B * pb = dynamic_cast<B *>(pa);
if (pb)
pb->test2();
C * pc = dynamic_cast<C *>(pa2);
if (pc)
pc->test2();
C ConStack;
Globaltest(ConStack);
// will fail because B knows nothing about C
B BonStack;
Globaltest(BonStack);
}
Output:
in C
test2 in B
in GlobalTest
Can't cast to C
static_cast
运算符
语法:
static_cast <type-id> (expression)
static_cast
通常用于数值类型转换,例如枚举和整型,整型和浮点类型的转换。
在标准 C++ 中,static_cast
转换没有运行时检测来保证安全性。在 C++/CX 中,则包含了编译和运行时检测。
static_cast
运算符能够用于将基类指针转换为派生类指针,但这样的转换不总是安全的。
下面还是通过示例进行讲解。
示例1:
class B {};
class D : public B {};
void f(B* pb, D* pd) {
D* pd2 = static_cast<D*>(pb); // Not safe, D can have fields
// and methods that are not in B.
B* pb2 = static_cast<B*>(pd); // Safe conversion, D always
// contains all of B.
}
示例1 中 pd2
不为空,当用指针 pd2
调用 B
类型对象不存在的方法或者成员时可能会发生运行时错误(比如调用虚函数)或者返回非预期的值。
示例2:
typedef unsigned char BYTE;
void f() {
char ch;
int i = 65;
float f = 2.5;
double dbl;
ch = static_cast<char>(i); // int to char
dbl = static_cast<double>(f); // float to double
i = static_cast<BYTE>(ch);
}
示例2 中 static_cast
运算符显示地将内置类型进行转换。
关于 static_cast
运算符,还有以下几种使用情况:
static_cast
能够显式的将整型转换为枚举类型。如果整型值不在枚举值范围内,那么返回的枚举值是未定义的。static_cast
能将任何expression
显式地转换为void
类型。static_cast
操作符不会去除const
,volatile
,__unaligned
属性。
区分几种强制转换的使用场景
dynamic_cast
主要用于多态类型的强制转换,而 static_cast
主要用于非多态类型的强制转换。
static_cast
转换不像 dynamic_cast
那样安全。因为 static_cast
没有运行时检测。通过 dynamic_cast
进行转换时,一旦存在歧义,就会导致失败,然而 static_cast
会像没有错误发生一样返回结果。尽管 dynamic_cast
更加安全,但 dynamic_cast
仅适用于指针和引用,并且运行时检测是需要消耗性能的。
示例:
class B {
public:
virtual void Test(){}
};
class D : public B {};
void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
如果 pb
实际指向类型 D
或者 pd == 0
,那么 pd1
和 pd2
将获得相同的值。
如果 pb
实际指向类型 B
,那么 dynamic_cast
会返回 0
。但是 static_cast
依赖于 expression
认定 pb
指向 D
类型对象,于是简单的返回 D
类型的指针。
结果就是,static_cast
转换会继续执行,但其返回结果是未定义的。这就需要调用者去进一步验证转换结果是有效的。
引用
https://docs.microsoft.com/en-us/cpp/cpp/casting?view=msvc-160
关于 C++ 中的强制转换 - 基础篇的更多相关文章
- c++中的强制转换static_cast、dynamic_cast、reinterpret_cast的不同用法儿
c++中的强制转换static_cast.dynamic_cast.reinterpret_cast的不同用法儿 虽然const_cast是用来去除变量的const限定,但是static_cast ...
- Php中的强制转换详解
强制转换中分为两种,第一种就只临时转换,和永久转换.在临时转换中呢,首先可以通过第一中方式来显示,就是小括号的形式,临时转换成整型我们可以通过(int)都是这样的形式,或者是(integer)临时转换 ...
- java 中的强制转换
强制转换分两种,一种是基础类型强制转换(Type Conversion),一种是引用类型强制转换(Class Casting):
- c++中的强制转换
一.C语言的强制转换1.1 隐性转换 不同数据类型之间赋值和运算,函数调用传递参数等等,由编译器完成 int nTmp = 10; short sTmp ...
- C++中类型强制转换
C++中强制类型转换有四种: 1.static_cast 格式:static_cast<Type>(Value); --用于基本类型间的转换,但不能用于基本类型指针间的转换: int i ...
- js中+号强制转换小例子
1 <script> console.log(([]+{}).length); </script> </head> 输出竟然是: 为什么会是15呢? 因为在+号的强 ...
- 在GO中调用C源代码#基础篇1
开坑说明 最近在编写客户端程序或与其他部门做功能集成时多次碰到了跨语言的sdk集成,虽说方案很多诸如rpc啊,管道啊,文件io啊,unix socket啊之类的不要太多,但最完美的基础方式还是让程序与 ...
- Java中的强制转换
特点: 1.需要程序员手动修改代码 2.语法:范围小的类型 变量名 = (范围小的类型)范围大的类型的数据 3.从范围小 到 范围大 注意: 强制类型转换可能会造成数据的丢失哦,小伙伴们在应用时一定 ...
- ARX中类型强制转换
比如 克隆 clone, 获得的是一个acrxobject, acrxobject *pobj=pployline->clone(); acdbpolyline *ppoly=acdbpolyl ...
随机推荐
- poj 2398Toy Storage
Toy Storage Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3146 Accepted: 1798 Descr ...
- zjnu1757Excellent (数学公式)
Description Let us begin with a positive integer N and find the smallest positive integer which does ...
- Codeforces Round #327 (Div. 1) C. Three States
C. Three States time limit per test 5 seconds memory limit per test 512 megabytes input standard inp ...
- 全局ID生成--雪花算法
分布式ID常见生成策略: 分布式ID生成策略常见的有如下几种: 数据库自增ID. UUID生成. Redis的原子自增方式. 数据库水平拆分,设置初始值和相同的自增步长. 批量申请自增ID. 雪花算法 ...
- SQL优化这么做就对了
目录 前言 SQL优化一般步骤 1.通过慢查日志等定位那些执行效率较低的SQL语句 2.explain 分析SQL的执行计划 3.show profile 分析 4.trace 5.确定问题并采用相应 ...
- 如何在ASP.NET Core 中使用IHttpClientFactory
利用IHttpClientFactory可以无缝创建HttpClient实例,避免手动管理它们的生命周期. 当使用ASP.Net Core开发应用程序时,可能经常需要通过HttpClient调用Web ...
- Clipboard API
Clipboard API click copy click copy demo clickGetNewsLink(data_ref = `newsLink`) { let that = this; ...
- TypeScript Developer Roadmap
TypeScript Developer Roadmap https://github.com/xgqfrms/TypeScript-Developer-Roadmap https://typescr ...
- React & Strict Mode
React & Strict Mode https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects ...
- nodejs 创建tcp/udp服务器和客户端
TCP server // https://nodejs.org/api/net.html const net = require("net"); // https://nodej ...