在C++中,static_cast, dynamic_castreinterpret_cast都可用于类型转换,它们在具体使用时有什么区别?此外,更为重要的是,为什么不推荐使用强制类型转换?

1. static_cast

static_cast是静态类型转换,“静态”一词是指在程序编译期间完成类型的转换,这应该是平时使用最多的类型转换。例如,将一个浮点数转化为整数,就可以使用static_cast:

float a = 10.5;
int b = static_cast<int>(a);

2. dynamic_cast

dynamic_cast是动态类型转换,“动态”一词是指在程序运行期间完成类型的转换,如果转换失败且转换的目标类型是指针,则返回一个空指针;如果转换失败且转换的目标类型是引用,则会抛出std::bad_cast异常。

动态类型转换与C++的多态有关,常用于基类与子类指针或引用的转换,且基类中至少要有一个虚函数。例如:

class Base {
virtual void f();
}; class Derived : public Base {
void f() override;
}; Derived* ptr = new Derived();
Base* ptrBase = dynamic_cast<Base*>(ptr);

当然,这个例子并没有很好地展示出dynamic_cast的功能,文章最后有一个例子展示了需要使用dynamic_cast的情形。

3. reinterpret_cast

reinterpret的意思是“重新解释”,它不会改变任何底层的数据,而是告诉编译器应该把当前数据当作哪种类型。例如,有一个指向整数的指针,你可以使用reinterpret_cast将其转化为一个指向浮点数的指针:

int a = 10;
float* ptrB = reinterpret_cast<float*>(&a);

转换完成后,没有任何数据被改变,只是ptrB之后会被编译器当作一个指向浮点数的指针,这种感觉有点像C语言中的union. 可以看出,这种转换更偏向底层,使用时一定要小心。

当然,既然存在这一转换,就一定有它的用处。例如,在OpenGL中,可以将一个指针传递到窗口:

class App {
...
}; App* app1 = new App();
glfwSetWindowUserPointer(app1);

函数glfwSetWindowUserPointer的参数是void*类型的指针,可以通过函数glfwGetWindowUserPointer获取这个指针,获取到的指针类型也是void*,但我们已经知道它必然是一个App*类型的指针,此时可以使用reinterpret_cast将其转换回来:

void* ptr = glfwGetWindowUserPointer();
App* app1 = reinterpret_cast<App*>(ptr);

4. 强制类型转换

C语言中,可以通过(T)xx转换为类型T,C++中也支持这种写法,这种写法被称为强制类型转换。它有什么问题呢?请看下面这个例子:

我们首先定义类Human

class Human {
protected:
int mAge; // 年龄 public:
virtual void say() {
std::cout << "I'm a human.\n";
}
};

这个类表示人类,它有一个成员mAge,表示人类的年龄;还有一个虚函数say, 此函数会输出一句话。

接下来定义类Man, 这个类继承自Human类:

class Man : public Human {
public:
Man(int age) {
mAge = age;
} void say() override {
std::cout << "I'm a man.\n";
} void howOld() {
std::cout << "I'm " << mAge << " now.\n";
}
};

这个类表示男人。其中,虚函数say被重载,此外还有一个成员函数howOld, 输出当前的年龄。

最后定义类Woman, 同样继承自Human类:

class Woman : public Human {
public:
Woman(int age) {
mAge = age;
} void say() override {
std::cout << "I'm a woman.\n";
}
};

这个类表示女人。它只重载了虚函数say, 并没有提供howOld方法(因为女性的年龄不会轻易告诉别人)。

我们在main函数中创建一个指向Woman对象的指针,并尝试通过指针访问howOld方法:

int main() {
auto pWoman = new Woman(32);
pWoman->howOld();
}

这段代码应当会报错,因为Woman类没有howOld方法。以下是博主使用g++编译时输出的报错信息:

error: 'class Woman' has no member named 'howOld'

但如果我们将指针pWoman强制转换Man*类型的指针呢?代码如下:

int main() {
auto pWoman = new Woman(32);
((Man*)pWoman)->howOld();
}

令人诧异的是,这段代码可以正常运行,输出如下:

I'm 32 now.

使用强制类型转换后,竟然输出了女性的年龄,这真的是太糟糕了!

我们不妨使用static_cast转换试试:

int main() {
auto pWoman = new Woman(32);
(static_cast<Man*>(pWoman))->howOld();
}

这段代码会在编译时报错:

error: invalid 'static_cast' from type 'Woman*' to type 'Man*'

再使用dynamic_cast试试:

int main() {
auto pWoman = new Woman(32);
(dynamic_cast<Man*>(pWoman))->howOld();
}

代码可以编译成功,但运行时会出错,因为类型转换失败,dynamic_cast会返回一个空指针。对返回值加以判断,程序就可以正常运行了:

int main() {
auto pWoman = new Woman(32);
auto pMan = dynamic_cast<Man*>(pWoman);
if (pMan) {
pMan->howOld();
}
else {
std::cout << "convert failed.\n";
}
}

运行后,程序输出如下:

convert failed.

最后使用reinterpret_cast试试:

int main() {
auto pWoman = new Woman(32);
(reinterpret_cast<Man*>(pWoman))->howOld();
}

编译后,程序输出如下:

I'm 32 now.

从这个例子中,可以看出不同类型转换方式的区别。最后,展示一个使用dynamic_cast的例子:

void howOldAreYou(Human* pHuman) {
Man* pMan = dynamic_cast<Man*>(pHuman);
if (pMan) {
pMan->howOld();
}
else {
std::cout << "My age is a secret.\n";
}
} int main() {
Man* p1 = new Man(23);
Woman* p2 = new Woman(35);
howOldAreYou(p1);
howOldAreYou(p2);
}

程序输出为:

I'm 23 now.
My age is a secret.

static_cast, dynamic_cast与reinterpret_cast的区别的更多相关文章

  1. static_cast dynamic_cast const_cast reinterpret_cast总结对比

    [本文链接] http://www.cnblogs.com/hellogiser/p/static_cast-dynamic_cast-const_cast-reinterpret_cast.html ...

  2. C++雾中风景11:厘清C++类型转换(static_cast,dynamic_cast,reinterpret_cast,const_cast)

    C++是一门弱类型的语言,提供了许多复杂和灵巧类型转换的方式.笔者之前写的Python与Go都是强类型的语言,对这种弱类型的设计实在是接受无力啊~~ ( 生活所迫,工作还得写C++啊~~)C++语言提 ...

  3. c++ 数据类型转换: static_cast dynamic_cast reinterpret_cast const_cast

    c++ 数据类型转换: static_cast dynamic_cast reinterpret_cast const_cast  [版权声明]转载请注明出处 http://www.cnblogs.c ...

  4. dynamic_cast 和 static_cast 隐式类型转换的区别

    首先回顾一下C++类型转换: C++类型转换分为:隐式类型转换和显式类型转换 第1部分. 隐式类型转换 又称为“标准转换”,包括以下几种情况:1) 算术转换(Arithmetic conversion ...

  5. c++中的强制转换static_cast、dynamic_cast、reinterpret_cast的不同用法儿

    c++中的强制转换static_cast.dynamic_cast.reinterpret_cast的不同用法儿   虽然const_cast是用来去除变量的const限定,但是static_cast ...

  6. C++的类型转换:static_cast、dynamic_cast、reinterpret_cast和const_cast

    在C++中,存在类型转换,通常意味着存在缺陷(并非绝对).所以,对于类型转换,有如下几个原则:(1)尽量避免类型转换,包括隐式的类型转换(2)如果需要类型转换,尽量使用显式的类型转换,在编译期间转换( ...

  7. static_cast、dynamic_cast、reinterpret_cast、和const_cast

    关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++ 之父的<C++ 的设计和演化>.最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_ ...

  8. C++ ------ static_cast,dynamic_cast,reinterpret_cast,const_cast

    C++类型转换分为:隐式类型转换和显式类型转换 第1部分. 隐式类型转换 又称为“标准转换”,包括以下几种情况:1) 算术转换(Arithmetic conversion) : 在混合类型的算术表达式 ...

  9. C++类型转换运算符 static_cast,dynamic_cast,reinterpret_cast,const_cast

    类型转换是一种让程序猿可以临时或永久性改变编译器对对象的解释机制.可改变对象解释方式的运算符称为类型转换运算符. 为何须要进行类型转换 通常为了实现使用不同环境的个人和厂商编写的模块可以相互调用和协作 ...

  10. static_cast 、const_cast、dynamic_cast、reinterpret_cast 关键字简单解释

    static_cast .const_cast.dynamic_cast.reinterpret_cast 关键字简单解释: Static_cast 静态类型转换 ①用于类层次结构中基类(父类)和派生 ...

随机推荐

  1. vivo 微服务 API 网关架构实践

    一.背景介绍 网关作为微服务生态中的重要一环,由于历史原因,中间件团队没有统一的微服务API网关,为此准备技术预研打造一个功能齐全.可用性高的业务网关. 二.技术选型 常见的开源网关按照语言分类有如下 ...

  2. 使用acme.sh、acme-dns自动申请ssl证书

    使用acme.acme-dns实现自动申请ssl证书并实现自动替换 有些dns没有dnsapi,所以用这种方式申请只需要添加一条dns解析即可完成 以下为linux系统操作 安装acme.sh 官方源 ...

  3. 1 Englishi 词根

    1 ab  离去,相反, 不 (负能量的) abnormal abuse 2 anti  反对,相反 antiwar antiaging antiforeign antinoise 3 co-/col ...

  4. SpringCloud Alibaba Security安全认证

    一. Security配置(auth认证中心) 代码地址 https://github.com/typ1805/blog-cloud Spring Security是一套安全框架,可以基于RBAC(基 ...

  5. IDE暗黑主题推荐-Dracula

    作为程序员,我们一天中会花费大量时间在编码和阅读代码上.优秀的代码编辑器主题可以减轻眼睛的疲劳,提高工作效率.本文向大家推荐一款非常流行的 JetBrains IDE 主题插件 - Dracula.它 ...

  6. Power Designer建模之餐饮在线点评系统——概念数据模型

    企业信息管理 局部概念模型 企业 餐饮企业 食材提供商 食材 特色菜 团购活动 优惠券 促销活动 会员团购订单 优惠券下载和浏览记录表 会员信息管理 局部概念模型 会员 会员扩展信息 会员积分记录 餐 ...

  7. jQuery组织后续事件 事件冒泡 事件委托 键被按下 批量操作 hover input 事件

    1. jQuery绑定事件的方式 1. $('').click(function(){}) 2. $('').on('click', function(){}) 2. 阻止后续事件执行 1. retu ...

  8. 根据返回的多层Json来进行创建文件,达到根据阶层创建,然后压缩成压缩包进行下载

    临时接到一个需求说让根据按照下面的这个图片的结构来打包下载指定位置下的文件到指定位置! 实现思路: 1.把已经实现的树形结构的代码进行调用,拿到他的数据进行创建对应的文件夹 2.因为结构下方的文件没有 ...

  9. Pgsql之查询一段时间内的所有年月yyyy-mm

    前几天干活儿的时候,报表中有这么个需求,需要用pgsql查询两个日期间的所有年月,下面贴代码: 1 with recursive t(n) as ( 2 select date('2020-01-01 ...

  10. Cortex M3 CORE

    Cortex CM3 内核架构 CM3内核主要包含几个部分:取指(Fetch)\指令译码(Decoder/DEC)\执行(EXEC)\ALU 内存取数通过load & store指令,就是通过 ...