在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    C++|寬窄基本類型轉(zhuǎn)換和父類子類轉(zhuǎn)換

    C++|寬窄基本類型轉(zhuǎn)換和父類子類轉(zhuǎn)換

    基本數(shù)據(jù)類型有不同的編碼方案(encoding scheme),需要不同的字節(jié)長度來適應(yīng)各自的值域。

    基本類型的寬窄轉(zhuǎn)換,是一個整體的轉(zhuǎn)換,其由窄到寬是安全的,由寬到窄可能會丟失數(shù)據(jù)。

    繼承鏈上父類子類轉(zhuǎn)換,不單只是數(shù)據(jù)域的寬窄問題,還有不同數(shù)量的成員函數(shù)去訪問數(shù)據(jù)的問題。

    1 基本類型的轉(zhuǎn)換

    C++雖然是強(qiáng)類型語言,但支持類型的隱式轉(zhuǎn)換和顯式轉(zhuǎn)換。類型轉(zhuǎn)換不僅發(fā)生在表達(dá)式中,還發(fā)生在函數(shù)調(diào)用時的實(shí)參與形參結(jié)合時,以及函數(shù)返回值時。

    1.1 基本類型由窄到寬,其隱式轉(zhuǎn)換為安全的

    char ch = 3; int n = ch; double f = n;

    1.2 類型提升

    表達(dá)式(右式)中不同類型參與運(yùn)算時,會有一個類型提升的問題(避免數(shù)據(jù)丟失),如整型提升到最大寬度的類型,有符號提升到無符號,單精度提升到雙精度等。

    unsigned char a = 0xa5; unsigned char b = ~a>>4+1; printf(“%d”,b); // 250 // 不考慮類型提升 考慮類型提升 // 10100101 0000000000000000000000000000000010100101 // 01011010 11111111111111111111111111101011010 // 00000010 11111010

    The implicit conversions that preserve values are commonly referred to as promotions. Before an arithmetic operation is performed, integral promotion is used to create ints out of shorter integer types. This reflects the original purpose of these promotions: to bring operands to the “natural” size for arithmetic operations. In addition, float to double is considered a promotion.

    保存值的隱式轉(zhuǎn)換通常稱為提升。在執(zhí)行算術(shù)運(yùn)算之前,整數(shù)提升用于從較短的整數(shù)類型中創(chuàng)建整數(shù)(ints)。這反映了這些提升的最初目的:將操作數(shù)調(diào)整為算術(shù)運(yùn)算的“自然”大?。╯izeof(int))。此外,float到double被認(rèn)為是一種提升。

    1.3 基本類型由寬到窄(narrowing conversions)

    In C++, a narrowing conversion is a numeric conversion that may result in the loss of data. Such narrowing conversions include:

    在C++中,窄向轉(zhuǎn)換是一種可能導(dǎo)致數(shù)據(jù)丟失的數(shù)字轉(zhuǎn)換。此類窄向轉(zhuǎn)換包括:

    ① From a floating point type to an integral type.

    浮點(diǎn)類型到整數(shù)類型。

    ② From a wider floating point type to a narrower floating point type, unless the value being converted is constexpr and is in range of the destination type (even if the narrower type doesn’t have the precision to store the whole number).

    從較寬的浮點(diǎn)類型到較窄的浮點(diǎn)類型,除非轉(zhuǎn)換的值是constexpr并且在目標(biāo)類型的范圍內(nèi)(即使較窄的類型沒有存儲整數(shù)的精度)。

    ③ From an integral to a floating point type, unless the value being converted is constexpr and is in range of the destination type and can be converted back into the original type without data loss.

    從整數(shù)類型轉(zhuǎn)換為浮點(diǎn)類型,除非轉(zhuǎn)換的值是constexpr,并且在目標(biāo)類型的范圍內(nèi),并且可以在不丟失數(shù)據(jù)的情況下轉(zhuǎn)換回原始類型。

    ④ From a wider integral type to a narrower integral type, unless the value being converted is constexpr and after integral promotion will fit into the destination type.

    從較寬的整數(shù)類型轉(zhuǎn)換為較窄的整數(shù)類型,除非轉(zhuǎn)換的值是constexpr,并且整數(shù)升級后將適合目標(biāo)類型。

    The good news is that you don’t need to remember these. Your compiler will usually issue a warning (or error) when it determines that an implicit narrowing conversion is required.

    好消息是你不需要記住這些。當(dāng)編譯器確定需要窄向轉(zhuǎn)換時,通常會發(fā)出警告(或錯誤)。

    Recommend to using static_cast to make narrowing conversions explicit.

    推薦使用static_cast顯式進(jìn)行窄向轉(zhuǎn)換。

    Compilers will often issue warnings when a potentially unsafe (narrowing) implicit type conversion is performed. For example, consider the following program:

    當(dāng)執(zhí)行潛在的不安全(縮小范圍)隱式類型轉(zhuǎn)換時,編譯器通常會發(fā)出警告。例如,考慮以下程序:

    int i { 48 };char ch = i; // implicit narrowing conversion

    Casting an int (2 or 4 bytes) to a char (1 byte) is potentially unsafe (as the compiler can’t tell whether the integer value will overflow the range of the char or not), and so the compiler will typically print a warning. If we used list initialization, the compiler would yield an error.

    將int(2或4字節(jié))轉(zhuǎn)換為char(1字節(jié))可能不安全(因為編譯器無法判斷整數(shù)值是否會溢出char的范圍),因此編譯器通常會打印警告。如果使用列表初始化,編譯器將產(chǎn)生錯誤。

    To get around this, we can use a static cast to explicitly convert our integer to a char:

    為了解決這個問題,我們可以使用靜態(tài)轉(zhuǎn)換將整數(shù)顯式轉(zhuǎn)換為字符:

    int i { 48 };// explicit conversion from int to char, so that a char is assigned to variable chchar ch { static_cast(i) };

    When we do this, we’re explicitly telling the compiler that this conversion is intended, and we accept responsibility for the consequences (e.g. overflowing the range of a char if that happens). Since the output of this static_cast is of type char, the initialization of variable ch doesn’t generate any type mismatches, and hence no warnings or errors.

    當(dāng)我們這樣做的時候,我們明確地告訴編譯器這個轉(zhuǎn)換是有意的,并且我們承擔(dān)后果的責(zé)任(例如,如果發(fā)生這種情況,就會溢出字符的范圍)。由于此static_cast的輸出是char類型,因此變量ch的初始化不會生成任何類型不匹配,因此不會產(chǎn)生警告或錯誤。

    1.3.1 長整型轉(zhuǎn)換為短整型:舍棄高位;

    1.3.2 浮點(diǎn)型轉(zhuǎn)換為整型:舍棄小數(shù)部分;

    1.3.3 double轉(zhuǎn)換為float:可能會有精度損失;

    #include int main(){ int n = 1027; // 0000 0000 0000 0000 0100 0000 0011 char ch = n; // 0000 0011,ch與n的編碼方式相同(補(bǔ)碼),直接取sizeof(ch)個字節(jié) // be reduced modulo (the remainder of an integer pision by the) char’s range printf(“%d”,ch); // 3 double f = 3.75; // 11.11b, 0100 0000 0111 0000 0000 0000 0000 0000 // 指數(shù) 100 0000 01, 尾數(shù) 11 n = f; // n與f的編碼方式不同(補(bǔ)碼與IEEE754浮點(diǎn)編碼方案),按語言預(yù)定規(guī)則轉(zhuǎn)換 printf(“%d”,n); // 3 , 棄掉小數(shù)部分 int a = 1000; // 0000 0000 0000 0000 0011 1110 1000 char b = a; // b becomes –24 (on some machines) // 取最后一個字節(jié)(小端存儲則地址不變,取第一個字節(jié)), printf(“%d”,b);// 1110 1000 = -11000 = -24 }// a double-to-int conversion truncates (always// rounds down, toward zero) rather than using the conventional 4/5 rounding.// how conversions from double to int and conversions from int to char // are done on your machine

    C++11 introduced an initialization notation that outlaws narrowing conversions.

    C++11引入了一種初始化符號,禁止窄向轉(zhuǎn)換。

    For example, we could (and should) rewrite the troublesome examples above using a {}-list notation, rather than the = notation:

    例如,我們可以(而且應(yīng)該)使用{}-list表示法而不是=表示法重寫上述麻煩的示例:

    double x {2.7}; // OKint y {x}; // error: double -> int might narrowint a {1000}; // OKchar b {a}; // error: int -> char might narrowint char b1 {1000}; // error: narrowing (assuming 8-bit chars)char b2 {48}; // OK

    We can use narrow_cast when we need to convert a value and we are not sure “if it will fit”; it is defined in std_lib_facilities. h and implemented using error(). narrow_cast can throw a runtime_error exception:

    當(dāng)我們需要轉(zhuǎn)換一個值并且我們不確定“它是否合適”時,我們可以使用narrow_cast;它在std_lib_facilities. h中定義,并使用error()實(shí)現(xiàn)。narrow_cast可以引發(fā)runtime_error異常:

    int x1 = narrow_cast(2.9); // throwsint x2 = narrow_cast(2.0); // OKchar c1 = narrow_cast(1066); // throwschar c2 = narrow_cast(85); // OK

    2 繼承鏈上父類子類轉(zhuǎn)換

    子類對象 父類對象,子可能有更多成員,可能存在越界;

    父類對象 子類對象,父可能只有更少空間,存在切割;

    父類指針或引用 子類指針或引用,不存在切割,且是多態(tài)的必備條件。

    派生類中的成員,包含兩大部分,一類是從基類繼承過來的,一類是自己增加的成員。

    從基類繼承過來的表現(xiàn)其共性,而新增的成員體現(xiàn)了其個性。

    code:

    #include #include using namespace std;class Student{public: Student(string sn,int n,char s); ~Student(); void dis();private: string name; int num; char sex;};Student::Student(string sn, int n, char s):name(sn),num(n),sex(s){}Student::~Student(){}void Student:: dis(){ cout<<name<<endl; cout<<num<<endl; cout<<sex<<endl;}class Graduate:public Student{public: Graduate(string sn,int in,char cs,float fs); ~Graduate(); void dump() { dis(); cout<<salary<<endl; }private: float salary;};Graduate::Graduate(string sn, int in, char cs, float fs):Student(sn,in,cs),salary(fs){}Graduate::~Graduate(){}class Birthday{public: Birthday(int y,int m,int d); ~Birthday(); void print();private: int year; int month; int day;};Birthday::Birthday(int y, int m, int d):year(y),month(m),day(d){}Birthday::~Birthday(){}void Birthday::print(){ cout<<year<<month<<day<<endl;}class Doctor:public Graduate{public: Doctor(string sn,int in,char cs,float fs,string st,int iy,int im,int id); ~Doctor(); void disdump();private: string title; //調(diào)用的默認(rèn)構(gòu)造器,初始化為”” Birthday birth; //類中聲明的類對象};Doctor::Doctor(string sn, int in, char cs, float fs, string st, int iy, int im, int id) :Graduate(sn,in,cs,fs),birth(iy,im,id),title(st){}Doctor::~Doctor(){}void Doctor::disdump(){ dump(); cout<<title<<endl; birth.print();}int main(){ Student s("zhaosi",2001,'m'); s.dis(); cout<<"—————-"<<endl; Graduate g("liuneng",2001,'x',2000); g.dump(); cout<<"—————-"<<endl; Doctor d("qiuxiang",2001,'y',3000,"doctor",2001,8,16); d.disdump(); getchar(); return 0;}/*zhaosi2001m—————-liuneng2001x2000—————-qiuxiang2001y3000doctor2001816*/

    As you have already seen, an object can be cast or assigned to its parent class. If the cast or assignment is performed on a plain old object, this results in slicing:

    正如您已經(jīng)看到的,可以強(qiáng)制轉(zhuǎn)換對象或?qū)⑵渲付ńo其父類。如果在普通舊對象上執(zhí)行強(qiáng)制轉(zhuǎn)換或指定,則會導(dǎo)致切片:

    Base myBase = myDerived; // Slicing!

    Slicing occurs in situations like this because the end result is a Base object, and Base objects lack the additional functionality defined in the Derived class. However, slicing does not occur if a derived class is assigned to a pointer or reference to its base class:

    切片發(fā)生在這樣的情況下,因為最終結(jié)果是一個基對象,而基對象缺少派生類中定義的其他功能。但是,如果將派生類分配給指針或?qū)ζ浠惖囊茫瑒t不會發(fā)生切片:

    Base& myBase = myDerived; // No slicing!

    This is generally the correct way to refer to a derived class in terms of its base class, also called upcasting. This is why it’s always a good idea to make your methods and functions take references to classes instead of directly using objects of those classes. By using references, derived classes can be passed in without slicing.

    這通常是根據(jù)派生類的基類(也稱為向上轉(zhuǎn)換)引用派生類的正確方法。這就是為什么讓方法和函數(shù)引用類而不是直接使用這些類的對象總是一個好主意。通過使用引用,可以傳入派生類而無需切片。

    Casting from a base class to one of its derived classes, also called downcasting, is often frowned upon by professional C++ programmers because there is no guarantee that the object really belongs to that derived class, and because downcasting is a sign of bad design. For example, consider the following code:

    從基類到其派生類之一的強(qiáng)制轉(zhuǎn)換(也稱為向下轉(zhuǎn)換)通常受到專業(yè)C++程序員的反對,因為無法保證對象確實(shí)屬于該派生類,而且向下轉(zhuǎn)換是糟糕設(shè)計的標(biāo)志。例如,考慮以下代碼:

    void presumptuous(Base* base){ Derived* myDerived = static_cast(base); // Proceed to access Derived methods on myDerived.}

    If the author of presumptuous() also writes code that calls presumptuous(), everything will probably be okay because the author knows that the function expects the argument to be of type Derived*. However, if other programmers call presumptuous(), they might pass in a Base*. There are no compile-time checks that can be done to enforce the type of the argument, and the function blindly assumes that base is actually a pointer to a Derived.

    如果presumptuous()的作者還編寫了調(diào)用presumptuous()的代碼,那么一切都可能正常,因為作者知道函數(shù)期望參數(shù)的類型是派生的*。然而,如果其他程序員調(diào)用presumptuous(),他們可能會傳入一個Base*。沒有可以執(zhí)行的編譯時檢查來強(qiáng)制參數(shù)的類型,并且函數(shù)盲目地假設(shè)base實(shí)際上是指向派生類型的指針。

    Downcasting is sometimes necessary, and you can use it effectively in controlled circumstances. However, if you are going to downcast, you should use a dynamic_cast(), which uses the object’s built-in knowledge of its type to refuse a cast that doesn’t make sense. This built-in knowledge typically resides in the vtable, which means that dynamic_cast() works only for objects with a vtable, that is, objects with at least one virtual member. If a dynamic_cast() fails on a pointer, the pointer’s value will be nullptr instead of pointing to nonsensical data. If a dynamic_cast() fails on an object reference, an std::bad_cast exception will be thrown.

    向下轉(zhuǎn)型有時是必要的,您可以在受控環(huán)境中有效地使用它。但是,如果要向下轉(zhuǎn)型,則應(yīng)使用dynamic_cast(),它使用對象的內(nèi)置類型知識來拒絕沒有意義的轉(zhuǎn)換。這種內(nèi)置知識通常駐留在vtable中,這意味著dynamic_cast()僅適用于具有vtable的對象,即至少具有一個虛擬成員的對象。如果指針上的dynamic_cast()失敗,指針的值將為nullptr,而不是指向無意義的數(shù)據(jù)。如果對象引用上的動態(tài)dynamic_cast()失敗,將引發(fā)std::bad_cast異常。

    The previous example could have been written as follows:

    前面的示例可以編寫如下:

    void lessPresumptuous(Base* base){ Derived* myDerived = dynamic_cast(base); if(myDerived != nullptr) { // Proceed to access Derived methods on myDerived. }}

    The use of downcasting is often a sign of a bad design. You should rethink and modify your design so that downcasting can be avoided. For example, the lessPresumptuous() function only really works with Derived objects, so instead of accepting a Base pointer, it should simply accept a Derived pointer. This eliminates the need for any downcasting. If the function should work with different derived classes, all inheriting from Base, then look for a solution that uses polymorphism.

    使用向下轉(zhuǎn)型通常是糟糕設(shè)計的標(biāo)志。您應(yīng)該重新考慮和修改您的設(shè)計,以避免向下轉(zhuǎn)型。例如,lessPresumptuous()函數(shù)實(shí)際上只適用于派生對象,因此它不應(yīng)該接受基指針,而應(yīng)該只接受派生指針。這消除了任何向下轉(zhuǎn)型的需要。如果函數(shù)應(yīng)該使用不同的派生類,所有派生類都是從基繼承的,那么請尋找使用多態(tài)性的解決方案。

    ref

    8.3 — Numeric conversions

    Marc Gregoire 《PROFESSIONAL C++》(比)格萊戈爾《C++高級編程 》

    -End-

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年6月13日 18:01
    下一篇 2022年6月13日 18:02

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息