【c++】四种类型转换形式
编译时:
static_cast(静态转换)
const_cast(去常性转换)
reinterpret_cast(重新解释转换,直接转换地址)
运行时:
dynamic_cast(动态转换,运行时类型识别 RTTI)
static_cast(静态转换)
用途 | 描述 | 注意事项 |
---|---|---|
基本数据类型之间的转换 | 用于 int 转 double 、char 转 int 等类型之间的转换。 |
适用于已知类型的转换,比 C 风格转换更安全。 |
void* 转换为其他类型 |
用于将 void* 指针还原为具体类型的指针。 |
必须确保指针类型正确,避免未定义行为。 |
左值转换为右值 | 用于将左值转换为右值引用,常用于移动语义。 | 强制转换为右值引用,触发移动构造。 |
类型层次结构中的指针和引用转换 | 在类继承关系中,用于基类和派生类之间的转换。 | 只能进行安全的向上转换,向下转换需要 dynamic_cast 。 |
int 类型转换为枚举 |
允许 int 与枚举类型之间的转换。 |
适用于整数到枚举的安全转换。 |
static_cast 在模板中的应用 |
用于确保目标类型和原始类型一致,并在模板编程中进行类型转换。 | 使用 static_assert 进行编译期类型检查。 |
1. 基本数据类型转换
static_cast
可用于基本数据类型之间的转换,如 int
转 double
、char
转 int
,等价于 C 风格转换,但更安全。
cpp
#include <iostream>
int main() {
double d = 3.14159;
int i = static_cast<int>(d); // 截断小数部分,转换为 3
char c = 'A';
int ascii = static_cast<int>(c); // 将字符 'A' 转换为 ASCII 码
std::cout << "i = " << i << ", ascii = " << ascii << std::endl;
return 0;
}
✅ 优点 :比 C 风格转换更安全,可读性更好,避免了 reinterpret_cast
的风险。
2. void*
转换为其他类型
static_cast
可以用于将 void*
还原为具体类型,但必须确保指针类型正确,否则可能导致未定义行为。
cpp
#include <iostream>
int main() {
int a = 42;
void* pVoid = &a; // int* → void*
int* pInt = static_cast<int*>(pVoid); // void* → int*
std::cout << "pInt = " << *pInt << std::endl;
return 0;
}
✅ 适用于已知原始类型的情况 ,如果不确定类型,应使用 reinterpret_cast
。
3. 左值转换为右值
在 C++11 及更高版本中,static_cast
可以将左值转换为右值引用 ,用于移动语义。
cpp
#include <iostream>
#include <utility> // std::move
class Data {
public:
Data() { std::cout << "构造函数\n"; }
Data(const Data&) { std::cout << "拷贝构造\n"; }
Data(Data&&) { std::cout << "移动构造\n"; }
};
int main() {
Data d;
Data d2 = static_cast<Data&&>(d); // 强制转换为右值引用,触发移动构造
return 0;
}
✅ 与 std::move
类似,但 static_cast<Data&&>
是显式的转换方式。
4. 类型层次结构中的指针和引用转换
在类的继承关系 中,static_cast
可以在基类和派生类 之间进行转换,但仅限安全的向上转换。
cpp
#include <iostream>
class Base {
public:
virtual void show() { std::cout << "Base 类\n"; }
};
class Derived : public Base {
public:
void show() override { std::cout << "Derived 类\n"; }
};
int main() {
Derived d;
Base* pBase = static_cast<Base*>(&d); // 向上转换
pBase->show(); // 仍然调用 Derived 的 `show()`
return 0;
}
⚠️ 向下转换(Base*
→ Derived*
)是不安全的,需要用 dynamic_cast
。
5. int
类型转换为枚举
static_cast
允许整数和枚举类型之间的转换。
cpp
#include <iostream>
enum Color { RED, GREEN, BLUE };
int main() {
int num = 1;
Color c = static_cast<Color>(num); // 将 int 转换为枚举类型
std::cout << "c = " << c << std::endl;
return 0;
}
✅ 适用于整数到枚举的安全转换。
6. static_cast
在模板中的应用
在模板编程中,可以使用 static_cast
确保目标类型和原始类型一致。
cpp
#include <iostream>
#include <type_traits>
template <typename T, typename U>
T convert(U value) {
static_assert(std::is_convertible<U, T>::value, "类型不兼容!");
return static_cast<T>(value);
}
int main() {
double d = 3.14;
int i = convert<int>(d); // double → int
std::cout << "i = " << i << std::endl;
return 0;
}
✅ static_assert
可用于编译期检查类型是否可以转换。
const_cast(去常性转换)
特点 | 描述 |
---|---|
只能对同类型使用 | const_cast 类型必须相同。 |
不能用于基本数据类型 | 不能用于基本数据类型之间的转换(例如:int → double )。 |
转换目标为指针或引用 | 只能将指针或引用的常量属性去除,而不能作用于值类型。 |
常量指针/引用被转换为非常量的指针/引用,并仍然指向原来的对象
例子:
-
常量指针转为非常量指针
cpp#include <iostream> void modifyValue(const int* ptr) { // const_cast 去常性转换 int* modifiablePtr = const_cast<int*>(ptr); *modifiablePtr = 20; // 可以修改原对象 } int main() { const int a = 10; modifyValue(&a); // 将常量指针转换为非常量指针 return 0; }
-
常量引用转为非常量引用
cpp#include <iostream> void modifyValue(const int& ref) { // const_cast 去常性转换 int& modifiableRef = const_cast<int&>(ref); modifiableRef = 20; // 可以修改原对象 } int main() { const int a = 10; modifyValue(a); // 将常量引用转换为非常量引用 return 0; }
注意事项:
const_cast
只会移除常量属性,并不会改变对象本身。- 对于常量对象的修改仍然是未定义行为,因此不应使用
const_cast
去修改那些原本是常量的数据。
reinterpret_cast(重新解释转换)
是 C++ 中最危险的类型转换之一,它将数据从一种类型"强制"转换为另一种类型,并且不进行任何类型检查。
它是按位解释的
特点 | 描述 |
---|---|
最危险的转换 | reinterpret_cast 不进行任何类型检查,可能导致未定义行为。 |
按位解释 | 转换直接基于内存的二进制表示,不考虑类型的语义。 |
指针之间的转换 | 可用于不同类型的指针之间的转换。 |
指针与整数之间的转换 | 可将指针转换为整数,反之亦然。 |
1. 指针类型转换
cpp
#include <iostream>
int main() {
int a = 42;
// 强制将 int* 转换为 double*,危险操作,按位解释
double* p = reinterpret_cast<double*>(&a);
std::cout << *p << std::endl; // 不确定行为,可能崩溃
return 0;
}
2. 指针与整数转换
cpp
#include <iostream>
int main() {
int* p = reinterpret_cast<int*>(0x1234); // 将整数转换为指针
std::cout << "Pointer: " << p << std::endl;
uintptr_t addr = reinterpret_cast<uintptr_t>(p); // 将指针转换为整数
std::cout << "Address as integer: " << addr << std::endl;
return 0;
}
动态转换(dynamic_cast
)
是一种用于在类层次结构中进行指针或引用类型转换的操作,特别适用于下行转换 (基类指针或引用转换为派生类指针或引用)。dynamic_cast
在运行时检查类型安全 ,依赖于虚函数表和运行时类型信息(RTTI),通过虚函数表中的信息来实现类型检查。
动态转换的基本概念:
-
上行转换(向上转换):
- 向上转换 是指派生类对象转换为基类指针或引用是在编译时进行的,与静态类型转换等价,不查虚表
-
下行转换(向下转换):
-
向下转换 是指基类指针或引用转换为派生类指针或引用。这种转换在运行时需要类型检查,
-
并且只有在公有继承和继承关系中存在虚函数时才有效,否则没有运行时类型信息(RTTI)进行类型检查。
-
使用
dynamic_cast
来进行下行转换时,转换成功时返回派生类指针或引用,转换失败时返回nullptr
(对于指针类型),或者抛出std::bad_cast
异常(对于引用类型)。 -
例如 :
cppclass Base { public: virtual ~Base() {} // 必须有虚析构函数,以支持RTTI }; class Derived : public Base {}; int main() { Base* pBase = new Derived(); Derived* pDerived = dynamic_cast<Derived*>(pBase); // 成功,转换为派生类指针 if (pDerived) { std::cout << "转换成功!" << std::endl; } else { std::cout << "转换失败!" << std::endl; } delete pBase; return 0; }
- 如果
pBase
实际上指向一个Derived
类型对象,则pDerived
会成功转换,并指向该对象。 - 如果
pBase
并不指向Derived
类型对象,dynamic_cast
将返回nullptr
,指示转换失败。
- 如果
-
如何判断是否能够进行下行转换:
- 条件 :只有当类之间存在虚函数(如虚析构函数)时,RTTI 才会被启用,
dynamic_cast
才能进行类型检查。 - 执行流程 :
dynamic_cast
会查询对象的虚函数表(vtable)。- 如果查询到类型匹配,则转换成功。
- 如果查询失败,则返回
nullptr
(对于指针类型),或抛出异常std::bad_cast
(对于引用类型)。