转载自:CSDN _小羊_
一、类型转换
1、C语言中的类型转换
如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与
接收返回值类型不一致时,就需要发生类型转化,转换的前提是类型之间有一定的关联。
隐式类型转换:编译器自动进行,比如整形家族(int、char、unsigned int)/ 整型和浮点数
强制类型转换:我们自己处理,比如整形和指针、指针之间
<p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>int i = 1;</p><p><span style="white-space:pre"> </span>// 隐式类型转换</p><p><span style="white-space:pre"> </span>double d = i;</p><p>
</p><p><span style="white-space:pre"> </span>int* p = &i;</p><p><span style="white-space:pre"> </span>// 显示的强制类型转换</p><p><span style="white-space:pre"> </span>int address = (int)p;</p><p>
</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
2、C++中的类型转换
上面举的例子都是内置类型之间,而内置类型和自定义类型之间、自定义类型和自定义类型之间都是可以通过一定的方式互相转换的。
| 内置类型和自定义类型之间:
在前面的学习中我们经常说:单参数构造函数支持隐式类型转换,多参数也可以通过加{}进行隐式类型转换。
<p>class A</p><p>{</p><p>public:</p><p><span style="white-space:pre"> </span>A(int a)</p><p><span style="white-space:pre"> </span>:_a1(a)</p><p><span style="white-space:pre"> </span>,_a2(a)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>A(int a1, int a2)</p><p><span style="white-space:pre"> </span>:_a1(a1)</p><p><span style="white-space:pre"> </span>,_a2(a2)</p><p><span style="white-space:pre"> </span>{}</p><p>private:</p><p><span style="white-space:pre"> </span>int _a1;</p><p><span style="white-space:pre"> </span>int _a2;</p><p>};</p><p>
</p><p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>string s("Are you ok?");//隐式类型转换</p><p><span style="white-space:pre"> </span>A a1(1);//借助构造函数完成类型转换</p><p><span style="white-space:pre"> </span>A a2({ 1, 2 });</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
构造函数前加explicit就不再支持隐式类型转换(但是还可以强转)
而自定义类型转换为内置类型需要通过下面这个函数:
<p>operator int()</p><p>{</p><p><span style="white-space:pre"> </span>//...</p><p>}</p>
这个函数没有返回类型,但是有返回值
函数前加explicit就不再支持隐式类型转换(但是还可以强转)
<p>class A</p><p>{</p><p>public:</p><p><span style="white-space:pre"> </span>A(int a)</p><p><span style="white-space:pre"> </span>:_a1(a)</p><p><span style="white-space:pre"> </span>,_a2(a)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>A(int a1, int a2)</p><p><span style="white-space:pre"> </span>:_a1(a1)</p><p><span style="white-space:pre"> </span>,_a2(a2)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>operator int()</p><p><span style="white-space:pre"> </span>{</p><p><span style="white-space:pre"> </span>return _a1 + _a2;</p><p><span style="white-space:pre"> </span>}</p><p>private:</p><p><span style="white-space:pre"> </span>int _a1;</p><p><span style="white-space:pre"> </span>int _a2;</p><p>};</p><p>
</p><p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>string s("Are you ok?");//隐式类型转换</p><p><span style="white-space:pre"> </span>A a1(1);//借助构造函数完成类型转换</p><p><span style="white-space:pre"> </span>A a2({ 1, 2 });</p><p>
</p><p><span style="white-space:pre"> </span>int x = a1;</p><p><span style="white-space:pre"> </span>int y = a2;</p><p><span style="white-space:pre"> </span>cout << x << endl;</p><p><span style="white-space:pre"> </span>cout << y << endl;</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
| 自定义类型和自定义类型之间:
自定义类型之间也可以借助构造函数来完成相互转换。
<p>class A</p><p>{</p><p>public:</p><p><span style="white-space:pre"> </span>A(int a)</p><p><span style="white-space:pre"> </span>:_a1(a)</p><p><span style="white-space:pre"> </span>,_a2(a)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>A(int a1, int a2)</p><p><span style="white-space:pre"> </span>:_a1(a1)</p><p><span style="white-space:pre"> </span>,_a2(a2)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>operator int()</p><p><span style="white-space:pre"> </span>{</p><p><span style="white-space:pre"> </span>return _a1 + _a2;</p><p><span style="white-space:pre"> </span>}</p><p>
</p><p><span style="white-space:pre"> </span>int get() const</p><p><span style="white-space:pre"> </span>{</p><p><span style="white-space:pre"> </span>return _a1 + _a2;</p><p><span style="white-space:pre"> </span>}</p><p>private:</p><p><span style="white-space:pre"> </span>int _a1;</p><p><span style="white-space:pre"> </span>int _a2;</p><p>};</p><p>
</p><p>class B</p><p>{</p><p>public:</p><p><span style="white-space:pre"> </span>B(int b)</p><p><span style="white-space:pre"> </span>:_b(b)</p><p><span style="white-space:pre"> </span>{}</p><p>
</p><p><span style="white-space:pre"> </span>B(const A& aa)</p><p><span style="white-space:pre"> </span>:_b(aa.get())</p><p><span style="white-space:pre"> </span>{}</p><p>private:</p><p><span style="white-space:pre"> </span>int _b;</p><p>};</p><p>
</p><p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>A aa(1);</p><p><span style="white-space:pre"> </span>B bb(2);</p><p><span style="white-space:pre"> </span>bb = aa;//这里走了B的拷贝构造</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
例如:我们之前实现的list的迭代器有普通迭代器和const迭代器两种,普通迭代器用普通迭代器接收,const迭代器用const迭代器接收,而库中的list是支持普通迭代器用const迭代器接收的,那我们也可以给自己的list加上这个功能。
增加一个用于类型转换的构造函数:
<p>//...</p><p>ListIterator(const ListIterator<T, T&, T*>& it)</p><p><span style="white-space:pre"> </span>:_node(it._node)</p><p>{}</p><p>//...</p>
注意:这里的参数一定是写死的,不能是const ListIterator<T, Ref, Ptr>& it
3、C语言类型转换的缺陷
转换的可视性差,所有的转换形式都是以一种相同的形式书写,难以跟踪错误的转换
隐式类型转换有些情况下可能会出现问题,比如数据精度丢失
显示类型转换将所有情况混在一起,代码不够清晰
4、C++中的四种强制类型转换
标准C++为了加强类型转换的可视性,引入了下面四种命名的强制类型转换操作符。主要是为了让类型转换有统一的规范,更加严谨。
4.1 static_cast
static_cast用于非多态类型的转换(对应隐式类型转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。
<p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>double d = 3.14;</p><p><span style="white-space:pre"> </span>int a = static_cast<int>(d);</p><p><span style="white-space:pre"> </span>cout << a << endl;</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
4.2 reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型(对应强制类型转换)。
<p>
</p><p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>double d = 3.14;</p><p><span style="white-space:pre"> </span>int a = static_cast<int>(d);</p><p><span style="white-space:pre"> </span>cout << a << endl;</p><p>
</p><p><span style="white-space:pre"> </span>//这里使用static_cast会报错,应该使用reinterpret_cast</p><p> //int *p = static_cast<int*>(a);</p><p><span style="white-space:pre"> </span>int* p = reinterpret_cast<int*>(a);</p><p><span style="white-space:pre"> </span>cout << p << endl;</p><p>
</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
4.3 const_cast
const_cast最常用的用途就是删除变量的const属性(对应强制类型转换中有风险的去掉const属性),方便赋值。
<p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>const int a = 2;</p><p><span style="white-space:pre"> </span>int* p = const_cast<int*>(&a);</p><p><span style="white-space:pre"> </span>*p = 3;</p><p><span style="white-space:pre"> </span>cout << a << endl;</p><p>}</p>
使用volatile可以确保编译器不会对这些变量的访问进行优化,从而确保每次访问都能读取到最新的值。
4.4 dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)。
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
dynamic_cast只能用于父类含有虚函数的类
dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回nullptr
<p>class A</p><p>{</p><p>public:</p><p><span style="white-space:pre"> </span>virtual void f() {}</p><p>};</p><p>class B : public A</p><p>{};</p><p>void fun(A* pa)</p><p>{</p><p><span style="white-space:pre"> </span>// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回</p><p><span style="white-space:pre"> </span>B* pb1 = static_cast<B*>(pa);</p><p><span style="white-space:pre"> </span>B* pb2 = dynamic_cast<B*>(pa);</p><p><span style="white-space:pre"> </span>cout << "pb1:" << pb1 << endl;</p><p><span style="white-space:pre"> </span>cout << "pb2:" << pb2 << endl;</p><p>}</p><p>
</p><p>int main()</p><p>{</p><p><span style="white-space:pre"> </span>A a;</p><p><span style="white-space:pre"> </span>B b;</p><p><span style="white-space:pre"> </span>fun(&a);</p><p><span style="white-space:pre"> </span>fun(&b);</p><p><span style="white-space:pre"> </span>return 0;</p><p>}</p>
dynamic_cast 作用于普通指针或引用,用于将基类指针(或引用)转换为派生类指针(或引用) 。
另外还有 dynamic_pointer_cast,专门用于智能指针的类型转换 ,将一个基类智能指针转换为派生类智能指针 。
二者都在运行时进行类型检查以确保转换安全性。
————————————————
|