在C++中類的對象創建分爲兩種,一種是靜態創建,如A a;另外一種是動態創建,如A* p=new A(),A*p=(A*)malloc();靜態創建一個類對象,是由編譯器爲對象在棧空間中分配內存,經過直接移動棧頂指針挪出適當的空間,而後在這片內存空間上調用構造函數造成一個棧對象。動態創建類對象,是使用new運算符將對象創建在堆空間中,在棧中只保留了指向該對象的指針。棧是由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值,對象的引用地址等。其操做方式相似於數據結構中的棧,一般都是被調用時處於存儲空間中,調用完畢當即釋放。堆中一般保存程序運行時動態建立的對象,C++堆中存放的對象須要由程序員分配釋放,它存在程序運行的整個生命期,直到程序結束由OS釋放。而在java中一般類的對象都分配在堆中,對象的回收由虛擬機的GC垃圾回收機制決定。java
1.下面的程序來看看靜態創建和動態創建對象的區別ios
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 class student 5 { 6 public: 7 string name; 8 int age; 9 void sayhello(); 10 }; 11 void student::sayhello() 12 { 13 cout<<"my name is: "+this->name+" I am: "<<this->age; 14 cout<<"\n"; 15 } 16 student setname(string name) 17 { 18 student stu; 19 stu.age=12; 20 stu.name=name; 21 return stu; 22 } 23 int main() 24 { 25 student stu=setname("jim"); 26 stu.sayhello(); 27 return 0; 28 }
程序運行結果:my name is: jim I am: 12;c++
程序定義了一個student類,在setname函數中定義一個局部對象做爲返回值。程序第18行靜態構建了一個student對象stu,它在棧上分配空間,在函數調用結束後就銷燬了,函數返回的類對應在內存中的值應該不存在啊?其實原來C++在用類做爲函數的返回值時調用了類的拷貝構造函數,並且該拷貝構造函數是在堆上分配存儲空間,後面再討論這個問題。程序員
在setname函數內的stu在函數調用結束後就銷燬了,能夠添加一個析構函數來證實:數組
在student類中加入析構函數:數據結構
student::~student() { cout<<this->name<<":gameover"<<endl; }
程序運行結果:ide
在sayhello()前,輸出jim:gameover,即爲setname()裏的stu對象執行了析構函數。函數
如將setname函數改成:this
student* setname(string name) { student stu; stu.age=12; stu.name=name; return &stu; }
main函數的調用改成:spa
int main() { student* p=setname("tom"); p->sayhello(); return 0; }
顯然這裏會出現問題,對象指針返回的是棧上的對象,在函數調用結束後已經銷燬了,對象指針即爲野指針,故程序在編譯時會提示:warning C4172: returning address of local variable or temporary。解決這個問題咱們天然想到把該對象構建在堆上便可。修改setname函數爲下:
student* setname(string name) { student* stu= new student(); stu->age=12; stu->name=name; return stu; }
main函數的調用不變;程序正常運行輸出:
上面輸出結果並無調用析構函數,在setname調用後,在main函數結束後也沒有調用。在對上的對象須要程序員本身delete釋放,將main改成以下:
int main() { student* p=setname("tom"); p->sayhello(); delete p; return 0; }
即加入delete p;運行結果:
C中用malloc函數來動態申請空間,該內存分配在堆上。這裏能夠驗證,加入#include <malloc.h>,將setname函數改成以下:
1 student* setname(string name) 2 { 3 student* stu=(student*)malloc(sizeof(student)); 4 stu->age=12; 5 stu->name=name; 6 return stu; 7 }
爲在student中加入構造函數:
student::student() { cout<<"constructor"<<endl; }
上面的程序執行到第5行會出錯,緣由是沒有調用構造函數,stu->name根本就沒有被初始化(string的構造函數沒有被調用),因此不能賦值 。具體的解釋是: 由於malloc只是分配堆內存(不會調用構造函數)它並不知道內存裏要存的是什麼。爲此用new便可,將第3行代碼改成:student* stu=new student;程序運行結果爲下:
即程序調用了構造函數。若非要用malloc來申請內存能夠將setname函數改成以下:
1 student* setname(string name) 2 { 3 student* stu=(student*)malloc(sizeof(student)); 4 new(stu) student; 5 stu->age=12; 6 stu->name=name; 7 return stu; 8 }
即加入了第4行程序正常運行,調用了構造函數。第4行大概能夠理解爲new了一個student對象,賦值轉換爲student的指針stu。
既然這樣能夠把第3行直接改成:student* stu;即
1 student* setname(string name) 2 { 3 //student*stu= new student; 4 student* stu; 5 new(stu) student; 6 stu->age=12; 7 stu->name=name; 8 return stu; 9 }
讓說第4,5行的效果應該和第3行相同,編譯程序提示:warning C4700: local variable 'stu' used without having been initialized,即stu沒有初始化。 new(stu) student;和stu= new student;並不等價。第5行並非初始化,這裏能夠看作第5行這種寫法(以前還真沒見過)是C++爲兼容malloc內存申請的用法,通常狀況下推薦確定是用new關鍵字。
到此其實這裏要說明的主題已經基本說明了,關於malloc的用法當申請的類的成員變量只包含基本的數據類型(數值型int,double等)(string等引用類除外)時是不會出錯的。下面的列子能夠證實;
1 #include<iostream> 2 #include<string> 3 #include <malloc.h> 4 using namespace std; 5 class course 6 { 7 public: 8 int id; 9 float score; 10 void printscore() 11 { 12 cout<<"id:"<<this->id; 13 cout<<" score:"<<this->score<<endl; 14 } 15 }; 16 17 course* setscore(int id,float score) 18 { 19 course* co= (course*)malloc(sizeof(course)); 20 co->id=id; 21 co->score=score; 22 return co; 23 } 24 int main() 25 { 26 course* cou=setscore(999,188.9); 27 cou->printscore(); 28 return 0; 29 }
程序運行結果以下:
程序第19行這樣的用法沒有問題,而以前的string類卻有問題。這樣看來,自定義類型做爲類的成員時也應該會有問題,來看下面的代碼:
1 #include<iostream> 2 #include<string> 3 #include <malloc.h> 4 using namespace std; 5 class course; 6 class student 7 { 8 public: 9 string name; 10 int age; 11 course cou; 12 void sayhello(); 13 ~student(); 14 student(); 15 }; 16 student::student() 17 { 18 cout<<"constructor"<<endl; 19 } 20 student::~student() 21 { 22 cout<<this->name<<":gameover"<<endl; 23 } 24 void student::sayhello() 25 { 26 cout<<"my name is: "+this->name+" I am: "<<this->age; 27 cout<<"\n"; 28 } 29 class course 30 { 31 public: 32 int id; 33 float score; 34 void printscore() 35 { 36 cout<<"id:"<<this->id; 37 cout<<" score:"<<this->score<<endl; 38 } 39 }; 40 course* setscore(int id,float score) 41 { 42 course* co= (course*)malloc(sizeof(course)); 43 co->id=id; 44 co->score=score; 45 return co; 46 } 47 student* setname_score(string name ,course* cou) 48 { 49 student* stu= (student*)malloc(sizeof(student)); 50 stu->age=12; 51 new(stu)student; 52 stu->cou.id=cou->id; 53 stu->cou.score=cou->score; 54 stu->name= name; 55 return stu; 56 } 57 int main() 58 { 59 course* cou=setscore(999,188.9); 60 student* stu=setname_score("jimm",cou); 61 stu->cou.printscore(); 62 stu->sayhello(); 63 return 0; 64 }
這段代碼中把course類對象做爲student的類成員。程序編譯出錯: error C2079: 'cou' uses undefined class 'course'。把course類的定義放在前面則沒有錯即:
1 #include<iostream> 2 #include<string> 3 #include <malloc.h> 4 using namespace std; 5 class course 6 { 7 public: 8 int id; 9 float score; 10 void printscore() 11 { 12 cout<<"id:"<<this->id; 13 cout<<" score:"<<this->score<<endl; 14 } 15 }; 16 class student 17 { 18 public: 19 string name; 20 int age; 21 course cou; 22 void sayhello(); 23 ~student(); 24 student(); 25 }; 26 student::student() 27 { 28 cout<<"constructor"<<endl; 29 } 30 student::~student() 31 { 32 cout<<this->name<<":gameover"<<endl; 33 } 34 void student::sayhello() 35 { 36 cout<<"my name is: "+this->name+" I am: "<<this->age; 37 cout<<"\n"; 38 } 39 course* setscore(int id,float score) 40 { 41 course* co= (course*)malloc(sizeof(course)); 42 co->id=id; 43 co->score=score; 44 return co; 45 } 46 student* setname_score(string name ,course* cou) 47 { 48 student* stu= (student*)malloc(sizeof(student)); 49 stu->age=12; 50 new(stu)student; 51 stu->cou.id=cou->id; 52 stu->cou.score=cou->score; 53 stu->name= name; 54 return stu; 55 } 56 int main() 57 { 58 course* cou=setscore(999,188.9); 59 student* stu=setname_score("jimm",cou); 60 stu->cou.printscore(); 61 stu->sayhello(); 62 return 0; 63 }
運行結果爲:
上面程序setname_score函數中若不用new(stu)student;這種寫法,則會出現未初始化的錯誤。這裏徹底能夠將類的成員改成指針的形式,在初始化時用new在堆上分配存儲。改寫的代碼以下:
1 #include<iostream> 2 #include<string> 3 #include <malloc.h> 4 using namespace std; 5 class course; 6 class student 7 { 8 public: 9 string* name; 10 int age; 11 course* cou; 12 void sayhello(); 13 ~student(); 14 student(); 15 }; 16 student::student() 17 { 18 cout<<"constructor"<<endl; 19 } 20 student::~student() 21 { 22 cout<<this->name<<":gameover"<<endl; 23 } 24 void student::sayhello() 25 { 26 cout<<"my name is: "+*(this->name)+" I am: "<<this->age; 27 cout<<"\n"; 28 } 29 class course 30 { 31 public: 32 int id; 33 float score; 34 void printscore() 35 { 36 cout<<"id:"<<this->id; 37 cout<<" score:"<<this->score<<endl; 38 } 39 }; 40 course* setscore(int id,float score) 41 { 42 course* co= (course*)malloc(sizeof(course)); 43 co->id=id; 44 co->score=score; 45 return co; 46 } 47 student* setname_score(string name ,course* cou) 48 { 49 student* stu= (student*)malloc(sizeof(student));//student*stu=new student;也同樣,只是一個調用構造函數,一個不調用 50 stu->age=12; 51 stu->cou=new course();//這裏用new創建對象 52 stu->cou->id=cou->id; 53 stu->cou->score=cou->score; 54 stu->name=new string(name);//new 55 return stu; 56 } 57 int main() 58 { 59 course* cou=setscore(999,188.9); 60 student* stu=setname_score("jimm",cou); 61 stu->cou->printscore(); 62 stu->sayhello(); 63 return 0; 64 65 }
上面程序運行結果爲:
綜上所述,C++中對象的創建能夠在堆和棧上。分別爲動態創建和動態創建的方式,構建堆上的對象時通常使用new關鍵字,而對象的指針在棧上。使用new在堆上構建的對象須要主動的delete銷燬。C++對象能夠在堆或棧中,函數的傳參能夠是對象(對象的拷貝),或是對象的指針。而在java中對象通常分配在堆上,對象的傳值只有值類型,即對象的引用(地址),這樣看來C++要靈活的多。關於c++數組的內存分配還有這裏提到的拷貝構造函數,下次再討論啊。上面的程序在VC++6.0編寫經過。