【C++】C++用new和不用new建立類物件區別
起初剛學C++時,很不習慣用new,後來看老外的程式,發現幾乎都是使用new,想一想區別也不是太大,但是在大一點的專案設計中,有時候不使用new的確會帶來很多問題。
當然這都是跟new的用法有關的。new建立類物件,使用完後需使用delete刪除,跟申請記憶體類似。所以,new有時候又不太適合,比如在頻繁呼叫場合,使用區域性new類物件就不是個好選擇,使用全域性類物件或一個經過初始化的全域性類指標似乎更加高效。
一、new建立類物件與不new區別
下面是自己總結的一些關於new建立類物件特點:
- new建立類物件需要指標接收,一處初始化,多處使用
- new建立類物件使用完需delete銷燬
- new建立物件直接使用堆空間,而區域性不用new定義類物件則使用棧空間
- new物件指標用途廣泛,比如作為函式返回值、函式引數等
- 頻繁呼叫場合並不適合new,就像new申請和釋放記憶體一樣
二、new建立類物件例項
1、new建立類物件例子:
CTest* pTest = new CTest();
delete pTest;
pTest用來接收類物件指標。
不用new,直接使用類定義申明:
CTest mTest;
此種建立方式,使用完後不需要手動釋放,該類解構函式會自動執行。而new申請的物件,則只有呼叫到delete時再會執行解構函式,如果程式退出而沒有執行delete則會造成記憶體洩漏。
2、只定義類指標
這跟不用new申明物件有很大區別,類指標可以先行定義,但類指標只是個通用指標,在new之前併為該類物件分配任何記憶體空間。比如:
CTest* pTest = NULL;
但使用普通方式建立的類物件,在建立之初就已經分配了記憶體空間。而類指標,如果未經過物件初始化,則不需要delete釋放。
3、new物件指標作為函式引數和返回值
下面是天緣隨手寫一個例子,不太嚴謹。主要示意一下類指標物件作為返回值和引數使用。
示例:
類的記憶體分配簡單總結,程式碼片段如下:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#include
<iostream> using namespace std; /** *
yanggang *
http://blog.ithomer.net *
2014-08-11 **/ class ClassA
{ private : int A; int B; void prin1()
{ } void prin2()
{ } virtual void prin3()
{ } }; class ClassB
: public ClassA
{ public : int C; int D; void prin4()
{ } void prin5()
{ } virtual void prin6()
{ } }; int main( int argc, char *
argv[]) { cout<< sizeof (ClassA)<<endl; //
16 cout<< sizeof (ClassB)<<endl; //
24 return 0; } |
測試環境:
Ubuntu 12.04.5 LTS x86_64 GNU/Linux
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 編譯引數 -m64
結果是
homer@homer-pc:~/Desktop$ g++ testMem.c -o testMem && ./testMem
16
24
結果為什麼是這樣?
32位系統int佔4個位元組,64位系統int也佔4個位元組(不是8個位元組),而一個類中所有的虛擬函式通過一個虛擬函式指標管理,類物件的大小隻包含這個vptr指標(在64位機器上指標sizeof為8個位元組),其他虛擬函式是放在別的記憶體空間中管理,vptr指標在64位機器上是8個位元組大小(32位機器上是4個位元組)。注意到普通成員函式並不佔類物件的大小空間,因為普通成員函式通過this指標管理,一個物件的this指標並不是物件本身的一部分,不會影響sizeof(物件)的結果。
sizeof(ClassA)
1)int A 和 int B 各佔4個位元組,考慮64位機器編譯器對其規則,合併為8個位元組
2)virtual void prin3() 虛擬函式的vptr指標,在64位機器編譯器上佔8個位元組
3)合計 sizeof(ClassA)為 8 + 8 = 16個位元組
this作用域是在類內部,當在類的非靜態成員函式中訪問類的非靜態成員的時候,編譯器會自動將物件本身的地址作為一個隱含引數傳遞給函式。這個this指標會因編譯器不同而有不同的放置位置,可能是棧,也可能是暫存器,甚至全域性變數。
子類其實不管如何繼承,用sizeof()算該類的大小都會把父類中的私有成員變數所佔的空間算進去,也就是說,私有變數也在子類中分配了記憶體,但你卻不可以直接訪問,這起到一個保護作用,這如同一個珠寶,共有繼承就是開放性的展覽,而私有繼承是把珠寶鎖起來,你卻不能動,要動珠寶如果有管家(基類的public中定義了一些對其私有變數操作的成員函式,)只能讓管家幫你代勞。
sizeof(ClassB)
1)int A 和 int B 各佔4個位元組,是父類ClassA中的私有變數,合併佔用8個位元組
2)virtual void prin3() 虛擬函式的vptr指標,父類ClassA在子類中不會分配空間
3)int C 和 int D 各佔4個位元組,在子類中會分配空間,合併佔用8個位元組
4)virtual void prin6() 虛擬函式的vptr指標,在64位機器編譯器上佔8個位元組
5)合計 sizeof(ClassB)為 8 + 8 + 8 = 24個位元組
註明: 上述示例在32位編譯器上測試,sizeof(ClassA)和sizeof(ClassB)分別為12和20位元組(虛擬函式指標佔用空間少了4位元組)
只有虛擬函式會佔用一個指標大小的記憶體,原因是系統用一個指標維護這個類的虛擬函式表,並且注意這個虛擬函式無論含有多少項(類中含有多個虛擬函式)都不會影響類的大小。
知識延伸:
1) 空 ClassA(驗證空Class佔用空間大小)
1
2
3
4
5
6
7
8
9
10
11
|
class ClassA
{ private : void prin1()
{ } void prin2()
{ } }; cout<< sizeof (ClassA)<<endl; //
1 |
2) ClassA 只有一個char(驗證編譯器對其規則)
1
2
3
4
5
6
7
8
9
10
11
12
|
class ClassA
{ private : char C; void prin1()
{ } void prin2()
{ } }; cout<< sizeof (ClassA)<<endl; //
1 |
3)ClassA 有一個char和一個虛擬函式(或一個long型)(驗證編譯器對其規則二)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class ClassA
{ private : char C; void prin1()
{ } void prin2()
{ } virtual void prin3()
{ } }; cout<< sizeof (ClassA)<<endl; //
16 |
4)ClassA 有一個char和一個虛擬函式(或一個long型),且對調char和虛擬函式的先後順序(驗證編譯器對其規則三)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class ClassA
{ void prin1()
{ } void prin2()
{ } virtual void prin3()
{ } private : char C; }; cout<< sizeof (ClassA)<<endl; //
16 |
5)ClassA 有一個char,一個int,一個虛擬函式(或一個long型)(驗證編譯器對其規則四)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class ClassA
{ private : char C; int A; void prin1()
{ } void prin2()
{ } virtual void prin3()
{ } }; cout<< sizeof (ClassA)<<endl; //
16 |
6)ClassA 有一個char,一個int(驗證編譯器對其規則五)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class ClassA
{ private : char C; int A; void prin1()
{ } void prin2()
{ } }; cout<< sizeof (ClassA)<<endl; //
8 |
7)ClassA 只有一個int(驗證編譯器對其規則六)
1
2
3
4
5
6
7
8
9
10
11
12
|
class ClassA
{ private : int A; void prin1()
{ } void prin2()
{ } }; cout<< sizeof (ClassA)<<endl; //
4 |
總結如下:
1) 空類的 sizeof 為1個位元組
2) 只有一個char的類,sizeof為一個位元組
3) 類中含有char和虛擬函式,將以最大的變數或指標為編譯器對齊規則,例如:虛擬函式指標佔8個位元組(64位編譯器),則char雖然只佔1個位元組,但對齊後空餘了7個位元組,合併類佔8(指標) + 1(char) + 7(對齊的空位元組) = 16個位元組
4) 對齊規則,跟變數或虛擬函式的先後順序無關,只跟最大變數型別或函式指標有關,函式指標跟編譯器最大對齊位數有關(不太好理解,請繼續往下看)
5) char和int合佔8個位元組,虛擬函式指標佔8個位元組,且以最大的虛擬函式指標的8位元組對其,其中char佔一個空餘3個位元組合併佔4個,int佔4個位元組,按8位規則對齊,合計16位元組
6) 一個char和int合併佔8個位元組,無虛擬函式,此時以最大變數型別int對齊,因此char佔1位元組空3位元組佔4位元組
7)一個int佔4位,自己便是最大的對齊規則; 2)一個char同理也佔1個位元組,此處跟編譯器最大64位對齊規則無關(即一個int或一個char,不會擴充到佔用8位元組)
這裡,又延伸出了一個很有意思的問題,空類sizeof為什麼不為0,而為1?
回答這個問題,需要回到編譯器和類的例項化問題上來,在學校看過《深度探索C++物件模型》,類分抽象類和普通類,空類屬於普通類,是可以被例項化的。
每個類的例項,在記憶體中都有一個獨一無二的地址,為了達到這個目的,編譯器往往會給一個空類隱含的加一個位元組,這樣空類在例項化後在記憶體得到了獨一無二的地址,因此1)中的空類ClassA預設會佔用1個位元組。
相關文章
- C++ new A 和 new A() 的區別詳解C++
- 構造器引用和直接用new建立物件區別物件
- C/C++——C++中new與malloc的10點區別C++
- C++ new 和異常C++
- C++用new建立二維陣列的方法C++陣列
- C++中new與malloc的10點區別C++
- The New C++ (轉)C++
- malloc和new的區別,new和operator new的區別,分配器介紹
- PHP new self()和new static()的區別PHP
- 【C++】C++ 記憶體分配(new,operator new)詳解C++記憶體
- C++ 使用 new 建立二維陣列C++陣列
- 深入 C++ 的 newC++
- JavaScript中的new map()和new set()使用詳細(new map()和new set()的區別)JavaScript
- C++中動態建立和刪除陣列(new 和delete)C++陣列delete
- new 和 malloc 的區別
- new和malloc的區別
- javascript使用new建立物件JavaScript物件
- C++ 記憶體分配(new,operator new)詳解C++記憶體
- 【C++】C++ new和malloc到底哪裡不一樣C++
- Golang make和new的區別Golang
- C++記憶體管理:new / delete 和 cookieC++記憶體deleteCookie
- C++中過載new和delete的使用C++delete
- c++中new和delete的使用方法C++delete
- C malloc() free(), C++ new() delete()C++delete
- c++ 關於new檔案C++
- c++類和物件C++物件
- C++ 類和物件C++物件
- new 和 malloc 的區別 及使用
- __new__和__init__的區別
- newInstance和new的區別(good)Go
- C++類和物件是什麼?C++類和物件詳解C++物件
- 【C++學習筆記】使用new建立動態結構C++筆記
- [C++之旅] 7 C++類和物件C++物件
- 物件建立模式和類建立模式的區別物件模式
- C++ new用法的個人見解C++
- C++中建立物件的兩種方法及其區別C++物件
- [C++]類和物件(一)C++物件
- [C++]類和物件(二)C++物件