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 关键字防止意外的转换。