摘要
使用static_cast可以将一个数据类型转换为另一个数据类型,就像魔法一样。例如,将浮点数转换为整数,让我们感受到了C++的神奇之处。
正文
1. static_cast
1.1 static_cast英语的语法
static_cast< new_type >(expression)
备注名称:new_type为总体目标基本数据类型,expression为原始记录种类自变量或是关系式。
C设计风格书写:
double scores = 96.5;
int n = (int)scores;
C 新设计风格的书写为:
double scores = 96.5;
int n = static_cast<int>(scores);
1.2 为何要有static_cast等
隐式数据转换是安全性的,显式数据转换是有风险性的,C语言往往提升强制性数据转换的英语的语法,便是为了更好地注重风险性,让程序猿意识到自身在干什么。
可是,这类注重风险性的方法或是较为粗放型,粒度分布较为大,它并沒有说明存有哪些风险性,风险性水平怎样。
为了更好地使潜在性风险性更为优化,使难题追朔更为便捷,使书写格式更为标准,C 对数据转换开展了归类,并增加了四个关键词来给予适用,他们分别是:
关键词 | 表明 |
---|---|
static_cast | 用以良好变换,一般不容易造成出现意外产生,风险性很低。 |
const_cast | 用以 const 和非 const、volatile 和非 volatile 中间的变换。 |
reinterpret_cast | 高宽比风险的变换,这类变换只是是对二进制位的再次表述,不容易依靠现有的转换方法对数据信息开展调节,可是能够 完成最灵便的 C 数据转换。 |
dynamic_cast | 依靠 RTTI,用以种类安全性的往下转型发展(Downcasting)。 |
1.2 static_cast的功效
static_cast等同于传统式的C语言里的强制转换,该操作符把expression变换为new_type种类,用于逼迫隐式变换如non-const目标变为const目标,编译程序时查验,用以非多态的变换,能够 变换表针以及他,但沒有运作时种类查验来确保变换的安全系数。它关键有以下几类使用方法:
风险性较低的使用方法:
- 原来的全自动数据转换,比如 short 转 int、int 转 double、const 转非 const、往上转型发展等;
- void 表针和实际种类表针中间的变换,比如
void *
转int *
、char *
转void *
等; - 有变换构造方法或是数据转换涵数的类与其他种类中间的变换,比如 double 转 Complex(启用变换构造方法)、Complex 转 double(启用数据转换涵数)。
必须 留意的是,static_cast 不可以用以不相干种类中间的变换,由于这种变换全是有风险性的,比如:
- 2个实际种类表针中间的变换,比如
int *
转double *
、Student *
转int *
等。不一样种类的数据储存文件格式不一样,长短也不一样,用 A 种类的表针偏向 B 种类的数据信息后,会依照 A 种类的方法来解决数据信息:如果是载入实际操作,很有可能会获得一堆没有意义的值;如果是载入实际操作,很有可能会使 B 种类的数据信息遭受毁坏,当再度以 B 种类的方法获取数据的时候会获得一堆没有意义的值。 - int 和表针中间的变换。将一个实际的详细地址取值给指针变量是十分风险的,由于该详细地址上的运行内存很有可能沒有分派,也很有可能沒有读写能力管理权限,正好是可用内存反倒是偶然性。
1.3 static_cast使用方法
#include <iostream>
#include <cstdlib>
using namespace std;
class Complex{
public:
Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:
operator double() const { return m_real; } //数据转换涵数
private:
double m_real;
double m_imag;
};
int main(){
//下边是恰当的使用方法
int m = 100;
Complex c(12.5, 23.8);
long n = static_cast<long>(m); //宽变换,沒有信息内容遗失
char ch = static_cast<char>(m); //窄变换,很有可能会遗失信息内容
int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) ); //将void表针变换为实际种类表针
void *p2 = static_cast<void*>(p1); //将实际种类表针,变换为void表针
double real= static_cast<double>(c); //启用数据转换涵数
//下边的使用方法是不正确的
float *p3 = static_cast<float*>(p1); //不可以在2个实际种类的表针中间开展变换
p3 = static_cast<float*>(0X2DF9); //不可以将整数金额变换为表针种类
return 0;
}
2. dynamic_cast
2.1 dynamic_cast 英语的语法
dynamic_cast <newType> (expression)
newType 和 expression 务必与此同时是表针种类或是引用类型。也就是说,dynamic_cast 只有变换表针种类和引用类型,其他种类(int、double、二维数组、类、建筑结构等)都不好。
针对表针,假如变换不成功将回到 NULL;针对引入,假如变换不成功将抛出去std::bad_cast
出现异常。
2.2 dynamic_cast 使用方法
dynamic_cast 用以在类的承继层级中间开展数据转换,它既容许往上转型发展(Upcasting),也容许往下转型发展(Downcasting)。往上转型发展是没有理由的,不容易开展一切检验,因此都能取得成功;往下转型发展的前提条件务必是安全性的,要依靠 RTTI 开展检验,全部仅有一部分能取得成功。
dynamic_cast 与 static_cast 是相对性的,dynamic_cast 是“动态性变换”的含意,static_cast 是“静态数据变换”的含意。dynamic_cast 会在程序执行期内依靠 RTTI 开展数据转换,这就规定基类务必包括虚函数;static_cast 在编译程序期内进行数据转换,可以更为立即地出现未知错误。
2.3 dynamic_cast 案例
2.3.1 往上转型发展(Upcasting)
往上转型发展时,只需待变换的2个种类中间存有承继关联,而且基类包括了虚函数(这种信息内容在编译程序期内就能明确),就一定能变换取得成功。由于往上转型发展自始至终是安全性的,因此 dynamic_cast 不容易开展一切运作期内的查验,这个时候的 dynamic_cast 和 static_cast 就没什么差别了。
「往上转型发展时不实行运作期检验」尽管提升 了高效率,但也留有了安全风险,请看下面的代码:
#include <iostream>
#include <iomanip>
using namespace std;
class Base{
public:
Base(int a = 0): m_a(a){ }
int get_a() const{ return m_a; }
virtual void func() const { }
protected:
int m_a;
};
class Derived: public Base{
public:
Derived(int a = 0, int b = 0): Base(a), m_b(b){ }
int get_b() const { return m_b; }
private:
int m_b;
};
int main(){
//状况①
Derived *pd1 = new Derived(35, 78);
Base *pb1 = dynamic_cast<Derived*>(pd1);
cout<<"pd1 = "<<pd1<<", pb1 = "<<pb1<<endl;
cout<<pb1->get_a()<<endl;
pb1->func();
//状况②
int n = 100;
Derived *pd2 = reinterpret_cast<Derived*>(&n);
Base *pb2 = dynamic_cast<Base*>(pd2);
cout<<"pd2 = "<<pd2<<", pb2 = "<<pb2<<endl;
cout<<pb2->get_a()<<endl; //輸出一个废弃物值
pb2->func(); //内存错误
return 0;
}
运作結果以下
能够 见到pd1与pb1的详细地址同样,且pb1能够 一切正常启用Base类的方式
针对状况②
pd 2偏向的是整型变量 n,并沒有偏向一个 Derived 类的目标,在应用 dynamic_cast 开展数据转换时都没有查验这一点(由于往上转型发展自始至终是安全性的,因此 dynamic_cast 不容易开展一切运作期内的查验)
只是将 pd 的值立即赋给了 pb(这儿并不一定调节偏移),最后造成 pb 也偏向了 n。由于 pb 偏向的并不是一个目标,因此get_a()
无法得到 m_a 的值(事实上获得的是一个废弃物值),pb2->func()
也无法得到 func() 涵数的恰当详细地址。
运作結果以下
简易而言便是往上转型发展不是查验的,因此大伙儿得了解自身在干什么,不可以随便的变换
2.3.2 往下转型发展(Downcasting)
往下转型发展是有风险性的,dynamic_cast 会依靠 RTTI 信息内容开展检验,明确安全性的才可以变换取得成功,不然就变换不成功。
下边看一个事例
#include <iostream>
using namespace std;
class A{
public:
virtual void func() const { cout<<"Class A"<<endl; }
private:
int m_a;
};
class B: public A{
public:
virtual void func() const { cout<<"Class B"<<endl; }
private:
int m_b;
};
class C: public B{
public:
virtual void func() const { cout<<"Class C"<<endl; }
private:
int m_c;
};
class D: public C{
public:
virtual void func() const { cout<<"Class D"<<endl; }
private:
int m_d;
};
int main(){
A *pa = new A();
B *pb;
C *pc;
//状况①
pb = dynamic_cast<B*>(pa); //往下转型发展不成功
if(pb == NULL){
cout<<"Downcasting failed: A* to B*"<<endl;
}else{
cout<<"Downcasting successfully: A* to B*"<<endl;
pb -> func();
}
pc = dynamic_cast<C*>(pa); //往下转型发展不成功
if(pc == NULL){
cout<<"Downcasting failed: A* to C*"<<endl;
}else{
cout<<"Downcasting successfully: A* to C*"<<endl;
pc -> func();
}
cout<<"-------------------------"<<endl;
//状况②
pa = new D(); //往上转型发展全是容许的
pb = dynamic_cast<B*>(pa); //往下转型发展取得成功
if(pb == NULL){
cout<<"Downcasting failed: A* to B*"<<endl;
}else{
cout<<"Downcasting successfully: A* to B*"<<endl;
pb -> func();
}
pc = dynamic_cast<C*>(pa); //往下转型发展取得成功
if(pc == NULL){
cout<<"Downcasting failed: A* to C*"<<endl;
}else{
cout<<"Downcasting successfully: A* to C*"<<endl;
pc -> func();
}
return 0;
}
运作結果
能够 见到,前2次变换不成功,可是后2次变换取得成功
这一段编码中类的继承顺序为:A –> B –> C –> D。pa 是A*
种类的表针,当 pa 偏向 A 种类的目标时,往下转型发展不成功,pa 不可以变换为B*
或C*
种类。当 pa 偏向 D 种类的目标时,往下转型发展取得成功,pa 能够 变换为B*
或C*
种类。一样全是往下转型发展,为何 pa 偏向的目标不一样,变换的結果就截然不同呢?
由于每一个类都是会在运行内存中储存一份类型信息,c语言编译器会将存有承继关联的类的类型信息应用表针“联接”起來,进而产生一个承继链(Inheritance Chain),也就是如下图所显示的模样:
当应用 dynamic_cast 对表针开展数据转换时,程序流程会先寻找该表针偏向的目标,再依据目标寻找当今类(表针偏向的目标隶属的类)的类型信息,并此后连接点逐渐顺着承继链往上解析xml,假如找到要转换的总体目标种类,那麼表明这类变换是安全性的,就可以变换取得成功,要是没有寻找要变换的总体目标种类,那麼表明这类变换存有很大的风险性,就不可以变换。
因此在第二种方法中,pa事实上是偏向的D,因此程序流程沿着D逐渐往上找,找到B和C,因此评定是安全性的,因此变换取得成功
总起来说,dynamic_cast 会在程序执行全过程中解析xml承继链,假如中途碰到了要变换的总体目标种类,那麼就可以变换取得成功,假如直至承继链的端点(最高层的基类)都还没碰到要变换的总体目标种类,那麼就变换不成功。针对同一个表针(比如 pa),它偏向的目标不一样,会造成解析xml承继链的起始点不一样,中途可以配对到的种类也不一样,因此同样的数据转换造成了不一样的結果。
3. 参照连接
http://c.biancheng.net/cpp/biancheng/view/3297.html
https://blog.csdn.net/u014624623/article/details/79837849
https://www.cnblogs.com/wanghongyang/ 【文中blog】
关注不迷路
扫码下方二维码,关注宇凡盒子公众号,免费获取最新技术内幕!
评论0