C++强制类型转换

C语言类型转换

c语言类型转换有如下两种(旧式转型)

1
2
(T)expression  //将 expression 转型为T
T(expression) //将 expression 转型为T

C++新式类型转换

C++类型转换有如下四种

1
2
3
4
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)

const_cast

一般用于移除对象的const与volatile。如下图所示,b可以修改a的值。但是注意,编译器会进行优化,将数字常量1替代a常量。所以cout << a << endl;输出为1。

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
const int a = 1;
int* b = const_cast<int*> (&a);
*b = 2;
cout << a << " " << *(&a) << " " << *b << endl;
}
// 输出:1 2 2

dynamic_cast

dynamic_cast 主要作用是将指向派生类对象基类指针或引用,安全的转换为指向派生类对象派生类指针或引用,并使用转换后的指针调用派生类独有的函数(非虚函数)。如果转换指针转换失败,则将返回空指针;如果转换引用失败,则将会抛出一个名为std::bad_cast的异常。
在如下3种情况中转换可以成功

  • expression的类型与待转换类型相同。则转换必定成功。
  • expression的类型为待转换类型的公有派生类。(指针向上转换)
  • expression的类型为待转换类型的公有基类时,必须满足以下两个要求,才会转换成功,否则转换失败。(指针向下转换)
    • 当expression为指向派生类的指针或引用派生类对象的基类引用。
    • 基类中必须包含虚函数,也就是必须具备多态性。

假设有如下两个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <string>
#include <iostream>
using namespace std;

class Base {
public:
Base() {}
Base(string s) : str(s) {}
virtual void Print() {cout << str << endl;}
private:
string str;
};

class Derived : public Base {
public:
Derived() {}
Derived(string s, int i) : Base(s), ival(i) {}
void Print() {
Base::Print();
cout << ival << endl;
}
void PrintIval() {
cout << ival << endl;
}
private:
int ival;
};

example:

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char *argv[])
{
//基类指针指向派生类对象,基类中包含虚函数,符合向下转换规则。
Base* b = new Derived("test", 1);
//使用基类指针无法调用派生类独有的函数,编译无法通过
b->PrintIval();
//类型转换至派生类指针就可以调用到派生类独有的函数
Derived* d = dynamic_cast<Derived*> (b);
d->PrintIval();
return 0;
}

reinterpret_cast

reinterpret_cast 主要的作用为允许任意长度相同的对象之间进行转换,而转换的安全性,则全部由程序员所保证,它只关注对象之间长度是否相同,长度不相同则无法通过编译。注意,reinterpret_cast无法去掉源对象的const、volatile属性

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char *argv[])
{
char a = 1;
//将a的指针转化为长整型数
long b = reinterpret_cast<long>(&a);
//将空指针转化为长整型数
long d = reinterpret_cast<long>(NULL);
//编译报错 int 4字节,而指针8字节
int e = reinterpret_cast<int>(&a);
return 0;
}

需注意在32位机器上,指针为4字节,而在64位机器上,指针为8字节。

static_cast

static_cast 类似C语言强制类型转换,它可以完成如下一些转换

  • 编译器隐式执行的类型转换,如int与float、double与char、enum与int之间的转换等。(精度大->精度小使用位截断处理)
  • 将任意类型表达式转换为void类型,或从void*指针中找回其中的值。
  • 基类与派生类指针或引用类型之间的转换,注意,由派生类转换至基类时(向上转换)是安全的,由基类至派生类转换时(向下转换)是非安全的。
    example(使用上述代码定义的类):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int main(int argc, char *argv[])
    {
    char a = 'a';
    //将 char -> int
    int i = static_cast<int>(a);

    Base* b = new Base("123");
    //将 Base 转换为 void*
    void* v = static_cast<void*>(b);
    //将 *void 转换为 Base
    b = static_cast<Base*>(v);

    Derived* d = new Derived("123", 123);
    //向上转换 将Derived* 转换为 Base*
    Base* bb = static_cast<Base*>(d);
    //向下转换 将Base* 转换为 Derived*
    Derived* dd = static_cast<Derived*>(b);
    //未定义的行为!非常危险!父类对象并不包含该函数,编译不会报错!
    dd->PrintIval();
    }
    一下内容取自effective C++

    如果可以,尽量避免转型,特别是在注重效率的代码中避免 dynamic_cast。如果有个设计需要转型的动作,试着发展无需转型的替代设计。
    如果转型是必要的,试着将它隐藏至某个函数背后,客户随后可以调用该函数,而不需将转型放进他们的代码内。
    宁可使用C++-style(新式转型),不要使用旧式转型。前者很容易辨识出来,而且有其不同的职责。

-------- 本文结束 感谢阅读 --------

本文标题:C++强制类型转换

文章作者:zyh

发布时间:2019年07月09日 - 20:17

最后更新:2019年07月31日 - 21:41

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持技术分享,您的支持将鼓励我继续创作!
0%