C++ Primer第三章
-
命名空间的using声明,声明之后无须专门的前缀。
比如using namespace::name;之后直接用name就行。
头文件中不应该包含using声明。这是因为头文件的内容会被拷贝到所有引用它的文件中,如果不经意的包含了某些名字可能引起名字冲突。 -
size函数返回的是一个string::size_type类型的值。所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型的。字符串比较时,如果两个string对象的长度不同,而且较短string对象每个字符都与长字符对应位置的字符相同,则较短string对象小于较长string对象。
getline(cin, line)从输入流中读入内容,将读入的内容存到那个string对象如line中。 -
字面值和string对象相加
当string对象与字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符两侧的运算对象至少有一个是string。(字符串字面值和string是不同的类型。) -
标砖库类型vector
vector表示对象的集合,其中所有对象的类型都相同,可以根据索引访问对象,因为其中包含其他对象,所以也被称为容器。
初始化vector类型的方法。
想vector中添加元素 push_back
size函数返回vector对象中元素的个数,返回值的类型是vector定义的size_type类型。 -
迭代器
可以通过迭代器获得string对象或者vector等容器的元素。这些类型都拥有begin和end的成员,begin成员负责返回指向第一个元素的迭代器,end成员负责返回容器 尾元素的下一个位置的迭代器。
*iter 返回迭代器iter所指元素的引用
iter->mem 解引用iter并获取该元素的名为mem的成员。
const_iterator表示迭代器的类型,和常量指针类似,可读但不能修改它所指的元素值。
检查元素是否为空,(*it).empty(),因为it是一个迭代器,没有empty成员,所以需要先解引用获得结果。可以根据箭头运算符(->)简化该操作。
注意使用了迭代器的循环体,不要向迭代器所属的容器添加对象。 -
数组
数组的声明形如a[d],a是数组的名字,d是数组的维度。编译时维度是已知的,维度必须是一个常量表达式。
unsigned cnt = 42;
constexpr unsigned sz=42;
int arr[10];
int *parr[10];//含有42个整型指针的数组
string bad[cnt];//错误,cnt不是常量表达式;
string strs[get_size()];//当get_size是const_expr时正确;否则错误
初始化数组元素
const unsigned sz = 3;
int ia1[sz] = {0,1,2}; // array of three ints with values 0, 1, 2
int a2[] = {0, 1, 2}; // an array of dimension 3
int a3[5] = {0, 1, 2}; // equivalent to a3[] = {0, 1, 2, 0, 0}
string a4[3] = {"hi", "bye"}; // same as a4[] = {"hi", "bye", ""}
int a5[2] = {0,1,2}; // error: too many initializers
字符数组的特殊性
char a1[] = {'C', '+', '+'}; // list initialization, no null
char a2[] = {'C', '+', '+', '\0'}; // list initialization, explicit null
char a3[] = "C++"; // null terminator added
automatically
const char a4[6] = "Daniel"; // error: no space for the null!
不允许拷贝和赋值
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组复制。
理解复杂的数组声明
int *ptrs[10]; // 含有10个整型指针的数组
int &refs[10] = /* ? */; // error: 不存在引用的数组
int (*Parray)[10] = &arr; // Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // arrRef 引用一个含有10个整数的数组
访问数组元素
访问数组下标的时候,通常将其定义为size_t类型,它是一种与机器无关的无符号类型,被设计的足够大以便能表示内存中任意对象的大小。
- 数组
使用数组的时候编译器一般会把它转换为指针。在大多数表达式中,使用数组类型的对象其实是在使用一个指向数组首元素的指针。
string nums[] = {"one", "two", "three"}; // array of strings
string *p = &nums[0]; //p指向nums的第一个元素
string *p2 = nums; //等价于p2=&nums[0];
int ia[]={0,1,2,3};
auto ia2(ia);//ia2是一个整型指针,指向ia数组的第一个元素
ia=42;//错误,ia2是一个指针
如果使用decltype关键字,decltype(ia)返回的类型时一个由10个整数构成的数组。
- 指针也是迭代器
vector和string的迭代器支持的运算,数组的指针都支持。允许使用递增运算符将指针向前移动到下一个位置上。
int arr[] = {0,1,2};
int *p=arr;//p指向arr的第一个元素
++p;//指向arr[1]
int *e = &arr[10];//获取尾元素下一位置的指针
标准库函数begin和end
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);
int *last = end(ia);
- 指针运算
constexpr size_t sz=5;
int arr[sz]={1,2,3,4,5};
int *ip = arr;
int *ip2 = ip+4;//指向arr[4]
int *p=arr+sz;//p指向arr尾元素的下一位置,警告:不要解引用
int *p2=arr+10;//产生错误,arr只有5个元素,p2的值未定义
auto n=end(arr)-begin(arr);//两个指针相减的结果是一种ptrdiff_t的标准库类型,可能为负,是有符号类型
如果两个指针分别指向不相关的对象,则不能比较它们。两个空指针允许彼此相减,结果为 0。
下标和指针
int i=ia[2];//相当于 *(ia+2)
int *p=&ia[2];//p指向索引为2的元素
int j=p[1];//等价于 *(p+1)
int k=p[-2];//等价于ia[0]
- C风格字符串
C标准库中定义的一组函数,可用于操作C风格字符串,定义于cstring头文件(string.h)中。
strcpy(s1, s2);
复制字符串 s2 到字符串 s1。
strcat(s1, s2);
连接字符串 s2 到字符串 s1 的末尾。
strlen(s1);
返回字符串 s1 的长度。
strcmp(s1, s2);
如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。
strchr(s1, ch);
返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
strstr(s1, s2);
返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
传入此类函数的指针必须指向以空字符作为结束的数组。
char ca[]={'a','b','c'};
cout<< strlen(ca)<<endl;//错误,ca没有以空字符结束
string对象和C风格字符串
之前提到过可以用字符串字面值来初始化string对象,string s(“Hello world”);
字符串的字面值可以由 以空字符结束的字符数组来替代:
- 允许使用空字符结束的字符数组来初始化string对象或者为string对象赋值
- 在string对象的加法运算中允许使用空字符结束的字符数组作为其中一个运算对象(不能两个对象都是)。
但是当需要一个C风格字符串,无法直接用string对象来代替。为此string专门提供了一个名为c_str函数,该函数的返回值是一个指针指向以空字符串结束的字符数组,指针的类型时const char*,从而确保字符数组的内容不会改变。
无法保证c_str()函数返回的数组一直有效,原输入的值改变了之后,之前返回的数组将失去效用。
- 使用数组初始化 vector 对象
需要指明拷贝区域的首元素地址和尾后地址。
int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr), end(int_arr));
- 多维数组
int ia[3][4] = { // three elements; each element is an array of size 4
{0, 1, 2, 3}, // initializers for the row indexed by 0
{4, 5, 6, 7}, // initializers for the row indexed by 1
{8, 9, 10, 11} // initializers for the row indexed by 2
};
// 和上一条语句作用相同
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
// 初始化每行首元素
int ia[3][4] = {{ 0 }, { 4 }, { 8 }};
// 初始化第一行元素
int ix[3][4] = {0, 3, 6, 9};
多维数组的下标引用
int (&row)[4]=ia[1];//将row绑定到ia的第二个4元素数组上。先将row定义为一个含有4个整数的数组的引用,然后将其绑定到ia的第二行。
遍历多维数组实例
// print the value of each element in ia, with each inner array on its own line
// p points to an array of four ints
for (auto p = ia; p != ia + 3; ++p) {
// q points to the first element of an array of four ints; that is, q points to an int
for (auto q = *p; q != *p + 4; ++q)
cout << *q << ' ';
cout << endl;
}
// p points to the first array in ia
for (auto p = begin(ia); p != end(ia); ++p) {
// q points to the first element in an inner array
for (auto q = begin(*p); q != end(*p); ++q)
cout << *q << ' '; // prints the int value to which q points
cout << endl;
}