《C++ Primer》 拾遗 第 3 章 字符串、向量和数组
Notes Cpp Primer
Lastmod: 2021-06-01 周二 22:40:38

第 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';
  }
Prev: 《C++ Primer》 拾遗 第 2 章 变量和基本类型
Next: 《C++ Primer》 拾遗 第 4 章 表达式