C++ 物件和例項的區別,以及用new和不用new建立類物件區別
起初剛學C++時,很不習慣用new,後來看老外的程式,發現幾乎都是使用new,想一想區別也不是太大,但是在大一點的專案設計中,有時候不使用new的確會帶來很多問題。當然這都是跟new的用法有關的。new建立類物件,使用完後需使用delete刪除,跟申請記憶體類似。所以,new有時候又不太適合,比如在頻繁呼叫場合,使用區域性new類物件就不是個好選擇,使用全域性類物件或一個經過初始化的全域性類指標似乎更加高效。
C++ 物件例項化的一些概念: C++ 如果直接定義類,如classA a; a存在棧上(也意味著複製了物件a在棧中);如果classA a = new classA就存在堆中。
一、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物件指標作為函式引數和返回值
下面是天緣隨手寫一個例子,不太嚴謹。主要示意一下類指標物件作為返回值和引數使用。
- class CTest { public: int a; };
- class CBest { public: int b; };
- CTest* fun(CBest* pBest) {
- CTest* pTest = new CTest();
- pTest->a = pBest->b; return pTest;
- }
- int main() {
- CBest* pBest = new CBest();
- CTest* pRes= fun(pBest);
- if(pBest!=NULL)
- delete pBest;
- if(pRes!=NULL)
- delete pRes ;
- return 0;
- }
C++物件例項化
- JAVA:
- A a = new A();
- 為A物件建立了一個例項,但在記憶體中開闢了兩塊空間:一塊空間在堆區,存放new A()這個物件;
- 另一塊空間在堆疊,也就是棧,存放a,a的值為new A()這個物件的記憶體地址。因為java在JVM中執行,
- 所以a 描述的記憶體地址不一定是這個物件真實記憶體的地址。
- Object o; // 這是宣告一個引用,它的型別是Object,他的值為null,還沒有指向任何物件,該引用放在記憶體的棧區域中。
- o = new Object(); // new Object()句,例項化了一個物件,就是在堆中申請了一塊連續空間用來存放該物件。
- = // 運算子,將引向o指向了物件。也就是說將棧中表示引用o的記憶體地址的內容改寫成了Object物件在堆中的地址。
- C++:
- C++ 如果直接定義類,如classA a; a 存在棧上(也意味著複製了物件a在棧中),如果classA a = new classA就存在堆中。
初學Java時,在很長一段時間裡,總覺得基本概念很模糊。後來才知道,在許多Java書中,把物件和物件的引用(例項)混為一談。
如果分不清物件與物件引用,那實在沒法很好地理解下面的面向物件技術,把自己的一點認識寫下來,或許能讓初學Java的朋友們少走一點彎路。
為便於說明,我們先定義一個簡單的類:- class student
- {
- int name;
- int age;
- int sex;
- }
有了這個類(模板),就可以用它來建立物件:student stu1 = new student();
通常把這條語句的動作稱之為建立一個物件,其實,它包含了四個動作。
1)右邊的"new student",是以student類為模板,在堆空間裡建立一個student類的物件(也簡稱為student物件)。
2)末尾的()意味著,在物件建立後,立即呼叫student類的建構函式,對剛生成的物件進行初始化。
建構函式是肯定有的。如果你沒寫,Java會給你補上一個預設的建構函式。
3)左邊的"student stu1"建立了一個student類引用變數。所謂student類引用,就是以後可以用來指向某個
student物件的物件引用,它指向的是某個student物件的記憶體地址(有點C語言中指標的味道)。
4)"="操作符使物件引用指向剛建立的那個student物件。
我們可以把這條語句拆成兩部分:student
stu1; (1)
stu1 = new student(); (2)
效果是一樣的。
這樣寫,就比較清楚了,有兩個實體:一是物件引用變數(stu1),在Sun公司的實現中,物件的引用是一個控制代碼,其中包含一對指標:一個指標指向該物件的方法表,一個指向該物件的資料;另一個是物件本身(就是new出來的那個物件)。
在堆空間裡建立的實體,與在資料段以及棧空間裡建立的實體不同。儘管它們也是確確實實存在的實體,但是,我們看不見,也摸不著。不僅如此,我們仔細研究一下第二句,想想剛剛建立的student物件叫什麼名字?
有人說,它叫"student"。不對,"student"是類(物件的建立模板)的名字。一個student類可以據此建立出無數個物件,這些物件不可能全叫"student"。物件連名都沒有,沒法直接訪問它。我們只能通過物件引用(例項)來間接訪問物件。
為了形象地說明物件、物件引用及它們之間的關係,可以做一個或許不很妥當的比喻:
物件好比是一隻沒有線的風箏,引用變數是一根線,可以用來系風箏。如果只執行了第一條語句,還沒執行第二條,此時建立的引用變數stu1還沒指向任何一個物件,它的值是null,引用變數可以指向某個物件,或者為null。這時stu1是一根線,一根還沒有繫上任何一個風箏的線。
執行了第二句後,一隻新風箏做出來了,並被系在stu1這根線上。我們抓住這根線,就等於抓住了那隻風箏。
再來一句:student stu2;就又做了一根線,還沒繫上風箏。如果再加一句:stu2=stu1;繫上風箏了。
這裡,發生了複製行為。但是,要說明的是,物件本身並沒有被複制,被複制的只是物件引用。
結果是,stu2也指向了stu1所指向的物件,也就是兩根線系的是同一只風箏。
如果用下句再建立一個物件:stu2=newstudent();則引用變數stu2改指向第二個物件。
從以上敘述再推演下去,我們可以獲得以下結論:
(1)一個物件引用可以指向0個或1個物件(一根線可以不繫風箏,也可以系一個風箏),而且可以改指;
(2)一個物件可以有N個引用指向它(可以有N條線系同一個風箏)。
如果有下面語句:stu1=stu2;
按上面的推斷,stu1也指向了第二個物件。這個沒問題。問題是第一個物件呢?沒有一條線繫住它,它飛了。
很多書裡說,它被Java的垃圾回收機制回收了,這不確切,準確地說,它已成為Java垃圾回收機制的處理物件。至於什麼時候真正被回收,那要看垃圾回收機制的心情了。由此看來,new student();該語句應該不合法吧,至少是沒用的吧?不對,它是合法的,而且可用的。譬如,如果我們僅僅為了列印而生成一個物件,就不需要用引用變數來繫住它。最常見的就是列印字串:System.out.println("Iam Java!");
字串物件"I amJava!"在列印後即被丟棄,有人把這種物件稱之為臨時物件。
最後再說明一下:
C++中:
但是在Java中Student student(20) ; //這裡student是引用 物件分配在 棧空間中,這裡只是我的理解
Student *student = new Student(20); //這裡student是指標,new Student(20)是分配在堆記憶體空間的
Student student(20) ; //注意:java中沒有這樣例項化物件的, 要想得到一個物件 必須要new出來.
Student student ; //這個只是定義了一個引用 ,沒有指向任何物件
Student student = new Student(20); //定義了一個引用,指向堆記憶體中的student物件