C++四种类型转换
1.static_cast 基本的类型转换(编译期)
用于进行比较“自然”和低风险的转换,如整型和浮点型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符 T(如 T 是 int、int* 或其他类型名),则 static_cast 也能用来进行对象到 T 类型的转换。不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换
2.const_cast 修改引用或指针的const限定
非必要不要使用,对一个常量进行修改是一个不明智的行为
std::add_const或std::remove_const只能用来修饰类型,不能修改变量的const限定
3.reinterpret_cast 指针类型转换
用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。
4.dynamic_cast(运行期,会影响性能)
reinterpret_cast 可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。即dynamic_cast是用来向下转型(父类转换为子类),向上转型本来就是安全的,没必要使用dynamic_cast。
dynamic_cast 是通过“运行时类型检查”来保证安全性的。dynamic_cast 不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性,只好用 reinterpret_cast 来完成。
对于不安全的指针转换,转换结果返回nullptr指针。引用则直接抛出异常,可以通过捕获异常来进行处理
dynamic_cast使用方法
#include <iostream>
class A
{
public:
void funA() { std::cout << "A\n"; }
};
class B
{
public:
void funB() { std::cout << "B\n"; }
};
class C
{
public:
virtual void funC() { std::cout << "C\n"; }
};
class Test :
public A,
public B,
public C
{
public:
void funT() { std::cout << "Test\n"; }
void funA() { std::cout << "TA\n"; };
virtual void funB() { std::cout << "TB\n"; };
//void funB() override; //不能用override 因为基类B没有将函数funB声明为virtual
void funC() { std::cout << "TC\n"; }
//void funC() override { std::cout<<"TC\n"; }
//virtual void funC() { std::cout << "TC\n"; }
};
int main()
{
Test* t = new Test();
t->funA(); //子类
t->funB(); //子类
t->funC(); //子类
A* a = new Test();
a->funA(); //父类
B* b = new Test();
b->funB(); //父类
C* c = new Test();
c->funC(); //子类,因为父类函数是virtual,子类会重写该函数
C* c1 = dynamic_cast<C*>(new Test()); //没必要使用dynamic_cast
c1->funC(); //子类
//Test* t1 = new C(); //语法错误,编译器报错
//Test* t1 = dynamic_cast<Test*>(new A()); //语法错误,因为类A不是多态基类,即没有virtual
Test* t1 = dynamic_cast<Test*>(new C()); //正确,但此处的t1为nullptr,如果转换引用此处会抛出异常,因为没有空引用
t1->funT(); // 正确,原因可以查看this指针博客,如果funT调用了成员变量就会报错
//< dynamic_cast的正确用法:
C* cc = new Test();
Test* t2 = dynamic_cast<Test*>(cc); //此处的t2不为nullptr
t2->funT(); // 正确,无论是否调用成员变量
return 0;
}
C++四种类型转换
#include <iostream>
class A {};
class B :public A {};
void testFunc1() {}
int testFunc2(int) { return 1; }
int main()
{
// static_cast
{
int n = 0, * pn = &n;
//auto r1 = static_cast<float*>(pn); // error
auto r2 = static_cast<float>(n); // ok r2类型为float
auto r3 = static_cast<int*>(nullptr); // ok r3类型为int*,且r3 = nullptr
//auto r4 = static_cast<float*>(pn); // error 不能转换空指针以外的指针类型
A* pA = new A();
B* pB = new B();
auto r5 = static_cast<B*>(pA); // ok 父类转子类,不安全,不建议这样做
auto r6 = static_cast<A*>(pB); // ok 子类转父类,安全
auto r7 = static_cast<void*>(nullptr); // ok
}
// const_cast
{
const int cn = 0, * pcn = &cn, & rcn = cn;
int n = 0, * pn = &n, & rn = n;
//auto r1 = const_cast<int>(cn); // error <>中的类型只能是指针或引用
auto r2 = const_cast<int*>(pcn); // ok 可以是指针,引用
auto r3 = const_cast<int&>(rcn); // ok
auto r4 = const_cast<const int&>(n); // ok r4类型为 int&
}
std::cout << "---------------\n";
{
const int a = 10;
const int* p = &a;
int* q = const_cast<int*>(p);
*q = 20; //fine 这句代码是一个未定义行为(UB)
// 指针p和指针q都是指向a变量的,指向地址相同,但是值却不一样
// 常量折叠 可以看00_10_TEST4
std::cout << "a=" << a << " " << "&a = " << &a << std::endl;
std::cout << "*p=" << *p << " " << "p = " << p << std::endl;
std::cout << "*q=" << *q << " " << "q = " << q << std::endl;
}
std::cout << "---------------\n";
{
int c = 11;
const int a = c;
const int* p = &a;
int* q = const_cast<int*>(p);
*q = 20; //fine
std::cout << "a=" << a << " " << "&a = " << &a << std::endl;
std::cout << "*p=" << *p << " " << "p = " << p << std::endl;
std::cout << "*q=" << *q << " " << "q = " << q << std::endl;
}
std::cout << "---------------\n";
{
const int c = 11;
const int a = c;
const int* p = &a;
int* q = const_cast<int*>(p);
*q = 20; //fine
std::cout << "a=" << a << " " << "&a = " << &a << std::endl;
std::cout << "*p=" << *p << " " << "p = " << p << std::endl;
std::cout << "*q=" << *q << " " << "q = " << q << std::endl;
}
// reinterpret_cast
{
int n = 666, * p = &n, & r = n;
auto r1 = reinterpret_cast<float*>(p); // ok r1的类型为float*
auto ret1 = *r1; // ret1是一个非法值
auto r2 = reinterpret_cast<float&>(r); // ok r2的类型为float不是float&
//auto r3 = reinterpret_cast<float>(n); // error
//auto r4 = reinterpret_cast<float>(r); // error
auto r5 = reinterpret_cast<float*>(r); // ok
// auto ret2 = *r5; // error 运行时报错
auto r6 = reinterpret_cast<float&>(n); // ok
// 函数指针转换
typedef void (*FUNC)();
auto r7 = reinterpret_cast<FUNC>(testFunc1);
auto r8 = reinterpret_cast<FUNC>(testFunc2);
r7(); // ok
r8(); // ok
//auto r9 = reinterpret_cast<int*>(nullptr); // error,空指针转换应该用static_cast
float* pf = nullptr;
auto r10 = reinterpret_cast<int*>(pf); //ok
// 一定要谨慎使用reinterpret_cast
// 例如{auto r5 = reinterpret_cast<float*>(r);}不应该被转换,但是编译器不会报错
// {auto r8 = reinterpret_cast<FUNC>(testFunc2);}不应该被转换,函数签名不一样
}
// dynamic_cast
{
A* pA = new A();
B* pB = new B();
auto r1 = dynamic_cast<A*>(pB); // 子类向父类转换,安全
//dynamic_cast<B*>(pA); // 因为类A中没有虚函数,所以编译失败
}
}