cin>>n;
pc2=newCGoods[n];
//动态建立数组,不能初始化,调用n次缺省构造函数
……
deletepc;
deletepc1;
delete[]pc2;}
申请堆空间之后构造函数运行;
释放堆空间之前析构函数运行;
再次强调:
由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。
如果没有缺省的构造函数,则不能创建对象数组。
---------------------下面我们再来看一下指针数组和数组指针―――――――――――――
如果你想了解指针最好理解以下的公式:
(1)int*ptr;//指针所指向的类型是int
(2)char*ptr;//指针所指向的的类型是char
(3)int**ptr;//指针所指向的的类型是int*(也就是一个int*型指针)
(4)int(*ptr)[3];//指针所指向的的类型是int()[3]//二维指针的声明
(1)指针数组:
一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。
比如int*a[10];它里边放了10个int*型变量,由于它是一个数组,已经在栈区分配了10个(int*)的空间,也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址,这个时候你可以为这个数组的每一个元素初始化,在,或者单独做个循环去初始化它。
例子:
int*a[2]={newint(3),newint(4)};//在栈区里声明一个int*数组,它的每一个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错
例如 :
int*a[2]={newint[3],newint[3]};
deletea[0];
deleta[10];
但是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,并且在VC中会报错,应该写成如下 :
int*a[2];
a[0]=newint[3];
a[1]=newint[3];
deletea[0];
deleta[10];
这样申请内存的风格感觉比较符合大家的习惯;由于是数组,所以就不可以deletea;编译会出警告.deletea[1];
注意这里是一个数组,不能delete[];
(2)数组指针 :
一个指向一维或者多维数组的指针;
int*b=newint[10]; 指向一维数组的指针b;
注意,这个时候释放空间一定要delete[],否则会造成内存泄露,b就成为了空悬指针.
int(*b2)[10]=newint[10][10];注意,这里的b2指向了一个二维int型数组的首地址.
注意:
在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!
就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。
这与数组的嵌套定义相一致。
int(*b3)[30][20];//三级指针――>指向三维数组的指针;
int(*b2)[20];//二级指针;
b3=newint[1][20][30];
b2=newint[30][20];
两个数组都是由600个整数组成,前者是只有一个元素的三维数组,每个元素为30行20列的二维数组,而另一个是有30个元素的二维数组,每个元素为20个元素的一维数组。
删除这两个动态数组可用下式:
delete[]b3;//删除(释放)三维数组;
delete[]b2;//删除(释放)二维数组;
再次重申:
这里的b2的类型是int(*),这样表示一个指向二维数组的指针。
b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.
(3)二级指针的指针
看下例:
int(**p)[2]=new(int(*)[3])[2];
p[0]=newint[2][2];
p[1]=newint[2][2];
p[2]=newint[2][2];
delete[]p[0];
delete[]p[1];
delete[]p[2];
delete[]p;
注意此地方的指针类型为int(*),碰到这种问题就把外边的[2]先去掉,然后回头先把int**p=newint(*)[n]申请出来,然后再把外边的[2]附加上去;
p代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是int(*)代表二级指针,而int(**)顾名思义就是代表指向二级指针的指针了。
既然是指针要在堆里申请空间,那首先要定义它的范围:
(int(*)[n])[2],n个这样的二级指针,其中的每一个二级指针的最低维是2个元素.(因为要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到)。
然后我们又分别为p[0],p[1],p[2]…在堆里分配了空间,尤其要注意的是:
在释放内存的时候一定要为p[0],p[1],p[2],单独delete[],否则又会造成内存泄露,在delete[]p的时候一定先deletep[0];deletep[1],然后再把给p申请的空间释放掉delete[]p……这样会防止内存泄露。
(3)指针的指针;
int**cc=new(int*)[10];声明一个10个元素的数组,数组每个元素都是一个int*指针,每个元素还可以单独申请空间,因为cc的类型是int*型的指针,所以你要在堆里申请的话就要用int*来申请;
看下边的例子(vc&GNU编译器都已经通过);
int**a=newint*[2]; //申请两个int*型的空间
a[1]=newint[3]; //为a的第二个元素又申请了3个int型空间,a[1]指向了此空间首地址处
a[0]=newint[4]; ////为a的第一个元素又申请了4个int型空间,a[0]指向了此空间的首地址处
int*b;
a[0][0]=0;
a[0][1]=1;
b=a[0];
delete[]a[0] //一定要先释放a[0],a[1]的空间,否则会造成内存泄露.;
delete[]a[1];
delete[]a;
b++;
cout<<*b<注意 :
因为a是在堆里申请的无名变量数组,所以在delete的时候要用delete[]来释放内存,但是a的每一个元素又单独申请了空间,所以在delete[]a之前要先delete[]掉a[0],a[1],否则又会造成内存泄露.
(4)指针数组 :
我们再来看看第二种 :
二维指针数组
int*(*c)[3]=newint*[3][2];
如果你对上边的介绍的个种指针类型很熟悉的话,你一眼就能看出来c是个二级指针,只不过指向了一个二维int*型的数组而已,也就是二维指针数组。
例子 :
int*(*b)[10]=newint*[2][10];//
b[0][0]=newint[100];
b[0][1]=newint[100];
*b[0][0]=1;
cout<<*b[0][0]<delete[]b[0][0];
delete[]b[0][1];
delete[]b;
cout<<*b[0][0]< 这里只为大家还是要注意内存泄露的问题,在这里就不再多说了。
如果看了上边的文章,大家估计就会很熟悉,这个b是一个二维指针,它指向了一个指针数组
第二种 :
int**d[2];表示一个拥有两个元素数组,每一个元素都是int**型,这个指向指针的指针:
)
d不管怎样变终究也是个数组,呵呵,
如果你读懂了上边的,那下边的声明就很简单了:
d[0]=newint*[10];
d[1]=newint*[10];
delete[]d[0];
delete[]d[1];
具体的就不再多说了:
)
二 :
函数指针
关于函数指针,我想在我们可能需要写个函数,这个函数体内要调用另一个函数,可是由于项目的进度有限,我们不知道要调用什么样的函数,这个时候可能就需要一个函数指针;
inta();这个一个函数的声明;
ing(*b)();这是一个函数指针的声明;
让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。
另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。
注意本例中还没有创建指针变量-只是声明了变量类型。
目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:
unsignedpsize=sizeof(int(*)());获得函数指针的大小
//为函数指针声明类型定义
typedefint(*PFUNC)();
PFUNC是一个函数指针,它指向的函数没有输入参数,返回int。
使用这个类型定义名可以隐藏复杂的函数指针语法,就我本人强烈建议我们大内弟子使用这种方式来定义;
下面是一个例子,一个简单函数指针的回调(在GNU编译器上通过,在VC上需要改变一个头文件就OK了)
#include//GNU编译器g++实现
usingnamespacestd;
/*//vc的实现
#include"stdafx.h"
#include
*/
#defineDF(F)intF(){cout<<"thisisinfunction"<<#F<return0;\
}
//声明定义DF(F)替代intF();函数;
DF(a);DF(b);DF(c);DF(d);DF(e);DF(f);DF(g);DF(h);DF(i);//声明定义函数abcdefghi
//int(*pfunc)();//一个简单函数指针的声明
typedefint(*FUNC)();//一个函数指针类型的声明
FUNCff[]={a,b,c,d,e,f,g,h,i};//声明一个函数指针数组,并初始化为以上声明的a,b,c,d,e,f,g,h,i函数
FUNCfunc3(FUNCvv){//定义函数func3,传入一个函数指针,并且返回一个同样类型的函数指针
vv();
returnvv;
}
/*FUNCfunc4(int(*vv)()){//func3的另一种实现
vv();
returnvv;
}*/
intmain(){
for(inti=0;iFUNCr=func3(ff[i]);
cout<}
return0;
}
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSIC/C++的编译器规范。
许多编译器有几种调用规范。
如在VisualC++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。
C++Builder也支持_fastcall调用规范。
调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。
好了,先到此为止吧,写这篇文章耗费了基本上快半天的时间了,很多事情还没有做,等改天有时间再回来整理,所有的源程序都放在openlab3服务器上我的目录下lib/cpp下,大家可以去拿。
不知道的登陆openlab3然后cd~chengx/lib/cpp就可以看到了。
还有很复杂的声明可能也是一种挑战比如<>里的
int(*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是BruceEckel说的“从右到左的辨认规则”是一种很好的方法,值得我们去学习,感谢他:
)
最后我想应该跟大家说一下,写程序应该就象JERRY所说的:
简单就是美;我们应该遵循一个原则:
KISS(KeepItSimple,Stupid,尽量保持程序简单出自:
《PracticalCprogramming》),把自己的程序尽量的简单明了,这是个非常非常好的习惯。
由于写的匆忙,可能其中有遗漏的地方,大家发现希望能指正:
)
GOODLUCK!