C++ Primer 第3章 字符串、向量和数组
C++ Primer 第3章 字符串、向量和数组
3.1 命名空间的using声明
using namespace::name;
头文件不应包含using声明
3.2 标准库类型string
使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。如果不使用等号,则执行的是直接初始化。
常用操作:
- getline(is,s)
从is中读取一行赋给s,返回is
- s.empty()
s为空返回true,否则返回false
- s.size()
返回s中字符的个数
其中getline函数从给定的输入流中读取内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到string对象中(注意不存换行符)。如果输入的一开始就是换行符,那么所得的结果是个空string。
触发getline函数返回的那个换行符实际上被丢弃掉了,得到的string对象中并不包含该换行符。
size()函数返回的类型是string::size_type
类型,是无符号整型数,这体现了标准库类型与机器无关的特性。在C++11新标准中,允许编译器使用auto或者decltype来推断变量的类型。
由于是无符号整型数,所以不能和负数比,因为负数会自动转换成一个比较大的无符号值。
字符串字面值与string是不同的类型,字符串无法用加号相连。
cctype头文件中的函数:
isalnum(c)/isalpha(c)/iscntrl(c)/isdigit(c)/isgraph(c)/islower(c)
等
ispunct(c)/isspace(c)/isupper(c)/isxdigit(c)/tolower(c)/toupper(c)
在名为cname的头文件中定义的名字属于命名空间std,而定义在.h的头文件中则不然。
处理每个字符,使用基于范围的for语句
for (auto c : str)
cout << c << endl;
使用范围for语句改变字符串中的字符(使用引用)
for (auto &c : s)
c = toupper(c);
cout << s << endl;
string的下标运算符[]接受的是string::size_type的值,其他值会自动转换为无符号类型。
3.3 标准库类型vector
vector的size返回的类型是vector<type>::size_type
不能通过下标添加元素,正确的方法是使用push_back
超出下标范围会产生缓冲区溢出(buffer overflow)
确保下标合法的一种有效手段就是尽可能使用范围for语句。
3.4 迭代器介绍
vecotr<int>::iterator it;
string::iterator it2;
vector<int>::const_iterator it3; //只读
string::const_iterator it4; //只读
begin负责返回指向第一个元素的迭代器
end返回“尾元素的下一位置”的迭代器,是本不存在的“尾后”元素。
如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
*iter
返回迭代器iter所指元素的引用iter->mem
解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
for (auto it = text.cbegin();
it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
关键概念:泛型编程
C++程序员习惯性的在循环中使用!=,因为所有标准库容器都定义了==和!=,但是它们大多数都没有定义<运算符。因此,只要养成使用迭代器和!=的习惯,就不用太在意用的到底是哪种容器类型。
常量begin和end用v.cbegin()
和v.cend()
获得
谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素,这样会使迭代器失效。
两个迭代器相减的结果是它们之间的距离
距离类型是difference_type,是带符号整数型。
取中间:auto mid = vi.begin() + vi.size() / 2;
使用迭代器完成二分查找:
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg)/2;
while (mid != eend && *mid != sought){
if (sought < *mid)
end = mid;
else
beg = mid + 1;
mid = beg + (end - beg)/2;
}
3.5 数组
定义
- 数组可使用定义为constexpr的函数来指定大小。
- 数组无法使用auto关键字
- 数组的元素应为对象
- 数组不允许拷贝给其他数组作为初始值,也不能为其他数组赋值
指向数组的指针和引用
int (*Parray) [10] = &arr;
int (&arrRef) [10] = arr;
访问数组元素
在使用数组下标的时候,通常将其定义位size_t类型,这是一种机器相关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小。在cstddef头文件中定义了size_t类型,这个文件是stddef.h的C++版本。
将数组初始化为0:unsigned scores[11] = {};
指针和数组
在很多用到数组名字的地方,编译器都会自动的将其转换位一个指向数组首元素的指针;意味着用auto时,初始化为数组时,其实是指向数组首元素的指针。二decltype返回的类型是数组,数组大小和原数组一致。
指针也是迭代器
我们可使用不存在的元素地址作为尾后指针
int *e = &arr[10];
for (int *b = arr; b != e; ++b)
cout << *b << endl;
C++11中为了避免出错引入了begin和end标准库函数,用数组名作为它们的参数。这两个函数定义在iterator头文件中
// Find first negative number.
int *pbeg = begin(arr), *pend = end(arr);
while (pbeg != pend && *pbeg >=0)
++pbeg;
两个指针相减的结果类型是一种名为ptrdiff_t
的标准库类型,和size_t
一样,也是一种定义在cstddef头文件中的机器相关的类型。因为差值可能位负值,所以ptrdiff_t是一种带符号类型。对于空指针同样适用
与旧代码的接口
string对象有c_str()
方法为字符数组赋值:
const char *str = s.c_str();
使用数组初始化vector对象:
vector<int> ivec(begin(int_arr), end(int_arr));
3.6 多维数组
对于二维数组来说,常把第一个维度称作行,第二个维度称作列。
用花括号初始化每行元素:
int ia[3][4] = {{ 0 }, { 4 }, { 8 }};
使用范围for语句处理多维数组
size_t cnt = 0;
for (auto &row : ia)
for (auto &col :row){
col = cnt;
++cnt ;
}
使用标准库函数begin和end也能实现:
for (auto p = begin(ia); p != end(ia); ++p){
for (auto q = begin(*p); q != end(*p); ++q)
cout << *q << ' ';
cout << endl;
}
将4个整数组成的数组命名位int_array:
using int_array = int[4];
typedef int int_array[4];