【c++primer第五版】第三章--字符串,向量和数组

目录

 

命名空间using

标准库string

标准库类型vector

迭代器

数组


命名空间using

很多库函数属于命名空间std; std::cin ;cin --  表示从标准输入中读取内容,:: -- 作用域操作符;std::cin -- 编译器从操作符左侧名字所示作用域中寻找右侧那个名字,就是使用命名空间std中的名字cin;

每个用到的名字都必须声明;一般using namespace std;

头文件不能有using 声明,因为头文件中的内容会拷贝到所用引用它的文件中去;

标准库string

string 类型表示可变长的字符序列;需要 #include<string>;

初始化:

string str;    默认初始化,str是一个空串
string str2(str);    str2 是str 的副本;
string str2 = str;    等价于上一个;
string str3("value");    str3是字面值value的副本,除了最后的空字符串外;
string str4 = "value";    等价于上一个;
string str5(n,'c')    str5 是n个c 组成的字符串

用 =  是拷贝初始化;不使用 =  是直接初始化;

string 对象的操作

os << s     将s 写到输出流os当中,返回os;
is >>s      从is 中读取字符串赋给s 字符串以空白分隔,返回is;

getline(is,s);    从is 中读取一行赋值给s,返回is;
--可以得到输入字符串的空白符,直到遇到换行符为止(每次读入一整行)(换行符也被读入进来了),之后把读入的内容存入string 对象中(不存 换行符),如果一开始就是换行符,那么存入的是一个空字符串;
eg
string line;
while(getline(cin,line))
    cout << line << endl;    //line 中没有换行符,所以手动的添加换行符,endl结束当前行并刷新缓冲区;
输入:yang an  lan ;
输出:yang an lan ;

s.empty();   s为空返回true;
s.size();    返回s中字符的个数;
s[n]    返回s 中第n个字符的引用;
s1+s2    返回s1+s2连接后的结果;
eg
两个string 对象相加
string str = "hello";
string str2 = " world";
string str3 = str + str2;
输出:hello world
字面值和string对象相加
标准库允许把字符字面值和字符串字面值转换成string对象,当把string对象和字符字面值或者字符串字面值混在一起使用时,必须确保每个加法运算符的两侧运算对象至少有一个时string对象;
string str4 = str + ",";    true
string str5 = "hello" + ",";    false,两个运算对象都不是string对象;
string str6 = "hello" + "," + str;    false,不能把两个字面值直接相加;
注意:字符字面值和字符串字面值和string 是不同的类型;

s1 = s2;s2 的副本代替s1中原先的字符;
s1 == s2;    s1,s2中的字符完全一样,则他们相等,对字符大小写敏感;
s1 != s2;
<,>,<=,>=    顺序比较
#include<iostream>
#include<string>
using namespace std;
int main() {
	string str, str2;
	cin >> str >> str2;    //读入字符串,一个读入到str,一个读入到str2; 空格为限
	cout << "str: " << str << endl;
	cout << "str2: " << str2 << endl;
	return 0;
}
输入:yang an lan
输出:str:yang ;str2: an;
#include<iostream>
#include<string>
using namespace std;
int main() {
	string word;     //反复读取,直到到达文件结尾
	while (cin >> word) {
		cout << word << endl;     //逐个输出单词,每个单词后面跟一个换行
	}		
	return 0;
}
输入:yang an lan;
输出:yang
      an
      lan
注:遇到文件结束标记或非法输入循环结束

string::size_type类型

size_type 类型是一个无符号类型的值,size()返回的就是一个无符号的整型数,表达式中不能混用带符号和无符号数,可能会产生意想不到的结果。

处理string对象中的字符

cctype 头文件中的函数
isalnum(c)    当c是字母或者数字时为真;
isalpha(c)    当c时字母时为真;
iscntrl(c)    当c是控制字符时为真;
isdigit(c)    当c时数字时为真;
isgraph(c)    当c不是空格但是可以打印时为真;
islower(c)    当c是小写字母时为真;
isupper(c)    当c是大写字符时为真;
isprint(c)    当c是可打印字符时为真;(即c是空格或c具有可视化形式);
ispunct(c)    当c是标点符号时为真;
isspace(c)    当c时空白时为真;
isxdigit(c)   当c是十六进制数字时为真;
tolower(c)    如果c是大写字母,则输出小写字母,否则原样输出;
toupper(c)    如果c是小写字母,输出对应大写字母,否则原样输出;

处理每个字符

处理每个字符--使用基于范围的for语句
for(declaration : expression)
    statement;
expression:是一个对象,表示一个序列;
declaration:定义一个变量,用于访问序列中的基础元素,每次迭代,declaration变量会被初始化为expression部分的下一个值;
eg
sting str("hello world!");
for(auto c : str){    //遍历str;auto -- 让编译器决定c的类型 这里c的类型是char
    cout << c << endl;   //逐个输出 
}

eg
sting str("hello world!");
decltype(str.size()) punct_cnt = 0;    //punct_cnt 的类型和str.size()类型相同
//统计str中标点符号的数量
for(auto c : str)
    if(ispunct(str))
        ++punct_cnt;
cout << punct_cnt << str <<endl;
eg
//改变字符串中的字符
sting str("hello world!");
for(auto &c : str)    //此处引用,将会该改变str中的值
    c = toupper(c);
cout << c << endl;
只访问string 中的某个字符或者处理一部分字符但是遇到某个值就要停止
--使用下标或者迭代器
--使用下标 下标是size_type类型,小标必须大于等于0且小于str.size()
eg
for(decltype(str.size())) index = 0;index != str.size() &&!isspace(str[index]);++index)
    str[index] = toupper(str[index]);    
注意:使用下标,就可以将下标设成 size_type类型;

eg
//编写程序,将0-15之间的十进制数转换成十六进制数
const string hexdigits = "0123456789ABCDEF";
cout << "enter ..." << endl;
string res;    //保存十六进制的字符串
string size_type index;    //用于保存从输入流读到的数
while(cin >> index)
    if(index < hexdigits.size())
        res += hexdigits[index];
cout << res <<endl;
decltype 类型指示符
选择并返回操作数的数据类型,编译器分析表达式并得到它的类型,但是不实际计算表达式的值;
decltpye(f()) sum = x;    //sum的类型就是函数f的返回类型;
decltype 处理顶层 const 和引用的方式于auto 有些许不同
const int ci = 0,&cj = ci;
decltype(ci) x = 0;    //x类型是const int
decltype(cj) y = x;    //y类型是const int&
decltype(cj) z;    //错误,z 是一个引用,必须初始化;

如果decltype 使用的表达式不是一个变量,则decltype 返回 表达式结果 对应的类型
如果表达式的内容是解引用操作,则decltype 将得到引用类型;
int i = 42,*p = &i;
declpyte(*p) 的结果类型就是int&,

标准库类型vector

表示对象的集合,其中所有的对象类型相同,每个对象都有与之对应的索引,用于访问对象;vector是一个类模板,模板本身不是类或者函数,可以将模板看作是编译器生成类或者模型的一个说明,编译器根据模板创建类或者函数的过程称为实例化,使用时必须包含头文件

#include<vector>

vector 能容纳大多数的对象作为其元素,但是引用不是对象,所以不能存放在vector中;

定义和初始化vector对象

定义和初始化
vector<type> v1;    v1是一个空的vector,默认初始化;
vector<type> v2(v1);    v2中包含v1中所有的副本;(两个vector的类型必须相同)
vector<type> v2=v1;  同上;
vector<type> v3(n,val);    包含n 个重复的元素,每个元素都是val;
vector<type> v4(n);    包含n个重复执行值初始化的元素;
vector<type> v5{a,b,c,d,e,f,....};   
vector<type> v5={a,b,c,d,e,f,....}; 
值初始化:库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素,初值由元素的类型决定;
int 赋值为0;
vector<type>()--构造vector对象
vector<type>{} --初始化vector对象
添加元素
一般地,定义一个空的vector,之后利用push_back()函数添加元素(该函数把一个值依次vector对象的尾元素压到vevtor对象的尾端)
eg
vector<int> v2;
for(int i = 0;i != 100;++i)
    v2.push_back(i);
vector 其他操作
v.empty()    vector无元素,返回真;
v.size()    返回元素个数
v.push_back()
v[n]    返回v中第n个位置上元素的引用
注意:vector 对象和string对象的下标运算符 可用于访问已经存在的元素,不能用于添加元素
缓冲区溢出就是指的通过下标访问不存在的元素
eg
//统计不同分数段的人数
vector<unsigned> scores(11,0);
unsigned grade;
while(cin >> grade){
    if(grade <= 100)
    ++scores[grade/10];    将对应分数段的计数值加1

}
  ++scores[grade/10];
等价于
auto index = grade/10;
scores[index] = scores[intex] + 1;

迭代器

就是通过下标来访问元素,是一种通用机制,所有的标准库容器都可以使用迭代器,但是只有少数几种才同时支持下标运算符,迭代器也提供对对象的间接访问。

迭代器不使用取地址符,但是拥有begin(),end()成员;
begin()返回指向第一个元素的迭代器;end()返回指向容器尾元素的下一位置;
如果容器为空,则返回的是同一个迭代器 -- 尾后迭代器
运算符
*iter    返回迭代器iter 所指元素的引用
iter -> mem     解引用iter并获取该元素名为mem的成员,等价(*iter).mem
++iter    令iter 指向容器中的下一个元素
--iter    令iter 指向容器中的上一个元素
iter1 == iter2
iter != iter2
eg
//把第一个字母改成大写,迭代器实现
string str("hello world");
if(str.begin() != str.end())    //判断string对象是否为空
    auto iter = str.begin();
    *iter = toupper(*iter);    //解引用

注:end()返回的迭代器并不会实际指向某个元素,所以不能对其进行递增或者解引用操作;
eg
//把第一个单词改成大写,迭代器实现
string str("hello world");
for(auto iter = str.begin();iter != str.end() && !isspace(*iter);++iter)    //遍历 直到结束或者遇到空白字符
    *iter = toupper(*iter);
cout << iter << endl;

迭代器类型

vector<int>::iterator it;    it可以vector<int>的内容
vector<int>::const_iterator it    it可以读vector<int>的内容,不能写
string::iterator it2;    it2可以读写string对象中的字符
string::const_iterator it2;  it2可以读string对象中的字符,不能写
迭代器这个名词有三种不同的类型:可能是迭代器概念本身,可能是容器定义的迭代器类型,可能是某个迭代器对象;

begin和end运算符
begin 和end 返回的具体类型是由对象是否是常量决定的,如果对象是常量,则返回const_iterator;如果对象不是常量,则返回iterator;
一般地,如果只需要读不需要写的话最好使用常量类型,定义cbegin 和cend,返回值是const_iterator;
结合解引用和成员访问操作
解引用迭代器可以访问迭代器所指的对象;如果该对象类型恰好是类,则可以进一步访问它的成员,
(*iter).empty()   true 解引用iter,调用结果对象的empty成员;
*iter.empty()    false iter是迭代器,没有empty()成员
为了简化运算,定义箭头运算符(->)表示解引用和成员访问;
iter->empty() = (*iter).empty()
eg
//遍历string 直到遇到空字符串
vrctor<string> str {"yang","an","lan" };
for(auto iter = str.cbegin();iter != str.cend() && !iter->empty();++iter)
    cout << *iter << endl;
注意:但凡使用了迭代器的循环体,都不能向迭代器所属的容器添加元素;

迭代器运算

vector 和 string 支持的额外的迭代器运算
iter + n    迭代器加上一个整数值仍然是迭代器,迭代器的位置相比以前 向前 移动了若干个元素。迭代器将会指向一个元素或者容器尾元素的下一个位置。
iter -n    迭代器加上一个整数值仍然是迭代器,迭代器的位置相比以前 向后 移动了若干个元素,迭代器将会指向一个元素或者容器尾元素的下一个位置。
iter += iter
iter -= iter
iter - iter2    结果是他们的距离,参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一个位置;
< > <= >=    比较的是他们的位置
二分法--迭代器实现
//text 是有序的 beg 和 end 表示搜索范围
//迭代器实现二分法找某个元素(sought)
vector<string> text;
auto beg = text.cbegin(), end = text.cend();
auto mid = beg + (end-beg)/2;	//初始状态下的中间点
while (mid != end && *mid != sought)
{
	if (sought < *mid)
		end = mid;
	else
		beg = mid + 1;
	mid = beg + (end - beg) / 2;
}

数组

类似vector,不同点:大小不变,不能添加元素;如果不清楚元素的个数,使用vector;

数组是一种符号类型,声明a[d] -- a是数组的名字,d 是数组的维度,必须大于0,必须是常量表达式;(值不会改变并且编译过程中就能得到结果的表达式)

unsigned cnt = 42;    不是常量表达式
constexpr unsigned sz = 3;    是常量表达式
int arr[10];    true
int *arr2[sz];    true
string arr3[cnt];    false
定义数组必须指定数组的类型,auto 不允许使用,和vector一样,数组的元素应该是对象,不存在引用的数组
int arr4[sz] = {0,1,2}; -- true
int arr5[] = {0,1,2};    -- true
int arr6[6] = {0,1,2}    等价 int arr6[6] = {0,1,2,0,0,0}
string arr7[3] = {"yang","an"};    等价string arr7[3] = {"yang","an"," "}
字符数组的特殊性
char arr[] = {'a','b','c'};    列表初始化,没有空字符 维度是3
char arr2[] = {'a','b','c','/0'};    列表初始化,含有显式空字符 维度是4
char arr3[] = {"c++"};    自动添加空字符 维度是4
char arr4[6] = {"daniel"};    错误,没有空间可以存放空字符
字符串字面值对字符数组进行初始化时,注意字符串字面值的结尾处还有一个空字符,也会被拷贝到字符数组中去;
注意:不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值;
理解复杂的数组声明
定义存放指针的数组比较简单,但是定义数组的指针或数组的引用就稍微复杂
int arr[10];
int *ptrs[10];    含有10个整型指针的数组
int (*array)[10] = &arr    array 指向一个含有10个整数的数组
int (&arrref)[10] = arr;    arrref 引用一个含有10个整数的数组
int *(&array)[10] = ptrs;    array 时数组的引用,该数组有10个指针
访问数组元素
和vector,string一样,数组的元素也能通过for 语句或下标运算符访问;
使用数组下标的时候,通常将下标定义为size_t类型,是一种机器相关的无符号类型,头文件#include<cstddef>
eg
unsigned scores[11] = {};    //11个分数段,初始化为0
undigned grade;
while(cin >> grade)
    if(grade <= 100)
        ++score[grade/10];
和vector,string 一样,遍历数组中的所有元素时,最好的办法是for语句
eg
for(auto i : scores)
    cout << i << endl;
定义一个int 数组,令每个元素的值就是其下标值
#include<iostream>
#include<string>

using namespace std;
constexpr int N = 10;
int main() {
	int arr[N] = {};
	for (auto i = 0; i < N; ++i)
		arr[i] = i;
	for (auto j : arr)
		cout << j << " ";
	cout <<  endl;

	return 0;
}

指针和数组

string num = {"abc","cdf","adf"};
string *p = &num[0];    指向第一个元素
string *p2 = num;    同上
使用数组类型的对象是在使用一个指向该数组首元素的指针
auto arr2(num);    arr2是一个字符指针,指向num第一个元素
使用数组作为一个auto 变量的初始值时,推断得到的类型是指针;
指向数组元素的指针拥有更多的功能,vector 和string的迭代器支持的运算,数组的指针全部都支持,允许使用递增运算符将数组元素指针向前移动到下一位置
int *p = arr;    指向第一个元素arr[0]
++p;    指向arr[1]
指针也能遍历数组中的元素,这样做的前提是提前获取到数组的第一个元素的指针和指向数组为元素的下一位置的指针(begin() 和 end() 函数的使用)
int ia [10] = {};
int *beg = begin(ia);   指向首元素的指针
*last = end(ia);    指向尾元素下一位置的指针
for(auto *beg = arr;beg != end;++beg)    //遍历
    cout << *beg << endl;    //循环输出

while(beg != end && *end > 0)
    ++beg;


利用指针将数组中的元素置为0
eg
#include<iostream>
#include<string>

using namespace std;
int main() {
	int arr[] = { 1,2,3,45,6,78,9 };
	int* beg = begin(arr), *last = end(arr);
	while (beg != last) {
		*beg = 0;
		++beg;
	}
	for (auto ix : arr)
		cout << ix << " ";
	cout << endl;
	return 0;
} 

多维数组

c++中没有多维数组,通常所说的多维数组实际上是数组的数组。当一个数组的元素仍然是数组时,通常使用两个维度来定义他,一个是数组本身的大小,一个是元素(也是数组)的大小。

多维数组的定义
int ia [3][4]    大小为3的数组,每个元素是含有4个元素的数组
int arr [10][20][30] = {0};    大小为10的数组,每个元素是含有20个元素的数组,每个元素是含有30个整数的数组,之后将所有的元素初始化为0;
对于一个二维数组来说,第一个维度称为行,第二个维度称为列;
多维数组的初始化
1.每一行的值都写上
int ia[3][4] = {
    {0,1,2,3},
    {4,5,6,7},
    {8,9,10,11}
};
2.int ia2[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};    1,2等价
3.int ia3[3][4] = {{0},{4},{8}}    显式的初始每行的首元素
4..int ia4[3][4] = {0,1,2,3};   显式的初始第一行的元素
多维数组的下标的引用
ia[2][3] = arr[0][0][0];    用arr的首元素为最后一个元素赋值;
int (&row) [4] = ia[1];    把row绑定到ia的第二个4元素数组上
使用for语句处理多维数组
size_t cnt = 0;
for(auto &row : ia)    循环外层的每一个元素,这里必须是 &,如果不加引用,则row在编译器初始化阶段就会将它转换成指针,这样row的类型是int*,在int*中遍历,显然是不合法的。
    for(auto &col : row)    循环内层的每一个元素,
        col = cnt;    将元素位置的索引作为它的值
        ++cnt;
注意:使用for语句处理多维数组,除了最内层的循环外,其他所有的循环的控制变量都应该是引用类型。
指针和多维数组
因为多维数组是数组的数组,所以多维数组名字转化的来的指针时加上是指向第一个的内层数组的指针。
int ia[3][4];
int (*p)[4] = ia;    p指向含有4个整数的数组;(圆括号必不可少)
p = &ia[2];    p指向ia的尾元素;
使用auto或者decltype可以尽可能避免在数组面前加上一个指针类型了。
//输出ia 中每个元素的值,每个内层数组各占一行
int ia[3][4];
for(auto p = ia; p != ia + 3;++p)    p指向含有4个整数的数组
    for(auto q = *p; q != *p +4; ++q)    q指向4个整数数组的首元素
        cout << *q <<' ';
    cout << endl;

使用begin和end 函数实现
int ia[3][4];
for(auto p = begin(ia); p != end(ia);++p)    p指向含有4个整数的数组
    for(auto q = begin(*p); q != end(*p) +4; ++q)    q指向4个整数数组的首元素
        cout << *q <<' ';
    cout << endl;

review

first -- 2021515;

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值