Code Snippet

/* Template Begin */
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <time.h>
#include <cmath>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <bitset>
#include <numeric>
#include <functional>
#include <iterator>
#include <tuple>
#include <complex>
#include <limits.h>
#include <stack>
#include <sstream>
using namespace std;
/* Template End */

ctype.h

Function Note
isalpha()
isdigit()
islower()
isupper()
ispunct()
isblank() C標準庫只為\t和space返回非0值
tolower()
toupper()

limits.h

Macro Constant
INT_MIN
LONG_MIN
LLONG_MIN
UINT_MIN
ULLONG_MIN

math.h

Trigonometric Functions

Function Note
sin(), cos(), tan(), asin(), acos(), atan()
atan2() 返回給定坐標的角度

註:這些 三角函數 輸入均為 浮點數,默認使用 弧度作為單位。

Hyperbolic Functions

Function
sinh(), cosh(), tanh(), asinh(), acosh(), atanh()

Exponential and Logarithmic Functions

Function Note
exp() 返回自然常數的指定指數值
log() 返回以自然常數為底的對數
log10() 返回以10為底的對數
log2() 返回以2為底的對數

Power Functions

Function Note
pow() 該函數底層通過浮點數進行計算, 存在精度損失問題. 若確實需要使用, 請配合round()
sqrt() 返回平方根
bqrt() 返回立方根
hypot() 返回直角三角形的斜邊

Rounding and Remainder Functions

Function
floow()
ceil()

Other Functions

Function Note
abs() 用於求整數的絕對值 (用於浮點數時存在精度問題)
fabs() 用於求浮點數的絕對值

Classification Macro/Functions

Macro/Function Note
isinf() 判斷浮點數是否無窮大
isnan() 判斷浮點數是否為實數

Comparison Macro/Functions

Function
isgreater()
isgreaterequal()

stdio.h

Formatted Input/Output

Function Note
scanf() 格式化輸入
printf() 格式化輸出
sscanf() 格式化輸入到字符串
sprintf() 格式化輸出到字符串
getchar() 該方法不會跳過空白字符(回車, 換行, 空格)
gets() 輸入1行 (以\n作為分隔符) 字符串
puts() 輸出1行字符串 (該方法會在末尾自動添加\n)

Macros

Macro Note
EOF 值為-1的整數, 作為 文件流的末尾標識字符 .
註: EOF並不屬於 文件內容 的一部分, 它只是一個 流狀態標識符
NULL 整數值為0的字符, 作為 字符串的末尾標識字符.
註: NULL字符 確實屬於 C語言風格字符串 的內容的一部分
size_t 無符號類型整數
註: 當其作為循環變量並與int類型的增量相運算時, 將存在類型轉化不兼容

stdlib.h

String Conversion

Function Note
atoi(), atof(), atol(), atoll() 轉換string到int, double, long, long long
strtod(), strtoll(), strtoul(), strtoull() 轉換string(解釋為以base為基數)到long, long long, unsigned long, unsigned long long

Pseudo-Random Sequence Generation

Function Note
srand() 初始化隨機數種子
rand() 生成下一個隨機數

Dynamic Memory Management

Function Note
malloc()
calloc() 該方法會自動進行填0初始化
reallocI()
free() 釋放動態申請的內存

Searching and Sorting

Function Note
qsort() 快速排序
bsearch() 二分查找

string.h

Copying

Function
memcpy()
memmove()
strcpy()

Concatenation

Function
strcat()

Comparison

Function
memcmp()
strcmp()

Searching

Function
memchr()
strchr()
strrchr()
strpbrk()
strcspn()
strspn()
strstr()
strtok()

Other

Function Note
memset() 該方法本質是 字符串. 若需要填充整數, 則只有下列整數的可行的: 0, -1
strlen() 該方法不計入字符串末尾的結束字符

time.h

Time Manipulation

Function
clock()
difftime()
mktime()
time()

Conversion

Function
asctime()
ctime()
gmtime()
localtime()
strftime()

Containers

vector

queue

deque

stack

list

set

map

unordered_map

unordered_set

Other Libraries

Library
algorithm
bitset
chrono
codecvt
complex
exception
functional
initializer_list
iterator
limits
locale
memory
new
numeric
random
ratio
regex
stdexcept
string
system_error
tuple
typeindex
typeinfo
type_traits
utility
valarray

Code Snippets

Initialize the Array with Fixed-Element

  int arr[10] = {1,2,3};
1 2 3 0 0 0 0 0 0 0

列表初始化列表的元素填充到 數組的前幾個元素數組的剩余元素一律 填0

  int arr[10];
  memset(arr, 0, sizeof(array));
0 0 0 0 0 0 0 0 0 0

註意:該種方法只能用於填充 0-1

int arr[10];
int main() {
  for (int i = 0; i < 10; ++i) {
    printf("%d ", arr[i]);
  }
  return 0;
}
0 0 0 0 0 0 0 0 0 0
  int arr[10];
  fill(arr, arr + 10, 2022);
2022 2022 2022 2022 2022 2022 2022 2022 2022 2022

fill_n() 只用 指定值 填充 前n個元素

int arr[10];
fill_n(arr, 3, 2020);
2020 2020 2020 0 8 0 75 0 16875376 0

Initialize the Array with Generated-Element

  int arr[10];
  generate(arr, arr + 10, []() { return rand() % 5; });
1 2 4 0 4 4 3 3 2 4

如果需要讓 generate() 保存 狀態,則可以利用 靜態局部變量來實現。

全局變量也可以,但 靜態局部變量使得 代碼結構更加 緊湊

  int arr[10];
  generate(arr, arr + 10, []() {
      static int i = 1;
      return i *= 2;
  });
2 4 8 16 32 64 128 256 512 1024

Generate Lexicographical-Permutation

  string words = "abc";
  do {
    cout << words << endl;
  } while (next_permutation(words.begin(), words.end()));
abc
acb
bac
bca
cab
cba

同理,prev_permutation()則是 生成上一個排列

相關輔助函數:is_permutation()

Hash Function

  hash<string> string_hash;
  string first_words = "abc";
  string second_words = "cba";

  printf("hash(first_words) = %lld", string_hash(first_words));
  printf("\n");
  printf("hash(second_words) = %lld", string_hash(second_words));
hash(first_words) = 3663726644998027833
hash(second_words) = -4830583261295167161

Find the Index of A Specific Element

  int arr[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
  int index = find(arr, arr + 10, 70) - arr;
  printf("index = %d", index);
index = 7
  int arr[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
  int index = find_if(arr, arr + 10, [](const auto &obj) {
      return obj != 0 && obj % 15 == 0;
  }) - arr;
  printf("index = %d", index);
index = 3

Find the Index of A Subsequence

  int arr[] = {0, 30, 40, 50, 10, 20, 30, 40, 50, 60, 70, 80, 90};
  int needle[] = {30, 40, 50};
  int index = find_first_of(arr, arr + 13, needle, needle + 3) - arr;
  printf("index = %d", index);
index = 1

find_first_of() :返回 第一個相等的子序列首元素下標

find_end():返回 最後一個相等的子序列首元素下標

  int arr[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
  int needle[] = {30, 40, 50};
  int index = search(arr, arr + 10, needle, needle + 3) - arr;
  printf("index = %d", index);
index = 3

search返回的是 第一個滿足條件的元素

默認的 search()使用的是 相等謂詞,我們也可以自定義 二元相等謂詞

int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int needle[] = {9, 16, 25};
int index = search(arr, arr + 10, needle, needle + 3, [](const auto &o1, const auto &o2) {
 return o1  * o1 == o2;
}) - arr;
printf("index = %d", index);
index = 3

此外,如果我們的 子序列相同元素組成的指定長度的子序列,則可以直接使用 search_n()

int arr[] = {0, 1, 2, 9, 9, 5, 6, 7, 8, 9, 10};
int index = search_n(arr, arr + 10, 2, 9) - arr;
printf("index = %d", index);
index = 3

Find in Two-Consecutive Elements

  int arr[] = {0, 10, 20, 20, 40, 50, 60, 70, 80, 90};
  int index = adjacent_find(arr, arr + 10, [](const auto &o1, const auto &o2){
    return o1 == o2;
  }) - arr;
  printf("index = %d", index);
index = 2

Count the number of elements for which predicate is true

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int cnt = count_if(arr, arr + 10, [](const auto &o1) {
      return o1 % 2 == 0;
  });
  printf("cnt = %d", cnt);
cnt = 5

Return the First Position where Two Ranges differs

  int arr[] = {0, 1, 2, 30, 4, 5, 6, 7, 8, 9};
  int needle[] = {0, 1, 2};
  pair<int *, int *> ret = mismatch(arr, arr + 10, needle);
  printf("ret->first = %d", ret.first - arr);
  printf("\n");
  printf("ret->second = %d", ret.second - needle);
ret->first = 3
ret->second = 3

mismatch() 默認使用的 二元相等謂詞 就是 operator=

如果需要,可以自定義 二元相等謂詞

equal()mismatch()類似,但它只返回 是否相等

Test For-All and Exist

  int arr[] = {0, 2, 4, 6, 8, 10};
  bool flag = all_of(arr, arr + 6, [](const auto&o1){
    return o1 % 2 == 0;
  });
  cout << flag;
1

any_of()同理,它可以測試是否有 任何一個元素滿足 條件

none_of() 可以用 all_of() 取反來實現。

Apply function to range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for_each(arr, arr + 10, [](const auto & o1){
    cout << o1 * o1 << " ";
  });
0 1 4 9 16 25 36 49 64 81

Remove Duplicates

  int arr[] = {10, 10, 20, 30, 40, 50, 10, 60, 70, 80};
  unique(arr, arr + 10);
  for (int i = 0; i < 10; ++i) {
    printf("%d ", arr[i]);
  }
10 20 30 40 50 10 60 70 80 80

n.b. unique()語義並不是 去除序列的重復元素,而是 去除序列的連續的重復元素 (只保留一個相同元素)

上述例子中只有 連續的10被去除。

如果確實要去除 序列中所有的重復元素,則應當先 sort()使得 所有的相同元素連續排列的,再使用 unique()

該函數也有 可復製到指定輸出流的版本 unique_copy()

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for_each(arr, arr + 10, [](const auto &item) {
      cout << item << " ";
  });
  copy(arr, arr + 10, ostream_iterator<int>(cout, " "));

Copy range of elements

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int dest[10];
  copy(arr, arr + 10, dest);

可以使用 帶謂詞的版本 copy_if()

如果需要 反向遍歷,可以使用 copy_backward()

Move range of elements

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  move(arr, arr + 2, arr + 3);
0 1 2 0 1 5 6 7 8 9

move()方法允許 目標內存區域來源內存區域發生 重疊

但是,move()不保證 來源內存區域內容保持原樣

Transform Range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  transform(arr, arr + 10, arr, [](const auto & o1){
    return o1 * o1;
  });
  copy(arr, arr + 10, ostream_iterator<int>(cout, " "));
0 1 4 9 16 25 36 49 64 81

transform()可以指定 輸出目標流,並且允許 輸出目標流輸入流發生 重疊

有點類似 for_each(),但 for_each()經常用於 非修改性操作,而 transform() 用於 修改性操作

Replace Values in Range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  replace(arr, arr + 10, 3, 3000);
0 1 2 3000 4 5 6 7 8 9

可以使用 帶謂詞版本的 replace_if()來替代 replace():只有當 謂詞 測試通過時,才會用 新值 替換 當前元素

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int dest[10];
  replace_copy(arr, arr + 10, dest, 3, 3000);
  copy(arr, arr + 10, ostream_iterator<int>(cout, " "));
  printf("\n");
  copy(dest, dest + 10, ostream_iterator<int>(cout, " "));
0 1 2 3000 4 5 6 7 8 9

相比於 replace()而言,replace_copy()可以指定 修改結果的輸出流

特別地,replace_copy()可以重新將 輸出流指定為 輸入流

在這個情況下,replace_copy()等價於 replace()

此外,還有一個 帶謂詞版本的 repalce_copy_if()

我們稱它是萬能的替換函數,因為它可以取代:repalce()replace_if()replace_copy()

Reverse Range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  reverse(arr, arr + 10);
9 8 7 6 5 4 3 2 1 0

可復製到指定輸出流的版本 reverse_copy()

Rotate left the elements in range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  rotate(arr, arr + 3, arr + 10);
3 4 5 6 7 8 9 0 1 2

Rearrange elements in range

  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  random_shuffle(arr, arr + 10);
8 1 9 2 0 5 7 3 4 6

該方法可用於 洗牌算法

Merge (Operating on Sorted Ranges)

  vector<int> A = {1, 2, 3};
  vector<int> B = {3, 4, 5, 6};
  sort(A.begin(), A.end());
  sort(B.begin(), B.end());
  vector<int> result(10);
  auto it = set_intersection(A.begin(), A.end(), B.begin(), B.end(), result.begin());
  copy(result.begin(), it, ostream_iterator<int>(cout, " "));
3

相比於 set_union()而言,merge()保留多個相同的元素

    vector<int> arr = {1, 1, 2, 3, 4, 2, 3, 4, 5, 6, 7};
    inplace_merge(arr.begin(), arr.begin() + 5, arr.end());
     1 1 2 2 3 3 4 4 5 6 7

n.b. 上述的 所有的歸並操作在使用之前,都需要用 sort()集合A集合B 進行 預處理

否則將導致輸出結果的錯誤。

Min/Max

Heap

堆操作函數將一個 數組維護 堆結構,但除此之外,我們還需要一個 表示堆大小的整數

n.b. 數組的大小 不一定等於 堆的大小,我們有可能 只使用數組中的一部分連續區域 來構成

n.b. 使用 數組時需要 額外維護一個表示堆大小的整數。但如果使用的是 vector,則可以 在每次堆操作後手動地 維護vector,使得 vector的大小始終與 heap的大小 相等。

Java的是,C++Heap默認是 最大堆,同理,基於Heap的PriorityQueue也默認是 最大優先隊列

除此之外,還有一些 輔助操作

大部分情況下,如果需要使用 優先隊列可以首先考慮 priority_queue

但如果需要一些更底層的操作,可以使用 heap functions

  vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int ret1 = lower_bound(v.begin(), v.end(), 3) - v.begin();
  int ret2 = upper_bound(v.begin(), v.end(), 3) - v.begin();
  printf("lower_bound = %d ", ret1);
  printf("\n");
  printf("upper_bound = %d ", ret2);
lower_bound = 3
upper_bound = 4

n.b. lower_bound()upper_bound()語義 可能直觀上和 名稱不同。

但實際上, lower_bound()指的是 not less than

upper_bound() 指的是 greater than

  vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  bool found = binary_search(v.begin(), v.end(), 3);
  printf("found = %d", found);
found = 1

如果 已知序列是有序的,則可以用 binary search代替 linear search來獲得更高的效率。

但是,如果需要獲取 下標,則需要使用 lower_bound()來進行 二分查找

盡管C標準庫確實有一個 bsearch()函數,但該函數原型對於C++的很多數據結構來說,並不適合。

Sorting

Partitions

Bitset

位圖 (bitset) 是C++ 容器庫之外的一個數據結構,它用 1個二進製位表示 1個邏輯型變量

相比於 char (8-bit)而言,更 節約空間

更重要的是,bitset提供了一些方便的 數據操作函數數據查詢函數,以及對 字符串輸出的支持。

  bitset<32> bits;
  bits.set(0,true);
  cout << bits;
00000000000000000000000000000001

空間緊張的情況下,bitset<size>可能會優於 vector<bool>

事實上,這並不一定。因為 vector<bool>普通的泛化vector不同,

它是 vector模板的特例化,具體庫實現很有可能針對 vector<bool>做出特別的優化。

但不同於 vector動態伸縮bitset的大小是通過 模板固定的,在編譯期確立和內存空間。

Complex

STL庫提供的 復數實現,同時重載了 運算符

  complex<int> a = {1,2};
  complex<int> b = {3,4};
  cout << a + b;
(4,6)

Ratio

由C++提供的 比例 (Ratio)庫。可以直接作為 分數庫使用。

  typedef ratio<1, 2> one_half;
  typedef ratio<2, 3> two_thirds;
  ratio_add<one_half, two_thirds> sum;
  printf("fraction: %d/%d", sum.num, sum.den);
fraction: 7/6

他們本質上是 ,只能在 編譯期確定數值。

Tuple

當需要表示 多種數據類型的有序序列,則可以使用 元組 (Tuple)

比如如果需要返回 2個返回值時,比起 structure來說,tuple會更加方便

實際上,有另一個東西叫 make_pair()

  tuple<string, int> t1("hello", 100);
  cout << "get<0> = " << get<0>(t1) << endl;
  cout << "get<1> = " << get<1>(t1);
get<0> = hello
get<1> = 100

實際上,比起 構造器而言, 我們更經常使用 make_tuple() 來構造元組。

 auto t2 = make_tuple("hello", "world", 100);
  auto tuple = make_tuple("hello", "world", 100);

  string str1;
  string str2;
  int num;
  tie(str1, str2, num) = tuple;

  cout << str1 << endl;
  cout << str2 << endl;
  cout << num << endl;
hello
world
100

如果只需要 unpack其中 某些元素,則可以使用 std::ignore來進行 占位

 auto tuple = make_tuple("hello", "world", 100);

 string str1;
 int num;
 tie(str1, std::ignore, num) = tuple;

 cout << str1 << endl;
 cout << num << endl;

Utility

Accumulate values in range

  vector<int> v = {0,1,2,3,4,5,6,7,8,9};
  int sum = accumulate(v.begin(), v.end(), 0);
  cout << sum;
45

accumulate() 不僅僅是用於 累加,它還支持 帶有二元謂詞的版本

可以利用 二元謂詞來實現 累減累乘累除

 vector<int> v = {1, 2, 3, 4};
 int sum = accumulate(v.begin(), v.end(), 1, [](const auto &o1, const auto &o2) {
    return o1 * o2;
 });
 cout << sum;
 24

Compute adjacent difference of range

  vector<int> v = {1,4,6,10};
  adjacent_difference(v.begin(), v.end(), v.begin());
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
1 3 2 4

adjacent_difference() 不但可以用於 差分,還可以進行 和分積分商分

 vector<int> v = {1, 4, 6, 10};
 adjacent_difference(v.begin(), v.end(), v.begin(), [](const auto &o1, const auto &o2) {
 return o1 * o2;
 });
 copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
 1 4 24 60

Compute cumulative inner product of range

  int init = 100;
  vector<int> a = {10, 20, 30};
  vector<int> b = {1, 2, 3};
  int ret = inner_product(a.begin(), a.end(), b.begin(), init);
  cout << ret;
240

n.b. 使用 inner_product() 時需要保證 矢量a矢量b長度相等

兩個矢量的長度不等的情況下,STL仍然會取 較長的矢量進行運算, 而較短的矢量越界值將是 不可預測的

可以使用 帶謂詞版本的 inner_product()來模擬 inner_product()

 int init = 100;
 vector<int> a = {10, 20, 30};
 vector<int> b = {1, 2, 3};
 int ret = inner_product(a.begin(), a.end(), b.begin(), init, [](const auto &o1, const auto &o2) {
  return o1 + o2;
 }, [](const auto &o1, const auto &o2) {
    return o1 * o2;
 });
 240

Compute partial sums of range

  vector<int> a = {1, 2, 3, 4, 5};
  partial_sum(a.begin(), a.end(), a.begin());
  copy(a.begin(), a.end(), ostream_iterator<int>(cout, " "));
1 3 6 10 15

同理,使用帶二元謂詞的版本,我們可以實現:部分差部分乘部分商

Store increasing sequence

  vector<int> v(10);
  iota(v.begin(), v.end(), 3);
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
3 4 5 6 7 8 9 10 11 12

iota()底層使用 operator++ 運算符來實現 遞增

Function Object

Base Classes

函數對象即利用 結構體/對象operator()運算符重載,完成類似 普通函數函數調用

struct IsOdd {
    bool operator() (int number) {
      return number % 2 != 0;
    }
};

int main() {
  IsOdd function_object;
  cout << function_object(3) << endl;
  cout << function_object(2);
  return 0;
}
1
0

本質上來說,函數對象就是一個 結構體

 struct IsOdd {
    bool operator() (int number) {
      return number % 2 != 0;
    }
 } function_object;
int main() {
  struct Add {
      int operator() (int o1, int o2) {
        return o1 + o2;
      }
  } function_object;
  cout << function_object(1, 2) << endl;
  return 0;
}
3

函數對象一般用於 predicate functioncomparison function

在大部分的場景下,可以用 lambda表達式 代替 函數對象 作為 callable

Operator Classes

Arithmetic Operation

上述 算術操作函數對象可以非常方便地用於 transform()

Comparison Operation

greaterless經常用於 UDT優先隊列定義。

比較器函數基本用法可以依據 傳入參數來進行 測試

但我們也可以 綁定某個 參數常數

 vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
 int cnt = count_if(v.begin(), v.end(), bind2nd(greater<int>(), 3));
 cout << cnt;
 6
Logical Operations
Adaptor and Conversion Functions
Negators

not1not2 可以對 現有的謂詞進行 取反

但他們僅支持 函數對象,並不適用於 lambda表達式

not1not2 實質上是 基於結構體模板的,而 lambda本質上屬於 函數而不是 結構體

在用 not1not2進行 取反時還必須要求定義 typedef int argument_type,而且對於 運算符函數必須用 const作修飾。

使用並不方便。

Parameter Binders

bind1st()bind2nd() 適用於 操作類的函數對象

一般與 Comparison Functions搭配使用。

Conversors

mem_fun()mem_fun_ref() 可以在 流操作時將 成員函數 轉化為 函數對象,可以用於 調用對象的getter函數
mem_fun():適用於 指針版本,如 容器的元素指針類型
mem_fun_ref():適用於 引用版本,如 容器的元素引用類型

Instrumental Types

Initializer List

initializer_list 是一種 數據類型,存儲 某種類型的元素列表

  auto a = {10,20,30};
  copy(a.begin(),a.end(),ostream_iterator<int>(cout, " "));
10 20 30

Vector

數據結構 矢量,提供和 數組等效的功能。

Constructor & Destructor

Constructor
  vector<int> v(10, 3);
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
3 3 3 3 3 3 3 3 3 3
  vector<int> src = {0,1,2,3};
  vector<int> v(src.begin() + 1, src.end());
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
1 2 3
  vector<int> src = {0,1,2,3};
  vector<int> v(src);
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
0 1 2 3

以下兩種寫法都是調用 拷貝構造函數

operator= 賦值運算符 等價於 copy constructor

 vector<int> a;
 vector<int> b(a);
 vector<int> b = a;
  vector<int> a = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  vector<int> b(a, a.get_allocator());

  cout << "a: ";
  copy(a.begin(), a.end(), ostream_iterator<int>(cout, " "));
  cout << endl;
  cout << "b: ";
  copy(b.begin(), b.end(), ostream_iterator<int>(cout, " "));
a: 0 1 2 3 4 5 6 7 8 9
b: 0 1 2 3 4 5 6 7 8 9
Destructor

vector自動地 釋放 元素所使用的內存

Iterators

Capacity

Element Access

Modifiers

  vector<int> v(10);
  v.assign(3, 5);
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
5 5 5

n.b. assign 相當於 重新初始化vector,vector中舊的數據會被 刪除

vector<bool>

也就是說,使用 vector<bool> 並不會比 bitset<> 占用 更多空間更多時間
在正常情況下,無法創建 真正的泛化的 vector<bool>,而取而代之的是 特例化的 vector<bool>

list

STL中的 listdoubly_linked_list

如果需要 singly_linked_list 則需要使用 forward_list

n.b. forward_list性能 幾乎和 Handwritten C-style singly-linked-list沒有差別!

forward_list為了 性能甚至沒有 size()

Operations

Array

STL的array數據結構僅僅是 ordinary array包裝類

  array<int, 10> num = {0,1,2,3};
  copy(num.begin(), num.end(), ostream_iterator<int>(cout, " "));
0 1 2 3 0 0 0 0 0 0

array提供了比 ordinal array更加方便的 輔助函數,但又沒有 vector復雜性

如果你期望保持 ordinal array的眾多 特性,同時不期望使用 auto extendend/extracted

 array<int, 10> num = {0, 1, 2, 3};
 num.fill(100);
 printf("size = %d\n", num.size());
 copy(num.begin(), num.end(), ostream_iterator<int>(cout, " "));
 size = 10
 100 100 100 100 100 100 100 100 100 100

deque

STL的 dequedoubly linked list

此外,STL的 stackqueue 均是基於 deque實現的

priority_queue

不同於Java的 PriorityQueue,C++的STL的 priority_queue默認是 大頂堆

template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

priority_queue 底層使用的是 heap functions

data source可以來自 多種類型的容器,只需要通過 range constructor來構造 priority_queue即可。

Map

map中的 條目 (entry)類型為

typedef pair<const Key, T> value_type;

map

STL中的 map底層數據結構是基於 Red-Black Tree 的,通過 operator <來完成 比較

而且,可以直接對 map進行 叠代來得出 已排序的key序列

int main() {

map<int, Person> m;
m[0];
printf("size = %d, value = %s\n", m.size(), m[0].name.c_str());

return 0;
}
```

```cpp
main.cpp:42:7: note: candidate: 'Person::Person(Person&&)'
main.cpp:42:7: note:   candidate expects 1 argument, 0 provided
```
  map<int, string> m;
  m[2] = "second";
  m[1] = "first";
  m[3] = "third";
  m[4] = "four";

  for (auto iter = m.begin(); iter != m.end(); iter++) {
    cout << iter->first << ", " << iter->second << endl;
  }
  return 0;
1, first
2, second
3, third
4, four

由於 map底層使用的是 red black tree,所以可以利用 map來進行 排序

  map<int, string> m;
  m[2] = "second";
  m[1] = "first";
  m[3] = "third";
  m[4] = "four";

  m.erase(2);
  m.erase(5);

  for (auto iter = m.begin(); iter != m.end(); iter++) {
    cout << iter->first << ", " << iter->second << endl;
  }
1, first
3, third
4, four

n.b. 如果 欲刪除的key不存在,則 無事發生

  map<int, string> m;
  m[2] = "second";
  m[1] = "first";
  m[3] = "third";
  m[4] = "four";

  auto iter = m.find(5);
  printf(iter == m.end() ? "not found" : "found");
  printf("\nsize = %d", m.size());
not found
size = 4

相比於 operator[]未找到元素時自動插入默認對而言,使用 find()判斷元素是否存在可以避免 浪費空間

而且更重要的,find()冪等的

miltimap

map 的區別在於, miltimap支持 不同pair有重復的key

Set

set

STL的 set 是基於 red-black tree的,此外,它的 元素必須是 const的,只能 插入元素刪除元素,而不能 修改元素

  set<int> S;
  S.insert(1);
  S.insert(2);
  S.insert(2);
  S.insert(3);
  S.insert(4);

  printf("size = %d\n", S.size());
  printf("count(2) = %d\n", S.count(2));
size = 4
count(2) = 1

n.b. 對於 set來說,如果 欲插入的元素已存在,則會忽略後續的插入(而不是 覆蓋插入)。

multiset

unordered_map

unordered_map

unordered_map 基於 bucket而非 red-black tree,通過 hash function索引 指定的 key

因此,unordered_map 中的 key無序的

因此,通常比 map 更快

Function Note
bucket_count() return number of buckets
max_bucket_count() return maximum number of buckets
bucket_size() return bucket size
bucket() locate element’s bucket
Function Note
load_factor() return load factor
max_load_factor() get or set maximum load factor
rehash() set number of buckets
reserve() request a capacity change

unordered_multimap

unordered_set

unordered_set

unordered_map類似,均是基於 bucket的。

unordered_multiset

Compute Remainder and Quotient

Branch

  jmp_buf  env;
  printf("before set jump\n");
  int val = setjmp(env);
  printf("after set jump\n");

  printf("val = %d\n", val);

  printf("before longjmp()\n");
  if (!val) longjmp(env, 1);
  printf("after longjmp()\n");
before set jump
after set jump
val = 0
before longjmp()
after set jump
val = 1
before longjmp()
after longjmp()

goto只支持 函數內跳轉,但 setjmp() and longjmp()可以支持 跨函數跳轉

使用 setjmp()保存 過程調用環境,然後用 longjmp()進行 jump

一個跨函數的例子。

 int foo();
 int bar();
 jmp_buf env;
 int val;

 int foo() {
   printf("enter foo()\n");

   val = setjmp(env);

   printf("before call bar()\n");
   bar();
   printf("after call bar()\n");

   printf("leave foo()\n");
   return 100;
 }

 int bar() {
   printf("enter bar()\n");


   printf("before longjmp()\n");
   if (val != 1) longjmp(env, 1);
   printf("before longjmp()\n");

   printf("leave bar()\n");
   return 200;
 }


 int main() {
   foo();
   return 0;
 }
 enter foo()
 before call bar()
 enter bar()
 before longjmp()
 before call bar()
 enter bar()
 before longjmp()
 before longjmp()
 leave bar()
 after call bar()
 leave foo()
int main() {

  for (int i = 0; i < 100; ++i) {
    for (int j = 0; j < 100; ++j) {

      if (i == 10 && j == 20) {
        goto ok;
      }

    }
  }

  ok: 0xdead;

  printf("bye\n");
  return 0;
}
bye

關於 跳出外層循環,更推薦的寫法是給 外層循環控製標記

 int main() {

   bool flag = true;
   for (int i = 0; flag && i < 100; ++i) {
     for (int j = 0; j < 100; ++j) {

       // break the outer loop
       if (i == 10 && j == 20) {
         flag = false;
         break;
       }

     }
   }

   printf("bye\n");
   return 0;
 }

The incremental of size_t and size_type

  char c_str[] = "china";
  string cpp_str = "china";

  printf("strlen(c_str) = %d\n", strlen(c_str));
  printf("cpp_str.size() = %d\n", cpp_str.size());

  //
  int c_str_size = strlen(c_str) + 1;
  printf("c_str_size = %d\n", c_str_size);

  int cpp_str_size = cpp_str.size() + 1;
  printf("cpp_str_size = %d\n", cpp_str_size);
strlen(c_str) = 5
cpp_str.size() = 5
c_str_size = 6
cpp_str_size = 6

size_tsize_type 本質上是 unsigned long long

強製轉化int 時屬於 narrowing conversion

對他們執行 遞增操作時應使用 遞增操作符 而不是直接 +1

直接 +1的含義為 將unsigned long long 窄化為int,然後加上int型的1。該操作是 未定義的,取決於具體實現。

 Clang-Tidy: Narrowing conversion from 'unsigned long long' to signed type 'int' is implementation-defined

Exit the program

- `at_quick_exit()`

Time

Time Manipulation

int main() {

  for (int i = 0; i < 1000000; ++i) {}

  clock_t t = clock();
  printf("clock ticks since the program launched = %d", t);

  return 0;
}
clock ticks since the program launched = 10

clock() 計數的是 從程序啟動以來所經歷的時間刻

   typedef long clock_t;

如果需要獲取 秒數則要除以 CLOCKS_PER_SECOND

int main() {

  time_t timer;
  time(&timer);

  printf("timer = %d\n", timer);
  return 0;
}
timer = 1650518640
 __MINGW_EXTENSION typedef __int64 __time64_t;
 typedef __time64_t time_t;

time_t本質上是 __int64

直接使用 __int64 或者 long long也可以被接受。

int main() {

  /* Get current timestamp & use localtime() to get related struct tm */
  time_t raw_time;
  time(&raw_time);
  struct tm * timeinfo = localtime(&raw_time);

  /* Modify the struct tm & use mktime() to get related timestamp */
  timeinfo->tm_year = 2022 - 1900;
  timeinfo->tm_mon = 4 - 1;
  timeinfo->tm_mday = 21;

  time_t timestamp = mktime(timeinfo);
  printf("timestamp = %d\n", timestamp);
  return 0;
}
timestamp = 1650519507

localtime():timestamp -> tm
mktime() :tm -> timestamp

對於 struct tm實例,我們不是直接構造,而是通過獲取 當前時間戳,然後轉化得到 當前時間戳所對應的tm,再修改 tm的屬性字段,最後通過 mktime()來獲得 修改後的tm對應的時間戳

tm_wdaytm_yday字段被 忽略

year1900開始,month0開始

Conversion

strftime()雖然提供了 格式化tm結構的功能,但 struct tm本身已經包含了足夠的信息,可以非常方便地 自定義格式化函數

Code Pointer

除了常規的類型指針外,傳統C語言還有一種指針稱作 代碼指針

通過對 代碼片段運用 地址運算符可以得到 該代碼片段的地址

int main() {

  s1:
  printf("hi\n");

  printf("address of s1 = %d\n", && s1);

  void (*foo) (void) = reinterpret_cast<void (*)(void)>(4199857);
  foo();

  return 0;
}
hi
address of s1 = 4199857
hi
address of s1 = 4199857
hi
address of s1 = 4199857
hi
address of s1 = 4199857
hi
address of s1 = 4199857
hi
......

Remove Const-Qualified

const_cast<>()

  string str = "abc";
  char *str_data = const_cast<char *>(str.data());
  str_data[0] = 'A';
  cout << str;
Abc