C++ 与算法

C++ 强制类型转换

整理 static_cast、dynamic_cast、const_cast 与 reinterpret_cast 的使用边界。

5 分钟阅读
  1. volatile
  • 告诉编译器不要优化;不要去寄存器中读取该变量的值,而是要从内存中读取
  • 有些时候操作系统会把硬件映射到内存中,程序通过操作内存来操作硬件(例如磁盘读写)
  • volatile与多线无关,并不能保证多线程内存安全,可引用头文件实现
  • 可以与const共用,const标识此变量不能被该程序修改,volatile标识此变量可能被该程序外的因素所更改

volatile会对程序运行结果造成影响,请看以下两端代码

int main()
{
volatile const int i = 1;

cout << i << endl; //输出1

//对编译器透明的情况下,把i的值加1
__asm {
mov dword ptr[ebp - 8], 1h
}

cout << i << endl; //输出2
}

如果不加volatile,输出会不同

int main()
{
const int i = 1;

cout << i << endl; //输出1

//对编译器透明的情况下,把i的值加1
__asm {
mov dword ptr[ebp - 8], 1h
}

cout << i << endl; //输出1
}

  1. static_cast

用于在编译期对某种类型的变量进行强制类型转换,但没有运行时的类型检查来保证转换的安全性 static_cast (variable)

主要用法:

  • 基类和派生类之间指针或引用的转换 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的
  • 基本数据类型的转换
  • 把void指针转换成目标类型的空指针
  • 把任何类型的表达式转换成void类型
int main()
{
auto a = 1;
auto b = static_cast<char>(a);
cout << typeid(a).name() << endl;  //int
cout << typeid(b).name() << endl;  //char

char ch = '8';
cout << ch - '\0' << endl; //56
int num = static_cast<int>(ch);
num++;
cout << num << endl; //57
///////////////////////////////////////////////////////
int *intP = new int(1);

void *charP = static_cast<void *>(intP);

void *p = nullptr;
float *fP = static_cast<float *>(p);

cout << typeid(charP).name() << endl; //void *
cout << typeid(fP).name() << endl;    //float *
delete intP;
}

  1. const_cast

用法为:const_cast(variable); 必须是指针或者引用

C++ the type in a const_cast must be a pointer, reference, or pointer to member to an object type

最好不要用这种方法,因为它会产生意想不到的情况(C++中未定义的行为),如以下代码所示

int main()
{
const int a = 1;
const int* ap = &a;
cout << *ap << endl;  //输出10

int b = const_cast<int&>(a);  //reference
int *bp = const_cast<int*>(ap);  //pointer

b = 10;
*bp = 18;
cout << b << endl;  //输出10
cout << a << endl;  //输出1
cout << *bp << endl;  //输出18
cout << *ap << endl;  //输出18
}

  1. reinterpret_cast

const_cast(variable); 所有映射中最危险的

主要有三种用途

  • 改变指针或引用的类型
  • 将指针或引用转换为一个足够长度的整形
  • 将整型转换为指针或引用类型
int main()
{
int a = 1;
int* p = &a;
cout << p << endl; //012FF710

auto c = reinterpret_cast<int>(p);
cout << c << endl;  //19920656
cout << typeid(c).name() << endl;  //int

auto d = reinterpret_cast<int*>(a);
cout << d << endl;  //00000001
cout << typeid(d).name() << endl;  //int *

auto e = reinterpret_cast<float&>(a);
cout << e << endl;  //1.4013e-45
cout << typeid(e).name() << endl;  //float
}

  1. dynamic_cast

dynamic_cast(variable); 必须是指针或者引用

You cannot use dynamic_cast to convert from a non-polymorphic class (a class with no virtual functions).

You can use static_cast to perform conversions of non-polymorphic types. However, static_cast does not perform a run-time check.

// C2683.cpp
// compile with: /c
class B { };
class D : public B { };

void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);  // C2683--Error Code
D* pd1 = static_cast<D*>(pb);   // OK
}

其特点如下:

  • 其他三种都是编译时完成的,dynamic_cast 是运行时处理的,运行时要进行类型检查
  • dynamic_cast 要求 <> 内所描述的目标类型必须为指针或引用。dynamic_cast 转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回 nullptr
  • 基类必须是多态类,必须要有虚函数
class Base
{
public:
virtual void print()
{
cout << "This is Base" << endl;
}

virtual ~Base()
{
cout << "Base's destructor called" << endl;
}
};

class derive : public Base
{
public:
void print() override
{
cout << "This is derive" << endl;
}

~derive()
{
cout << "derive's destructor called" << endl;
}
};

int main()
{
derive a;
Base* p = &a;

auto b = dynamic_cast<derive*>(p);
cout << typeid(b).name() << endl;  //class derive *
if (b == nullptr)
cout << "failed - 1" << endl;

auto c = dynamic_cast<Base*>(p);
cout << typeid(c).name() << endl;  //class Base *
if (c == nullptr)
cout << "failed - 2" << endl;

derive* p2 = new derive();
auto d = dynamic_cast<Base*>(p2);
cout << typeid(d).name() << endl;  //class Base *
if (d == nullptr)
cout << "failed - 3" << endl;

Base* p3 = new Base();
derive* e = dynamic_cast<derive*>(p3);  //class derive *
cout << typeid(e).name() << endl;
if (e == nullptr)
cout << "failed - 4" << endl;    //failed - 4

delete p2;
delete p3;
//derive's destructor called
//Base's destructor called
//Base's destructor called
//derive's destructor called
//Base's destructor called
}