目录
一,C语言类型转换
1.1 两种转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化。C语言中有两种形式的类型转换:
①隐式类型转换:编译器在编译阶段地总进行,能转就转,不能转就编译失败
②显式类型转换:用户自定义处理转换
void main()
{
//隐式类型转换
int i = 1;
double d = i;
cout << i << " " << d << endl;
//显示类型转换
int* p = &i;
int address = (int)p;
cout << p << " " << address << endl;
}
1.2 C语言类型转换缺陷
①C的转换可视性比较差,所有转换形式都是以一种相同形式书写,难以跟踪错误的转换。
②隐式类型转换有些情况下可能会出问题:比如double转int后数据精度丢失
③显式类型转换将所有的情况混合在一起,代码不够清晰
//string有一个场景
void insert(size_t pos, char ch)
{
size_t _size = 5;
int end = _size - 1;
while (end >= pos)//如果end为0,下面--end后不会变为-1,因为end是size_t,所以陷入死循环
{ //就算把end改为int也不行,因为pos是size_t,end会发生隐式类型转换
//_str[end + 1] = _str[end];
--end;
}
}
所以C++看中了C的转换缺陷,推出了属于自己的转换标准,但是要注意,C++是兼容C的,所以C++仍然可以使用C的转换方法
二,C++类型转换
2.1 static_cast
static_cast用于相近类型之间的转换,编译器隐式执行的任何类型都可以用static_cast来转换,但是不能用于两个不相关的类型进行转换。(相当于C的隐式类型转换)
void main()
{
double d = 12.34;
int a = static_cast<int>(d);//static_cast,相关类型转换
cout << "d:" << d << endl;
cout << "a:" << a << endl;
int* p = &a;
//不支持不想关类型转换
//int address = static_cast<int>(p);
}
2.2 reinterpret_cast
reinterpret_cast表示从位的较底层的角度对操作数进行重新解释,简单来说就是用于不同类型之间的转换
void main()
{
double d = 12.34;
int a = static_cast<int>(d);//static_cast,相关类型转换
cout << "d:" << d << endl;
cout << "a:" << a << endl;
cout << "----------" << endl;
int* p = &a;
//int address = static_cast<int>(p); 报错
cout << p << endl;
//reinterpret 不相关类型的转换
int address = reinterpret_cast<int>(p);
printf("%x", address); //%x是int转16进制数字,只是字母全是小写
}
2.3 const_cast
const_cast表示删除变量的const属性,方便赋值,但是这个以后尽量少用,因为删除了const属性就导致原本数据的不安全了
void main()
{
//某些编译器对于const优化处理的方式不一样,由于a是const的,所以VS把a放到寄存器里去了,虽然监视窗口看到的a是3,但是打印的时候不是从内存取的而是从寄存器里取的,所以打印2
const int a = 2;
//volatile const int a = 2;//volatile告诉编译器,不要优化了,直接去内存去取,这样就打印3 3了
int* p = const_cast<int*>(&a); //去掉const属性
//int* p = (int*)&a;//可以强制类型转换,C++兼容C
*p = 3;
cout << a << endl;
cout << *p << endl;
}
2.4 dynamic_cast
dynamic_cast是C++独有的,用于父子间指针或引用的转换
在类与对象我们已经学习过了子类指针或引用转父类指针或引用的向上转型,是一种类系切割或切片的转型,是语法天然支持,无类型转换,但是向下转型不天然支持,需要类型转换,需要用到dynamic_cast
向下转型有两种情况:
①父类指针或引用指向的是父类对象时,这时候转为子类是不安全的,因为会访问到子类的资源,而这个资源可能父类没有
②父类指针或引用指向的是子类对象时,这时候转为子类是安全的
所以使用C的强制类型转换进行向下转型是不安全的,因为不论是父类指向或引用指向的是父类还是子类对象都会进行转换。所以C++推出了dynamic_cast专门进行向下转换,它表示:
①如果父类对象的指针或引用指向的是子类对象那么dynamic_cast会转换成功,返回转换后的地址
②如果父类对象的指针或引用指向的是父类对象,那么dynamic_cast就会转换失败,并且返回一个空指针
class A
{
public:
virtual void f(){}
public:
int _a = 0;
};
class B : public A
{
public:
int _b = 1;
};
//A*指针pa可能指向父类,也可能指向子类
void fun(A* pa, const string& s)
{
cout <<"pa指向" << s << endl;
//向下转型 -- 父类转子类,用dynamic_cast最安全
// 如果pa是指向子类,那么可以转换,转换表达式返回正确的地址
// 如果pa是指向父类,那么不能转换,转换表达式返回nullptr
B* pb1 = (B*)pa; // 不安全
B* pb2 = dynamic_cast<B*>(pa); // 安全的
cout << "[强制转换]pb1:" << pb1 << endl;
cout << "[dynamic_cast转换]pb2:" << pb2 << endl << endl;
}
void main()
{
A a;
B b;
fun(&a, "指向父类对象");
fun(&b, "指向子类对象");
}
注意 :对象本身是无论如何都不允许转换的,所以转换的只是指向对象的指针或引用
三,RTTI
Run-time Type identification的简称,即:运行时类型识别
C++通过这几种方式支持RTTI:typeid运算符,dynamic_cast运算符,decltype
void main()
{
A a;
cout << typeid(a).name() << endl; //仅仅是获取类型的字符串
decltype(a) aa; //拿到类型定义对象,一般没用,但是在下面这个场景有用
//lambda是匿名对象或者仿函数,我们一般拿不到类型
function<bool(int, int)> gt = [](int x, int y) {return x > y; }; //lamdba是防拷贝的,而set可能有拷贝选项,所以用function包装一下
set<int, decltype(gt)> s;
}
四,常见面试题
4.1 C++中的四种类型转化分别是
分别是static_cast,reinterpret_cast,const_cast和dynamic_cast
4.2 请简单说明这四种类型转化的应用场景
①static_cast用于相关类型之间的转换,译器隐式执行的任何类型都可以用static_cast来转换,比如int和double的转换,相当于C的隐式类型转换。
②reinterpret_cast用于两个不相关类型之间的转换,比如int和int*,相当于C的强制类型转换。
③const_cast用于删除变量的const属性,方便赋值。
④dynamic_cast用于父类对象和子类对象之间的类型转换,是安全的。