C++數組:常類型數組和類對象數組
常類型數組:
1. 數組的定義和初始化:
一維數組:
1>
//stack--------------------------------------------
int a[10];
//cout<<a[0]<<endl; error 未初始化不能訪問
int a[10]={1,2}; //a[0]==1,a[1]==2,a[3]==0,a[3~9]==0
int b[]={1,3}; //length of b is 2.
//heap----------------------------------------------
int d=3;
int *c=new int[d];
delete c;// 防止內存泄漏
2> 動態數組的含義
const int i=3;
int a[i];// OK
int j=3;
int b[j];// error
int *c=new int[j]; // OK
在定義int[]時,編譯器必須知道要在棧中分配多少內存,所以[]內的必須是常數或常量。在定義int *a=new的時候,由於是動態分配內存,所以編譯的時候不實際分配,在運行纔會指定內存分配的大小單元。
3> 求數組的長度通用做法
int a[]={1,2,3,4,5,6};
int length=sizeof(a)/sizeof(a[0]);
因爲這裏sizeof(a)==sizeof(int)*n;而sizeof(a[0])==sizeof(int)。所以可以求得數組的長度。
注意不能在其他函數中使用,因爲數組在傳參過程中會退化成指針,使得sizeof(a)==sizeof(int*):
void foo(int a[])
{
cout<<sizeof(a)/sizeof(a[0])<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a[]={1,2,3,4,5,6};
foo(a);
system("pause");
return 0;
}
在window 64bit結果是2。注意如果編譯64bit程序,vs必須調成64bit編譯。Vs默認的是32bit編譯
二維數組:
//stack------------------------------------------------------------
int a[5][5];
int b[5][5]={{1,2},{5}};
int c[][4]={{1,2},{6,9}};
//int c[][]={{1,2},{6,9}}; error
//heap--------------------------------------------------------------
//1
int m=2;const int n=3;
int (*a)[n]=new int[m][n];//n必須是常量,m可以是變量
a[0][0]=1;
delete[] a;//因爲申請的空間是連續的,所以可以一下子釋放
//2
int i;
int **d=new int*[m];//m可以是變量
for(i=0;i<m;i++) d[i]=newint[i+1];//之後才能使用d[i][j]
d[0][0]=2;
for(i=0;i<m;i++) delete[]d[i];//因爲申請的空間不連續,所以要一個一個釋放
delete[] d;//注意最後別忘了這句,感覺new的數量應該始終和delete一樣。
1> 注意:new和delete的數量應該始終是一樣的。因爲無論是delete還是delete[],它一次都只能釋放一塊連續的內存(對於普通類型來說)。而每次new出來的內存一般都是和上次new出來的內存是不連續的,所以要分別delete掉new出來的內存。
2> New是動態申請數組,後面申請的大小一般都可以是變量。而”=new”之前的,例如上面的int (*a)[n]=new。這裏編譯器需要知道n的大小,所以=new之前的一般必須都是常量。
3> 指針數組和數組指針
int (*a)[10]=new int[10][10];//數組指針,a就是一個指針,它指向一個數組。
int *p[10];//指針數組,其實應該這麼寫:int* p[10];這樣看就是一個一維數組,數組的每個元素都是一個指針。p是數組名。而不是指針,所以不能delete p;
for(inti=0;i<10;i++)
p[i]=new int[10];
p[0][0]=1;
for(inti=0;i<10;i++)
delete p[i];
4> 二維數組的訪問
int (*a)[10]=new int[10][10];
a[3][3]=2;
cout<<(*(a+3))[3]<<endl;
cout<<*(a[3]+3)<<endl;
cout<<*(*(a+3)+3)<<endl;//這3個其實都是訪問的a[3][3]
解釋一下:首先a可以看作該二維數組的首地址。而編譯器把二維數組a看成一個一維數組,只是這個一維數組的每個元素又是一個一維數組。所以*(a+3)其實是訪問a一維數組的第4個元素(也就是a[3])。因爲a[3]又是一個一維數組,所以*(a[3]+3)就是訪問a[3]這個數組中的第4個元素。即a[3][3]。
標準寫法:*(*(a+i)+j) == a[i][j]
2. 數組和指針的關係
1. 數組名只是表示一種數據結構。而指向數組的指針,表示它存放的是數組的地址。
char *a="123456";//編譯器會在常量區存放“123456”,而會在棧區存放一個指針a,a指向這個常量區的內容。
//也就是說是絕不能去改a指向的內容的。不可以a[0]=='9'.
char a[]="123456";//編譯器只是在棧區開闢一個連續的空間,就是數組。每個空間內分別存放'1','2','3','4','5','6','\0'
//該空間大小爲7,且這些存放的值是可以去改的。例如a[0]='9';
2. 指向數組的指針,是可以自加,自減,重新指向別的內容,可以deletep;
數組名是不可以修改的,有點像常量指針。更不可以delete a;
char *a="123";
a++;
cout<<*a<<endl;
char b[]="123";
//b++; error
3. 當數組名作爲實參傳遞時,無論形參的形式怎樣,編譯器都是用指針來接收實參的。
void foo(char a[])
{
a++;
cout<<*a<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
char b[]="123";
foo(b);
system("pause");
return 0;
}
也就是說,在函數foo內,a就是一個指針,不再是函數名了。它可以自加,自減。完全就是一個指針了。
所以在函數foo內sizeof(a)是等於一個指針的大小的。
3. 數組的存儲格式
多維數組的在內存中的存儲是按照最低維連續的格式來存儲的。(注意和mat的區別,在mat中是按列儲存的)
比如:a[3][3]內存中的存儲是:
a[0][0] a[1][0] a[2][0]
a[0][1] a[1][1] a[2][1]
a[0][2] a[1][2] a[2][2]
類對象數組
1. 棧存儲,局部變量數組。
class A{};
class B
{
public: B(int i){}
};
class C
{
public: C(int i, int j){}
};
int _tmain(int argc, _TCHAR* argv[])
{
Aa[10];//構造函數可以無參的。
Bb[5]={1,2,3,4,5};//構造函數只有一個參數的,其實是利用了隱式轉換
Cc[3]={C(0,1),C(1,2),C(2,3)};//構造函數有兩個以上的參數的,對象數組只能這樣定義。
system("pause");
return 0;
}
2. 堆存儲。
class A{};
class B
{
public: B(int i){}
void foo(){}
};
int _tmain(int argc, _TCHAR* argv[])
{
A*a=new A[10];
delete[] a;//這裏只能是delete[] a,不能用delete a。delete[] a調用a[0~9]的所有析構。而delete a只調用可一個a[0]的析構
//注意!!這裏由於A中沒有動態分配內存,所以delete a,這裏也不會產生內存泄漏。只是沒有調用全部的析構函數而已。
//如果A中new了堆內存,而在析構中釋放這些內存,就必須要delete[] a了。不然就會引發內存泄漏。
B*b[10]; //有參的構造函數,想要在堆中分配,就要用到指針數組。
for(inti=0;i<10;i++)
b[i]=new B(i);
b[0]->foo();
for(inti=0;i<10;i++)
delete b[i];//由於分開new的,所以要一個一個delete。否則一定會產生內存泄漏。
_CrtDumpMemoryLeaks();//檢測內存是否泄漏函數
system("pause");
return 0;
}
總結一下:就是說delete和delete[]的本質區別,就是調用了一個析構函數還是調用一組析構函數。
引申總結:delete
1. delete和delete[]的本質區別?
衆所周知在普通類型對象中,delete和delete[]是沒有區別的。
在類對象數組中是有區別的,區別是調用了析構函數的多少。
delete只調用了一個析構。Delete[]調用了一組析構。
2. 對於數組的釋放,是一下子delete,還是要分開一個一個delete?
delete使用的次數取決於new的個數。
如果數組是連續的,一下子new出來的,只需要delete/delete[]一次即可。
如果數組是不連續的,分開new出來的,則需要一個一個delete/delete[]。