- C 语言强转问题?
- 强制转换原因:编译时检查
- 检查失败:引用和指针的区别(空引用和异常)?
- RTTI:typeid,type_info 及其在虚表中的位置?dynamic_cast;
- static_cast:不提供什么检查?安全性如何?
- dynamic_cast?什么检查?什么是向下转换?返回值?如何实现,存储什么信息?继承都可以正确实现转换么?转成 void*?菱形继承的向上转换?
- const_cast:常量转换为非常(引用/值/指针)?
- reinterpret_cast:问题?
为什么 C++要进行特别的类型转换,因为方便编译器增加编译时检查,告诉我们什么转换是无效的,使得代码更可靠。
RTTI(Runtime type identification)运行时类型检查¶
- type_info:结果储存类的信息。运行时使用类型信息就叫做“run-time type identification”, 即 RTTI。
- typeid():返回一个指出对象的类型的值;
如果有虚函数;
Derived *p = new Derived();//输出derived Base *pp = new Derived();//输出的还是derived(如果有虚函数) Base *ppp = dynamic_cast<Base>(p);//打印出来的就是Base std::string aa = typeid(*pp).name();
- 虚函数去除掉?
展示的就是 Base 函数(没有虚映射了)
Base *pp = new Derived();//输出的是Base std::string aa= typrid(*pp).name();
- 因此,可以根据 typeid 来判断是否是虚函数
if(typeid(pp) == typeid(Derived)) { }
- 向下转型:(安全判定,返回 NULL 指针)
Derived *pppp = pp;//会报错,显示有问题 Derived *pppp = dynamic_cast<Derived*>(pp);//这样就会返回一个强转失败的NULL指针。,比较安全
1.C 语言风格的隐式转换和强制转换¶
转换过程
int
强制转换:
int a = (int)value;
2.static_cast<>静态类型转换¶
任何编写程序时能够明确的类型转换都可以使用 static_cast(static_cast 不能转换掉底层 const,volatile 和__unaligned 属性)。由于不提供运行时的检查,所以叫 static_cast,因此,需要在编写程序时确认转换的安全性。 安全转换方式: - 用于基本数据类型之间的转换,例如把 int 转 char,int 转 enum 等,需要编写程序时来确认安全性;
double s = static_cast<int>(value) + 5.3;
不安全转换方式,推荐 dynamic_cast<> 1.向下转换, 2.把 void 指针转换成目标类型的指针(这是极其不安全的);
void *p = &d;
double *dp = static_cast<double*>(p);
3.dynamic_cast<>¶
向下转换¶
- 用于沿继承结构的强制转换,子类转基类指针(可以不用强转,能隐式转换),基类转子类指针。
- 进行运行时检查,如果转换不成功,可能返回 NULL,其更像一个函数(值类型返回为 0)
- 因此常用于验证,如果是实体,可以转换为玩家。如果其是个实体指针,玩家不知道它是敌人还是实体类,那么转换到玩家就会报错。
- 如果使用 C 风格(Player)告知编译器,则不会编译出错。然而,其本身可能是 Enemy,是无法转换到 Player 的,此时程序可能会崩溃*。而使用
dynamic<cast>
便可以在转换不成功时返回 NULL。 - 如何做到的?:RTTI,使用 runtime type information 存储信息,会增加内存
class Base
{
}
class Derived : Base
{
}
class AnotherClass : Base
{
}
int main()
{
Derived* derived = new Derived();
Base* base = derived;//不会编译错误
AnotherClass* ac = dynamic_cast<AnotherClass*>(base);
//返回NULL,因为无法正常转换
Derived* de = dynamic_cast<AnotherClass*>(base);
//成功
}
void* 转换¶
一些情况下,我们需要将指针转换为 void,然后再合适的时候重新将 void 转换为目标类型指针。
class A { virtual void f(){} };
int main()
{
A *pA = new A;
void *pV = dynamic_cast<void *>(pA);
}
菱形继承的上行转换¶
直觉来说是可以的,因为从子类向父类转化,无论如何都是安全的。但实际上,如果尝试这样的转换,只能得到一个空指针。因为 B 和 C 都继承了 A,并且都实现了虚函数 f(),导致在进行转换时,无法选择一条转换路径。
一种可行的方法是,自行指定一条转换路径:
class A { virtual void f() {}; };
class B :public A { void f() {}; };
class C :public A { void f() {}; };
class D :public B, public C { void f() {}; };
void main()
{
D *pD = new D;
B *pB = dynamic_cast<B *>(pD);
A *pA = dynamic_cast<A *>(pB);
}
4.const_cast<> 去掉常量的 const¶
const_cast¶
const_cast 用于移除类型的const、volatile 和__unaligned 属性。
常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。
const char *pc;
char *p = const_cast<char*>(pc);
5. reinterpret_cast 非常激进的指针类型转换¶
非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用。
int *ip;
char *pc = reinterpret_cast<char*>(ip);