【C++】C++用new和不用new建立類物件區別
起初剛學C++時,很不習慣用new,後來看老外的程式,發現幾乎都是使用new,想一想區別也不是太大,但是在大一點的專案設計中,有時候不使用new的確會帶來很多問題。
當然這都是跟new的用法有關的。new建立類物件,使用完後需使用delete刪除,跟申請記憶體類似。所以,new有時候又不太適合,比如在頻繁呼叫場合,使用區域性new類物件就不是個好選擇,使用全域性類物件或一個經過初始化的全域性類指標似乎更加高效。
一、new建立類物件與不new區別
下面是自己總結的一些關於new建立類物件特點:
- new建立類物件需要指標接收,一處初始化,多處使用
- new建立類物件使用完需delete銷燬
- 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
結果是
[email protected]:~/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) 對齊規則,跟變數或虛擬函式的先後順序無關,只跟最大變數型別或函式指標有關,函式指標跟編譯器最大對齊位數有關(不太好理解,請繼續往下看)