C语言中的类型转换
在C语言中,类型转换有隐式类型转换和强制类型转换,例:
1. 隐式类型转换
可以转换基础类型,指针类型不能隐式类型转换。
1. int a = 97; 2. char c1 = a; //隐式类型转换 3. cout << c1 << endl; //打印结果为字符 'a'
2. 强制类型转换
1. char p1[] = "abcdefg"; 2. int* p2 = (int*)p1; //强制类型转换 3. //int* p2 = p1; //注意:指针类型不能隐式类型转换 4. //错误(活动) E0513 不能将 "char *" 类型的值分配到 "int *" 类型的实体 5. cout << p2 << endl; //打印结果为字符串地址
C++中的类型转换
在C++中一共提供了4种类型转换,他们分别是
- static_cast:静态类型转换;
- reinterpret_cast:重新解释类型;
- dynamic_cast:动态类型转换;
- const_cast:const只读类型变量转为非const变量;
这四种类型转换分别应用于各自的应用场景,一般不能混场景使用,否则可能会出现问题,下面分别举例说明,并结合程序详细分析。
1. static_cast
静态类型转换,对应于C语言中的隐式类型转换场景,可以转换基础数据类型,但是不能转换指针类型。该类型转换会在编译时进行类型检查。
1. #include <iostream> 2. using namespace std; 3. 4. int main() 5. { 6. //1.转换基础数据类型 7. int a = 97; 8. char b = static_cast<char>(a); 9. cout << b << endl; 10. 11. //2.转换指针类型 12. char str[] = "static_cast"; 13. int* p; 14. //报错 15. //p = static_cast<int*>(str); 16. //错误 C2440 “static_cast” : 无法从“char[12]”转换为“int* ” 17. //错误(活动) E0171 类型转换无效 18. //解决方法,强制转换 19. p = reinterpret_cast<int*>(str); //相当于 p = (int*)str; //强制类型转换 20. cout << p << endl; 21. 22. system("pause"); 23. return 0; 24. }
2. reinterpret_cast
重新解释类型,可用于转换指针,但是不能转换基础数据类型。
1. #include <iostream> 2. using namespace std; 3. 4. int main() 5. { 6. //1.转换指针类型 7. char str[] = "reinterpret_cast"; 8. int* p; 9. p = reinterpret_cast<int*>(str); //相当于 p = (int*)str; //强制类型转换 10. cout << p << endl; 11. 12. //2.转换基础数据类型 13. int a = 97; 14. char b; 15. //b = reinterpret_cast<char>(a); 16. //错误 C2440 “reinterpret_cast” : 无法从“int”转换为“char” 17. //错误(活动) E0171 类型转换无效 18. 19. system("pause"); 20. return 0; 21. }
3. dynamic_cast
动态类型转换,会进行动态类型检查,应用场景是在多态场景中(子类对象传给父类指针或引用),可以动态检查子类的类型,下面通过程序举例说明:
1. #include <iostream> 2. using namespace std; 3. 4. class GetArea 5. { 6. public: 7. virtual double get_area(int a, int b) = 0; 8. }; 9. 10. class Square : public GetArea 11. { 12. public: 13. virtual double get_area(int a, int b) 14. { 15. return a * b; 16. } 17. void print_Square() 18. { 19. cout << "Is Square" << endl; 20. } 21. }; 22. 23. class Triangle : public GetArea 24. { 25. public: 26. virtual double get_area(int a, int b) 27. { 28. return a * b * 0.5; 29. } 30. void print_Triangle() 31. { 32. cout << "Is Triangle" << endl; 33. } 34. }; 35. 36. void print_area(int a, int b, GetArea* g) 37. { 38. cout << g->get_area(a, b) << endl; 39. 40. //使用 dynamic_cast 进行动态类型检查 41. if (dynamic_cast<Square*>(g)) 42. { 43. Square* s_temp = dynamic_cast<Square*>(g); 44. s_temp->print_Square(); 45. } 46. else if (dynamic_cast<Triangle*>(g)) 47. { 48. Triangle* t_temp = dynamic_cast<Triangle*>(g); 49. t_temp->print_Triangle(); 50. } 51. else 52. { 53. cout << "Is Parent Class" << endl; 54. } 55. } 56. 57. int main() 58. { 59. Square s; 60. Triangle t; 61. print_area(10, 10, &s); 62. print_area(10, 10, &t); 63. 64. system("pause"); 65. return 0; 66. }
运行程序并查看打印结果
可以看到,通过dynamic_cast自动检查出了传入的子类究竟是矩形类还是三角形类。在上面的基础上添加代码继续分析以下两种情况,一是使用其他转换对子类父类进行转换,二是将类转换为另一种类。具体分析如下:
1. class GetLen{}; 2. 3. void func_test() 4. { 5. GetArea* g = NULL; 6. Square* s = NULL; 7. g = s; //子类可以传给父类 //类型兼容性原则 8. //s = g; //不能直接将父类传给子类 9. //错误 C2440 “ = ”: 无法从“GetArea * ”转换为“Square * ” 10. //错误(活动) E0513 不能将 "GetArea *" 类型的值分配到 "Square *" 类型的实体 11. s = dynamic_cast<Square*>(g); //ok 12. s = static_cast<Square*>(g); //ok 13. s = reinterpret_cast<Square*>(g); //ok 14. 15. GetLen* gl = NULL;; //其他类 16. //g = dynamic_cast<GetArea*>(gl); 17. //错误 C2683 “dynamic_cast” : “GetLen”不是多态类型 18. //错误(活动) E0698 运行时 dynamic_cast 的操作数必须包含多态类类型 19. 20. //g = static_cast<GetArea*>(gl); //err 21. //错误 C2440 “static_cast” : 无法从“GetLen * ”转换为“GetArea* ” 22. //错误(活动) E0171 类型转换无效 23. 24. g = reinterpret_cast<GetArea*>(gl); //ok 25. }
4. const_cast
应用场景是去除只读属性,但有一个前提是内存本身必须是可以修改的。
1. #include <iostream> 2. using namespace std; 3. 4. void MyPrintStr1(const char* str) //const char* str,对实参设置只读属性,防止误修改 5. { 6. cout << str << endl; 7. } 8. 9. void MyPrintStr2(const char* str) 10. { 11. cout << str << endl; 12. //str[0] = 'H'; //需要修改实参的场景,但const只读属性,无法修改内存 13. char* temp = NULL; 14. temp = const_cast<char*>(str); 15. temp[0] = 'H'; 16. 17. cout << str << endl; 18. } 19. 20. int main() 21. { 22. //1.在堆栈上分配内存 23. char str1[] = "how to use const_cast"; 24. MyPrintStr2(str1); 25. 26. //2.使用常量区字符串(内存的常量区不可修改) 27. const char* pStr = "how to use const_cast"; 28. MyPrintStr1(pStr); 29. //MyPrintStr2(pStr); 30. //引发了异常: 写入访问权限冲突。temp 是 0xDD9BF4。 31. 32. //3.const 类型的字符串 33. const char str2[] = "how to use const_cast"; 34. MyPrintStr2(str2); 35. 36. system("pause"); 37. return 0; 38. }
在测试函数中,我们分了三种情况,可以看出第1、3种情况可以正常通过const_cast去除只读属性,从而达到修改内存数据的目的,而第2种情况却出现了异常。这是因为,第1、3种情况虽然对字符串增加了只读属性,但是内存本身是在栈上分配的,而栈内存本身是可以修改的,所以运行成功。而第二种情况是使用了常量区(全局区)的内存,这段内存是不可修改的,所以即使我们使用const_cast去除了字符串的只读属性,也无法正常运行,这是因为内存本身不可修改的原因。所以,在使用const_cast的时候,首先要知道,const属性的变量是在哪分配的内存,该内存本身是否可以修改。
总结
一般C语言中能进行隐式类型转换的,都可以用 static_cast进行类型转换,指针不能进行隐式类型转换,也不能用static_cast转换;reinterpret_cast强制类型转换,重新解释类型,但不能用于C语言中能进行隐式类型转换的情况,也就是说static_cast和reinterpret_cast各司其职,不能混用;通过static_cast和reinterpret_cast,覆盖了C语言中的类型转换功能。dynamic_cast运行时类型识别,可用于区分子类。const_cast用于去除只读属性。
static_cast与dynamic_cast区别:static_cast是静态类型转换,在编译时进行类型检查;dynamic_cast是动态类型转换,在运行时进行类型检查。