C++學習筆記(十一):void*指針、類型轉換和動態內存分配


void*指針

void關鍵字表示“空類型”的概念。但是,這里的“空類型”不表示“任意類型”,而是表示不存在的意思,也就是說C/C++不允許你寫語句void a,不存在類型為void的東西.

void*表示“空類型指針”,與void不同,void*表示“任意類型的指針”或表示“該指針與一地址值相關,但是不清楚在此地址上的對象的類型”。

 

類型轉換

C風格轉換:

1 int i;
2 double d;
3 
4 i = (int) d;
5 //
6 i = int (d);

C風格轉換在C++中是適用的。但是C++也提供了4種轉換方法。

那為什么還需要一個新的C++類型的強制轉換呢?

新類型的強制轉換可以提供更好的控制強制轉換過程,允許控制各種不同種類的強制轉換。C++中風格是static_cast<type>(content)。C++風格的強制轉換其他的好處是,它們能更清晰的表明它們要干什么。程序員只要掃一眼這樣的代碼,就能立即知道一個強制轉換的目的。

C++轉換:

static_cast(exp)

用於相關類型之間的轉換,諸如:在同一個類的繼承層次關系中,向上或向下轉換;枚舉類型與整數類型之間的轉換;浮點類型與指數類型之間的轉換。

static_cast它能在內置的數據類型間互相轉換,對於類只能在有聯系的指針類型間進行轉換。可以在繼承體系中把指針轉換來、轉換去,但是不能轉換成繼承體系外的一種類型

 1 class A { ... };
 2 class B { ... };
 3 class D : public B { ... };
 4 void f(B* pb, D* pd)
 5 {
 6     D* pd2 = static_cast<D*>(pb);        // 不安全, pb可能只是B的指針
 7     B* pb2 = static_cast<B*>(pd);        // 安全的
 8     A* pa2 = static_cast<A*>(pb);        //錯誤A與B沒有繼承關系
 9     ...
10 }

reinterpret_cast(exp)

字面理解即re-interpret,重新解析(釋)的意思。故名思意,它主要用於不相關類型之間的轉換,好一個英文單詞在不同的上下文中,詞性和詞義可能完全不同。它為不同類型之間轉換帶來的便利,但是也伴隨着風險的,如將一個十六進制整數轉換為內存地址(由int-->指針類型,這兩種類型完全不關聯)。既然是用於不相關類型之間的轉換,也就意味着編譯器不會做太多的確認和承諾。
reinterpret_cast方式還有一個特點就是:目標和原始值之間至少有相同的位數,我們可以將轉換之后的值再轉換回去,而不像其它3種類型可能會導致精度丟失。

dynamic_cast(exp)

一種運行時(run-time)檢測的類型轉換,因此轉換可能需要較大的運行時代價,這種類型也是用C-style是無法實現的。主要用於執行類型向下轉換和繼承之間的轉換。

dynamic_cast 僅能應用於指針或者引用,不支持內置數據類型
表達式dynamic_cast<T*>(a) 將a值轉換為類型為T的對象指針。如果類型T不是a的某個基類型,該操作將返回一個空指針。
它不僅僅像static_cast那樣,檢查轉換前后的兩個指針是否屬於同一個繼承樹,它還要檢查被指針引用的對象的實際類型,確定轉換是否可行。

const_cast(exp)

用於消除變量的const限定,轉換之后的變量就不再具有“const”了,如果是一個const指針的話,轉換之后可以改變指向而指向其它對象。

說明:

通常情況下dynamic_cast最好些,它檢查的更嚴格些,其次是static_cast,而后兩者也就是const_cast和reinterpret_cast較之前兩者貌似不太常用,而且也不推薦使用,const_cast在用於去除const的地方還是有所發揮的,reinterpret_cast在轉換時,不會在內存中進行補足比特位(例如int轉換到double,需要補足4字節),這往往是不安全的,而且代碼也是不可移植的。

 

動態內存分配

一般來說,編譯器將內存分為三部分:靜態存儲區域、棧、堆。靜態存儲區主要保存全局變量和靜態變量,棧存儲調用函數相關的變量、地址等,堆存儲動態生成的變量。 在c中是指由malloc,free運算產生釋放的存儲空間,在c++中就是指new和delete運算符作用的存儲區域。

對象創建的兩種方式:

類示例:

 1 #include <iostream>  
 2 using namespace std;  
 3   
 4 class TestNew  
 5 {  
 6 private:  
 7     int ID;  
 8 public:  
 9     TestNew(int ID);  
10     ~TestNew();  
11 };  
12   
13 TestNew::TestNew(int ID)  
14 {  
15     this->ID = ID;  
16 }  
17   
18 TestNew::~TestNew()  
19 {  
20     std::cout<<"對象 "<<this->ID<<" 執行析構函數"<<std::endl;  
21 }

方法一:

ClassName object(param);

1 TestNew test(1);

這樣就聲明了一個ClassName類型的object對象,C++會為它分配足夠的存放對象所有成員的存儲空間。
這種方法創建的對象,內存分配是分配到棧中的,由C++缺省創建和撤銷,自動調用構造函數和析構函數。
該方法創建的對象調用類方法時,必須用“.”,而不能用“->”。

在函數中使用該方法時,函數內局部變量的存儲單元都在棧上創建,函數執行結束后在將這些局部變量的內存空間回收。
在棧上分配內存空間效率很高,但是分配的內存容量有限。

方法二:

ClassName *object=new ClassName(param);
delete object;

1 TestNew *pTest = new TestNew(1);
2 delete pTest;

C++用new創建對象時返回的是一個對象指針,object指向一個ClassName的對象,C++分配給object的僅僅是存放指針值的空間。
用new 動態創建的對象必須用delete來撤銷該對象。只有delete對象才會調用其析構函數。
new創建的對象不是用“*”或“.”來訪問該對象的成員函數的,而是用運算符“->”;

new創建類對象特點:

  1. new創建類對象需要指針接收,一處初始化,多處使用
  2. new創建類對象使用完需delete銷毀
  3. new創建對象直接使用堆空間,而局部不用new定義類對象則使用棧空間
  4. new對象指針用途廣泛,比如作為函數返回值、函數參數等


免責聲明!

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



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