C/C++中的类型转换分为两种:隐式类型转换和显式类型转换。
而对于隐式类型转换,在很多时候,不经意间就发生了,比如int类型和float类型赋值时,int类型就会被隐式的转换位float类型。
举例:
int i = 5;
float d = i;//隐式类型的转换
int* p = (int*)i;//C style,强制类型的转换.
C++对C兼容,所以上述方式的类型转换是可以的,但是有时候会有问题,所以推荐使用C++中的四个强制类型转换的关键字:
static_cast、reinterpret_cast、const_cast 和 dynamic_cast。下面将逐一介绍。
static_cast
这应该四种中是最常见的。用法为:
static_cast < new_type > ( expression )
该运算符把 expression 转换为new_type 类型。
特点:
1、 用于非多态类型的转换
2、 不执行运行时类型检查(转换安全性不如 dynamic_cast)
3、通常用于转换数值数据类型(如 float -> int)
4、 可以在整个类层次结构中移动指针,子类转化为父类安全(向上转换),父类转化为子类不安全(因为子类可能有不在父类的字段或方法)
通常用于转换数值数据类型(如 float -> int):
#include
using namespace std;
int main(int argc, char const *argv[])
{
float f = 4.5;
int a = f; //C-style
int b = static_cast<int>(f);
cout << b << endl;
return 0;
}
编译输出:
#include
using namespace std;
int main(int argc, char const *argv[])
{
int a = 10;
char c = 'a';
int* q = (int*) &c;
int *p = static_cast<int *>(&c);//编译错误,static_cast 不能将字符转换成指针
return 0;
}
这意味着,即使您认为可以将特定对象类型转换为另一个对象,但这是非法的,static_cast也不允许这样做。
static_cast 用于基本数据类型之间的转换,如把int转换成char,把int转换成float。在c++ primer 中说道:c++ 的任何的隐式转换都是使用 static_cast 来实现。
让我们以继承为例,来看看这个例子:
#include
using namespace std;
class Base {
};
class Derived : public Base {
};
int main(int argc, char const *argv[])
{
Derived d1;
Base* b1 = (Base*)(&d1); // 允许
Base* b2 = static_cast<Base*>(&d1);
return 0;
}
编译:
编译,上面的代码将不会出现任何错误。
1、我们取&d1,显式的存入Base中,存储在b1中。
2、我们取&d1,用static_cast将其转换成Base,存储在b2中。
我们知道static_cast执行严格的类型检查,让我们稍微修改一下代码看看,把继承改为private。
#include
using namespace std;
class Base {
};
class Derived : private Base {
};
int main(int argc, char const *argv[])
{
Derived d1;
Base* b1 = (Base*)(&d1); // 允许
Base* b2 = static_cast<Base*>(&d1);
return 0;
}
编译:
即使继承为受保护的,上面的代码也不会编译。因此,要使用static_cast,请将其继承为public。
使用static_cast将“向空指针”和“从空指针”进行强制转换。例子:
#include
using namespace std;
int main(int argc, char const *argv[])
{
int i = 10;
void* v = static_cast<void*>(&i);
int* ip = static_cast<int*>(v);
return 0;
}
用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 )
const_cast可用于更改const成员函数内的非const类成员。考虑以下代码片段。
#include
using namespace std;
class student
{
private:
int roll;
public:
// 构造函数
student(int r):roll(r) {}
// 在const_cast的帮助下改变roll的const函数
void fun() const
{
( const_cast <student*> (this) )->roll = 5;
}
int getRoll() { return roll; }
};
int main(int argc, char const *argv[])
{
student s(3);
cout << "Old roll number: " << s.getRoll() << endl;
s.fun();
cout << "New roll number: " << s.getRoll() << endl;
return 0;
}
在const成员函数fun()中,编译器将“ this”视为“ const student * const this”,即“ this”是指向常量对象的常量指针,因此编译器不允许通过以下方式更改数据成员“这个”指针。const_cast将“ this”指针的类型更改为“ student * const this”。
const_cast可用于将const数据传递给不接收const的函数。例如:
#include
using namespace std;
int fun(int* ptr)
{
return (*ptr + 10);
}
int main(int argc, char const *argv[])
{
const int val = 10;
const int *ptr = &val;
int *ptr1 = const_cast <int *>(ptr);
cout << fun(ptr1)<<endl;
return 0;
}
编译输出:
const_cast比简单类型转换更安全。从某种意义上讲,如果强制类型与原始对象不相同,则强制强制转换不会发生,这是比较安全的。例如:
#include
using namespace std;
int main(int argc, char const *argv[])
{
int a1 = 40;
const int* b1 = &a1;
char* c1 = const_cast <char *> (b1); // 编译程序时出错
*c1 = 'A';
return 0;
}
编译:
程序编译失败,因为将“ int *”类型转换为“ char *”
const_cast也可以用来抛弃volatile属性。例如:
#include
#include // typeid
using namespace std;
int main(int argc, char const *argv[])
{
int a1 = 40;
const volatile int* b1 = &a1;
cout << "typeid of b1 " << typeid(b1).name() << '\n';
int* c1 = const_cast <int *> (b1);
cout << "typeid of c1 " << typeid(c1).name() << '\n';
return 0;
}
编译输出:
在程序中,b1的typeid是PVKi(指向易失性和整数的指针),而c1的typeid是Pi(指向整数的指针)。
reinterpret_cast
reinterpret_cast是C ++中使用的一种强制转换运算符。
它用于转换任何类型的另一个指针的一个指针,而不管该类是否相互关联。
它不检查指针类型和指针所指向的数据是否相同。
用法如下:
reinterpret_cast <数据类型*>(指针变量);
来看看 reinterpret_cast 简单用法:
#include
using namespace std;
int main(int argc, char const *argv[])
{
int* p = new int(65);
char* ch = reinterpret_cast<char*>(p);
cout << *p << endl;
cout << *ch << endl;
cout << p << endl;
cout << ch << endl;
return 0;
}
1、reinterpret_cast是一种非常特殊且危险的类型转换操作符。并且建议使用适当的数据类型使用它,即(指针数据类型应与原始数据类型相同)。
2、它可以将任何指针类型转换为任何其他数据类型。
3、当我们要使用位时使用它。
4、它仅用于将任何指针转换为原始类型。
5、布尔值将转换为整数值,即0表示false,1表示true。
来看看下面一个例子:
#include
using namespace std;
// 创建结构体mystruct
struct mystruct {
int x;
int y;
char c;
bool b;
};
int main(int argc, char const *argv[])
{
mystruct s;
// 结构体赋值
s.x = 5;
s.y = 10;
s.c = 'a';
s.b = true;
// 在强制转换期间,数据类型必须与原始数据类型相同
// 将's'指针转换为'p'中int类型的指针。
int* p = reinterpret_cast<int*>(&s);
cout << sizeof(s) << endl;
// 打印当前由*p指向的值
cout << *p << endl;
// 将指针增加1
p++;
// 打印下一个整数值
cout << *p << endl;
p++;
// 用char *ch将char *指向p。
//
char* ch = reinterpret_cast<char*>(p);
// 打印(*ch)指定的字符值
cout << *ch << endl;
ch++;
bool* n = reinterpret_cast<bool*>(ch);
cout << *n << endl;
cout << *(reinterpret_cast<bool*>(ch));
return 0;
}
编译输出:
#include
using namespace std;
class A {
public:
void fun_a()
{
cout << " In class A\n";
}
};
class B {
public:
void fun_b()
{
cout << " In class B\n";
}
};
int main(int argc, char const *argv[])
{
// 创建B类的对象
B* x = new B();
// 将指向B类引用的对象的指针转换为A类
A* new_a = reinterpret_cast<A*>(x);
// 访问A类的函数。
new_a->fun_a();
return 0;
}
编译输出:
dynamic_cast的转换格式:
dynamic_cast <type-id> (expression)
将expression转换为type-id类型,type-id必须是类的指针、类的引用或者是void *;如果type-id是指针类型,那么expression也必须是一个指针;如果type-id是一个引用,那么expression也必须是一个引用。
最简单的上行转换,比如Derived 继承自Basic,Derived 转换为Basic,进行上行转换时,是安全的,如下:
#include
using namespace std;
class Basic{
public:
virtual int test(){return 0;} // 必须为多态以使用运行时检查的 dynamic_cast
};
class Derived : public Basic{
public:
int test(){return 1;}
};
int main(int argc, char const *argv[]){
Basic cBasic;
Derived cDerived;
Basic * pB1 = new Basic;
Basic * pB2 = new Derived;
//动态强制转换失败,因此pD1为空。
Derived * pD1 = dynamic_cast<Derived * > (pB1);
//动态强制转换成功,因此rD2引用派生对象。
Derived & rD2 = dynamic_cast<Derived &> (*pB2);
return 0;
}
编译:
参考:static_cast in C++ | Type Casting operators
reinterpret_cast in C++ | Type Casting operators
const_cast in C++ | Type Casting operators
(微信公众号【程序猿编码】)
(添加本人微信号,备注加群,进入程序猿编码交流群,领取学习资料,获取每日干货)
微信公众号【程序猿编码】,这里Linux c/c++ 、Python、Go语言、数据结构与算法、网络编程相关知识,常用的程序员工具。还有汇聚精炼每日时政、民生、文化、娱乐新闻简报,即刻知晓天下事!