真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

聊聊 C++ 中的四種類型轉(zhuǎn)換符

一:背景

在玩 C 的時(shí)候,經(jīng)常會(huì)用 void* 來(lái)指向一段內(nèi)存地址開端,然后再將其強(qiáng)轉(zhuǎn)成尺度更小的 char*int* 來(lái)丈量一段內(nèi)存,參考如下代碼:

站在用戶的角度思考問題,與客戶深入溝通,找到楚雄州網(wǎng)站設(shè)計(jì)與楚雄州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、主機(jī)域名、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋楚雄州地區(qū)。


int main()
{
	void* ptr = malloc(sizeof(int) * 10);

	int* int_ptr = (int*)ptr;
	char* char_ptr = (char*)ptr;
}

由于 C 的自由度比較大,想怎么玩就怎么玩,帶來(lái)的弊端就是容易隱藏著一些不易發(fā)現(xiàn)的bug,歸根到底還是程序員的功底不扎實(shí),C++ 設(shè)計(jì)者覺得不能把程序員想的太厲害,應(yīng)該要力所能及的幫助程序員避掉一些不必要的潛在 bug,并且還要盡最大努力的避免對(duì)性能有過多的傷害,所以就出現(xiàn)了 4 個(gè)強(qiáng)制類型轉(zhuǎn)換運(yùn)算符。

  1. const_cast
  2. reinterpret_cast
  3. dynamic_cast
  4. static_cast

既然 C++ 做了歸類,必然就有其各自用途,接下來(lái)我們逐一和大家聊一下。

二:理解四大運(yùn)算符

1. const_cast

這是四個(gè)運(yùn)算符中最好理解的,玩過 C++ 的都知道,默認(rèn)情況下是不能修改一個(gè) const 變量,比如下面這樣:


int main()
{
	const int i = 10;
	i = 12;
}

這段代碼肯定是要報(bào)錯(cuò)的,那如果我一定要實(shí)現(xiàn)這個(gè)功能,如何做呢?這就需要用到 const_cast 去掉它的常量符號(hào),然后對(duì) i 進(jìn)行操作即可,所以修改代碼如下:


int main()
{
	const int i = 10;
	auto j = const_cast(&i);
	*(j) = 12;
}

2. reinterpret_cast

從名字上看就是一個(gè) 重新解釋轉(zhuǎn)換,很顯然這個(gè)非常底層,如果大家玩過 windbg ,應(yīng)該知道用 dt 命令可以將指定的內(nèi)存地址按照某一個(gè)結(jié)構(gòu)體丈量出來(lái),比如說(shuō) C# 的 CLR 在觸發(fā) GC 時(shí),會(huì)有 gc_mechanisms 結(jié)構(gòu),參考代碼如下:


0:000> dt WKS::gc_mechanisms 0x7ffb6ba96e60
coreclr!WKS::gc_mechanisms
   +0x000 gc_index         : 1
   +0x008 condemned_generation : 0n0
   +0x00c promotion        : 0n0
   +0x010 compaction       : 0n1
   +0x014 loh_compaction   : 0n0
   +0x018 heap_expansion   : 0n0
   +0x01c concurrent       : 0
   +0x020 demotion         : 0n0
   +0x024 card_bundles     : 0n1
   +0x028 gen0_reduction_count : 0n0
   +0x02c should_lock_elevation : 0n0
   +0x030 elevation_locked_count : 0n0
   +0x034 elevation_reduced : 0n0
   +0x038 minimal_gc       : 0n0
   +0x03c reason           : 0 ( reason_alloc_soh )
   +0x040 pause_mode       : 1 ( pause_interactive )
   +0x044 found_finalizers : 0n0
   +0x048 background_p     : 0n0
   +0x04c b_state          : 0 ( bgc_not_in_process )
   +0x050 allocations_allowed : 0n1
   +0x054 stress_induced   : 0n0
   +0x058 entry_memory_load : 0
   +0x05c exit_memory_load : 0

其實(shí) reinterpret_cast 大概也是干這個(gè)事的,參考代碼如下:


typedef struct _Point {
	int x;
	int y;
} Point;

int main()
{
	Point point = { 10,11 };

	//內(nèi)存地址
	void* ptr = &point;

	//根據(jù)內(nèi)存地址 丈量出  Point
	Point* ptr_point = reinterpret_cast(ptr);

	printf("x=%d", ptr_point->x);
}

從代碼看,我直接根據(jù) ptr 地址丈量出了 Point 結(jié)構(gòu),說(shuō)實(shí)話這個(gè)和 C 玩法就比較類似了。

3. dynamic_cast

在多態(tài)場(chǎng)景下,有時(shí)候會(huì)遇到這樣的一個(gè)問題,一個(gè)父類有多個(gè)子類,我現(xiàn)在手擁一個(gè)父類,我不知道能不能將它轉(zhuǎn)換為其中一個(gè)子類,要試探一下看看,那怎么去試探呢? 類似 C# 中的 as 運(yùn)算符,在 C++ 中就需要用 dynamic_cast 來(lái)做這件事情,參考如下:


//點(diǎn)
class Point {
public:
	Point(int x, int y) :x(x), y(y) {}
	virtual void show() {}
public:
	int x;
	int y;
};

//矩形
class Rectangle :public Point {
public:
	Rectangle(int x, int y, int w, int h) : Point(x, y), w(w), h(h) {}
public:
	int w;
	int h;
};

//三角形
class Triangle :public Point {
public:
	Triangle(int x, int y, int z) :Point(x, y), z(z) {}
public:
	int z;
};

int main()
{
	Point* p1 = new Rectangle(10, 20, 100, 200);
	Point* p2 = new Triangle(4, 5, 6);

	//將  p1 轉(zhuǎn)成 子類 Triangle 會(huì)報(bào)錯(cuò)的
	Triangle* t1 = dynamic_cast(p1);

	if (t1 == nullptr) {
		printf("p1 不能轉(zhuǎn)成 Triangle");
	}
}

對(duì),場(chǎng)景就是這個(gè),p1 其實(shí)是 Rectangle 轉(zhuǎn)上去的, 這時(shí)候你肯定是不能將它向下轉(zhuǎn)成 Triangle , 問題就在這里,很多時(shí)候你并不知道此時(shí)的 p1 是哪一個(gè)子類。

接下來(lái)的一個(gè)問題是,C++ 并不像C# 有元數(shù)據(jù),那它是如何鑒別呢? 其實(shí)這用了 RTTI 技術(shù),哪里能看出來(lái)呢?哈哈,看匯編啦。


	Triangle* t1 = dynamic_cast(p1);
00831D57  push        0  
00831D59  push        offset Triangle `RTTI Type Descriptor' (083C150h)  
00831D5E  push        offset Point `RTTI Type Descriptor' (083C138h)  
00831D63  push        0  
00831D65  mov         eax,dword ptr [p1]  
00831D68  push        eax  
00831D69  call        ___RTDynamicCast (0Bh)  
00831D6E  add         esp,14h  
00831D71  mov         dword ptr [t1],eax  

從匯編可以看到編譯器這是帶夾私貨了,在底層偷偷的調(diào)用了一個(gè) ___RTDynamicCast 函數(shù)在運(yùn)行時(shí)幫忙檢測(cè)的,根據(jù) cdcel 調(diào)用協(xié)定,參數(shù)是從右到左,恢復(fù)成代碼大概是這樣。


___RTDynamicCast(&p1, 0, &Point, &Triangle,0)

3. static_cast

從名字上就能看出,這個(gè)強(qiáng)轉(zhuǎn)具有 static 語(yǔ)義,也就是 編譯階段 就生成好了,具體安全不安全,它就不管了,就拿上面的例子,將 dynamic_cast 改成 static_cast 看看有什么微妙的變化。


int main()
{
	Point* p1 = new Rectangle(10, 20, 100, 200);
	Point* p2 = new Triangle(4, 5, 6);

	Triangle* t1 = static_cast(p1);

	printf("x=%d, y=%d,z=%d", t1->x, t1->y, t1->z);
}

我們發(fā)現(xiàn)居然轉(zhuǎn)成功了,而且 Triangle 的值也是莫名奇怪,直接取了 Rectangle 的前三個(gè)值,如果這是生產(chǎn)代碼,肯定要挨批了。。。

接下來(lái)簡(jiǎn)單看下匯編代碼:


	Triangle* t1 = static_cast(p1);
00DF5B17  mov         eax,dword ptr [p1]  
00DF5B1A  mov         dword ptr [t1],eax  

	printf("x=%d, y=%d,z=%d", t1->x, t1->y, t1->z);
00DF5B1D  mov         eax,dword ptr [t1]  
00DF5B20  mov         ecx,dword ptr [eax+0Ch]  
00DF5B23  push        ecx  
00DF5B24  mov         edx,dword ptr [t1]  
00DF5B27  mov         eax,dword ptr [edx+8]  
00DF5B2A  push        eax  
00DF5B2B  mov         ecx,dword ptr [t1]  
00DF5B2E  mov         edx,dword ptr [ecx+4]  
00DF5B31  push        edx  
00DF5B32  push        offset string "x=%d, y=%d,z=%d" (0DF8C80h)  
00DF5B37  call        _printf (0DF145Bh)  
00DF5B3C  add         esp,10h

從代碼中看,它其實(shí)就是將 p1 的首地址給了 t1,然后依次把copy偏移值 +4,+8,+0C, 除了轉(zhuǎn)換這個(gè),還可以做一些 int ,long ,double 之間的強(qiáng)轉(zhuǎn),當(dāng)然也是一樣,編譯時(shí)匯編代碼就已經(jīng)生成好了。

好了,本篇就說(shuō)這么多,希望對(duì)你有幫助。


網(wǎng)站欄目:聊聊 C++ 中的四種類型轉(zhuǎn)換符
新聞來(lái)源:http://weahome.cn/article/dsoijdi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部