C++ 中定义了 4 种显示类型转换,初学 C++,不免以为这一部份内容复杂、难以理解(固然我也不例外),但掌握它又是颇有必要的,毕竟事物的存在,必有它存在的道理,而这个道理,就是相比其余设计而言(例如传统的 C 风格的类型转换),C++ 的类型转换可以减小出错的几率,咱们在前面的文章中也提到过这个设计原则,想成为一名优秀的程序员,该原则值得你谨记在心。java
这 4 种类型转换分别是:ios
static_cast
既能够在对象间进行类型转换(前提是有相应的转换构造函数或转换操做符支持),也能够在指针或引用间作类型转换(通常是父类和子类之间的向上或向下转换),表达式以下程序员
static_cast<new_type>(expression);
复制代码
下面是一个简单的例子,将 float
类型转换为 int
类型express
#include <iostream>
using namespace std;
int main() {
float f = 3.5;
int i1 = f; // C 语言的用法
int i2 = static_cast<int>(f);
cout << i1 << endl;
cout << i2 << endl;
return 0;
}
复制代码
稍微修改上面的代码,看看若是类型转换针对的是指针类型,会发生什么编程
#include <iostream>
using namespace std;
int main() {
char c = 'a';
int* i1 = (int*)&c; // C 风格,compile ok
int* i2 = static_cast<int*>(&c); // compile error
cout << *i1 << endl;
return 0;
}
复制代码
能够看到,在 C++ 中,编译器不容许你将一个 char*
类型的指针转换为 int*
类型的,由于这样作可能会产生整型溢出的错误,虽然你依然能够采用 C 风格的强制类型转换达到这一目的,但不建议你这么作,这也是在 C++ 中,为何要用 _cast
来取代 C 类型转换的缘由之一。安全
但 void*
类型的指针是能够转换为任何其余类型的指针的,这一点也是 C 中经常使用的技巧:bash
int score = 90;
void* p = &score;
int* pscore = static_cast<int*>(p);
复制代码
此外,static_cast
很适合将隐式类型转换显示化,一般,使用显示类型转换是较好的编程习惯。依然以上一篇文章的例子为例:函数
class dog {
public:
dog(string name) {m_name = name;} // 转换构造函数
operator string () const { return m_name; } // 转换操做符
private:
string m_name;
};
int main() {
string dogname = "dog";
dog d1 = dogname; // a1. 隐式 string->dog
dog d2 = static_cast<dog>(dogname); // a2. 显示 string->dog
string other_dog_name = d2; // b1. 隐式 dog->string
string my_dog_name = static_cast<string>(d2); // b2. 显示 dog->string
cout << my_dog_name << endl;
cout << other_dog_name << endl;
return 0;
};
复制代码
上面的代码中,注释 a1
和 b1
分别是利用隐式构造函数和隐式操做符实现的隐式类型转换,而注释 a2
和 b2
将它们显示化了 。性能
前文提到了 static_cast
还能够在父子类之间进行向上或向下的类型转换,要注意的是,子类的继承做用域须要是 public
,且转换的对象须要是指针或引用类型this
#include <vector>
#include <iostream>
class B {
public:
void hello() const {
std::cout << "Hello world, this is B!\n";
}
};
class D : public B {
public:
void hello() const {
std::cout << "Hello world, this is D!\n";
}
};
int main()
{
D d;
B& br = d; // 经过隐式转换向上转型
br.hello();
D& another_d = static_cast<D&>(br); // 向下转型
another_d.hello();
}
复制代码
以上代码会输出
Hello world, this is B!
Hello world, this is D!
复制代码
dynamic_cast
主要针对的是有继承关系的类的指针和引用,且要求类中必需要有 virtual
关键字,这样才能提供运行时类型检查,当类型检查发现不匹配时,dynamic_cast
返回 0;实际运用中,dynamic_cast
主要用于向下转型。在 cppreference.com 页面,提供了很好的例子
#include <iostream>
struct Base {
virtual ~Base() {}
};
struct Derived: Base {
virtual void name() {}
};
int main() {
Base* b1 = new Base;
if(Derived* d = dynamic_cast<Derived*>(b1)) {
std::cout << "downcast from b1 to d successful\n";
d->name(); // 调用安全
}
Base* b2 = new Derived; // 向上转型,能够用 dynamic_cast ,但没必要须
if(Derived* d = dynamic_cast<Derived*>(b2)) {
std::cout << "downcast from b2 to d successful\n";
d->name(); // 调用安全
}
delete b1;
delete b2;
}
复制代码
上面代码仅输出
downcast from b2 to d successful
复制代码
另外一个 cout
没有输出的缘由是 Base* b1
和 Derived* d
类型不一致,转型失败,dynamic_cast
返回为 0。
一样是运行时多态,使用 C++ 的虚函数列表要比 dynamic_cast
在性能上有很大的优点,并且前者可以写出更通用的代码(更少的类型相关的硬编码)。
const_cast
在用法上要简单不少,它可以移除 const 引用
或 const 指针
上的 const
属性,须要注意的是
const引用
或 const指针
指向的变量不能也是 const
类型const_cast
不能做用于函数指针或成员函数指针上典型的使用方法以下:
int i = 3;
const int& cri = i;
const int* cpi = &i;
const_cast<int&>(cri) = 4; // i = 4
*const_cast<int*>(cpi) = 5; // i = 5
复制代码
上面的 i
不能够是 const
类型,不然程序行为是未定义的,如
const i = 3;
const int& cri = i;
const_cast<int&>(cri) = 4; // 可编译,但行为未定义
复制代码
reinterpret_cast
是 C++ 中最危险的转型操做,它能够将指针所指的类型从新解释为其余任意类型,且没有任何检验机制。reinterpret_cast
主要用于这样的场景:先将一个指针转型为另外一种类型(一般为 void*
),在使用前,再把它转型为原始类型,以下:
int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);
复制代码
但实际上,这样的使用方法彻底能够用 static_cast
代替,即
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
复制代码
因此,若是不能保证必定安全,尽可能避免使用这种转型。
C++ 除了支持以上 4 种转型方式,还兼容 C 风格的转型,但读了本文后,你能够把 C 风格的转型当作是 static_cast
、const_cast
和 reinterpret_cast
这 3 种转型的并集。因此,咱们更应该使用 C++
风格的转型,而不是 C 风格的,缘由是:
_cast
关键字,来查看代码中哪些地方用到了转型操做参考: