文章目录
类型转换
说起C++的类型转换,必须首先的说C风格的类型转换,因为C++兼容C语言,C语言有两种类型转换,一种隐式的,一种显式的。
C语言隐式转换:
double n = 1.68;
int m = n; //隐式转换, double -> int
cout << "int m: " << m << ", double n: " << n << endl;
C语言显式转换:
double n = 1.68;
int m = n; //隐式转换, double -> int
cout << "int m: " << m << ", double n: " << n << endl;
int* p = &m;
int int_p = (int)p; //显式转换, int* -> int
cout << "p: " << p << ", *p: " << *p << ", int_p: " << int_p << endl;
int* p2 = (int*)int_p; //显式转换, int -> int*
cout << "p2: " << p2 << ", *p2: " << *p2 << ", int_p: " << int_p << endl;
程序运行结果:
为什么有了C语言的类型转换,还需要C++的类型转换呢?
因为C++最重要的一个特性就是面向对象编程,对象就是由类构造而成的,C++有各种各样自定义的类,基类,派生类等等之间的转换强制使用以前的C风格转换不安全,简单点说,C++提供了四种更为安全规范的强制类型转换,分别为static_cast,reinterpret_cast,dynamic_cast,const_cast。
一、static_cast
静态类型转换(斯文的劝导,温柔的转换),如 int 转换成 char。
用法:
-
用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。上行(派生类到基类)指针或引用转换安全,下行(基类到派生类)不安全。
简单理解为什么子类到父类就是安全的,因为子类可能有父类没有的方法,而父类所有的方法子类都有,从一个可以使用很多方法的对象到只能使用很多方法中的一部分方法的对象的转换肯定是安全的,如果你从父类转到子类,那么当调用子类专属方法时,这个转换子类就会报错,因为父类没有这个方法,所以不安全。
-
用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成enum。这种转换的安全性也要开发人员来保证。
-
把空指针转换成目标类型的空指针。
-
把任何类型的表达式转换成 void 类型。
class Animal
{
public:
virtual void cry() = 0;
};
class Cat : public Animal
{
public:
void cry() {
cout << "喵喵喵" << endl;
}
void play() {
cout << "主人我想去爬树" << endl;
}
};
class Dog : public Animal
{
public:
void cry() {
cout << "汪汪汪" << endl;
}
void play() {
cout << "主人带我出去溜溜弯" << endl;
}
};
int test_static() {
//1:父类与子类之间的转换,上行安全(子类到父类),下行不安全(父类到子类)
Dog dog;
Animal* animal = static_cast<Animal*>(&dog); //上行转换,dog->animal animal指向dog
animal->cry(); //cry是纯虚函数,指向dog方法
Dog* dog2 = static_cast<Dog*>(animal); //下行转换,animal->dog
dog2->cry();
Cat* cat = static_cast<Cat*>(animal); //下行转换,animal->cat 虽然可以转换,但是不安全
cat->cry(); //因为animal指向dog,输出dog的cry,与本意不符,不安全,所以static_cast下行转换不安全
//2:基本类型的转换
char ch = 'a';
int n = static_cast<int>(ch); //'a'的ASIIC码为:97
cout << n << endl;
//3:把空指针转换成目标类型的空指针
int* p = static_cast<int*>(NULL);
Dog* dog3 = static_cast<Dog*>(nullptr);
cout << p << endl;
cout << dog3 << endl;
//4:把任何类型的表达式转换成void类型
int* pi = new int[10];
void* vpt = static_cast<void*>(pi); //用途:在设计通用函数或模板时,可以使用void指针来创建与类型无关的接口。
cout << "pi: " << pi << ", vpt: " << vpt << endl;
//5:static_cast类似与C语言中的隐式类型准转,你不写也可以执行这个转换
//比如3:中的 int* p = static_cast<int*>(NULL);可以直接写成:
int* intp = NULL;
return 0;
}
程序运行结果:
忠告:
- 上面static_cast中的四种情况,除了父类转换到子类中需要显示使用static_cast外,其余几种情况都不需要额外显示使用,都是C/C++中默认隐式转换的。
二、reinterpret_cast
重新解释类型(挂羊头,卖狗肉) 不同类型间的互转,数值与指针间的互转。
TYPE b = reinterpret_cast <TYPE> ( a ),TYPE必须是一个指针、引用、算术类型、函数指针。
用法:
- 基本类型中不同类型之间的转换,类似C语言中的显式转换。
- 父类与子类之间的的转换,子类与子类之间的转换(static_cast不可以进行子类与子类之间的转换)。
int test_reinterpret_cast() {
//1:不同类型之间的转换
int* p = reinterpret_cast<int*>(0x88aa88);
cout << p << endl;
//2:父子类之间的转换,子类跟子类之间的转换
Dog dog;
Animal* animal = reinterpret_cast<Animal*>(&dog); //上行转换 dog->animal,animal指向dog
animal->cry();
Dog* dog1 = reinterpret_cast<Dog*>(animal); //下行转换 animal->dog
Cat* cat1 = reinterpret_cast<Cat*>(animal); //下行转换 animal->cat
//Cat* cat = static_cast<Cat*>(&dog); //用法错误,不可以使用static进行子类之间的准换
Cat* cat2 = reinterpret_cast<Cat*>(&dog); //dog->cat 可以使子类之间互转,但是static_cast不行
//能尽量不用reinterpert就尽量不用,使用static安全些
animal->cry();
dog1->cry();
cat1->cry();
cat2->cry();
return 0;
}
程序运行结果:
忠告:
- 滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
三、dynamic_cast
动态类型转换,在程序运行之后才进行类型检查,dynamic_cast 在将父类 cast 到子类时,父类必须要有虚函数。
用法:
- 将一个基类对象指针 cast 到继承类指针,dynamic_cast 会根据基类指针是否真正指向继承类指针来做相应处理。失败返回 null,成功返回正常 cast
后的对象指针。 - 将一个基类对象引用 cast 到继承类对象,dynamic_cast 会根据基类对象是否真正属于继承类来做相应处理。失败抛出异常 bad_cast。
//1:基类指针接受派生类
void dynamic_demo1(Animal* animal) {
animal->cry(); //多态方法,dynamic操作必须是多态类型
Dog* dog = dynamic_cast<Dog*>(animal); //返回animal中子类原来的模样,如果是dog,转换成dog,如果不是,返回null
if (dog) { //如果dog不为null
cout << "这是一只狗,将执行dog自己私有的方法:" << endl;
dog->play();
}
else { //如果为null
cout << "这不是一只狗,这是其他动物" << endl;
}
Cat* cat = dynamic_cast<Cat*>(animal); //返回animal中子类原来的模样,如果是cat,转换成cat,如果不是,返回null
if (cat) {
cout << "这是一只猫,将执行cat自己私有的方法:" << endl;
cat->play();
}
else {
cout << "这不是一只猫,这是其他动物" << endl;
}
}
void test_dynamic_demo1() { //用于测试dynamic_demo1:1:基类指针接受派生类
Dog dog;
Cat cat;
Animal* animal = &dog;
dynamic_demo1(animal);
Animal* animal2 = &cat;
dynamic_demo1(animal2);
return 0;
}
//2:基类引用接受派生类
void dynamic_demo2(Animal& animal) {
animal.cry(); //多态方法,dynamic操作必须是多态类型
try {
Dog& dog = dynamic_cast<Dog&>(animal); //如果animal引用的不是dog,就会抛出异常,是的话就转换成dog
cout << "这是一只狗,将执行dog自己私有的方法:" << endl;
dog.play();
}
catch (exception ex) {
cout << "animal不是一只dog:" << ex.what() << endl;
}
try {
Cat& cat = dynamic_cast<Cat&>(animal); //如果animal引用的不是cat,就会抛出异常,是的话就转换成dog
cout << "这是一只猫,将执行cat自己私有的方法:" << endl;
cat.play();
}
catch (exception ex) {
cout << "animal不是一只cat:" << ex.what() << endl;
}
}
void test_dynamic_demo2() {
Dog dog;
Cat cat;
Animal& animal = dog;
dynamic_demo2(animal);
Animal& animal2 = cat;
dynamic_demo2(animal2);
}
程序运行结果:
test_dynamic_demo1():
test_dynamic_demo2():
忠告:
- dynamic_cast只能在多态(也就是父类必须要要有虚函数)中使用,判断父类具体指向哪个子类,也可以判断父类是否指向父类。
四、const_cast
去 const 属性 ( 仅针对于指针和引用 )。
用法:
- 直接修改,在当前指针直接进行转换修改。
- 间接修改,把当前指针去掉const属性,使用一个没const属性的指针接受,使用这个指针进行修改内容操作。
void const_demo(const char* p) {
//1:直接修改,将p的去掉const属性,修改第一个字符为'A'
const_cast<char*>(p)[0] = 'A'; //const_cast只能修改指针,引用类型
//2:间接修改,定义一个p2接受被去掉const属性的p指针,使用p2修改第二个字符为'B'
char* p2 = const_cast<char*>(p);
p2[1] = 'B';
}
void test_const_demo() {
//1:const字符串数组可以去掉const
const char p[] = "Thank you for your likes and comments collection";
cout << p << endl;
const_demo(p);
cout << p << endl;
//2:字面值常量不可以去掉const,字面值常量存储在常量区,不允许进行修改
const char* p2 = "谢谢你的点赞评论收藏";
cout << p2 << endl;
//const_demo(p2); //不允许修改,编译会报错
cout << p2 << endl;
}
程序运行结果:
忠告:
- const_cast不能去除字面值常量的const,必须是可以修改的内存才可以去掉const。
- 也不能对非指针和非引用进行 const 转换。
五、类型转换使用建议
-
static_cast 静态类型转换,C++编译器会做编译时的类型检查,可以进行隐式转换;基本类型转换;父子类之间合理转换。
-
若不同类型之间,进行强制类型转换,用 reinterpret_cast<>() 进行重新解释。
C 语言中能隐式类型转换的,在 C++中可用 static_cast<>()进行类型转换,C++编译器在编译检查一般都能通过;C 语言中不能隐式类型转换的,在 C++中可以用reinterpret_cast<>() 进行强制类型解释。static_cast<>() 和 reinterpret_cast<>()基本上把C语言中的强制类型转换给覆盖,注意 reinterpret_cast<>()很难保证移植性。
-
dynamic_cast<>(),动态类型转换,安全的虚基类和子类之间转换,运行时类型检查。
-
const_cast<>(),去除变量的只读属性。
最后的忠告:程序员必须清楚的知道:要转的变量,类型转换前是什么类型,类型转换后是什么类型,转换后有什么后果。
C++大牛建议:一般情况下,不建议进行类型转换,避免进行类型转换,能少用就少用。