目录
3.1 命名空间的using声明
using namespace::name;
每个名字都需要独立的using声明
头文件不应包含using声明
3.2 标准库类型string
3.2.1 定义和初始化string对象
string s1; // 默认初始化,s1是空串
string s2(s1); // s2是s1的副本,直接初始化
string s2 = s1; // 等价于s2(s1),拷贝初始化
string s3("value"); // s3是字面值"value"的副本,除了字面值最后的空字符外
string s3 = "value"; // 等价于s3("value"),s3是字面值"value"的副本
string s4(n, 'c'); // 把s4初始化为连续 n 个字符 'c' 组成的字符串,直接初始化
string s4 = string(n, 'c'); // 拷贝初始化,等价于s4(n, 'c')
3.2.2 string对象上的操作
os << s | 将s写到输出流os当中,返回os |
is >> s | 从输入流is中读取字符串赋给s,字符串以空白分隔,返回is |
getline(is, s) | 从输入流is读取一行赋给s,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s中字符的个数,返回类型为string::size_type |
s[n] | 返回s中第n个字符的引用,位置n从0机起 |
s1 + s2 | 返回s1和s2连接后的结果 |
s1 = s2 | 用s2的副本代替s1中原来的字符 |
s1 == s2 | 如果s1和s2中所含字符完全一样,则它们相等;string对象的相 等性判断对字母的大小写敏感 |
s1 != s2 | |
<, <=, >, >= | 利用字符在字典中的顺序进行比较,对字母的大小写敏感 |
3.2.3 处理string对象中的字符
isalnum(c) | c是字母或数字时为true |
isalpha(c) | c是字母时为true |
iscntrl() | c是控制字符时为true |
isdigit(c) | c是数字时为true |
isgraph(c) | c不是空格但可以打印时为true |
islower(c) | c是小写字母时为true |
isprint(c) | c是可打印的字符时为true |
ispunct(c) | c是标点符号时为true |
isspace(c) | c是空白字符时为true |
isupper(c) | c是大写字母时为true |
isxdigit(c) | c是十六进制数时为true |
tolower(c) | c是大写字母,则返回小写字母形式,否则返回c。 |
toupper(c) | c是小写字母,则返回大些字母形式,否则返回c。 |
处理每个字符?使用范围for(range for)语句
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main()
{
string s("Hello Word!!!");
decltype(s.size()) punct_cnt = 0; // 标点符号计数器
// 统计s中标点符号的数量
for (char c : s)
{
if (ispunct(c))
++punct_cnt;
}
cout << punct_cnt << " punctuation characters in " << s << endl;
return 0;
}
使用范围for语句改变字符串中的字符
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main()
{
string s("Hello Word!!!");
// 转换成大写形式
for (char& c : s)
c = toupper(c); // c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;
return 0;
}
使用下标执行迭代
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main()
{
string s("some string");
// 依次处理s中的字符直至处理完全部字符或者遇到空白
for (string::size_type index = 0;
index != s.size() && !isspace(s[index]); ++index)
s[index] = toupper(s[index]); // 将当前字符改成大写形式
cout << s << endl;
return 0;
}
使用下标执行随机访问
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main()
{
// 把0到15之间的十进制数转换成对应的十六进制数
const string hexdigits("0123456789ABCDEF"); // 可能的十六进制数
cout << "输入一系列0到15之间的十进制数: " << endl;
string hex_str; // 用于保存十六进制的字符串
string::size_type n; // 用于保存从输入流读取的数
while (cin >> n)
{
if (n < hexdigits.size()) // 忽略无效输入
{
hex_str += hexdigits[n]; // 得到对应的十六进制数
}
}
cout << "对应的十六进制数:" << hex_str << endl;
return 0;
}
3.3 标准库类型vector
3.3.1 定义和初始化vector
vector<T> v1; // 默认初始化,v1是一个空的vector,它潜在的元素是T类型的
vector<T> v2(v1); // 直接初始化,v2中包含v1所有元素的副本
vector<T> v2 = v1; // 拷贝初始化,v2中包含v1所有元素的副本
vector<T> v3(n, val); // v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n); // v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a,b,c...}; // 列表初始化,v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5 = {a,b,c...}; // 等价于v5{a,b,c...}
3.3.2 向vector对象中添加元素
// 使用vector的成员函数push_back向其中添加元素
vector<int> v;
for (int i = 0; i != 10; ++i)
v.push_back(i);
3.3.3 其他vector操作
v.empty() | v为空返回true,否则返回false |
v.size() | 返回v中元素的个数 |
v.push_back(t) | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v中第n个字符的引用 |
v1 = v2 | 用v2中元素的拷贝替换v1中的元素 |
v1 = {a, b, c...} | 用列表中元素的拷贝替换v1中的元素 |
v1 == v2 | v1和v2相等当且仅当它们的元素数量相同且对应位置的元素 值都相同 |
v1 != v2 | |
<, <=, >, >= | 利用字典顺序进行比较 |
3.4 迭代器介绍
3.4.1 使用迭代器
迭代器运算符
string s("some thing");
if (s.begin() != s.end())
{
// 首字母改为大写形式
auto it = s.begin();
*it = toupper(*it);
}
将迭代器从一个元素移动到另一个元素
// 将所有字符改写为大写形式
for (auto it = s.begim(); it != s.end(); ++it)
*it = toupper(*it)
迭代器类型
vector<T>::iterator 读写 vector<T>::const_iterator 只读
3.4.2 迭代器运算
两个迭代器相减,所得结果是俩个迭代器的距离,类型为difference_type的带符号整数。
使用迭代器计算
// 二分搜索
auto beg = text.begin(), end = text.end();
auto mid = beg + (end - beg) / 2; // 初始状态下的中间点
while (mid != end && *mid != sought)
{
if (sought < *mid)
end = mid;
else
beg = mid + 1;
mid = beg + (end - beg) / 2; // 新的中点
}
3.5 数组
3.5.1 定义和初始化数组
- 数组的维度必须是一个常量表达式
- 数组不允许拷贝和赋值
理解复杂的数组声明
int* ptrs[10]; // ptrs是数组,该数组含有10个int型指针
int& refs[10] = /* ? */; // 不存在引用的数组
int(*Parray)[10] = &arr; // Parray是一个指针,指向一个int数组,数组含有10个元素
int(&arrRef)[10] = arr; // arrRef是数组的引用,该数组有10个int元素
int*(&arry)[10] = ptrs; // arry是数组的引用,该数组有10个int型指针
3.5.2 访问数组元素
使用数组下标时,通常将其定义为size_t类型。
3.5.3 指针和数组
在很多用到数组名字的地方,编译器会自动地将其替换为一个指向数组首元素地指针:
string* p = arr; // 等价于 string* p = &arr[0];
当使用数组作为一个auto变量地初始值时,推断得到的类型是指针而非数组;当使用decltype关键字时上述转换不会发生。
int ia[] = {0,1,2,3,4,5,6};
auto ia2(ia); // ia2是一个整型指针,指向ia地第一个元素
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9}; // ia3是一个整型数组
标准库函数begin和end
int ia[] = {0,1,2,3,4,5,6};
int* beg = begin(ia); // 指向ia首元素地指针
int* end = end(ia); // 之现象ia尾元素地下一位置地指针
指针运算
两个指针相减地结果地类型是一种名为ptrdiff_t地标准库类型
3.5.4 C风格字符串
strlen(p) | 返回p地长度,空字符不计算在内 |
strcmp(p1, p2) | 比较p1和p2地相等性。返回值为0、正值和负值 |
strcat(p1, p2) | 将p2附加到p1之后,返回p1 |
strcpy(p1, p2) | 将p2拷贝给p1,返回p1 |
3.5.5 与旧代码地接口
混用string对象和C风格字符串
- 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值
- 不能用string对象直接初始化指向字符的指针,调用string的成员函数c_str返回一个C风格的字符串。
3.6 多维数组
多维数组其实是数组的数组
使用范围for语句处理多维数组
要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。
int a[3][4] = {
{0,1,2,3},
{1,2,3,4},
{2,3,4,5}
};
for (auto& row : a)
{
for (auto& col : row)
cout << col << " ";
cout << endl;
}
练习
练习3.2:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line, word;
while (getline(cin, line))
cout << line << endl;
while (cin >> word)
cout << word << endl;
return 0;
}
练习3.3:
string类的输入操作符空白字符的处理:读取并忽略有效字符(非空白字符)之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止(该空白字符仍留在输入流中)。
getline函数对空白字符的处理:不忽略行开头的空白字符,读取字符直至遇到换行符,读取终止并丢弃换行符。
练习3.4:
#include<iostream>
#include<string>
using namespace std;
int main()
{
// 输出较大的字符串
cout << "输入两个字符串:" << endl;
string str1, str2;
while (cin >> str1 >> str2)
{
if (str1 == str2)
cout << "两个字符串相等" << endl;
else
cout << ((str1 > str2) ? str1 : str2) << endl;
}
// 输出最长的字符串
while (cin >> str1 >> str2)
{
if (str1 == str2)
cout << "两个字符串等长" << endl;
else
cout << ((str1.size() >= str2.size()) ? str1 : str2) << endl;
}
return 0;
}
练习3.5:
#include<iostream>
#include<string>
using namespace std;
int main()
{
// 无空格
string str, largeStr;
while (cin >> str)
{
largeStr += str;
}
cout << largeStr << endl;
// 有空格
while (cin >> str)
{
largeStr += str + ' ';
}
cout << largeStr << endl;
return 0;
}
练习3.6:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str("abcdefg");
for (auto &c : str)
c = 'X';
cout << str << endl;
return 0;
}
练习3.7:字符串内的字符将不会改变,必须是char&。
练习3.8:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str("some string");
// 使用while
string::size_type i = 0;
while (i != str.size()) {
str[i] = 'X';
++i;
}
cout << str << endl;
// 使用for
for (i = 0; i != str.size(); ++i)
str[i] = 'Y';
cout << str << endl;
return 0;
}
练习3.9:使用下标时必须确保其在合理范围之内,s为空串,s[0]输出空字符('\0')。
练习3.10:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
while (getline(cin, s))
{
for (auto c : s)
if (!ispunct(c))
cout << c;
cout << endl;
}
return 0;
}
练习3.11:合法 const char &
练习3.12:(a)正确;(b)错误,元素类型不匹配;(c)正确。
练习3.13:
(a)元素数量0;(b)元素数量10,每个都为0;(c)元素数量10,每个都为42;(d)元素数量1,值为10; (e)元素数量2,值为10和42;(f)元素数量10,都为空串;(g)元素数量10,每个字符串都为"hi"。
练习3.14:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> iv;
int num;
while (cin >> num)
{
iv.push_back(num);
}
return 0;
}
练习3.15:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<string> str_v;
string str;
while (cin >> str)
{
str_v.push_back(str);
}
return 0;
}
练习3.17:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<string> str_v;
string str;
while (cin >> str)
{
for (auto& c : str)
{
c = toupper(c);
}
str_v.push_back(str);
}
for (auto elem : str_v)
{
cout << elem << endl;
}
return 0;
}
练习3.18:不合法;ivec为空,不能用下标,应改为ivec.push_back(42)。
练习3.19:
vector<int> vec1(10, 42);
vector<int> vec2{ 42,42,42,42,42,42,42,42,42,42 };
vector<int> vec3;
for (int i = 0; i < 10; ++i)
vec3.push_back(42);
练习3.20:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
int num;
while (cin >> num)
{
ivec.push_back(num);
}
//相邻两数相加
if (ivec.empty())
{
cout << "请输入至少一个数据" << endl;
return -1;
}
else if (ivec.size() == 1)
{
cout << ivec[0] << " 没有相邻数据。" << endl;
}
else
{
for (decltype(ivec.size()) i = 0; i != ivec.size() - 1; ++i)
{
cout << ivec[i] + ivec[i + 1] << " ";
}
cout << endl;
}
//首尾两数相加
for (int i = 0, j = ivec.size()- 1;i <= j; ++i, --j )
{
cout << ivec[i] + ivec[j] << " ";
}
cout << endl;
return 0;
}
练习3.22:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> text;
for (string line; getline(cin, line);)
text.push_back(line);
for (auto it = text.begin(); it != text.end() && !it->empty(); ++it)
{
for (auto& c : *it)
c = toupper(c);
cout << *it << endl;
}
return 0;
}
练习3.23:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec{ 1,2,3,4,5,6,7,8,9,10 };
for (vector<int>::iterator it = ivec.begin(); it != ivec.end(); it++)
*it *= 2;
for (auto value : ivec)
cout << value << " ";
cout << endl;
return 0;
}
练习3.24:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
int num;
while (cin >> num)
ivec.push_back(num);
//相邻两数相加
if (ivec.empty())
{
cout << "请输入至少一个数据" << endl;
return -1;
}
else if (ivec.size() == 1)
cout << ivec[0] << " 没有相邻数据。" << endl;
else
{
for (auto it = ivec.begin(); it != ivec.end() - 1; )
cout << *it + *(++it) << " ";
cout << endl;
}
//首尾两数相加
for (auto beg = ivec.begin(), j = ivec.end()- 1; beg <= end; ++beg, --end)
cout << *beg + *end << " ";
cout << endl;
return 0;
}
练习3.25:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<unsigned> scores(11, 0); // 11个分数段,初始化为0
unsigned grade;
grade = -1;
cout << grade << endl;
while (cin >> grade)
{
if (grade <= 100)
++(*(scores.begin() + grade / 10));
}
for (auto it = scores.begin(); it != scores.end(); it++)
cout << *it<< " ";
cout << endl;
return 0;
}
练习3.26:迭代器之间没有定义两个迭代器之间的加法
练习3.27:
(a)、(c)非法,数组维度必须是常量表达式
(d)非法,没有空间存放空字符。
练习3.28:sa 和 sa2 是空串 ia全为0 ia2 是随机值
练习3.29:
与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。因为数组的大小固定,因此对某些特殊的应用来说,程序的运行时性能较好,但是相应地也损失了一些灵活性。
练习3.30:ix不能等于array_size;
练习3.31:
#include <iostream>
using namespace std;
int main()
{
int ia[10];
for (size_t i = 0; i < 10; ++i)
ia[i] = i;
return 0;
}
练习3.32:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
// 数组
int ia[10];
for (size_t i = 0; i < 10; ++i)
ia[i] = i;
int ia2[10];
for (size_t i = 0; i < 10; ++i)
ia2[i] = ia[i];
// vector
vector<int> iv(10);
for (auto iter = iv.begin(); iter != iv.end(); ++iter)
*iter = iter - iv.begin();
vector<int> iv2(iv);
return 0;
}
练习3.33:如果scores不初始化,编译器不会报错,但输出结果是一个未知数
练习3.34:p1指针移动p1-p2的距离,当p1是常量指针时,程序非法。
练习3.35:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int arr[] = { 0,1,2,3,4,5,6 };
for (auto ptr = begin(arr); ptr != end(arr); ++ptr)
*ptr = 0;
for (auto c : arr)
cout << c << " ";
return 0;
}
练习3.36:
#include<iostream>
#include<vector>
using namespace std;
bool compare(int* const pbeg1, int* const pend1, int* const pbeg2, int* const pend2)
{
if ((pend1 - pbeg1) != (pend2 - pbeg2))
return false;
else
{
for (auto it1 = pbeg1, it2 = pbeg2; it1 != pend1 && it2 != pend2; ++it1, ++it2)
if (*it1 != *it2)
return false;
}
return true;
}
int main()
{
int arr1[3] = { 0, 1, 2 };
int arr2[3] = { 1, 1, 2 };
cout << compare(begin(arr1), end(arr1), begin(arr2), end(arr2)) << endl;
vector<int> vec1 = { 0, 1, 2 };
vector<int> vec2 = { 0, 1, 2 };
cout << (vec1 == vec2) << endl;
return 0;
}
练习3.37: 输出hello后仍输出一段乱码字符。
练习3.38:指针的值只是所指对象的内存地址,将两个地址值相加没有意义。
练习3.39:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
string s1("hello");
string s2("hellx");
if (s1 > s2)
cout << "s1 > s2" << endl;
else if (s1 < s2)
cout << "s1 < s2" << endl;
else
cout << "si = s2" << endl;
const char cs1[] = "hello";
const char cs2[] = "hellx";
if (strcmp(cs1, cs2) > 0)
cout << "s1 > s2" << endl;
else if (strcmp(cs1, cs2) < 0)
cout << "s1 < s2" << endl;
else
cout << "si = s2" << endl;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
const char cstr1[] = "Hello";
const char cstr2[] = "world!";
char cstr3[20];
strcpy_s(cstr3, cstr1);
strcat_s(cstr3, " ");
strcat_s(cstr3, cstr2);
cout << cstr3 << endl;
return 0;
}
练习3.41:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<int> ivec(begin(ia), end(ia));
for (auto i : ivec)
cout << i << " ";
cout << endl;
return 0;
}
练习3.42:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int ia[10];
for (int* i = begin(ia); i != end(ia); ++i)
*i = ivec[i - begin(ia)];
for (auto i : ia)
cout << i << " ";
cout << endl;
return 0;
}
练习3.43:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };
for (const int(&p)[4] : ia)
{
for (int q : p)
cout << q << " ";
cout << endl;
}
cout << endl;
for (size_t i = 0; i != 3; i++)
{
for (size_t j = 0; j != 4; j++)
cout << ia[i][j] << " ";
cout << endl;
}
cout << endl;
for (int(*p)[4] = ia; p != ia + 3; p++) // p 是外层数组的首地址
{
for (int* q = *p; q != *p + 4; q++) // *p 是内层数组的首地址
{
cout << *q << " ";
}
cout << endl;
}
cout << endl;
return 0;
}
练习3.44:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };
// 使用类型别名
using int_array = int[4];
for (int_array(&p) : ia)
{
for (int q : p)
{
cout << q << " ";
}
cout << endl;
}
cout << endl;
for (size_t i = 0; i != 3; i++)
{
for (size_t j = 0; j != 4; j++)
cout << ia[i][j] << " ";
cout << endl;
}
cout << endl;
for (int_array* p = ia; p != ia + 3; p++) // p 是外层数组的首地址
{
for (int* q = *p; q != *p + 4; q++) // *p 是内层数组的首地址
{
cout << *q << " ";
}
cout << endl;
}
cout << endl;
return 0;
}
练习3.45:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int ia[3][4] = { {0,1,2,3},{2,3,4,5},{4,5,6,7} };
// 使用auto
for (auto& p : ia)
{
for (auto q : p)
cout << q << " ";
cout << endl;
}
cout << endl;
for (size_t i = 0; i != 3; i++)
{
for (size_t j = 0; j != 4; j++)
cout << ia[i][j] << " ";
cout << endl;
}
cout << endl;
for (auto p = ia; p != ia + 3; ++p)
{
for (int* q = *p; q != *p + 4; ++q)
cout << *q << " ";
cout << endl;
}
cout << endl;
return 0;
}