C# · 12月 20, 2021

[C++] 类型转换符static_cast、const_cast、dynamic_cast、reinterpret_cast

C++中为了避免松散的、无意义的类型转换,添加了四个类型转换操作符,以更严格地限制类型转换:static_cast,const_cast,reinterpret_cast,dynamic_cast,下面逐一看看他们的使用。

static_cast

用隐式转换、或者用户定义转换的组合在类型间的转换,可以使用static_cast,该运算符语法格式如下:

static_cast(expression);

仅当type_name可以被隐式转换为expression所属的类型时或者expression可以被隐式转换为type_name所属的类型时,可以使用static_cast.

它在以下场景时的类型转换时可用:

1.内置数据类型;

2.具有继承关系的类的指针和引用;

3.用户定义的可转换的类;

在继承类的指针或引用之间进行转换时,会把一个基类的指针转化成了一个派生类的指针,但实际上这个指针指向的还是原来的基类对象,因此在对virtual函数调用时需要注意其多态的特性。

如下示例中是对static_cast的使用:

#include

using namespace std;

class Animal{

public:

virtual void show() {

cout << "Animal show…." << endl;

}

};

class Person : public Animal{

private:

std::string name;

public:

Person(){}

//转换构造函数

explicit Person(std::string& name):name(name){}

virtual void show() {

cout << "Person show…." << endl;

}

virtual void getName() { cout << "Person's name:" << name << endl;}

};

class Flower{};

int main()

{

//1.隐式转换内置类型

cout << "====内置数据类型转换====" << endl;

double d1 = 3.14;

int i1 = 4;

int i2 = static_cast(d1);

double d2 = static_cast(i1);

cout << "i:"<< i2 << ",d:" << d2 << endl;

Animal ani;

Person per;

cout << "====对象地址====" << endl;

cout << "ani address:" << &ani << ",per address: " << &per << endl;

//2.具有继承关系的类的指针

cout << "====指针转换====" << endl;

Animal* pani = static_cast(&per);

Person* pper = static_cast(&ani);

cout <show(): ";

pani->show();

cout <show(): ";

pper->show();

cout << "pani:" << pani << ",pper: " << pper << endl;

//pper类型为Person*,但实际指向Animal对象,因此,调用将出现Segmentation fault

//若取掉该方法的virtual关键字,则不该方法多态特性消失,可以调用

//pper->getName();

//3.具有继承关系的类的引用

Animal& rani = static_cast(per);

Person& rper = static_cast(ani);

cout << "====引用转换====" << endl;

cout << "rani address: " << &rani << ",rper address: " << &rper << endl;

rani.show();

rper.show();

//rper类型为Person&,但引用的是Animal对象,因此调用将出现Segmentation fault,

//若取掉该方法的virtual,则不该方法多态特性消失,可以调用

//rper.getName();

//4.用户组合的可以进行隐式转换类之间的转换

cout << "====string转换为Person:====" << endl;

string str(“person1”);

Person p = static_cast(str);

p.getName();

//不相关类不能使用

//Flower* pflo = static_cast(&ani);

return 0;

}

/*

编译并运行:

@ubuntu:~$ g++ staticcast.cpp -o staticcast

@ubuntu:~$ ./staticcast

====内置数据类型转换====

i:3,d:4

====对象地址====

ani address:0x7ffe798cd2b0,per address: 0x7ffe798cd310

====指针转换====

Animal* pani->show(): Person show….

Person* pper->show(): Animal show….

pani:0x7ffe798cd310,pper: 0x7ffe798cd2b0

====引用转换====

rani address: 0x7ffe798cd310,rper address: 0x7ffe798cd2b0

Person show….

Animal show….

====string转换为Person:====

Person’s name:person1

*/

const_cast

const_cast运算符则用于在有不同cv限定符之间的转换,也就是说在const和volatile之间进行转换,且只能用于指针、引用或指向成员类型的指针,如果使用对象,编译器将提示错误:

error: invalid use of const_cast with type ‘int’,which is not a pointer,reference,nor a pointer-to-data-member type

使用const_cast示例如下:

#include

using namespace std;

int main()

{

const int i = 20;

int* k = const_cast(&i);

cout << "before modify,k:" << *k << ",i:" << i << endl;

*k = 40;

cout << "after modify,i:" << i << endl;

return 0;

}

/*

编译并运行:

@ubuntu:~$ g++ constcast.cpp -o constcast

@ubuntu:~$ ./constcast

before modify,k:20,i:20

after modify,k:40,i:20

*/

通过以上示例还可以发现,虽然将i的const取消了,但是依然无法修改i的值,所以说const_cast虽然可以取消指针的const性,但无法修改const值。

dynamic_cast

dynamic_cast运算符只能用于具有继承关系的类型之间的向上转换,且只能是指针或者引用。其语法格式如下:

dynamic_cast (expression)

若转型成功,则返回type-name的值,若转型失败且type-name是指针类型,则返回该类型的空指针,若转型失败且type-name是引用类型,则它将抛出bad_cast异常。

此外,dynamic_cast会进行运行时类型识别,而所需信息是存储在虚函数表中的,因此需要有virtual方法,以生成虚函数表,否则将出现如下异常:

error: cannot dynamic_cast ‘& ani’ (of type ‘class Animal*’) to type ‘class Person*’ (source type is not polymorphic)

使用示例如下:

#include

#include

using namespace std;

class Animal{

public:

virtual void show() {

cout << "Animal show…." << endl;

}

};

class Person : public Animal{

private:

string name;

public:

Person(){ this->name = “None”;}

Person(string & name):name(name){}

virtual void show() {

cout << "Person show…." << endl;

}

virtual void getName() { cout << "Person's name:" << name << endl;}

};

class Dog : public Animal

{

public:

virtual void show() {

cout << "Dog show…." << endl;

}

};

int main()

{

//不能使用对象转换,只能是指针或引用

//Animal a = dynamic_cast(per);

Animal ani;

Person per;

// 1.指针转换

Animal* pani = dynamic_cast(&per);

Person* pper = dynamic_cast(&ani);

cout << "====指针转换=====" << endl;

pani->show();

if (pper != NULL)

pper->show();

else

cout << "pper is null pointer" << endl;

// 2.引用转换

cout << "====引用转换=====" << endl;

Animal& rani = dynamic_cast(per);

rani.show();

try{

Person& rper = dynamic_cast(ani);

} catch (bad_cast& ex) {

cerr << ex.what() << endl;

}

// 3.不相关的类不能使用

//Dog dog;

//Person& rper2 = dynamic_cast(dog);

return 0;

}

/*

@ubuntu:~$ g++ dynamiccast.cpp -o dynamiccast

@ubuntu:~$ ./dynamiccast

====指针转换=====

Person show….

pper is null pointer

====引用转换=====

Person show….

std::bad_cast

*/

dynamic_cast和static_cast都可以对具有继承关系的指针和引用进行向上转换,优先使用前者。

reinterpret_cast

reinterpret_cast可以用于处理任意无关类型的指针或引用的转换,以及指针类型转换为足以存储指针表示的整型。但他有如下限制:

1.不能删除const;

2.不能将指针转换为更小的整型或者浮点型;

3.不能将数据指针转换为函数指针.

使用示例如下:

#include

#include

using namespace std;

class Dog{};

class Flower {

public:

void show() {

cout << "Flower show…" << endl;

}

};

typedef int (*PFunc)(int);

int sum(int i);

int main()

{

Flower oflo;

// 可以用于任意类型的指针/引用的转换

Dog* od = reinterpret_cast(&oflo);

Flower* flower = reinterpret_cast(od);

flower->show();

// 可以用于指针转换为足以存储指针表示的整型

int i = 20;

int* pi = &i;

long l = reinterpret_cast(pi);

cout << "l value:" << l << endl;

// 不能将指针转换为更小的整型或者浮点型

//short s = reinterpret_cast(pi);

// 不能将数据指针转换为函数指针

PFunc p;

//p = reinterpret_cast(&i);

// 可以将函数指针转换为数据指针

Dog* pi2 = reinterpret_cast(sum);

return 0;

}

int sum(int i) {

return 2 * i;

}

总的来说,使用reinterpret_cast是比较危险的。