国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

c++教程(二十二:Type conversions)

2019-11-11 00:10:49
字體:
來源:轉載
供稿:網友

————————————————————————

該系列教程為翻譯c++官方教程,點擊參考英文原版,水平有限,翻譯不通之處敬請諒解!

————————————————————————

隱式轉換

當將值復制到兼容類型時,將自動執行隱式轉換。例如:

short a=2000;int b;b=a;

在這里,a的值被從short轉換到int而沒有任何明確的操作。這就是所謂的標準轉換。標準轉換的影響基本數據類型,并允許數值類型之間的轉換(short到int,int到float,double到int…),或從bool,和一些指針的轉換。

從一些較小的整數類型轉換為int,或從float到double稱為擴展(PRomotion),保證在目的類型產生完全相同的值。其他算術類型之間的轉換并不總是能夠準確地表示相同的值:

(1)如果一個負整數的值轉換為無符號類型,所產生的值相當于其補數表示。(即,-1成為表示該類型的最大值,-2是第二大,…)。 (2)bool類型轉換例如flase,就可以相當于零(對于數值類型)或者空指針(指針類型);true相當于所有其他的值轉換為等效的1。 (3)如果轉換是從浮點類型到整型,則值被截斷(小數部分被刪除)。如果結果超出表示值類型范圍,則導致未定義的行為。 (4)否則,如果轉換介于同類型(int到int或float到float)的數值類型之間,則轉換是有效的,但值是具體實現的(并且可能不可移植)。

這些轉換可能意味著精度的損失,編譯器可能發出警告信號。這個警告可以避免轉換。

對于非基本類型,數組和函數隱式地轉換為指針,而指針一般允許下列轉換: (1)空指針可以轉換為任何類型的指針 (2)指向任何類型的指針可以轉換為空指針。 (3)指針向上:一個派生類指針可以轉換為一個accessible 和unambiguous 基類的指針,而無需修改其const或volatile的屬性。

隱式類轉換

在類的世界里,隱式轉換可以通過三個成員函數來控制: (1)單參數構造函數:允許從特定類型的隱式轉換初始化對象。 (2)賦值運算符:允許在賦值時從特定類型允許隱式轉換。 (3)類型轉換運算符:允許隱式轉換為特定類型。

例如:

// implicit conversion of classes:#include <iostream>using namespace std;class A {};class B {public: // conversion from A (constructor): B (const A& x) {} // conversion from A (assignment): B& Operator= (const A& x) {return *this;} // conversion to A (type-cast operator) operator A() {return A();}};int main (){ A foo; B bar = foo; // calls constructor bar = foo; // calls assignment foo = bar; // calls type-cast operator return 0;}

類型轉換運算符使用特定語法:它使用運算符關鍵字后跟目標類型和空括號集。注意,返回類型是目標類型,因此在運算符關鍵字之前沒有指定。

關鍵詞顯示

在一個函數調用中,C++允許隱式轉換發生在每個參數中。這對于某些類可能是有些問題的,因為它并不總是它想做的。例如,如果我們在最后一個例子中添加下面的函數:

void fn (B arg) {}

這個函數需要類型B的參數,但它也可以用類型A的對象作為參數調用:

fn (foo);

這可能是或可能不是想要實現的。但是,在任何情況下,可以通過顯式關鍵字標記受影響的構造函數來防止:

// explicit:#include <iostream>using namespace std;class A {};class B {public: explicit B (const A& x) {} B& operator= (const A& x) {return *this;} operator A() {return A();}};void fn (B x) {}int main (){ A foo; B bar (foo); bar = foo; foo = bar;// fn (foo); // not allowed for explicit ctor. fn (bar); return 0;}

此外,顯式標記的構造函數不能用賦值語法來調用;在上面的示例中,不能用:

B bar = foo;

類型的成員函數(上一節中描述的)也可以被指定為顯式。這可以防止與目標類型的顯式指定構造函數一樣的隱式轉換。

強制類型轉換

C++是強類型語言。許多的轉換,特別是那些隱含的值不同的解釋,需要顯式轉換,在C++中稱為type-casting。有兩種通用type-casting主要語法:functional 和c-like:

double x = 10.3;int y;y = int (x); // functional notationy = (int) x; // c-like cast notation

這些通用類型的強制轉換功能是足以滿足基本數據類型的大多數需求。然而,這些操作符可以套用在類和類的指針,從而導致代碼在語法正確但是會導致運行時錯誤的情況。例如,下面的代碼沒有編譯錯誤:

// class type-casting#include <iostream>using namespace std;class Dummy { double i,j;};class Addition { int x,y; public: Addition (int a, int b) { x=a; y=b; } int result() { return x+y;}};int main () { Dummy d; Addition * padd; padd = (Addition*) &d; cout << padd->result(); return 0;}

程序聲明了一個指向加法的指針,但是它使用顯式類型賦值將它分配給另一個無關類型的對象的引用:

padd = (Addition*) &d;

不受限制的顯式類型轉換允許將任何指針轉換為其他指針類型,而獨立于它們指向的類型。對成員結果的后續調用將產生運行時錯誤或其他一些意想不到的結果。

為了控制這些類型之間的相互轉換的類,我們有四個具體的casting 操作: dynamic_cast,reinterpret_cast,static_cast和const_cast。它們的格式是按照新類型的括號括在角度括號之間,然后在括號之間轉換表達式。

dynamic_cast <new_type> (expression)reinterpret_cast <new_type> (expression)static_cast <new_type> (expression)const_cast <new_type> (expression)

傳統類型type-casting 等價于這些表達式將:

(new_type) expressionnew_type (expression)

但每一個有其特殊的特點:

dynamic_cast

dynamic_cast只能使用指針和引用的類(或void *)。其目的是確保類型轉換的結果指向目標指針類型的有效完整對象。

這自然包括 pointer upcast(從指針的指針轉換指針的基源),同樣允許隱式轉換。

但dynamic_cast也可以downcast,(將指針指向派生基地)多態類(那些虛擬成員)當且僅當指向的對象是一個有效的完整的目標對象類型。例如:

// dynamic_cast#include <iostream>#include <exception>using namespace std;class Base { virtual void dummy() {} };class Derived: public Base { int a; };int main () { try { Base * pba = new Derived; Base * pbb = new Base; Derived * pd; pd = dynamic_cast<Derived*>(pba); if (pd==0) cout << "Null pointer on first type-cast./n"; pd = dynamic_cast<Derived*>(pbb); if (pd==0) cout << "Null pointer on second type-cast./n"; } catch (exception& e) {cout << "Exception: " << e.what();} return 0;}

兼容性注意:這種類型的dynamic_cast需要運行時類型信息(RTTI)跟蹤動態類型。一些編譯器支持此功能作為默認禁用的選項。這需要運行時進行類型檢查使得dynamic_cast與這些類型能正常工作。

上面的代碼試圖執行從類型 base*指針的對象動態模型(PBA和PBB)到另一種指針類型Derived*派生的對象的兩種dynamic_cast,但只有第一個是成功的。注意各自的初始化:

Base * pba = new Derived;Base * pbb = new Base;

雖然都是類型base*指針,PBA其實指向類型派生的對象,而pbb 指向的是Base類型。因此,當各自的類型轉換都進行dynamic_cast,pba 是指向一個完整的對象派生類Derived,而pbb 是指向基類Base的對象,這是一個不完整的對象派生類。

當dynamic_cast不能使用指針因為它不是一個完整的對象所需的類,而在前面的例子中第二轉換,它返回一個空指針來表明是失敗的。dynamic_cast用來轉換為引用類型的轉換是不可能的,反而是bad_cast拋出的異常類型。

dynamic_cast也可以允許在指針中執行其他隱式強制轉換:casting 之間的指針類型的空指針(即使在不相關的類中),和casting 任何指針任何類型void*指針。

static_cast

static_cast能在指針之間的相互轉換相關的類,不僅upcasts(從pointer-to-derived到pointer-to-base),而且downcasts 強制類型轉換(從pointer-to-base到pointer-to-derived)。在運行時不執行檢查,以確保正在轉換的對象實際上是目標類型的完整對象。因此,它是由程序 員來確保轉換是安全的。另一方面,它不承擔對dynamic_cast類型安全檢查的開銷。

class Base {};class Derived: public Base {};Base * a = new Base;Derived * b = static_cast<Derived*>(a);

這是有效的代碼,雖然b指向的類的一個對象的引用,如果不完全,還可能導致運行時錯誤。 因此,static_cast能夠執行類的指針不僅可以隱式轉換,而且其相反的轉換。

static_cast也能夠執行所有的轉換允許隱式(不只是那些類的指針),也能完成這些相反的。它可以: (1)從void*轉換為任何指針類型。在這種情況下,它保證如果從相同的指針類型轉換為void*值,得到的指針值是相同的。 (2)轉換為整數,浮點值和枚舉類型的枚舉類型。

此外,static_cast還可以執行以下: (1)顯式調用單個參數構造函數或轉換運算符。 (2)轉換為右值引用。 (3)將枚舉類的值轉換為整數或浮點值。 (4)將任何類型轉換為void,評估和丟棄這個值。

reinterpret_cast

reinterpret_cast是轉換任何指針類型的任何其他類型的指針,甚至不相關的類。操作結果是從一個指針到另一個指針的簡單二進制復制。允許所有指針轉換:既不指向指針的內容,也不檢查指針類型本身。 它也可以將指針轉換為整數類型。該整數值表示指針的格式是環境所特定的。唯一可以肯定的是,指針轉換到一個大到足以完全包含它的整數類型(如intptr_t),保證可以追溯到一個有效的指針。 可以由reinterpret_cast但不能由static_cast轉換的類型的二進制表示是低級操作的轉換,這在大多數情況下,結果是系統特定的代碼,因而非便攜式。例如:

class A { /* ... */ };class B { /* ... */ };A * a = new A;B * b = reinterpret_cast<B*>(a);

編譯這個代碼,雖然它沒有多大意義,因為現在b指向一個完全不相關和可能不相容類的對象。引用b是不安全的。

const_cast

這類casting 操縱對象所指的常量指針,或者被設置或者被刪除。例如,為了通過const函數的指針,預設為非const參數:

// const_cast#include <iostream>using namespace std;void print (char * str){ cout << str << '/n';}int main () { const char * c = "sample text"; print ( const_cast<char *> (c) ); return 0;}

上面的例子保證工作,因為函數print 不向指針對象寫入。需要注意的是,這消除指針對象從而寫入它導致未定義的行為。

typeid

typeid 可以用于到檢查的表達式的類型:

typeid (expression)

這個操作符返回一個在標準頭文件typeinfo中定義的到一個對象類型的type_info型類型信息。typeid返回的值可以與另一個typeid操作符==和!= 對比,或者可以得到一個空終止字符序列,利用其name()成員表示數據類型或類的名字。

// typeid#include <iostream>#include <typeinfo>using namespace std;int main () { int * a,b; a=0; b=0; if (typeid(a) != typeid(b)) { cout << "a and b are of different types:/n"; cout << "a is: " << typeid(a).name() << '/n'; cout << "b is: " << typeid(b).name() << '/n'; } return 0;}結果為a and b are of different types:a is: int *b is: int

當typeid應用于類,typeid使用RTTI來跟蹤動態對象的類型。當typeid用于表達式的類型是類的多態性,結果是大部分來自完整的對象類型:

// typeid, polymorphic class#include <iostream>#include <typeinfo>#include <exception>using namespace std;class Base { virtual void f(){} };class Derived : public Base {};int main () { try { Base* a = new Base; Base* b = new Derived; cout << "a is: " << typeid(a).name() << '/n'; cout << "b is: " << typeid(b).name() << '/n'; cout << "*a is: " << typeid(*a).name() << '/n'; cout << "*b is: " << typeid(*b).name() << '/n'; } catch (exception& e) { cout << "Exception: " << e.what() << '/n'; } return 0;}

注意:type_info成員的name字符串返回的名稱取決于你的編譯器和庫的具體實現。它不一定是一個具有典型類型名稱的簡單字符串,就像在編譯器中用來生成這個輸出一樣。

注意typeid認為指針的類型是指針類型本身(A和B的類型都是class Base )。然而,當typeid用于對象(如*a和 b)typeid得到的就是的動態類型(即他們的大部分來自完整的對象類型)。

如果typeid測試的類型是在解引用操作符(*)前的指針,這個指針有一個空值,那么typeid拋出一個異常bad_typeid。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 民乐县| 侯马市| 论坛| 西平县| 南京市| 寻乌县| 伊川县| 辽阳县| 蒙自县| 韶关市| 上思县| 哈巴河县| 德惠市| 射阳县| 兴仁县| 昆山市| 宁安市| 宣汉县| 墨竹工卡县| 浪卡子县| 六安市| 西平县| 金昌市| 铁力市| 应城市| 炉霍县| 兴仁县| 阿勒泰市| 安图县| 重庆市| 滦平县| 合江县| 雷波县| 长乐市| 房产| 枣庄市| 隆尧县| 永修县| 砚山县| 民勤县| 攀枝花市|