C++类型转换
- 引言
- 1. C语言中的类型转换
- 2. 为什么C++需要四种类型转换
- C++强制类型转换
- 1.static_cast
- 补充
- 2.dynamic_cast
- 3.const_cast
- 4.reinterpret_cast
- RTTI
引言
1. C语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值
类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
- 显式类型转化:需要用户自己处理
- 缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
2. 为什么C++需要四种类型转换
C风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格
C++强制类型转换
1.static_cast
static_cast 是 C++ 中的一种类型转换操作符,用于在编译时进行静态类型转换。它提供了一种类型安全的转换方式,并在编译时进行类型检查,以确保转换的安全性。
static_cast 可以用于以下几个方面:
-
类型转换:用于在具有相关类型之间进行显式转换,例如将整数类型转换为浮点类型、将指针类型转换为整数类型等。
-
类层次转换:用于在类之间进行向上转型(基类指针或引用指向派生类对象)和向下转型(派生类指针或引用转换为基类指针或引用)。
-
避免隐式类型转换:可以使用 static_cast 来明确指定想要的转换,而不依赖于隐式转换的规则。
-
指针类型转换:可以用于将指针类型进行转换,但需要注意确保转换的安全性,因为 static_cast 在指针转换时无法进行动态类型检查。
应用场景包括但不限于:
- 在基本数据类型之间进行转换,例如将 int 转换为 float。
- 在类层次结构中进行向上转型和向下转型。
- 避免隐式类型转换,明确指定所需的转换方式。
- 在指针之间进行转换,但需要谨慎处理,确保转换的安全性。
- 需要注意的是,static_cast 并不适用于所有类型转换的情况,某些情况下可能需要使用其他类型转换操作符,如 dynamic_cast、reinterpret_cast 或 const_cast,具体选择取决于需要实现的转换类型和转换的安全性。在进行类型转换时,应仔细考虑转换的语义和安全性,并确保不违反类型系统的规则。
- 测试代码
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
#include<iostream>
/*int main()
{
enum Week
{
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
Week noday1 = static_cast<Week>(7); //sucess
Week noday2 = static_cast<Week>(8); //error
int i1 = 10;
char c = static_cast<char>(i1);//正确,int转换为char 只是编译通过 无意义
cout << "C:"<<c << endl;
int* pI = NULL;
//char* cI = static_cast<void*>(pI);//将int类型空指针转换为char空指针
int i2 = 10;
const int j = static_cast<const int>(i2);//正确,将int型数据转换成const int型数据
//j = 230; 报错
const int i3 = 20;
int ii = static_cast<int>(i3);//编译通过 ,static_cast不能转换掉i的const属性
ii = 211; //这里跟编译器的处理机制有关系 在vs2019中可以通过 换到linux下编译就报错了
//C+规定static_cast 是不可以换掉变量的const属性的
cout << "ii:" << ii << endl;
}*/
#include <iostream>
class Shape {
public:
virtual void print() const {
std::cout << "Shape" << std::endl;
}
};
class Circle : public Shape {
public:
void print() const override {
std::cout << "Circle" << std::endl;
}
void circleSpecificFunction() {
std::cout << "Circle-specific function" << std::endl;
}
};
int main() {
Circle circle;
// 向上转型:派生类指针转换为基类指针
Shape* shapePtr = static_cast<Shape*>(&circle);
shapePtr->print(); // 输出 "Circle",调用的是派生类的 print() 函数
// 向下转型:基类指针转换为派生类指针
Circle* circlePtr = static_cast<Circle*>(shapePtr);
circlePtr->print(); // 输出 "Circle",调用的是派生类的 print() 函数
circlePtr->circleSpecificFunction(); // 调用派生类特有的函数
return 0;
}
/*在上述代码中,Shape 是基类,Circle 是派生类,它们之间存在继承关系。
通过将派生类指针 &circle 使用 static_cast 转换为基类指针 Shape*,
可以进行向上转型,将派生类对象视为基类对象。同样地,
通过将基类指针 shapePtr 使用 static_cast 转换回派生类指针 Circle*,
可以进行向下转型,将基类对象还原为派生类对象。在向下转型后,
可以调用派生类特有的函数 circleSpecificFunction()。
需要注意的是,在进行向下转型时,需要确保基类指针指向的实际对象是派生类对象,
否则可能导致未定义的行为。因此,在实际应用中,通常会在进行向下转型之前使用
dynamic_cast 进行类型检查,以确保安全转型。*/
补充
关于static_cast不能去除变量const特性这点 ,有些编译器可能可以编译通过,这取决于编译器自身特殊的处理机制,但是C+规定是不允许的。
如上面,在我的vs2019中编译就可以通过 。
相同的代码我放在Linux下编译就会报错:
报错:
2.dynamic_cast
dynamic_cast 是 C++ 中的一种类型转换运算符,用于在类层次结构中进行安全的向上转型和向下转型,并进行运行时类型检查。与 static_cast 不同,dynamic_cast 具有一定的安全性检查机制,可以在转型过程中检查类型的有效性,避免出现错误的转型。
下面是一个示例代码,演示了 dynamic_cast 的用法和应用场景:
#include <iostream>
class Animal {
public:
virtual void makeSound() const {
std::cout << "Animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
void makeSound() const override {
std::cout << "Dog barking" << std::endl;
}
void dogSpecificFunction() {
std::cout << "Dog-specific function" << std::endl;
}
};
class Cat : public Animal {
public:
void makeSound() const override {
std::cout << "Cat meowing" << std::endl;
}
void catSpecificFunction() {
std::cout << "Cat-specific function" << std::endl;
}
};
int main() {
Animal* animalPtr = new Dog();
// 向下转型前先进行类型检查
if (Dog* dogPtr = dynamic_cast<Dog*>(animalPtr)) {
dogPtr->makeSound(); // 输出 "Dog barking"
dogPtr->dogSpecificFunction(); // 调用 Dog 特有的函数
} else {
std::cout << "Invalid downcast" << std::endl;
}
delete animalPtr;
return 0;
}
-
在上述代码中,Animal 是基类,Dog 和 Cat 是派生类,它们之间存在继承关系。通过创建一个指向 Dog 对象的 Animal* 指针 animalPtr,然后使用 dynamic_cast 进行向下转型,将基类指针转换为派生类指针。在转型之前,使用 dynamic_cast 对转型结果进行检查,确保转型是有效的。如果转型成功,即指针指向的对象是 Dog 类型,就可以安全地调用 Dog 类的成员函数。如果转型失败,即指针指向的对象不是 Dog 类型,可以根据需要进行错误处理。
-
需要注意的是,dynamic_cast 只能用于具有多态性的类层次结构,即基类必须至少定义一个虚函数。此外,dynamic_cast 只能在指针或引用之间进行转型,不能用于转换普通的数值类型。
-
通过使用 dynamic_cast,可以在进行类层次结构中的向下转型时进行类型检查,避免转型错误,提高程序的安全性
3.const_cast
const_cast 是 C++ 中的一种类型转换运算符,用于在特定情况下去除常量性 (constness)。它主要用于将对象的常量性转换为非常量性,从而可以修改原本被声明为常量的对象。
下面是一个示例代码,演示了 const_cast 的用法和应用场景:
#include <iostream>
void modifyValue(int& value) {
value = 42;
}
int main() {
const int constantValue = 10;
// 错误示例:无法将常量对象传递给非常量引用参数
// modifyValue(constantValue); // 编译错误
// 正确示例:使用 const_cast 去除常量性
modifyValue(const_cast<int&>(constantValue)); // OK
std::cout << "Modified value: " << constantValue << std::endl; // 输出 "Modified value: 42"
return 0;
}
-
在上述代码中,constantValue 被声明为常量,因此无法直接传递给一个非常量引用参数。这是因为常量对象是不可修改的。但是,如果我们确实需要在某种情况下修改该对象,可以使用 const_cast 去除其常量性。通过将 constantValue 强制转换为非常量引用,我们可以将其传递给 modifyValue 函数,修改其值。注意,这种转换只能用于移除常量性,而不能用于将非常量对象转换为常量对象。
-
需要注意的是,对于本身是常量的对象使用 const_cast 进行转换并尝试修改其值是一种不安全的操作,可能会导致未定义行为。因此,在使用 const_cast 时要小心,并确保不会导致程序的逻辑错误或潜在的错误。
-
总之,const_cast 主要用于去除常量性,以便在特定情况下修改被声明为常量的对象。然而,应该谨慎使用 const_cast,并且只在确实需要修改常量对象的情况下使用它。
4.reinterpret_cast
reinterpret_cast 是 C++ 中的一种类型转换运算符,用于在不同类型之间进行强制类型转换。它提供了一种低级别的类型转换,可以将一个指针或引用转换为不同类型的指针或引用,甚至可以将一个整数类型转换为一个指针类型,反之亦然。
- reinterpret_cast 的使用非常谨慎,因为它执行的是一种底层的、不安全的类型转换。它不会进行任何类型检查或转换操作,仅仅是将一个类型的位模式重新解释为另一个类型的位模式。因此,使用 reinterpret_cast 进行类型转换需要确保转换是有意义和合法的,否则可能导致未定义行为。
下面是一些示例代码,演示了 reinterpret_cast 的用法和应用场景
#include <iostream>
struct A {
int x;
};
struct B {
double y;
};
int main() {
A a;
a.x = 42;
// 将 A 对象的地址转换为 B 对象的地址
B* b = reinterpret_cast<B*>(&a);
// 修改 B 对象的成员
b->y = 3.14;
// 输出 A 对象的成员,将会看到修改后的值
std::cout << "a.x: " << a.x << std::endl; // 输出 "a.x: 1078530011"
return 0;
}
在上述代码中,我们将一个 A 对象的地址强制转换为一个 B 对象的地址,并通过这个指针修改了 B 对象的成员。由于 A 和 B 是不同的类型,这种转换是不安全的,会导致对象被重新解释为不同类型。在这个例子中,我们修改了 B 对象的成员 y,但实际上这个对象应该被看作是 A 类型的对象,因此 a.x 的值也会受到影响。
需要注意的是,reinterpret_cast 可能导致类型安全性问题和不可移植性问题。因此,应该避免在正常情况下使用 reinterpret_cast 进行类型转换,除非有明确的理由和合理的解释。一般而言,使用 reinterpret_cast 应该限制在与底层内存表示相关的特定情况下,如与位模式操作、底层编程或与外部系统的交互等。
总之,reinterpret_cast 提供了一种低级别的类型转换,可以在不同类型之间进行强制转换。然而,由于其不安全性和潜在的不可移植性,应该谨慎使用,并确保转换是有意义和合法的。
RTTI
Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
- typeid运算符
- dynamic_cast运算符