c++ primer学习笔记 第三章

第三章 字符串 向量和数组

第二章介绍的内置类型是c++直接定义的,标准库定义了一组具有更高级性质的类型,例如string和vector。

1.命名空间的using声明

目前为止,我们用到的所有库函数基本都属于命名空间std。

格式:using 命名空间 :: 名字,如using std :: cin。

头文件最好不要包含using声明,因为头文件内容会被拷贝到所有引用它的文件中,如果头文件有个using声明,那么所有使用了该头文件的文件都会有这个声明。不经意间可能会产生问题。

2.标准库类型string

表示可变字符序列。必须包含string头文件和命名空间声明。

#include <string>

using std::string

1.定义和初始化string 对象

string s1;

string s2=s1;string s3="value";//

string s2(s1);string s3(value);string s4(n,'c')//

2.string对象的操作

1.相加

可以相加s1+s2,可以和字符字面值(单引号’\n‘)和字符串字面值”,“相加,但是确保+两侧至少有一个对象是string;

注意:字符串字面值不同于string

2.比较

可以比较相等或者不相等s1==s2还可以比较大小(大小写敏感)

3.读写

可以用<< >>读写,自动忽略开头的空白,直到遇到下一个空白

可以用getline读取一行,直到遇到换行符

4.s.size(),s.empty()

s.size()返回的不是int类型,而是size_type类型,是无符号整型数,所以一个表达式不要同时包含s.size()和int这种有符号的

5.处理每个字符:范围for语句

string s("hello");

for (auto c:s)

cout<<c<<endl

for(auto &c:s)

c=toupper(c);

6.处理一部分字符:下标索引

使用下标/索引,s[0]代表第一个字符。[ ]接受的输入参数是string::size_type类型

3.标准库类型vector(容器)

表示对象的集合,所有对象类型相同,每个对象有一个索引。必须包含头文件和using声明。

#include<vector>

using std::vector

vector是一个类模板。模板不是类或者函数,是编译器生成类或者函数的一份声明。编译器根据模板创建类或者函数的对象称为实例化。使用模板时,需要指出编译器应把类或者函数实例化为何种类型。

vector<int> ivec;

vector的元素(对象)可以是很多类型,甚至可以是vector,但是不能是引用,因为引用不是对象。

1.初始化

常用默认初始化,也可以是拷贝初始化,直接初始化,列表初始化

vector<int> v1(10); //v1有10个元素,每个的值都是0

vector<int> {10}, //v1有1个元素10

列表初始化{ }里面的值类型要和元素类型一样,不然编译器会用默认值初始化。

vector<string> v1{10}; //v1有10个默认元素

vector<string> v1{"hi"}; //正确

通常先创建一个空的,再向里面添加元素。

2.向vector对象添加元素

使用成员函数push_back,把值添加到vector对象的尾端。

string word;

vector<string> text;

while(cin>>word)

text.push_back(word);

范围for语句不应改变其遍历序列的大小,所以循环体内包含有向vector对象添加元素的语句时不能使用范围for循环。**

3.其它vector操作

也可以比较

与string类似,size返回的是size_type类型

使用下标索引,返回的也是size_type类型。

缓冲区溢出(buffer flow)——通过下标访问不存在的元素

4.迭代器(iterator)介绍

迭代器可以用来访问容器对象的元素。所有标准库容器都可以使用迭代器,但只有少数同时支持下标访问元素。

严格说,string不属于容器,但是支持迭代器。

迭代器类似于指针,也提供对对象的间接访问。

1.使用迭代器

获取迭代器不是通过取址运算符,支持迭代器的类型都有返回迭代器的成员函数,如begin(返回第一个元素或字符)和end(返回最后一个元素或字符的下一个位置,尾后迭代器)

cbegin和cend返回的是常量迭代器。只能读不能写。

如果容器为空,两个返回的是同一个迭代器。

迭代器类型一般使用自动推导auto。实际上iterator和const_iterator通常表示迭代器类型。

vector<int> v;

const vector<int> cv;

auto it1=v.begin();

auto it2=cv.begin();

迭代器运算符
1.解引用

获得迭代器指向的元素。执行解引用的迭代器必须合法指向某个元素。

string s("hello");

if(s.begin()!=s.end()) //s不为空

auto it=s.begin();

*it=toupper(*it)

2.移动迭代器

递增运算符++。下面使用!=而不是用<,因为c++中素有标准库容器的迭代器都定义了==和!=,但不是所有的都有<。

for(auto it=s.begin();it!=s.end()&&!=isspace(*it);++it)

*it=toupper(*it);

3.解引用的成员访问

如果容器对象是类,可以进一步访问成员函数。字符串组成vector对象,要检查其元素是否为空:

(*it).empty();也可以写成it->empty();

向迭代器所属容器中添加元素会使迭代器失效。

2.迭代器运算

string和vector的迭代器提供了更多额外的运算符。可以相加,相减,比较大小。

二分搜索

//text必须有序

auto beg=text.begin(),end=text.end();

auto mid=text.begin()+(end-beg)/2;

while(mid!=end&&*mid!=sought)

if(*mid>sought)

end=mid;

else

beg=mid+1;

mid=beg+(end-beg)/2;

5.数组

数组与vector类似,但是大小固定,程序运行性能较好,但不灵活。

1.定义和初始化

a[d],a是名字,d是维度。

不允许使用auto由初始值列表推断类型;数组元素为对象因此不存在引用的数组

利用字符串字面值初始化数组,注意结尾有个空字符。

char a[]="c++";

不允许将一个数组的值赋给另一个数组。

可以定义存放指针的数组, 但是不存在引用的数组。int *ptr[10]

可以定义数组的指针和数组的引用。

int (*parry)[10] = &arr; parry指向一个含有10个整数的数组

int (&arref)[10] = arr; arref引用一个含有10和整数的数组

int *(&arry)[10] =ptrs; arry是数组的引用,该数组含有10个指针。

2.访问数组元素

范围for语句或者下标。

3.数组和指针

使用数组的时候编译器一般会将其转换为指针,大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。

string *p=&num[0];等价于string *p=num;

自动类型推导时,如果ia是数组

auto ia2(ia); 等价于auto ia2(&ia[0]) 所以ia2的类型时指针

decltype(ia) ia3;不会发生上述情况,ia3是数组。

指针也是迭代器

获取指向数组首元素的指针:int *p=arr;或者int *beg=begin(arr)

获取指向数组尾元素下一个位置的指针:int *p=&arr[最后一个下标+1];或者int *last=end(arr)

指针不是类类型,所以begin和end函数不是成员函数,只是标准库函数。

指针运算

解引用,递增,比较,相加,但要指向同一个对象。

int a[]={0,2,4,6,8};

int p=*(a+4);

下标

很多时候使用数组的名字其实使用的是指向数组首元素的指针。

a[2]等效于*(a+2)

与标准库类型string和vector的下标区别是:标准库类型限定下标必须为无符号类型,数组的内置下标运算可以处理负值。

4.c风格字符串

字符串字面值就是C风格字符串,即按此习惯书写的字符串存**存放在字符数组中并以 ‘\0’ 结束。**而字符数组的名字实际上代表的是指向数组首元素的指针。

比较两个标准库string对象用的是==,<这些,而比较C风格字符串需要调用函数。

连接两个标准库string对象用+,而连接C风格字符串需要调用函数且容易出错。

标准库string比C风格字符串更安全高效。

5.与旧代码接口

1.string对象和C风格字符串

允许以字符串字面值初始化string,string对象的运算中允许出现字符串字面值。

但是string对象不能初始化C风格字符串,需要c_str的成员函数。

string s("frghj");

char *str=s; 错误

const char *str=s.c_str(); 正确

2.使用数组初始化vector对象

不允许使用内置类型的数组或者vector对象初始化数组,但是数组可以用来初始化vector对象。指明要拷贝区域的首元素地址和尾后地址就可以了。

vector<int> ivec(begin(arr),end(arr));

vector<int> sub(arr+1,arr+4);

尽量使用标准库类型而非数组。

6.多维数组

严格来说,c++中没有数组,多维数组其实是数组的数组。

1.初始化

采用花括号。

2.下标引用

int ia[3][4];

int (&row)[4]=ia[1];

3.范围for语句

int ia[3][4];

for (auto &row:ia)

for(auto &col:row)

{col=cnt;

++cnt;}

上面,为了改变元素的值我们使用了引用。

但是还有一个原因是避免数组被自动给转换为指针,所以范围for语句处理多维数组除了最内层的循环,其它循环的控制变量都应该是引用类型。

4.指针和多维数组

记住,多为数组是数组的数组。所以多维数组的名字是指向第一个内层数组的指针。

int *p[4]; 整型指针的数组

int (*p)[4]; 指向含有4个整数的数组

C++11中可以通过自动类型推导避免在数组前面加一个指针类型。

for(auto p=begin(ia);p!=end(ia);++p)

for(auto q=begin(*p);q!=end(*p);++q)

cout<<*q<<endl;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值