第 3 章 字符串、向量和数组
3.1 命名空间的 using 声明
using 声明 using declaration
using namesapce::name;
头文件不应包含 using 声明。因为头文件会被包含到其他文件中去,使用 using 声明则可能带来名字冲突。
3.2 标准库类型 string
3.2.1 定义和初始化 string 对象
#include <iostream>
#include <cassert>
#include <iterator>
#include <string>
#include <cctype>
int main()
{
{
// string::string()
std::string s;
assert(s.empty() && (s.length() == 0) && (s.size() == 0));
}
{
// string::string(size_type count, charT ch)
std::string s(4, '=');
std::cout << s << '\n'; // "===="
}
{
std::string const other("Exemplary");
// string::string(string const& other, size_type pos, size_type count)
std::string s(other, 0, other.length()-1);
std::cout << s << '\n'; // "Exemplar"
}
{
std::string const other("Mutatis Mutandis");
// ^
// basic_string( const basic_string& other, size_type pos)
std::string s(other, 8);
std::cout << s << '\n'; // "Mutandis", i.e. [8, 16)
}
{
// string::string(charT const* s, size_type count)
std::string s("C-style string", 7);
std::cout << s << '\n'; // "C-style", i.e. [0, 7)
}
{
// string::string(charT const* s)
std::string s("C-style\0string");
std::cout << s << '\n'; // "C-style"
}
{
char mutable_c_str[] = "another C-style string";
// string::string(InputIt first, InputIt last)
std::string s(std::begin(mutable_c_str)+8, std::end(mutable_c_str)-1);
std::cout << s << '\n'; // "C-style string"
}
{
std::string const other("Exemplar");
std::string s(other);
std::cout << s << '\n'; // "Exemplar"
}
{
// string::string(string&& str)
std::string s(std::string("C++ by ") + std::string("example"));
std::cout << s << '\n'; // "C++ by example"
}
{
// string(std::initializer_list<charT> ilist)
std::string s({ 'C', '-', 's', 't', 'y', 'l', 'e' });
std::cout << s << '\n'; // "C-style"
}
{
// overload resolution selects string(InputIt first, InputIt last) [with InputIt = int]
// which behaves as if string(size_type count, charT ch) is called
std::string s(3, std::toupper('a'));
std::cout << s << '\n'; // "AAA"
}
}
====
Exemplar
Mutandis
C-style
C-style
C-style string
Exemplar
C++ by example
C-style
AAA
拷贝构造与直接构造
#include <iostream>
using namespace std;
class A {
int a;
public:
A(int a) : a(a) {
cout << "normal init\n";
}
A(const A &B) {
a = B.a;
cout << "copy init\n";
}
void operator=(const A &B) {
a = B.a;
cout << "copy assignment\n";
}
};
void bar() {
for(int i = 0; i < 50; i++)
putchar('-');
putchar('\n');
}
int main() {
A obj1(10);
bar();
A obj2 = obj1;
bar();
A obj3(obj1);
bar();
A obj4{obj1};
bar();
A obj5 = 10;
bar();
A obj6 = A(10);
bar();
return 0;
}
normal init
--------------------------------------------------
copy init
--------------------------------------------------
copy init
--------------------------------------------------
copy init
--------------------------------------------------
normal init
--------------------------------------------------
normal init
--------------------------------------------------
3.2.2 string 对象上的操作
3.2.3 处理 string 对象中的字符
#include <cctype>
isalnum(c) // 字母或数字
isalpha(c) // 字母
iscntrl(c) // 控制字符
isdigit(c) // 数字
isgraph(c) // 不为空格且可打印
islower(c) // 小写字符
isprint(c) // 可打印字符
ispunct(c) // 标点符号(不是控制字符、数字、字母、可打印空白)
isspace(c) // 空格、水平/垂直制表符、回车符、换行符、进纸符
isupper(c) // 大写字符
isxdigit(c) // 16进制数字
tolower(c)
toupper(c)
3.3 标准库类型 vector
根据模板创建类或函数的过程称为实例化 instantiation
。
注意引用不是元素,因此没有引用的 vector。
3.4 迭代器介绍
3.4.1 使用迭代器
end 返回尾后迭代器 off-the-end iterator
指向的是尾元素的下一个位置。
迭代器类型
一般来说迭代器有 iterator 类型和 const_iterator 类型。 const 容器通过 begin、end 得到的是 const_iterator。
对于非 const 的容器 begin、end 得到的是 iterator,但也可以使用 const_iterator 进行访问。
C++11 引入 cbegin 和 cend 来始终得到 const_iterator。
3.5 数组
定义数组不能使用 auto 关键字推断基本类型。
关于数组的维度
int i = 10;
// int a[i];
// array bound is not an integer constant before ']' token
const int j = 10;
int b[j];
const int k = i;
// int c[k];
// size of array 'c' is not an integral constant-expression
指向数组的指针和数组的引用
int arr[10];
int (*p)[10] = &arr;
int (&r)[10] = arr;
数组大小一般可以使用无符号 size_t 类型,它定义在 cstddef 中。
3.5.3 指针和数组
很多时候编译器会自动将数组名替换为指向数组首元素的指针。
int arr[] = {1, 2, 3, 4, 5};
auto pa = arr;
int main() {
cout << *(pa+2) << '\n'; // 3
return 0;
}
但是 decltype 行为会不同,会得到数组类型。
int arr[]={1,2,3,4,5};
decltype(arr) brr={1,2,3};
可以用 begin 和 end 得到数组的头指针和尾后指针。
int arr[] = {1,2,3,4,5};
int *ih = begin(arr);
int *it = end(arr);
两个指针相减的结果类型是 ptrdiff_t,这个类型同样定义在 cstddef 头文件中。
注意内置的下标运算符可以支持负值
#include <iostream>
using namespace std;
const int M=100;
int a[M];
int *p = a + 50;
int main() {
for (int i = 0; i < M; i++)
a[i] = i;
cout << p[-20] << '\n'; // 30
return 0;
}
3.5.5 与旧代码的接口
string s;
s.c_str(); // 注意这个函数的返回值,当 s 变化时可能会变化
3.6 多维数组
严格来说 C++ 没有多维数组,只有数组的数组。
范围 for 可以用于多维数组
#include <iostream>
using namespace std;
int arr[2][3];
int main() {
int i = 0;
for (auto &row : arr)
for (auto &item : row)
item = i++;
for (const auto &row : arr) {
for (auto item : row)
cout << item << ' ';
cout << '\n';
}
return 0;
}
注意 row 必须是引用,否则将被视为一个指针int*
,从而内层循环不合法。
或者也可以写作
for (decltype(arr[0]) row : arr) {
for (auto item : row)
cout << item << ' ';
cout << '\n';
}
Next: 《C++ Primer》 拾遗 第 4 章 表达式