c++字符串、向量和数组

1. 标志库类型string

1.1 string的初始化

string的初始化方式有5种,详细见下面程序1。在c++标准规定了对象初始化分为直接初始化和拷贝初始化,在string对象中有体现,详见程序2。那问题来了什么时候使用直接初始化或者拷贝初始化?如果初始单个值,无论使用哪种初始化都行,如果初始是多个值,推荐使用直接初始化,这样可以减少拷贝成本。

程序1
string s;   //默认初始化,初始的值为空串
string s2(s1);  //s2是s1的副本
string s3 = s1; // 与s2(s1)等价
string s4 = "hello"; // s4是字面值“hello”的副本
string s5("hello");  //与s4="hello"等价
string s6(10,'h'); //把s6初始化为10个h字符

程序2
拷贝初始化:它是执行拷贝完成初始化操作的,因此他最大的特点就是带有=号;
直接初始化:与拷贝初始化相反,没有等号;
string s7="hello2"; //拷贝初始化
string s8("hello2"); //直接初始化
string s9(s7); //直接初始化
string s10(10,'c'); //直接初始化

1.2 string的操作

1.2.1 string的读写

string的写通常只有一种输出方式,就是通过cout输出,见程序3:

程序3:
string s = "hello";
cout << s << endl;

string的读有两种方式,一种是从输入流读取当个字符串给string对象,另一种是从输入流读出一行给string对象。详细区别见程序4:

程序4:
string s;
cin >> s; // 在读取的过程中,string对象会自动忽略开头的空白(空格符、换行符、制表符等),并从第一个字符读起,
//直到遇到下一个空白为止。读取的字符对象不包含结尾空白,同时读取流的缓冲区有不包含结尾的空白
//例如,如果程序输入"  hello  ",输入实际字符串值为:"hello"
getline(cin,s); //在读取的过程中,它会最开始输入字符(包含空白)开始,直到遇到换行符为止;读取的
//字符对象不包含结尾的换行符,但是读取流的缓冲区有包含结尾的换行符的,这有时会影响下一次字符串读取

1.2.2 string::size_type类型

string::size_type是string的size函数的返回值,由于string的下标是非负的,因此该类型为无符号类型的值。

1.2.3 string对象的比较

>、=、<、<=、>=是string对象比较大小的关系运算符,都是按照字典顺序比较大小的

1.2.4 string对象赋值和相加

string对象允许把一个对象的值赋值另外一个对象,并且允许两个string对象相加,相加string就等价于两个字符串的拼接,同时也支持字面值和string对象相加,但是不允许两个字面值相加。见程序5:

程序5
string s = s1+",";  //正确,字面值会自动转换成string对象,再执行string加法运算
string s2 = "hello"+"kuang"; //错误,两个都是字面值,字面值无法转换,无法执行string加法运算,报错

1.3 string中字符处理

string对象对字符可以对整个string对象全部字符处理,通常采用for循环的方法;也可对单独、特殊字符进行处理,通常采用下标或者迭代器,采用下标运算符访问当个元素,在string对象下标运算符[]返回值是字符的引用。注意:下标可以访问或修改string对象已有字符,但不能通过下标添加字符到string对象,vector下标符号也是一样的。见程序6:

程序6
//访问string对象内字符
string str("hello kuang");
for(auto c:str)
 {
        cout << c << endl;
 }
//修改string对象内字符
string str("hello kuang");
for(auto &c:str)             //auto &c = char,把变量c变成单个字符的引用
 {
        c = toupper(c);  //小写转大写
 }
//采用下标运算符访问当个元素,在string对象下标运算符[]返回值是字符的引用
str[0]='y'; //str的值会变成"yello kuang"

2. vector

vector表示对象的集合,其中包含的对象类型相同。vector是对象的集合,而引用不是对象,在c++内置对象中引用和指针区别中讲过,所以不存在包含引用的vector。

2.1 vector的初始化

vector初始化的方法有7中,详细见程序7。一般情况下对vector对象初始一般用括号,如果要对vector对象包含值初始化,通常是使用花括号,采用初始化列表的方式进行初始化。有时这两种情况会出现等价的情况,当vector集合包含的对象不是int,vector v1(10)vector v1={10}是等价的,造成这种情况是由c++首先判断10能不能用来作为列表初始值
,显然不能,c++编译器就会把它作为初始化对象个数的参数,如果还不满足就报错。当vector集合包含的对象是int,就必须严格按照规定来初始化,因为此时编译器无法判断这个整形参数是列表初始值还是元素数量。详细见程序8:

程序7
vector v1; //默认初始化
vector v2(v1); // v2包含v1的副本
vector v2 = v1; //等价于 v2(v1)
vector v3(n,val) //v3包含了n个重复元素,其中n为int,val为T类型
vector v4(n);  // v4包含了n个具有默认值重复元素
vector v5{a,b,c}; //a、b、c类型为T,v5包含了3个具有不同初值的元素
vector v5={a,b,c}; //v5{a,b,c}等价,其中a、b、c类型为T

程序8
//输入元素的个数
vector v2(99);              
cout << v2.size() << endl;    // 99
vector v3(22,100);
cout << v3.size() << endl;  // 22
vector v4{33};
cout << v4.size() << endl;  // 1
vector v5{11,33};
cout << v5.size() << endl; // 2
vector v6{1000};
cout << v6.size() << endl; // 1000
vector v7{44,"he"};
cout << v7.size() << endl; // 44

2.2 vector操作

vector的操作与string操作大致相同,那咱们说vector独有方法,v.push_back(t)方法是vector添加元素的方法,还有vector对象比较和string对象比较也类似,如果两个vector对象容量不同,但在相同位置上元素都相等的话,则元素多的大,如果元素的值有区别,则vector对象大小有第一个开始不等元素大小决定。vector比string不同的是,只有vector包含元素是能够比较的,才能够进行比较。最后一点,vector不能用下标形式添加元素,这与string对象一致。

3. 迭代器

在c++标准库中容器对象和string对象都具有(包含)迭代器对象,所谓的迭代器对象类似于指针,它提供了对象元素的访问,其对象元素比如说:容器中的元素或者string对象中的字符。那么迭代器对象该如何获得呢?所有的容器对象和string对象可通过自己成员函数beginend获得,begin函数返回指向第一个元素的迭代器;end函数返回指向最后一个元素下一个位置的迭代器(尾迭代器),这是本身就容器或者string对象末尾的一个标志,并不指向容器或者string内任何一个元素。

3.1 标准迭代器运算符号

程序 解释
*iter 返回迭代器指向元素的引用
iter->mem 解引用iter并获取该元素对象的mem成员,等价于(*iter).mem
++iter 令iter指向容器或string对象的下一个元素
--iter 令iter指向容器或string对象的上一个元素
iter1 == iter2 判断两个迭代器是否相等,如果两个迭代器指向的是同一个元素或者他们同时指向尾迭代器,则相等,反之,不相等

注:从标准的迭代运算符号表来看,迭代器跟指针太像了

3.2 迭代器类型(类似于指针类型)

迭代器为了更好控制程序访问元素读写权限,迭代器引入了iterator和const_iterator两种类型,其中iterator类型的迭代器可以对元素可读可写,而const_iterator只能读取,不能写。这两种迭代器类型是由其对象(容器或string)决定的,如果它们是常量,它们的迭代器类型就是const_iterator;如果它们不是是常量,它们的迭代器类型可以是两种类型的任何一种,但通常它们调用begin或者end函数返回的迭代器类型一定是iterator。

vector::iterator it; //it指向元素可读可写
vector::const_iterator ct;//it指向元素可读不可写

3.3 string或vector支持迭代器运算

程序 解释
iter+n 迭代器指示由当前元素向后移动n个元素
iter-n 迭代器指示由当前元素向前移动n个元素
iter1-iter2 返回两个迭代器指向元素之间相差的元素,这个是有正负的,正的表示iter1比iter2在容器中位置靠后,负的反过来
>、>=、<、<= 比较迭代器指向的位置,位置越小代表的值越小

4. 数组

数据与vector类似,也是保存一系列对象,引用不是对象,因此也不存在引用数组,这一点与vector一样的;它们之间的区别,数据大小在初始化时候固定不变的,而vector大小是随时可变的,同时vector具有良好动态性能,适合动态添加元素。

4.1 数组初始化

通用数组初始化。

unsigned cnt = 42; //不是常量表达式
constexpr unsigned sz = 42; //常量表达式
int arr[sz];  //正确
int arr2[cnt]; //错误,在定义数组形如a[d]时候,d只能是常量表达式、字面值、整形常量
const unsinged yz = 3;
int ial[yz] = {0,1,2}; //采用列表初始化数组
int a2[] = {0,2};  //等价于维度为2的数组
int a3[4] = {0,1}; //等价于a3={0,1,0,0}
int a4[2]={0,1,2}; //错误:当定义数组的大小要比列表初始化值个数要小时候,报错

字符数组初始化在使用列表初始化和使用字面值初始化是不同的,字符串数组使用字面值初始化会自动在字面值字符串后面加上空字符('\0'),这是因为字符串字面值在c++底层就是字符数组,并且在末尾带有'\0',而使用列表初始化时候不会加。注意,使用字面值初始化字符数组要加上空字符,因此字符数据长度=可见字符数目+1,见程序

char a1[] = {'c','h','i','n','a'} ;  //列表初始化字符数组,没有空字符
char a2 = "china" ;  //字面值初始化字符数组,其实相当于拷贝初始化,带有空字符'\0'

在标准c++当中,数组之间是不允许拷贝和赋值的。注意,有些编译器上可以行的,我们一般建议采用c++标准,这样的代码具有更强的兼容性

int a[] = {1,2,3};
int b[] = a; //错误,不能使用一个数组采用拷贝初始化方式初始化数据
b = a; //错误,数组之间不能赋值

复杂数组声明:对于复杂的数据声明我们安装向内向外,再从右到左的顺序阅读,理解含义。

int (*Parray)[10] = &arr;  // 首先看最里面括号部分,*Parray是一个指针,然后在看外层右边,[10]可知Parray是个指向大小为10的数组指针,
//最后看到外层左边int,最终可知:Parray是个指向大小为10的整形数组指针
int *(&array)[10] = ptrs; //array是个数组引用,这个数组包含10个int型的指针

4.2 数组元素的访问

数组元素的访问是采用下标访问的,同时也支持for循环,大致与string对象元素访问类似,不同一点是,string对象可以采用迭代器访问元素。

4.3 指针和数组

c++编译器会把数组名自动替换成指向数组首元素的指针,数组操作等同于指针操作。

int ia[] = {0,1,2,3,4,5}
int *p = ia;
int *p1 = &ia[0]; //两种写法等价

指针也是迭代器,数组的指针也支持运算,运算方式也迭代器类似,同时为了数组指针能够更好的获得首指针和尾后指针(最后一个元素的下一位置),c++提供了begin(数组名)和end(数组名)两个函数,例如:

int a[] = {1,2,3,4};
int *beg = begin(a);
int *end = end(a);
for(int *b = beg;b!=end;++b)
{
  cout << *b << endl;
}

数组的指针和string、vector都支持下标运算,不同的是指针的下标运算可以为负值下标。

int a=[0,2,4,6,8}
int *p = &a[2];
int j = p[1];  // j = a[3]
int x = p[-2];  // x = a[0]

4.4 C风格的字符串与标准c++的string的区别

c风格字符串在c中是使用字符数组存储的,c为了表达字符,通常在存放字符数组的最后加上'\0'字符,而标准string不用。例如:

char ca[] = {'a','b','c'};
cout << strlen(ca) << endl; //严重错误,ca数组没有空字符结束,strlen函数无法判断c字符串的长度

字符串比较的差别,标准字符串string通过>、<、==号就比较,而c风格的字符串只能通过strcmp函数来实现。标准字符串可通过加号实现string对象和string对象或者string对象和字符串字面值实现字符串的拼接,而c风格的字符串只能通过strcat()和strcpy()实现字符串的拼接,而且目标字符串的需要程序员自己调节,一旦目标字符串的元素个数设置不合理,就会导致程序出错。
4.5 标准库提供给与旧代码的接口
string的c_str()函数,能够把标准库的string对象转换为字符数组;c++标准不允许用vector初始化数组,但数组可以初始化vector对象:

int a = {0,2,3,4};
vector ivec = (begin(a),end(a)); //通过首数组指针和尾后指针来初始化vector对象

5 多维数组

二维数组的第一个维度称为行,第二个维度称为列。

5.1 多维数组的初始化

int a[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};   //初始化 3行*4列数组
int a2[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //与上一句等价
int a3[3][4] = { {0},{4},{8}};  //显示初始化每行的首元素

你可能感兴趣的:(c++字符串、向量和数组)