目录
static_cast:static_cast(expression)
C++ 提供以下几类转换
static_cast:static_cast<type-id>(expression)
tatic_cast 主要用于以下几种情况:
- 用于显式地将一个表达式转换为另一种类型,包括基本类型、指针类型和引用类型。
- 用于将基类指针或引用转换为派生类指针或引用(向下转型)。
- 用于将 void* 指针转换为任意类型的指针。
- 用于将任意类型的指针转换为 void* 指针。
需要注意的是tatic_cast 不会在运行时检查该对象的运行时类型,安全的向下转换可以用dynamic_cast
执行
示例
cpp
#include <vector>
#include <iostream>
struct B
{
int m = 0;
void hello() const
{
std::cout << "Hello world,这里是 B!\n";
}
};
struct D : B
{
void hello() const
{
std::cout << "Hello world,这里是 D!\n";
}
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
// 1: 静态向下转型
D d;
B& br = d; // 通过隐式转换向上转型
br.hello();
D& another_d = static_cast<D&>(br); // 向下转型
another_d.hello();
// 2: 左值到右值
std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
std::cout << "移动后,v.size() = " << v.size() << '\n';
// 3: 初始化转换
int n = static_cast<int>(3.14);
std::cout << "n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "v.size() = " << v.size() << '\n';
// 4: 弃值表达式
static_cast<void>(v2.size());
// 5. 隐式转换的逆转换
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "*ni = " << *ni << '\n';
// 6. 数组到指针后随向上转型
D a[10];
B* dp = static_cast<B*>(a);
// 7. 有作用域枚举到 int 或 float
E e = E::ONE;
int one = static_cast<int>(e);
std::cout << one << '\n';
// 8. int 到枚举,枚举到另一枚举
E e2 = static_cast<E>(one);
EU eu = static_cast<EU>(e2);
// 9. 指向成员指针向上转型
int D::*pm = &D::m;
std::cout << br.*static_cast<int B::*>(pm) << '\n';
// 10. void* 到任何类型
void* voidp = &e;
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}
输出
Hello world,这里是 B! Hello world,这里是 D! 移动后,v.size() = 0 n = 3 v.size() = 10 *ni = 3 1 0
const_cast
类型转换运算符,可以将 const 和 volatile 限定符从指针或引用类型中去除。它可以用于将常量对象变成非常量对象,从而允许对其进行修改,函数指针和成员函数指针无法用于 const_cast
示例
cpp
#include <iostream>
struct type
{
int i;
type(): i(3) {}
void f(int v) const {
// this->i = v; // 编译错误:this 是指向 const 的指针
const_cast<type*>(this)->i = v; // 只要该对象不是 const 就 OK
}
};
int main()
{
int i = 3; // 不声明 i 为 const
const int& rci = i;
const_cast<int&>(rci) = 4; // OK:修改 i
std::cout << "i = " << i << '\n';
type t; // 如果这是 const type t,那么 t.f(4) 会是未定义行为
t.f(4);
std::cout << "type::i = " << t.i << '\n';
const int j = 3; // 声明 j 为 const
[[maybe_unused]] //可能是有意不使用,编译器不会发出警告
int* pj = const_cast<int*>(&j);
// *pj = 4; // 未定义行为
[[maybe_unused]]
void (type::* pmf)(int) const = &type::f; // 指向成员函数的指针
// const_cast<void(type::*)(int)>(pmf); // 编译错误:const_cast 不能用于成员函数指针
}
dynamic_cast
沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用,失败会返回空指针
cpp
#include <iostream>
struct V
{
virtual void f() {} // 必须为多态以使用运行时检查的 dynamic_cast
};
struct A : virtual V {};
struct B : virtual V
{
B(V* v, A* a)
{
// 构造中转型(见后述 D 的构造函数中的调用)
dynamic_cast<B*>(v); // 良好定义:v 有类型 V*,V 是 B 的基类,产生 B*
dynamic_cast<B*>(a); // 未定义行为:a 有类型 A*,A 不是 B 的基类
}
};
struct D : A, B
{
D() : B(static_cast<A*>(this), this) {}
};
struct Base
{
virtual ~Base() {}
};
struct Derived: Base
{
virtual void name() {}
};
int main()
{
D d; // 最终派生对象
A& a = d; // 向上转型,可以用 dynamic_cast,但不是必须的
[[maybe_unused]]
D& new_d = dynamic_cast<D&>(a); // 向下转型
[[maybe_unused]]
B& new_b = dynamic_cast<B&>(a); // 侧向转型
Base* b1 = new Base;
if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
{
std::cout << "成功从 b1 向下转型到 d\n";
d->name(); // 可以安全调用
}
Base* b2 = new Derived;
if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
{
std::cout << "成功从 b2 向下转型到 d\n";
d->name(); // 可以安全调用
}
delete b1;
delete b2;
}
reinterpret_cast
重新解释底层位模式在类型间任意转换,实际上转换比static_cast更不安全,
static_cast就是利用C++类型之间的继承关系图和聚合关系图(编译器必须知道),根据一个子对象地址计算另一个子对象的地址。
reinterpret_cast不关心继承关系,直接把数据类型A的地址解释成另一个数据类型B的地址
示例
cpp
#include <cstdint>
#include <cassert>
#include <iostream>
int f() { return 42; }
int main()
{
int i = 7;
// 指针到整数并转回
std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // 不能误用 static_cast
std::cout << "&i 的值是 " << std::showbase << std::hex << v1 << '\n';
int* p1 = reinterpret_cast<int*>(v1);
assert(p1 == &i);
// 到另一函数指针并转回
void(*fp1)() = reinterpret_cast<void(*)()>(f);
// fp1(); 未定义行为
int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
std::cout << std::dec << fp2() << '\n'; // 安全
// 通过指针的类型别名化
char* p2 = reinterpret_cast<char*>(&i);
std::cout << (p2[0] == '\x7' ? "本系统是小端的\n"
: "本系统是大端的\n");
// 通过引用的类型别名化
reinterpret_cast<unsigned int&>(i) = 42;
std::cout << i << '\n';
[[maybe_unused]] const int &const_iref = i;
// int &iref = reinterpret_cast<int&>(const_iref); // 编译错误------不能去除 const
// 必须用 const_cast 代替:int &iref = const_cast<int&>(const_iref);
}