C++ Primer 第三章笔记
Chapter 3. Strings, Vectors, and Arrays
3.1 Namespace Using Declarations
using声明:
using namespace::cin;
一旦有此声明,则之后可以直接使用cin
头文件中不要使用using声明
c++标准库中的所有标识符都被定义在一个叫std的namespace
3.2 Library String Type
3.2.1 Defining and Initializing Strings
string的常见初始化方法:
string s1; //默认初始化,s1是一个empty string
string s2(s1); //s2是s1的副本
string s2 = s1; //同上
string s3("value"); //s3是字面值的副本,不包含结尾的'\0'
string s3 = "value"; //同上
string s4(n, 'c'); //s4是n个c
复制初始化,直接初始化
使用等号的往往是复制初始化
3.2.2 Operations on Strings
string的常用操作:
os << s;
is >> s;
getline(is, s); //从input is中读入一行到s,返回s
s.empty();
s.size();
s[n];
s1+s2; //返回s1与s2的拼接string
s1 = s2; //用s2的副本代替s1原有值
s1 == s2;
s1 != s2;
<,<=,>,>=; //比较
用is >> s;
时,输入会读入并 丢弃所有前导空白字符
">>"运算符返回istream对象,因此可以连续使用
istream可以作为条件判断的检测对象,如果流遇到EOF或错误,则为false,否则为true
getline(): 读入一行, 包括newline,丢掉newline,存入参数string中
getline()返回一个istream
string的size()方法返回的是一个string::size_type类型的值,这个类型的具体细节未知,但一定是unsigned
可用auto或decltype来获取它的类型
string可以和字符串字面值、字符字面值用"+“混合连接,只要每一个”+"的两个操作数至少有一个是string类型
3.2.3 Dealing with the Characters in a String
头文件<cctype>
中包含了字符分类和更改大小写的函数
在C++中,所有C语言.h头文件,都改成以c为前缀,去掉.h的名字,c表示来自C语言
所有定义在c前缀的文件中的标识符都在命名空间std中定义,而定义在.h文件中的标识符不是
函数isspace(c)
判断一个字符是不是空白字符
range for:
for(declaration : expression)
statement;
expression: 一个对象,它的类型是一种序列
declaration: 一个变量,用于访问序列的元素,每一次迭代,这个变量都会成为序列中的下一个元素
string是一种序列,因此可以作为range for的遍历对象
如果要用range for改变string中字符的值,那么在declaration部分一定要使用引用类型,,如:
for(auto &c : s)
c = toupper(c);
用下标来访问string中的字符时,下标的类型时string::size_type,大小范围是0<index<size()-1
如果下标大小不在这个范围内,undefined
对于一个空string使用下标,undefined
每次使用string下标之前,一定要检查string是否为空
可以对string下标操作返回的字符进行赋值
3.3 Library Vector Type
vector是一个类模板
由于引用不是对象,因此不能使用vector存放引用变量
3.3.1 Defining and Initializing Vectors
vector的初始化方法:
vector<T> v1; //默认初始化为空vector
vector<T> v2(v1); //v2是v1的副本
vector<T> v2 = v1; //同上
vector<T> v3(n, val); //v3包含n个val
vector<T> v4(n); //v4包含n个默认初始化的对象
vector<T> v5{a, b, c,...}; //每个元素按照列表初始化
vector<T> v5 = {a, b, c,...}; //同上
元素的默认初始化:
- 如果是基本类型,则初始化为0
- 如果不是,则用该类的默认初始化
用大括号初始化时,如果大括号中的值并不能用来初始化vector的对象,那么这些值被用来以其他方式构造vector,如:
vector<string> v7(10); //ok
vector<string> v8(10, "hi"); //ok
3.3.2 Adding Elements to a Vector
不能使用range for的同时向vector中添加元素
3.3.3 Other Vector Operations
重要的vector操作:
v.empty();
v.size();
v.push_back(t);
v[n];
v1 = v2; //V1成为v2的副本
v1 = {a, b, c,...}; //v1中的元素被全部替换成列表中元素的副本
v1 == v2,v1 != v2; //相同大小,每个元素==成立,则==成立
<, <=, >, >=; //字典序
vector支持range for
vector有自己的size_type,使用的时候需要加上实例类型:
vector<int>::size_type; //ok
vector::size_type; //error
只有vector中的元素可以比较时,才能比较两个vector
3.4 Introducing Iterators
string不是容器,但string有iterators
3.4.1 Using Iterators
end()返回的iterator是最后一个元素后面的元素的iterator: off-the-end iterator
iterator的一些操作:
*iter; //返回iter代表的元素的引用
iter->mem; //解引用并使用对象成员
++iter; //让iter指向下一个iterator
--iter; //让iter指向上一个iterator
iter1 == iter2, iter1 != iter2; //如果两个iterator指向同一个元素,或者都是同一个容器的off-the-end iterator,==成立
所有容器的iterator都支持"++"
off-the-end iterator不应当使用"++"
iterator的类型是各个container定义的iterator/const_iterator,如:
vector<int>::iterator it;
string::const_iterator it1;
const_iterator类型的iterator,只能读不能写
如果一个容器是const的,那么只能使用它的const_iterator类型,并且begin()和end()也会返回const_iterator类型的iterator。非const容器的begin()和end()返回iterator类型的iterator
**cbegin()和cend()**方法返回const_iterator类型的iterator
3.4.2 Iterator Arithmetic
一些iterator运算:
iter + n;
iter - n;
iter += n;
iter -= n;
iter1 - iter2;
<, <=, >. >=;
string和vector支持以上运算
两个iterator相减得到的值的类型为difference_type,为一个有符号整数
一个利用iterator运算的左闭右开的二分查找:
auto beg = text.begin(), end = text.end();
auto mid = text.beging() + (end - begin) / 2;
while(mid != end && *mid != sought){
if(sought < *mid)
end = mid;
else
begin = mid + 1;
mid = begin + (end - begin) / 2;
}
3.5 Arrays
3.5.1 Defining and Initializing Built-in Arrays
数组的大小必须是常量表达式,const和constexpr皆可
被定义在函数中的数组,默认初始化undefined
没有引用的数组,因为数组装载对象,而引用不是对象
数组的初始化:
-
可以使用列表初始化,使用列表初始化可以省略数组大小参数
-
如果省略了数组大小,则数组大小为列表的长度
-
如果没有省略数组大小,列表长度不能超过数组大小,如果小于数组大小,则前几个元素用列表初始化,后面的元素也初始化成默认值
-
列表为空则所有元素初始化成默认值,如:
unsigned scores[11] = {};
也可以使用字符串字面值,末尾不需要’\0’,数组大小可以省略
字符串字面值末尾是自带’\0’的,如果指定数组大小,要留足够的空间
3.5.2 Accessing the Elements of an Array
数组下标的类型是size_t,size_t被定义在头文件<cstddef>
中
数组可以使用range for
3.5.3 Pointers and Arrays
如果用一个数组初始化一个变量,并且声明为auto形式,那么这个变量的类型为指针
decltype不同:
int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
decltype(ia) ia3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
decltype返回的是一个10个元素的指针
指向数组元素的指针具有和iterator一样的操作
数组元素的指针可以指向数组最后一个元素之后的一个元素(并不真的存在)的地址
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *e = &arr[10];
标准库中的**begin(), end()**函数
将数组作为参数传给begin()或end()能够得到它们的第一个元素的指针或最后一个元素后面的元素的指针
两个指针相减得到的值为一个ptrdiff_t类型的值,在cstddef中定义,为signed
对于两个同类型但无关的对象的指针,无法进行指针比较
数组的下标可以指向数组最后一个元素后面的一个元素
元素的指针也可以用数组下标[]操作符,下标中的数可以是负数
而vector和string的下标必须是unsigned数
3.5.4 C-Style Character Strings
对数组的操作本质上是对指针的操作
strcat()和strcpy()的调用者需要自己保证目标数组的大小足够(包括数组尾部的’\n’)
对于多数应用, 使用string库不仅比C-style string安全,也更为高效
3.5.5 Interfacing to Older Code
string类具有很好的向前兼容性
- 可以用以null结尾的字符数组初始化/赋值string
- 可以用以null结尾的字符数组作为string加法操作的操作数(至多一个)
string类的c_str()函数可以返回string对应的C-style string:
const char *str = s.c_str();
注意:对于string进行的其他操作可能会使c_str()返回的字符串无效
可以使用数组来初始化vector,需要头尾两个指针
具体方法是:
int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr), end(int_arr));
传入的指针也可以不是原数组的头尾指针,可以是原数组子数组的头尾指针,以构造一个用子数组初始化的vector
现代C++程序应当更多地使用vector和string,来代替数组和C-style字符串
3.6 Multidimensional Arrays
初始化二维数组时,往往用两层大括号,但内层的大括号是可以省略的,高维数组与此相同。例如:
int ia[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
如果使用两层大括号,只初始化每行的前几个值,则在每个大括号中可以缺省后几个值
如果使用一层大括号,只初始化前几行的值,则缺省后面的值
被缺省的值自动初始化为0
可以用range for操作多维数组:
for(auto &row : ia)
for(auto &col : row){
}
使用range for操作多维数组,除最内层以外的每一层的声明一定要用引用,因为用auto取数组对象的类型,会得到一个指针,而不是一个数组类型
Chapter Summary
Defined Terms
default initialization: built-in类型根据位置决定是value-initialize还是undefined,class类型用默认构造函数初始化
direct initialization: form of initialization that does not include an “=”
value initialization: built-in类型被初始化为0,class类型用默认构造函数初始化