C++primer小结3-4章

第三章 字符串、向量和数组

3.1 命名空间的using声明

域操作符(::):表示编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。
使用using声明也能使用命名空间中的成员。

3.2 标准库类型string

初始化string对象

标准库类型string表示可变长的字符序列。包含#include string头文件。
初始化string对象的方式:直接初始化和拷贝初始化。
使用拷贝初始化,使用等号初始化一个变量:string s = “hiya”;
使用直接初始化,初始化要用到的值有多个:string s(n, ‘c’);//s初始化为n个字符c做成的串

string对象上的操作

读写string对象:string s; cin >> s;
执行读取操作时,string对象会自动忽略开头的空白并从第一个真正的字符开始读起,直到遇见下一处空白为止

string s1, s2;
cin >> s1 >> s2;
cout << s1 << s2 << endl;

我们希望能在最终得到的字符串中保留输入时的空白符,使用getline函数。getline()函数的第一个参数是一个输入流和一个string对象,函数从给定的输入流中读取内容,直到遇到换行符为止,然后把所读的内容存入到那个string对象中去。

string line;
while(getline(cin, line))
	cout << line << endl;

empty()函数根据string对象是否为空返回一个对应的布尔值。
size()函数返回string对象的长度(即string对象中字符的个数)。
size()函数返回的是一个string::size_type类型的值。是一个无符号类型的值而且足够存放下任何string对象的大小。
Tip:如果一条表达式中已经有了size函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。
两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧的运算对象串接而成。也就是说,对string对象使用加法运算发(+)的结果是一个新的string对象。
C++语言中的字符串字面值并不是标准库类型string的对象。字符串字面值与string是不同的类型。

处理string对象中的字符

使用基于范围的for语句

string str("some string");
//每行输出str中的一个字符
for(auto c: str)
	cout << c << endl;

如果想要改变string对象中字符的值,必须把循环变量定义为引用类型。当使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。

string s("Hello world!");
forauto &c:s)
	c = toupper(c);

只处理一部分字符?
使用下标运算符。下标运算符([ ])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置,返回值是该位置上字符的引用。
string对象的下标必须大于等于0而小于s.size()。
只要字符串不是常量(const),就能为下标运算符返回的字符赋新值。

3.3 标准库类型vector

标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。因为vector“容纳着”其他对象,所以也常被称作容器
C++语言既有类模板,也有函数模板,其中vector是一个类模板。
模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化,当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
对于类模板来说,通过提供一些额外信息来指定模板到底实例化成什么样的类。提供信息的方式:在模板名字后面跟一队尖括号,在括号内放上信息。
例如:vector<int> ivec; vector<vector<string>> file
vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型。
vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。除此之外,其他大多数内置类型和类类型都可以构成vector对象,甚至组成vector的元素也可以是vector。

定义和初始化vector对象

vector<T> v1;默认初始化
最常见的方式就是先定义一个空的vector,然后当运行时获取到元素的值后再逐一添加。
列表初始化vector对象
如果提供的是初始元素值的列表,则只能把初始值都放在花括号里进行列表初始化,而不能放在圆括号里:

vector<string> v1{"a", "an", "the"}

还可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象。
值初始化
通常情况下,可以只提供vector对象容纳的元素数量而略去初始值。此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号{ }还是圆括号( )。

向vector对象中添加元素

对vector对象来说,直接初始化的方式适用于三种情况:初始值已知且数量较少、初始值是另一个vector对象的副本、所有元素的初始值都一样。
更常见的做法是,先创建一个空的vector,在运行时再利用vector的成员函数push_back向其中添加元素。

vector<int> v2;
for(int i = 0; i < 100; i++)
	v2.push_back(i);

其他vector操作

empty(), size()返回vector对象中元素的个数,返回值的类型是由vector定义的size_type类型。
可以通过计算得到vector内对象的索引,然后直接获取索引位置上的元素。
vector对象的下标运算符可以用于访问已存在的元素,而不能用于添加元素。
只能对确知已存在的元素执行下标操作!

3.4 迭代器介绍

类似于指针类型,迭代器也提供了对对象的间接访问。其对象是容器中的元素或者string对象中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另一个元素。迭代器有有效和无效之分。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效。

使用迭代器

和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为beginend的成员,end成员负责返回指向容器“尾元素的下一位置”的迭代器,也就是说,指示的是容器的一个本不存在的“尾后”元素。

标准容器迭代器的运算符
*iter返回迭代器iter所指元素的引用
iter->mem解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iter令iter指示容器中的下一个元素
–iter令iter指示容器中的上一个元素
iter1 == iter2判断两个迭代器是否相等。如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器,则相等;否则,则不相等。

Note:end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用的操作。
C++程序员习惯使用!=,其原因和他们更愿意使用迭代器而非下标的原因一样:这种编程风格在标准库提供的所有容器上都有效。
迭代器类型
拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型。const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。

vector<int>::iterator it;//it能读写vector<int>的元素
string::iterator it2;//it2能读写string对象中的字符

vector<int>::const_iterator it3;//it3只能读字符,不能写元素
string::const_iterator it4;//it4只能读字符,不能写字符

迭代器这个名词有三种不同的含义:可能是迭代器概念本身,也可能是指容器定义的迭代器类型,还可能是指某个迭代器对象。
结合解引用和成员访问操作
解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能进一步访问它的成员。
C++语言定义了箭头运算符(->),把解引用和成员访问两个操作结合在一起。也就是说,it->mem和(*it).mem表达的意思相同。
warning:凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。

迭代器运算

可以令迭代器和一个整数值相加(或相减),其返回值是向前(或向后)移动了若干个位置的迭代器。

//二分搜索
auto beg = text.begin(), end = text.end();
auto mid = text.begin + (text.end()-text.begin())/2;
while(mid != end && *mid != sought)
{
	if(*mid > sought)
		end = mid;
	else
		begin = mid + 1;
	mid = beg + (end - beg) / 2;
}

3.5 数组

与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。
如果不清楚元素的确切个数,请使用vector。

定义和初始化内置数组

和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
定义数组的时候必须指定数组的类型,不允许用auto关键字由初始值的列表推断类型。数组的元素应为对象,因此不存在引用的数组。
字符数组的特殊性
字符数组有一种额外的初始化形式,可以用字符串字面值对此类数组初始化。一定注意字符串字面值的结尾处还有一个空字符(’\0’)。
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
复杂的数组声明

int *ptrs[10]; //ptrs是含有10个整形指针的数组
int &refs[10] = /* ? */ //错误,不存在引用的数组
int (*Parray)[10] = &arr; //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组

默认情况下,类型修饰符从右向左依次绑定。对于数组来说,最好的办法是从数组的名字开始按照由内向外的顺序阅读。Parray是一个指针,它指向一个int的数组,数组中包含10个元素。(&arrRef)是一个引用,引用的对象是一个大小为10的数组,数组中的元素类型是int。

访问数组元素

数组的元素也能使用范围for语句或下标运算符来访问。数组的索引从0开始。
在使用数组下标的时候,通常将其定义为size_t类型。

指针和数组

数组有一个特性:在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针。

string *p2 = nums; //等价于p2 = &nums[0];

Note:在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
当使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组。
但是当使用decltype关键字时上述转换不会发生,decltype(ia)返回的类型是由10个整数构成的数组。
指针也是迭代器
就像使用迭代器遍历vector对象中的元素一样,使用指针也能遍历数组中的元素。
Note:一个指针如果指向了某种内置类型数组的尾元素的”下一位置“,则其具备与vector的end函数返回的与迭代器类似的功能。

与旧代码的接口

混用string对象和C风格字符串
string专门提供了一个名为c_str的成员函数,返回值是一个C风格的字符串。也就是说函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存的数据恰好与那个string对象的一样。
不允许使用一个数组为另一个内置类型的数组赋初值,也不允许使用vector对象初始化数据。相反的,允许使用数组来初始化vector对象。

3.6 多维数组

严格来说,C++语言中没有多维数组,通常说的多维数组其实是数组的数组。
指针和多维数组
当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针。

第四章 表达式

表达式由一个或多个运算对象组成,对表达式求值将得到一个结果。字面值和变量是最简单的表达式,其结果就是字面值和变量的值。把一个**运算符(operator)**和一个或多个运算对象组合起来可以生成较复杂的表达式。
处理复合表达式:

  1. 拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求
  2. 如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
    赋值运算满足右结合律
int ival, jval;
ival = jval = 0;

4.5 递增和递减运算符

递增和递减运算符有两种形式:前置版本和后置版本。前置版本的运算符首先将运算对象加1(或减1),然后将改变后的对象作为求值结果。后置版本也会将运算对象加1(或减1),但是求值结果是运算对象改变之前那个值的副本:

int i = 0, j;
j = ++i; //j = 1, i = 1, 前置版本得到递增之后的值
j = i++; //j = 1, i = 2, 后置版本得到递增之前的值
建议:除非必须,否则不用递增递减运算符的后置版本。
auto pbeg = v.begin();
while(pbeg != v.end() && *pbeg >= 0)
	cout << *pbeg++ << endl;
后置递增运算符的优先级高于解引用运算符,因此*pbeg++等价于*(pbeg++)
当对这种形式熟悉后,书写
cout << *iter++ << endl;
要比书写下面的等价语句更简洁,更少出错
cout << *iter << endl;
iter++;

4.6成员访问运算符

点运算符获取类对象的一个成员;箭头运算符与点运算符有关,表达式ptr->mem等价于(*ptr).mem
解引用运算符的优先级低于点运算符。

4.7 条件运算符

条件运算符(? :)允许我们把简单的if-else逻辑嵌入到单个表达式当中。
条件运算符的优先级非常低,通常在两端加上括号。

4.8 位运算符

左移运算符(<<)在右侧插入值为0的二进制位。右移运算符(>>)的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位;如果该运算符号是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定。

4.9 sizeof运算符

sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,其所得的值是一个size_t类型的常量表达式。
sizeof不会把数组转换成指针来处理。可以用数组的大小除以单个元素的大小得到数组中元素的个数:sizeof(ia) / sizeof(*ia);

4.10 逗号运算符

对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果丢弃掉。逗号运算符真正的结果是右侧表达式的值。

4.11 类型转换

隐式类型转换:
算术转换;
数组转换成指针;
指针的转换;
转换成布尔类型;
转换成常量;
类类型定义的转换;
显示转换:
强制类型转换,一个命名的强制类型转换具有如下形式:cast-name<type>(expression)
type是转换的目标类型,expression是转换的值。cast-name是static_cast, dynamic_cast, const_cast和reinterpret_cast中的一种。dynamic_cast支持运行时类型识别,cast_name指定了执行的是哪种转换。
强制转换的结果将与原始的地址值相等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值