基本数据类型有不同的编码方案(encodingscheme),需要不同的字节长度来适应各自的值域。 基本类型的宽窄转换,是一个整体的转换,其由窄到宽是安全的,由宽到窄可能会丢失数据。 继承链上父类子类转换,不单只是数据域的宽窄问题,还有不同数量的成员函数去访问数据的问题。1基本类型的转换 C虽然是强类型语言,但支持类型的隐式转换和显式转换。类型转换不仅发生在表达式中,还发生在函数调用时的实参与形参结合时,以及函数返回值时。 1。1基本类型由窄到宽,其隐式转换为安全的charch3;intnch;doublefn; 1。2类型提升 表达式(右式)中不同类型参与运算时,会有一个类型提升的问题(避免数据丢失),如整型提升到最大宽度的类型,有符号提升到无符号,单精度提升到双精度等。unsignedchara0xa5;unsignedcharba41;printf(d,b);250不考虑类型提升考虑类型提升10100101000000000000000000000000000000001010010101011010111111111111111111111111111010110100000001011111010Theimplicitconversionsthatpreservevaluesarecommonlyreferredtoaspromotions。Beforeanarithmeticoperationisperformed,integralpromotionisusedtocreateintsoutofshorterintegertypes。Thisreflectstheoriginalpurposeofthesepromotions:tobringoperandstothenaturalsizeforarithmeticoperations。Inaddition,floattodoubleisconsideredapromotion。 保存值的隐式转换通常称为提升。在执行算术运算之前,整数提升用于从较短的整数类型中创建整数(ints)。这反映了这些提升的最初目的:将操作数调整为算术运算的自然大小(sizeof(int))。此外,float到double被认为是一种提升。 1。3基本类型由宽到窄(narrowingconversions) InC,anarrowingconversionisanumericconversionthatmayresultinthelossofdata。Suchnarrowingconversionsinclude: 在C中,窄向转换是一种可能导致数据丢失的数字转换。此类窄向转换包括: Fromafloatingpointtypetoanintegraltype。 从浮点类型到整数类型。 Fromawiderfloatingpointtypetoanarrowerfloatingpointtype,unlessthevaluebeingconvertedisconstexprandisinrangeofthedestinationtype(evenifthenarrowertypedoesn’thavetheprecisiontostorethewholenumber)。 从较宽的浮点类型到较窄的浮点类型,除非转换的值是constexpr并且在目标类型的范围内(即使较窄的类型没有存储整数的精度)。 Fromanintegraltoafloatingpointtype,unlessthevaluebeingconvertedisconstexprandisinrangeofthedestinationtypeandcanbeconvertedbackintotheoriginaltypewithoutdataloss。 从整数类型转换为浮点类型,除非转换的值是constexpr,并且在目标类型的范围内,并且可以在不丢失数据的情况下转换回原始类型。 Fromawiderintegraltypetoanarrowerintegraltype,unlessthevaluebeingconvertedisconstexprandafterintegralpromotionwillfitintothedestinationtype。 从较宽的整数类型转换为较窄的整数类型,除非转换的值是constexpr,并且整数升级后将适合目标类型。 Thegoodnewsisthatyoudon’tneedtorememberthese。Yourcompilerwillusuallyissueawarning(orerror)whenitdeterminesthatanimplicitnarrowingconversionisrequired。 好消息是你不需要记住这些。当编译器确定需要窄向转换时,通常会发出警告(或错误)。 Recommendtousingstaticcasttomakenarrowingconversionsexplicit。 推荐使用staticcast显式进行窄向转换。 Compilerswilloftenissuewarningswhenapotentiallyunsafe(narrowing)implicittypeconversionisperformed。Forexample,considerthefollowingprogram: 当执行潜在的不安全(缩小范围)隐式类型转换时,编译器通常会发出警告。例如,考虑以下程序:inti{48};charchi;implicitnarrowingconversion Castinganint(2or4bytes)toachar(1byte)ispotentiallyunsafe(asthecompilercan’ttellwhethertheintegervaluewilloverflowtherangeofthecharornot),andsothecompilerwilltypicallyprintawarning。Ifweusedlistinitialization,thecompilerwouldyieldanerror。 将int(2或4字节)转换为char(1字节)可能不安全(因为编译器无法判断整数值是否会溢出char的范围),因此编译器通常会打印警告。如果使用列表初始化,编译器将产生错误。 Togetaroundthis,wecanuseastaticcasttoexplicitlyconvertourintegertoachar: 为了解决这个问题,我们可以使用静态转换将整数显式转换为字符:inti{48};explicitconversionfrominttochar,sothatacharisassignedtovariablechcharch{staticcastchar(i)}; Whenwedothis,we’reexplicitlytellingthecompilerthatthisconversionisintended,andweacceptresponsibilityfortheconsequences(e。g。overflowingtherangeofacharifthathappens)。Sincetheoutputofthisstaticcastisoftypechar,theinitializationofvariablechdoesn’tgenerateanytypemismatches,andhencenowarningsorerrors。 当我们这样做的时候,我们明确地告诉编译器这个转换是有意的,并且我们承担后果的责任(例如,如果发生这种情况,就会溢出字符的范围)。由于此staticcast的输出是char类型,因此变量ch的初始化不会生成任何类型不匹配,因此不会产生警告或错误。 1。3。1长整型转换为短整型:舍弃高位; 1。3。2浮点型转换为整型:舍弃小数部分; 1。3。3double转换为float:可能会有精度损失;includestdio。hintmain(){intn1027;0000000000000000010000000011charchn;00000011,ch与n的编码方式相同(补码),直接取sizeof(ch)个字节bereducedmodulo(theremainderofanintegerpisionbythe)char’srangeprintf(d,ch);3doublef3。75;11。11b,01000000011100000000000000000000指数100000001,尾数11nf;n与f的编码方式不同(补码与IEEE754浮点编码方案),按语言预定规则转换printf(d,n);3,弃掉小数部分inta1000;0000000000000000001111101000charba;bbecomes24(onsomemachines)取最后一个字节(小端存储则地址不变,取第一个字节),printf(d,b);111010001100024}adoubletointconversiontruncates(alwaysroundsdown,towardzero)ratherthanusingtheconventional45rounding。howconversionsfromdoubletointandconversionsfrominttochararedoneonyourmachine C11introducedaninitializationnotationthatoutlawsnarrowingconversions。 C11引入了一种初始化符号,禁止窄向转换。 Forexample,wecould(andshould)rewritethetroublesomeexamplesaboveusinga{}listnotation,ratherthanthenotation: 例如,我们可以(而且应该)使用{}list表示法而不是表示法重写上述麻烦的示例:doublex{2。7};OKinty{x};error:doubleintmightnarrowinta{1000};OKcharb{a};error:intcharmightnarrowintcharb1{1000};error:narrowing(assuming8bitchars)charb2{48};OK Wecanusenarrowcastwhenweneedtoconvertavalueandwearenotsureifitwillfit;itisdefinedinstdlibfacilities。handimplementedusingerror()。narrowcastcanthrowaruntimeerrorexception: 当我们需要转换一个值并且我们不确定它是否合适时,我们可以使用narrowcast;它在stdlibfacilities。h中定义,并使用error()实现。narrowcast可以引发runtimeerror异常:intx1narrowcastint(2。9);throwsintx2narrowcastint(2。0);OKcharc1narrowcastchar(1066);throwscharc2narrowcastchar(85);OK2继承链上父类子类转换 子类对象父类对象,子可能有更多成员,可能存在越界; 父类对象子类对象,父可能只有更少空间,存在切割; 父类指针或引用子类指针或引用,不存在切割,且是多态的必备条件。 派生类中的成员,包含两大部分,一类是从基类继承过来的,一类是自己增加的成员。 从基类继承过来的表现其共性,而新增的成员体现了其个性。 code:includeiostreamincludestringusingnamespacestd;classStudent{public:Student(stringsn,intn,chars);Student();voiddis();private:stringname;intnum;charsex;};Student::Student(stringsn,intn,chars):name(sn),num(n),sex(s){}Student::Student(){}voidStudent::dis(){coutnameendl;coutnumendl;coutsexendl;}classGraduate:publicStudent{public:Graduate(stringsn,intin,charcs,floatfs);Graduate();voiddump(){dis();coutsalaryendl;}private:floatsalary;};Graduate::Graduate(stringsn,intin,charcs,floatfs):Student(sn,in,cs),salary(fs){}Graduate::Graduate(){}classBirthday{public:Birthday(inty,intm,intd);Birthday();voidprint();private:intyear;intmonth;intday;};Birthday::Birthday(inty,intm,intd):year(y),month(m),day(d){}Birthday::Birthday(){}voidBirthday::print(){coutyearmonthdayendl;}classDoctor:publicGraduate{public:Doctor(stringsn,intin,charcs,floatfs,stringst,intiy,intim,intid);Doctor();voiddisdump();private:stringtitle;调用的默认构造器,初始化为Birthdaybirth;类中声明的类对象};Doctor::Doctor(stringsn,intin,charcs,floatfs,stringst,intiy,intim,intid):Graduate(sn,in,cs,fs),birth(iy,im,id),title(st){}Doctor::Doctor(){}voidDoctor::disdump(){dump();couttitleendl;birth。print();}intmain(){Students(zhaosi,2001,m);s。dis();coutendl;Graduateg(liuneng,2001,x,2000);g。dump();coutendl;Doctord(qiuxiang,2001,y,3000,doctor,2001,8,16);d。disdump();getchar();return0;}zhaosi2001mliuneng2001x2000qiuxiang2001y3000doctor2001816 Asyouhavealreadyseen,anobjectcanbecastorassignedtoitsparentclass。Ifthecastorassignmentisperformedonaplainoldobject,thisresultsinslicing: 正如您已经看到的,可以强制转换对象或将其指定给其父类。如果在普通旧对象上执行强制转换或指定,则会导致切片:BasemyBasemyDerived;Slicing! SlicingoccursinsituationslikethisbecausetheendresultisaBaseobject,andBaseobjectslacktheadditionalfunctionalitydefinedintheDerivedclass。However,slicingdoesnotoccurifaderivedclassisassignedtoapointerorreferencetoitsbaseclass: 切片发生在这样的情况下,因为最终结果是一个基对象,而基对象缺少派生类中定义的其他功能。但是,如果将派生类分配给指针或对其基类的引用,则不会发生切片:BasemyBasemyDerived;Noslicing! Thisisgenerallythecorrectwaytorefertoaderivedclassintermsofitsbaseclass,alsocalledupcasting。Thisiswhyit’salwaysagoodideatomakeyourmethodsandfunctionstakereferencestoclassesinsteadofdirectlyusingobjectsofthoseclasses。Byusingreferences,derivedclassescanbepassedinwithoutslicing。 这通常是根据派生类的基类(也称为向上转换)引用派生类的正确方法。这就是为什么让方法和函数引用类而不是直接使用这些类的对象总是一个好主意。通过使用引用,可以传入派生类而无需切片。 Castingfromabaseclasstooneofitsderivedclasses,alsocalleddowncasting,isoftenfrowneduponbyprofessionalCprogrammersbecausethereisnoguaranteethattheobjectreallybelongstothatderivedclass,andbecausedowncastingisasignofbaddesign。Forexample,considerthefollowingcode: 从基类到其派生类之一的强制转换(也称为向下转换)通常受到专业C程序员的反对,因为无法保证对象确实属于该派生类,而且向下转换是糟糕设计的标志。例如,考虑以下代码:voidpresumptuous(Basebase){DerivedmyDerivedstaticcastDerived(base);ProceedtoaccessDerivedmethodsonmyDerived。} Iftheauthorofpresumptuous()alsowritescodethatcallspresumptuous(),everythingwillprobablybeokaybecausetheauthorknowsthatthefunctionexpectstheargumenttobeoftypeDerived。However,ifotherprogrammerscallpresumptuous(),theymightpassinaBase。Therearenocompiletimechecksthatcanbedonetoenforcethetypeoftheargument,andthefunctionblindlyassumesthatbaseisactuallyapointertoaDerived。 如果presumptuous()的作者还编写了调用presumptuous()的代码,那么一切都可能正常,因为作者知道函数期望参数的类型是派生的。然而,如果其他程序员调用presumptuous(),他们可能会传入一个Base。没有可以执行的编译时检查来强制参数的类型,并且函数盲目地假设base实际上是指向派生类型的指针。 Downcastingissometimesnecessary,andyoucanuseiteffectivelyincontrolledcircumstances。However,ifyouaregoingtodowncast,youshoulduseadynamiccast(),whichusestheobject’sbuiltinknowledgeofitstypetorefuseacastthatdoesn’tmakesense。Thisbuiltinknowledgetypicallyresidesinthevtable,whichmeansthatdynamiccast()worksonlyforobjectswithavtable,thatis,objectswithatleastonevirtualmember。Ifadynamiccast()failsonapointer,thepointer’svaluewillbenullptrinsteadofpointingtononsensicaldata。Ifadynamiccast()failsonanobjectreference,anstd::badcastexceptionwillbethrown。 向下转型有时是必要的,您可以在受控环境中有效地使用它。但是,如果要向下转型,则应使用dynamiccast(),它使用对象的内置类型知识来拒绝没有意义的转换。这种内置知识通常驻留在vtable中,这意味着dynamiccast()仅适用于具有vtable的对象,即至少具有一个虚拟成员的对象。如果指针上的dynamiccast()失败,指针的值将为nullptr,而不是指向无意义的数据。如果对象引用上的动态dynamiccast()失败,将引发std::badcast异常。 Thepreviousexamplecouldhavebeenwrittenasfollows: 前面的示例可以编写如下:voidlessPresumptuous(Basebase){DerivedmyDeriveddynamiccastDerived(base);if(myDerived!nullptr){ProceedtoaccessDerivedmethodsonmyDerived。}} Theuseofdowncastingisoftenasignofabaddesign。Youshouldrethinkandmodifyyourdesignsothatdowncastingcanbeavoided。Forexample,thelessPresumptuous()functiononlyreallyworkswithDerivedobjects,soinsteadofacceptingaBasepointer,itshouldsimplyacceptaDerivedpointer。Thiseliminatestheneedforanydowncasting。Ifthefunctionshouldworkwithdifferentderivedclasses,allinheritingfromBase,thenlookforasolutionthatusespolymorphism。 使用向下转型通常是糟糕设计的标志。您应该重新考虑和修改您的设计,以避免向下转型。例如,lessPresumptuous()函数实际上只适用于派生对象,因此它不应该接受基指针,而应该只接受派生指针。这消除了任何向下转型的需要。如果函数应该使用不同的派生类,所有派生类都是从基继承的,那么请寻找使用多态性的解决方案。 ref https:www。learncpp。comcpptutorialnumericconversions MarcGregoire《PROFESSIONALC》(比)格莱戈尔《C高级编程》 End