1.C语言的强制类型转换
C语言的强制类型转换不安全
比如我们看下面这个例子
这个地方为什么是*p和n打印出来结果不一样?
我们通过汇编可以看到n在汇编的时候被替换成10(也就是0A)所以打印出来就是10 而不是11
还有不少例子可以说明C语言的强制类型转换可能并不是那么安全!!! 这里不一一列举
接下来我们来介绍几种C++的类型转换!!!
2.static_cast
语法参数:static_cast<目标类型>(源对象)
- 使用场景:主要用于基本数据类型之间的转换,以及类层次结构中基类和派生类之间指针或引用的上行转换(派生类到基类)。
int a = 10;
float b = static_cast<float>(a); // 基本数据类型转换
class Base {};
class Derived : public Base {};
Derived d;
Base* b_ptr = static_cast<Base*>(&d); // 派生类指针转换为基类指针
基类指针转换成派生类指针 编译可以通过 不安全 不建议使用
像下面这种情况pb是指向A对象的 但是pb类型是B* 所以可以访问B类的成员y 但是它指向的是A类型的对象 去哪找B类的成员y啊 这个行为是危险的!!!
基类指针转换成派生类指针推荐dynamic_cast
优点:转换过程在编译期完成,效率高,能进行一定的类型检查,比 C 语言强制转换更安全。
- 缺点:不能用于无关类型之间的转换,对于类层次结构中的下行转换(基类到派生类)不进行运行时检查,可能存在风险。
3.reinterpret_cast
语法参数:reinterpret_cast<目标类型>(源对象)
- 使用场景:用于将一种类型的指针或引用转换为另一种完全不同类型的指针或引用,通常用于底层编程,如将指针转换为整数类型。
使用准则与风险警示 避免使用:除非绝对必要(如系统编程、与 C 代码交互),否则应避免使用 reinterpret_cast。
类型一致性:确保转换后的类型与实际数据类型兼容
底层二进制重解释:直接将一个类型的二进制位模式(bit pattern)解释为另一个类型,不进行任何类型检查或数据转换。
无视继承关系:可用于完全不相关的类之间的指针转换。
平台依赖性:转换结果可能因平台(如指针大小、字节序)而异,不可移植。
极高风险:容易导致未定义行为,需谨慎使用。
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
// 这里使用static_cast会报错,应该使用reinterpret_cast
//int *p = static_cast<int*>(a);
int* p = reinterpret_cast<int*>(a);
return 0;
}
4. dynamic_cast
dynamic_cast 是 C++ 中四种类型转换操作符之一,专门用于处理继承关系中的多态类型转换。与其他转换操作符不同,它在运行时检查类型的兼容性,确保转换的安全性。
dynamic_cast<目标类型>(源对象);
其核心特性包括:
运行时类型检查:通过对象的虚函数表(VTABLE)验证转换的合法性。
仅用于多态类型:必须作用于包含至少一个虚函数的类(即基类有虚函数)。
支持向上 / 向下 / 交叉转型: 向上转型(派生类→基类):与 static_cast 效果相同。
向下转型(基类→派生类):安全检查,避免未定义行为。
交叉转型(兄弟类之间):通过公共基类进行安全转换。 指针转换失败返回 nullptr,引用转换失败抛出 std::bad_cast 异常。
(1). 指针类型转换(最常用)
语法:Derived* ptr = dynamic_cast<Derived*>(basePtr);
成功:返回派生类指针;
失败:返回 nullptr。
class Shape {
public:
virtual ~Shape() {} // 必须有虚函数
};
class Circle : public Shape {};
class Square : public Shape {};
void example() {
Shape* ptr = new Circle(); // 基类指针指向派生类对象
Circle* circle = dynamic_cast<Circle*>(ptr); // 安全,返回Circle*
Square* square = dynamic_cast<Square*>(ptr); // 失败,返回nullptr
if (circle) std::cout << "ptr is a Circle";
if (!square) std::cout << "ptr is not a Square";
}
Shape* ptr = new Shape(); // 动态类型是Shape
Circle* circle = dynamic_cast<Circle*>(ptr); // 失败,返回nullptr
Square* square = dynamic_cast<Square*>(ptr); // 失败,返回nullptr
(2). 引用类型转换
语法:Derived& ref = dynamic_cast<Derived&>(baseRef);
成功:返回派生类引用;
失败:抛出 std::bad_cast 异常。
void printCircleArea(const Shape& shape) {
try {
const Circle& circle = dynamic_cast<const Circle&>(shape);
std::cout << "Circle area"; // 安全,shape是Circle类型
} catch (const std::bad_cast&) {
std::cout << "Not a circle"; // shape不是Circle类型
}
}
(3). 转换为 void*(高级用法)
语法:void* rawPtr = dynamic_cast<void*>(basePtr);
作用:获取对象的真实内存地址(用于调试或内存操作)。
Shape* ptr = new Circle();
void* rawAddr = dynamic_cast<void*>(ptr); // 获取对象的原始地址
(1)场景 1:基类指针→派生类指针(向下转型)
需求:调用派生类特有的方法。
class Animal {
public:
virtual void speak() { std::cout << "Animal"; }
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Woof"; }
void fetch() { std::cout << "Fetching..."; }
};
void interact(Animal* a) {
Dog* dog = dynamic_cast<Dog*>(a);
if (dog) dog->fetch(); // 安全调用Dog特有的方法
}
(2)场景 2:接口验证(判断对象是否实现特定接口)
需求:检查对象是否支持某个接口。
class ILoggable {
public:
virtual void log() = 0;
};
class FileLogger : public ILoggable {
public:
void log() override { /* 写日志到文件 */ }
};
void saveLog(ILoggable* logger) {
if (logger) logger->log(); // 安全调用接口方法
}
5.const_cast
作用:去除(或添加)对象的 const 或 volatile 限定符,唯一能修改类型限定的转换操作符。
核心风险:若原对象是 const,通过 const_cast 修改它会导致未定义行为。
(1). 去除 const 限定(最常用)
语法:const_cast<类型&>(常量引用) 或 const_cast<类型*>(常量指针)
场景:需要在函数内部修改传入的 const 参数。
void printAndModify(const int& num) {
std::cout << "Before: " << num << std::endl;
// 去除const限定,获取可修改的引用
int& nonConstNum = const_cast<int&>(num);
nonConstNum = 100; // 修改值
std::cout << "After: " << num << std::endl;
}
int main() {
int x = 10;
printAndModify(x); // 输出: Before: 10 After: 100
const int y = 20;
printAndModify(y); // 危险!修改const对象导致未定义行为
}
(2).添加 const 限定
语法:const_cast<const 类型&>(变量)
场景:将非 const 对象传递给接受 const 参数的函数。
void process(const std::string& text) {
// 只读操作
}
void wrapper(std::string& text) {
// 添加const限定,调用process函数
process(const_cast<const std::string&>(text));
}
(3).修改 const 成员函数内部状态
场景:在 const 成员函数中修改不影响对象逻辑状态的成员(如缓存、计数器)。
(1)场景 1:适配旧 API
需求:调用不接受 const 参数的旧函数。
// 旧函数(未声明const)
void legacyPrint(int* num) {
std::cout << *num << std::endl;
}
void safePrint(const int& num) {
// 仅当num确实不是const对象时才可安全转换
legacyPrint(const_cast<int*>(&num));
}
(2)场景 2:实现 const/ 非 const 版本的成员函数
需求:避免代码重复,通过 const 版本调用非 const 版本。
class Container {
private:
std::vector<int> data;
public:
// 非const版本(实际修改数据)
int& operator[](size_t index) {
return data[index];
}
// const版本(通过const_cast调用非const版本)
const int& operator[](size_t index) const {
return const_cast<Container*>(this)->operator[](index);
}
};
6.mutale
mutable 关键字:突破 const 的限制
作用:允许在 const 成员函数中修改被标记的成员变量。
核心场景:维护与对象逻辑状态无关的辅助数据(如缓存、计数器)
class Counter {
private:
mutable int count = 0; // 可在const函数中修改
public:
void increment() const {
count++; // 合法:mutable成员可突破const限制
}
int getCount() const {
return count;
}
};
7.volatile
volatile 关键字:对抗编译器优化
作用:告诉编译器 “不要优化这个变量”,每次都从内存读取 / 写入。
核心场景:访问硬件寄存器、多线程共享的敏感变量。
比如文章开头那个例子
编译器就不会在编译的时候进行替换 而是直接从内存内取 就不会出现开头的问题了