C++中四種類型轉換以及const_cast是否能改變常量的問題


we have four specific casting operators:dynamic_castreinterpret_caststatic_cast and const_cast. Their format is to follow the new type enclosed between angle-brackets (<>) and immediately after, the expression to be converted between parentheses.

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


一、對C++中四種類型轉換總結如下:
const_cast<T>(expr)
用來移除對象的常量性(cast away the constness)
const_cast一般用於指針或者引用
使用const_cast去除const限定的目的不是為了修改它的內容
使用const_cast去除const限定,通常是為了函數能夠接受這個實際參數

static_cast<T>(expr)
編譯器隱式執行的任何類型轉換都可以由static_cast完成
當一個較大的算術類型賦值給較小的類型時,可以用static_cast進行強制轉換。

可以將void*指針轉換為某一類型的指針

可以將基類指針強制轉換為派生類指針,但是不安全。
無法將const轉化為nonconst,這個只有const_cast才可以辦得到

reinterpret_cast<T>(expr)
“通常為操作數的位模式提供較低層的重新解釋”也就是說將數據以二進制存在形式的重新解釋。
int i;
char *p = "This is a example.";
i = reinterpret_cast<int>(p);
//此時結果,i與p的值是完全相同的。

int *ip
char *pc = reinterpret_cast<char*>(ip);
// 程序員需要記得pc所指向的真實對象是int型,並非字符串。
// 如果將pc當作字符指針進行操作,可能會造成運行時錯誤
// 如int len = strlen(pc);
多重繼承時reinterpret_cast不安全。static_cast會根據父子類指針轉換的偏移量轉換到正確地址,而reinterpret_cast不會。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
#include <cassert>
#include <iostream>
using namespace std;

class A
{
public:
    int m_a;
};

class B
{
public:
    int m_B;
};

class C: public A, public B
{

};

int main(void)
{

    C c;
    cout << (void *)&c << endl;
    cout << (void *)reinterpret_cast<B *>(&c) << endl;

    cout << (void *)static_cast<B *>(&c) << endl;

    return 0;
}



dynamic_cast<T>(expr)
執行“安全向下”轉型操作,也就是說支持運行時識別指針或所指向的對象,這是唯一個無法用舊式語來進行的轉型操作。

dynamic_cast可謂是最嚴格的轉換,static_cast次之,而reinterpret_cast則是最寬松的。如果你遇到不能將整型轉變為函數指針的問題,你可以這樣解決:
reinterpret_cast<LPFUN&>(nAddress);
注意LPFUN這里有個“&”符號,表示引用,C++的引用其實就是用指針實現的,而這些“轉換”其實都是指針的轉換,所以加上引用符號編譯才能通過。

 

二、也許大家都有過這樣的疑惑:const_cast可以去除一個常量的const屬性,去除const屬性后應該可以對“常量”進行修改,通過調試器發現內存中的值是被改變的,可是再傳遞這個“常量”的時候,值卻一直保持原狀,實在古怪,在Windows下用VC、嘗試如此,在Linux下用g++嘗試也如此,我原先以為和編譯器的優化選項有關系,把所有優化選項關閉,照樣沒用,為什么?

寫了個程序進行測試:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 
#include <iostream>
using namespace std;

void Fun(int &value)
{
    cout << "Fun(val) = " << value << endl;
}

int main(void)
{
    const int val = 100;

    int *ptr = const_cast<int *>(&val);
    *ptr = 200;
    cout << &val << endl;
    cout << ptr << endl;
    cout << "val = " << val << endl;
    cout << "*ptr = " << *ptr << endl;


    int &ref = const_cast<int &>(val);
    ref = 300;
    cout << "val = " << val << endl;
    cout << "ref = " << ref << endl;

    Fun(const_cast<int &>(val));

    return 0;
}

輸出為:


可以看出打印的地址是一樣的,而且奇怪的是val還是等於100,而通過*ptr打印出來的卻是更改后的200,再者Fun函數打印的是300,即被引用再次修改了一次,在打印語句附近設置斷點並調試反匯編,截取一段如下圖:


可以明顯地看出系統是對val這個const進行了預處理般的替換,將它替換成“64h”(十六進制的64就是十進制的100),即在編譯生成的指令中val就已經被替換成100了,其實加const只是告訴編譯器不能修改而不是真正地不可修改,如果程序員不注意而去修改了它會報錯,現在我們利用const_cast去除了常量性,然后通過指針和引用對其進行了修改,所以通過指針打印或者引用傳參的時候就能看出其內存確實變化了,但為了保護val這個變量本來的const特性,所以每次我們使用val時,系統都將其替換成初始值100,確保了val還是“不可變”的。記住,只有當const限定符加在已經初始化的全局變量前面的時候,此時變量處在.rodata段(linux下),才是真正的不可修改,否則通過指針都是可以修改的,雖然編譯過程中會產生警告。

在linux下測試也是同樣的輸出結果:



附錄:

MSDN上關於四種cast的說明:

reinterpret_cast Operator

The reinterpret_cast operator allows any pointer to be converted into any other pointer type. It also allows any integral type to be converted into any pointer type and vice versa. Misuse of the reinterpret_cast operator can easily be unsafe. Unless the desired conversion is inherently low-level, you should use one of the other cast operators.


dynamic_cast Operator

The expression dynamic_cast( expression ) converts the operand expression to an object of type type-id. The type-id must be a pointer or a reference to a previously defined class type or apointer to void. The type of expression must be a pointer if type-id is a pointer, or an l-value if type-id is a reference.


static_cast Operator

The expression static_cast < type-id > ( expression ) converts expression to the type of type-id based solely on the types present in the expression. No run-time type check is made to ensure the safety of the conversion.


const_cast operator

The const_cast operator can be used to remove the const, volatile, and __unaligned attribute(s) from a class.


參考:http://blog.csdn.net/guogangj/archive/2007/03/29/1545119.aspx

 

 

 

 

C++的const_cast的問題

 

 

和上一篇文章一樣了,還是提起一下大約一年前我來公司面試所遇到的一道題目,題目很簡單:C++有多少種cast,它們的名稱和功能各是什么。(我之前的文章曾經提到過,但后來我發現自己寫得並不夠簡明)答案如下:

 

 

一共四種cast。

1、static_cast,支持子類指針到父類指針的轉換,並根據實際情況調整指針的值,反過來也支持,但會給出編譯警告,它作用最類似C風格的“強制轉換”,一般來說可認為它是安全的;

2、dynamic_cast,支持子類指針到父類指針的轉換,並根據實際情況調整指針的值,和static_cast不同,反過來它就不支持了,會導致編譯錯誤,這種轉換是最安全的轉換;

3、reinterpret_cast,支持任何轉換,但僅僅是如它的名字所描述的那樣“重解釋”而已,不會對指針的值進行任何調整,用它完全可以做到“指鹿為馬”,但很明顯,它是最不安全的轉換,使用它的時候,你得頭腦清醒,知道自己在干什么;

4、const_cast,這個轉換能剝離一個對象的const屬性,也就是說允許你對常量進行修改。

 

 

 

這樣回答即使得不了滿分,拿個八九十分應該也沒問題了,我后來還專門寫了些測試程序來驗證過,對於第一第二第三種轉換都沒什么問題,而const_cast卻似乎不能正常工作,代碼如下:

 

int main(int argc, char* argv[])

{

     const int ic=100;

     const_cast<int &>(ic)=200;

     printf("%d/n", ic);

     return 0;

}

 

結果不是我期待的200,而是100,我一開始以為這是由於優化選項的問題,於是調整編譯器選項,全部不優化,但還是一樣的結果,我開始懷疑這是VC++的bug,於是使用Linux的g++來編譯,結果一樣,看來這和編譯器沒有關系,那我究竟做錯了哪里呢?或者我對const_cast理解錯了呢?我開始改進我的代碼,我嘗試不同的類型:

 

class CTest

{

public:

     CTest(int i){m_val = i;printf("construction [%d]/n", m_val);};

     ~CTest(){printf("destruction/n");};

     void SelfAdd(){m_val++;};

     int m_val;

};

 

int main(int argc, char* argv[])

{

     const CTest test(1000);

     CTest test2(1050);

     const_cast<CTest &>(test)= test2;

     printf("%d/n", test.m_val);

     return 0;

}

 

這次總算得到了我想要得到結果,打印出了1050,說明const_cast並沒有問題,但前一個程序為什么不能正常工作?我繼續嘗試了不同的類型,比如char,short,float,double等,發現了規律,凡是對結構體或類進行這個轉換,都是成功的,但對char,short等基本類型的轉換都沒有成功。我進一步改進代碼,為了查看它們的值是否真的已經被修改,我使用了指針:

 

int main(int argc, char* argv[])

{

     const int ic = 100;

     const int *pc=&ic;

     const_cast<int &>(ic)++;

     printf("%d,%d/n", ic, *pc);

     return 0;

}

 

這次打印出來的結果是“100,101”,這就說明常量ic的值確實已經被改變了,但為什么直接打印ic就得不到正確的結果?那估計還是前邊想到的“優化”的原因,可我一再確認我並沒有使用優化編譯選項,而且g++的表現也如此。看來只好使用最后一招了,直接查看printf究竟做了些什么,在printf處設置斷點,調試程序,然后打開disassembly視圖查看反匯編代碼,一切真相大白。

原來雖然我沒有使用優化,但系統還是對ic這個const進行了預編譯般的替換,將它替換成“64h”(十六進制的64就是十進制的100),這究竟是不是C++的規范?我不知道,但我肯定這不是一般用戶想要的結果,對我來說,算是個C++的bug吧。通過解決這個問題,我也學會了些東西,如果以后遇到類似這種表面上再顯淺不過,但就是不能正常工作的代碼片斷,要學會查看反匯編代碼,也許一切問題迎刃而解,另外使用const_cast的時候應該注意些什么東西,嗯,自己思考一下吧。Java沒有const_cast,很多語言都沒有,(我只知道C++有)既然已經被定義為常量,就是不希望它被改變,但現在又允許你改變它,這不是很可笑嗎?但難道它不是C++強大又靈活的又一體現嗎?不過話說回來要看你怎么用了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM