C++提供了两种类似于vector和迭代器的低级复合操作--数组和指针。与vector类似,数组也可以保存某种类型的一组对象;而它们的区别在于,数组的长度是固定的。数组一经创建,就不允许添加新的元素。指针则可以像迭代器一样用于遍历和检查数组中的元素。
C++中尽量使用vector和迭代器,而避免使用低级的数组和指针。
1、数组
在定义数组时可以为其提供一组用逗号分隔的初值,这些初值用花括号{}括起来,称为初始化列表:
const unsigned array_size=3;
int ia[array_size]={1,2,3};
如果没有显示提供元素初值,则数组元素会像普通变量一样初始化:
、在函数体外定义的内置数组,其元素初始化为0;
‚、在函数体内定义的内置数组,其元素无初始化;
ƒ、不管数组在哪里定义,如果其元素为类类型,其自动调用该类的默认构造函数进行初始化,如果该类没有默认构造函数,则必须为该数组的元素提供显示初始化。
显示初始化的数组不需要指定数组的维数,如
int ia[ ]={1,2,3};
如果指定了数组维数,那么初始化列表提供的元素个数不能超过维数值。如果维数大于元素初始值个数,则只初始化前面的数组元素;剩下的其他元素,若是内置类型初始化为0,若是类类型则调用该类的默认构造函数进行初始化。
特殊的字符数组
字符数组既可以用字符进行初始化,也可以用字符串字面值进行初始化。注意这两种形式并不完全相同,字符串字面值包含一个额外的空字符(null)用于结束字符串。
char ca1[]={'c','+','+'}; //no null,3维
char ca2[]={'c','+','+','\n'}; //no null,4维
char ca3[]="c++"; //null terminator added automatically,4维
与vector不同,一个数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组。
int ia[ ]={1,2,3};
int ia2[](ia); //error
与vector元素一样,数组元素可用下标操作符来访问,数组元素也是从0开始计数。
vector使用vector::size_type作为下标类型,而数组下标正确的类型则是size_t。
for循环遍历数组的10个元素,并以下标值作为各个元素的初始值:
<pre name="code" class="cpp"><pre name="code" class="cpp">#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const size_t array_size=10;
int ia[array_size]; //已分配内存,不为空,未初始化
cout<<"ia[10]={";
for(size_t ix=0;ix!=array_size;++ix)
{
ia[ix]=ix;
cout<<ia[ix];
if(ix!=9)
cout<<",";
}
cout<<"}";
system ("pause");
return 0;
}
‚
int main()
{
vector<int> ivec; //ivec为空
for(vector<int>::size_type ix=0;ix!=ivec.size();++ix)
{
ivec[ix]=0; //正确
}
system("pause");
return 0;
}
ƒ为vector添加10个元素
int main()
{
vector<int> ivec; //ivec为空
for(vector<int>::size_type ix=0;ix!=10;++ix)
{
ivec[ix]=ix; //错误,不能用下标为vector添加元素
ivec.push_back(ix); //正确,用push_back为vector添加元素
}
system("pause");
return 0;
}
2、指针
vector的遍历使用下标和迭代器,同理可以使用下标和指针遍历数组。
指针用于指向对象,与迭代器一样,指针提供对其所指对象的间接访问,与迭代器不同的是,指针指向单个对象,而迭代器只能用于访问容器内的元素。
string s("hello world");
string *sp=&s; //sp为指向string类型的指针,指向s
指针的定义
string *sp;
int *ip1,*ip2;
vector<int> *pvec;
另一种定义形式:
string* sp; //和上面的形式是一样的意思,此形式容易引起误解
string* sp,sp1; //sp为指向string类型的指针,sp1为string类型变量
避免使用未初始化的指针,建议在使用之前初始化所有的变量,尤其是指针。如果可能的话,除非所指向的对象已经存在,否则不要先定义指针。如果必须分开定义指针和其指向的对象,则将指针初始化为0.
int *ip=0;
void*类型的指针可以保存任何类型对象的地址:
double obj=3.14;
double *pf=&obj;
void *pv=&obj;
void*表明该指针与一地址相关,但不清楚存储在该地址上的对象的类型。
与对迭代器进行解引用一样,对指针进行解引用可以访问它所指向的对象。
string s("hello world");
string *ps=&s;
cout<<*ps; //print hello world
给指针赋值和通过指针进行赋值区别:如果对左操作数进行解引用,则修改的是指针所指对象的值;如果没有使用解引用操作,则修改的是指针本身的值。
string s1("some thing");
string *sp1=&s1;
string s2("another");
string *sp2=&s2;
*sp1="a new value"; //修改的是sp1所指向的对象s1的值,s1="a new value"
sp1=sp2; //修改的是sp1的值,sp1指向s2
指针和引用的比较:
‚引用总是指向某个对象,定义时必须初始化
‚给引用赋值修改的是该引用所关联的对象的值
ƒ引用一经初始化,始终指向同一个对象。指针可以改变所指向的对象。
int ival=1024,ival2=2048;
int *pi=&ival,*pi2=&ival2;
pi=pi2;
以上程序修改了pi指针的值,使pi指向ival2;而ival的值没变。
int ival=1024,ival2=2048;
int &ri=ival,&ri2=ival2;
ri=ri2;
以上程序修改了ival的值,ival=ival2=2048。
指针访问数组
在表达式中使用数组名时,该名字会自动转换为指向数组第一个元素的指针:
int ia[]={0,2,4,6,8};
int *ip=ia; //ip=&ia[0]
int *ip2=ia+4; //ip2=&ia[4]
指针加上一个整数值其结果仍然是指针,允许在这个结果上直接进行解引用操作。
int last=*(ia+4); //last=ia[4]
int last=*ia+4; //last=ia[0]+4
解引用的优先级比加法操作符要高。
数组的超出末端指针:数组名+数组长度
vector类型提供end操作返回超出末端的一个迭代器,类似地,可以计算数组的超出末端指针,不允许对超出末端指针进行解引用操作。
const size_t array_size=5;
int arr[array_size]={0,2,4,6,8};
int *ip=arr; //ip=&ia[0]
int *p2=ip+array_size; //p2为超出末端指针
输出数组元素:
const size_t array_size=5;
int arr[array_size]={0,2,4,6,8};
for(int *pbegin=arr,*pend=arr+array_size;pbegin!=pend;++pbegin)
{
cout<<*pbegin<<" ";
}
指向const对象的指针
指向const对象的指针也必须具有const特性。
const double *cptr;
cptr是一个指向double类型const对象的指针,const限定了cptr指针所指向的对象类型,而非cptr本身。就是说cptr本身并不是const。在定义时不需要初始化,如果需要可以对cptr重新赋值,使其指向另一个const对象,但是不能通过cptr修改其所指向的对象的值。
const double pi=3.14;
double *ptr=&pi //error
const double *cptr=&pi //ok
允许把非const对象的地址赋值给指向const对象的指针
double dval=3.14;
const double *cptr=&dval;
尽管dval不是const对象,但不能通过cptr指针修改dval的值。
const指针
const指针其本身的值不能修改:
int errNumb=0;
int *const curErr=&errNumb;
curErr是指向int型变量的const指针。与其他const量一样,const指针的值不能修改,不能使curErr指向其他对象,在定义时必须初始化。
创建动态数组
数组有三个重要限制:数组长度固定不变,在编译时必须知道其长度,数组只在定义它的块语句内存在。
动态数组不必在编译时知道其长度,可以在运行时才确定数组长度。动态数组创建后将一直存在,直到程序显示释放它为止。c++使用new和delete实现创建动态数组和释放。
创建动态数组时,只需要指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:
int *pia=new int[10];
此new表达式分配了一个含有10个int型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针pia
size_t n=getsize();
int *p=new int[n];
动态空间的释放
动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。
delete [] pia;
该语句回收了pia所指向的数组,把相应的内存返回给自由存储区。在关键字delete和指针之间的方括号是必不可少的,它告诉编译器 该指针指向的是自由存储区中的数组,而并非单个对象。