c++有哪四个c 四种强制类型转换换相关的关键

3659人阅读
C++(158)
C++中的类型转换分为两种:
1.&&&&& 隐式类型转换(而对于隐式变换,就是标准的转换,在很多时候,不经意间就发生了,比如int类型和float类型相加时,int类型就会被隐式的转换位float类型,然后再进行相加运算。);
2.&&&&& 显式类型转换。
& & & & 关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++ 之父的《C++的设计和演化》。最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_cast, dynamic_cast。标准C++中有四个类型转换符:static_cast、dynamic_cast、reinterpret_cast、和 const_cast。下面对它们一一进行介绍。
1.&&&&&static_cast
static_cast的转换格式:static_cast& type-id & ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换。如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
2.&&&&&dynamic_cast
& & & & 主要用于执行“安全的向下转型(safe down casting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。
dynamic_cast的转换格式:dynamic_cast& type-id & ( expression )
& & & & 该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
& & & & 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast,因为类型提供了运行时信息。
(1).上行转换
& & & & 比如B继承自A,B转换为A,进行上行转换时,是安全的,如下:
#include &iostream&
class B : public A
int main()
B *pB = new B;
A *pA = dynamic_cast&A *&(pB); // Safe and will succeed
(2).多重继承之间的上行转换:
& & & & &C继承自B,B继承自A,这种多重继承的关系;但是,关系很明确,使用dynamic_cast进行转换时,也是很简单的:
class B : public A
class C : public B
int main()
C *pC = new C;
B *pB = dynamic_cast&B *&(pC); // OK
A *pA = dynamic_cast&A *&(pC); // OK
& & & & &而上述的转换,static_cast和dynamic_cast具有同样的效果。而这种上行转换,也被称为隐式转换;比如我们在定义变量时经常这么写:B *pB = new C;这和上面是一个道理的,只是多加了一个dynamic_cast转换符而已。
(3).转换成void*
可以将类转换成void *,例如:
virtual void f(){}
virtual void f(){}
int main()
A *pA = new A;
B *pB = new B;
void *pV = dynamic_cast&void *&(pA); // pV points to an object of A
pV = dynamic_cast&void *&(pB); // pV points to an object of B
& & & & 但是,在类A和类B中必须包含虚函数,为什么呢?因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
(4).如果expression是type-id的基类,使用dynamic_cast进行转换时,在运行时就会检查expression是否真正的指向一个type-id类型的对象,如果是,则能进行正确的转换,获得对应的值;否则返回NULL,如果是引用,则在运行时就会抛出异常;例如:
virtual void f(){};
class D : public B
virtual void f(){};
void main()
B* pb = new D;
// unclear but ok
B* pb2 = new B;
D* pd = dynamic_cast&D*&(pb);
// ok: pb actually points to a D
D* pd2 = dynamic_cast&D*&(pb2);
// pb2 points to a B not a D, now pd2 is NULL
这个就是下行转换,从基类指针转换到派生类指针。
对于一些复杂的继承关系来说,使用dynamic_cast进行转换是存在一些陷阱的;比如,钻石结构:
D类型可以安全的转换成B和C类型,但是D类型要是直接转换成A类型呢?
virtual void Func() = 0;
class B : public A
void Func(){};
class C : public A
void Func(){};
class D : public B, public C
void Func(){}
int main()
D *pD = new D;
A *pA = dynamic_cast&A *&(pD); // You will get a pA which is NULL
& & & & 如果进行上面的直接转,你将会得到一个NULL的pA指针;这是因为,B和C都继承了A,并且都实现了虚函数Func,导致在进行转换时,无法进行抉择应该向哪个A进行转换。正确的做法是:
int main()
D *pD = new D;
B *pB = dynamic_cast&B *&(pD);
A *pA = dynamic_cast&A *&(pB);
这就是我在实现QueryInterface时,得到IUnknown的指针时,使用的是*ppv = static_cast&IX *&(this);而不是*ppv = static_cast&IUnknown *&(this);
dynamic_cast的讨论:
& & & & &在探究 dynamic_cast 的设计意图之前,值得留意的是很多 dynamic_cast 的实现都相当慢。
& & & & &例如,至少有一种通用的实现部分地基于对类名字进行字符串比较。如果你在一个位于四层深的单继承体系中的对象上执行 dynamic_cast,在这样一个实现下的每一个 dynamic_cast 都要付出相当于四次调用strcmp 来比较类名字的成本。对于一个更深的或使用了多继承的继承体系,付出的代价会更加昂贵。
& & & & 对 dynamic_cast 的需要通常发生在这种情况下:你要在一个你确信为派生类的对象上执行派生类的操作,但是你只能通过一个基类的指针或引用来操控这个对象。
有两个一般的方法可以避免这个问题:
& & & & 第一个,使用存储着直接指向派生类对象的指针的容器,从而消除通过基类接口操控这个对象的需要。当然,这个方法不允许你在同一个容器中存储所有可能的基类的派生类的指针。为了与不同的窗口类型一起工作,你可能需要多个类型安全(type-safe)的容器。
& & & & 一个候选方法可以让你通过一个基类的接口操控所有可能的 Window 派生类,就是在基类中提供一个让你做你想做的事情的虚函数。例如,尽管只有 SpecialWindows 能 blink,在基类中声明这个函数,并提供一个什么都不做的缺省实现或许是有意义的:
(1)避免强制转型的随时应用,特别是在性能敏感的代码中应用 dynamic_casts,如果一个设计需要强制转型,设法开发一个没有强制转型的侯选方案。
(2)如果必须要强制转型,设法将它隐藏在一个函数中。客户可以用调用那个函数来代替在他们自己的代码中加入强制转型。
(3)尽量用 C++ 风格的强制转型替换旧风格的强制转型。它们更容易被注意到,而且他们做的事情也更加明确。
3.&&&&&reinpreter_cast
用法:reinpreter_cast&type-id&(expression)
& & & & type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
该运算符的用法比较多。
4.&&&&&const_cast
const_cast的转换格式:const_cast&type_id& (expression)
& & & &该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
Voiatile和const类试。举如下一例:
#include &iostream&
CA() :m_iA(10){}
int main()
const CA *pA = new CA;
// pA-&m_iA = 100; // Error
CA *pB = const_cast&CA *&(pA);
pB-&m_iA = 100;
// Now the pA and the pB points to the same object
cout && pA-&m_iA &&
cout && pB-&m_iA &&
const CA &a = *pA;
// a.m_iA = 200; // Error
CA &b = const_cast&CA &&(a);
b.m_iA = 200;
// Now the a and the b reference to the same object
cout && b.m_iA &&
cout && a.m_iA &&
访问:513074次
积分:8271
排名:第2876名
原创:328篇
转载:45篇
评论:36条
文章:16篇
阅读:5760
文章:20篇
阅读:37513
阅读:8069
文章:165篇
阅读:195428
文章:34篇
阅读:73601
文章:28篇
阅读:89557
(10)(12)(14)(13)(10)(19)(11)(16)(13)(16)(13)(13)(14)(13)(18)(12)(13)(9)(21)(21)(12)(11)(67)(4)(3)
大数据2.0,将创造出更新更具竞争力的超级企业!
资源整合和企业成本节约的一把利器!
合理配置规划和整合资源,利用最少的资源,打造高效的开发团队,使产出智能化,数量化,自动化!创造最大的价值!他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)C++的4种类型转换关键字及其特点
1 reinterpret_cast
'reinterpret_cast'转换一个指针为其它类型的指针。它也允许从一个指针转换为整数类型。反之亦然。(译注:是指针具体的地址值作为整数值?)
这个操作符能够在非相关的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。
如果情况是从一个指针到整型的拷贝,内容的解释是系统相关的,所以任何的实现都不是方便的。一个转换到足够大的整型能够包含它的指针是能够转换回有效的指针的。
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast&B
'reinterpret_cast'就像传统的类型转换一样对待所有指针的类型转换。
2 static_cast
'static_cast'允许执行任意的隐式转换和相反转换动作。(即使它是不允许隐式的)
应用到类的指针上,意思是说它允许子类类型的指针转换为父类类型的指针(这是一个有效的隐式转换),同时,也能够执行相反动作:转换父类为它的子类。
在这最后例子里,被转换的父类没有被检查是否与目的类型相一致。
class Base {};
class Derived : public Base {};
Base *a = new B
Derived *b = static_cast&Derived
'static_cast'除了操作类型指针,也能用于执行类型定义的显式的转换,以及基础类型之间的标准转换:
double d = 3.;
int i = static_cast&int&(d);
3 dynamic_cast
'dynamic_cast'只用于对象的指针和引用。当用于多态类型时,它允许任意的隐式类型转换以及相反过程。不过,与static_cast不同,在后一种情况里(注:即隐式转换的相反过程),dynamic_cast会检查操作是否有效。也就是说,它会检查转换是否会返回一个被请求的有效的完整对象。
检测在运行时进行。如果被转换的指针不是一个被请求的有效完整的对象指针,返回值为NULL.
class Base { virtual dummy() {} };
class Derived : public Base {};
Base* b1 = new D
Base* b2 = new B
Derived* d1 = dynamic_cast&Derived
*&(b1); // succeeds
Derived* d2 = dynamic_cast&Derived
*&(b2); // fails: returns 'NULL'
如果一个引用类型执行了类型转换并且这个转换是不可能的,一个bad_cast的异常类型被抛出:
class Base { virtual dummy() {} };
class Derived : public Base { };
Base* b1 = new D
Base* b2 = new B
Derived d1 = dynamic_cast&Derived
&*&(b1); // succeeds
Derived d2 = dynamic_cast&Derived
&*&(b2); // fails: exception
4 const_cast
这个转换类型操纵传递对象的const属性,或者是设置或者是移除:
class C {};
const C *a = new C;
C *b = const_cast&C *&(a);
其它三种操作符是不能修改一个对象的常量性的。
注意:'const_cast'也能改变一个类型的volatile qualifier。
--------------------------------------------------------------------
C++的4种类型转换
一、C 风格(C-style)强制转型如下:
(T) expression // cast expression to be of type T
函数风格(Function-style)强制转型使用这样的语法:
T(expression) // cast expression to be of type T
这两种形式之间没有本质上的不同,它纯粹就是一个把括号放在哪的问题。我把这两种形式称为旧风格(old-style)的强制转型。
二、 C++的四种强制转型形式:
  C++ 同时提供了四种新的强制转型形式(通常称为新风格的或 C++ 风格的强制转型):
  const_cast(expression)
  dynamic_cast(expression)
  reinterpret_cast(expression)
  static_cast(expression)
  每一种适用于特定的目的:
  ·dynamic_cast 主要用于执行“安全的向下转型(safe
downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。
·static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为
double,等等),它还可以用于很多这样的转换的反向转换(例如,void*
指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象(只有
const_cast 能做到),它最接近于C-style的转换。
  ·const_cast 一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型。
  ·reinterpret_cast
是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。
  旧风格的强制转型依然合法,但是新的形式更可取。首先,在代码中它们更容易识别(无论是人还是像 grep
这样的工具都是如此),这样就简化了在代码中寻找类型系统被破坏的地方的过程。第二,更精确地指定每一个强制转型的目的,使得编译器诊断使用错误成为可能。例如,如果你试图使用一个
const_cast 以外的新风格强制转型来消除常量性,你的代码将无法编译。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。c++ 四种类型转换机制 - 暴力的轮胎 - 博客园
Help u Help myself
类型转换机制可以分为:隐式类型转换 和 显示类型转换(强制类型转换)
C中的类型转换:
  事情要从头说起,这个头就是C语言.我们已经习惯了使用C-like类型转换,因为它强大而且简单.
主要有一下两种形式:
(new-type) expression
new-type (expression)
C艹中的类型转换:
隐式类型转换比较常见,在混合类型表达式中经常发生.比如在表达式中存在short和int,那么就过会发生整型提升.四种强制类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。
1. static_cast与dynamic_cast:
  把这两个放在一起比较容易记忆,"一静一动".从字面上也可以看出,前者提供的是编译时期的静态类型检测,后者提供的是
运行时检测.
static_cast: 1)完成基础数据类型,2)同一个继承体系中类型的转换 3)任意类型与空指针类型void*之间的转换。
dynamic_cast:使用多态的场景,增加了一层对真实调用对象类型的检查
int *p = (int *)&c;
cout&&(char)*p&&//'A'
int *q = static_cast&int *&(&c); //编译报错:error: invalid static_cast from type &char*& to type &int*&
  在上面的例子中,Clike可以运行,然而修改过的*p如果使用,运行结果将会出现错误,而使用static_cast可以将错误在编译时期检查出,.
  在不同继承体系的自定义类型中:
void printC()
std::cout &&"call printC() in class C" &&std::
char c1, c2;
A *ptrA = new A();
C *ptrC = (C *)(ptrA);
ptrC-&printC(); //"call printC() in class C"
//ptrC = static_cast&C*&(ptrA); //编译报错:error: invalid static_cast from type 'A*& to type C*&
  上面A C是两个无关的类,然而使用Clike可以实现这种类型的强制转换,这是十分危险的! 使用static_cast可以将这种潜在的危险在编译器找出来.
  在同一继承体系中:
  upcast(向上转换即子类转成父类):没有问题.因为父类的行为都包含在子类中;
  downcast(向下转换):有可能会出现问题,编译时可能不会发现.
  一个类的行为和自身的类型相关.也就是一个A类型的指针总会优先调用自己A类内的函数,当然发生继承中的重写(虚继承等)例外.
#include &iostream&
#include &cstdio&
using namespace
A():i(1), j(1){}
void printA()
std::cout &&"call printA() in class A" &&std::
void printSum()
std::cout &&"sum = " &&i+j &&std::
class B : public A
B():a(2), b(2) {}
void printB()
std::cout &&"call printB() in class B" &&std::
void printSum()
std::cout &&"sum = " &&a+b &&std::
void Add()
int main()
B *ptrB = new B;
ptrB -& printSum();
A *ptrA = static_cast&B *&(ptrB);
ptrA -& printA();
ptrA -& printSum();
//打印结果:sum = 2
//在进行upcast的时候,指针指向的对象的行为与指针的类型相关。
ptrA = new A;
ptrB = static_cast&B *&(ptrA);
ptrB -& printB();
ptrB -& printSum();
//打印结果:sum = 0
//在进行downcast的时候,其行为是&undefined&。
rB.printSum();
//打印结果: sum = 4
A &rA = static_cast&A &&(b);
rA.printA();
rA.printSum();
//打印结果: sum = 2
//在进行upcast的时候,指针指向的对象的行为与引用类型相关.
rA.printSum();
B &rB1 = static_cast&B &&(a);
rB1.printB();
rB1.printSum();
//打印结果 :sum = 1.4
//在进行downcast的时候,其行为是&undefined&。
  这里其实很明显,在downcast转换的时候,会出现一些跟指针或者引用类型相关的函数调用,但是因为指针或者引用(父类)
没有定义这些行为,因为调用到了这些行为导致出现了未定义的行为.
  明显解决这个问题的办法就是,虚函数!如果声明A类中的printSum未 虚函数,那么子类B就会有一个虚表,虚表中的第一个函数就是printSum函数其实是
B类的该函数.所以,A类指针调用该函数就会调用B类中的该函数 显示结果sum= 4. 在未定义之前sum = 2(A类中的该函数).
  PS:引用类型必须被初始化,这是引用和指针类型的重要区别.
  总之,就是尽可能不要使用downcast也就是 使用子类的指针指向父类.
  感觉这里又不得不说,c++内存对象的对齐方式.所以 ,在另外一篇blog&c++内存的对齐方式&中理清楚这些问题.
dynamic_cast
1.dynamic_cast是在运行时检查的,用于在集成体系中进行安全的向下转换downcast(当然也可以向上转换,但没必要,因为可以用虚函数实现)
& &即:基类指针/引用 -& 派生类指针/引用
& &如果源和目标没有继承/被继承关系,编译器会报错!
2.dynamic_cast是4个转换中唯一的RTTI操作符,提供运行时类型检查。
3.dynamic_cast不是强制转换,而是带有某种&咨询&性质的,如果不能转换,返回NULL。这是强制转换做不到的。
4.源类中必须要有虚函数,保证多态,才能使用dynamic_cast&source&(expression)
static_cast
用法:static_cast & type-id & ( expression )
该运算符把expression转换为type-id类型,在编译时使用类型信息执行转换,在转换执行必要的检测(指针越界,类型检查),其操作数相对是安全的。
但没有运行时类型检查来保证转换的安全性。
reinterpret_cast
仅仅是复制n1的比特位到d_r, 没有进行必要的分析.interpret_cast是为了映射到一个完全不同类型\
的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄\
玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话
const_cast
去除const常量属性,使其可以修改
volatile属性的转换& 易变类型&-&不同类型.
#include &iostream&
#include &cstdio&
#include &string&
using namespace
class C : public A, public B {};
int main()
//a.num = 1;
const_cast&A&&(a).m_a = 2;
//a.num = 3;编译不能通过,说明const_cast只能转换一次,不是永久脱离原有const属性
cout&&a.m_a&&
int n = 9;
double d_s = static_cast&double&(n);
double d_r = reinterpret_cast&double&&(n);
cout&&d_r&&//4.2
//在进行计算以后, d_r包含无用值. 这是因为 reinterpret_cast\
仅仅是复制n1的比特位到d_r, 没有进行必要的分析.interpret_cast是为了映射到一个完全不同类型\
的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄\
玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话
printf("%p, %p, %p\n", &c, reinterpret_cast&B*&(&c), static_cast &B*&(&c));
//前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,\
并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换\
因此, 你需要谨慎使用 reinterpret_cast.
#include &iostream&
#include &typeinfo&
#include &cstdio&
using namespace
virtual void foo(){
cout&&"A foo"&&
//虚函数的出现会带来动态机制 Class A 至少要有一个虚函数
void pp(){
cout&&"A pp"&&
class B: public A{
void foo(){
cout&&"B foo"&&
void pp(){
cout&&"B PP"&&
void functionB(){
cout&&"Excute FunctionB!"&&
int main()
A *pa = &b;
pa-&foo();
//基类指针可以指向派生类,但是只能调用基类和派生类都存在的成员,也就是说不能调用派生类中新增的成员!
//pa-&FunctionB();//error: 'class A' has no member named 'FunctionB'
if(dynamic_cast&B*&(pa) == NULL){
cout&&"NULL"&&
cout&&typeid((dynamic_cast&B*&(pa))).name()&&
dynamic_cast&B*&(pa)-&foo();
dynamic_cast&B*&(pa)-&pp();
dynamic_cast&B*&(pa)-&functionB();
//B *pb = &派生类不能指向基类
B *pbNull = NULL;
pbNull-&functionB();//fine
pbNull-&pp();//fine
//pbNull-&functionB(); crash!foo调用了虚函数,编译器需要根据对象的虚函数指针查找虚函数表,但为空,crash!
随笔 - 224

我要回帖

更多关于 c 四中类型转换 的文章

 

随机推荐