C++ Primer 5th Ch03字符串向量和数组
C++ Primer 5th Ch03字符串向量和数组

Directory


Note

using

头文件中一般不应使用using声明。避免造成名字冲突或重复声明

string

  • 头文件#include <string>`
  • string s = "hiya"对于字面值字符串编译器会在结尾处添加一个空字符(`\0`),string对象在创建时直接拷贝除了最后的空字符之外的内容。
  • getline(is,s)从is中读取一行赋给s,返回is。返回的行中不包含换行符,在得到string对象时丢弃了。
  • s.size()返回s中字符的个数,类型为string::size_type,为无符号类型。

拷贝初始化与直接初始化

拷贝初始化与直接初始化

vector

  • vector表示相同对象的集合。

  • #include <vector>
    using std::vector;
    
  • vector是一个类模板而不是类型

  • vector容纳大多数类型的对象作为其元素,引用不是对象,因此不存在包含引用的vector

  • push_back添加元素

  • 如果循环体的内部存在vector对象添加元素,不能使用for循环,因为添加或删除元素会导致end值无效

  • size返回的类型是由vector定义的size_type类型vector<int>::size_type

  • vector对象和string 对象的下标运算符可用于访问已存在的元素,而不能用于添加元素

vector的限制

vector对象可以动态的增长,但同时也存在一定的限制

  1. 不能在for循环中向vector对象添加、删除元素,会造成end值无效
  2. 任何一种可能改变vector容量的操作,都可能会使迭代器、引用、指针无效
  • 如果存储空间重新分配,则都会失效
  • 如果删除元素,则指向被删元素之前的仍有效

迭代器

  • *iter返回迭代器iter所指元素的引用
  • iter->mem解引用iter并获取该元素名为mem的成员,等价于(*iter).mem
  • 迭代器的标准库类型使用iteratorconst_iterator
  • 为了得到const_iterator,C++11标准引入了cbegincend

«««< HEAD ###数组

  • 数组大小固定
  • 和内置类型的变量一样,如果函数内部定义了数组,默认初始化会使数组包含有未定义的值。
  • 字符串字面值总的空字符串在初始化数组时会被拷贝到字符数组中
  • 不允许拷贝和赋值
  • string提供c_str函数用于返回c风格的字符串
  • 可使用数组初始化vector对象,指明拷贝区域的首尾地址
  • 要使用范围for语句处理多维数组,除了最内层循环,其他索引值均应使用引用类型。

    数组

  • 与vector不同之处在于大小确定不变
  • 维度必须为一个常量表达式,可使用constexpr关键字
  • 数组的元素应为对象,不存在引用的数组
  • 对于char a3[] = "C++";,在字符串的结尾处还有一个空字符,即a3长度为4
  • 不能把一个数组直接赋值给另一个数组

    6a8ae7e4f2b087657d32d3d608966a8841cee5f3

Exercise

3.1 使用恰当的using 声明重做 1.4.1节和2.6.2节的练习。

就是改成使用using声明。

PS:我本来就用的using :wink:

3.2 编写一段程序从标准输入中一次读入一行,然后修改该程序使其一次读入一个词。

3_2.cpp

3_2

3.3 请说明string类的输入运算符和getline函数分别是如何处理空白字符的。

string类型:

  • 自动忽略开头的空白(包括空格、换行、制表符等),遇到下一处空白停止
  • 赋值对象为字符串字面值时,不拷贝最后的空字符串

getline函数:

  • 读入所有内容,遇到换行符停止
  • 触发getline函数的换行符会被丢弃

3.4 编写一段程序读取两个字符串,比较其是否相等并输出结果。如果不相等,输出比较大的那个字符串。改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串。

3_4.cpp

3_4

3.5 编写一段程序从标准输入中读入多个字符串并将他们连接起来,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分割开来。

3_5.cpp

3_5

3.6 编写一段程序,使用范围for语句将字符串内所有字符用X代替。 3_6.cpp

3.7 就上一题完成的程序而言,如果将循环控制的变量设置为char将发生什么?先估计一下结果,然后实际编程进行验证。

正常不变。

3.8 分别用while循环和传统for循环重写第一题的程序,你觉得哪种形式更好呢?为什么? 3_8.cpp

范围for循环形式更好,简单省事

3.9 下面的程序有何作用?它合法吗?如果不合法?为什么?

string s;
cout << s[0] << endl;

试图获取空字符串的第一个元素。 超出元素范围,但编译器不报错!

3_9

3.10 编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩余的部分。

我觉得题意没说清,去除的标点符号是直接删呢?还是换成空格。我默认换成空格好了。

在循环中,使用 auto && 和auto &均可得到一样的结果。详见:

3_10.cpp

3.11 下面的范围for语句合法吗?如果合法,c的类型是什么?

const string s = "Keep out!";
for(auto &c : s){ /* ... */ }

3_11.cpp

如代码所见,auto在类型转换时自动转换成了 const char类型。因此语句合法

const int ci = 42;   // 不能改变 ci 的值,这是一个顶层 const  
int &r = ci;  // 错误:普通的 int& 不能绑定到 int 常量上
auto c = ci; //c 是一个整数,忽略了顶层const属性
auto &g = ci; // g是一个整型常量引用,绑定到ci

如上,int类型的引用不能直接绑定到const int类型上,但auto &可以。auto类型在变量自动转换会忽略顶层const属性。 3.12 下列vector对象的定义有不正确的吗?如果有,请指出来。对于正确的,描述其执行结果;对于不正确的,说明其错误的原因。

vector<vector<int>> ivec;         // 在C++11当中合法
vector<string> svec = ivec;       // 不合法,类型不一样
vector<string> svec(10, "null");  // 合法

3.13 下列的vector对象各包含多少个元素?这些元素的值分别是多少?

vector<int> v1;         // size:0,  no values.
vector<int> v2(10);     // size:10, value:0
vector<int> v3(10, 42); // size:10, value:42
vector<int> v4{ 10 };     // size:1,  value:10
vector<int> v5{ 10, 42 }; // size:2,  value:10, 42
vector<string> v6{ 10 };  // size:10, value:""
vector<string> v7{ 10, "hi" };  // size:10, value:"hi"

在确认无法执行列表初始化后,编译器会尝试使用默认值初始化vector对象

3.14 编写一段程序,用cin读入一组整数并把它们存入一个vector对象。

3_14.cpp

3.15 改写上题程序,不过这次读入的是字符串。

3_14.cpp

3.16 编写一段程序,把练习3.13中vector对象的容量和具体内容输出出来

3_16.cpp

3.17 从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改为大写形式。输出改变后的结果,每个词占一行。

3_17.cpp

3.18 下面的程序合法吗?如果不合法,你准备如何修改?

vector<int> ivec;
ivec[0] = 42;

不合法,初始化元素个数。或使用push_back函数

3.19 如果想定义一个含有10个元素的vector对象,所有元素的值都是42,请例举三种不同的实现方法,哪种方式更好呢?

vector<int> v = (10,42);
vector<int> v = {42,42,...,42};
拷贝复制
值插入 v.push_back

第一种方式更方便

3.20 读入一组整数并把他们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求先输出第一个和最后一个元素的和,接着输入第二个和倒数第二个元素的和,以此类推。

没有交代输出相邻整数时,如果元素个数为奇数,最后一个元素如何处理?默认打印咯:sunglasses:

3_20.cpp

3.21 请使用迭代器重做3.3.3节的第一个练习。

3_21.cpp

3.22 修改之前那个输出text第一段的程序,首先把text的第一段全部改成大写形式,然后输出它。

对于这个题目碰到一个问题,在输入vecString2文件时,中间的空行实际上并不为空,使用size函数可以看到大小为1,强制转换为int值打印发现ascii码值为13,即回车符\r。大坑啊

3_22.cpp

3_22

3.23 编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。

3.24 请使用迭代器重做3.3.3节的最后一个练习。

3_24.cpp

3.25 3.3.3节划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序实现完全相同的功能。

3_25.cpp

«««< HEAD 3.26 在100页的二分搜索程序中,为什么用的是 mid = beg + (end - beg) / 2, 而非 mid = (beg + end) / 2 ; ?

迭代器没有-操作,mid指向beg与end中间的位置。

注意:解引用*的优先级要低于++。

3.26 在100页的二分搜索程序中,为什么用的是 mid = beg + (end - beg) / 2, 而非 mid = (beg + end) / 2 ; ?

mid指向的时索引值的位置,所以应指向迭代器中间的位置,在二分搜索过程中,end和begin的值是会变化的。

6a8ae7e4f2b087657d32d3d608966a8841cee5f3

3.27 假设txt_size 是一个无参函数,它的返回值是int。请回答下列哪个定义是非法的,为什么?

<<<<<<< HEAD
unsigned buf_size = 1024; // 非法, 维度为常量表达式
(a) int ia[buf_size]; 
(b) int ia[4 * 7 - 14];
(c) int ia[txt_size()]; // 非法,数组维度应为常量
(d) char st[11] = "fundamental"; // 非法,字符串字面值在结尾包含有空字符
=======
unsigned buf_size = 1024;
(a) int ia[buf_size]; //非法, 维度必须为常量表达式
(b) int ia[4 * 7 - 14];
(c) int ia[txt_size()];//非法
(d) char st[11] = "fundamental";//非法,长度不够
>>>>>>> 6a8ae7e4f2b087657d32d3d608966a8841cee5f3

3.28 下列数组中元素的值是什么?

string sa[10];
int ia[10];
int main() {
<<<<<<< HEAD
  string sa2[10];
  int ia2[10];
}

函数中的int数组会认为是未定义的

3.29 相比于vector 来说,数组有哪些缺点,请例举一些。

大小固定 不能拷贝和赋值

string sa2[10];
int ia2[10]; //未定义! } ```

###3.29 相比于vector 来说,数组有哪些缺点,请例举一些。

大小固定,不允许拷贝和赋值

6a8ae7e4f2b087657d32d3d608966a8841cee5f3

3.30 指出下面代码中的索引错误。

constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix)
<<<<<<< HEAD
  ia[ix] = ix;

索引从0开始

3.31 编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。

3_31.cpp

3.32 将上一题刚刚创建的数组拷贝给另一数组。利用vector重写程序,实现类似的功能。

3.33 对于104页的程序来说,如果不初始化scores将会发生什么?

数组未定义

3.34 假定p1 和 p2 都指向同一个数组中的元素,则下面程序的功能是什么?什么情况下该程序是非法的?

p1 += p2 - p1;

将p1移动到p2处

3.35 编写一段程序,利用指针将数组中的元素置为0。

3_35.cpp

3.36 编写一段程序,比较两个数组是否相等。再写一段程序,比较两个vector对象是否相等。

数组不能直接进行比较 需要大小元素都相等两个数组才相等。 当时用数组的时候实际上用的是指向数组首元素的指针

vector对象可利用=直接进行比较

3.37 下面的程序是何含义,程序的输出结果是什么?

const char ca[] = { 'h', 'e', 'l', 'l', 'o' };
const char *cp = ca;
while (*cp) {
    cout << *cp << endl;
    ++cp;
}

打印字符数组 hello, 但编辑器会报warning,因为C style的字符串没有以\0结尾

3.38 在本节中我们提到,将两个指针相加不但是非法的,而且也没有什么意义。请问为什么两个指针相加没有意义?

指针相减表示它们之间的距离,两个指针相加也就是内存地址相加可能会导致溢出同时没有什么意义

3.39 编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容。

string对象可使用=直接进行比较 c风格字符串使用strcmp函数

3.40 编写一段程序,定义两个字符数组并用字符串字面值初始化它们;接着再定义一个字符数组存放前面两个数组连接后的结果。使用strcpy和strcat把前两个数组的内容拷贝到第三个数组当中。

3_40.cpp

3.41 编写一段程序,用整型数组初始化一个vector对象。

3_41.cpp

3.42 编写一段程序,将含有整数元素的vector对象拷贝给一个整型数组。

逐个元素拷贝吧

3.43 编写3个不同版本的程序,令其均能输出ia的元素。版本1使用范围for语句管理迭代过程;版本2和版本3都使用普通for语句,其中版本2要求使用下标运算符,版本3要求使用指针。此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、auto关键字和decltype关键字。

3.44 改写上一个练习中的程序,使用类型别名来代替循环控制变量的类型。

类型别名typedef与using

3.45 再一次改写程序,这次使用auto关键字。

ia[ix] = ix; ```

超范围,索引从0开始

3.31 编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。

3_31.cpp

3.32 将上一题刚刚创建的数组拷贝给另一数组。利用vector重写程序,实现类似的功能。

3_32.cpp

3.33 对于104页的程序来说,如果不初始化scores将会发生什么?

score未初始化,会造成未定义,值不确定。

6a8ae7e4f2b087657d32d3d608966a8841cee5f3

上一篇   顶层const与底层const

下一篇   auto&&