C++ 中的类型转换是将一种数据类型转换为另一种数据类型的过程。理解类型转换对于编写安全、高效的代码至关重要。

隐式类型转换

隐式类型转换(也称为自动类型转换)是编译器自动执行的类型转换,无需程序员显式指定。

基本类型的隐式转换

算术类型转换

int i = 42;
double d = i;        // int 转 double(安全)
float f = 3.14;
int j = f;           // float 转 int(可能丢失精度)
 
char c = 'A';
int ascii = c;       // char 转 int(提升)
 
bool b = 42;         // int 转 bool(非零为 true)
int n = true;        // bool 转 int(true 为 1)

算术运算中的类型提升

short s1 = 10;
short s2 = 20;
int result = s1 + s2;  // short 提升为 int
 
int i = 10;
double d = 3.14;
auto result2 = i + d;  // int 提升为 double,结果是 double

指针类型的隐式转换

// 派生类指针转基类指针
class Base {};
class Derived : public Base {};
 
Derived* d = new Derived();
Base* b = d;  // 隐式向上转型(安全)
 
// 数组到指针的转换
int arr[10];
int* p = arr;  // 数组名隐式转换为指向首元素的指针
 
// nullptr 转换
int* ptr = nullptr;  // nullptr 可以隐式转换为任何指针类型
 
// void* 转换
void* vp = &i;  // 任何指针都可以隐式转换为 void*
// int* ip = vp;  // 错误!void* 到其他指针需要显式转换

用户定义的隐式转换

转换构造函数

class Complex {
    double real, imag;
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // 单参数构造函数允许隐式转换
    Complex(double r) : real(r), imag(0) {}
};
 
Complex c1 = 3.14;  // 隐式调用 Complex(3.14)
Complex c2 = Complex(2.0, 3.0);
 
// 使用 explicit 禁止隐式转换
class Fraction {
    int num, den;
public:
    explicit Fraction(int n, int d = 1) : num(n), den(d) {}
};
 
// Fraction f = 5;  // 错误!构造函数是 explicit 的
Fraction f(5);      // 正确,显式调用

类型转换运算符

class Number {
    int value;
public:
    Number(int v) : value(v) {}
    
    // 类型转换运算符
    operator int() const { return value; }
    operator double() const { return static_cast<double>(value); }
    
    // explicit 类型转换运算符(C++11)
    explicit operator bool() const { return value != 0; }
};
 
Number n(42);
int i = n;        // 隐式转换为 int
double d = n;     // 隐式转换为 double
// bool b = n;    // 错误!bool 转换是 explicit 的
bool b = static_cast<bool>(n);  // 正确

显式类型转换

显式类型转换需要程序员明确指定转换操作。

C 风格的类型转换

double d = 3.14;
int i = (int)d;         // C 风格转换
int j = int(d);         // 函数风格转换
 
// 危险的转换
const char* str = "Hello";
char* mutable_str = (char*)str;  // 去除 const(危险!)

C++ 风格的类型转换

C++ 提供了四种类型转换运算符,每种都有特定的用途:

static_cast

用于编译时已知的类型转换,是最常用的转换方式:

// 基本类型转换
double d = 3.14159;
int i = static_cast<int>(d);  // 明确的转换
 
// 指针转换(有继承关系)
class Base { virtual void func() {} };
class Derived : public Base {};
 
Base* base = new Derived();
Derived* derived = static_cast<Derived*>(base);  // 向下转型(不安全)
 
// void* 转换
void* vp = &i;
int* ip = static_cast<int*>(vp);
 
// 枚举类型转换
enum Color { RED, GREEN, BLUE };
int colorValue = static_cast<int>(RED);

dynamic_cast

用于运行时的安全向下转型,只能用于多态类型(有虚函数的类):

class Base {
public:
    virtual ~Base() {}
};
 
class Derived : public Base {
public:
    void derivedMethod() {}
};
 
class AnotherDerived : public Base {};
 
Base* base = new Derived();
 
// 安全的向下转型
Derived* d1 = dynamic_cast<Derived*>(base);
if (d1 != nullptr) {
    d1->derivedMethod();  // 转换成功
}
 
// 失败的转换
AnotherDerived* ad = dynamic_cast<AnotherDerived*>(base);
if (ad == nullptr) {
    std::cout << "转换失败" << std::endl;
}
 
// 引用类型的 dynamic_cast
try {
    Derived& dref = dynamic_cast<Derived&>(*base);
} catch (std::bad_cast& e) {
    std::cout << "转换失败: " << e.what() << std::endl;
}

const_cast

用于添加或移除 const/volatile 修饰符:

const int ci = 10;
int* pi = const_cast<int*>(&ci);  // 移除 const(危险!)
// *pi = 20;  // 未定义行为!
 
// 合法用例:调用非 const 版本的函数
class Widget {
    int value;
public:
    const int& getValue() const { return value; }
    
    int& getValue() {
        // 调用 const 版本并移除 const
        return const_cast<int&>(
            static_cast<const Widget*>(this)->getValue()
        );
    }
};
 
// 处理遗留 API
void legacyAPI(char* str) { /* ... */ }
 
void modernFunction(const std::string& s) {
    // 需要调用只接受 char* 的旧 API
    legacyAPI(const_cast<char*>(s.c_str()));
}

reinterpret_cast

用于低级别的重新解释,将一种类型的位模式重新解释为另一种类型:

// 指针类型之间的转换
int i = 42;
char* cp = reinterpret_cast<char*>(&i);
 
// 整数与指针之间的转换
std::uintptr_t addr = reinterpret_cast<std::uintptr_t>(&i);
int* ip = reinterpret_cast<int*>(addr);
 
// 函数指针转换
typedef void (*FuncPtr)();
typedef int (*IntFuncPtr)();
 
FuncPtr fp = reinterpret_cast<FuncPtr>(IntFuncPtr());
 
// 查看对象的字节表示
struct Data {
    int a;
    double b;
};
 
Data d{42, 3.14};
unsigned char* bytes = reinterpret_cast<unsigned char*>(&d);
for (size_t i = 0; i < sizeof(Data); ++i) {
    std::cout << std::hex << static_cast<int>(bytes[i]) << " ";
}

类型转换的陷阱和最佳实践

避免的情况

// 窄化转换
double d = 3.14159;
int i = d;  // 警告:可能丢失数据
 
// 符号问题
unsigned int ui = 10;
int si = -1;
if (ui > si) {  // si 被转换为 unsigned,结果可能出乎意料
    // -1 转换为 unsigned 是一个很大的数
}
 
// 指针转换错误
class A { int x; };
class B { double y; };
A a;
B* b = reinterpret_cast<B*>(&a);  // 危险!类型不兼容

最佳实践

// 1. 优先使用 C++ 风格的转换
int i = static_cast<int>(3.14);  // 明确意图
 
// 2. 使用 explicit 防止意外的隐式转换
class SafeString {
    std::string str;
public:
    explicit SafeString(const char* s) : str(s) {}
};
 
// 3. 使用模板进行安全转换
template<typename To, typename From>
To safe_cast(From from) {
    static_assert(sizeof(To) >= sizeof(From), 
                  "Target type is smaller than source type");
    return static_cast<To>(from);
}
 
// 4. 使用 uniform initialization 防止窄化
int x{3.14};  // 编译错误:窄化转换
 
// 5. 自定义转换函数
template<typename T>
std::optional<T> try_convert(const std::string& str) {
    std::stringstream ss(str);
    T value;
    if (ss >> value && ss.eof()) {
        return value;
    }
    return std::nullopt;
}

现代 C++ 的类型转换

// std::bit_cast (C++20)
float f = 3.14f;
auto i = std::bit_cast<std::uint32_t>(f);  // 类型双关
 
// std::to_underlying (C++23)
enum class Color { RED = 1, GREEN = 2, BLUE = 3 };
auto value = std::to_underlying(Color::RED);  // 获取底层值
 
// 使用 concepts 约束转换 (C++20)
template<typename To, typename From>
    requires std::convertible_to<From, To>
To convert(const From& from) {
    return static_cast<To>(from);
}

类型转换是 C++ 中的重要特性,合理使用可以提高代码的灵活性,但不当使用可能导致难以发现的错误。建议优先使用 C++ 风格的转换运算符,它们更加安全且意图明确。在设计类时,谨慎使用隐式转换,必要时使用 explicit 关键字防止意外的转换。