棧與堆、作用域與生存期
阿新 • • 發佈:2019-05-19
原文地址:https://www.jianshu.com/p/29e8f2930cf5
問題引入
// correct #include<iostream> #include<math.h> #include<stdlib.h> using namespace std; /* 幾何形體處理程式: * 輸入若干個幾何形體的引數,要求按面積排序輸出。 * 輸出時要指明形狀。 * Input: * 第一行是幾何形體數目n(不超過100) * 下面有n行,每行以一個字母c開頭 * 若 c 是 ‘R’ ,則代表一個矩形,本行後面跟著兩個整數,分別是矩形的寬和高 * 若 c 是 ‘C’ ,則代表一個圓,本行後面跟著一個整數代表其半徑 * 若 c 是 ‘T’ ,則代表一個三角形,本行後面跟著三個整數,代表三條邊的長度 * Output: * 按面積從小到大依次輸出每個幾何形體的種類及面積。 * 每行一個幾何形體,輸 出格式為: * 形體名稱:面積*/ class CShape{ public: virtual double Area()=0; virtual void PrintInfo()=0; }; class CRectangle:public CShape { int w,h; public: CRectangle(int x,int y):w(x),h(y){} double Area(){return w*h;} void PrintInfo(){cout<<"Rectangle:"<<Area()<<endl;} }; class CCircle:public CShape{ int r; public: CCircle(int x):r(x){} double Area(){return r*r*3.14;} void PrintInfo(){cout<<"Circle:"<<Area()<<endl;} }; class CTriangle:public CShape{ int a,b,c; public: CTriangle(int x,int y,int z):a(x),b(y),c(z){} double Area(){ //海倫公式 double p=double(a+b+c)/2; return sqrt(p*(p-a)*(p-b)*(p-c)); } void PrintInfo(){cout<<"Triangle:"<<Area()<<endl;} }; int MyCompare(const void* p1,const void* p2){ CShape** pc1; CShape** pc2; pc1=(CShape**)p1; pc2=(CShape**)p2; double a1,a2; a1=(*pc1)->Area(); a2=(*pc2)->Area(); if(a1>a2) return 1; else if(a1<a2) return -1; else return 0; } int main(){ CShape* p[100]; int n; int i; cin>>n; for(i=0;i<n;i++){ char ch; cin>>ch; switch(ch){ case 'R':{ int w,h; cin>>w>>h; CRectangle* pr=new CRectangle(w,h); p[i]=pr; cout<<p[i]<<endl; break;} case 'C':{ int r; cin>>r; CCircle* pc=new CCircle(r); p[i]=pc; cout<<p[i]<<endl; break;} case 'T':{ int a,b,c; cin>>a>>b>>c; CTriangle* pt=new CTriangle(a,b,c); p[i]=pt; cout<<p[i]<<endl; break;} default:{break;} } p[0]->PrintInfo(); } qsort(p,n,sizeof(CShape*),MyCompare); for(i=0;i<n;i++) { p[i]->PrintInfo(); } return 0; }
若將case里程序實現改為新建派生類物件,然後將指標指向該物件方式,會發現p[i]裡存放的地址相同,結果會出錯。
// wrong answer int main(){ CShape* p[100]; int n; int i; cin>>n; for(i=0;i<n;i++){ char ch; cin>>ch; switch(ch){ case 'R':{ int w,h; cin>>w>>h; //CRectangle* pr=new CRectangle(w,h); CRectangle CR(w,h); p[i]=&CR; cout<<p[i]<<endl; break;} case 'C':{ int r; cin>>r; //CCircle* pc=new CCircle(r); CCircle CC(r); p[i]=&CC; cout<<p[i]<<endl; break;} case 'T':{ int a,b,c; cin>>a>>b>>c; //CTriangle* pt=new CTriangle(a,b,c); CTriangle CT(a,b,c); p[i]=&CT; cout<<p[i]<<endl; break;} default:{break;} } p[0]->PrintInfo(); } qsort(p,n,sizeof(CShape*),MyCompare); for(i=0;i<n;i++) { p[i]->PrintInfo(); } return 0; }
原因分析
新建的派生類物件為棧物件,case結束後會自動呼叫解構函式析構;
而new出來的為堆物件,需要delete析構。
知識點總結
C++記憶體分配方式
在C++中,記憶體分成5個區,分別是堆、棧、自由儲存區、全域性/靜態區和常量儲存區。
- 棧
存放函式引數以及區域性變數,在出作用域時,將自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但分配的記憶體容量有限。 - 堆
new分配的記憶體塊(包括陣列、類例項等),需delete手動釋放。如果未釋放,在整個程式結束後,OS會幫你回收掉。 - 自由儲存區
malloc分配的記憶體塊,需free手動釋放。它和堆有些相似。 - 全域性/靜態區
全域性變數(global)和靜態變數(static)存於此處。在以前的C語言中,全域性變數又分為初始化的和未初始化的,C++中不區分。 - 常量儲存區
常量(const)存於此處,此儲存區不可修改。
棧與堆的區別
主要區別:
- 管理方式不同
棧是編譯器自動管理的,堆需手動釋放。 - 空間大小不同
在32位OS下,堆記憶體可達到4GB的的空間;而棧就小得可憐。VC6中棧預設大小是1M,當然可以對其進行修改。 - 能否產生碎片不同
對於棧來說,進棧/出棧都有著嚴格的順序(先進後出),不會產生碎片;而堆頻繁的new/delete,會造成記憶體空間的不連續,容易產生碎片。 - 生長方向不同
棧向下生長,以降序分配記憶體地址;堆向上生長,以升序分配內在地址。 - 分配方式不同
堆動態分配,無靜態分配;棧分為靜態分配和動態分配。 - 分配效率不同
棧是系統提供的資料結構,計算機會在底層對棧提供支援,進棧/出棧都有專門的指令,這就決定了棧的效率比較高;堆則不然,它由C/C++函式庫提供,機制複雜,堆的效率要比棧低得多。
可以看出,棧的效率要比堆高很多,所以,儘量用棧。不過,雖然棧有如此多的好處,但遠沒有堆使用靈活。
作用域和生存期
作用域,顧名思義,就是作用的區域,分為函式原型作用域,區域性作用域,類作用域和名稱空間作用域。它們的作用範圍按此順序變大。
生存期,顧名思義,就是生存的時間,分為靜態生存期和動態生存期。靜態生存期有兩種情況:1.名稱空間作用域中的變數具有靜態生存期;2.在區域性作用域中用static宣告的變數也具有靜態生存期。除了這兩種情況之外的變數都具有動態生存期。
在區域性作用域中宣告的變數如果用static修飾,則具有靜態生存期,注意,雖然在整個程式中這個變數都存在,但它的作用域還是原來的作用域,而且這個變數叫做靜態區域性變數,也就是說生存期和作用域沒有關係。