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

void*指針ios

void關鍵字表示「空類型」的概念。可是,這裏的「空類型」不表示「任意類型」,而是表示不存在的意思,也就是說C/C++不容許你寫語句void a,不存在類型爲void的東西.c++

void*表示「空類型指針」,與void不一樣,void*表示「任意類型的指針」或表示「該指針與一地址值相關,可是不清楚在此地址上的對象的類型」。程序員

 

類型轉換安全

C風格轉換:函數

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

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

那爲何還須要一個新的C++類型的強制轉換呢?spa

新類型的強制轉換能夠提供更好的控制強制轉換過程,容許控制各類不一樣種類的強制轉換。C++中風格是static_cast<type>(content)。C++風格的強制轉換其餘的好處是,它們能更清晰的代表它們要幹什麼。程序員只要掃一眼這樣的代碼,就能當即知道一個強制轉換的目的。指針

C++轉換:code

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對象指針用途普遍,好比做爲函數返回值、函數參數等
相關文章
相關標籤/搜索