【C++】C++四种类型转换操作符详解

目录

1、static_cast

2、dynamic_cast

3、const_cast

4、reinterpret_cast

实际应用


为了解决C语言传统强制类型转换的缺陷,提供更安全、明确的语义和编译时检查,C++引入四种类型转换操作符(static_castdynamic_castconst_castreinterpret_cast),本篇文章主要介绍这四种类型转换符的使用,以及使用时需要注意的方面。

1、static_cast

编译时类型转换,比较常用,用于相关类型之间的转换。

语法:

static_cast<目标类型>(表达式)

常用示例

基本数据类型转换:

cpp 复制代码
int i = 10;
double d = static_cast<double>(i);  // int转double

float f = 3.14f;
int j = static_cast<int>(f);        // float转int(截断小数部分)

char c = static_cast<char>(65);     // int转char

类层次结构转换:

cpp 复制代码
class Base { virtual ~Base() {} };
class Derived : public Base {};

// 向上转换(安全)
Derived d;
Base* b = static_cast<Base*>(&d);

// 向下转换(不安全,需要程序员保证正确性)
Base* basePtr = new Derived();
Derived* derivedPtr = static_cast<Derived*>(basePtr);

类之间的转换需要注意的是,向上转换(子类向父类转换)是安全的,但是向下转换(父类向子类可能是不安全的)。

为什么向下转换是不安全的呢?

static_cast 进行向下转换(downcasting)不安全的主要原因是 缺乏运行时类型检查 。

编译时检查,运行时不验证,static_cast只在编译时检查类型关系是否合法,运行时不会验证对象的实际类型,如果基类指针实际指向不是目标派生类对象,编译时检查可以通过,但是结果是未定义的,如下具体demo:

cpp 复制代码
class Base {
public:
    virtual ~Base() = default;
};

class Derived1 : public Base {
public:
    void func1() { /* ... */ }
};

class Derived2 : public Base {
public:
    void func2() { /* ... */ }
};

// 危险的向下转换
Base* ptr = new Derived2();  // 实际指向Derived2对象
Derived1* d1 = static_cast<Derived1*>(ptr);  // 编译通过!
d1->func1();  // 未定义行为!访问了错误的对象

编译器检查流程如下:

Base* ptr = new Derived2();

这里编译器只知道:

  1. ptr的静态类型是Base*

  2. 赋值语句语法正确

  3. Derived2继承自Base

Derived1* d1 = static_cast<Derived1*>(ptr);*

这里编译器检查:

1. ptr是Base类型

  1. Derived1继承自Base

  2. static_cast语法合法

如上,这种错误的向下转换,因为static_cast是在编译期间检查静态类型关系,因此是无法识别的。

2、dynamic_cast

运行时类型检查的安全向下转换,主要用于多态类型的转换。

语法:

dynamic_cast<目标类型>(表达式)

dynamic_cast可以保证基类指针正确的向下转换,如下:

cpp 复制代码
class Base {
public:
    virtual ~Base() {}  // 必须有虚函数
};

class Derived : public Base {
public:
    void derivedMethod() {}
};

Base* basePtr = new Derived();

// 安全的向下转换
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr != nullptr) {
    derivedPtr->derivedMethod();  // 转换成功
} else {
    // 转换失败
}

向下转换就可能存在转换成功或者转换失败的情况,转换失败时,指针返回nullptr,引用抛出bad_cast异常。

虽然dynamic_cast能够确保基类向下转换时的安全问题,但是它也付出了一些代价,运行时,开销比较大,因为要进行类型检查(查询RTTI信息),虚函数表访问等。

3、const_cast

主要用于添加或移除const、volatile限定符

语法:

const_cast<目标类型>(表达式)

适用场景

移除const限定符

cpp 复制代码
const int ci = 10;
int* pi = const_cast<int*>(&ci);  // 移除const
*pi = 20;  // 危险!可能导致未定义行为

// 更安全的用法
void func(const char* str) {
    char* modifiableStr = const_cast<char*>(str);
    // 只有当原始数据不是const时才安全修改
}

这里需要注意的是,不是说移除了const限定符,我们就能对这个属性进行随意修改了。

移除了const限定符,技术上可以对属性修改了,但是能否安全修改取决于原始对象是否真的是const,这里分别举安全的情况、不安全的情况的例子,方便大家理解

安全的情况

原始的对象不是const

cpp 复制代码
int value = 42;  // 原始对象不是 const
const int* ptr = &value;  // 通过 const 指针访问

// 使用 const_cast 移除 const,可以安全修改
int* modifiable = const_cast<int*>(ptr);
*modifiable = 100;  // ✅ 安全,因为原始对象不是 const
std::cout << value << std::endl;  // 输出: 100

不安全的情况

修改真正的const对象

cpp 复制代码
const int readonly = 42;  // 真正的 const 对象
const int* ptr = &readonly;

// 危险!修改真正的 const 对象
int* modifiable = const_cast<int*>(ptr);
*modifiable = 100;  // ❌ 未定义行为!

// 编译器可能已经优化,认为 readonly 永远是 42
std::cout << readonly << std::endl;  // 可能仍然输出 42

添加const限定符

cpp 复制代码
int i = 10;
const int* ci = const_cast<const int*>(&i);  // 添加const
  • 只能改变const/volatile限定符,不能改变类型

4、reinterpret_cast

reinterpret_cast 是最危险的类型转换,它重新解释对象的位模式,不进行任何类型检查。

语法:

reinterpret_cast<类型>(表达式)

使用示例:

类似于C语言的强转

cpp 复制代码
int value = 0x12345678;
int* int_ptr = &value;

// 将 int* 重新解释为 char*
char* char_ptr = reinterpret_cast<char*>(int_ptr);

// 查看内存中的字节表示
for (size_t i = 0; i < sizeof(int); ++i) {
    printf("Byte %zu: 0x%02X\n", i, static_cast<unsigned char>(char_ptr[i]));
}

实际应用

1、实现一个安全的向下转换函数

cpp 复制代码
template<typename Target, typename Source>
Target* safe_cast(Source* source) {
    static_assert(std::is_base_of_v<Source, Target> || 
                  std::is_base_of_v<Target, Source>,
                  "Types must be related by inheritance");
    
    if constexpr (std::is_base_of_v<Source, Target>) {
        // 向下转换,使用 dynamic_cast
        return dynamic_cast<Target*>(source);
    } else {
        // 向上转换,使用 static_cast
        return static_cast<Target*>(source);
    }
}

调用方法如下:

cpp 复制代码
// 可以这样调用:
int result1 = convert<int>(3.14);        // Target=int, Source=double(推导)
int result2 = convert<int, double>(3.14); // Target=int, Source=double(显式)

2、如何避免const_cast的危险

使用mutable关键字

cpp 复制代码
// 使用 mutable 关键字
class Cache {
private:
    mutable std::unordered_map<int, std::string> cache_;
public:
    const std::string& get(int key) const {
        // 可以修改 mutable 成员
        if (cache_.find(key) == cache_.end()) {
            cache_[key] = compute_value(key);
        }
        return cache_[key];
    }
};

// 使用函数重载
class Data {
public:
    const int& get() const { return value_; }
    int& get() { return value_; }
private:
    int value_;
};
相关推荐
weixin_4723394612 分钟前
高效处理大体积Excel文件的Java技术方案解析
java·开发语言·excel
枯萎穿心攻击1 小时前
响应式编程入门教程第二节:构建 ObservableProperty<T> — 封装 ReactiveProperty 的高级用法
开发语言·unity·c#·游戏引擎
Eiceblue2 小时前
【免费.NET方案】CSV到PDF与DataTable的快速转换
开发语言·pdf·c#·.net
tan180°3 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
m0_555762903 小时前
Matlab 频谱分析 (Spectral Analysis)
开发语言·matlab
程序视点3 小时前
Window 10文件拷贝总是卡很久?快来试试这款小工具,榨干硬盘速度!
windows
wuk9983 小时前
基于MATLAB编制的锂离子电池伪二维模型
linux·windows·github
浪裡遊4 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
彭祥.4 小时前
Jetson边缘计算主板:Ubuntu 环境配置 CUDA 与 cudNN 推理环境 + OpenCV 与 C++ 进行目标分类
c++·opencv·分类