C++的string跟vector库分别定义了大小可变的字符串和集合。
string跟vector往往将迭代器用作配套类型来访问string中的字符或者vector中的元素。
string字符串的size操作的返回值是字符串的长度,类型属于string的配套类型,即string::size_type(是一个unsigned int 或者unsigend long类型)
string类型可以通过下标操作来访问string对象中的单个字符
vector类型size操作的返回值是vector集合里元素的个数,类型同理是vector的配套类型vector<T>::size_type (类型同string::size_type)
此处<T>可为int,string 甚至是vector<T>类型分辨表示是一个元素类型为int,string,vector<T>的集合。
vector也可以通过下标操作来访问其中的元素。但是vector下标操作只能用来获取已经存在的元素。
vector元素对象还可以使用迭代器来实现。
迭代器是一种支持可用来遍历容器内的元素,并访问这些元素值操作的类型。如数组的迭代器就是指针。
每种容器都定义了自己迭代起的类型,vector的就是vector<T>::iterator
每一种容器也都定义了一队命名为begin()和end()的函数,用于返回迭代器。
如果vector不空 那么他的begin返回的迭代器就是指向第一个元素
end()函数返回的是vector的末端元素的下一个,它不指向容器里的任何一个元素,只表示我们已经处理完所有的元素
迭代器的解引用操作符(*)可以用来访问迭代器所指向的元素
如:
string str("some string");
vector<int> ivec(5); //定义一个5个元素的vector<int>类型
// 利用下标操作符分别输出str字符串中所有字符
for(string::size_type index=0;index!=str.size();++index)
cout<<str[index]<<endl;
//利用下标操作分别输出vector中的各元素值
for(vector<int>::size_type index=0;index!=ivec.size();++index)
cout<<ivec[index]<<endl;
//下标操作可用作左值
//利用下标操作符将str每个字符全部赋值为'a'
for(string::size_type index=0;index!=str.size();++index)
str[index]='a';
//vector下标操作只能用于获取已经存在的元素
for(vector<int>::size_type index=0;index!=10;++index)
ivec[index]=index; //试图将0-9分别赋值到ivec的第0到第9个元素
//这个是错误的,下标操作只能用于获取已经存在的元素
//此处ivec是只初始化5个元素个数的vector<int>类型
//正确方法应使用push_back();函数
for(vector<int>::size_type index=0;index!=10;++index)
ivec.push_back(index); //依次在ivec后面添加新元素
//利用迭代器分别输出str字符串中各字符
for(string::iterator iter=str.begin();iter!=str.end();++iter)
cout<<*iter<<endl; //使用解引用操作符访问迭代器所指向元素的值
//利用迭代器分别输出vector中的各元素值
for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();++iter)
cout<<*iter<<endl; //使用解引用操作符访问迭代器所指向元素的值
//使用迭代器将str字符串各字符赋值为'a'
for(string::iterator iter=str.begin();iter!=str.end();++iter)
*iter='a';
//使用迭代器赋值容器中的各元素的值
for(vector<int>::iterator iter =ivec.begin();iter!=ivec.end();++iter)
*iter=0; //利用解引用操作符将容器中各元素依次赋值为0
iterator类型跟const_iterator
上面使用了iterator来读取或者改写其对应的值
const_iterator只能用来读取对应的值,而不能对其重新赋值
数组与指针:
数组的定义及初始化
数组的维数必须用值大于等于1的常量表达式定义,此表达式只能包含整型字面值常量(如 5),枚举常量,或者用常量表达式初始化的整型const对象。如:
const unsigned buf_size=512,max_file=20;
int staff_size=127;
const unsigned sz=get_size(); //调用get_size()函数获取一个const unsigned类型
char input_buffer[buf_size]; //ok,因为buf_size是一个已经初始化的整型const对象
string fileTable[max_file+1]; //ok,因为max_file也为一个const对象,
//max_file+1在运行的时候已经可以确定他的值
double salaries[staff_size]; //errer staff_size不是一个const对象
int test_scores[get_size()]; //errer get_size()要在程序运行的时候才能计算得它的返回值
int vals[sz]; //errer 同上
初始化
const size_t array_size=3;
//显式初始化
int ia1[array_size]={0,1,2}; //如果显式初始化个数小于维数,只初始化前面的数组元素
//剩余的若为内置类型则初始化为0,若是类类型就按默认构造函数初始化
int ia2[]={3,4,5,6} ; //不指定维数,按初始化个数来确定数组长度
特殊的字符数组
char ca1[]={'C','+','+'}; //ca1有3个元素
char ca2[]={'C','+','+','/0'}; //ca2有4个元素
char ca3[]="C++"; //用字符串字面值初始化字符数组,ca3有4个元素,隐性加一个空字符
char ca4[6]="string"; //错误,因为"string"有7个字符
不允许数组直接复制跟赋值。
数组可以使用下标对每个元素进行操作,数组下标类型为 size_t类型
//利用下标遍历数组中的各个元素,将其下标赋值给各个元素
const size_t array_size=10;
int ia[array_size];
for(size_t index=0;index!=array_size;++index)
ia[index]=index;
同理可以利用下标操作加for循环将一个数组的元素赋值给另外一个同类型同数组
指针的定义
c++中使用*符号把一个标识符申明为一个指针
string *pstr;
指针的另一种声明风格
string* pstr;
第二种风格容易使人错以为string*是一种新类型,且在声明多个指针时容易出错。如:
string* ptr1,ptr2;
上句声明了一个字符串指针pstr跟一个字符串ptr2。
使用第一种风格就会很好的避免此类问题
string *ptr1, *ptr2;
避免使用未初始化的指针。
如可能的话,除非所指对象已经存在,否则不要先定义指针,这样可以避免一个未初始化的指针。
如果不需分开定义指针跟所指对象,则应将指针初始化成0;
指针的操作
指针提供简介操作其指向的对象,跟迭代器一样可以用解引用操作符获取指针所指向的对象。
string s1("hello world");
string s2="abcde";
string *sp=&s1;
cout<<*sp<<end; //用解引用操作符获取指针所指向对象的值
解引用操作符返回值可以当左值,用来改变指针所指向的对象的值。
*sp="goodbye"; //解引用操作符用作左值来修改指针所指向的对象
给指针直接赋值也可以修改指针所指向对象的值,这样不需要使用解引用操作符
sp=&s2;
左操作值如果有解引用操作符,修改的是指针指向的对象的值
左操作值中没有解引用操作符,修改的是指针的本身
指针跟引用的比较
引用是变量的一个别名,定义引用时必须指定绑定对象,修改引用时同时修改了变量的值
指针是指向一个变量,当修改了指针本身时,并不修改原来指针所指向对象的值
使用指针访问数组元素:
C++中的表达式使用数组名,改名字自动转化为指向该数组第一个元素的指针
int ia[]={0,2 ,4 ,6};
int *ip=&ia; //此时ip指向ia[0]
若要指向到其他元素,使用下标先定位在取地址符获取该元素的地址
ip=&ia[3]; //此时IP指向ia[3]
与其使用下标,使用指针的算术操作更方便。指针加上或者减去一个整型数值n等效获得一个新的指针。
2个指针的减法操作的结果是标准库类型ptrdiff_t的数据.
size_t是unsigend 类型 因为数组的下标不能为负数
ptrdiff_t是signed类型 而指针的减法操作的结果有可能是负数
下标跟指针
使用下标访问数组时,实际上是使用下标访问指针
int ia[]={0,2,4,6,8};
int i=ia[0];
int *p=&ia[2];
int j=p[1]; //p[1]=*(p+1),此处p[1]=ia[3]
int k=p[-2]; //p[-2]其实就是*(p-2)等效于ia[0]
利用指针遍历输出数组元素
const size_t array_size=5;
int ia[array_size]={0,1,2,3,4};
for(int *pbegin=ia,*pend=ia+array_size;pbegin!=pend;++pbegin)
cout<<*pbegin<<endl;
上例中for循环定义了2个int类型的指针pbegin pend
pbegin 指向数组的第一个元素,而pend指向的是数组最后一个元素的下一个(类似vector提供的end()返回的迭代器)
指针跟const限定符
C++强制要求指向const对象的指针也具有const特性
const double *cptr; //cptr是一个指向double类型的const对象的指针,const限定了cptr指针所指向的对象类型,而非cptr本身
也就是说 cptr本身不是一个const,所以在定义时可以不初始化,后面也可以对其重新赋值,使其指向另一个const对象
但不能通过cptr修改它指向的值,如 *cptr=42;
指向const对象的指针理解为“自以为指向const对象的指针”
不能通过指向const对象的指针修改基础对象,然后如果该对象指向的是一个非const对象,可以通过其他方式修改其值
const指针---本身不能修改的指针
int i=0;
int *const ip=&i; //iph是指向int类型对象的const指针,它的值不能修改,且定义时必须初始化。
指向const对象的const指针
const double pi=3.14159;
const double *const pi_ptr=π //pi是一个指向double类型的const对象的const指针
此时pi_ptr本身不能修改,它指向的对象也不能修改
指针跟typedef
typedef string *pstring;
const pstring cstr;
声明const pstring cstr时const修饰的是pstring类型,它是一个指针。
所以cstr定义为一个指向string对象的const指针。等价于string *const cstr;
理解复杂的const类型的声明
阅读const类型的声明难易理解是因为const限定符既可以放在类型名前,也可以放在类型名后
const string s1;
string const s2;
s1跟s2都是一个const string类型
用typedef写const类型定义时,const放在类型名前容易对所定义的类型产生误解,
string s1;
typedef string *pstring;
const pstring cstr1=&s1;
pstring const cstr2=&s1;
string *const cstr3=&s1;
把const放在类型pstring之后,然后从右向左阅读该声明语句就会非常清楚的指导cstr2是一个const pstring类型,即指向string类型的const指针。