日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

static_cast, dynamic_cast, const_cast学习和探讨

發布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 static_cast, dynamic_cast, const_cast学习和探讨 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一?C語言中存在著兩種類型轉換:

隱式轉換和顯式轉換

隱式轉換:不同數據類型之間賦值和運算,函數調用傳遞參數……編譯器完成

1)?算術轉換(Arithmetic?conversion)?:?在混合類型的算術表達式,?最寬的數據類型成為目標轉換類型。

2)?一種類型表達式賦值給另一種類型的對象:目標類型是被賦值對象的類型

3)?將一個表達式作為實參傳遞給函數調用,此時形參和實參類型不一致:目標轉換類型為形參的類型

4)從一個函數返回一個表達式,表達式類型與返回類型不一致:目標轉換類型為函數的返回類型

顯示轉換:在類型前增加?:(Type)變量?對變量進行的轉換。用戶顯式增加
二?C++中的類型轉換

  通過這兩種方式,C語言中大部分的類型轉換都可以順利進行。至于能不能進行轉換,轉換后的結果如何,編譯器不管需要用戶自己去控制C++繼承了C中的隱式和顯式轉換的方式。但這種轉換并不是安全和嚴格的,加上C++本身對象模型的復雜性,C++增加了四個顯示轉換的關鍵字。(C++是強類型語言)

static_castdynamic_castconst_staticreinterpret_cast

?

//

static_cast:?靜態類型轉換;一般是普通數據類型(int?m=static_cast<int>(3.14));只能提供編譯時的類型安全;一般的轉換,如果你不知道該用哪個,就用這個。

dynamic_cast:?動態類型轉換;一般用在父類和子類指針或應用的互相轉化;可以提供運行時類型安全

const_cast:?常量類型轉換;?cosntvolatile

reinterpret_cast:?重新解釋類型轉換;很像c的一般類型轉換操作性去掉

?

//

static_cast

(1)用于基本的數據類型轉換(charint),及指針之間的轉換

(2)類層次中基類與子類成員函數指針的轉換

(3)類層次結構中基類與子類指針或引用之間的轉換  

上行轉換:子類指針或引用轉換成基類表示——安全

下行轉換:基類指針或引用轉換成子類表示——危險(沒有動態類型檢查)

(4)把空指針轉換成目標類型的空指針。(不安全!!)

(5)把任何類型的表達式轉換成void類型。

?

注意:static_cast不能轉換掉expression_r_rconstvolitale、或者__unaligned屬性。

?

static_cast和普通強制轉換的區別:

//兩個無關類,

B*?pB?=new?B();

A?*pA1?=?(A?*)pB;//正常運行

A?*pA2?=?static_cast<A?*>(pB);//error?C2440:?“static_cast”:?無法從“B?*”轉換為“A?*”

?

static_cast和reinterpret_cast的區別:

C++primer第十五章里寫了編譯器隱式執行任何類型轉換都可由static_cast顯示完成;reinterpret_cast通常為操作數的位模式提供較低層的重新解釋

?

//

dynamic_cast:

用法:dynamic_cast?<?type-id?>?(?expression_r_r?)

該運算符把expression_r_r轉換成type-id類型的對象Type-id必須是類的指針、類的引用或者void?*;如果type-id是類指針類型,那么expression_r_r也必須是一個指針,如果type-id是一個引用,那么expression_r_r也必須是一個引用。

dynamic_cast主要用于類層次間的上行轉換和下行轉換,還可以用于類之間的交叉轉換。在類層次間進行上行轉換時,dynamic_caststatic_cast的效果是一樣的;在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。

?

(1)繼承關系的類指針對象或引用之間轉換

(2)包含有虛函數之間對象指針的轉換

?

除了基類指針指向子類對象,可以沒有虛函數外,其它要進行dynamic_cast轉換必須具有虛函數才行。下面解釋為什么:

虛函數對于dynamic_cast轉換的作用

Dynamic_cast轉換是在運行時進行轉換,運行時轉換就需要知道類對象的信息(繼承關系等)。如何在運行時獲取到這個信息——虛函數表。C++對象模型中,對象實例最前面的就是虛函數表指針,通過這個指針可以獲取到該類對象的所有虛函數,包括父類的。因為派生類會繼承基類的虛函數表,所以通過這個虛函數表,我們就可以知道該類對象的父類,在轉換的時候就可以用來判斷對象有無繼承關系。

所以虛函數對于正確的基類指針轉換為子類指針是非常重要的。

情況1:?static_cast轉換???????

class?A
{
};

class?B:public?A
{
public:
????int?m;?????????//B?成員
};
A*?pObjA?=?new?A();
B*?pObjB??=?NULL;
pObjB?=?static_cast<B*>(pObjA);???//基類指針轉化為子類指針?成功轉換
pObjB->m?=?10;?????????????????//實際中pObj所指向的對象?是A類對象
//上面會發生什么呢,在VC6.0中正常運行。。。?測試結果正常運行
//如果:
pObjB?=?dynamic_cast<B*>(pObjA);?//error?基類A沒有虛函數?不構成多態

?

情況2:?????dynamic_cast轉換????

class?A
{
public:
???virtual?~A(){}?????????//虛函數?多態
};
class?B:public?A
{
public:
????int?m;
};

A*?pObjA?=?new?A();
B*?pObjB??=?NULL;
pObjB?=?dynamic_cast<B*>(pObjA);??//編譯通過?
//實際運行結果:pObjB?==?NULL???//?dynamic_cast保證轉換無效?返回NULL

?

dynamic_cast轉換的安全性

dynamic_cast是動態轉換,只有在基類指針轉換為子類指針時才有意義。(子類指針轉換為基類指針本來就是可以的:基類指針指向子類對象OK)。

?

但是基類指針轉換為子類指針,并不是每一次都有效:只有基類指針本身指向的是一個派生類的對象,然后將此基類指針轉換為對應的派生類指針才是有效的。這種情況在表面上是無法判定的。此時dynamic就發揮了作用。dynamic_cast主要用于類層次間的上行轉換和下行轉換,還可以用于類之間的交叉轉換。在類層次間進行上行轉換時,dynamic_caststatic_cast的效果是一樣的;在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。

?

示例1

class?B

{

public:

int?m_iNum;

virtual?void?foo();

};

?

class?D:public?B

{

public:

char?*m_szName[100];

};

?

void?func(B?*pb)

{

D?*pd1?=?static_cast<D?*>(pb);

D?*pd2?=?dynamic_cast<D?*>(pb);

}

?

在上面的代碼段中:

如果pb指向一個D類型的對象,pd1pd2是一樣的,并且對這兩個指針執行D類型的任何操作都是安全的;但是,如果pb指向的是一個?B類型的對象,那么pd1將是一個指向該對象的指針,對它進行D類型的操作將是不安全的(如訪問m_szName),而pd2將是一個空指針。另外要注意:B要有虛函數,否則會編譯出錯static_cast則沒有這個限制。這是由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表(關于虛函數表的概念,詳細可見<Inside?c++?object?model>)中,只有定義了虛函數的類才有虛函數表,沒有定義虛函數的類是沒有虛函數表的。

?

示例2

另外,dynamic_cast還支持交叉轉換(cross?cast)。如下代碼所示。

class?A

{

public:

int?m_iNum;

virtual?void?f(){}

};

class?B:public?A{};

class?D:public?A{};

?

int?_tmain(int?argc,?_TCHAR*?argv[])

{

B?*pb?=?new?B;

pb->m_iNum?=?100;

D?*pd1?=?static_cast<D?*>(pb);?//編譯出錯?error?C2440:?“static_cast”:?無法從“B?*”轉換為“D?*”

D?*pd2?=?dynamic_cast<D?*>(pb);?//pd2?is?NULL

delete?pb;

?

return?0;

}

使用static_cast進行轉換是不被允許的,將在編譯時出錯;而使用dynamic_cast的轉換則是允許的,結果是空指針。

const_cast

用法:const_cast<type_id>?(expression_r_r)

該運算符用來修改類型的constvolatile屬性。除了const?volatile修飾之外,?type_idexpression_r_r的類型是一樣的。

常量指針被轉化成非常量指針,并且仍然指向原來的對象;常量引用被轉換成非常量引用,并且仍然指向原來的對象;常量對象被轉換成非常量對象。

Voiatileconst類試。舉如下一例:

class?B

{

public:

int?m_iNum;

}

int?_tmain(int?argc,?_TCHAR*?argv[])

{

const?B?b1;

b1.m_iNum?=?100;?//編譯出錯:error?C3892:?“b1”:?不能給常量賦值

B?&b2?=?static_cast<B&>(b1);//編譯出錯:error?C2440:?“static_cast”:?無法從“const?B”轉換為“B?&”

B?b2?=?const_cast<B>(b1);//編譯出錯:error?C2440:?“const_cast”:?無法從“const?B”轉換為“B”

B?&b2?=?const_cast<B&>(b1);

b2.?m_iNum?=?200;?//fine

?

return?0;

}*/

?

來源:為什么需要dynamic_cast強制轉換?
簡單的說,當無法使用virtual函數的時候

典型案例:
Wicrosoft公司提供給我們一個類庫,其中提供一個類Employee.以頭文件Eemployee.h和類庫.lib分發給用戶
顯然我們并無法得到類的實現的源代碼

//Emplyee.h
class?Employee?
{
public:
????virtual?int?salary();
};

class?Manager?:?public?Employee
{
public:?
????int?salary();
};

class?Programmer?:?public?Employee
{
public:
????int?salary();
};


我們公司在開發的時候建立有如下類:

class?MyCompany
{
public:
????void?payroll(Employee?*pe);
????//
};

void?MyCompany::payroll(Employee?*pe)
{
????//do?something
}


但是開發到后期,我們希望能增加一個bonus()的成員函數到W$公司提供的類層次中。
假設我們知道源代碼的情況下,很簡單,增加虛函數:

//Emplyee.h
class?Employee?
{
public:
????virtual?int?salary();
????virtual?int?bonus();
};

class?Manager?:?public?Employee
{
public:?
????int?salary();
};

class?Programmer?:?public?Employee
{
public:
????int?salary();
????int?bonus();
};

//Emplyee.cpp

int?Programmer::bonus()
{
????//
}

payroll()通過多態來調用bonus()

class?MyCompany
{
public:
????void?payroll(Employee?*pe);
????//
};

void?MyCompany::payroll(Employee?*pe)
{
????//do?something
????//pe->bonus();
}


但是現在情況是,我們并不能修改源代碼,怎么辦?dynamic_cast華麗登場了!
Employee.h中增加bonus()聲明,在另一個地方定義此函數,修改調用函數payroll().重新編譯,ok

//Emplyee.h
class?Employee?
{
public:
????virtual?int?salary();
};

class?Manager?:?public?Employee
{
public:?
????int?salary();
};

class?Programmer?:?public?Employee
{
public:
????int?salary();
????int?bonus();//直接在這里擴展
};

//somewhere.cpp

int?Programmer::bonus()
{
????//define
}

?

class?MyCompany
{
public:
????void?payroll(Employee?*pe);
????//
};

void?MyCompany::payroll(Employee?*pe)
{
????Programmer?*pm?=?dynamic_cast<Programmer?*>(pe);
????
????//如果pe實際指向一個Programmer對象,dynamic_cast成功,并且開始指向Programmer對象起始處
????if(pm)
????{
????????//call?Programmer::bonus()
????}
????//如果pe不是實際指向Programmer對象,dynamic_cast失敗,并且pm?=?0
????else
????{
????????//use?Employee?member?functions
????}
}

?

?

?

reinterpret_cast

可以轉換任意一個32bit整數,包括所有的指針和整數。可以把任何整數轉成指針,也可以把任何指針轉成整數,以及把指針轉化為任意類型的指針,

威力最為強大!但不能將非32bit的實例轉成指針。總之,只要是32bit的東東,怎么轉都行!??

*/

?

前言

這篇文章總結的是C++中的類型轉換,這些小的知識點,有的時候,自己不是很注意,但是在實際開發中確實經常使用的。俗話說的好,不懂自己寫的代碼的程序員,不是好的程序員;如果一個程序員對于自己寫的代碼都不懂,只是知道一昧的的去使用,終有一天,你會迷失你自己的。

C++中的類型轉換分為兩種:

  • 隱式類型轉換;
  • 顯式類型轉換。
  • 而對于隱式變換,就是標準的轉換,在很多時候,不經意間就發生了,比如int類型和float類型相加時,int類型就會被隱式的轉換位float類型,然后再進行相加運算。而關于隱式轉換不是今天總結的重點,重點是顯式轉換。在標準C++中有四個類型轉換符:static_cast、dynamic_cast、const_cast和reinterpret_cast;下面將對它們一一的進行總結。

    static_cast

    static_cast的轉換格式:static_cast <type-id> (expression)

    將expression轉換為type-id類型,主要用于非多態類型之間的轉換,不提供運行時的檢查來確保轉換的安全性。主要在以下幾種場合中使用:

  • 用于類層次結構中,基類和子類之間指針和引用的轉換;
    當進行上行轉換,也就是把子類的指針或引用轉換成父類表示,這種轉換是安全的;
    當進行下行轉換,也就是把父類的指針或引用轉換成子類表示,這種轉換是不安全的,也需要程序員來保證;
  • 用于基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum等等,這種轉換的安全性需要程序員來保證;
  • 把void指針轉換成目標類型的指針,是及其不安全的;
  • 注:static_cast不能轉換掉expression的const、volatile和__unaligned屬性。

    dynamic_cast

    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,因為類型提供了運行時信息。下面我將分別在以下的幾種場合下進行dynamic_cast的使用總結:

  • 最簡單的上行轉換
    比如B繼承自A,B轉換為A,進行上行轉換時,是安全的,如下:

    #include <iostream> using namespace std; class A {// ...... }; class B : public A {// ...... }; int main() {B *pB = new B;A *pA = dynamic_cast<A *>(pB); // Safe and will succeed }
  • 多重繼承之間的上行轉換
    C繼承自B,B繼承自A,這種多重繼承的關系;但是,關系很明確,使用dynamic_cast進行轉換時,也是很簡單的:

    class A {// ...... }; class B : public A {// ...... }; class C : public B {// ...... }; int main() {C *pC = new C;B *pB = dynamic_cast<B *>(pC); // OKA *pA = dynamic_cast<A *>(pC); // OK }

    而上述的轉換,static_cast和dynamic_cast具有同樣的效果。而這種上行轉換,也被稱為隱式轉換;比如我們在定義變量時經常這么寫:B *pB = new C;這和上面是一個道理的,只是多加了一個dynamic_cast轉換符而已。

  • 轉換成void *
    可以將類轉換成void *,例如:

    class A { public:virtual void f(){}// ...... }; class B { public: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 ApV = dynamic_cast<void *>(pB); // pV points to an object of B }

    但是,在類A和類B中必須包含虛函數,為什么呢?因為類中存在虛函數,就說明它有想讓基類指針或引用指向派生類對象的情況,此時轉換才有意義;由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表中,只有定義了虛函數的類才有虛函數表。

  • 如果expression是type-id的基類,使用dynamic_cast進行轉換時,在運行時就會檢查expression是否真正的指向一個type-id類型的對象,如果是,則能進行正確的轉換,獲得對應的值;否則返回NULL,如果是引用,則在運行時就會拋出異常;例如: class B {virtual void f(){}; }; class D : public B {virtual void f(){}; }; void main() {B* pb = new D; // unclear but okB* pb2 = new B;D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a DD* pd2 = dynamic_cast<D*>(pb2); // pb2 points to a B not a D, now pd2 is NULL }

    這個就是下行轉換,從基類指針轉換到派生類指針。
    對于一些復雜的繼承關系來說,使用dynamic_cast進行轉換是存在一些陷阱的;比如,有如下的一個結構:
    D類型可以安全的轉換成B和C類型,但是D類型要是直接轉換成A類型呢?

    class 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);

    對于多重繼承的情況,從派生類往父類的父類進行轉時,需要特別注意;比如有下面這種情況:
    現在,你擁有一個A類型的指針,它指向E實例,如何獲得B類型的指針,指向E實例呢?如果直接進行轉的話,就會出現編譯器出現分歧,不知道是走E->C->B,還是走E->D->B。對于這種情況,我們就必須先將A類型的指針進行下行轉換,獲得E類型的指針,然后,在指定一條正確的路線進行上行轉換。

  • 上面就是對于dynamic_cast轉換的一些細節知識點,特別是對于多重繼承的情況,在實際項目中,很容易出現問題。

    const_cast

    const_cast的轉換格式:const_cast <type-id> (expression)

    const_cast用來將類型的const、volatile和__unaligned屬性移除。常量指針被轉換成非常量指針,并且仍然指向原來的對象;常量引用被轉換成非常量引用,并且仍然引用原來的對象。看以下的代碼例子:

    /* ** FileName : ConstCastDemo ** Author : Jelly Young ** Date : 2013/12/27 ** Description : More information, please go to http://www.jellythink.com */ #include <iostream> using namespace std; class CA { public:CA():m_iA(10){}int m_iA; }; int main() {const CA *pA = new CA;// pA->m_iA = 100; // ErrorCA *pB = const_cast<CA *>(pA);pB->m_iA = 100;// Now the pA and the pB points to the same objectcout<<pA->m_iA<<endl;cout<<pB->m_iA<<endl;const CA &a = *pA;// a.m_iA = 200; // ErrorCA &b = const_cast<CA &>(a);pB->m_iA = 200;// Now the a and the b reference to the same objectcout<<b.m_iA<<endl;cout<<a.m_iA<<endl; }

    注:你不能直接對非指針和非引用的變量使用const_cast操作符去直接移除它的const、volatile和__unaligned屬性。

    reinterpret_cast

    reinterpret_cast的轉換格式:reinterpret_cast <type-id> (expression)

    允許將任何指針類型轉換為其它的指針類型;聽起來很強大,但是也很不靠譜。它主要用于將一種數據類型從一種類型轉換為另一種類型。它可以將一個指針轉換成一個整數,也可以將一個整數轉換成一個指針,在實際開發中,先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還可以得到原來的指針值;特別是開辟了系統全局的內存空間,需要在多個應用程序之間使用時,需要彼此共享,傳遞這個內存空間的指針時,就可以將指針轉換成整數值,得到以后,再將整數值轉換成指針,進行對應的操作。

    總結

    這篇博文總結了C++中的類型轉換,重點總結了其中的顯式轉換。對于C++支持的這四種顯式轉換都進行了詳細的描述。如果大家有什么補充的,或者我總結的有誤的地方,請大家多多指教。

    聽聞雷哥在北京面試不順,公司對數據結構和算法都要求很多,過段日子準備再將數據結構和算法進行整理一下。

    2013年12月27日 于大連,東軟。

    總結

    以上是生活随笔為你收集整理的static_cast, dynamic_cast, const_cast学习和探讨的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。