C/C++类型转换

C/C++类型转换

1. C类型转换

C 语言中的类型转换主要分为两种:

  1. 隐式类型转换 (Implicit Conversion) - 由编译器自动完成。

  2. 显式类型转换 (Explicit Conversion) - 由程序员强制指定,也称为强制类型转换

1.2 隐式类型转换

编译器在编译时自动进行的转换,通常发生在不同数据类型的变量混合运算、赋值或函数调用时。

转换规则(通常遵循向上转换原则):

c 复制代码
int -> unsigned int -> long -> unsigned long -> long long -> unsigned long long -> float -> double -> long double

常见发生的场景

c 复制代码
int i = 10;
float f = 3.14;
double d = i + f; // i 被自动转换为 float 参与运算,结果再转换为 double 赋值给 d

1.3 强制类型转换

当用户需要明确地 将一种数据类型转换为另一种时使用。它使用强制类型转换运算符,语法是在要转换的目标类型前加上括号,然后放在值或表达式前面。

常见发生的场景

c 复制代码
int a = 5, b = 2;
float result;
result = a / b;       // 错误:结果是 2.0 (整数除法)
result = (float)a / b; // 正确:结果是 2.5。将 a 转为 float,b 也会被隐式转换为 float

将 void 指针转换为具体类型指针:

c 复制代码
void* generic_ptr;
int* int_ptr;
int x = 10;

generic_ptr = &x;          // 合法:任何指针都可以赋值给 void*
// *generic_ptr;           // 错误:不能对 void* 解引用
int_ptr = (int*)generic_ptr; // 必须强制转换回 int*
printf("%d\n", *int_ptr);    // 输出 10

注意: 在 C 语言中,从 void* 转换到其他指针类型必须 使用强制转换,而在 C++ 中 static_cast 可以完成这个工作,并且从 void* 到其他指针类型的隐式转换在 C++ 中是非法的。

2. C++中的类型转换

2.1 隐式类型转换

  • C++支持内置类型向自定义类型之间的转换,内置类型转换为自定义类型需要构造函数的支持。

  • C++支持自定义类型转换为内置类型,通过运算符重载 operator type () 的函数支持。

  • C++支持自定义类型向自定义类型之间的转换,需要对应类型的构造函数支持。

2.1.1 内置类型 -> 自定义类型(通过构造函数)
cpp 复制代码
#include <iostream>

class Meter {
private:
    double value;
public:
    // 关键:接收单一参数的构造函数
    // 定义了从 double -> Meter 的转换规则
    Meter(double val) : value(val) {
        std::cout << "构造函数被调用,将 double " << val << " 转换为 Meter" << std::endl;
    }
};

int main() {

    Meter m1 = 5.7;     // 隐式转换:double -> Meter

    return 0;
}

注意: 使用 explicit 关键字可以禁止隐式转换,只允许显式转换

2.1.2 自定义类型 -> 内置类型(通过转换函数)

通过在类中定义 operator type() 成员函数,可以实现从自定义类型到内置类型的转换。

cpp 复制代码
#include <iostream>

class Meter {
private:
    double value;
public:
    Meter(double val) : value(val) {}

    // 转换函数:Meter -> double
    operator double() const {
        std::cout << "转换函数被调用,将 Meter 转换为 double: " << value << std::endl;
        return value;
    }
};

int main() {

    Meter m(5.7);

    // 自定义类型 -> 内置类型
    double length_in_double = m;  // 隐式调用 operator double()
    std::cout << "转换为 double 的值: " << length_in_double << std::endl;

    return 0;
}

通过operator bool()函数,可以将自定义类型当作判断条件。

2.1.3 自定义类型 -> 自定义类型(通过构造函数或转换函数)
cpp 复制代码
#include <iostream>

class Kilometer; // 前向声明

class Meter {
private:
    double value;
public:
    Meter(double val) : value(val) {}
    double getValue() const { return value; }

    // 也可以定义到 Kilometer 的转换函数
    // operator Kilometer() const;
};

class Kilometer {
private:
    double value;
public:
    Kilometer(double val) : value(val) {}

    // 关键:定义接收 Meter 参数的构造函数
    // 提供了 Meter -> Kilometer 的转换路径
    Kilometer(const Meter& m) : value(m.getValue() / 1000.0) {
        std::cout << "Kilometer 构造函数:将 Meter 转换为 Kilometer" << std::endl;
    }

    void display() const {
        std::cout << value << " kilometers" << std::endl;
    }
};

// Meter 中转换函数的实现
// Meter::operator Kilometer() const {
//     return Kilometer(value / 1000.0);
// }

int main() {
    Meter m(1500.0); 

    // 自定义类型 -> 自定义类型
    Kilometer km = m; // 隐式转换:Meter -> Kilometer
    km.display();     // 输出: 1.5 kilometers

    return 0;
}

对于转换函数,也可以使用 explicit 关键字来禁止隐式转换:

cpp 复制代码
explicit operator double() const {
    return value;
}

Meter m(5.7);
double d1 = m;          // 错误:不能隐式转换
double d2 = static_cast<double>(m); // 正确:显式转换

2.2 显示类型转换

2.2.1 static_cast

静态转换:最常用的显式转换,用于在编译期进行有明确关联的安全转换。

场景:

  • 基本数据类型之间的转换

  • void 指针与具体类型指针之间的转换

  • 类层次结构中的上行转换(派生类→基类)

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

void* voidPtr = &i;
int* intPtr = static_cast<int*>(voidPtr); // void* -> int*
2.2.2 reinterpret_cast

reinterpret_cast 用于在两种不相关类型之间进行转换,其本质是对原始数据的底层位模式进行重新解释,也就是说转换后对原有内存的访问解释已经完全改变了。

场景:

  • 任意指针类型之间的转换

  • 指针和整数之间的转换

cpp 复制代码
int i = 0x12345678;
char* p1 = reinterpret_cast<char*>(a);
2.2.3 const_cast

const_cast⽤于const类型到⾮const类型的转换,去掉了const属性。

cpp 复制代码
void oldFunction(char* str) {
    cout << str << endl;
}

const char* message = "Hello";

// oldFunction(message); // 错误:不能将 const char* 转换为 char*
oldFunction(const_cast<char*>(message)); // 正确

// 危险示例!
const int ci = 10;
int* badPtr = const_cast<int*>(&ci);
*badPtr = 20; // 未定义行为!
2.2.4 dynamic_cast
  • dynamic_cast⽤于将基类的指针或者引⽤安全的转换成派⽣类的指针或者引⽤。如果基类的指针或者引⽤时指向派⽣类对象的,则转换回派⽣类指针或者引⽤时可以成功的,如果基类的指针指向基类对象,则转换失败返回nullptr,如果基类引⽤指向基类对象,则转换失败,抛出bad_cast异常。

  • 其次dynamic_cast要求基类必须是多态类型,也就是基类中必须有虚函数。因为dynamic_cast是运⾏时通过虚表中存储的type_info判断基类指针指向的是基类对象还是派⽣类对象。

场景:

  • 安全的下行转换(基类→派生类)

要求:

  • 基类必须有虚函数(多态类型)
cpp 复制代码
class Base { virtual void foo() {} };
class Derived : public Base {};

Base* basePtr1 = new Derived(); // 实际指向 Derived
Base* basePtr2 = new Base();    // 实际指向 Base

// 安全的下行转换
Derived* d1 = dynamic_cast<Derived*>(basePtr1); // 成功
Derived* d2 = dynamic_cast<Derived*>(basePtr2); // 失败,返回 nullptr

if (d2) {
    // 安全操作
} else {
    cout << "转换失败!" << endl;
}

// 引用转换(失败会抛出异常)
try {
    Derived& rd = dynamic_cast<Derived&>(*basePtr2);
} catch (const std::bad_cast& e) {
    cout << "引用转换失败" << endl;
}
  • dynamic_cast 的实现基础是 RTTI(运行时类型信息)。编译器会为每个包含虚函数的类生成额外的类型信息,这些信息在程序运行时可用。

  • 什么是RTTI?

    • RTTI 是 C++ 提供的一种机制,允许程序在运行时获取和操作对象的类型信息。它让程序能够动态地确定对象的实际类型,即使是通过基类指针或引用来操作对象。

    • typeid运算符:用于获取对象的类型信息,返回一个 std::type_info 对象的引用。

    • dynamic_cast:用于在继承层次中进行安全的类型转换,依赖于 RTTI 信息。

注意:C/C++都不是类型安全的语言。

相关推荐
枫fengw2 小时前
9.8 C++
开发语言·c++
JCBP_3 小时前
QT(3)
开发语言·汇编·c++·qt·算法
XFF不秃头3 小时前
力扣刷题笔记-三数之和
c++·笔记·算法·leetcode
Pafey3 小时前
VS2022 + Qt5.9 中文乱码/项目设置utf-8编码
c++·qt·中文乱码
minji...3 小时前
C++ STL之list的使用
开发语言·c++
青草地溪水旁4 小时前
23 种设计模式
开发语言·c++·设计模式
Want5954 小时前
C/C++圣诞树②
c语言·c++·算法
天天代码码天天5 小时前
基于海康SDK的C++实时视频流逐帧抓取存图小工具
c++·海康sdk·逐帧抓图
沐怡旸5 小时前
【底层机制】稀疏文件--是什么、为什么、好在哪、实现机制
c++·面试