// 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修饰,则具有静态生存期,注意,虽然在整个程序中这个变量都存在,但它的作用域还是原来的作用域,而且这个变量叫做静态局部变量,也就是说生存期和作用域没有关系。