文章目录
- 一、隐式类型转换
- 二、显式类型转换
- 三、c++风格的类型转换
一、隐式类型转换
隐式类型转换,顾名思义,就是没有明显的声明要进行类型转换,隐式类型转换有可能造成数据精度的丢失,所以通常所做的类型转换都是从size小的数据到size大的数据。
我们以数字为例,在程序中见到的所有整形立即数都被当作int
类型,所有浮点数立即数都被当作double
类型,如下所示:
int main() {
auto a = 12.34;
auto b = 100;
std::cout << typeid(a).name() << std::endl; //double
std::cout << typeid(b).name() << std::endl; //int
return 0;
}
在进行加减乘除等基本运算的时候,首先要保证运算符两边是同类型的数据,这时候,编译器会做隐式的类型转换,都是将数据从size小转到size大,这时不会损失数据精度,⚠️同时存在signed
和unsigned
的时候,signed
会转换成unsigned
。其他的转换借鉴下图:
对象中包含的隐式转换:
- 对象构造的时候使用如果编译器可以找到对象的构造函数,此时也会发生隐式的类型转换,但是通常不希望这种情况发生,可以在构造函数前加
explicit
关键字进行避免;
二、显式类型转换
这个没什么好说的,通过类型强转的方式,程序上能直接指定要转换成何种类型。但是需要注意一点的是,对象也能通过运算符重载函数强转成基本类型。使用方法如下:
class A {
public:
explicit A(int ma) : _ma(ma) {
}
explicit operator double() const {
double a = 13.34;
return a;
}
private:
int _ma;
};
三、c++风格的类型转换
- static_cast
static_cast类似于强制类型转换,只是是一种tight type check的类型转换,而且是编译时期的类型转换。之所以说他是tight type check是因为static_cast不允许做一些不合理的类型转换,如下所示:
#include <iostream>
#include <iostream>
using namespace std;
int main()
{
int a = 10;
char c = 'a';
// pass at compile time, may fail at run time
int* q = (int*)&c; //clang编译通过
int* p = static_cast<int*>(&c); //编译失败
return 0;
}
另外在继承结构中,必须是public继承才能使用static_cast进行类型向上转换,如下所示:
class Base {
};
class Derived : private Base { // Inherited private/protected not public
};
int main() {
Derived d1;
Base *b1 = (Base *) (&d1); // allowed
Base *b2 = static_cast<Base *>(&d1); //编译失败
return 0;
}
2. dynamic_cast
这种转换需要用到RTTI机制,关于RTTI的原理和对象内存布局,见我的另一篇博客C++对象内存布局。
既然涉及到RTTI,那就必须有虚函数,如果继承结构中没有虚函数,那也就用不了dynamic_cast,dynamic_cast支持downcasting和upcasting,但是指针必须指向的是同一个对象,如果转换失败,dynamic_cast返回nullptr。使用方法如下:
// C++ program demonstrate if there
// is no virtual function used in
// the Base class
#include <iostream>
using namespace std;
// Base class declaration
class Base {
void print()
{
cout << "Base" << endl;
}
};
// Derived Class 1 declaration
class Derived1 : public Base {
void print()
{
cout << "Derived1" << endl;
}
};
// Derived class 2 declaration
class Derived2 : public Base {
void print()
{
cout << "Derived2" << endl;
}
};
// Driver Code
int main()
{
Derived1 d1;
// Base class pointer hold Derived1
// class object
Base* bp = dynamic_cast<Base*>(&d1);
// Dynamic casting
Derived2* dp2 = dynamic_cast<Derived2*>(bp);
if (dp2 == nullptr)
cout << "null" << endl;
return 0;
}
3. reinterpret_cast
顾名思义,重新解释的意思,这种类型转换将任意一种指针转换成另一种指针,这是一种危险的转换,使用方式如下:
#include <iostream>
using namespace std;
int main()
{
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;
}
4. const_cast
const_cast用于将变量的const约束去掉,const_cast并不像想象的那样可以直接修改const变量,const_cast去除掉的一般是指针的约束,使用方法见下:
const int a=10;
int b = const_cast<int>(a); //这里会编译失败,因为实际上a的const限制是无法去除的
int main(void) {
int a1 = 40;
const int *b1 = &a1;
int *c1 = const_cast <int *> (b1);
*c1 = 100; //此处没有问题,可以通过c1修改a1;
*b1 = 1000; //此处编译失败,不能通过b1修改a1;
cout << a1 << endl;
return 0;
}
如上所示,a1并不是const变量,只是我们定义了一个const指针,我们不能通过这个指针修改a1,但是我们可以把指针的const去掉,然后用去掉const限制的指针来修改a1。
const_cast还可以用来在类的const函数中修改成员变量,因为在const方法中的this指针实际上是一个const指针,我们可以用const_cast去掉const限制,然后来修改成员变量,使用方法如下所示:
class student
{
private:
int roll;
public:
// constructor
student(int r):roll(r) {}
// A const function that changes roll with the help of const_cast
void fun() const
{
( const_cast <student*> (this) )->roll = 5;
}
int getRoll() { return roll; }
};
const_cast是一种比强转更安全的类型转换,比如将const int* 转为int是允许的,但是将const int转为char*是不允许的。
const_cast还可以将volatile约束去掉,如下所示:
int main(void)
{
int a1 = 40;
const volatile int* b1 = &a1;
cout << "typeid of b1 " << typeid(b1).name() << '\n'; //PVKi
int* c1 = const_cast <int *> (b1);
cout << "typeid of c1 " << typeid(c1).name() << '\n'; //Pi
return 0;
}