Repository: huihut/interview Branch: master Commit: aba5cd97e817 Files: 76 Total size: 665.1 KB Directory structure: gitextract_t2y4mj5e/ ├── .gitignore ├── Algorithm/ │ ├── BSTSearch.h │ ├── BinarySearch.h │ ├── BubbleSort.h │ ├── BucketSort.cpp │ ├── CountSort.cpp │ ├── FibonacciSearch.cpp │ ├── HeapSort.cpp │ ├── InsertSort.h │ ├── InsertionSearch.h │ ├── MergeSort.h │ ├── QuickSort.h │ ├── RadixSort.h │ ├── SelectionSort.h │ ├── SequentialSearch.h │ └── ShellSort.h ├── DataStructure/ │ ├── BinaryTree.cpp │ ├── HashTable.cpp │ ├── LinkList.cpp │ ├── LinkList_with_head.cpp │ ├── RedBlackTree.cpp │ ├── SqList.cpp │ └── SqStack.cpp ├── DesignPattern/ │ ├── AbstractFactoryPattern/ │ │ ├── Factory.cpp │ │ ├── Factory.h │ │ ├── FactoryMain.cpp │ │ ├── FactoryMain.h │ │ ├── concrete_factory.h │ │ ├── concrete_product.h │ │ └── product.h │ ├── AdapterPattern/ │ │ ├── AdapterMain.h │ │ ├── adaptee.h │ │ ├── adapter.h │ │ └── target.h │ ├── BridgePattern/ │ │ ├── BridgeMain.cpp │ │ ├── BridgeMain.h │ │ ├── abstraction.h │ │ ├── concrete_implementor.h │ │ ├── implementor.h │ │ └── refined_abstraction.h │ ├── ObserverPattern/ │ │ ├── ObserverMain.cpp │ │ ├── ObserverMain.h │ │ ├── concrete_observer.h │ │ ├── concrete_subject.h │ │ ├── observer.h │ │ └── subject.h │ ├── README.md │ ├── SingletonPattern/ │ │ ├── README.md │ │ ├── Singleton.cpp │ │ ├── Singleton.h │ │ └── SingletonMain.h │ └── main.cpp ├── LICENSE ├── Problems/ │ ├── ChessboardCoverageProblem/ │ │ ├── ChessboardCoverage.cpp │ │ └── README.md │ ├── KnapsackProblem/ │ │ ├── README.md │ │ └── pack.cpp │ ├── NeumannNeighborProblem/ │ │ ├── Formula/ │ │ │ ├── Neumann2_3_12.cpp │ │ │ └── README.md │ │ ├── README.md │ │ └── Recursive/ │ │ ├── Neumann2_4_12.cpp │ │ └── README.md │ ├── RoundRobinProblem/ │ │ ├── MatchTable.cpp │ │ └── README.md │ └── TubingProblem/ │ ├── README.md │ └── Tubing.cpp ├── README.md ├── README_en.md ├── STL/ │ ├── README.md │ └── STL.md └── docs/ ├── .nojekyll ├── CNAME ├── README.md ├── _navbar.md ├── en.md └── index.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .vscode *.txt *.bat ================================================ FILE: Algorithm/BSTSearch.h ================================================ /* 二叉搜索树的查找算法: 在二叉搜索树b中查找x的过程为: 1. 若b是空树,则搜索失败,否则: 2. 若x等于b的根节点的数据域之值,则查找成功;否则: 3. 若x小于b的根节点的数据域之值,则搜索左子树;否则: 4. 查找右子树。 */ // 在根指针T所指二叉查找树中递归地查找其关键字等于key的数据元素,若查找成功, // 则指针p指向該数据元素节点,并返回TRUE,否则指针指向查找路径上访问的最终 // 一个节点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL Status SearchBST(BiTree T, KeyType key, BiTree f, BiTree &p){ if(!T) { //查找不成功 p=f; return false; } else if (key == T->data.key) { //查找成功 p=T; return true; } else if (key < T->data.key) //在左子树中继续查找 return SearchBST(T->lchild, key, T, p); else //在右子树中继续查找 return SearchBST(T->rchild, key, T, p); } ================================================ FILE: Algorithm/BinarySearch.h ================================================ // 二分查找(折半查找):对于已排序,若无序,需要先排序 // 非递归 int BinarySearch(vector v, int value , int low, int high) { if (v.size() <= 0) { return -1; } while (low <= high) { int mid = low + (high - low) / 2; if (v[mid] == value) { return mid; } else if (v[mid] > value) { high = mid - 1; } else { low = mid + 1; } } return -1; } // 递归 int BinarySearch2(vector v, int value, int low, int high) { if (low > high) return -1; int mid = low + (high - low) / 2; if (v[mid] == value) return mid; else if (v[mid] > value) return BinarySearch2(v, value, low, mid - 1); else return BinarySearch2(v, value, mid + 1, high); } ================================================ FILE: Algorithm/BubbleSort.h ================================================ /* (无序区,有序区)。从无序区通过交换找出最大元素放到有序区前端。 选择排序思路: 1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 3. 针对所有的元素重复以上的步骤,除了最后一个。 4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 */ // 冒泡排序 void BubbleSort(vector& v) { int len = v.size(); for (int i = 0; i < len - 1; ++i) for (int j = 0; j < len - 1 - i; ++j) if (v[j] > v[j + 1]) swap(v[j], v[j + 1]); } // 模板实现冒泡排序 template //整數或浮點數皆可使用,若要使用物件(class)時必須設定大於(>)的運算子功能 void bubble_sort(T arr[], int len) { for (int i = 0; i < len - 1; i++) for (int j = 0; j < len - 1 - i; j++) if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]); } // 冒泡排序(改进版) void BubbleSort_orderly(vector& v) { int len = v.size(); bool orderly = false; for (int i = 0; i < len - 1 && !orderly; ++i) { orderly = true; for (int j = 0; j < len - 1 - i; ++j) { if (v[j] > v[j + 1]) { // 从小到大 orderly = false; // 发生交换则仍非有序 swap(v[j], v[j + 1]); } } } } ================================================ FILE: Algorithm/BucketSort.cpp ================================================ #include #include #include using std::vector; /***************** 桶排序:将值为i的元素放入i号桶,最后依次把桶里的元素倒出来。 桶排序序思路: 1. 设置一个定量的数组当作空桶子。 2. 寻访序列,并且把项目一个一个放到对应的桶子去。 3. 对每个不是空的桶子进行排序。 4. 从不是空的桶子里把项目再放回原来的序列中。 假设数据分布在[0,100)之间,每个桶内部用链表表示,在数据入桶的同时插入排序,然后把各个桶中的数据合并。 *****************/ const int BUCKET_NUM = 10; struct ListNode{ explicit ListNode(int i=0):mData(i),mNext(NULL){} ListNode* mNext; int mData; }; ListNode* insert(ListNode* head,int val){ ListNode dummyNode; ListNode *newNode = new ListNode(val); ListNode *pre,*curr; dummyNode.mNext = head; pre = &dummyNode; curr = head; while(NULL!=curr && curr->mData<=val){ pre = curr; curr = curr->mNext; } newNode->mNext = curr; pre->mNext = newNode; return dummyNode.mNext; } ListNode* Merge(ListNode *head1,ListNode *head2){ ListNode dummyNode; ListNode *dummy = &dummyNode; while(NULL!=head1 && NULL!=head2){ if(head1->mData <= head2->mData){ dummy->mNext = head1; head1 = head1->mNext; }else{ dummy->mNext = head2; head2 = head2->mNext; } dummy = dummy->mNext; } if(NULL!=head1) dummy->mNext = head1; if(NULL!=head2) dummy->mNext = head2; return dummyNode.mNext; } void BucketSort(int n,int arr[]){ vector buckets(BUCKET_NUM,(ListNode*)(0)); for(int i=0;imData; head = head->mNext; } } ================================================ FILE: Algorithm/CountSort.cpp ================================================ /***************** 计数排序:统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。 计数排序基于一个假设,待排序数列的所有数均为整数,且出现在(0,k)的区间之内。 如果 k(待排数组的最大值) 过大则会引起较大的空间复杂度,一般是用来排序 0 到 100 之间的数字的最好的算法,但是它不适合按字母顺序排序人名。 计数排序不是比较排序,排序的速度快于任何比较排序算法。 时间复杂度为 O(n+k),空间复杂度为 O(n+k) 算法的步骤如下: 1. 找出待排序的数组中最大和最小的元素 2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项 3. 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加) 4. 反向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1 *****************/ #include #include #include using namespace std; // 计数排序 void CountSort(vector& vecRaw, vector& vecObj) { // 确保待排序容器非空 if (vecRaw.size() == 0) return; // 使用 vecRaw 的最大值 + 1 作为计数容器 countVec 的大小 int vecCountLength = (*max_element(begin(vecRaw), end(vecRaw))) + 1; vector vecCount(vecCountLength, 0); // 统计每个键值出现的次数 for (int i = 0; i < vecRaw.size(); i++) vecCount[vecRaw[i]]++; // 后面的键值出现的位置为前面所有键值出现的次数之和 for (int i = 1; i < vecCountLength; i++) vecCount[i] += vecCount[i - 1]; // 将键值放到目标位置 for (int i = vecRaw.size(); i > 0; i--) // 此处逆序是为了保持相同键值的稳定性 vecObj[--vecCount[vecRaw[i - 1]]] = vecRaw[i - 1]; } int main() { vector vecRaw = { 0,5,7,9,6,3,4,5,2,8,6,9,2,1 }; vector vecObj(vecRaw.size(), 0); CountSort(vecRaw, vecObj); for (int i = 0; i < vecObj.size(); ++i) cout << vecObj[i] << " "; cout << endl; return 0; } ================================================ FILE: Algorithm/FibonacciSearch.cpp ================================================ // 斐波那契查找 #include "stdafx.h" #include #include using namespace std; const int max_size=20;//斐波那契数组的长度 /*构造一个斐波那契数组*/ void Fibonacci(int * F) { F[0]=0; F[1]=1; for(int i=2;iF[k]-1)//计算n位于斐波那契数列的位置 ++k; int * temp;//将数组a扩展到F[k]-1的长度 temp=new int [F[k]-1]; memcpy(temp,a,n*sizeof(int)); for(int i=n;itemp[mid]) { low=mid+1; k-=2; } else { if(mid=n则说明是扩展的数值,返回n-1 } } delete [] temp; return -1; } int main() { int a[] = {0,16,24,35,47,59,62,73,88,99}; int key=100; int index=FibonacciSearch(a,sizeof(a)/sizeof(int),key); cout< #include using namespace std; // 堆排序:(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。 void max_heapify(int arr[], int start, int end) { //建立父節點指標和子節點指標 int dad = start; int son = dad * 2 + 1; while (son <= end) { //若子節點指標在範圍內才做比較 if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點大小,選擇最大的 son++; if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數 return; else { //否則交換父子內容再繼續子節點和孫節點比較 swap(arr[dad], arr[son]); dad = son; son = dad * 2 + 1; } } } void heap_sort(int arr[], int len) { //初始化,i從最後一個父節點開始調整 for (int i = len / 2 - 1; i >= 0; i--) max_heapify(arr, i, len - 1); //先將第一個元素和已经排好的元素前一位做交換,再從新調整(刚调整的元素之前的元素),直到排序完畢 for (int i = len - 1; i > 0; i--) { swap(arr[0], arr[i]); max_heapify(arr, 0, i - 1); } } int main() { int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 }; int len = (int) sizeof(arr) / sizeof(*arr); heap_sort(arr, len); for (int i = 0; i < len; i++) cout << arr[i] << ' '; cout << endl; return 0; } ================================================ FILE: Algorithm/InsertSort.h ================================================ /* (有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。 插入排序思路: 1. 从第一个元素开始,该元素可以认为已经被排序 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描 3. 如果该元素(已排序)大于新元素,将该元素移到下一位置 4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 5. 将新元素插入到该位置后 6. 重复步骤2~5 */ // 插入排序 void InsertSort(vector& v) { int len = v.size(); for (int i = 1; i < len; ++i) { int temp = v[i]; for(int j = i - 1; j >= 0; --j) { if(v[j] > temp) { v[j + 1] = v[j]; v[j] = temp; } else break; } } } ================================================ FILE: Algorithm/InsertionSearch.h ================================================ //插值查找 int InsertionSearch(int a[], int value, int low, int high) { int mid = low+(value-a[low])/(a[high]-a[low])*(high-low); if(a[mid]==value) return mid; if(a[mid]>value) return InsertionSearch(a, value, low, mid-1); if(a[mid] void merge_sort(T arr[], int len) { T* a = arr; T* b = new T[len]; for (int seg = 1; seg < len; seg += seg) { for (int start = 0; start < len; start += seg + seg) { int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len); int k = low; int start1 = low, end1 = mid; int start2 = mid, end2 = high; while (start1 < end1 && start2 < end2) b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++]; while (start1 < end1) b[k++] = a[start1++]; while (start2 < end2) b[k++] = a[start2++]; } T* temp = a; a = b; b = temp; } if (a != arr) { for (int i = 0; i < len; i++) b[i] = a[i]; b = a; } delete[] b; } /***************** 递归版 *****************/ template void merge_sort_recursive(T arr[], T reg[], int start, int end) { if (start >= end) return; int len = end - start, mid = (len >> 1) + start; int start1 = start, end1 = mid; int start2 = mid + 1, end2 = end; merge_sort_recursive(arr, reg, start1, end1); merge_sort_recursive(arr, reg, start2, end2); int k = start; while (start1 <= end1 && start2 <= end2) reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++]; while (start1 <= end1) reg[k++] = arr[start1++]; while (start2 <= end2) reg[k++] = arr[start2++]; for (k = start; k <= end; k++) arr[k] = reg[k]; } //整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能 template void merge_sort(T arr[], const int len) { T *reg = new T[len]; merge_sort_recursive(arr, reg, 0, len - 1); delete[] reg; } ================================================ FILE: Algorithm/QuickSort.h ================================================ /* (小数,基准元素,大数)。在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。 快速排序思路: 1. 选取第一个数为基准 2. 将比基准小的数交换到前面,比基准大的数交换到后面 3. 对左右区间重复第二步,直到各区间只有一个数 */ // ---------------------------------------------------- // 快速排序(递归) void QuickSort(vector& v, int low, int high) { if (low >= high) // 结束标志 return; int first = low; // 低位下标 int last = high; // 高位下标 int key = v[first]; // 设第一个为基准 while (first < last) { // 将比第一个小的移到前面 while (first < last && v[last] >= key) last--; if (first < last) v[first++] = v[last]; // 将比第一个大的移到后面 while (first < last && v[first] <= key) first++; if (first < last) v[last--] = v[first]; } // 基准置位 v[first] = key; // 前半递归 QuickSort(v, low, first - 1); // 后半递归 QuickSort(v, first + 1, high); } // ---------------------------------------------------- // 模板实现快速排序(递归) template void quick_sort_recursive(T arr[], int start, int end) { if (start >= end) return; T mid = arr[end]; int left = start, right = end - 1; while (left < right) { while (arr[left] < mid && left < right) left++; while (arr[right] >= mid && left < right) right--; std::swap(arr[left], arr[right]); } if (arr[left] >= arr[end]) std::swap(arr[left], arr[end]); else left++; quick_sort_recursive(arr, start, left - 1); quick_sort_recursive(arr, left + 1, end); } template //整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)、"大於"(>)、"不小於"(>=)的運算子功能 void quick_sort(T arr[], int len) { quick_sort_recursive(arr, 0, len - 1); } // ---------------------------------------------------- // 模板实现快速排序(迭代) struct Range { int start, end; Range(int s = 0, int e = 0) { start = s, end = e; } }; template // 整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)、"大於"(>)、"不小於"(>=)的運算子功能 void quick_sort(T arr[], const int len) { if (len <= 0) return; // 避免len等於負值時宣告堆疊陣列當機 // r[]模擬堆疊,p為數量,r[p++]為push,r[--p]為pop且取得元素 Range r[len]; int p = 0; r[p++] = Range(0, len - 1); while (p) { Range range = r[--p]; if (range.start >= range.end) continue; T mid = arr[range.end]; int left = range.start, right = range.end - 1; while (left < right) { while (arr[left] < mid && left < right) left++; while (arr[right] >= mid && left < right) right--; std::swap(arr[left], arr[right]); } if (arr[left] >= arr[range.end]) std::swap(arr[left], arr[range.end]); else left++; r[p++] = Range(range.start, left - 1); r[p++] = Range(left + 1, range.end); } } ================================================ FILE: Algorithm/RadixSort.h ================================================ // 基数排序:一种多关键字的排序算法,可用桶排序实现。 int maxbit(int data[], int n) //辅助函数,求数据的最大位数 { int maxData = data[0]; ///< 最大数 /// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。 for (int i = 1; i < n; ++i) { if (maxData < data[i]) maxData = data[i]; } int d = 1; int p = 10; while (maxData >= p) { //p *= 10; // Maybe overflow maxData /= 10; ++d; } return d; /* int d = 1; //保存最大的位数 int p = 10; for(int i = 0; i < n; ++i) { while(data[i] >= p) { p *= 10; ++d; } } return d;*/ } void radixsort(int data[], int n) //基数排序 { int d = maxbit(data, n); int *tmp = new int[n]; int *count = new int[10]; //计数器 int i, j, k; int radix = 1; for(i = 1; i <= d; i++) //进行d次排序 { for(j = 0; j < 10; j++) count[j] = 0; //每次分配前清空计数器 for(j = 0; j < n; j++) { k = (data[j] / radix) % 10; //统计每个桶中的记录数 count[k]++; } for(j = 1; j < 10; j++) count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶 for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中 { k = (data[j] / radix) % 10; tmp[count[k] - 1] = data[j]; count[k]--; } for(j = 0; j < n; j++) //将临时数组的内容复制到data中 data[j] = tmp[j]; radix = radix * 10; } delete []tmp; delete []count; } ================================================ FILE: Algorithm/SelectionSort.h ================================================ /* (有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。对数组:比较得多,换得少。 选择排序思路: 1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置 2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾 3. 以此类推,直到所有元素均排序完毕 */ // 选择排序 void SelectionSort(vector& v) { int min, len = v.size(); for (int i = 0; i < len - 1; ++i) { min = i; for (int j = i + 1; j < len; ++j) { if (v[j] < v[min]) { // 标记最小的 min = j; } } if (i != min) // 交换到前面 swap(v[i], v[min]); } } // 模板实现 template void Selection_Sort(std::vector& arr) { int len = arr.size(); for (int i = 0; i < len - 1; i++) { int min = i; for (int j = i + 1; j < len; j++) if (arr[j] < arr[min]) min = j; if(i != min) std::swap(arr[i], arr[min]); } } ================================================ FILE: Algorithm/SequentialSearch.h ================================================ // 顺序查找 int SequentialSearch(vector& v, int k) { for (int i = 0; i < v.size(); ++i) if (v[i] == k) return i; return -1; } /* The following is a Sentinel Search Algorithm which only performs just one test in each loop iteration thereby reducing time complexity */ int BetterSequentialSearch(vector& v, int k) { int last = v[v.size()-1]; v[v.size()-1] = k; int i = 0; while (v[i]!= k) i++; v[v.size()-1] = last; if(i < v.size()-1 || v[v.size()-1] == k) return i; return -1; } ================================================ FILE: Algorithm/ShellSort.h ================================================ // 希尔排序:每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。 template void shell_sort(T array[], int length) { int h = 1; while (h < length / 3) { h = 3 * h + 1; } while (h >= 1) { for (int i = h; i < length; i++) { for (int j = i; j >= h && array[j] < array[j - h]; j -= h) { std::swap(array[j], array[j - h]); } } h = h / 3; } } ================================================ FILE: DataStructure/BinaryTree.cpp ================================================ #include #include #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 #define SUCCESS 1 #define UNSUCCESS 0 #define dataNum 5 int i = 0; int dep = 0; char data[dataNum] = { 'A', 'B', 'C', 'D', 'E' }; typedef int Status; typedef char TElemType; // 二叉树结构 typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; // 初始化一个空树 void InitBiTree(BiTree &T) { T = NULL; } // 构建二叉树 BiTree MakeBiTree(TElemType e, BiTree L, BiTree R) { BiTree t; t = (BiTree)malloc(sizeof(BiTNode)); if (NULL == t) return NULL; t->data = e; t->lchild = L; t->rchild = R; return t; } // 访问结点 Status visit(TElemType e) { printf("%c", e); return OK; } // 对二叉树T求叶子结点数目 int Leaves(BiTree T) { int l = 0, r = 0; if (NULL == T) return 0; if (NULL == T->lchild && NULL == T->rchild) return 1; // 求左子树叶子数目 l = Leaves(T->lchild); // 求右子树叶子数目 r = Leaves(T->rchild); // 组合 return r + l; } // 层次遍历:dep是个全局变量,高度 int depTraverse(BiTree T) { if (NULL == T) return ERROR; dep = (depTraverse(T->lchild) > depTraverse(T->rchild)) ? depTraverse(T->lchild) : depTraverse(T->rchild); return dep + 1; } // 高度遍历:lev是局部变量,层次 void levTraverse(BiTree T, Status(*visit)(TElemType e), int lev) { if (NULL == T) return; visit(T->data); printf("的层次是%d\n", lev); levTraverse(T->lchild, visit, ++lev); levTraverse(T->rchild, visit, lev); } // num是个全局变量 void InOrderTraverse(BiTree T, Status(*visit)(TElemType e), int &num) { if (NULL == T) return; visit(T->data); if (NULL == T->lchild && NULL == T->rchild) { printf("是叶子结点"); num++; } else printf("不是叶子结点"); printf("\n"); InOrderTraverse(T->lchild, visit, num); InOrderTraverse(T->rchild, visit, num); } // 二叉树判空 Status BiTreeEmpty(BiTree T) { if (NULL == T) return TRUE; return FALSE; } // 打断二叉树:置空二叉树的左右子树 Status BreakBiTree(BiTree &T, BiTree &L, BiTree &R) { if (NULL == T) return ERROR; L = T->lchild; R = T->rchild; T->lchild = NULL; T->rchild = NULL; return OK; } // 替换左子树 Status ReplaceLeft(BiTree &T, BiTree <) { BiTree temp; if (NULL == T) return ERROR; temp = T->lchild; T->lchild = LT; LT = temp; return OK; } // 替换右子树 Status ReplaceRight(BiTree &T, BiTree &RT) { BiTree temp; if (NULL == T) return ERROR; temp = T->rchild; T->rchild = RT; RT = temp; return OK; } // 合并二叉树 void UnionBiTree(BiTree &Ttemp) { BiTree L = NULL, R = NULL; L = MakeBiTree(data[i++], NULL, NULL); R = MakeBiTree(data[i++], NULL, NULL); ReplaceLeft(Ttemp, L); ReplaceRight(Ttemp, R); } int main() { BiTree T = NULL, Ttemp = NULL; InitBiTree(T); if (TRUE == BiTreeEmpty(T)) printf("初始化T为空\n"); else printf("初始化T不为空\n"); T = MakeBiTree(data[i++], NULL, NULL); Ttemp = T; UnionBiTree(Ttemp); Ttemp = T->lchild; UnionBiTree(Ttemp); Status(*visit1)(TElemType); visit1 = visit; int num = 0; InOrderTraverse(T, visit1, num); printf("叶子结点是 %d\n", num); printf("叶子结点是 %d\n", Leaves(T)); int lev = 1; levTraverse(T, visit1, lev); printf("高度是 %d\n", depTraverse(T)); getchar(); return 0; } ================================================ FILE: DataStructure/HashTable.cpp ================================================ #include #include #define SUCCESS 1 #define UNSUCCESS 0 #define OVERFLOW -1 #define OK 1 #define ERROR -1 #define MAXNUM 9999 // 用于初始化哈希表的记录 key typedef int Status; typedef int KeyType; // 哈希表中的记录类型 typedef struct { KeyType key; }RcdType; // 哈希表类型 typedef struct { RcdType *rcd; int size; int count; int *tag; }HashTable; // 哈希表每次重建增长后的大小 int hashsize[] = { 11, 31, 61, 127, 251, 503 }; int index = 0; // 初始哈希表 Status InitHashTable(HashTable &H, int size) { int i; H.rcd = (RcdType *)malloc(sizeof(RcdType)*size); H.tag = (int *)malloc(sizeof(int)*size); if (NULL == H.rcd || NULL == H.tag) return OVERFLOW; KeyType maxNum = MAXNUM; for (i = 0; i < size; i++) { H.tag[i] = 0; H.rcd[i].key = maxNum; } H.size = size; H.count = 0; return OK; } // 哈希函数:除留余数法 int Hash(KeyType key, int m) { return (3 * key) % m; } // 处理哈希冲突:线性探测 void collision(int &p, int m) { p = (p + 1) % m; } // 在哈希表中查询 Status SearchHash(HashTable H, KeyType key, int &p, int &c) { p = Hash(key, H.size); int h = p; c = 0; while ((1 == H.tag[p] && H.rcd[p].key != key) || -1 == H.tag[p]) { collision(p, H.size); c++; } if (1 == H.tag[p] && key == H.rcd[p].key) return SUCCESS; else return UNSUCCESS; } //打印哈希表 void printHash(HashTable H) { int i; printf("key : "); for (i = 0; i < H.size; i++) printf("%3d ", H.rcd[i].key); printf("\n"); printf("tag : "); for (i = 0; i < H.size; i++) printf("%3d ", H.tag[i]); printf("\n\n"); } // 函数声明:插入哈希表 Status InsertHash(HashTable &H, KeyType key); // 重建哈希表 Status recreateHash(HashTable &H) { RcdType *orcd; int *otag, osize, i; orcd = H.rcd; otag = H.tag; osize = H.size; InitHashTable(H, hashsize[index++]); //把所有元素,按照新哈希函数放到新表中 for (i = 0; i < osize; i++) { if (1 == otag[i]) { InsertHash(H, orcd[i].key); } } return OK; } // 插入哈希表 Status InsertHash(HashTable &H, KeyType key) { int p, c; if (UNSUCCESS == SearchHash(H, key, p, c)) { //没有相同key if (c*1.0 / H.size < 0.5) { //冲突次数未达到上线 //插入代码 H.rcd[p].key = key; H.tag[p] = 1; H.count++; return SUCCESS; } else recreateHash(H); //重构哈希表 } return UNSUCCESS; } // 删除哈希表 Status DeleteHash(HashTable &H, KeyType key) { int p, c; if (SUCCESS == SearchHash(H, key, p, c)) { //删除代码 H.tag[p] = -1; H.count--; return SUCCESS; } else return UNSUCCESS; } int main() { printf("-----哈希表-----\n"); HashTable H; int i; int size = 11; KeyType array[8] = { 22, 41, 53, 46, 30, 13, 12, 67 }; KeyType key; //初始化哈希表 printf("初始化哈希表\n"); if (SUCCESS == InitHashTable(H, hashsize[index++])) printf("初始化成功\n"); //插入哈希表 printf("插入哈希表\n"); for (i = 0; i <= 7; i++) { key = array[i]; InsertHash(H, key); printHash(H); } //删除哈希表 printf("删除哈希表中key为12的元素\n"); int p, c; if (SUCCESS == DeleteHash(H, 12)) { printf("删除成功,此时哈希表为:\n"); printHash(H); } //查询哈希表 printf("查询哈希表中key为67的元素\n"); if (SUCCESS == SearchHash(H, 67, p, c)) printf("查询成功\n"); //再次插入,测试哈希表的重建 printf("再次插入,测试哈希表的重建:\n"); KeyType array1[8] = { 27, 47, 57, 47, 37, 17, 93, 67 }; for (i = 0; i <= 7; i++) { key = array1[i]; InsertHash(H, key); printHash(H); } getchar(); return 0; } ================================================ FILE: DataStructure/LinkList.cpp ================================================ /** * @author huihut * @E-mail:huihut@outlook.com * @version 创建时间:2016年9月18日 * 说明:本程序实现了一个单链表。 */ #include "stdio.h" #include "stdlib.h" #include "malloc.h" //5个常量定义 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 //类型定义 typedef int Status; typedef int ElemType; //测试程序长度定义 #define LONGTH 5 //链表的类型 typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; //创建包含n个元素的链表L,元素值存储在data数组中 Status create(LinkList &L, ElemType *data, int n) { LNode *p, *q; int i; if (n < 0) return ERROR; L = NULL; p = L; for (i = 0; i < n; i++) { q = (LNode *)malloc(sizeof(LNode)); if (NULL == q) return OVERFLOW; q->data = data[i]; q->next = NULL; if (NULL == p) L = q; else p->next = q; p = q; } return OK; } //e从链表末尾入链表 Status EnQueue_LQ(LinkList &L, ElemType &e) { LinkList p, q; if (NULL == (q = (LNode *)malloc(sizeof(LNode)))) return OVERFLOW; q->data = e; q->next = NULL; if (NULL == L) L = q; else { p = L; while (p->next != NULL) { p = p->next; } p->next = q; } return OK; } //从链表头节点出链表到e Status DeQueue_LQ(LinkList &L, ElemType &e) { if (NULL == L) return ERROR; LinkList p; p = L; e = p->data; L = L->next; free(p); return OK; } //遍历调用 Status visit(ElemType e) { printf("%d\t", e); return OK; } //遍历单链表 void ListTraverse_L(LinkList L, Status(*visit)(ElemType e)) { if (NULL == L) return; for (LinkList p = L; NULL != p; p = p->next) { visit(p->data); } } int main() { int i; ElemType e, data[LONGTH] = { 1, 2, 3, 4, 5 }; LinkList L; //显示测试值 printf("---【单链表】---\n"); printf("待测试元素为:\n"); for (i = 0; i < LONGTH; i++) printf("%d\t", data[i]); printf("\n"); //创建链表L printf("创建链表L\n"); if (ERROR == create(L, data, LONGTH)) { printf("创建链表L失败\n"); return -1; } printf("成功创建包含%d个元素的链表L\n元素值存储在data数组中\n", LONGTH); //遍历单链表 printf("此时链表中元素为:\n"); ListTraverse_L(L, visit); //从链表头节点出链表到e printf("\n出链表到e\n"); DeQueue_LQ(L, e); printf("出链表的元素为:%d\n", e); printf("此时链表中元素为:\n"); //遍历单链表 ListTraverse_L(L, visit); //e从链表末尾入链表 printf("\ne入链表\n"); EnQueue_LQ(L, e); printf("入链表的元素为:%d\n", e); printf("此时链表中元素为:\n"); //遍历单链表 ListTraverse_L(L, visit); printf("\n"); getchar(); return 0; } ================================================ FILE: DataStructure/LinkList_with_head.cpp ================================================ /** * @author huihut * @E-mail:huihut@outlook.com * @version 创建时间:2016年9月23日 * 说明:本程序实现了一个具有头结点的单链表。 */ #include "stdio.h" #include "stdlib.h" #include "malloc.h" //5个常量定义 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 //类型定义 typedef int Status; typedef int ElemType; //测试程序长度定义 #define LONGTH 5 //链表的类型 typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; //创建包含n个元素的链表L,元素值存储在data数组中 Status create(LinkList &L, ElemType *data, int n) { LNode *p, *q; int i; if (n < 0) return ERROR; p = L = NULL; q = (LNode *)malloc(sizeof(LNode)); if (NULL == q) return OVERFLOW; q->next = NULL; p = L = q; for (i = 0; i < n; i++) { q = (LNode *)malloc(sizeof(LNode)); if (NULL == q) return OVERFLOW; q->data = data[i]; q->next = NULL; p->next = q; p = q; } return OK; } //e从链表末尾入链表 Status EnQueue_LQ(LinkList &L, ElemType &e) { LinkList p, q; if (NULL == (q = (LNode *)malloc(sizeof(LNode)))) return OVERFLOW; q->data = e; q->next = NULL; if (NULL == L) { L = (LNode *)malloc(sizeof(LNode)); if (NULL == L) return OVERFLOW; L->next = q; } else if (NULL == L->next) { L->next = q; } else { p = L; while (p->next != NULL) { p = p->next; } p->next = q; } return OK; } //从链表头节点出链表到e Status DeQueue_LQ(LinkList &L, ElemType &e) { if (NULL == L || NULL == L->next) return ERROR; LinkList p; p = L->next; e = p->data; L->next = p->next; free(p); return OK; } //遍历调用 Status visit(ElemType e) { printf("%d\t", e); return OK; } //遍历单链表 void ListTraverse_L(LinkList L, Status(*visit)(ElemType e)) { if (NULL == L || NULL == L->next) return; for (LinkList p = L->next; NULL != p; p = p->next) { visit(p->data); } } int main() { int i; ElemType e, data[LONGTH] = { 1, 2, 3, 4, 5 }; LinkList L; //显示测试值 printf("---【有头结点的单链表】---\n"); printf("待测试元素为:\n"); for (i = 0; i < LONGTH; i++) printf("%d\t", data[i]); printf("\n"); //创建链表L printf("创建链表L\n"); if (ERROR == create(L, data, LONGTH)) { printf("创建链表L失败\n"); return -1; } printf("成功创建包含1个头结点、%d个元素的链表L\n元素值存储在data数组中\n", LONGTH); //遍历单链表 printf("此时链表中元素为:\n"); ListTraverse_L(L, visit); //从链表头节点出链表到e printf("\n出链表到e\n"); DeQueue_LQ(L, e); printf("出链表的元素为:%d\n", e); printf("此时链表中元素为:\n"); //遍历单链表 ListTraverse_L(L, visit); //e从链表末尾入链表 printf("\ne入链表\n"); EnQueue_LQ(L, e); printf("入链表的元素为:%d\n", e); printf("此时链表中元素为:\n"); //遍历单链表 ListTraverse_L(L, visit); printf("\n"); getchar(); return 0; } ================================================ FILE: DataStructure/RedBlackTree.cpp ================================================ #define BLACK 1 #define RED 0 #include using namespace std; class bst { private: struct Node { int value; bool color; Node *leftTree, *rightTree, *parent; Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { } Node* grandparent() { if (parent == NULL) { return NULL; } return parent->parent; } Node* uncle() { if (grandparent() == NULL) { return NULL; } if (parent == grandparent()->rightTree) return grandparent()->leftTree; else return grandparent()->rightTree; } Node* sibling() { if (parent->leftTree == this) return parent->rightTree; else return parent->leftTree; } }; void rotate_right(Node *p) { Node *gp = p->grandparent(); Node *fa = p->parent; Node *y = p->rightTree; fa->leftTree = y; if (y != NIL) y->parent = fa; p->rightTree = fa; fa->parent = p; if (root == fa) root = p; p->parent = gp; if (gp != NULL) { if (gp->leftTree == fa) gp->leftTree = p; else gp->rightTree = p; } } void rotate_left(Node *p) { if (p->parent == NULL) { root = p; return; } Node *gp = p->grandparent(); Node *fa = p->parent; Node *y = p->leftTree; fa->rightTree = y; if (y != NIL) y->parent = fa; p->leftTree = fa; fa->parent = p; if (root == fa) root = p; p->parent = gp; if (gp != NULL) { if (gp->leftTree == fa) gp->leftTree = p; else gp->rightTree = p; } } void inorder(Node *p) { if (p == NIL) return; if (p->leftTree) inorder(p->leftTree); cout << p->value << " "; if (p->rightTree) inorder(p->rightTree); } string outputColor(bool color) { return color ? "BLACK" : "RED"; } Node* getSmallestChild(Node *p) { if (p->leftTree == NIL) return p; return getSmallestChild(p->leftTree); } bool delete_child(Node *p, int data) { if (p->value > data) { if (p->leftTree == NIL) { return false; } return delete_child(p->leftTree, data); } else if (p->value < data) { if (p->rightTree == NIL) { return false; } return delete_child(p->rightTree, data); } else if (p->value == data) { if (p->rightTree == NIL) { delete_one_child(p); return true; } Node *smallest = getSmallestChild(p->rightTree); swap(p->value, smallest->value); delete_one_child(smallest); return true; } else { return false; } } void delete_one_child(Node *p) { Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree; if (p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL) { p = NULL; root = p; return; } if (p->parent == NULL) { delete p; child->parent = NULL; root = child; root->color = BLACK; return; } if (p->parent->leftTree == p) { p->parent->leftTree = child; } else { p->parent->rightTree = child; } child->parent = p->parent; if (p->color == BLACK) { if (child->color == RED) { child->color = BLACK; } else delete_case(child); } delete p; } void delete_case(Node *p) { if (p->parent == NULL) { p->color = BLACK; return; } if (p->sibling()->color == RED) { p->parent->color = RED; p->sibling()->color = BLACK; if (p == p->parent->leftTree) rotate_left(p->sibling()); else rotate_right(p->sibling()); } if (p->parent->color == BLACK && p->sibling()->color == BLACK && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) { p->sibling()->color = RED; delete_case(p->parent); } else if (p->parent->color == RED && p->sibling()->color == BLACK && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) { p->sibling()->color = RED; p->parent->color = BLACK; } else { if (p->sibling()->color == BLACK) { if (p == p->parent->leftTree && p->sibling()->leftTree->color == RED && p->sibling()->rightTree->color == BLACK) { p->sibling()->color = RED; p->sibling()->leftTree->color = BLACK; rotate_right(p->sibling()->leftTree); } else if (p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == RED) { p->sibling()->color = RED; p->sibling()->rightTree->color = BLACK; rotate_left(p->sibling()->rightTree); } } p->sibling()->color = p->parent->color; p->parent->color = BLACK; if (p == p->parent->leftTree) { p->sibling()->rightTree->color = BLACK; rotate_left(p->sibling()); } else { p->sibling()->leftTree->color = BLACK; rotate_right(p->sibling()); } } } void insert(Node *p, int data) { if (p->value >= data) { if (p->leftTree != NIL) insert(p->leftTree, data); else { Node *tmp = new Node(); tmp->value = data; tmp->leftTree = tmp->rightTree = NIL; tmp->parent = p; p->leftTree = tmp; insert_case(tmp); } } else { if (p->rightTree != NIL) insert(p->rightTree, data); else { Node *tmp = new Node(); tmp->value = data; tmp->leftTree = tmp->rightTree = NIL; tmp->parent = p; p->rightTree = tmp; insert_case(tmp); } } } void insert_case(Node *p) { if (p->parent == NULL) { root = p; p->color = BLACK; return; } if (p->parent->color == RED) { if (p->uncle()->color == RED) { p->parent->color = p->uncle()->color = BLACK; p->grandparent()->color = RED; insert_case(p->grandparent()); } else { if (p->parent->rightTree == p && p->grandparent()->leftTree == p->parent) { rotate_left(p); rotate_right(p); p->color = BLACK; p->leftTree->color = p->rightTree->color = RED; } else if (p->parent->leftTree == p && p->grandparent()->rightTree == p->parent) { rotate_right(p); rotate_left(p); p->color = BLACK; p->leftTree->color = p->rightTree->color = RED; } else if (p->parent->leftTree == p && p->grandparent()->leftTree == p->parent) { p->parent->color = BLACK; p->grandparent()->color = RED; rotate_right(p->parent); } else if (p->parent->rightTree == p && p->grandparent()->rightTree == p->parent) { p->parent->color = BLACK; p->grandparent()->color = RED; rotate_left(p->parent); } } } } void DeleteTree(Node *p) { if (!p || p == NIL) { return; } DeleteTree(p->leftTree); DeleteTree(p->rightTree); delete p; } public: bst() { NIL = new Node(); NIL->color = BLACK; root = NULL; } ~bst() { if (root) DeleteTree(root); delete NIL; } void inorder() { if (root == NULL) return; inorder(root); cout << endl; } void insert(int x) { if (root == NULL) { root = new Node(); root->color = BLACK; root->leftTree = root->rightTree = NIL; root->value = x; } else { insert(root, x); } } bool delete_value(int data) { return delete_child(root, data); } private: Node *root, *NIL; }; int main() { cout << "---【红黑树】---" << endl; // 创建红黑树 bst tree; // 插入元素 tree.insert(2); tree.insert(9); tree.insert(-10); tree.insert(0); tree.insert(33); tree.insert(-19); // 顺序打印红黑树 cout << "插入元素后的红黑树:" << endl; tree.inorder(); // 删除元素 tree.delete_value(2); // 顺序打印红黑树 cout << "删除元素 2 后的红黑树:" << endl; tree.inorder(); // 析构 tree.~bst(); getchar(); return 0; } ================================================ FILE: DataStructure/SqList.cpp ================================================ /** * @author huihut * @E-mail:huihut@outlook.com * @version 创建时间:2016年9月9日 * 说明:本程序实现了一个顺序表。 */ #include "stdio.h" #include "stdlib.h" #include "malloc.h" //5个常量定义 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 //测试程序长度定义 #define LONGTH 5 //类型定义 typedef int Status; typedef int ElemType; //顺序栈的类型 typedef struct { ElemType *elem; int length; int size; int increment; } SqList; //初始化顺序表L Status InitList_Sq(SqList &L, int size, int inc) { L.elem = (ElemType *)malloc(size * sizeof(ElemType)); if (NULL == L.elem) return OVERFLOW; L.length = 0; L.size = size; L.increment = inc; return OK; } //销毁顺序表L Status DestroyList_Sq(SqList &L) { free(L.elem); L.elem = NULL; return OK; } //将顺序表L清空 Status ClearList_Sq(SqList &L) { if (0 != L.length) L.length = 0; return OK; } //若顺序表L为空表,则返回TRUE,否则FALSE Status ListEmpty_Sq(SqList L) { if (0 == L.length) return TRUE; return FALSE; } //返回顺序表L中元素个数 int ListLength_Sq(SqList L) { return L.length; } // 用e返回顺序表L中第i个元素的值 Status GetElem_Sq(SqList L, int i, ElemType &e) { e = L.elem[--i]; return OK; } // 在顺序表L顺序查找元素e,成功时返回该元素在表中第一次出现的位置,否则返回 - 1 int Search_Sq(SqList L, ElemType e) { int i = 0; while (i < L.length && L.elem[i] != e) i++; if (i < L.length) return i; else return -1; } //遍历调用 Status visit(ElemType e) { printf("%d\t", e); return OK; } //遍历顺序表L,依次对每个元素调用函数visit() Status ListTraverse_Sq(SqList L, Status(*visit)(ElemType e)) { if (0 == L.length) return ERROR; for (int i = 0; i < L.length; i++) { visit(L.elem[i]); } return OK; } //将顺序表L中第i个元素赋值为e Status PutElem_Sq(SqList &L, int i, ElemType e) { if (i > L.length) return ERROR; e = L.elem[--i]; return OK; } //在顺序表L表尾添加元素e Status Append_Sq(SqList &L, ElemType e) { if (L.length >= L.size) return ERROR; L.elem[L.length] = e; L.length++; return OK; } //删除顺序表L的表尾元素,并用参数e返回其值 Status DeleteLast_Sq(SqList &L, ElemType &e) { if (0 == L.length) return ERROR; e = L.elem[L.length - 1]; L.length--; return OK; } int main() { //定义表L SqList L; //定义测量值 int size, increment, i; //初始化测试值 size = LONGTH; increment = LONGTH; ElemType e, eArray[LONGTH] = { 1, 2, 3, 4, 5 }; //显示测试值 printf("---【顺序栈】---\n"); printf("表L的size为:%d\n表L的increment为:%d\n", size, increment); printf("待测试元素为:\n"); for (i = 0; i < LONGTH; i++) { printf("%d\t", eArray[i]); } printf("\n"); //初始化顺序表 if (!InitList_Sq(L, size, increment)) { printf("初始化顺序表失败\n"); exit(0); } printf("已初始化顺序表\n"); //判空 if (TRUE == ListEmpty_Sq(L)) printf("此表为空表\n"); else printf("此表不是空表\n"); //入表 printf("将待测元素入表:\n"); for (i = 0; i < LONGTH; i++) { if (ERROR == Append_Sq(L, eArray[i])) printf("入表失败\n");; } printf("入表成功\n"); //遍历顺序表L printf("此时表内元素为:\n"); ListTraverse_Sq(L, visit); //出表 printf("\n将表尾元素入表到e:\n"); if (ERROR == DeleteLast_Sq(L, e)) printf("出表失败\n"); printf("出表成功\n出表元素为%d\n", e); //遍历顺序表L printf("此时表内元素为:\n"); ListTraverse_Sq(L, visit); //销毁顺序表 printf("\n销毁顺序表\n"); if (OK == DestroyList_Sq(L)) printf("销毁成功\n"); else printf("销毁失败\n"); getchar(); return 0; } ================================================ FILE: DataStructure/SqStack.cpp ================================================ /** * @author huihut * @E-mail:huihut@outlook.com * @version 创建时间:2016年9月9日 * 说明:本程序实现了一个顺序栈。 * 功能:有初始化、销毁、判断空、清空、入栈、出栈、取元素的操作。 */ #include "stdio.h" #include "stdlib.h" #include "malloc.h" //5个常量定义 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 //测试程序长度定义 #define LONGTH 5 //类型定义 typedef int Status; typedef int ElemType; //顺序栈的类型 typedef struct { ElemType *elem; int top; int size; int increment; } SqSrack; //初始化顺序栈 Status InitStack_Sq(SqSrack &S, int size, int inc) { S.elem = (ElemType *)malloc(size * sizeof(ElemType)); if (NULL == S.elem) return OVERFLOW; S.top = 0; S.size = size; S.increment = inc; return OK; } //销毁顺序栈 Status DestroyStack_Sq(SqSrack &S) { free(S.elem); S.elem = NULL; return OK; } //判断S是否空,若空则返回TRUE,否则返回FALSE Status StackEmpty_Sq(SqSrack S) { if (0 == S.top) return TRUE; return FALSE; } //清空栈S void ClearStack_Sq(SqSrack &S) { if (0 == S.top) return; S.size = 0; S.top = 0; } //元素e压入栈S Status Push_Sq(SqSrack &S, ElemType e) { ElemType *newbase; if (S.top >= S.size) { newbase = (ElemType *)realloc(S.elem, (S.size + S.increment) * sizeof(ElemType)); if (NULL == newbase) return OVERFLOW; S.elem = newbase; S.size += S.increment; } S.elem[S.top++] = e; return OK; } //取栈S的栈顶元素,并用e返回 Status GetTop_Sq(SqSrack S, ElemType &e) { if (0 == S.top) return ERROR; e = S.elem[S.top - 1]; return e; } //栈S的栈顶元素出栈,并用e返回 Status Pop_Sq(SqSrack &S, ElemType &e) { if (0 == S.top) return ERROR; e = S.elem[S.top - 1]; S.top--; return e; } int main() { //定义栈S SqSrack S; //定义测量值 int size, increment, i; //初始化测试值 size = LONGTH; increment = LONGTH; ElemType e, eArray[LONGTH] = { 1, 2, 3, 4, 5 }; //显示测试值 printf("---【顺序栈】---\n"); printf("栈S的size为:%d\n栈S的increment为:%d\n", size, increment); printf("待测试元素为:\n"); for (i = 0; i < LONGTH; i++) { printf("%d\t", eArray[i]); } printf("\n"); //初始化顺序栈 if (!InitStack_Sq(S, size, increment)) { printf("初始化顺序栈失败\n"); exit(0); } printf("已初始化顺序栈\n"); //入栈 for (i = 0; i < S.size; i++) { if (!Push_Sq(S, eArray[i])) { printf("%d入栈失败\n", eArray[i]); exit(0); } } printf("已入栈\n"); //判断非空 if (StackEmpty_Sq(S)) printf("S栈为空\n"); else printf("S栈非空\n"); //取栈S的栈顶元素 printf("栈S的栈顶元素为:\n"); printf("%d\n", GetTop_Sq(S, e)); //栈S元素出栈 printf("栈S元素出栈为:\n"); for (i = 0, e = 0; i < S.size; i++) { printf("%d\t", Pop_Sq(S, e)); } printf("\n"); //清空栈S ClearStack_Sq(S); printf("已清空栈S\n"); getchar(); return 0; } ================================================ FILE: DesignPattern/AbstractFactoryPattern/Factory.cpp ================================================ // // Created by xiemenghui on 2018/7/20. // #include "Factory.h" #include "concrete_factory.h" Factory* Factory::CreateFactory(FACTORY_TYPE factory) { Factory *pFactory = nullptr; switch (factory) { case FACTORY_TYPE::BENZ_FACTORY: // Benz factory pFactory = new BenzFactory(); break; case FACTORY_TYPE::BMW_FACTORY: // BMW factory pFactory = new BmwFactory(); break; case FACTORY_TYPE::AUDI_FACTORY: // Audi factory pFactory = new AudiFactory(); break; default: break; } return pFactory; } ================================================ FILE: DesignPattern/AbstractFactoryPattern/Factory.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_FACTORY_H #define DESIGNPATTERN_FACTORY_H #include "product.h" // Abstract factory pattern class Factory { public: enum FACTORY_TYPE { BENZ_FACTORY, // Benz factory BMW_FACTORY, // BMW factory AUDI_FACTORY // Audi factory }; virtual ICar* CreateCar() = 0; // Production car virtual IBike* CreateBike() = 0; // Production bicycle static Factory * CreateFactory(FACTORY_TYPE factory); // Create factory }; #endif //DESIGNPATTERN_FACTORY_H ================================================ FILE: DesignPattern/AbstractFactoryPattern/FactoryMain.cpp ================================================ // // Created by xiemenghui on 2018/7/20. // #include "Factory.h" #include "product.h" #include "FactoryMain.h" #include using namespace std; void FactoryMain() { // Benz Factory * pFactory = Factory::CreateFactory(Factory::FACTORY_TYPE::BENZ_FACTORY); ICar * pCar = pFactory->CreateCar(); IBike * pBike = pFactory->CreateBike(); cout << "Benz factory - Car: " << pCar->Name() << endl; cout << "Benz factory - Bike: " << pBike->Name() << endl; SAFE_DELETE(pCar); SAFE_DELETE(pBike); SAFE_DELETE(pFactory); // BMW pFactory = Factory::CreateFactory(Factory::FACTORY_TYPE::BMW_FACTORY); pCar = pFactory->CreateCar(); pBike = pFactory->CreateBike(); cout << "Bmw factory - Car: " << pCar->Name() << endl; cout << "Bmw factory - Bike: " << pBike->Name() << endl; SAFE_DELETE(pCar); SAFE_DELETE(pBike); SAFE_DELETE(pFactory); // Audi pFactory = Factory::CreateFactory(Factory::FACTORY_TYPE::AUDI_FACTORY); pCar = pFactory->CreateCar(); pBike = pFactory->CreateBike(); cout << "Audi factory - Car: " << pCar->Name() << endl; cout << "Audi factory - Bike: " << pBike->Name() << endl; SAFE_DELETE(pCar); SAFE_DELETE(pBike); SAFE_DELETE(pFactory); } ================================================ FILE: DesignPattern/AbstractFactoryPattern/FactoryMain.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_FACTORYMAIN_H #define DESIGNPATTERN_FACTORYMAIN_H #ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p) {delete(p); (p)=nullptr;}} #endif void FactoryMain(); #endif //DESIGNPATTERN_FACTORYMAIN_H ================================================ FILE: DesignPattern/AbstractFactoryPattern/concrete_factory.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_CONCRETE_FACTORY_H #define DESIGNPATTERN_CONCRETE_FACTORY_H #include "Factory.h" #include "concrete_product.h" // Benz factory class BenzFactory : public Factory { public: ICar* CreateCar() { return new BenzCar(); } IBike* CreateBike() { return new BenzBike(); } }; // BMW factory class BmwFactory : public Factory { public: ICar* CreateCar() { return new BmwCar(); } IBike* CreateBike() { return new BmwBike(); } }; // Audi factory class AudiFactory : public Factory { public: ICar* CreateCar() { return new AudiCar(); } IBike* CreateBike() { return new AudiBike(); } }; #endif //DESIGNPATTERN_CONCRETE_FACTORY_H ================================================ FILE: DesignPattern/AbstractFactoryPattern/concrete_product.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_CONCRETE_PRODUCT_H #define DESIGNPATTERN_CONCRETE_PRODUCT_H #include "product.h" /********** Car **********/ // Benz class BenzCar : public ICar { public: string Name() { return "Benz Car"; } }; // BMW class BmwCar : public ICar { public: string Name() { return "Bmw Car"; } }; // Audi class AudiCar : public ICar { public: string Name() { return "Audi Car"; } }; /********** Bicycle **********/ // Benz class BenzBike : public IBike { public: string Name() { return "Benz Bike"; } }; // BMW class BmwBike : public IBike { public: string Name() { return "Bmw Bike"; } }; // Audi class AudiBike : public IBike { public: string Name() { return "Audi Bike"; } }; #endif //DESIGNPATTERN_CONCRETE_PRODUCT_H ================================================ FILE: DesignPattern/AbstractFactoryPattern/product.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_PRODUCT_H #define DESIGNPATTERN_PRODUCT_H #include using std::string; // Car Interface class ICar { public: virtual string Name() = 0; }; // Bike Interface class IBike { public: virtual string Name() = 0; }; #endif //DESIGNPATTERN_PRODUCT_H ================================================ FILE: DesignPattern/AdapterPattern/AdapterMain.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_ADAPTERMAIN_H #define DESIGNPATTERN_ADAPTERMAIN_H #include "adapter.h" void AdapterMain() { // Create a power adapter IRussiaSocket * pAdapter = new PowerAdapter(); // Recharge pAdapter->Charge(); SAFE_DELETE(pAdapter); } #endif //DESIGNPATTERN_ADAPTERMAIN_H ================================================ FILE: DesignPattern/AdapterPattern/adaptee.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_ADAPTEE_H #define DESIGNPATTERN_ADAPTEE_H #include // Built-in charger (two-leg flat type) class OwnCharger { public: void ChargeWithFeetFlat() { std::cout << "OwnCharger::ChargeWithFeetFlat\n"; } }; #endif //DESIGNPATTERN_ADAPTEE_H ================================================ FILE: DesignPattern/AdapterPattern/adapter.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_ADAPTER_H #define DESIGNPATTERN_ADAPTER_H #include "target.h" #include "adaptee.h" #ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} } #endif // Power Adapter class PowerAdapter : public IRussiaSocket { public: PowerAdapter() : m_pCharger(new OwnCharger()){} ~PowerAdapter() { SAFE_DELETE(m_pCharger); } void Charge() { // Use the built-in charger (two-pin flat) to charge m_pCharger->ChargeWithFeetFlat(); } private: // Hold the interface object that needs to be adapted (the built-in charger) OwnCharger* m_pCharger; }; #endif //DESIGNPATTERN_ADAPTER_H ================================================ FILE: DesignPattern/AdapterPattern/target.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_TARGET_H #define DESIGNPATTERN_TARGET_H // Sockets provided by Russia class IRussiaSocket { public: // Use both feet to charge in a round shape (not implemented yet) virtual void Charge() = 0; }; #endif //DESIGNPATTERN_TARGET_H ================================================ FILE: DesignPattern/BridgePattern/BridgeMain.cpp ================================================ // // Created by xiemenghui on 2018/7/21. // #include "BridgeMain.h" void BridgeMain() { // Create electrical appliances (electric lights, electric fans) IElectricalEquipment * light = new Light(); IElectricalEquipment * fan = new Fan(); // Create switch (pull chain switch, two-position switch) // Associating a pull chain switch with a light and a two-position switch with a fan ISwitch * pullChain = new PullChainSwitch(light); ISwitch * twoPosition = new TwoPositionSwitch(fan); // Lights on, lights off pullChain->On(); pullChain->Off(); // Turn on the fan, turn off the fan twoPosition->On(); twoPosition->Off(); SAFE_DELETE(twoPosition); SAFE_DELETE(pullChain); SAFE_DELETE(fan); SAFE_DELETE(light); } ================================================ FILE: DesignPattern/BridgePattern/BridgeMain.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_BRIDGEMAIN_H #define DESIGNPATTERN_BRIDGEMAIN_H #include "refined_abstraction.h" #include "concrete_implementor.h" #ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p){delete(p); (p)=nullptr;} } #endif void BridgeMain(); #endif //DESIGNPATTERN_BRIDGEMAIN_H ================================================ FILE: DesignPattern/BridgePattern/abstraction.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_ABSTRACTION_H #define DESIGNPATTERN_ABSTRACTION_H #include "implementor.h" // Switch class ISwitch { public: ISwitch(IElectricalEquipment *ee){ m_pEe = ee; } virtual ~ISwitch(){} virtual void On() = 0; // Turn on the appliance virtual void Off() = 0; // Turn off the appliance protected: IElectricalEquipment * m_pEe; }; #endif //DESIGNPATTERN_ABSTRACTION_H ================================================ FILE: DesignPattern/BridgePattern/concrete_implementor.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_CONCRETE_IMPLEMENTOR_H #define DESIGNPATTERN_CONCRETE_IMPLEMENTOR_H #include "implementor.h" #include // Electric lights class Light : public IElectricalEquipment { public: // Turn on the lights virtual void PowerOn() override { std::cout << "Light is on." << std::endl; } // Turn off the lights virtual void PowerOff() override { std::cout << "Light is off." << std::endl; } }; // Electric Fan class Fan : public IElectricalEquipment { public: // Turn on the fan virtual void PowerOn() override { std::cout << "Fan is on." << std::endl; } // Turn off the fan virtual void PowerOff() override { std::cout << "Fan is off." << std::endl; } }; #endif //DESIGNPATTERN_CONCRETE_IMPLEMENTOR_H ================================================ FILE: DesignPattern/BridgePattern/implementor.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_IMPLEMENTOR_H #define DESIGNPATTERN_IMPLEMENTOR_H // Electric equipment class IElectricalEquipment { public: virtual ~IElectricalEquipment(){} virtual void PowerOn() = 0; virtual void PowerOff() = 0; }; #endif //DESIGNPATTERN_IMPLEMENTOR_H ================================================ FILE: DesignPattern/BridgePattern/refined_abstraction.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_REFINED_ABSTRACTION_H #define DESIGNPATTERN_REFINED_ABSTRACTION_H #include "abstraction.h" #include // Zipper switch class PullChainSwitch : public ISwitch { public: PullChainSwitch(IElectricalEquipment *ee) : ISwitch(ee) {} // Turn on the equipment with a zipper switch virtual void On() override { std::cout << "Turn on the equipment with a zipper switch." << std::endl; m_pEe->PowerOn(); } // Turn off the equipment with a zipper switch virtual void Off() override { std::cout << "Turn off the equipment with a zipper switch." << std::endl; m_pEe->PowerOff(); } }; // Two-position switch class TwoPositionSwitch : public ISwitch { public: TwoPositionSwitch(IElectricalEquipment *ee) : ISwitch(ee) {} // Turn on the equipment with a two-position switch virtual void On() override { std::cout << "Turn on the equipment with a two-position switch." << std::endl; m_pEe->PowerOn(); } // Turn off the equipment with a two-position switch virtual void Off() override { std::cout << "Turn off the equipment with a two-position switch." << std::endl; m_pEe->PowerOff(); } }; #endif //DESIGNPATTERN_REFINED_ABSTRACTION_H ================================================ FILE: DesignPattern/ObserverPattern/ObserverMain.cpp ================================================ // // Created by xiemenghui on 2018/7/21. // #include "ObserverMain.h" void ObserverMain() { // Create Subject ConcreteSubject * pSubject = new ConcreteSubject(); // Create Observer IObserver * pObserver1 = new ConcreteObserver("Jack Ma"); IObserver * pObserver2 = new ConcreteObserver("Pony"); // Attach Observers pSubject->Attach(pObserver1); pSubject->Attach(pObserver2); // Change the price and notify the observer pSubject->SetPrice(12.5); pSubject->Notify(); // Detach Observers pSubject->Detach(pObserver2); // Change the state again and notify the observer pSubject->SetPrice(15.0); pSubject->Notify(); SAFE_DELETE(pObserver1); SAFE_DELETE(pObserver2); SAFE_DELETE(pSubject); } ================================================ FILE: DesignPattern/ObserverPattern/ObserverMain.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_OBSERVERMAIN_H #define DESIGNPATTERN_OBSERVERMAIN_H #include "concrete_subject.h" #include "concrete_observer.h" #ifndef SAFE_DELETE #define SAFE_DELETE(p) { if(p){delete(p); (p)=nullptr;} } #endif void ObserverMain(); #endif //DESIGNPATTERN_OBSERVERMAIN_H ================================================ FILE: DesignPattern/ObserverPattern/concrete_observer.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_CONCRETE_OBSERVER_H #define DESIGNPATTERN_CONCRETE_OBSERVER_H #include "observer.h" #include #include class ConcreteObserver : public IObserver { public: ConcreteObserver(std::string name) { m_strName = name; } void Update(float price) { std::cout << m_strName << " - price" << price << "\n"; } private: std::string m_strName; // name }; #endif //DESIGNPATTERN_CONCRETE_OBSERVER_H ================================================ FILE: DesignPattern/ObserverPattern/concrete_subject.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_CONCRETE_SUBJECT_H #define DESIGNPATTERN_CONCRETE_SUBJECT_H #include "subject.h" #include "observer.h" #include #include // Specific Subject class ConcreteSubject : public ISubject { public: ConcreteSubject(){ m_fPrice = 10.0; } void SetPrice(float price) { m_fPrice = price; } void Attach(IObserver * observer) { m_observers.push_back(observer); } void Detach(IObserver * observer) { m_observers.remove(observer); } // Notify all observers void Notify() { std::list::iterator it = m_observers.begin(); while (it != m_observers.end()) { (*it)->Update(m_fPrice); ++it; } } private: std::list m_observers; // Observer list float m_fPrice; // Price }; #endif //DESIGNPATTERN_CONCRETE_SUBJECT_H ================================================ FILE: DesignPattern/ObserverPattern/observer.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_OBSERVER_H #define DESIGNPATTERN_OBSERVER_H // Abstract observer class IObserver { public: virtual void Update(float price) = 0; // Update price }; #endif //DESIGNPATTERN_OBSERVER_H ================================================ FILE: DesignPattern/ObserverPattern/subject.h ================================================ // // Created by xiemenghui on 2018/7/21. // #ifndef DESIGNPATTERN_SUBJECT_H #define DESIGNPATTERN_SUBJECT_H class IObserver; class ISubject { public: virtual void Attach(IObserver *) = 0; // Attach observer virtual void Detach(IObserver *) = 0; // Detach observer virtual void Notify() = 0; // Notify observer }; #endif //DESIGNPATTERN_SUBJECT_H ================================================ FILE: DesignPattern/README.md ================================================ # 设计模式 > 各大设计模式例子参考:[CSDN专栏 . C++ 设计模式](https://blog.csdn.net/liang19890820/article/details/66974516) 系列博文 此文件夹为一个 CLion 工程,由 CMake 构建,各个文件夹为各个设计模式的具体实现。文件中可能会有中文乱码问题,请以 `GB2312`(中文) 编码打开。 * [单例模式例子](SingletonPattern) * [抽象工厂模式例子](AbstractFactoryPattern) * [适配器模式例子](AdapterPattern) * [桥接模式例子](BridgePattern) * [观察者模式例子](ObserverPattern) ================================================ FILE: DesignPattern/SingletonPattern/README.md ================================================ # 单例模式 ```cpp // 懒汉式单例模式 class Singleton { private: Singleton() { } static Singleton * pInstance; public: static Singleton * GetInstance() { if (pInstance == nullptr) pInstance = new Singleton(); return pInstance; } }; // 线程安全的单例模式 class Singleton { private: Singleton() { } ~Singleton() { } Singleton(const Singleton &); Singleton & operator = (const Singleton &); public: static Singleton & GetInstance() { static Singleton instance; return instance; } }; ``` ================================================ FILE: DesignPattern/SingletonPattern/Singleton.cpp ================================================ // // Created by xiemenghui on 2018/7/20. // #include #include "Singleton.h" void Singleton::DoSomething() { std::cout << "Singleton do something\n"; } ================================================ FILE: DesignPattern/SingletonPattern/Singleton.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_SINGLETON_H #define DESIGNPATTERN_SINGLETON_H // Singleton mode class Singleton { private: Singleton(){} ~Singleton(){} Singleton(const Singleton &); Singleton & operator= (const Singleton &); public: static Singleton & GetInstance() { static Singleton instance; return instance; } void DoSomething(); }; #endif //DESIGNPATTERN_SINGLETON_H ================================================ FILE: DesignPattern/SingletonPattern/SingletonMain.h ================================================ // // Created by xiemenghui on 2018/7/20. // #ifndef DESIGNPATTERN_SINGLETONMAIN_H #define DESIGNPATTERN_SINGLETONMAIN_H #include "Singleton.h" void SingletonMain() { Singleton::GetInstance().DoSomething(); } #endif //DESIGNPATTERN_SINGLETONMAIN_H ================================================ FILE: DesignPattern/main.cpp ================================================ // // Created by xiemenghui on 2018/7/20. // #include #include "SingletonPattern/SingletonMain.h" #include "AbstractFactoryPattern/FactoryMain.h" #include "AdapterPattern/AdapterMain.h" #include "BridgePattern/BridgeMain.h" #include "ObserverPattern/ObserverMain.h" int main() { std::cout << "*******************" << std::endl; std::cout << "** Design pattern example **" << std::endl; std::cout << "*******************" << std::endl; std::cout << "*******************" << std::endl; std::cout << "** Singleton mode **" << std::endl; std::cout << "*******************" << std::endl; SingletonMain(); std::cout << "*******************" << std::endl; std::cout << "** Abstract factory pattern **" << std::endl; std::cout << "*******************" << std::endl; FactoryMain(); std::cout << "*******************" << std::endl; std::cout << "** Adapter mode **" << std::endl; std::cout << "*******************" << std::endl; AdapterMain(); std::cout << "*******************" << std::endl; std::cout << "** Bridge mode **" << std::endl; std::cout << "*******************" << std::endl; BridgeMain(); std::cout << "*******************" << std::endl; std::cout << "** Observer mode **" << std::endl; std::cout << "*******************" << std::endl; ObserverMain(); return 0; } ================================================ FILE: LICENSE ================================================ Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and b. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ================================================ FILE: Problems/ChessboardCoverageProblem/ChessboardCoverage.cpp ================================================ #include #include #include using namespace std; int num_Now = 0; // 记录L型骨牌编号 int **board = NULL; // 棋盘指针 // 函数声明 void ChessBoard(int num_BoardTopLeftRow, int num_BoardTopLeftColumn, int num_SpecialRow, int num_SpecialColumn, int boardSize); int main() { int num_BoardTopLeftRow = 0, // 棋盘左上角的行号 num_BoardTopLeftColumn = 0, // 棋盘左上角的列号 num_SpecialRow = 0, // 特殊方格所在的行号 num_SpecialColumn = 0, // 特殊方格所在的列号 boardSize = 0, // 棋盘大小 k = 0; // 构成的(2^k)*(2^k)个方格的棋盘 // 用户界面 cout << "---------------- 棋盘覆盖问题 ----------------" << endl; cout << "请输入k(k>=0),构成(2^k)*(2^k)个方格的棋盘" << endl; // 输入k值 cin >> k; // 判断输入数据合法性,包括检查输入是否为数字,k值是否大于0 if (cin.fail() || k < 0) { cout << "输入k错误!" << endl; system("pause"); return 0; } // 计算棋盘大小 boardSize = pow(2, k); cout << "请输入特殊方格所在的行号和列号(从0开始,用空格隔开)" << endl; // 输入特殊方格所在的行号和列号 cin >> num_SpecialRow >> num_SpecialColumn; // 判断输入数据合法性,包括检查输入是否为数字,特殊方格行号列号是否大于0,特殊方格行号列号是否不大于棋盘大小 if (cin.fail() || num_SpecialRow < 0 || num_SpecialColumn < 0 || num_SpecialRow >= boardSize || num_SpecialColumn >= boardSize) { cout << "输入行号或列号错误!" << endl; system("pause"); return 0; } // 分配棋盘空间 board = new int *[boardSize]; for (auto i = 0; i < boardSize; i++) { board[i] = new int[boardSize]; } // 为特殊方格赋初值0 board[num_SpecialRow][num_SpecialColumn] = 0; //执行棋盘覆盖函数 ChessBoard(num_BoardTopLeftRow, num_BoardTopLeftColumn, num_SpecialRow, num_SpecialColumn, boardSize); // 显示输出 cout << "------------------------------------------------" << endl; for (auto i = 0; i < boardSize; i++) { for (auto j = 0; j < boardSize; j++) { cout << board[i][j] << "\t"; } cout << endl; } cout << "------------------------------------------------" << endl; // 暂停查看结果 system("pause"); // 释放内存 for (int i = 0; i <= boardSize; i++) delete[] board[i]; delete[] board; // 指针置空 board = NULL; return 0; } // 棋盘覆盖函数 void ChessBoard(int num_BoardTopLeftRow, int num_BoardTopLeftColumn, int num_SpecialRow, int num_SpecialColumn, int boardSize) { // 棋盘大小为1则直接返回 if (boardSize == 1) return; int num = ++num_Now, // L型骨牌编号 size = boardSize / 2; // 分割棋盘,行列各一分为二 // 覆盖左上角子棋盘 if (num_SpecialRow < num_BoardTopLeftRow + size && num_SpecialColumn < num_BoardTopLeftColumn + size) { // 递归覆盖含有特殊方格的子棋盘 ChessBoard(num_BoardTopLeftRow, num_BoardTopLeftColumn, num_SpecialRow, num_SpecialColumn, size); } else { // 用编号为num的L型骨牌覆盖右下角 board[num_BoardTopLeftRow + size - 1][num_BoardTopLeftColumn + size - 1] = num; // 递归覆盖其余棋盘 ChessBoard(num_BoardTopLeftRow, num_BoardTopLeftColumn, num_BoardTopLeftRow + size - 1, num_BoardTopLeftColumn + size - 1, size); } // 覆盖右上角子棋盘 if (num_SpecialRow < num_BoardTopLeftRow + size && num_SpecialColumn >= num_BoardTopLeftColumn + size) { // 递归覆盖含有特殊方格的子棋盘 ChessBoard(num_BoardTopLeftRow, num_BoardTopLeftColumn + size, num_SpecialRow, num_SpecialColumn, size); } else { // 用编号为num的L型骨牌覆盖左下角 board[num_BoardTopLeftRow + size - 1][num_BoardTopLeftColumn + size] = num; // 递归覆盖其余棋盘 ChessBoard(num_BoardTopLeftRow, num_BoardTopLeftColumn + size, num_BoardTopLeftRow + size - 1, num_BoardTopLeftColumn + size, size); } // 覆盖左下角子棋盘 if (num_SpecialRow >= num_BoardTopLeftRow + size && num_SpecialColumn < num_BoardTopLeftColumn + size) { // 递归覆盖含有特殊方格的子棋盘 ChessBoard(num_BoardTopLeftRow + size, num_BoardTopLeftColumn, num_SpecialRow, num_SpecialColumn, size); } else { // 用编号为num的L型骨牌覆盖右上角 board[num_BoardTopLeftRow + size][num_BoardTopLeftColumn + size - 1] = num; // 递归覆盖其余棋盘 ChessBoard(num_BoardTopLeftRow + size, num_BoardTopLeftColumn, num_BoardTopLeftRow + size, num_BoardTopLeftColumn + size - 1, size); } // 覆盖右下角子棋盘 if (num_SpecialRow >= num_BoardTopLeftRow + size && num_SpecialColumn >= num_BoardTopLeftColumn + size) { // 递归覆盖含有特殊方格的子棋盘 ChessBoard(num_BoardTopLeftRow + size, num_BoardTopLeftColumn + size, num_SpecialRow, num_SpecialColumn, size); } else { // 用编号为num的L型骨牌覆盖左上角 board[num_BoardTopLeftRow + size][num_BoardTopLeftColumn + size] = num; // 递归覆盖其余棋盘 ChessBoard(num_BoardTopLeftRow + size, num_BoardTopLeftColumn + size, num_BoardTopLeftRow + size, num_BoardTopLeftColumn + size, size); } } ================================================ FILE: Problems/ChessboardCoverageProblem/README.md ================================================ ## 棋盘覆盖问题 ### 代码 [棋盘覆盖问题代码](ChessboardCoverage.cpp) ### 问题说明 在一个2^k * 2^k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格。 棋盘覆盖问题就是要用图示的4种不同形态的L型骨牌覆盖给定棋盘上除特殊方格之外的所有方格,且任何2个L型骨牌不得重叠覆盖。 ![](http://blog.chinaunix.net/attachment/201303/1/26548237_1362125215RWwI.png) ### 功能说明 本程序用分治法的思想解决了棋盘覆盖问题,显示输出 ### 代码简述 用户输入数据,程序输入检测,动态分配空间,调用棋盘覆盖函数,把计算结果存储到board(二维数组指针),显示输出。 其中棋盘覆盖函数用分治的思想把棋盘分成四份,递归求解。 ================================================ FILE: Problems/KnapsackProblem/README.md ================================================ ## 背包问题 ### 代码 [背包问题代码](pack.cpp) ### 问题说明 有N件物品和一个容量为V的背包。 第i件物品的重量是w[i],价值是v[i]。 求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量, 且价值总和最大。 ### 功能说明 本程序用动态规划的思想解决了背包问题,并用了两种算法: 迭代法、递归法。在迭代法中实现了打印背包问题的表格。 ### 代码简述 通过用户输入数据,程序输入检测,动态分配空间,选择算法, 用动态规划的思想求解背包问题。 #### 迭代法: 通过遍历n行W列,迭代每行每列的值,并把最优解放到 n行(在数组中为第n+1行)W列(在数组中为第W+1列)中。 #### 递归法: 通过每次返回前i个物品和承重为j的最优解, 递归计算总背包问题的最优解。 ================================================ FILE: Problems/KnapsackProblem/pack.cpp ================================================ #include #include using namespace std; int **T = NULL; // 存储背包问题表格的数组指针 // 返回两个值的最大值 int max(int a, int b) { return (a > b) ? a : b; } // 迭代法,能显示背包问题的表格 int packIterative(int n, int W, int *w, int *v) { // 循环遍历n行 for (int i = 1; i <= n; ++i) { // 循环遍历W列 for (int j = 1; j <= W; ++j) { //第i个物品能装下,则比较包括第i个物品和不包括第i个物品,取其最大值 if (w[i] <= j) T[i][j] = max(v[i] + T[i - 1][j - w[i]], T[i - 1][j]); // 第i个物品不能装下,则递归装i-1个 else T[i][j] = T[i - 1][j]; } } return T[n][W]; } // 递归法,不支持显示背包问题的表格 int packRecursive(int n, int W, int *w, int *v) { // 结束条件(初始条件),i或者j为0时最大总价值为0 if (n == 0 || W == 0) { return 0; } // 第i个物品不能装下,则递归装i-1个 if (w[n] > W) { return packRecursive(n - 1, W, w, v); } //第i个物品能装下,则比较包括第i个物品和不包括第i个物品,取其最大值 else { return max(v[n] + packRecursive(n - 1, W - w[n], w, v), packRecursive(n - 1, W, w, v)); } } // 打印背包问题的表格 void printT(int n, int W) { // 打印n行 for (auto i = 0; i <= n; i++) { // 打印行数 cout << i << ":\t"; // 打印W列 for (int w = 0; w <= W; w++) { cout << T[i][w] << "\t"; } // 换行 cout << endl; } } int main() { int *w = NULL; // 存储每件物品重量的数组指针 int *v = NULL; // 存储每件物品价值的数组指针 int n; // 物品个数n int W; // 背包总承重W cout << "---------------- 背包问题 ----------------" << endl; cout << "请输入物品数 n (n>=0) " << endl; // 输入背包数 cin >> n; if (cin.fail() || n < 0) { cout << "输入n错误!" << endl; system("pause"); return 0; } cout << "请输入背包承重量 W (W>=0) " << endl; // 输入背包承重量 cin >> W; if (cin.fail() || W < 0) { cout << "输入W错误!" << endl; system("pause"); return 0; } // 分配空间 // 对w和v分配n+1大小 w = new int[n + 1]; v = new int[n + 1]; // 对T分配n+1行,并初始化为0 T = new int *[n + 1](); // 对T分配W+1列,并初始化为0 for (auto i = 0; i <= n; i++) { T[i] = new int[W + 1](); } // 输入背包的重量和价值 for (auto i = 1; i <= n; i++) { cout << "请输入第 " << i << " 个物品的重量和价值(用空格隔开)" << endl; cin >> w[i] >> v[i]; if (cin.fail() || w[i] < 0 || v[i] < 0) { cout << "输入错误!" << endl; system("pause"); return 0; } } cout << "------------------------------------------------" << endl; cout << "请选择算法:" << endl; cout << "【1】迭代法" << endl; cout << "【2】递归法" << endl; cout << "------------------------------------------------" << endl; int choose; // 输入算法的选择 cin >> choose; switch (choose) { case 1: { // 迭代法,能显示背包问题的表格 cout << "能装下物品的最大价值为 " << packIterative(n, W, w, v) << endl; cout << "------------------------------------------------" << endl; printT(n, W); break; } case 2: { // 递归法,不支持显示背包问题的表格 cout << "能装下物品的最大价值为 " << packRecursive(n, W, w, v) << endl; break; } default: { cout << "输入错误!" << endl; break; } } cout << "------------------------------------------------" << endl; delete w; delete v; for (int i = 0; i <= n; ++i) { delete[] T[i]; } delete[] T; system("pause"); return 0; } ================================================ FILE: Problems/NeumannNeighborProblem/Formula/Neumann2_3_12.cpp ================================================ #include //通项法 int Neumann2_3_12(int n) { //通项公式的求解请查看说明文档 return 2 * n*n + 2 * n + 1; } int main() { int n = 0, a = 0; printf("------冯诺依曼邻居问题------\n"); printf("已知:\n"); printf(" 0 阶冯诺依曼邻居的元胞数为 1 \n"); printf(" 1 阶冯诺依曼邻居的元胞数为 5 \n"); printf(" 2 阶冯诺依曼邻居的元胞数为 13 \n"); printf("求:\n"); printf(" n 阶冯诺依曼邻居的元胞数\n"); printf("----------------------------\n"); printf("请输入n\n"); scanf("%d", &n); //用通项公式求解 a = Neumann2_3_12(n); printf("------------通项法-------------\n"); printf(" %d 阶冯诺依曼邻居的元胞数为 %d\n", n, a); getchar(); getchar(); return 0; } ================================================ FILE: Problems/NeumannNeighborProblem/Formula/README.md ================================================ ## 冯诺依曼邻居问题(通项公式) ### 代码 [冯诺依曼邻居问题(通项公式)代码](Neumann2_3_12.cpp) ### 问题说明 某算法从一个1×1的方格开始,每次都会在上次图形的周围再加上一圈方格,在第n次的时候要生成多少个方格?下图给出了n = 0,1,2是的结果。 ![](https://huihut-img.oss-cn-shenzhen.aliyuncs.com/NeumannNeighborProblem.jpg) ### 功能说明 本程序使用通项公式求解。 ### 代码简述 若设第n次生成的方格数是a(n),则: a(1) = a(0) + 4 * 1 a(2) = a(1) + 4 * 2 a(3) = a(2) + 4 * 3 ... a(n) = a(n-1) + 4 * n 化简可得: a(n) - a(1) = 4 * (n + (n-1) + ... + 2 ) 即: a(n) = 2 * n*n + 2 * n + 1 则可得出a(n)的通项公式,即可用通项公式直接求解。 在程序中用Neumann2_3_12函数返回a(n)的值。 ================================================ FILE: Problems/NeumannNeighborProblem/README.md ================================================ ## 冯诺依曼邻居问题 ### 问题说明 某算法从一个1×1的方格开始,每次都会在上次图形的周围再加上一圈方格,在第n次的时候要生成多少个方格?下图给出了n = 0,1,2是的结果。 ![](https://huihut-img.oss-cn-shenzhen.aliyuncs.com/NeumannNeighborProblem.jpg) ### 解法 * [通项公式解法](Formula) * [递推关系解法](Recursive) ================================================ FILE: Problems/NeumannNeighborProblem/Recursive/Neumann2_4_12.cpp ================================================ #include //递归法 int Neumann2_4_12(int n) { //由图可知第0次有1个方格 if (n == 0) return 1; //递推关系的求解请查看说明文档 return Neumann2_4_12(n - 1) + 4 * n; } int main() { int n = 0, a = 0; printf("------冯诺依曼邻居问题------\n"); printf("已知:\n"); printf(" 0 阶冯诺依曼邻居的元胞数为 1 \n"); printf(" 1 阶冯诺依曼邻居的元胞数为 5 \n"); printf(" 2 阶冯诺依曼邻居的元胞数为 13 \n"); printf("求:\n"); printf(" n 阶冯诺依曼邻居的元胞数\n"); printf("----------------------------\n"); printf("请输入n\n"); scanf("%d", &n); //建立递推关系,使用递归求解 a = Neumann2_4_12(n); printf("------------通项法-------------\n"); printf(" %d 阶冯诺依曼邻居的元胞数为 %d\3n", n, a); getchar(); getchar(); return 0; } ================================================ FILE: Problems/NeumannNeighborProblem/Recursive/README.md ================================================ ## 冯诺依曼邻居问题(递推关系) ### 代码 [冯诺依曼邻居问题(递推关系)代码](Neumann2_4_12.cpp) ### 问题说明 某算法从一个1×1的方格开始,每次都会在上次图形的周围再加上一圈方格,在第n次的时候要生成多少个方格?下图给出了n = 0,1,2是的结果。 ![](https://huihut-img.oss-cn-shenzhen.aliyuncs.com/NeumannNeighborProblem.jpg) ### 功能说明 本程序使用递推关系求解。 ### 代码简述 若设第n次生成的方格数是a(n),则: a(1) = a(0) + 4 * 1 a(2) = a(1) + 4 * 2 a(3) = a(2) + 4 * 3 ... a(n) = a(n-1) + 4 * n 则可得: a(n) = a(n - 1) + 4 * n 然后在代码中使用递归法,递归结束条件为n = 0,即 if (n == 0) return 1; 则可写出递归法的代码。 在程序中用Neumann2_4_12函数进行递归求解。 ================================================ FILE: Problems/RoundRobinProblem/MatchTable.cpp ================================================ #include #include using namespace std; // 循环赛日程安排函数声明 void MatchTable(int k, int n, int **table); int main() { int n = 0, k = 0; // 用户界面 cout << "---------------- 循环赛日程安排问题 ----------------" << endl; cout << "请输入k(k>=0),构成 n=(2^k) 个选手的循环赛" << endl; // 输入k值 cin >> k; // 判断输入数据合法性,包括检查输入是否为数字,k值是否大于0 if (cin.fail() || k < 0) { cout << "输入k错误!" << endl; system("pause"); return 0; } // 计算比赛日程表大小 n = pow(2, k); // 分配日程表空间 int **table = new int *[n + 1]; for (int i = 0; i <= n; i++) { table[i] = new int[n + 1]; } // 进行循环赛日程安排,生成日程表 MatchTable(k, n, table); // 显示输出 cout << "------------------------------------------------" << endl; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cout << table[i][j] << "\t"; } cout << endl; } cout << "------------------------------------------------" << endl; // 暂停查看结果 system("pause"); // 释放内存 for (int i = 0; i <= n; i++) delete[] table[i]; delete[] table; // 指针置空 table = NULL; return 0; } // 进行循环赛日程安排,生成日程表 void MatchTable(int k, int n, int **table) { // 设置日程表第一行的值 for (int i = 1; i <= n; i++) table[1][i] = i; // 每次填充的起始填充位置 int begin = 1; // 用分治法分separate份,循环求解 for (int separate = 1; separate <= k; separate++) { // 日程表进行划分 n /= 2; // flag为每一小份的列的标记 for (int flag = 1; flag <= n; flag++) { // 操作行 for (int i = begin + 1; i <= 2 * begin; i++) { // 操作列 for (int j = begin + 1; j <= 2 * begin; j++) { // 把左上角的值赋给右下角 table[i][j + (flag - 1) * begin * 2] = table[i - begin][j + (flag - 1) * begin * 2 - begin]; // 把右上角的值赋给左下角 table[i][j + (flag - 1) * begin * 2 - begin] = table[i - begin][j + (flag - 1) * begin * 2]; } } } // 进入日程表的下一个划分进行填充 begin *= 2; } } ================================================ FILE: Problems/RoundRobinProblem/README.md ================================================ ## 循环赛日程安排问题 ### 代码 [循环赛日程安排问题代码](MatchTable.cpp) ### 问题说明 设有n=2k个选手要进行网球循环赛, 要求设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次; (2)每个选手一天只能赛一次。 按此要求,可将比赛日程表设计成一个 n 行n-1列的二维表, 其中,第 i 行第 j 列表示和第 i 个选手在第 j 天比赛的选手。 ### 功能说明 本程序运用分治的思想,实现了循环赛日程安排问题的求解, 生成日程表,输出。 ### 代码简述 通过用户输入数据,程序输入检测,动态分配空间, 调用生成日程表函数,显示输出。 其中,生成日程表函数运用分治的思想,分成separate份, 先安排第一行(第一份),然后每一份填充,最终求解完毕, 生成日程表。 ================================================ FILE: Problems/TubingProblem/README.md ================================================ ## 输油管道问题 ### 代码 [输油管道问题代码](Tubing.cpp) ### 问题说明 某石油公司计划建造一条由东向西的主输油管道。 该管道要穿过一个有n 口油井的油田。 从每口油井都要有一条输油管道沿最短路经(或南或北)与主管道相连。 如果给定n口油井的位置,即它们的x 坐标(东西向)和y 坐标(南北向), 应如何确定主管道的最优位置, 即使各油井到主管道之间的输油管道长度总和最小的位置? ### 功能说明 本程序用排序求中值的方法求解输油管道问题。 ### 代码简述 通过用户输入数据(只输入油井数n、每个油井的y坐标), 程序输入检测,动态分配空间,排序(使用快速排序), 求出中间值,输出。 输出有以下两种情况: 1. 当n为奇数,则最优位置为y数组的第n/2个油井的y坐标 2. 当n为偶数,则最优位置为y数组的中间两个油井的y坐标的区间 ================================================ FILE: Problems/TubingProblem/Tubing.cpp ================================================ #include using namespace std; // 油井y坐标指针 float * y = NULL; // 快速排序 void quick_sort(int low, int high) { if (low >= high) // 结束标志 return; int first = low; // 低位下标 int last = high; // 高位下标 float key = y[first]; // 设第一个为基准 while (first < last) { // 将比第一个小的移到前面 while (first < last && y[last] >= key) last--; if (first < last) y[first++] = y[last]; // 将比第一个大的移到后面 while (first < last && y[first] <= key) first++; if (first < last) y[last--] = y[first]; } // 基准置位 y[first] = key; // 前半递归 quick_sort(low, first - 1); // 后半递归 quick_sort(first + 1, high); } int main() { int n; // 油井数 float mid; // y数组的中间位置的数 float minDistance = 0; // 各油井到主管道之间的管道长度总和最小位置 cout << "---------------- 输油管问题 ----------------" << endl; cout << "请输入油井数 n (n>=0) " << endl; // 输入油井数 cin >> n; // 判断输入数据合法性,包括检查输入是否为数字,k值是否大于0 if (cin.fail() || n < 0) { cout << "输入n错误!" << endl; system("pause"); return 0; } // 分配n个y坐标存储空间 y = new float[n]; cout << "请输入 " << n << " 个油井的 y 坐标(用空格隔开)" << endl; // 输入油井的 y 坐标 for (auto i = 0; i < n; i++) { cin >> y[i]; } // 判断输入数据合法性 if (cin.fail()) { cout << "输入y坐标错误!" << endl; system("pause"); return 0; } // 运用快速排序对y坐标数组进行排序 quick_sort(0, n - 1); // 计算y数组的中间位置的数 mid = y[n / 2]; // 计算各个油井到主输油管的长度之和 for (auto i = 0; i < n; i++) { minDistance += abs(y[i] - mid); } // 显示输出 cout << "------------------------------------------------" << endl; // 判断油井奇偶,做不同的输出 if (n & 1) { // n为奇数,则最优位置为y数组的第n/2个油井的y坐标 cout << "主管道的最优位置为:y = " << mid << endl; } else { // n为偶数,则最优位置为y数组的中间两个油井的y坐标的区间 cout << "主管道的最优位置为:y = [" << y[n / 2 - 1] << "," << mid << "]" << endl; } // 输出各油井到主管道之间的管道总长度 cout << "各油井到主管道之间的管道总长度为:" << minDistance << endl; cout << "------------------------------------------------" << endl; // 暂停查看结果 system("pause"); // 释放内存 delete[] y; // 指针置空 y = NULL; return 0; } ================================================ FILE: README.md ================================================
📖 Github    |    📚 Docsify

简体中文    |    English

💡 关于 📚 本仓库是面向 C/C++ 技术方向校招求职者、初学者的基础知识总结,包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。 💡 侧边目录支持方式:[📚 Docsify 文档](https://interview.huihut.com)、[Github + TOC 导航](https://github.com/jawil/GayHub)([TOC预览.png](https://gitee.com/huihut/interview/raw/master/images/TOC预览.png)) 📄 保存为 PDF 方式:使用 Chrome 浏览器打开 📚 Docsify 文档 页面,缩起左侧目录-右键 - 打印 - 选择目标打印机是另存为PDF - 保存([打印预览.png](https://gitee.com/huihut/interview/raw/master/images/打印预览.png)) 🙏 仓库内容如有错误或改进欢迎 issue 或 pr,建议或讨论可在 [#12](https://github.com/huihut/interview/issues/12) 提出。由于本人水平有限,仓库中的知识点有来自本人原创、读书笔记、书籍、博文等,非原创均已标明出处,如有遗漏,请 issue 提出。本仓库遵循 [CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享)](https://github.com/huihut/interview/blob/master/LICENSE) 协议,转载请注明出处,不得用于商业目的。
## 📑 目录 * [➕ C/C++](#cc) * [⭐️ Effective](#effective) * [📦 STL](#stl) * [〽️ 数据结构](#data-structure) * [⚡️ 算法](#algorithm) * [❓ Problems](#problems) * [💻 操作系统](#os) * [☁️ 计算机网络](#computer-network) * [🌩 网络编程](#network-programming) * [💾 数据库](#database) * [📏 设计模式](#design-pattern) * [⚙️ 链接装载库](#link-loading-library) * [📚 书籍](#books) * [🔱 C/C++ 发展方向](#cc-development-direction) * [💯 复习刷题网站](#review-of-brush-questions-website) * [📝 面试题目经验](#interview-questions-experience) * [📆 招聘时间岗位](#recruitment-time-post) * [👍 内推](#recommend) * [👬 贡献者](#contributor) * [📜 License](#license) ## ➕ C/C++ ### const #### 作用 1. 修饰变量,说明该变量不可以被改变; 2. 修饰指针,分为指向常量的指针(pointer to const)和自身是常量的指针(常量指针,const pointer); 3. 修饰引用,指向常量的引用(reference to const),用于形参类型,即避免了拷贝,又避免了函数对值的修改; 4. 修饰成员函数,说明该成员函数内不能修改成员变量。 #### const 的指针与引用 * 指针 * 指向常量的指针(pointer to const) * 自身是常量的指针(常量指针,const pointer) * 引用 * 指向常量的引用(reference to const) * 没有 const reference,因为引用只是对象的别名,引用不是对象,不能用 const 修饰 > (为了方便记忆可以想成)被 const 修饰(在 const 后面)的值不可改变,如下文使用例子中的 `p2`、`p3` #### 使用 const 使用 ```cpp // 类 class A { private: const int a; // 常对象成员,可以使用初始化列表或者类内初始化 public: // 构造函数 A() : a(0) { }; A(int x) : a(x) { }; // 初始化列表 // const可用于对重载函数的区分 int getValue(); // 普通成员函数 int getValue() const; // 常成员函数,不得修改类中的任何数据成员的值 }; void function() { // 对象 A b; // 普通对象,可以调用全部成员函数 const A a; // 常对象,只能调用常成员函数 const A *p = &a; // 指针变量,指向常对象 const A &q = a; // 指向常对象的引用 // 指针 char greeting[] = "Hello"; char* p1 = greeting; // 指针变量,指向字符数组变量 const char* p2 = greeting; // 指针变量,指向字符数组常量(const 后面是 char,说明指向的字符(char)不可改变) char* const p3 = greeting; // 自身是常量的指针,指向字符数组变量(const 后面是 p3,说明 p3 指针自身不可改变) const char* const p4 = greeting; // 自身是常量的指针,指向字符数组常量 } // 函数 void function1(const int Var); // 传递过来的参数在函数内不可变 void function2(const char* Var); // 参数指针所指内容为常量 void function3(char* const Var); // 参数指针为常量 void function4(const int& Var); // 引用参数在函数内为常量 // 函数返回值 const int function5(); // 返回一个常数 const int* function6(); // 返回一个指向常量的指针变量,使用:const int *p = function6(); int* const function7(); // 返回一个指向变量的常指针,使用:int* const p = function7(); ``` #### 宏定义 #define 和 const 常量 宏定义 #define|const 常量 ---|--- 宏定义,相当于字符替换|常量声明 预处理器处理|编译器处理 无类型安全检查|有类型安全检查 不分配内存|要分配内存 存储在代码段|存储在数据段 可通过 `#undef` 取消|不可取消 ### static #### 作用 1. 修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。 2. 修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。 3. 修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。 4. 修饰成员函数,修饰成员函数使得不需要生成对象就可以访问该函数,但是在 static 函数内不能访问非静态成员。 ### this 指针 1. `this` 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。 2. 当对一个对象调用成员函数时,编译程序先将对象的地址赋给 `this` 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 `this` 指针。 3. 当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。 4. `this` 指针被隐含地声明为: `ClassName *const this`,这意味着不能给 `this` 指针赋值;在 `ClassName` 类的 `const` 成员函数中,`this` 指针的类型为:`const ClassName* const`,这说明不能对 `this` 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作); 5. `this` 并不是一个常规变量,而是个右值,所以不能取得 `this` 的地址(不能 `&this`)。 6. 在以下场景中,经常需要显式引用 `this` 指针: 1. 为实现对象的链式引用; 2. 为避免对同一对象进行赋值操作; 3. 在实现一些数据结构时,如 `list`。 ### inline 内联函数 #### 特征 * 相当于把内联函数里面的内容写在调用内联函数处; * 相当于不用执行进入函数的步骤,直接执行函数体; * 相当于宏,却比宏多了类型检查,真正具有函数特性; * 编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数; * 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。 #### 使用 inline 使用 ```cpp // 声明1(加 inline,建议使用) inline int functionName(int first, int second,...); // 声明2(不加 inline) int functionName(int first, int second,...); // 定义 inline int functionName(int first, int second,...) {/****/}; // 类内定义,隐式内联 class A { int doA() { return 0; } // 隐式内联 } // 类外定义,需要显式内联 class A { int doA(); } inline int A::doA() { return 0; } // 需要显式内联 ``` #### 编译器对 inline 函数的处理步骤 1. 将 inline 函数体复制到 inline 函数调用点处; 2. 为所用 inline 函数中的局部变量分配内存空间; 3. 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中; 4. 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。 #### 优缺点 优点 1. 内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。 2. 内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。 3. 在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。 4. 内联函数在运行时可调试,而宏定义不可以。 缺点 1. 代码膨胀。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。 2. inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译,不像 non-inline 可以直接链接。 3. 是否内联,程序员不可控。内联函数只是对编译器的建议,是否对函数内联,决定权在于编译器。 #### 虚函数(virtual)可以是内联函数(inline)吗? > [Are "inline virtual" member functions ever actually "inlined"?](http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html) * 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。 * 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。 * `inline virtual` 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 `Base::who()`),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。 虚函数内联使用 ```cpp #include using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // 不写inline时隐式内联 { cout << "I am Derived\n"; } }; int main() { // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 Base b; b.who(); // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 Base *ptr = new Derived(); ptr->who(); // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 delete ptr; ptr = nullptr; system("pause"); return 0; } ``` ### volatile ```cpp volatile int i = 10; ``` * volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。 * volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值) * const 可以是 volatile (如只读的状态寄存器) * 指针可以是 volatile ### assert() 断言,是宏,而非函数。assert 宏的原型定义在 ``(C)、``(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 `NDEBUG` 来关闭 assert,但是需要在源代码的开头,`include ` 之前。 assert() 使用 ```cpp #define NDEBUG // 加上这行,则 assert 不可用 #include assert( p != NULL ); // assert 不可用 ``` ### sizeof() * sizeof 对数组,得到整个数组所占空间大小。 * sizeof 对指针,得到指针本身所占空间大小。 ### 编译器扩展与标准对齐控制 * 编译器扩展:`#pragma pack(n)`,将随后定义的 `struct`/`class`/`union` 的成员最大对齐限制为 n 字节。 * 标准对齐控制: * `alignas(k)`,要求类型或变量至少按 k 字节对齐(向上取整到 ≥ 自然对齐)。 * `alignof(T)`,获取类型 T 的自然对齐要求(编译时常量)。 特性 |#pragma pack|alignas --------|--------------|------------ 标准化 |❌ 编译器扩展 | ✅ C++11标准 对齐方向|⬇️ 只能减小对齐| ⬆️ 只能增大对齐 可移植性|❌ 编译器依赖 | ✅ 跨平台 作用范围|🔄 影响整个结构| 🎯 可针对单个成员 性能影响|⚠️ 可能降低内存访问速度| ⚠️ 过度对齐浪费空间 使用 ```cpp #include #include #pragma pack(push, 1) // 最大对齐 1 字节,紧凑布局 struct PackedHeader { uint16_t len; // offset 0 uint32_t id; // offset 2 }; #pragma pack(pop) struct alignas(8) Align8 { double value; // offset 0, 占 8 字节 int flag; // offset 8 }; int main() { std::cout << "PackedHeader size: " << sizeof(PackedHeader) << "\n"; // 6 std::cout << "Align8 size: " << sizeof(Align8) << "\n"; // 16 } ``` ### 位域 ```cpp Bit mode: 2; // mode 占 2 位 ``` 类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。 * 位域在内存中的布局是与机器有关的 * 位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定 * 取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域 ### extern 与 extern "C" * `extern` 是存储类说明符(storage-class-specifier),用于声明变量或函数具有外部链接,表示实体的定义可能在其他翻译单元中。 * `extern "C"` 是链接指示(linkage directive),它指定函数或变量使用 C 语言链接(不影响编译规则)。 1. 禁止 C++ 名称修饰。确保符号名称与该平台下 C 编译器生成的名称一致,避免链接时因名称修饰导致的未定义符号错误,但不保证平台 ABI(应用二进制接口)一致性。 2. 实现 C/C++ 互操作。允许 C++ 函数被 C 代码调用(或反之)。 `extern "C"` 使用 ```cpp #ifdef __cplusplus extern "C" { #endif void *memset(void *, int, size_t); #ifdef __cplusplus } #endif ``` ### struct 和 typedef struct #### C 中 ```c // c typedef struct Student { int age; } S; ``` 等价于 ```c // c struct Student { int age; }; typedef struct Student S; ``` 此时 `S` 等价于 `struct Student`,但两个标识符名称空间不相同。 另外还可以定义与 `struct Student` 不冲突的 `void Student() {}`。 #### C++ 中 由于编译器定位符号的规则(搜索规则)改变,导致不同于C语言。 一、如果在类标识符空间定义了 `struct Student {...};`,使用 `Student me;` 时,编译器将搜索全局标识符表,`Student` 未找到,则在类标识符内搜索。 即表现为可以使用 `Student` 也可以使用 `struct Student`,如下: ```cpp // cpp struct Student { int age; }; void f( Student me ); // 正确,"struct" 关键字可省略 ``` 二、若定义了与 `Student` 同名函数之后,则 `Student` 只代表函数,不代表结构体,如下: ```cpp typedef struct Student { int age; } S; void Student() {} // 正确,定义后 "Student" 只代表此函数 //void S() {} // 错误,符号 "S" 已经被定义为一个 "struct Student" 的别名 int main() { Student(); struct Student me; // 或者 "S me"; return 0; } ``` ### C++ 中 struct 和 class 总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。 #### 区别 * 最本质的一个区别就是默认的访问控制 1. 默认的继承访问权限。struct 是 public 的,class 是 private 的。 2. struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。 ### union 联合 联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点: * 默认访问控制符为 public * 可以含有构造函数、析构函数 * 不能含有引用类型的成员 * 不能继承自其他类,不能作为基类 * 不能含有虚函数 * 匿名 union 在定义所在作用域可直接访问 union 成员 * 匿名 union 不能包含 protected 成员或 private 成员 * 全局匿名联合必须是静态(static)的 union 使用 ```cpp #include union UnionTest { UnionTest() : i(10) {}; int i; double d; }; static union { int i; double d; }; int main() { UnionTest u; union { int i; double d; }; std::cout << u.i << std::endl; // 输出 UnionTest 联合的 10 ::i = 20; std::cout << ::i << std::endl; // 输出全局静态匿名联合的 20 i = 30; std::cout << i << std::endl; // 输出局部匿名联合的 30 return 0; } ``` ### C 实现 C++ 类 C 实现 C++ 的面向对象特性(封装、继承、多态) * 封装:使用函数指针把属性与方法封装到结构体中 * 继承:结构体嵌套 * 多态:父类与子类方法的函数指针不同 > [Can you write object-oriented code in C? [closed]](https://stackoverflow.com/a/351745) ### explicit(显式)关键字 * explicit 修饰构造函数时,可以防止隐式转换和复制初始化 * explicit 修饰转换函数时,可以防止隐式转换,但 [按语境转换](https://zh.cppreference.com/w/cpp/language/implicit_conversion) 除外 explicit 使用 ```cpp struct A { A(int) { } operator bool() const { return true; } }; struct B { explicit B(int) {} explicit operator bool() const { return true; } }; void doA(A a) {} void doB(B b) {} int main() { A a1(1); // OK:直接初始化 A a2 = 1; // OK:复制初始化 A a3{ 1 }; // OK:直接列表初始化 A a4 = { 1 }; // OK:复制列表初始化 A a5 = (A)1; // OK:允许 static_cast 的显式转换 doA(1); // OK:允许从 int 到 A 的隐式转换 if (a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a8 = static_cast(a1); // OK :static_cast 进行直接初始化 B b1(1); // OK:直接初始化 B b2 = 1; // 错误:被 explicit 修饰构造函数的对象不可以复制初始化 B b3{ 1 }; // OK:直接列表初始化 B b4 = { 1 }; // 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化 B b5 = (B)1; // OK:允许 static_cast 的显式转换 doB(1); // 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换 if (b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 bool b6(b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 bool b7 = b1; // 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换 bool b8 = static_cast(b1); // OK:static_cast 进行直接初始化 return 0; } ``` ### friend 友元类和友元函数 * 能访问私有成员 * 破坏封装性 * 友元关系不可传递 * 友元关系的单向性 * 友元声明的形式及数量不受限制 ### using #### using 声明 一条 `using 声明` 语句一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字。如: ```cpp using namespace_name::name; ``` #### 构造函数的 using 声明 在 C++11 中,派生类能够重用其直接基类定义的构造函数。 ```cpp class Derived : Base { public: using Base::Base; /* ... */ }; ``` 如上 using 声明,对于基类的每个构造函数,编译器都生成一个与之对应(形参列表完全相同)的派生类构造函数。生成如下类型构造函数: ```cpp Derived(parms) : Base(args) { } ``` #### using 指示 `using 指示` 使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。如: ```cpp using namespace namespace_name; ``` #### 尽量少使用 `using 指示` 污染命名空间 > 一般说来,使用 using 命令比使用 using 编译命令更安全,这是由于它**只导入了指定的名称**。如果该名称与局部名称发生冲突,编译器将**发出指示**。using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则**局部名称将覆盖名称空间版本**,而编译器**并不会发出警告**。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。 using 使用 尽量少使用 `using 指示` ```cpp using namespace std; ``` 应该多使用 `using 声明` ```cpp int x; std::cin >> x ; std::cout << x << std::endl; ``` 或者 ```cpp using std::cin; using std::cout; using std::endl; int x; cin >> x; cout << x << endl; ``` ### :: 范围解析运算符 #### 分类 1. 全局作用域符(`::name`):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间 2. 类作用域符(`class::name`):用于表示指定类型的作用域范围是具体某个类的 3. 命名空间作用域符(`namespace::name`):用于表示指定类型的作用域范围是具体某个命名空间的 :: 使用 ```cpp int count = 11; // 全局(::)的 count class A { public: static int count; // 类 A 的 count(A::count) }; int A::count = 21; void fun() { int count = 31; // 初始化局部的 count 为 31 count = 32; // 设置局部的 count 的值为 32 } int main() { ::count = 12; // 测试 1:设置全局的 count 的值为 12 A::count = 22; // 测试 2:设置类 A 的 count 为 22 fun(); // 测试 3 return 0; } ``` ### enum 枚举类型 #### 限定作用域的枚举类型 ```cpp enum class open_modes { input, output, append }; ``` #### 不限定作用域的枚举类型 ```cpp enum color { red, yellow, green }; enum { floatPrec = 6, doublePrec = 10 }; ``` ### decltype decltype 关键字用于检查实体的声明类型或表达式的类型及值分类。语法: ```cpp decltype ( expression ) ``` decltype 使用 ```cpp // 尾置返回允许我们在参数列表之后声明返回类型 template auto fcn(It beg, It end) -> decltype(*beg) { // 处理序列 return *beg; // 返回序列中一个元素的引用 } // 为了使用模板参数成员,必须用 typename template auto fcn2(It beg, It end) -> typename remove_reference::type { // 处理序列 return *beg; // 返回序列中一个元素的拷贝 } ``` ### 引用 #### 左值引用 常规引用,一般表示对象的身份。 #### 右值引用 右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。 右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面: * 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。 * 能够更简洁明确地定义泛型函数。 #### 引用折叠 * `X& &`、`X& &&`、`X&& &` 可折叠成 `X&` * `X&& &&` 可折叠成 `X&&` ### 宏 * 宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对 “参数” 进行的是一对一的替换。 ### 成员初始化列表 好处 * 更高效:少了一次调用默认构造函数的过程。 * 有些场合必须要用初始化列表: 1. 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面 2. 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面 3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化 ### initializer_list 列表初始化 用花括号初始化器列表初始化一个对象,其中对应构造函数接受一个 `std::initializer_list` 参数. initializer_list 使用 ```cpp #include #include #include template struct S { std::vector v; S(std::initializer_list l) : v(l) { std::cout << "constructed with a " << l.size() << "-element list\n"; } void append(std::initializer_list l) { v.insert(v.end(), l.begin(), l.end()); } std::pair c_arr() const { return {&v[0], v.size()}; // 在 return 语句中复制列表初始化 // 这不使用 std::initializer_list } }; template void templated_fn(T) {} int main() { S s = {1, 2, 3, 4, 5}; // 复制初始化 s.append({6, 7, 8}); // 函数调用中的列表初始化 std::cout << "The vector size is now " << s.c_arr().second << " ints:\n"; for (auto n : s.v) std::cout << n << ' '; std::cout << '\n'; std::cout << "Range-for over brace-init-list: \n"; for (int x : {-1, -2, -3}) // auto 的规则令此带范围 for 工作 std::cout << x << ' '; std::cout << '\n'; auto al = {10, 11, 12}; // auto 的特殊规则 std::cout << "The list bound to auto has size() = " << al.size() << '\n'; // templated_fn({1, 2, 3}); // 编译错误!“ {1, 2, 3} ”不是表达式, // 它无类型,故 T 无法推导 templated_fn>({1, 2, 3}); // OK templated_fn>({1, 2, 3}); // 也 OK } ``` ### 面向对象 面向对象程序设计(Object-oriented programming,OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。 ![面向对象特征](https://gitee.com/huihut/interview/raw/master/images/面向对象基本特征.png) 面向对象三大特征 —— 封装、继承、多态 ### 封装 把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。关键字:public, protected, private。不写默认为 private。 * `public` 成员:可以被任意实体访问 * `protected` 成员:只允许被子类及本类的成员函数访问 * `private` 成员:只允许被本类的成员函数、友元类或友元函数访问 ### 继承 * 基类(父类)——> 派生类(子类) ### 多态 * 多态,即多种状态(形态)。简单来说,我们可以将多态定义为消息以多种形式显示的能力。 * 多态是以封装和继承为基础的。 * C++ 多态分类及实现: 1. 重载多态(Ad-hoc Polymorphism,编译期):函数重载、运算符重载 2. 子类型多态(Subtype Polymorphism,运行期):虚函数 3. 参数多态性(Parametric Polymorphism,编译期):类模板、函数模板 4. 强制多态(Coercion Polymorphism,编译期/运行期):基本类型转换、自定义类型转换 > [The Four Polymorphisms in C++](https://catonmat.net/cpp-polymorphism) #### 静态多态(编译期/早绑定) 函数重载 ```cpp class A { public: void do(int a); void do(int a, int b); }; ``` #### 动态多态(运行期/晚绑定) * 虚函数:用 virtual 修饰成员函数,使其成为虚函数 * 动态绑定:当使用基类的引用或指针调用一个虚函数时将发生动态绑定 **注意:** * 可以将派生类的对象赋值给基类的指针或引用,反之不可 * 普通函数(非类成员函数)不能是虚函数 * 静态函数(static)不能是虚函数 * 构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间中,必须要构造函数调用完成后才会形成虚表指针) * 内联函数不能是表现多态性时的虚函数,解释见:[虚函数(virtual)可以是内联函数(inline)吗?](https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97) 动态多态使用 ```cpp class Shape // 形状类 { public: virtual double calcArea() { ... } virtual ~Shape(); }; class Circle : public Shape // 圆形类 { public: virtual double calcArea(); ... }; class Rect : public Shape // 矩形类 { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); Shape * shape2 = new Rect(5.0, 6.0); shape1->calcArea(); // 调用圆形类里面的方法 shape2->calcArea(); // 调用矩形类里面的方法 delete shape1; shape1 = nullptr; delete shape2; shape2 = nullptr; return 0; } ``` ### 虚析构函数 虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。 虚析构函数使用 ```cpp class Shape { public: Shape(); // 构造函数不能是虚函数 virtual double calcArea(); virtual ~Shape(); // 虚析构函数 }; class Circle : public Shape // 圆形类 { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); shape1->calcArea(); delete shape1; // 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。 shape1 = NULL; return 0; } ``` ### 纯虚函数 纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。 ```cpp virtual int A() = 0; ``` ### 虚函数、纯虚函数 * 类里如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。 * 虚函数在子类里面可以不重写;但纯虚函数必须在子类实现才可以实例化子类。 * 虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。 * 带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。 * 虚基类是虚继承中的基类,具体见下文虚继承。 > [CSDN . C++ 中的虚函数、纯虚函数区别和联系](https://blog.csdn.net/u012260238/article/details/53610462) ### 虚函数指针、虚函数表 * 虚函数指针:在含有虚函数类的对象中,指向虚函数表,在运行时确定。 * 虚函数表:在程序只读数据段(`.rodata section`,见:[目标文件存储结构](#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84)),存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建。 > [C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现](https://blog.twofei.com/496/) ### 虚继承 虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。 底层实现原理与编译器相关,一般通过**虚基类指针**和**虚基类表**实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。 实际上,vbptr 指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。 ### 虚继承、虚函数 * 相同之处:都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间) * 不同之处: * 虚继承 * 虚基类依旧存在继承类中,只占用存储空间 * 虚基类表存储的是虚基类相对直接继承类的偏移 * 虚函数 * 虚函数不占用存储空间 * 虚函数表存储的是虚函数地址 ### 类模板、成员模板、虚函数 * 类模板中可以使用虚函数 * 一个类(无论是普通类还是类模板)的成员模板(本身是模板的成员函数)不能是虚函数 ### 抽象类、接口类、聚合类 * 抽象类:含有纯虚函数的类 * 接口类:仅含有纯虚函数的抽象类 * 聚合类:用户可以直接访问其成员,并且具有特殊的初始化语法形式。满足如下特点: * 所有成员都是 public * 没有定义任何构造函数 * 没有类内初始化 * 没有基类,也没有 virtual 函数 ### 内存分配和管理 #### malloc、calloc、realloc、alloca 1. malloc:申请指定字节数的内存。申请到的内存中的初始值不确定。 2. calloc:为指定长度的对象,分配能容纳其指定个数的内存。申请到的内存的每一位(bit)都初始化为 0。 3. realloc:更改以前分配的内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定。 4. alloca:在栈上申请内存。程序在出栈的时候,会自动释放内存。但是需要注意的是,alloca 不具可移植性, 而且在没有传统堆栈的机器上很难实现。alloca 不宜使用在必须广泛移植的程序中。C99 中支持变长数组 (VLA),可以用来替代 alloca。 #### malloc、free 用于分配、释放内存 malloc、free 使用 申请内存,确认是否申请成功 ```cpp char *str = (char*) malloc(100); assert(str != nullptr); ``` 释放内存后指针置空 ```cpp free(p); p = nullptr; ``` #### new、delete 1. new / new[]:完成两件事,先底层调用 malloc 分配了内存,然后调用构造函数(创建对象)。 2. delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用 free 释放空间。 3. new 在申请内存时会自动计算所需字节数,而 malloc 则需我们自己输入申请内存空间的字节数。 new、delete 使用 申请内存,确认是否申请成功 ```cpp int main() { T* t = new T(); // 先内存分配 ,再构造函数 delete t; // 先析构函数,再内存释放 return 0; } ``` #### 定位 new 定位 new(placement new)允许我们向 new 传递额外的地址参数,从而在预先指定的内存区域创建对象。 ```cpp new (place_address) type new (place_address) type (initializers) new (place_address) type [size] new (place_address) type [size] { braced initializer list } ``` * `place_address` 是个指针 * `initializers` 提供一个(可能为空的)以逗号分隔的初始值列表 ### delete this 合法吗? > [Is it legal (and moral) for a member function to say delete this?](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) 合法,但: 1. 必须保证 this 对象是通过 `new`(不是 `new[]`、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的 2. 必须保证调用 `delete this` 的成员函数是最后一个调用 this 的成员函数 3. 必须保证成员函数的 `delete this ` 后面没有调用 this 了 4. 必须保证 `delete this` 后没有人使用了 ### 智能指针 #### C++ 标准库(STL)中 头文件:`#include ` #### C++ 98 ```cpp std::auto_ptr ps (new std::string(str)); ``` #### C++ 11 1. shared_ptr 2. unique_ptr 3. weak_ptr 4. auto_ptr(被 C++11 弃用) * Class shared_ptr 实现共享式拥有(shared ownership)概念。多个智能指针指向相同对象,该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作,标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。 * Class unique_ptr 实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏(resource leak)——如 new 后忘记 delete ——特别有用。 ##### shared_ptr 多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。 * 支持定制型删除器(custom deleter),可防范 Cross-DLL 问题(对象在动态链接库(DLL)中被 new 创建,却在另一个 DLL 内被 delete 销毁)、自动解除互斥锁 ##### weak_ptr weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。 * 可打破环状引用(cycles of references,两个其实已经没有被使用的对象彼此互指,使之看似还在 “被使用” 的状态)的问题 ##### unique_ptr unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有者被销毁或变成空或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。 * unique_ptr 用于取代 auto_ptr ##### auto_ptr 被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 `std::move` 语义,以及其他瑕疵。 ##### auto_ptr 与 unique_ptr 比较 * auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了`move` 语义; * auto_ptr 对象不能管理数组(析构调用 `delete`),unique_ptr 可以管理数组(析构调用 `delete[]` ); ### 强制类型转换运算符 > [MSDN . 强制转换运算符](https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx) #### static_cast * 编译时类型转换(无运行时检查) * 不依赖 RTTI | 转换类型 | 安全性 | 示例 | |--------------------------|--------------|--------------------------| | 数值类型转换 | ✅ 安全 | `float f=3.14; int i=static_cast(f);` | | 类向上转换 | ✅ 安全 | `Derived* d; Base* b=static_cast(d);` | | 类向下转换 | ⚠️ 不安全 | `Base* b=new Base; Derived* d=static_cast(b);` | | 类不变转换 | ✅ 安全 | `MyClass* p; MyClass* same=static_cast(p);` | | 显式构造函数调用 | ✅ 安全 | `func(static_cast("text"));` | | 任意→void* | ✅ 安全 | `int* p; void* vp=static_cast(p);` | | 枚举↔整型 | ✅ 安全 | `enum Color{RED}; int c=static_cast(RED);` | #### dynamic_cast * 运行时类型检查(依赖 RTTI) * 多态类型要求(基类需至少一个虚函数) * 安全失败机制(nullptr 或异常) | 转换类型 | 安全性 | 运行时开销 | 多态要求 | 失败处理 | 示例 | |--------------|--------|------------|----------|------------------------|------| | 类向上转换 | ✅ 安全 | 无 | ❌ 不需要 | 不适用(总是成功) | `Derived* d; Base* b = dynamic_cast(d);` | | 类向下转换 | ✅ 安全 | 有 | ✅ 需要 | 指针→`nullptr`
引用→`std::bad_cast` | `Base* b=new Base; Derived* d = dynamic_cast(b);` | | 类旁支转换 | ✅ 安全 | 有 | ✅ 需要 | 指针→`nullptr`
引用→`std::bad_cast` | `B2* b2 = dynamic_cast(b1); // 菱形继承中 B1*→B2*` | | 类不变转换 | ✅ 安全 | 无 | ❌ 不需要 | 不适用(总是成功) | `Derived* d2 = dynamic_cast(d1);` | | 任意→void* | ✅ 安全 | 有 | ✅ 需要 | `nullptr` | `void* p = dynamic_cast(obj);` | #### const_cast * 编译时类型修饰符操作 * 仅修改 `const`/`volatile` 属性 * 不改变底层二进制表示 | 转换类型 | 安全性 | 示例 | |------------------------|--------------|-------------------------------| | 移除 const | ⚠️ 风险 | `const int* cp; int* p=const_cast(cp);` | | 移除 volatile | ⚠️ 风险 | `volatile int* vp; int* p=const_cast(vp);` | | 添加 const | ✅ 安全 | `int* p; const int* cp=const_cast(p);` | | 兼容旧式 API | ⚠️ 必要风险 | `legacy_api(const_cast(str.c_str()));` | #### reinterpret_cast * 无编译时类型安全检查 * 二进制位级重新解释 * 最危险的转换操作符 | 转换类型 | 安全性 | 示例 | |------------------------|--------------|-------------------------------| | 指针↔指针 | ❌ 高危 | `MyClass* obj; void* p=reinterpret_cast(obj);` | | 指针↔整型 | ❌ 高危 | `intptr_t addr=reinterpret_cast(&obj);` | | 函数指针转换 | ❌ 极高危 | 不同签名函数指针转换 | | 内存映射 I/O | ⚠️ 系统编程 | 硬件寄存器访问 | #### bad_cast * `dynamic_cast` 引用转换失败的异常类型。 bad_cast 使用 ```cpp try { Circle& ref_circle = dynamic_cast(ref_shape); } catch (bad_cast b) { cout << "Caught: " << b.what(); } ``` ### 运行时类型信息 (RTTI) #### typeid * typeid 运算符允许在运行时确定对象的类型 * typeid 返回一个 type\_info 对象的引用 * 如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数 * 只能获取对象的实际类型 #### type_info * type_info 类描述编译器在程序中生成的类型信息。 此类的对象可以有效存储指向类型的名称的指针。 type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。 类型的编码规则和排列顺序是未指定的,并且可能因程序而异。 * 头文件:`typeinfo` typeid、type_info 使用 ```cpp #include using namespace std; class Flyable // 能飞的 { public: virtual void takeoff() = 0; // 起飞 virtual void land() = 0; // 降落 }; class Bird : public Flyable // 鸟 { public: void foraging() {...} // 觅食 virtual void takeoff() {...} virtual void land() {...} virtual ~Bird(){} }; class Plane : public Flyable // 飞机 { public: void carry() {...} // 运输 virtual void takeoff() {...} virtual void land() {...} }; class type_info { public: const char* name() const; bool operator == (const type_info & rhs) const; bool operator != (const type_info & rhs) const; int before(const type_info & rhs) const; virtual ~type_info(); private: ... }; void doSomething(Flyable *obj) // 做些事情 { obj->takeoff(); cout << typeid(*obj).name() << endl; // 输出传入对象类型("class Bird" or "class Plane") if(typeid(*obj) == typeid(Bird)) // 判断对象类型 { Bird *bird = dynamic_cast(obj); // 对象转化 bird->foraging(); } obj->land(); } int main(){ Bird *b = new Bird(); doSomething(b); delete b; b = nullptr; return 0; } ``` ## ⭐️ Effective ### Effective C++ 1. 视 C++ 为一个语言联邦(C、Object-Oriented C++、Template C++、STL) 2. 宁可以编译器替换预处理器(尽量以 `const`、`enum`、`inline` 替换 `#define`) 3. 尽可能使用 const 4. 确定对象被使用前已先被初始化(构造时赋值(copy 构造函数)比 default 构造后赋值(copy assignment)效率高) 5. 了解 C++ 默默编写并调用哪些函数(编译器暗自为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符、析构函数) 6. 若不想使用编译器自动生成的函数,就应该明确拒绝(将不想使用的成员函数声明为 private,并且不予实现) 7. 为多态基类声明 virtual 析构函数(如果 class 带有任何 virtual 函数,它就应该拥有一个 virtual 析构函数) 8. 别让异常逃离析构函数(析构函数应该吞下不传播异常,或者结束程序,而不是吐出异常;如果要处理异常应该在非析构的普通函数处理) 9. 绝不在构造和析构过程中调用 virtual 函数(因为这类调用从不下降至 derived class) 10. 令 `operator=` 返回一个 `reference to *this` (用于连锁赋值) 11. 在 `operator=` 中处理 “自我赋值” 12. 赋值对象时应确保复制 “对象内的所有成员变量” 及 “所有 base class 成分”(调用基类复制构造函数) 13. 以对象管理资源(资源在构造函数获得,在析构函数释放,建议使用智能指针,资源取得时机便是初始化时机(Resource Acquisition Is Initialization,RAII)) 14. 在资源管理类中小心 copying 行为(普遍的 RAII class copying 行为是:抑制 copying、引用计数、深度拷贝、转移底部资源拥有权(类似 auto_ptr)) 15. 在资源管理类中提供对原始资源(raw resources)的访问(对原始资源的访问可能经过显式转换或隐式转换,一般而言显示转换比较安全,隐式转换对客户比较方便) 16. 成对使用 new 和 delete 时要采取相同形式(`new` 中使用 `[]` 则 `delete []`,`new` 中不使用 `[]` 则 `delete`) 17. 以独立语句将 newed 对象存储于(置入)智能指针(如果不这样做,可能会因为编译器优化,导致难以察觉的资源泄漏) 18. 让接口容易被正确使用,不易被误用(促进正常使用的办法:接口的一致性、内置类型的行为兼容;阻止误用的办法:建立新类型,限制类型上的操作,约束对象值、消除客户的资源管理责任) 19. 设计 class 犹如设计 type,需要考虑对象创建、销毁、初始化、赋值、值传递、合法值、继承关系、转换、一般化等等。 20. 宁以 pass-by-reference-to-const 替换 pass-by-value (前者通常更高效、避免切割问题(slicing problem),但不适用于内置类型、STL迭代器、函数对象) 21. 必须返回对象时,别妄想返回其 reference(绝不返回 pointer 或 reference 指向一个 local stack 对象,或返回 reference 指向一个 heap-allocated 对象,或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象。) 22. 将成员变量声明为 private(为了封装、一致性、对其读写精确控制等) 23. 宁以 non-member、non-friend 替换 member 函数(可增加封装性、包裹弹性(packaging flexibility)、机能扩充性) 24. 若所有参数(包括被this指针所指的那个隐喻参数)皆须要类型转换,请为此采用 non-member 函数 25. 考虑写一个不抛异常的 swap 函数 26. 尽可能延后变量定义式的出现时间(可增加程序清晰度并改善程序效率) 27. 尽量少做转型动作(旧式:`(T)expression`、`T(expression)`;新式:`const_cast(expression)`、`dynamic_cast(expression)`、`reinterpret_cast(expression)`、`static_cast(expression)`、;尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型) 28. 避免使用 handles(包括 引用、指针、迭代器)指向对象内部(以增加封装性、使 const 成员函数的行为更像 const、降低 “虚吊号码牌”(dangling handles,如悬空指针等)的可能性) 29. 为 “异常安全” 而努力是值得的(异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏,分为三种可能的保证:基本型、强列型、不抛异常型) 30. 透彻了解 inlining 的里里外外(inlining 在大多数 C++ 程序中是编译期的行为;inline 函数是否真正 inline,取决于编译器;大部分编译器拒绝太过复杂(如带有循环或递归)的函数 inlining,而所有对 virtual 函数的调用(除非是最平淡无奇的)也都会使 inlining 落空;inline 造成的代码膨胀可能带来效率损失;inline 函数无法随着程序库的升级而升级) 31. 将文件间的编译依存关系降至最低(如果使用 object references 或 object pointers 可以完成任务,就不要使用 objects;如果能够,尽量以 class 声明式替换 class 定义式;为声明式和定义式提供不同的头文件) 32. 确定你的 public 继承塑模出 is-a(是一种)关系(适用于 base classes 身上的每一件事情一定适用于 derived classes 身上,因为每一个 derived class 对象也都是一个 base class 对象) 33. 避免遮掩继承而来的名字(可使用 using 声明式或转交函数(forwarding functions)来让被遮掩的名字再见天日) 34. 区分接口继承和实现继承(在 public 继承之下,derived classes 总是继承 base class 的接口;pure virtual 函数只具体指定接口继承;非纯 impure virtual 函数具体指定接口继承及缺省实现继承;non-virtual 函数具体指定接口继承以及强制性实现继承) 35. 考虑 virtual 函数以外的其他选择(如 Template Method 设计模式的 non-virtual interface(NVI)手法,将 virtual 函数替换为 “函数指针成员变量”,以 `tr1::function` 成员变量替换 virtual 函数,将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数) 36. 绝不重新定义继承而来的 non-virtual 函数 37. 绝不重新定义继承而来的缺省参数值,因为缺省参数值是静态绑定(statically bound),而 virtual 函数却是动态绑定(dynamically bound) 38. 通过复合塑模 has-a(有一个)或 “根据某物实现出”(在应用域(application domain),复合意味 has-a(有一个);在实现域(implementation domain),复合意味着 is-implemented-in-terms-of(根据某物实现出)) 39. 明智而审慎地使用 private 继承(private 继承意味着 is-implemented-in-terms-of(根据某物实现出),尽可能使用复合,当 derived class 需要访问 protected base class 的成员,或需要重新定义继承而来的时候 virtual 函数,或需要 empty base 最优化时,才使用 private 继承) 40. 明智而审慎地使用多重继承(多继承比单一继承复杂,可能导致新的歧义性,以及对 virtual 继承的需要,但确有正当用途,如 “public 继承某个 interface class” 和 “private 继承某个协助实现的 class”;virtual 继承可解决多继承下菱形继承的二义性问题,但会增加大小、速度、初始化及赋值的复杂度等等成本) 41. 了解隐式接口和编译期多态(class 和 templates 都支持接口(interfaces)和多态(polymorphism);class 的接口是以签名为中心的显式的(explicit),多态则是通过 virtual 函数发生于运行期;template 的接口是奠基于有效表达式的隐式的(implicit),多态则是通过 template 具现化和函数重载解析(function overloading resolution)发生于编译期) 42. 了解 typename 的双重意义(声明 template 类型参数是,前缀关键字 class 和 typename 的意义完全相同;请使用关键字 typename 标识嵌套从属类型名称,但不得在基类列(base class lists)或成员初值列(member initialization list)内以它作为 base class 修饰符) 43. 学习处理模板化基类内的名称(可在 derived class templates 内通过 `this->` 指涉 base class templates 内的成员名称,或藉由一个明白写出的 “base class 资格修饰符” 完成) 44. 将与参数无关的代码抽离 templates(因类型模板参数(non-type template parameters)而造成代码膨胀往往可以通过函数参数或 class 成员变量替换 template 参数来消除;因类型参数(type parameters)而造成的代码膨胀往往可以通过让带有完全相同二进制表述(binary representations)的实现类型(instantiation types)共享实现码) 45. 运用成员函数模板接受所有兼容类型(请使用成员函数模板(member function templates)生成 “可接受所有兼容类型” 的函数;声明 member templates 用于 “泛化 copy 构造” 或 “泛化 assignment 操作” 时还需要声明正常的 copy 构造函数和 copy assignment 操作符) 46. 需要类型转换时请为模板定义非成员函数(当我们编写一个 class template,而它所提供之 “与此 template 相关的” 函数支持 “所有参数之隐式类型转换” 时,请将那些函数定义为 “class template 内部的 friend 函数”) 47. 请使用 traits classes 表现类型信息(traits classes 通过 templates 和 “templates 特化” 使得 “类型相关信息” 在编译期可用,通过重载技术(overloading)实现在编译期对类型执行 if...else 测试) 48. 认识 template 元编程(模板元编程(TMP,template metaprogramming)可将工作由运行期移往编译期,因此得以实现早期错误侦测和更高的执行效率;TMP 可被用来生成 “给予政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码) 49. 了解 new-handler 的行为(set\_new\_handler 允许客户指定一个在内存分配无法获得满足时被调用的函数;nothrow new 是一个颇具局限的工具,因为它只适用于内存分配(operator new),后继的构造函数调用还是可能抛出异常) 50. 了解 new 和 delete 的合理替换时机(为了检测运用错误、收集动态分配内存之使用统计信息、增加分配和归还速度、降低缺省内存管理器带来的空间额外开销、弥补缺省分配器中的非最佳齐位、将相关对象成簇集中、获得非传统的行为) 51. 编写 new 和 delete 时需固守常规(operator new 应该内涵一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就应该调用 new-handler,它也应该有能力处理 0 bytes 申请,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”;operator delete 应该在收到 null 指针时不做任何事,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”) 52. 写了 placement new 也要写 placement delete(当你写一个 placement operator new,请确定也写出了对应的 placement operator delete,否则可能会发生隐微而时断时续的内存泄漏;当你声明 placement new 和 placement delete,请确定不要无意识(非故意)地遮掩了它们地正常版本) 53. 不要轻忽编译器的警告 54. 让自己熟悉包括 TR1 在内的标准程序库(TR1,C++ Technical Report 1,C++11 标准的草稿文件) 55. 让自己熟悉 Boost(准标准库) ### More Effective c++ 1. 仔细区别 pointers 和 references(当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由 pointers 达成,你就应该选择 references;任何其他时候,请采用 pointers) 2. 最好使用 C++ 转型操作符(`static_cast`、`const_cast`、`dynamic_cast`、`reinterpret_cast`) 3. 绝不要以多态(polymorphically)方式处理数组(多态(polymorphism)和指针算术不能混用;数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用) 4. 非必要不提供 default constructor(避免对象中的字段被无意义地初始化) 5. 对定制的 “类型转换函数” 保持警觉(单自变量 constructors 可通过简易法(explicit 关键字)或代理类(proxy classes)来避免编译器误用;隐式类型转换操作符可改为显式的 member function 来避免非预期行为) 6. 区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式(前置式累加后取出,返回一个 reference;后置式取出后累加,返回一个 const 对象;处理用户定制类型时,应该尽可能使用前置式 increment;后置式的实现应以其前置式兄弟为基础) 7. 千万不要重载 `&&`,`||` 和 `,` 操作符(`&&` 与 `||` 的重载会用 “函数调用语义” 取代 “骤死式语义”;`,` 的重载导致不能保证左侧表达式一定比右侧表达式更早被评估) 8. 了解各种不同意义的 new 和 delete(`new operator`、`operator new`、`placement new`、`operator new[]`;`delete operator`、`operator delete`、`destructor`、`operator delete[]`) 9. 利用 destructors 避免泄漏资源(在 destructors 释放资源可以避免异常时的资源泄漏) 10. 在 constructors 内阻止资源泄漏(由于 C++ 只会析构已构造完成的对象,因此在构造函数可以使用 try...catch 或者 auto_ptr(以及与之相似的 classes) 处理异常时资源泄露问题) 11. 禁止异常流出 destructors 之外(原因:一、避免 terminate 函数在 exception 传播过程的栈展开(stack-unwinding)机制种被调用;二、协助确保 destructors 完成其应该完成的所有事情) 12. 了解 “抛出一个 exception” 与 “传递一个参数” 或 “调用一个虚函数” 之间的差异(第一,exception objects 总是会被复制(by pointer 除外),如果以 by value 方式捕捉甚至被复制两次,而传递给函数参数的对象则不一定得复制;第二,“被抛出成为 exceptions” 的对象,其被允许的类型转换动作比 “被传递到函数去” 的对象少;第三,catch 子句以其 “出现于源代码的顺序” 被编译器检验对比,其中第一个匹配成功者便执行,而调用一个虚函数,被选中执行的是那个 “与对象类型最佳吻合” 的函数) 13. 以 by reference 方式捕获 exceptions(可避免对象删除问题、exception objects 的切割问题,可保留捕捉标准 exceptions 的能力,可约束 exception object 需要复制的次数) 14. 明智运用 exception specifications(exception specifications 对 “函数希望抛出什么样的 exceptions” 提供了卓越的说明;也有一些缺点,包括编译器只对它们做局部性检验而很容易不经意地违反,与可能会妨碍更上层的 exception 处理函数处理未预期的 exceptions) 15. 了解异常处理的成本(粗略估计,如果使用 try 语句块,代码大约整体膨胀 5%-10%,执行速度亦大约下降这个数;因此请将你对 try 语句块和 exception specifications 的使用限制于非用不可的地点,并且在真正异常的情况下才抛出 exceptions) 16. 谨记 80-20 法则(软件的整体性能几乎总是由其构成要素(代码)的一小部分决定的,可使用程序分析器(program profiler)识别出消耗资源的代码) 17. 考虑使用 lazy evaluation(缓式评估)(可应用于:Reference Counting(引用计数)来避免非必要的对象复制、区分 operator[] 的读和写动作来做不同的事情、Lazy Fetching(缓式取出)来避免非必要的数据库读取动作、Lazy Expression Evaluation(表达式缓评估)来避免非必要的数值计算动作) 18. 分期摊还预期的计算成本(当你必须支持某些运算而其结构几乎总是被需要,或其结果常常被多次需要的时候,over-eager evaluation(超急评估)可以改善程序效率) ### Google C++ Style Guide * 英文:[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) * 中文:[C++ 风格指南](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/) ### 其他 * [Bjarne Stroustrup 的常见问题](http://www.stroustrup.com/bs_faq.html) * [Bjarne Stroustrup 的 C++ 风格和技巧常见问题](http://www.stroustrup.com/bs_faq2.html) ## 📦 STL ### STL 索引 [STL 方法含义索引](https://github.com/huihut/interview/tree/master/STL) ### STL 容器 容器 | 底层数据结构 | 时间复杂度 | 有无序 | 可不可重复 | 其他 ---|---|---|---|---|--- [array](https://github.com/huihut/interview/tree/master/STL#array)|数组|随机读改 O(1)|无序|可重复|支持随机访问 [vector](https://github.com/huihut/interview/tree/master/STL#vector)|数组|随机读改、尾部插入、尾部删除 O(1)
头部插入、头部删除 O(n)|无序|可重复|支持随机访问 [deque](https://github.com/huihut/interview/tree/master/STL#deque)|双端队列|头尾插入、头尾删除 O(1)|无序|可重复|一个中央控制器 + 多个缓冲区,支持首尾快速增删,支持随机访问 [forward_list](https://github.com/huihut/interview/tree/master/STL#forward_list)|单向链表|插入、删除 O(1)|无序|可重复|不支持随机访问 [list](https://github.com/huihut/interview/tree/master/STL#list)|双向链表|插入、删除 O(1)|无序|可重复|不支持随机访问 [stack](https://github.com/huihut/interview/tree/master/STL#stack)|deque / list|顶部插入、顶部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 [queue](https://github.com/huihut/interview/tree/master/STL#queue)|deque / list|尾部插入、头部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 [priority_queue](https://github.com/huihut/interview/tree/master/STL#priority_queue)|vector + max-heap|插入、删除 O(log2n)|有序|可重复|vector容器+heap处理规则 [set](https://github.com/huihut/interview/tree/master/STL#set)|红黑树|插入、删除、查找 O(log2n)|有序|不可重复| [multiset](https://github.com/huihut/interview/tree/master/STL#multiset)|红黑树|插入、删除、查找 O(log2n)|有序|可重复| [map](https://github.com/huihut/interview/tree/master/STL#map)|红黑树|插入、删除、查找 O(log2n)|有序|不可重复| [multimap](https://github.com/huihut/interview/tree/master/STL#multimap)|红黑树|插入、删除、查找 O(log2n)|有序|可重复| [unordered_set](https://github.com/huihut/interview/tree/master/STL#unordered_set)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复| [unordered_multiset](https://github.com/huihut/interview/tree/master/STL#unordered_multiset)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复| [unordered_map](https://github.com/huihut/interview/tree/master/STL#unordered_map)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复| [unordered_multimap](https://github.com/huihut/interview/tree/master/STL#unordered_multimap)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复| ### STL 算法 算法 | 底层算法 | 时间复杂度 | 可不可重复 ---|---|---|--- [find](http://www.cplusplus.com/reference/algorithm/find/)|顺序查找|O(n)|可重复 [sort](https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808)|[内省排序](https://en.wikipedia.org/wiki/Introsort)|O(n*log2n)|可重复 ## 〽️ 数据结构 ### 顺序结构 #### 顺序栈(Sequence Stack) [SqStack.cpp](DataStructure/SqStack.cpp) 顺序栈数据结构和图片 ```cpp typedef struct { ElemType *elem; int top; int size; int increment; } SqStack; ``` ![](https://gitee.com/huihut/interview/raw/master/images/SqStack.png) #### 队列(Sequence Queue) 队列数据结构 ```cpp typedef struct { ElemType * elem; int front; int rear; int maxSize; }SqQueue; ``` ##### 非循环队列 非循环队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqQueue.png) `SqQueue.rear++` ##### 循环队列 循环队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqLoopStack.png) `SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize` #### 顺序表(Sequence List) [SqList.cpp](DataStructure/SqList.cpp) 顺序表数据结构和图片 ```cpp typedef struct { ElemType *elem; int length; int size; int increment; } SqList; ``` ![](https://gitee.com/huihut/interview/raw/master/images/SqList.png) ### 链式结构 [LinkList.cpp](DataStructure/LinkList.cpp) [LinkList_with_head.cpp](DataStructure/LinkList_with_head.cpp) 链式数据结构 ```cpp typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; ``` #### 链队列(Link Queue) 链队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkQueue.png) #### 线性表的链式表示 ##### 单链表(Link List) 单链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkList.png) ##### 双向链表(Du-Link-List) 双向链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/DuLinkList.png) ##### 循环链表(Cir-Link-List) 循环链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/CirLinkList.png) ### 哈希表 [HashTable.cpp](DataStructure/HashTable.cpp) #### 概念 哈希函数:`H(key): K -> D , key ∈ K` #### 构造方法 * 直接定址法 * 除留余数法 * 数字分析法 * 折叠法 * 平方取中法 #### 冲突处理方法 * 链地址法:key 相同的用单链表链接 * 开放定址法 * 线性探测法:key 相同 -> 放到 key 的下一个位置,`Hi = (H(key) + i) % m` * 二次探测法:key 相同 -> 放到 `Di = 1^2, -1^2, ..., ±(k)^2,(k<=m/2)` * 随机探测法:`H = (H(key) + 伪随机数) % m` #### 线性探测的哈希表数据结构 线性探测的哈希表数据结构和图片 ```cpp typedef char KeyType; typedef struct { KeyType key; }RcdType; typedef struct { RcdType *rcd; int size; int count; bool *tag; }HashTable; ``` ![](https://gitee.com/huihut/interview/raw/master/images/HashTable.png) ### 递归 #### 概念 函数直接或间接地调用自身 #### 递归与分治 * 分治法 * 问题的分解 * 问题规模的分解 * 折半查找(递归) * 归并排序(递归) * 快速排序(递归) #### 递归与迭代 * 迭代:反复利用变量旧值推出新值 * 折半查找(迭代) * 归并排序(迭代) #### 广义表 ##### 头尾链表存储表示 广义表的头尾链表存储表示和图片 ```cpp // 广义表的头尾链表存储表示 typedef enum {ATOM, LIST} ElemTag; // ATOM==0:原子,LIST==1:子表 typedef struct GLNode { ElemTag tag; // 公共部分,用于区分原子结点和表结点 union { // 原子结点和表结点的联合部分 AtomType atom; // atom 是原子结点的值域,AtomType 由用户定义 struct { struct GLNode *hp, *tp; } ptr; // ptr 是表结点的指针域,prt.hp 和 ptr.tp 分别指向表头和表尾 } a; } *GList, GLNode; ``` ![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList1.png) ##### 扩展线性链表存储表示 扩展线性链表存储表示和图片 ```cpp // 广义表的扩展线性链表存储表示 typedef enum {ATOM, LIST} ElemTag; // ATOM==0:原子,LIST==1:子表 typedef struct GLNode1 { ElemTag tag; // 公共部分,用于区分原子结点和表结点 union { // 原子结点和表结点的联合部分 AtomType atom; // 原子结点的值域 struct GLNode1 *hp; // 表结点的表头指针 } a; struct GLNode1 *tp; // 相当于线性链表的 next,指向下一个元素结点 } *GList1, GLNode1; ``` ![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList2.png) ### 二叉树 [BinaryTree.cpp](DataStructure/BinaryTree.cpp) #### 性质 1. 非空二叉树第 i 层最多 2(i-1) 个结点 (i >= 1) 2. 深度为 k 的二叉树最多 2k - 1 个结点 (k >= 1) 3. 度为 0 的结点数为 n0,度为 2 的结点数为 n2,则 n0 = n2 + 1 4. 有 n 个结点的完全二叉树深度 k = ⌊ log2(n) ⌋ + 1 5. 对于含 n 个结点的完全二叉树中编号为 i (1 <= i <= n) 的结点 1. 若 i = 1,为根,否则双亲为 ⌊ i / 2 ⌋ 2. 若 2i > n,则 i 结点没有左孩子,否则孩子编号为 2i 3. 若 2i + 1 > n,则 i 结点没有右孩子,否则孩子编号为 2i + 1 #### 存储结构 二叉树数据结构 ```cpp typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; ``` ##### 顺序存储 二叉树顺序存储图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqBinaryTree.png) ##### 链式存储 二叉树链式存储图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkBinaryTree.png) #### 遍历方式 * 先序遍历 * 中序遍历 * 后续遍历 * 层次遍历 #### 分类 * 满二叉树 * 完全二叉树(堆) * 大顶堆:根 >= 左 && 根 >= 右 * 小顶堆:根 <= 左 && 根 <= 右 * 二叉查找树(二叉排序树):左 < 根 < 右 * 平衡二叉树(AVL树):| 左子树树高 - 右子树树高 | <= 1 * 最小失衡树:平衡二叉树插入新结点导致失衡的子树:调整: * LL型:根的左孩子右旋 * RR型:根的右孩子左旋 * LR型:根的左孩子左旋,再右旋 * RL型:右孩子的左子树,先右旋,再左旋 ### 其他树及森林 #### 树的存储结构 * 双亲表示法 * 双亲孩子表示法 * 孩子兄弟表示法 #### 并查集 一种不相交的子集所构成的集合 S = {S1, S2, ..., Sn} #### 平衡二叉树(AVL树) ##### 性质 * | 左子树树高 - 右子树树高 | <= 1 * 平衡二叉树必定是二叉搜索树,反之则不一定 * 最小二叉平衡树的节点的公式:`F(n)=F(n-1)+F(n-2)+1` (1 是根节点,F(n-1) 是左子树的节点数量,F(n-2) 是右子树的节点数量) 平衡二叉树图片 ![](https://gitee.com/huihut/interview/raw/master/images/Self-balancingBinarySearchTree.png) ##### 最小失衡树 平衡二叉树插入新结点导致失衡的子树 调整: * LL 型:根的左孩子右旋 * RR 型:根的右孩子左旋 * LR 型:根的左孩子左旋,再右旋 * RL 型:右孩子的左子树,先右旋,再左旋 #### 红黑树 [RedBlackTree.cpp](DataStructure/RedBlackTree.cpp) ##### 红黑树的特征是什么? 1. 节点是红色或黑色。 2. 根是黑色。 3. 所有叶子都是黑色(叶子是 NIL 节点)。 4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)(新增节点的父节点必须相同) 5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。(新增节点必须为红) ##### 调整 1. 变色 2. 左旋 3. 右旋 ##### 应用 * 关联数组:如 STL 中的 map、set ##### 红黑树、B 树、B+ 树的区别? * 红黑树的深度比较大,而 B 树和 B+ 树的深度则相对要小一些 * B+ 树则将数据都保存在叶子节点,同时通过链表的形式将他们连接在一起。 #### B 树(B-tree)、B+ 树(B+-tree) B 树、B+ 树图片 ![B 树(B-tree)、B+ 树(B+-tree)](https://i.stack.imgur.com/l6UyF.png) ##### 特点 * 一般化的二叉查找树(binary search tree) * “矮胖”,内部(非叶子)节点可以拥有可变数量的子节点(数量范围预先定义好) ##### 应用 * 大部分文件系统、数据库系统都采用B树、B+树作为索引结构 ##### 区别 * B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中。 * B+树中所有叶子节点都是通过指针连接在一起,而B树不会。 ##### B树的优点 对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。 ##### B+树的优点 * 非叶子节点不会带上 ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。 * 叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。 > B 树、B+ 树区别来自:[differences-between-b-trees-and-b-trees](https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees)、[B树和B+树的区别](https://www.cnblogs.com/ivictor/p/5849061.html) #### 八叉树 八叉树图片 ![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png) 八叉树(octree),或称八元树,是一种用于描述三维空间(划分空间)的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。 ##### 用途 * 三维计算机图形 * 最邻近搜索 ## ⚡️ 算法 ### 排序 排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 ---|---|---|---|--- [冒泡排序](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|稳定 [选择排序](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|数组不稳定、链表稳定 [插入排序](Algorithm/InsertSort.h) | O(n2)|O(n2)|O(1)|稳定 [快速排序](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n) | 不稳定 [堆排序](Algorithm/HeapSort.cpp) | O(n*log2n)|O(n*log2n)|O(1)|不稳定 [归并排序](Algorithm/MergeSort.h) | O(n*log2n) | O(n*log2n)|O(n)|稳定 [希尔排序](Algorithm/ShellSort.h) | O(n*log2n)|O(n2)|O(1)|不稳定 [计数排序](Algorithm/CountSort.cpp) | O(n+m)|O(n+m)|O(n+m)|稳定 [桶排序](Algorithm/BucketSort.cpp) | O(n)|O(n)|O(m)|稳定 [基数排序](Algorithm/RadixSort.h) | O(k*n)|O(n2)| |稳定 > * 均按从小到大排列 > * k:代表数值中的 “数位” 个数 > * n:代表数据规模 > * m:代表数据的最大值减最小值 > * 来自:[wikipedia . 排序算法](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95) ### 查找 查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件 ---|---|---|--- [顺序查找](Algorithm/SequentialSearch.h) | O(n) | O(1) | 无序或有序 [二分查找(折半查找)](Algorithm/BinarySearch.h) | O(log2n)| O(1) | 有序 [插值查找](Algorithm/InsertionSearch.h) | O(log2(log2n)) | O(1) | 有序 [斐波那契查找](Algorithm/FibonacciSearch.cpp) | O(log2n) | O(1) | 有序 [哈希查找](DataStructure/HashTable.cpp) | O(1) | O(n) | 无序或有序 [二叉查找树(二叉搜索树查找)](Algorithm/BSTSearch.h) |O(log2n) | | [红黑树](DataStructure/RedBlackTree.cpp) |O(log2n) | | 2-3树 | O(log2n - log3n) | | B树/B+树 |O(log2n) | | ### 图搜索算法 图搜索算法 |数据结构| 遍历时间复杂度 | 空间复杂度 ---|---|---|--- [BFS广度优先搜索](https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵
邻接链表|O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) [DFS深度优先搜索](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵
邻接链表|O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) ### 其他算法 算法 |思想| 应用 ---|---|--- [分治法](https://zh.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95)|把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并|[循环赛日程安排问题](https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem)、排序算法(快速排序、归并排序) [动态规划](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92)|通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,适用于有重叠子问题和最优子结构性质的问题|[背包问题](https://github.com/huihut/interview/tree/master/Problems/KnapsackProblem)、斐波那契数列 [贪心法](https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95)|一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法|旅行推销员问题(最短路径问题)、最小生成树、哈夫曼编码 ## ❓ Problems ### Single Problem * [Chessboard Coverage Problem(棋盘覆盖问题)](Problems/ChessboardCoverageProblem) * [Knapsack Problem(背包问题)](Problems/KnapsackProblem) * [Neumann Neighbor Problem(冯诺依曼邻居问题)](Problems/NeumannNeighborProblem) * [Round Robin Problem(循环赛日程安排问题)](Problems/RoundRobinProblem) * [Tubing Problem(输油管道问题)](Problems/TubingProblem) ### Leetcode Problems * [Github . haoel/leetcode](https://github.com/haoel/leetcode) * [Github . pezy/LeetCode](https://github.com/pezy/LeetCode) ### 剑指 Offer * [Github . zhedahht/CodingInterviewChinese2](https://github.com/zhedahht/CodingInterviewChinese2) * [Github . gatieme/CodingInterviews](https://github.com/gatieme/CodingInterviews) ### Cracking the Coding Interview 程序员面试金典 * [Github . careercup/ctci](https://github.com/careercup/ctci) * [牛客网 . 程序员面试金典](https://www.nowcoder.com/ta/cracking-the-coding-interview) ### 牛客网 * [牛客网 . 在线编程专题](https://www.nowcoder.com/activity/oj) ## 💻 操作系统 ### 进程与线程 对于有线程系统: * 进程是资源分配的独立单位 * 线程是资源调度的独立单位 对于无线程系统: * 进程是资源调度、分配的独立单位 #### 进程之间的通信方式以及优缺点 * 管道(PIPE) * 有名管道(命名管道):一种先进先出的通信方式,它允许无亲缘关系进程间的通信 * 优点:可以实现任意关系的进程间的通信 * 缺点: 1. 长期存于系统中,使用不当容易出错 2. 缓冲区有限 * 无名管道(匿名管道):一种单工先进先出的通信方式,只能在具有亲缘关系的进程间使用(父子进程) * 优点:简单方便 * 缺点: 1. 局限于单向通信 2. 只能创建在它的进程以及其有亲缘关系的进程之间 3. 缓冲区有限 * 信号量(Semaphore):一个计数器,可以用来控制多个线程对共享资源的访问 * 优点:可以同步进程 * 缺点:信号量有限 * 信号(Signal):一种比较复杂的通信方式,用于通知接收进程某个事件已经发生 * 消息队列(Message Queue):是消息的链表,存放在内核中并由消息队列标识符标识 * 优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便 * 缺点:信息的复制需要额外消耗 CPU 的时间,不适宜于信息量大或操作频繁的场合 * 共享内存(Shared Memory):映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问 * 优点:无须复制,快捷,信息量大 * 缺点: 1. 通信是通过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,因此进程间的读写操作的同步问题 2. 利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信 * 套接字(Socket):可用于不同计算机间的进程通信 * 优点: 1. 传输数据为字节级,传输数据可自定义,数据量小效率高 2. 传输数据时间短,性能高 3. 适合于客户端和服务器端之间信息实时交互 4. 可以加密,数据安全性强 * 缺点:需对传输的数据进行解析,转化成应用级的数据。 #### 线程之间的通信方式 * 锁机制:包括互斥锁/量(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)、条件变量(condition) * 互斥锁/量(mutex):提供了以排他方式防止数据结构被并发修改的方法。 * 读写锁(reader-writer lock):允许多个线程同时读共享数据,而对写操作是互斥的。 * 自旋锁(spin lock)与互斥锁类似,都是为了保护共享资源。互斥锁是当资源被占用,申请者进入睡眠状态;而自旋锁则循环检测保持者是否已经释放锁。 * 条件变量(condition):可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。 * 信号量机制(Semaphore) * 无名线程信号量 * 命名线程信号量 * 信号机制(Signal):类似进程间的信号处理 * 屏障(barrier):屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。 线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制 > 进程之间的通信方式以及优缺点来源于:[进程线程面试题总结](http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977) #### 进程之间私有和共享的资源 * 私有:地址空间、堆、全局变量、栈、寄存器 * 共享:代码段,公共数据,进程目录,进程 ID #### 线程之间私有和共享的资源 * 私有:线程栈,寄存器,程序计数器 * 共享:堆,地址空间,全局变量,静态变量 #### 多进程与多线程间的对比、优劣与选择 ##### 对比 对比维度 | 多进程 | 多线程 | 总结 ---|---|---|--- 数据共享、同步|数据共享复杂,需要用 IPC;数据是分开的,同步简单|因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂|各有优势 内存、CPU|占用内存多,切换复杂,CPU 利用率低|占用内存少,切换简单,CPU 利用率高|线程占优 创建销毁、切换|创建销毁、切换复杂,速度慢|创建销毁、切换简单,速度很快|线程占优 编程、调试|编程简单,调试简单|编程复杂,调试复杂|进程占优 可靠性|进程间不会互相影响|一个线程挂掉将导致整个进程挂掉|进程占优 分布式|适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单|适应于多核分布式|进程占优 ##### 优劣 优劣|多进程|多线程 ---|---|--- 优点|编程、调试简单,可靠性较高|创建、销毁、切换速度快,内存、资源占用小 缺点|创建、销毁、切换速度慢,内存、资源占用大|编程、调试复杂,可靠性较差 ##### 选择 * 需要频繁创建销毁的优先用线程 * 需要进行大量计算的优先使用线程 * 强相关的处理用线程,弱相关的处理用进程 * 可能要扩展到多机分布的用进程,多核分布的用线程 * 都满足需求的情况下,用你最熟悉、最拿手的方式 > 多进程与多线程间的对比、优劣与选择来自:[多线程还是多进程的选择及区别](https://blog.csdn.net/lishenglong666/article/details/8557215) ### Linux 内核的同步方式 #### 原因 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。 #### 同步方式 * 原子操作 * 信号量(semaphore) * 读写信号量(rw_semaphore) * 自旋锁(spinlock) * 大内核锁(BKL,Big Kernel Lock) * 读写锁(rwlock) * 大读者锁(brlock-Big Reader Lock) * 读-拷贝修改(RCU,Read-Copy Update) * 顺序锁(seqlock) > 来自:[Linux 内核的同步机制,第 1 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part1/)、[Linux 内核的同步机制,第 2 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part2/) ### 死锁 #### 原因 * 系统资源不足 * 资源分配不当 * 进程运行推进顺序不合适 #### 产生条件 * 互斥 * 请求和保持 * 不剥夺 * 环路 #### 预防 * 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。 * 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。 * 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。 * 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。 * 有序资源分配法 * 银行家算法 ### 文件系统 * Windows:FCB 表 + FAT + 位图 * Unix:inode + 混合索引 + 成组链接 ### 主机字节序与网络字节序 #### 主机字节序(CPU 字节序) ##### 概念 主机字节序又叫 CPU 字节序,其不是由操作系统决定的,而是由 CPU 指令集架构决定的。主机字节序分为两种: * 大端字节序(Big Endian):高序字节存储在低位地址,低序字节存储在高位地址 * 小端字节序(Little Endian):高序字节存储在高位地址,低序字节存储在低位地址 ##### 存储方式 32 位整数 `0x12345678` 是从起始位置为 `0x00` 的地址开始存放,则: 内存地址 | 0x00 | 0x01 | 0x02 | 0x03 ---|---|---|---|--- 大端|12|34|56|78 小端|78|56|34|12 大端小端图片 ![大端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Big-Endian.svg.png) ![小端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Little-Endian.svg.png) ##### 判断大端小端 判断大端小端 可以这样判断自己 CPU 字节序是大端还是小端: ```cpp #include using namespace std; int main() { int i = 0x12345678; if (*((char*)&i) == 0x12) cout << "大端" << endl; else cout << "小端" << endl; return 0; } ``` ##### 各架构处理器的字节序 * x86(Intel、AMD)、MOS Technology 6502、Z80、VAX、PDP-11 等处理器为小端序; * Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除 V9 外)等处理器为大端序; * ARM(默认小端序)、PowerPC(除 PowerPC 970 外)、DEC Alpha、SPARC V9、MIPS、PA-RISC 及 IA64 的字节序是可配置的。 #### 网络字节序 网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。 网络字节顺序采用:大端(Big Endian)排列方式。 ### 页面置换算法 在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。 #### 分类 * 全局置换:在整个内存空间置换 * 局部置换:在本进程中进行置换 #### 算法 全局: * 工作集算法 * 缺页率置换算法 局部: * 最佳置换算法(OPT) * 先进先出置换算法(FIFO) * 最近最久未使用(LRU)算法 * 时钟(Clock)置换算法 ## ☁️ 计算机网络 > 本节部分知识点来自《计算机网络(第 7 版)》 计算机网络体系结构: ![计算机网络体系结构](https://gitee.com/huihut/interview/raw/master/images/计算机网络体系结构.png) ### 各层作用及协议 分层 | 作用 | 协议 ---|---|--- 物理层 | 通过媒介传输比特,确定机械及电气规范(比特 Bit) | RJ45、CLOCK、IEEE802.3(中继器,集线器) 数据链路层|将比特组装成帧和点到点的传递(帧 Frame)| PPP、FR、HDLC、VLAN、MAC(网桥,交换机) 网络层|负责数据包从源到宿的传递和网际互连(包 Packet)|IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP(路由器) 运输层|提供端到端的可靠报文传递和错误恢复( 段Segment)|TCP、UDP、SPX 会话层|建立、管理和终止会话(会话协议数据单元 SPDU)|NFS、SQL、NETBIOS、RPC 表示层|对数据进行翻译、加密和压缩(表示协议数据单元 PPDU)|JPEG、MPEG、ASII 应用层|允许访问OSI环境的手段(应用协议数据单元 APDU)|FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS ### 物理层 * 传输数据的单位:比特 * 数据传输系统:源系统(源点、发送器) --> 传输系统 --> 目的系统(接收器、终点) 通道: * 单向通道(单工通道):只有一个方向通信,没有反方向交互,如广播 * 双向交替通信(半双工通信):通信双方都可发消息,但不能同时发送或接收 * 双向同时通信(全双工通信):通信双方可以同时发送和接收信息 通道复用技术: * 频分复用(FDM,Frequency Division Multiplexing):不同用户在不同频带,所用用户在同样时间占用不同带宽资源 * 时分复用(TDM,Time Division Multiplexing):不同用户在同一时间段的不同时间片,所有用户在不同时间占用同样的频带宽度 * 波分复用(WDM,Wavelength Division Multiplexing):光的频分复用 * 码分复用(CDM,Code Division Multiplexing):不同用户使用不同的码,可以在同样时间使用同样频带通信 ### 数据链路层 主要信道: * 点对点信道 * 广播信道 #### 点对点信道 * 数据单元:帧 三个基本问题: * 封装成帧:把网络层的 IP 数据报封装成帧,`SOH - 数据部分 - EOT` * 透明传输:不管数据部分什么字符,都能传输出去;可以通过字节填充方法解决(冲突字符前加转义字符) * 差错检测:降低误码率(BER,Bit Error Rate),广泛使用循环冗余检测(CRC,Cyclic Redundancy Check) 点对点协议(Point-to-Point Protocol): * 点对点协议(Point-to-Point Protocol):用户计算机和 ISP 通信时所使用的协议 #### 广播信道 广播通信: * 硬件地址(物理地址、MAC 地址) * 单播(unicast)帧(一对一):收到的帧的 MAC 地址与本站的硬件地址相同 * 广播(broadcast)帧(一对全体):发送给本局域网上所有站点的帧 * 多播(multicast)帧(一对多):发送给本局域网上一部分站点的帧 ### 网络层 * IP(Internet Protocol,网际协议)是为计算机网络相互连接进行通信而设计的协议。 * ARP(Address Resolution Protocol,地址解析协议) * ICMP(Internet Control Message Protocol,网际控制报文协议) * IGMP(Internet Group Management Protocol,网际组管理协议) #### IP 网际协议 IP 地址分类: * `IP 地址 ::= {<网络号>,<主机号>}` IP 地址类别 | 网络号 | 网络范围 | 主机号 | IP 地址范围 ---|---|---|---|--- A 类 | 8bit,第一位固定为 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255 B 类 | 16bit,前两位固定为 10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255 C 类 | 24bit,前三位固定为 110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255 D 类 | 前四位固定为 1110,后面为多播地址 E 类 | 前五位固定为 11110,后面保留为今后所用 IP 数据报格式: ![IP 数据报格式](https://gitee.com/huihut/interview/raw/master/images/IP数据报格式.png) #### ICMP 网际控制报文协议 ICMP 报文格式: ![ICMP 报文格式](https://gitee.com/huihut/interview/raw/master/images/ICMP报文格式.png) 应用: * PING(Packet InterNet Groper,分组网间探测)测试两个主机之间的连通性 * TTL(Time To Live,生存时间)该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量 #### 内部网关协议 * RIP(Routing Information Protocol,路由信息协议) * OSPF(Open Shortest Path First,开放最短路径优先) #### 外部网关协议 * BGP(Border Gateway Protocol,边界网关协议) #### IP多播 * IGMP(Internet Group Management Protocol,网际组管理协议) * 多播路由选择协议 #### VPN 和 NAT * VPN(Virtual Private Network,虚拟专用网) * NAT(Network Address Translation,网络地址转换) #### 路由表包含什么? 1. 网络 ID(Network ID, Network number):就是目标地址的网络 ID。 2. 子网掩码(subnet mask):用来判断 IP 所属网络 3. 下一跳地址/接口(Next hop / interface):就是数据在发送到目标地址的旅途中下一站的地址。其中 interface 指向 next hop(即为下一个 route)。一个自治系统(AS, Autonomous system)中的 route 应该包含区域内所有的子网络,而默认网关(Network id: `0.0.0.0`, Netmask: `0.0.0.0`)指向自治系统的出口。 根据应用和执行的不同,路由表可能含有如下附加信息: 1. 花费(Cost):就是数据发送过程中通过路径所需要的花费。 2. 路由的服务质量 3. 路由中需要过滤的出/入连接列表 ### 运输层 协议: * TCP(Transmission Control Protocol,传输控制协议) * UDP(User Datagram Protocol,用户数据报协议) 端口: 应用程序 | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP --- | --- | --- |--- |--- |--- |--- |--- |--- 端口号 | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161 #### TCP * TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,其传输的单位是报文段。 特征: * 面向连接 * 端到端通信 * 可靠交互 * 全双工通信 * 面向字节流 TCP 如何保证可靠传输: * 确认和超时重传 * 数据合理分片和排序 * 流量控制 * 拥塞控制 * 数据校验 TCP 报文结构 ![TCP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP报文.png) TCP 首部 ![TCP 首部](https://gitee.com/huihut/interview/raw/master/images/TCP首部.png) TCP:状态控制码(Code,Control Flag),占 6 比特,含义如下: * URG:紧急比特(urgent),当 `URG=1` 时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。 * ACK:确认比特(Acknowledge)。只有当 `ACK=1` 时确认号字段才有效,代表这个封包为确认封包。当 `ACK=0` 时,确认号无效。 * PSH:(Push function)若为 1 时,代表要求对方立即传送缓冲区内的其他对应封包,而无需等缓冲满了才送。 * RST:复位比特(Reset),当 `RST=1` 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。 * SYN:同步比特(Synchronous),SYN 置为 1,就表示这是一个连接请求或连接接受报文,通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。 * FIN:终止比特(Final),用来释放一个连接。当 `FIN=1` 时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。 #### UDP * UDP(User Datagram Protocol,用户数据报协议)是 OSI(Open System Interconnection 开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,其传输的单位是用户数据报。 特征: * 无连接 * 尽最大努力交付 * 面向报文 * 没有拥塞控制 * 支持一对一、一对多、多对一、多对多的交互通信 * 首部开销小 UDP 报文结构 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/UDP报文.png) UDP 首部 ![UDP 首部](https://gitee.com/huihut/interview/raw/master/images/UDP首部.png) > TCP/UDP 图片来源于: #### TCP 与 UDP 的区别 1. TCP 面向连接,UDP 是无连接的; 2. TCP 提供可靠的服务,也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付 3. TCP 的逻辑通信信道是全双工的可靠信道;UDP 则是不可靠信道 5. 每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信 6. TCP 面向字节流(可能出现黏包问题),实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的(不会出现黏包问题) 7. UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等) 8. TCP 首部开销20字节;UDP 的首部开销小,只有 8 个字节 #### TCP 黏包问题 ##### 原因 TCP 是一个基于字节流的传输服务(UDP 基于报文的),“流” 意味着 TCP 所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况。 ##### 解决 * 发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。 * 包头加上包体长度。包头是定长的 4 个字节,说明了包体的长度。接收对等方先接收包头长度,依据包头长度来接收包体。 * 在数据包之间设置边界,如添加特殊符号 `\r\n` 标记。FTP 协议正是这么做的。但问题在于如果数据正文中也含有 `\r\n`,则会误判为消息的边界。 * 使用更加复杂的应用层协议。 #### TCP 流量控制 ##### 概念 流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。 ##### 方法 利用可变窗口进行流量控制 ![](https://gitee.com/huihut/interview/raw/master/images/利用可变窗口进行流量控制举例.png) #### TCP 拥塞控制 ##### 概念 拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。 ##### 方法 * 慢开始( slow-start ) * 拥塞避免( congestion avoidance ) * 快重传( fast retransmit ) * 快恢复( fast recovery ) TCP的拥塞控制图 ![](https://gitee.com/huihut/interview/raw/master/images/TCP拥塞窗口cwnd在拥塞控制时的变化情况.png) ![](https://gitee.com/huihut/interview/raw/master/images/快重传示意图.png) ![](https://gitee.com/huihut/interview/raw/master/images/TCP的拥塞控制流程图.png) #### TCP 传输连接管理 > 因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述: ##### TCP 三次握手建立连接 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP三次握手建立连接.png) 【TCP 建立连接全过程解释】 1. 客户端发送 SYN 给服务器,说明客户端请求建立连接; 2. 服务端收到客户端发的 SYN,并回复 SYN+ACK 给客户端(同意建立连接); 3. 客户端收到服务端的 SYN+ACK 后,回复 ACK 给服务端(表示客户端收到了服务端发的同意报文); 4. 服务端收到客户端的 ACK,连接已建立,可以数据传输。 ##### TCP 为什么要进行三次握手? 【答案一】因为信道不可靠,而 TCP 想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值。(而 UDP 则不需建立可靠传输,因此 UDP 不需要三次握手。) > [Google Groups . TCP 建立连接为什么是三次握手?{技术}{网络通信}](https://groups.google.com/forum/#!msg/pongba/kF6O7-MFxM0/5S7zIJ4yqKUJ) 【答案二】因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信。 > [知乎 . TCP 为什么是三次握手,而不是两次或四次?](https://www.zhihu.com/question/24853633/answer/115173386) 【答案三】为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。 > [《计算机网络(第 7 版)-谢希仁》](https://gitee.com/huihut/interview/raw/master/images/TCP-transport-connection-management.png) ##### TCP 四次挥手释放连接 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP四次挥手释放连接.png) 【TCP 释放连接全过程解释】 1. 客户端发送 FIN 给服务器,说明客户端不必发送数据给服务器了(请求释放从客户端到服务器的连接); 2. 服务器接收到客户端发的 FIN,并回复 ACK 给客户端(同意释放从客户端到服务器的连接); 3. 客户端收到服务端回复的 ACK,此时从客户端到服务器的连接已释放(但服务端到客户端的连接还未释放,并且客户端还可以接收数据); 4. 服务端继续发送之前没发完的数据给客户端; 5. 服务端发送 FIN+ACK 给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放); 6. 客户端收到服务端的 FIN+ACK,并回复 ACK 给服务端(同意释放从服务端到客户端的连接); 7. 服务端收到客户端的 ACK 后,释放从服务端到客户端的连接。 ##### TCP 为什么要进行四次挥手? 【问题一】TCP 为什么要进行四次挥手? / 为什么 TCP 建立连接需要三次,而释放连接则需要四次? 【答案一】因为 TCP 是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向客户端的连接关闭(三四次挥手)。所以 TCP 释放连接时服务器的 ACK 和 FIN 是分开发送的(中间隔着数据传输),而 TCP 建立连接时服务器的 ACK 和 SYN 是一起发送的(第二次握手),所以 TCP 建立连接需要三次,而释放连接则需要四次。 【问题二】为什么 TCP 连接时可以 ACK 和 SYN 一起发送,而释放时则 ACK 和 FIN 分开发送呢?(ACK 和 FIN 分开是指第二次和第三次挥手) 【答案二】因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端 FIN 请求(服务端发送 ACK),然后数据传输,传输完成后,服务端再提出 FIN 请求(服务端发送 FIN);而连接时则没有中间的数据传输,因此连接时可以 ACK 和 SYN 一起发送。 【问题三】为什么客户端释放最后需要 TIME-WAIT 等待 2MSL 呢? 【答案三】 1. 为了保证客户端发送的最后一个 ACK 报文能够到达服务端。若未成功到达,则服务端超时重传 FIN+ACK 报文段,客户端再重传 ACK,并重新计时。 2. 防止已失效的连接请求报文段出现在本连接中。TIME-WAIT 持续 2MSL 可使本连接持续的时间内所产生的所有报文段都从网络中消失,这样可使下次连接中不会出现旧的连接报文段。 #### TCP 有限状态机 TCP 有限状态机图片 ![TCP 的有限状态机](https://gitee.com/huihut/interview/raw/master/images/TCP的有限状态机.png) ### 应用层 #### DNS * DNS(Domain Name System,域名系统)是互联网的一项服务。它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS 使用 TCP 和 UDP 端口 53。当前,对于每一级域名长度的限制是 63 个字符,域名总长度则不能超过 253 个字符。 域名: * `域名 ::= {<三级域名>.<二级域名>.<顶级域名>}`,如:`blog.huihut.com` #### FTP * FTP(File Transfer Protocol,文件传输协议)是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式,使用 TCP 数据报,提供交互式访问,双向传输。 * TFTP(Trivial File Transfer Protocol,简单文件传输协议)一个小且易实现的文件传输协议,也使用客户-服务器方式,使用UDP数据报,只支持文件传输而不支持交互,没有列目录,不能对用户进行身份鉴定 #### TELNET * TELNET 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。 * HTTP(HyperText Transfer Protocol,超文本传输协议)是用于从 WWW(World Wide Web,万维网)服务器传输超文本到本地浏览器的传送协议。 * SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 * Socket 建立网络通信连接至少要一对端口号(Socket)。Socket 本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。 #### WWW * WWW(World Wide Web,环球信息网,万维网)是一个由许多互相链接的超文本组成的系统,通过互联网访问 ##### URL * URL(Uniform Resource Locator,统一资源定位符)是因特网上标准的资源的地址(Address) 标准格式: * `协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]` 完整格式: * `协议类型:[//[访问资源需要的凭证信息@]服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]` > 其中【访问凭证信息@;:端口号;?查询;#片段ID】都属于选填项 > 如:`https://github.com/huihut/interview#cc` ##### HTTP HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。 请求方法 方法 | 意义 --- | --- OPTIONS | 请求一些选项信息,允许客户端查看服务器的性能 GET | 请求指定的页面信息,并返回实体主体 HEAD | 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改 PUT | 从客户端向服务器传送的数据取代指定的文档的内容 DELETE | 请求服务器删除指定的页面 TRACE | 回显服务器收到的请求,主要用于测试或诊断 状态码(Status-Code) * 1xx:表示通知信息,如请求收到了或正在进行处理 * 100 Continue:继续,客户端应继续其请求 * 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议 * 2xx:表示成功,如接收或知道了 * 200 OK: 请求成功 * 3xx:表示重定向,如要完成请求还必须采取进一步的行动 * 301 Moved Permanently: 永久移动。请求的资源已被永久的移动到新 URL,返回信息会包括新的 URL,浏览器会自动定向到新 URL。今后任何新的请求都应使用新的 URL 代替 * 4xx:表示客户的差错,如请求中有错误的语法或不能完成 * 400 Bad Request: 客户端请求的语法错误,服务器无法理解 * 401 Unauthorized: 请求要求用户的身份认证 * 403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求(权限不够) * 404 Not Found: 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 “您所请求的资源无法找到” 的个性页面 * 408 Request Timeout: 服务器等待客户端发送的请求时间过长,超时 * 5xx:表示服务器的差错,如服务器失效无法完成请求 * 500 Internal Server Error: 服务器内部错误,无法完成请求 * 503 Service Unavailable: 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中 * 504 Gateway Timeout: 充当网关或代理的服务器,未及时从远端服务器获取请求 > 更多状态码:[菜鸟教程 . HTTP状态码](http://www.runoob.com/http/http-status-codes.html) ##### 其他协议 * SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是在 Internet 传输 Email 的标准,是一个相对简单的基于文本的协议。在其之上指定了一条消息的一个或多个接收者(在大多数情况下被确认是存在的),然后消息文本会被传输。可以很简单地通过 Telnet 程序来测试一个 SMTP 服务器。SMTP 使用 TCP 端口 25。 * DHCP(Dynamic Host Configuration Protocol,动态主机设置协议)是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途: * 用于内部网络或网络服务供应商自动分配 IP 地址给用户 * 用于内部网络管理员作为对所有电脑作中央管理的手段 * SNMP(Simple Network Management Protocol,简单网络管理协议)构成了互联网工程工作小组(IETF,Internet Engineering Task Force)定义的 Internet 协议族的一部分。该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。 ## 🌩 网络编程 ### Socket > [Linux Socket 编程(不限 Linux)](https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html) ![Socket 客户端服务器通讯](https://gitee.com/huihut/interview/raw/master/images/socket客户端服务器通讯.jpg) #### Socket 中的 read()、write() 函数 ```cpp ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); ``` ##### read() * read 函数是负责从 fd 中读取内容。 * 当读成功时,read 返回实际所读的字节数。 * 如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。 * 如果错误为 EINTR 说明读是由中断引起的;如果是 ECONNREST 表示网络连接出了问题。 ##### write() * write 函数将 buf 中的 nbytes 字节内容写入文件描述符 fd。 * 成功时返回写的字节数。失败时返回 -1,并设置 errno 变量。 * 在网络程序中,当我们向套接字文件描述符写时有俩种可能。 * (1)write 的返回值大于 0,表示写了部分或者是全部的数据。 * (2)返回的值小于 0,此时出现了错误。 * 如果错误为 EINTR 表示在写的时候出现了中断错误;如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。 #### Socket 中 TCP 的三次握手建立连接 我们知道 TCP 建立连接要进行 “三次握手”,即交换三个分组。大致流程如下: 1. 客户端向服务器发送一个 SYN J 2. 服务器向客户端响应一个 SYN K,并对 SYN J 进行确认 ACK J+1 3. 客户端再想服务器发一个确认 ACK K+1 只有就完了三次握手,但是这个三次握手发生在 Socket 的那几个函数中呢?请看下图: ![socket 中发送的 TCP 三次握手](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png) 从图中可以看出: 1. 当客户端调用 connect 时,触发了连接请求,向服务器发送了 SYN J 包,这时 connect 进入阻塞状态; 2. 服务器监听到连接请求,即收到 SYN J 包,调用 accept 函数接收请求向客户端发送 SYN K ,ACK J+1,这时 accept 进入阻塞状态; 3. 客户端收到服务器的 SYN K ,ACK J+1 之后,这时 connect 返回,并对 SYN K 进行确认; 4. 服务器收到 ACK K+1 时,accept 返回,至此三次握手完毕,连接建立。 #### Socket 中 TCP 的四次握手释放连接 上面介绍了 socket 中 TCP 的三次握手建立过程,及其涉及的 socket 函数。现在我们介绍 socket 中的四次握手释放连接的过程,请看下图: ![socket 中发送的 TCP 四次握手](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png) 图示过程如下: 1. 某个应用进程首先调用 close 主动关闭连接,这时 TCP 发送一个 FIN M; 2. 另一端接收到 FIN M 之后,执行被动关闭,对这个 FIN 进行确认。它的接收也作为文件结束符传递给应用进程,因为 FIN 的接收意味着应用进程在相应的连接上再也接收不到额外数据; 3. 一段时间之后,接收到文件结束符的应用进程调用 close 关闭它的 socket。这导致它的 TCP 也发送一个 FIN N; 4. 接收到这个 FIN 的源发送端 TCP 对它进行确认。 这样每个方向上都有一个 FIN 和 ACK。 ## 💾 数据库 > 本节部分知识点来自《数据库系统概论(第 5 版)》 ### 基本概念 * 数据(data):描述事物的符号记录称为数据。 * 数据库(DataBase,DB):是长期存储在计算机内、有组织的、可共享的大量数据的集合,具有永久存储、有组织、可共享三个基本特点。 * 数据库管理系统(DataBase Management System,DBMS):是位于用户与操作系统之间的一层数据管理软件。 * 数据库系统(DataBase System,DBS):是有数据库、数据库管理系统(及其应用开发工具)、应用程序和数据库管理员(DataBase Administrator DBA)组成的存储、管理、处理和维护数据的系统。 * 实体(entity):客观存在并可相互区别的事物称为实体。 * 属性(attribute):实体所具有的某一特性称为属性。 * 码(key):唯一标识实体的属性集称为码。 * 实体型(entity type):用实体名及其属性名集合来抽象和刻画同类实体,称为实体型。 * 实体集(entity set):同一实体型的集合称为实体集。 * 联系(relationship):实体之间的联系通常是指不同实体集之间的联系。 * 模式(schema):模式也称逻辑模式,是数据库全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。 * 外模式(external schema):外模式也称子模式(subschema)或用户模式,它是数据库用户(包括应用程序员和最终用户)能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图,是与某一应用有关的数据的逻辑表示。 * 内模式(internal schema):内模式也称为存储模式(storage schema),一个数据库只有一个内模式。他是数据物理结构和存储方式的描述,是数据库在数据库内部的组织方式。 ### 常用数据模型 * 层次模型(hierarchical model) * 网状模型(network model) * 关系模型(relational model) * 关系(relation):一个关系对应通常说的一张表 * 元组(tuple):表中的一行即为一个元组 * 属性(attribute):表中的一列即为一个属性 * 码(key):表中可以唯一确定一个元组的某个属性组 * 域(domain):一组具有相同数据类型的值的集合 * 分量:元组中的一个属性值 * 关系模式:对关系的描述,一般表示为 `关系名(属性1, 属性2, ..., 属性n)` * 面向对象数据模型(object oriented data model) * 对象关系数据模型(object relational data model) * 半结构化数据模型(semistructure data model) ### 常用 SQL 操作
对象类型 对象 操作类型
数据库模式 模式 CREATE SCHEMA
基本表 CREATE SCHEMAALTER TABLE
视图 CREATE VIEW
索引 CREATE INDEX
数据 基本表和视图 SELECTINSERTUPDATEDELETEREFERENCESALL PRIVILEGES
属性列 SELECTINSERTUPDATEREFERENCESALL PRIVILEGES
> SQL 语法教程:[runoob . SQL 教程](http://www.runoob.com/sql/sql-tutorial.html) ### 关系型数据库 * 基本关系操作:查询(选择、投影、连接(等值连接、自然连接、外连接(左外连接、右外连接))、除、并、差、交、笛卡尔积等)、插入、删除、修改 * 关系模型中的三类完整性约束:实体完整性、参照完整性、用户定义的完整性 #### 索引 * 数据库索引:顺序索引、B+ 树索引、hash 索引 * [MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) ### 数据库完整性 * 数据库的完整性是指数据的正确性和相容性。 * 完整性:为了防止数据库中存在不符合语义(不正确)的数据。 * 安全性:为了保护数据库防止恶意破坏和非法存取。 * 触发器:是用户定义在关系表中的一类由事件驱动的特殊过程。 ### 关系数据理论 * 数据依赖是一个关系内部属性与属性之间的一种约束关系,是通过属性间值的相等与否体现出来的数据间相关联系。 * 最重要的数据依赖:函数依赖、多值依赖。 #### 范式 * 第一范式(1NF):属性(字段)是最小单位不可再分。 * 第二范式(2NF):满足 1NF,每个非主属性完全依赖于主键(消除 1NF 非主属性对码的部分函数依赖)。 * 第三范式(3NF):满足 2NF,任何非主属性不依赖于其他非主属性(消除 2NF 非主属性对码的传递函数依赖)。 * 鲍依斯-科得范式(BCNF):满足 3NF,任何非主属性不能对主键子集依赖(消除 3NF 主属性对码的部分和传递函数依赖)。 * 第四范式(4NF):满足 3NF,属性之间不能有非平凡且非函数依赖的多值依赖(消除 3NF 非平凡且非函数依赖的多值依赖)。 ### 数据库恢复 * 事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。 * 事物的 ACID 特性:原子性、一致性、隔离性、持续性。 * 恢复的实现技术:建立冗余数据 -> 利用冗余数据实施数据库恢复。 * 建立冗余数据常用技术:数据转储(动态海量转储、动态增量转储、静态海量转储、静态增量转储)、登记日志文件。 ### 并发控制 * 事务是并发控制的基本单位。 * 并发操作带来的数据不一致性包括:丢失修改、不可重复读、读 “脏” 数据。 * 并发控制主要技术:封锁、时间戳、乐观控制法、多版本并发控制等。 * 基本封锁类型:排他锁(X 锁 / 写锁)、共享锁(S 锁 / 读锁)。 * 活锁死锁: * 活锁:事务永远处于等待状态,可通过先来先服务的策略避免。 * 死锁:事务永远不能结束 * 预防:一次封锁法、顺序封锁法; * 诊断:超时法、等待图法; * 解除:撤销处理死锁代价最小的事务,并释放此事务的所有的锁,使其他事务得以继续运行下去。 * 可串行化调度:多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同。可串行性时并发事务正确调度的准则。 ## 📏 设计模式 > 各大设计模式例子参考:[CSDN专栏 . C++ 设计模式](https://blog.csdn.net/liang19890820/article/details/66974516) 系列博文 [设计模式工程目录](DesignPattern) ### 单例模式 [单例模式例子](DesignPattern/SingletonPattern) ### 抽象工厂模式 [抽象工厂模式例子](DesignPattern/AbstractFactoryPattern) ### 适配器模式 [适配器模式例子](DesignPattern/AdapterPattern) ### 桥接模式 [桥接模式例子](DesignPattern/BridgePattern) ### 观察者模式 [观察者模式例子](DesignPattern/ObserverPattern) ### 设计模式的六大原则 * 单一职责原则(SRP,Single Responsibility Principle) * 里氏替换原则(LSP,Liskov Substitution Principle) * 依赖倒置原则(DIP,Dependence Inversion Principle) * 接口隔离原则(ISP,Interface Segregation Principle) * 迪米特法则(LoD,Law of Demeter) * 开放封闭原则(OCP,Open Close Principle) ## ⚙️ 链接装载库 > 本节部分知识点来自《程序员的自我修养——链接装载库》 ### 内存、栈、堆 一般应用程序内存空间有如下区域: * 栈:由操作系统自动分配释放,存放函数的参数值、局部变量等的值,用于维护函数调用的上下文 * 堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收,用来容纳应用程序动态分配的内存区域 * 可执行文件映像:存储着可执行文件在内存中的映像,由装载器装载是将可执行文件的内存读取或映射到这里 * 保留区:保留区并不是一个单一的内存区域,而是对内存中受到保护而禁止访问的内存区域的总称,如通常 C 语言讲无效指针赋值为 0(NULL),因此 0 地址正常情况下不可能有效的访问数据 #### 栈 栈保存了一个函数调用所需要的维护信息,常被称为堆栈帧(Stack Frame)或活动记录(Activate Record),一般包含以下几方面: * 函数的返回地址和参数 * 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量 * 保存上下文:包括函数调用前后需要保持不变的寄存器 #### 堆 堆分配算法: * 空闲链表(Free List) * 位图(Bitmap) * 对象池 #### “段错误(segment fault)” 或 “非法操作,该内存地址不能 read/write” 典型的非法指针解引用造成的错误。当指针指向一个不允许读写的内存地址,而程序却试图利用指针来读或写该地址时,会出现这个错误。 普遍原因: * 将指针初始化为 NULL,之后没有给它一个合理的值就开始使用指针 * 没用初始化栈中的指针,指针的值一般会是随机数,之后就直接开始使用指针 ### 编译链接 #### 各平台文件格式 平台 | 可执行文件 | 目标文件 | 动态库/共享对象 | 静态库 ---|---|---|---|--- Windows|exe|obj|dll|lib Unix/Linux|ELF、out|o|so|a Mac|Mach-O|o|dylib、tbd、framework|a、framework #### 编译链接过程 1. 预编译(预编译器处理如 `#include`、`#define` 等预编译指令,生成 `.i` 或 `.ii` 文件) 2. 编译(编译器进行词法分析、语法分析、语义分析、中间代码生成、目标代码生成、优化,生成 `.s` 文件) 3. 汇编(汇编器把汇编码翻译成机器码,生成 `.o` 文件) 4. 链接(连接器进行地址和空间分配、符号决议、重定位,生成 `.out` 文件) > 现在版本 GCC 把预编译和编译合成一步,预编译编译程序 cc1、汇编器 as、连接器 ld > MSVC 编译环境,编译器 cl、连接器 link、可执行文件查看器 dumpbin #### 目标文件 编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。 > 可执行文件(Windows 的 `.exe` 和 Linux 的 `ELF`)、动态链接库(Windows 的 `.dll` 和 Linux 的 `.so`)、静态链接库(Windows 的 `.lib` 和 Linux 的 `.a`)都是按照可执行文件格式存储(Windows 按照 PE-COFF,Linux 按照 ELF) ##### 目标文件格式 * Windows 的 PE(Portable Executable),或称为 PE-COFF,`.obj` 格式 * Linux 的 ELF(Executable Linkable Format),`.o` 格式 * Intel/Microsoft 的 OMF(Object Module Format) * Unix 的 `a.out` 格式 * MS-DOS 的 `.COM` 格式 > PE 和 ELF 都是 COFF(Common File Format)的变种 ##### 目标文件存储结构 段 | 功能 --- | --- File Header | 文件头,描述整个文件的文件属性(包括文件是否可执行、是静态链接或动态连接及入口地址、目标硬件、目标操作系统等) .text section | 代码段,执行语句编译成的机器代码 .data section | 数据段,已初始化的全局变量和局部静态变量 .bss section | BSS 段(Block Started by Symbol),未初始化的全局变量和局部静态变量(因为默认值为 0,所以只是在此预留位置,不占空间) .rodata section | 只读数据段,存放只读数据,一般是程序里面的只读变量(如 const 修饰的变量)和字符串常量 .comment section | 注释信息段,存放编译器版本信息 .note.GNU-stack section | 堆栈提示段 > 其他段略 #### 链接的接口————符号 在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。 如下符号表(Symbol Table): Symbol(符号名) | Symbol Value (地址) --- | --- main| 0x100 Add | 0x123 ... | ... ### Linux 的共享库(Shared Library) Linux 下的共享库就是普通的 ELF 共享对象。 共享库版本更新应该保证二进制接口 ABI(Application Binary Interface)的兼容 #### 命名 `libname.so.x.y.z` * x:主版本号,不同主版本号的库之间不兼容,需要重新编译 * y:次版本号,高版本号向后兼容低版本号 * z:发布版本号,不对接口进行更改,完全兼容 #### 路径 大部分包括 Linux 在内的开源系统遵循 FHS(File Hierarchy Standard)的标准,这标准规定了系统文件如何存放,包括各个目录结构、组织和作用。 * `/lib`:存放系统最关键和最基础的共享库,如动态链接器、C 语言运行库、数学库等 * `/usr/lib`:存放非系统运行时所需要的关键性的库,主要是开发库 * `/usr/local/lib`:存放跟操作系统本身并不十分相关的库,主要是一些第三方应用程序的库 > 动态链接器会在 `/lib`、`/usr/lib` 和由 `/etc/ld.so.conf` 配置文件指定的,目录中查找共享库 #### 环境变量 * `LD_LIBRARY_PATH`:临时改变某个应用程序的共享库查找路径,而不会影响其他应用程序 * `LD_PRELOAD`:指定预先装载的一些共享库甚至是目标文件 * `LD_DEBUG`:打开动态链接器的调试功能 #### so 共享库的编写 使用 CLion 编写共享库 创建一个名为 MySharedLib 的共享库 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(MySharedLib) set(CMAKE_CXX_STANDARD 11) add_library(MySharedLib SHARED library.cpp library.h) ``` library.h ```cpp #ifndef MYSHAREDLIB_LIBRARY_H #define MYSHAREDLIB_LIBRARY_H // 打印 Hello World! void hello(); // 使用可变模版参数求和 template T sum(T t) { return t; } template T sum(T first, Types ... rest) { return first + sum(rest...); } #endif ``` library.cpp ```cpp #include #include "library.h" void hello() { std::cout << "Hello, World!" << std::endl; } ``` #### so 共享库的使用(被可执行项目调用) 使用 CLion 调用共享库 创建一个名为 TestSharedLib 的可执行项目 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(TestSharedLib) # C++11 编译 set(CMAKE_CXX_STANDARD 11) # 头文件路径 set(INC_DIR /home/xx/code/clion/MySharedLib) # 库文件路径 set(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug) include_directories(${INC_DIR}) link_directories(${LIB_DIR}) link_libraries(MySharedLib) add_executable(TestSharedLib main.cpp) # 链接 MySharedLib 库 target_link_libraries(TestSharedLib MySharedLib) ``` main.cpp ```cpp #include #include "library.h" using std::cout; using std::endl; int main() { hello(); cout << "1 + 2 = " << sum(1,2) << endl; cout << "1 + 2 + 3 = " << sum(1,2,3) << endl; return 0; } ``` 执行结果 ``` Hello, World! 1 + 2 = 3 1 + 2 + 3 = 6 ``` ### Windows 应用程序入口函数 * GUI(Graphical User Interface)应用,链接器选项:`/SUBSYSTEM:WINDOWS` * CUI(Console User Interface)应用,链接器选项:`/SUBSYSTEM:CONSOLE` _tWinMain 与 _tmain 函数声明 ```cpp Int WINAPI _tWinMain( HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int nCmdShow); int _tmain( int argc, TCHAR *argv[], TCHAR *envp[]); ``` 应用程序类型|入口点函数|嵌入可执行文件的启动函数 ---|---|--- 处理ANSI字符(串)的GUI应用程序|_tWinMain(WinMain)|WinMainCRTSartup 处理Unicode字符(串)的GUI应用程序|_tWinMain(wWinMain)|wWinMainCRTSartup 处理ANSI字符(串)的CUI应用程序|_tmain(Main)|mainCRTSartup 处理Unicode字符(串)的CUI应用程序|_tmain(wMain)|wmainCRTSartup 动态链接库(Dynamic-Link Library)|DllMain|_DllMainCRTStartup ### Windows 的动态链接库(Dynamic-Link Library) > 部分知识点来自《Windows 核心编程(第五版)》 #### 用处 * 扩展了应用程序的特性 * 简化了项目管理 * 有助于节省内存 * 促进了资源的共享 * 促进了本地化 * 有助于解决平台间的差异 * 可以用于特殊目的 #### 注意 * 创建 DLL,事实上是在创建可供一个可执行模块调用的函数 * 当一个模块提供一个内存分配函数(malloc、new)的时候,它必须同时提供另一个内存释放函数(free、delete) * 在使用 C 和 C++ 混编的时候,要使用 extern "C" 修饰符 * 一个 DLL 可以导出函数、变量(避免导出)、C++ 类(导出导入需要同编译器,否则避免导出) * DLL 模块:cpp 文件中的 __declspec(dllexport) 写在 include 头文件之前 * 调用 DLL 的可执行模块:cpp 文件的 __declspec(dllimport) 之前不应该定义 MYLIBAPI #### 加载 Windows 程序的搜索顺序 1. 包含可执行文件的目录 2. Windows 的系统目录,可以通过 GetSystemDirectory 得到 3. 16 位的系统目录,即 Windows 目录中的 System 子目录 4. Windows 目录,可以通过 GetWindowsDirectory 得到 5. 进程的当前目录 6. PATH 环境变量中所列出的目录 #### DLL 入口函数 DllMain 函数 ```cpp BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: // 第一次将一个DLL映射到进程地址空间时调用 // The DLL is being mapped into the process' address space. break; case DLL_THREAD_ATTACH: // 当进程创建一个线程的时候,用于告诉DLL执行与线程相关的初始化(非主线程执行) // A thread is bing created. break; case DLL_THREAD_DETACH: // 系统调用 ExitThread 线程退出前,即将终止的线程通过告诉DLL执行与线程相关的清理 // A thread is exiting cleanly. break; case DLL_PROCESS_DETACH: // 将一个DLL从进程的地址空间时调用 // The DLL is being unmapped from the process' address space. break; } return (TRUE); // Used only for DLL_PROCESS_ATTACH } ``` #### 载入卸载库 LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread 函数声明 ```cpp // 载入库 HMODULE WINAPI LoadLibrary( _In_ LPCTSTR lpFileName ); HMODULE LoadLibraryExA( LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags ); // 若要在通用 Windows 平台(UWP)应用中加载 Win32 DLL,需要调用 LoadPackagedLibrary,而不是 LoadLibrary 或 LoadLibraryEx HMODULE LoadPackagedLibrary( LPCWSTR lpwLibFileName, DWORD Reserved ); // 卸载库 BOOL WINAPI FreeLibrary( _In_ HMODULE hModule ); // 卸载库和退出线程 VOID WINAPI FreeLibraryAndExitThread( _In_ HMODULE hModule, _In_ DWORD dwExitCode ); ``` #### 显示地链接到导出符号 GetProcAddress 函数声明 ```cpp FARPROC GetProcAddress( HMODULE hInstDll, PCSTR pszSymbolName // 只能接受 ANSI 字符串,不能是 Unicode ); ``` #### DumpBin.exe 查看 DLL 信息 在 `VS 的开发人员命令提示符` 使用 `DumpBin.exe` 可查看 DLL 库的导出段(导出的变量、函数、类名的符号)、相对虚拟地址(RVA,relative virtual address)。如: ``` DUMPBIN -exports D:\mydll.dll ``` #### LoadLibrary 与 FreeLibrary 流程图 LoadLibrary 与 FreeLibrary 流程图 ##### LoadLibrary ![WindowsLoadLibrary](https://gitee.com/huihut/interview/raw/master/images/WindowsLoadLibrary.png) ##### FreeLibrary ![WindowsFreeLibrary](https://gitee.com/huihut/interview/raw/master/images/WindowsFreeLibrary.png) #### DLL 库的编写(导出一个 DLL 模块) DLL 库的编写(导出一个 DLL 模块) DLL 头文件 ```cpp // MyLib.h #ifdef MYLIBAPI // MYLIBAPI 应该在全部 DLL 源文件的 include "Mylib.h" 之前被定义 // 全部函数/变量正在被导出 #else // 这个头文件被一个exe源代码模块包含,意味着全部函数/变量被导入 #define MYLIBAPI extern "C" __declspec(dllimport) #endif // 这里定义任何的数据结构和符号 // 定义导出的变量(避免导出变量) MYLIBAPI int g_nResult; // 定义导出函数原型 MYLIBAPI int Add(int nLeft, int nRight); ``` DLL 源文件 ```cpp // MyLibFile1.cpp // 包含标准Windows和C运行时头文件 #include // DLL源码文件导出的函数和变量 #define MYLIBAPI extern "C" __declspec(dllexport) // 包含导出的数据结构、符号、函数、变量 #include "MyLib.h" // 将此DLL源代码文件的代码放在此处 int g_nResult; int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return g_nResult; } ``` #### DLL 库的使用(运行时动态链接 DLL) DLL 库的使用(运行时动态链接 DLL) ```cpp // A simple program that uses LoadLibrary and // GetProcAddress to access myPuts from Myputs.dll. #include #include typedef int (__cdecl *MYPROC)(LPWSTR); int main( void ) { HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; // Get a handle to the DLL module. hinstLib = LoadLibrary(TEXT("MyPuts.dll")); // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts"); // If the function address is valid, call the function. if (NULL != ProcAdd) { fRunTimeLinkSuccess = TRUE; (ProcAdd) (L"Message sent to the DLL function\n"); } // Free the DLL module. fFreeResult = FreeLibrary(hinstLib); } // If unable to call the DLL function, use an alternative. if (! fRunTimeLinkSuccess) printf("Message printed from executable\n"); return 0; } ``` ### 运行库(Runtime Library) #### 典型程序运行步骤 1. 操作系统创建进程,把控制权交给程序的入口(往往是运行库中的某个入口函数) 2. 入口函数对运行库和程序运行环境进行初始化(包括堆、I/O、线程、全局变量构造等等)。 3. 入口函数初始化后,调用 main 函数,正式开始执行程序主体部分。 4. main 函数执行完毕后,返回到入口函数进行清理工作(包括全局变量析构、堆销毁、关闭I/O等),然后进行系统调用结束进程。 > 一个程序的 I/O 指代程序与外界的交互,包括文件、管程、网络、命令行、信号等。更广义地讲,I/O 指代操作系统理解为 “文件” 的事物。 #### glibc 入口 `_start -> __libc_start_main -> exit -> _exit` 其中 `main(argc, argv, __environ)` 函数在 `__libc_start_main` 里执行。 #### MSVC CRT 入口 `int mainCRTStartup(void)` 执行如下操作: 1. 初始化和 OS 版本有关的全局变量。 2. 初始化堆。 3. 初始化 I/O。 4. 获取命令行参数和环境变量。 5. 初始化 C 库的一些数据。 6. 调用 main 并记录返回值。 7. 检查错误并将 main 的返回值返回。 #### C 语言运行库(CRT) 大致包含如下功能: * 启动与退出:包括入口函数及入口函数所依赖的其他函数等。 * 标准函数:有 C 语言标准规定的C语言标准库所拥有的函数实现。 * I/O:I/O 功能的封装和实现。 * 堆:堆的封装和实现。 * 语言实现:语言中一些特殊功能的实现。 * 调试:实现调试功能的代码。 #### C语言标准库(ANSI C) 包含: * 标准输入输出(stdio.h) * 文件操作(stdio.h) * 字符操作(ctype.h) * 字符串操作(string.h) * 数学函数(math.h) * 资源管理(stdlib.h) * 格式转换(stdlib.h) * 时间/日期(time.h) * 断言(assert.h) * 各种类型上的常数(limits.h & float.h) * 变长参数(stdarg.h) * 非局部跳转(setjmp.h) ## 📚 书籍 > [huihut/CS-Books](https://github.com/huihut/CS-Books):📚 Computer Science Books 计算机技术类书籍 PDF ### 语言 * 《C++ Primer》 * 《Effective C++》 * 《More Effective C++》 * 《深度探索 C++ 对象模型》 * 《深入理解 C++11》 * 《STL 源码剖析》 ### 算法 * 《剑指 Offer》 * 《编程珠玑》 * 《程序员面试宝典》 ### 系统 * 《深入理解计算机系统》 * 《Windows 核心编程》 * 《Unix 环境高级编程》 ### 网络 * 《Unix 网络编程》 * 《TCP/IP 详解》 ### 其他 * 《程序员的自我修养》 ## 🔱 C/C++ 发展方向 > C/C++ 发展方向甚广,包括不限于以下方向, 以下列举一些大厂校招岗位要求。 ### 后台/服务器 【后台开发】 * 编程基本功扎实,掌握 C/C++/JAVA 等开发语言、常用算法和数据结构; * 熟悉 TCP/UDP 网络协议及相关编程、进程间通讯编程; * 了解 Python、Shell、Perl 等脚本语言; * 了解 MYSQL 及 SQL 语言、编程,了解 NoSQL, key-value 存储原理; * 全面、扎实的软件知识结构,掌握操作系统、软件工程、设计模式、数据结构、数据库系统、网络安全等专业知识; * 了解分布式系统设计与开发、负载均衡技术,系统容灾设计,高可用系统等知识。 ### 桌面客户端 【PC 客户端开发】 * 计算机软件相关专业本科或以上学历,热爱编程,基础扎实,理解算法和数据结构相关知识; * 熟悉 windows 操作系统的内存管理、文件系统、进程线程调度; * 熟悉 MFC/windows 界面实现机制,熟练使用 VC,精通 C/C++,熟练使用 STL,以及 Windows 下网络编程经验; * 熟练掌握 Windows 客户端开发、调试,有 Windows 应用软件开发经验优先; * 对于创新及解决具有挑战性的问题充满激情,具有良好的算法基础及系统分析能力。 ### 图形学/游戏/VR/AR 【游戏客户端开发】 * 计算机科学/工程相关专业本科或以上学历,热爱编程,基础扎实,理解算法、数据结构、软件设计相关知识; * 至少掌握一种游戏开发常用的编程语言,具 C++/C# 编程经验优先; * 具游戏引擎(如 Unity、Unreal)使用经验者优先; * 了解某方面的游戏客户端技术(如图形、音频、动画、物理、人工智能、网络同步)者优先考虑; * 对于创新及解决具有挑战性的问题充满激情,有较强的学习能力、分析及解决问题能力,具备良好的团队合作意识; * 具阅读英文技术文档能力; * 热爱游戏。 ### 测试开发 【测试开发】 * 计算机或相关专业本科及以上学历; * 一至两年的 C/C++/Python 或其他计算机语言的编程经验; * 具备撰写测试计划、测试用例、以及实现性能和安全等测试的能力; * 具备实现自动化系统的能力; * 具备定位调查产品缺陷能力、以及代码级别调试缺陷的能力; * 工作主动积极,有责任心,具有良好的团队合作精神。 ### 网络安全/逆向 【安全技术】 * 热爱互联网,对操作系统和网络安全有狂热的追求,专业不限; * 熟悉漏洞挖掘、网络安全攻防技术,了解常见黑客攻击手法; * 掌握基本开发能力,熟练使用 C/C++ 语言; * 对数据库、操作系统、网络原理有较好掌握; * 具有软件逆向,网络安全攻防或安全系统开发经验者优先。 ### 嵌入式/物联网 【嵌入式应用开发】 * 有良好的编程基础,熟练掌握 C/C++ 语言; * 掌握操作系统、数据结构等软件开发必备知识; * 具备较强的沟通理解能力及良好的团队合作意识; * 有 Linux/Android 系统平台的开发经验者优先。 ### 音视频/流媒体/SDK 【音视频编解码】 1. 硕士及以上学历,计算机、信号处理、数学、信息类及相关专业和方向; 2. 视频编解码基础扎实,熟常用的 HEVC 或 H264,有较好的数字信号处理基础; 3. 掌握 C/C++,代码能力强, 熟悉一种汇编语言尤佳; 4. 较强的英文文献阅读能力; 5. 学习能力强,具有团队协作精神,有较强的抗压能力。 ### 计算机视觉/机器学习 【计算机视觉研究】 * 计算机、应用数学、模式识别、人工智能、自控、统计学、运筹学、生物信息、物理学/量子计算、神经科学、社会学/心理学等专业,图像处理、模式识别、机器学习相关研究方向,本科及以上,博士优先; * 熟练掌握计算机视觉和图像处理相关的基本算法及应用; * 较强的算法实现能力,熟练掌握 C/C++ 编程,熟悉 Shell/Python/Matlab 至少一种编程语言; * 在计算机视觉、模式识别等学术会议或者期刊上发表论文、相关国际比赛获奖、及有相关专利者优先。 ## 💯 复习刷题网站 * [cplusplus](http://www.cplusplus.com/) * [cppreference](https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5) * [runoob](http://www.runoob.com/cplusplus/cpp-tutorial.html) * [leetcode](https://leetcode.com/) | [leetcode-cn](https://leetcode-cn.com/) * [lintcode](https://www.lintcode.com/) * [nowcoder](https://www.nowcoder.net/) ## 📝 面试题目经验 * [牛客网 . 2020秋招面经大汇总!(岗位划分)](https://www.nowcoder.com/discuss/205497) * [牛客网 . 【备战秋招】2020届秋招备战攻略](https://www.nowcoder.com/discuss/197116) * [牛客网 . 2019校招面经大汇总!【每日更新中】](https://www.nowcoder.com/discuss/90907) * [牛客网 . 2019校招技术类岗位面经汇总【技术类】](https://www.nowcoder.com/discuss/146655) * [牛客网 . 2018校招笔试真题汇总](https://www.nowcoder.com/discuss/68802) * [牛客网 . 2017秋季校园招聘笔经面经专题汇总](https://www.nowcoder.com/discuss/12805) * [牛客网 . 史上最全2017春招面经大合集!!](https://www.nowcoder.com/discuss/25268) * [牛客网 . 面试题干货在此](https://www.nowcoder.com/discuss/57978) * [知乎 . 互联网求职路上,你见过哪些写得很好、很用心的面经?最好能分享自己的面经、心路历程。](https://www.zhihu.com/question/29693016) * [知乎 . 互联网公司最常见的面试算法题有哪些?](https://www.zhihu.com/question/24964987) * [CSDN . 全面整理的C++面试题](http://blog.csdn.net/ljzcome/article/details/574158) * [CSDN . 百度研发类面试题(C++方向)](http://blog.csdn.net/Xiongchao99/article/details/74524807?locationNum=6&fps=1) * [CSDN . c++常见面试题30道](http://blog.csdn.net/fakine/article/details/51321544) * [CSDN . 腾讯2016实习生面试经验(已经拿到offer)](http://blog.csdn.net/onever_say_love/article/details/51223886) * [cnblogs . C++面试集锦( 面试被问到的问题 )](https://www.cnblogs.com/Y1Focus/p/6707121.html) * [cnblogs . C/C++ 笔试、面试题目大汇总](https://www.cnblogs.com/fangyukuan/archive/2010/09/18/1829871.html) * [cnblogs . 常见C++面试题及基本知识点总结(一)](https://www.cnblogs.com/LUO77/p/5771237.html) * [segmentfault . C++常见面试问题总结](https://segmentfault.com/a/1190000003745529) ## 📆 招聘时间岗位 * [牛客网 . 名企校招日程](https://www.nowcoder.com/school/schedule) ## 👍 内推 * [Github . CyC2018/Job-Recommend](https://github.com/CyC2018/Job-Recommend):🔎 互联网内推信息(社招、校招、实习) * [Github . amusi/AI-Job-Recommend](https://github.com/amusi/AI-Job-Recommend):国内公司人工智能方向(含机器学习、深度学习、计算机视觉和自然语言处理)岗位的招聘信息(含全职、实习和校招) ## 👬 贡献者 ## 📜 License 本仓库遵循 CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享) 协议,转载请注明出处,不得用于商业目的。 [![CC BY-NC-SA 4.0](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)](https://github.com/huihut/interview/blob/master/LICENSE) ================================================ FILE: README_en.md ================================================
📖 Github    |    📚 Docsify

简体中文    |    English

💡 ON 📚 This repository is a summary of the basic knowledge of recruiting job seekers and beginners in the direction of C / C ++ technology, including language, program library, data structure, algorithm, system, network, link loading library and other knowledge and interview experience, recruitment, internal push, etc. information. 💡 Side directory support methods:[📚 Docsify Doc](https://interview.huihut.com)、[Github + TOC navigation](https://github.com/jawil/GayHub)([TOC preview.png](https://raw.githubusercontent.com/huihut/interview/master/images/TOC预览.png)) 📄 Save as PDF: Use the Chrome browser to open the 📚 Docsify document page, shrink the left directory-right click-print-select the target printer is Save as PDF-Save ( [Print Preview.png](https://raw.githubusercontent.com/huihut/interview/master/images/PrintPreview.png) ) 🙏 If there are any errors or improvements in the contents of the warehouse, issues or pr are welcome. Suggestions or discussions can be submitted at [# 12](https://github.com/huihut/interview/issues/12). Due to my limited level, the knowledge points in the warehouse are from my original, reading notes, books, blog posts, etc. Non-original has been marked with the source, if there is any omission, please issue an issue. This warehouse follows the [CC BY-NC-SA 4.0](https://github.com/huihut/interview/blob/master/LICENSE) agreement, please indicate the source for the reprint, and may not be used for commercial purposes.
## 📑 Table of contents * [➕ C/C++](#cc) * [⭐️ Effective](#effective) * [📦 STL](#stl) * [〽️ Data Structure](#data-structure) * [⚡️ Algorithm](#algorithm) * [❓ Problems](#problems) * [💻 OS](#os) * [☁️ Computer Network](#computer-network) * [🌩 Network Programming](#network-programming) * [💾 Database](#database) * [📏 Design Pattern](#design-pattern) * [⚙️ Link Loading Library](#link-loading-library) * [📚 Books](#books) * [🔱 C/C++ development direction](#cc-development-direction) * [💯 Review of Brush Questions Website](#review-of-brush-questions-website) * [📝 Interview Questions Experience](#interview-questions-experience) * [📆 Recruitment time post](#recruitment-time-post) * [👍 Recommend](#recommend) * [👬 Contributor](#contributor) * [📜 License](#license) ## ➕ C/C++ ### const #### Function 1. Modify the variable, indicating that the variable cannot be changed; 2. Modified pointers, divided into pointers to const (pointer to const) and pointers that are constants themselves (const pointer, const pointer); 3. Modified references, references to constants (reference to const), are used for formal parameter types, which avoids copying and function modification of values; 4. Decorate a member function, stating that member variables cannot be modified within the member function. #### const Pointers and references * Pointer * Pointer to const * A pointer to a constant itself (const pointer) * Reference * Reference to const * There is no const reference because the reference is an alias of an object, the reference is not an object > (Think of it for convenience) The value modified by const (after const) cannot be changed, such as `p2`, `p3` in the usage example below #### use const use ```cpp // class class A { private: const int a; // constant object member, can use initialization list or in-class initializer public: // Constructor A() : a(0) { }; A(int x) : a(x) { }; // initialize list // const can be used to distinguish between overloaded functions int getValue(); // ordinary member function int getValue() const; // constant member function, must not modify the value of any data member in the class }; void function() { // object A b; // ordinary object, can call all member functions const A a; // constant object, can only call constant member functions const A *p = &a; // pointer variable, point to a constant object const A &q = a; // reference to constant object // pointer char greeting[] = "Hello"; char* p1 = greeting; // pointer variable, pointing to a character array variable const char* p2 = greeting; // pointer variable, pointing to a character array constant (char followed by const, indicating that the character pointed to (char) cannot be changed) char* const p3 = greeting; // itself is a constant pointer to a character array variable (const followed by p3, indicating that the p3 pointer itself cannot be changed) const char* const p4 = greeting; // a pointer to a constant itself, pointing to a character array constant } // function void function1(const int Var); // the passed parameters are immutable within the function void function2(const char* Var); // The content pointed to by the parameter pointer is constant void function3(char* const Var); // parameter pointer is constant void function4(const int& Var); // the reference parameter is constant inside the function // function return value const int function5(); // returns a constant const int* function6(); // returns a pointer variable to a constant, use: const int * p = function6 (); int* const function7(); // returns a constant pointer to a variable, use: int * const p = function7 (); ``` #### #define and const constants #define|const constants ---|--- Macro definitions, equivalent to character substitution|constant declarations preprocessor processing|compiler processing without type safety checking|with type safety checking no memory allocation|memory allocation required stored in code segment|stored in data segment Can be canceled by `#undef`|Not cancelable ### static #### Function 1. Modify ordinary variables, modify the storage area and life cycle of the variables, make the variables stored in the static area, allocate space before the `main` function runs, if there is an initial value, initialize it with the initial value, if there is no initial value, the system uses the default Value to initialize it. 2. Modify ordinary functions to indicate the scope of the function, which can only be used in the file where the function is defined. When developing a project with multiple people, in order to prevent duplicate names from functions in other people's namespaces, you can position functions as static. 3. Decorate member variables. Decorate member variables so that all objects hold only one of the variable, and you can access the member without generating an object. 4. Decorate member functions. Decorate member functions so that they can be accessed without generating objects, but non-static members cannot be accessed within static functions. ### this pointer 1. `this` pointer is a special pointer hidden in every non-static member function. It points to the object that called the member function. 2. When calling a member function on an object, the compiler first assigns the address of the object to the `this` pointer, and then calls the member function. Each time the member function accesses a data member, the `this` pointer is implicitly used. 3. When a member function is called, it is automatically passed an implicit parameter, which is a pointer to the object where the member function is located. 4. The `this` pointer is implicitly declared as: `ClassName * const this`, which means that the `this` pointer cannot be assigned; in the `const` member function of the `ClassName` class, the type of the `this` pointer For: `const ClassName * const`, this means that the object pointed to by the `this` pointer cannot be modified (that is, the data members of such objects cannot be assigned); 5. `this` is not a regular variable, but an rvalue, so you cannot get the address of `this` (you can't `& this`). 6. It is often necessary to explicitly reference the `this` pointer in the following scenarios: 1. To implement a chained reference to an object; 2. To avoid performing assignments on the same object; 3. When implementing some data structures, such as `list`. ### inline function #### Features * Equivalent to writing the contents of the inline function at the call of the inline function; * It is equivalent to directly execute the function body without executing the steps of entering the function; * Equivalent to a macro, but with more type checking than a macro, it really has function characteristics; * The compiler generally does not inline inline functions that include complex operations such as loops, recursion, and switch; * Functions defined in class declarations, other than virtual functions, are automatically implicitly treated as inline functions. #### use inline use ```cpp // Statement 1 (plus inline, recommended) inline int functionName(int first, int second,...); // statement 2 (without inline) int functionName(int first, int second,...); // definition inline int functionName(int first, int second,...) {/****/}; // inside class definition, implicitly inline class A { int doA() { return 0; } // implicit inlining } // definition outside the class, need to be explicitly inlined class A { int doA(); } inline int A::doA() { return 0; } // requires explicit inlining ``` #### Compiler processing steps for inline functions 1. Copy the inline function body to the `inline` function call point; 2. Allocate memory space for local variables in the used `inline` function; 3. Map the input parameters and return values of the `inline` function to the local variable space of the calling method; 4. If the `inline` function has multiple return points, turn it into a branch at the end of the inline function code block (using `GOTO`). #### Advantages and disadvantages Advantages 1. Inline functions, like macro functions, perform code expansion at the callee's place, eliminating the need to push parameters on the stack, open and recover stack frames, and return results, etc., thereby improving program execution speed. 2. Compared to macro functions, inline functions do security checks or automatic type conversions (as with normal functions) when code is expanded, while macro definitions do not. 3. Declaring a member function that is also defined in a class automatically converts it into an inline function, so inline functions can access class member variables, while macro definitions cannot. 4. Inline functions are debuggable at runtime, while macro definitions are not. Disadvantages 1. Code bloat. Inlining is at the expense of code bloat (copy), eliminating the overhead of function calls. If the time to execute the code in the function body is greater than the cost of the function call, then the efficiency gain will be small. On the other hand, copying the code for each inline function call will increase the total code size of the program and consume more memory space. 2. The inline function cannot be upgraded with the function library upgrade. Changes to the inline function require recompilation, unlike non-inline, which can be linked directly. 3. Whether it is inline or not is beyond the programmer's control. Inline functions are just suggestions to the compiler. The decision whether to inline functions is up to the compiler. #### Can a virtual function be an inline function? > [Are "inline virtual" member functions ever actually "inlined"?](Http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html) * A virtual function can be an inline function. Inline can modify a virtual function, but it cannot be inlined when the virtual function exhibits polymorphism. * Inlining is recommended by the compiler, and the polymorphism of virtual functions is at runtime. The compiler cannot know which code is called at runtime, so virtual functions cannot be inlined at runtime (runtime). . * `inline virtual` The only time it can be inlined is: the compiler knows which class the object is called (such as `Base::who()` ), only if the compiler has an actual object instead of a pointer or reference to the object Will happen. Virtual function inline use ```cpp #include using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // Implicit inlining when not writing inline { cout << "I am Derived\n"; } }; int main() { // The virtual function who () here is called through the concrete object (b) of the class (Base), which can be determined during compilation, so it can be inlined, but whether it is inlined depends on the compilation Device. Base b; b.who(); // The virtual function here is called through a pointer, which is polymorphic and needs to be determined during runtime, so it cannot be inlined. Base *ptr = new Derived(); ptr->who(); // Because Base has a virtual destructor (virtual ~ Base () {}), when deleting, the Derived destructor is called first, and then the Base destructor is called to prevent memory leaks. delete ptr; ptr = nullptr; system("pause"); return 0; } ``` ### volatile ```cpp volatile int i = 10; ``` * The volatile keyword is a type modifier, and the type variable declared with it indicates that it can be changed by some factors unknown to the compiler (operating system, hardware, other threads, etc.). So using volatile tells the compiler that such objects should not be optimized. * Variables declared by the volatile keyword must be fetched from memory each time they are accessed (variables that are not modified by volatile may be fetched from CPU registers due to compiler optimizations) * const can be volatile (such as a read-only status register) * Pointer can be volatile ### assert() Assertions are macros, not functions. The prototype of the assert macro is defined in `` (C), `` (C ++), and its role is to terminate program execution if its condition returns an error. You can turn off assert by defining `NDEBUG`, but it needs to be at the beginning of the source code, before `include ` . assert () uses ```cpp #define NDEBUG // Add this line, assert is not available #include assert( p != NULL ); // assert is not available ``` ### sizeof() * sizeof For arrays - get the size of the entire array. * sizeof For pointers - get the size of the space occupied by the pointer itself. ### Compiler Extensions vs Standard Alignment Control * Compiler Extension `#pragma pack(n)`, restricts the maximum alignment of members in subsequently defined `struct`/`class`/`union` to n bytes. * Standard Alignment Control: * `alignas(k)`, requires types or variables to be aligned to at least k bytes (rounds up to ≥ natural alignment). * `alignof(T)`, gets the natural alignment requirement of type T (compile-time constant). Feature | `#pragma pack` | `alignas` ----------------|-------------------------|--------------------- Standardization | ❌ Compiler Extension | ✅ C++11 Standard Alignment Direction | ⬇️ Only decreases alignment | ⬆️ Only increases alignment Portability | ❌ Compiler Dependent | ✅ Cross-platform Scope | 🔄 Affects entire struct | 🎯 Per-member control Performance Impact | ⚠️ May reduce memory access speed | ⚠️ Over-alignment wastes space #### Usage Examples ```cpp #include #include #pragma pack(push, 1) // Max alignment 1 byte (compact layout) struct PackedHeader { uint16_t len; // offset 0 uint32_t id; // offset 2 }; #pragma pack(pop) struct alignas(8) Align8 { // Force 8-byte alignment double value; // offset 0 (8 bytes) int flag; // offset 8 }; int main() { std::cout << "PackedHeader size: " << sizeof(PackedHeader) << "\n"; // Output: 6 std::cout << "Align8 size: " << sizeof(Align8) << "\n"; // Output: 16 } ### Bit field ```cpp Bit mode: 2; // mode is 2 digits ``` A class can define its (non-static) data members as bit-fields, which contain a certain number of binary bits in a bit-field. When a program needs to transfer binary data to other programs or hardware devices, the bit field is usually used. * The layout of the bit field in memory is machine-dependent * The type of the bit field must be an integer or enumerated type. The behavior of the bit field in a signed type will depend on the implementation. * The fetch operator (&) cannot be applied to the bit field, and no pointer can point to the bit field of the class ### `extern` vs `extern "C"` * `extern` is a **storage-class specifier** used to declare that a variable or function has **external linkage**, indicating that the entity's definition may reside in another translation unit. * `extern "C"` is a **linkage directive** that specifies functions or variables should use **C language linkage** (without affecting compilation rules). 1. **Suppresses C++ name mangling**: Ensures symbol names match those generated by the C compiler _on that specific platform_, preventing undefined symbol errors during linking due to name decoration. **Does not guarantee platform ABI (Application Binary Interface) consistency**. 2. **Enables C/C++ interoperability**: Allows C++ functions to be called from C code (and vice versa). `extern "C"` demo ```cpp #ifdef __cplusplus extern "C" { #endif void *memset(void *, int, size_t); #ifdef __cplusplus } #endif ``` ### struct with typedef struct #### In C ```c // c typedef struct Student { int age; } S; ``` Equivalent to ```c // c struct Student { int age; }; typedef struct Student S; ``` At this time `S` is equivalent to` struct Student`, but the two identifier namespaces are different. You can also define `void Student(){}` that does not conflict with `struct Student` . #### In C++ Because the compiler's rules for positioning symbols (search rules) have changed, it is different from the C language. First, if `struct Student {...};` is defined in the class identifier space, when `Student me;` is used, the compiler will search the global identifier table. If `Student` is not found, it will be in the class identifier. search for. That means it can use `Student` or `struct Student`, as follows: ```cpp // cpp struct Student { int age; }; void f( Student me ); // correct, the "struct" keyword can be omitted ``` If a function with the same name as `Student` is defined, `Student` only represents the function, not the structure, as follows: ```cpp typedef struct Student { int age; } S; void Student() {} //Correct, "Student" only represents this function after definition //void S() {} // Error, symbol "S" has been defined as an alias for "struct Student" int main() { Student(); struct Student me; // Or "S me"; return 0; } ``` ### struct and class in C ++ In general, struct is more suitable as an implementation of a data structure, and class is more suitable as an implementation of an object. #### The difference * The most essential difference is the default access control 1. Default inherited access rights. struct is public and class is private. 2. struct as the data structure implementation body, its default data access control is public, and class as the object implementation body, its default member variable access control is private. ### union union Union is a special class that saves space. A union can have multiple data members, but only one data member can have a value at any time. When a member is assigned, other members become undefined. Union has the following characteristics: * The default access control character is public * May contain constructors and destructors * Cannot contain members of reference type * Cannot inherit from other classes and cannot be used as a base class * Cannot contain virtual functions * Anonymous union can directly access union members in the scope where it is defined * Anonymous union cannot contain protected members or private members * Global anonymous union must be static union demo ```cpp #include union UnionTest { UnionTest() : i(10) {}; int i; double d; }; static union { int i; double d; }; int main() { UnionTest u; union { int i; double d; }; std::cout << u.i << std::endl; // Output UnionTest union 10 ::i = 20; std::cout << ::i << std::endl; // Output global static anonymous union 20 i = 30; std::cout << i << std::endl; // Output of locally anonymous union of 30 return 0; } ``` ### C implements C ++ classes C implements object-oriented features of C ++ (encapsulation, inheritance, polymorphism) * Encapsulation: Use function pointers to encapsulate properties and methods into structures * Inheritance: structure nesting * Polymorphism: function pointers of parent and child methods are different > [Can you write object-oriented code in C? [closed]](https://stackoverflow.com/a/351745) ### explicit (keyword) * explicit constructor modification prevents implicit conversion and copy initialization * explicit conversions can prevent implicit conversions, except [conversion by context](https://en.cppreference.com/w/cpp/language/implicit_conversion) explicit demo ```cpp struct A { A(int) { } operator bool() const { return true; } }; struct B { explicit B(int) {} explicit operator bool() const { return true; } }; void doA(A a) {} void doB(B b) {} int main() { A a1(1); // OK:direct initialization A a2 = 1; // OK:copy initialization A a3{ 1 }; // OK:direct list initialization A a4 = { 1 }; // OK:copy list initialization A a5 = (A)1; // OK:Allow explicit conversion of static_cast doA(1); // OK:Allow implicit conversion from int to A if (a1); // OK: implicit conversion from A to bool using conversion function A ::operator bool() bool a6(a1); // OK: implicit conversion from A to bool using conversion function A::operator bool() bool a7 = a1; // OK: implicit conversion from A to bool using conversion function A::operator bool() bool a8 = static_cast(a1); // OK: static_cast for direct initialization B b1(1); // OK:direct initialization B b2 = 1; // Error: Object modified by explicit constructor cannot be initialized by copying B b3{ 1 }; // OK:direct list initialization B b4 = { 1 }; // Error: Object modified by explicit constructor cannot copy list initialization B b5 = (B)1; // OK: Allow explicit conversion of static_cast doB(1); // Error: Objects whose constructor is explicitly modified cannot be implicitly converted from int to B if (b1); // OK: objects modified by explicit conversion function B::operator bool() can be converted from B to bool by context bool b6(b1); // OK: Explicitly modified conversion function B::operator The object of bool() can be converted from B to bool by context bool b7 = b1; // Error: Objects modified by explicit conversion function B :: operator bool () cannot be implicitly converted bool b8 = static_cast(b1); // OK: static_cast performs direct initialization return 0; } ``` ### friend - friend class and friend function * Access to private members * Destruction of encapsulation * Friendship is not transitive * One-way friendship * There are no restrictions on the form and number of friend declarations ### using #### using statement A `using declaration` introduces only one member of a namespace at a time. It allows us to know exactly which name is referenced in the program. Such as: ```cpp using namespace_name::name; ``` #### Using declaration of constructor In C ++ 11, a derived class can reuse the constructor defined by its direct base class. ```cpp class Derived : Base { public: using Base::Base; /* ... */ }; ``` As above using statement, for each constructor of the base class, the compiler generates a derived class constructor corresponding to it (the parameter list is exactly the same). Generates the following type constructor: ```cpp Derived (parms): Base (args) {} ``` #### using instructions The `using directive` makes all names in a particular namespace visible, so we don't need to add any prefix qualifiers to them. Such as: ```cpp using namespace namespace_name; ``` #### Minimize `using directives` to pollute namespaces > Generally speaking, it is safer to use the using command than the using compile command, because it** imports only the specified name**. If the name conflicts with a local name, the compiler will** issue instructions**. The using compile command imports all names, including names that may not be needed. If there is a conflict with a local name, the local name will override the namespace version, and the compiler will not issue a warning. In addition, the openness of the namespace means that the names of the namespace may be scattered in multiple places, which makes it difficult to know exactly which names have been added. using demo Minimize `using directives` ```cpp using namespace std; ``` You should use `using declarations` more often ```cpp int x; std::cin >> x ; std::cout << x << std::endl; ``` or ```cpp using std::cin; using std::cout; using std::endl; int x; cin >> x; cout << x << endl; ``` ### :: scope resolution operator #### classification 1. Global scope (`:: name`): used before type names (classes, class members, member functions, variables, etc.) to indicate that the scope is a global namespace 2. Class scope character (`class :: name`): used to indicate that the scope of the specified type is specific to a class 3. Namespace scope (`namespace :: name`): used to indicate that the scope of the specified type is specific to a namespace :: demo ```cpp int count = 11; // Global (: :) count class A { public: static int count; // Count (A::count) of class A }; int A::count = 21; void fun() { int count = 31; // Initialize the local count to 31 count = 32; // Set the local count to 32 } int main() { ::count = 12; // Test 1: Set the global count to 12 A::count = 22; // Test 2: Set the count of class A to 22 fun(); // Test 3 return 0; } ``` ### enum - enum type #### Scoped Enumeration Type ```cpp enum class open_modes { input, output, append }; ``` #### Unscoped enumeration type ```cpp enum color { red, yellow, green }; enum { floatPrec = 6, doublePrec = 10 }; ``` ### decltype decltype keyword is used to check the declared type or expression type and value classification of an entity. grammar: ```cpp decltype ( expression ) ``` decltype demo ```cpp // Tail return allows us to declare the return type after the parameter list template auto fcn(It beg, It end) -> decltype(*beg) { / process the sequence return *beg; // return a reference to an element in the sequence } // In order to use template parameter members, you must use typename template auto fcn2(It beg, It end) -> typename remove_reference::type { // process the sequence return * beg; // return a copy of an element in the sequence } ``` ### reference #### lvalue reference Regular reference, which generally represents the identity of the object. #### rvalue reference An rvalue reference is a reference that must be bound to an rvalue (a temporary object, an object to be destroyed) and generally represents the value of the object. An rvalue reference implements Move Sementics and Perfect Forwarding. Its main purpose is twofold: * Eliminate unnecessary copying of objects when two objects interact, saving computing storage resources and improving efficiency. * Ability to define generic functions more concisely. #### Reference Collapse * `X & &`, `X & &&`, `X && &` can be folded into `X &` * `X && &&` can be folded into `X &&` ### Macro * A macro definition can implement a function similar to a function, but it is not a function after all, and the "parameters" in the brackets in the macro definition are not real parameters. The "parameters" are replaced one-to-one when the macro is expanded . ### Member initialization list benefit * More efficient: There is no need to call the default constructor once. * In some cases it is necessary to use the initialization list: 1. Constant members, because constants can only be initialized and cannot be assigned, so they must be placed in the initialization list 2. Reference types. References must be initialized at the time of definition and cannot be reassigned, so they must also be written in the initialization list. 3. There is no class type for the default constructor, because the initialization list can be used to initialize without having to call the default constructor. ### initializer_list list initialization Initialize an object with a curly brace initializer list, where the corresponding constructor accepts a `std :: initializer_list` parameter. initializer_list uses ```cpp #include #include #include template struct S { std::vector v; S(std::initializer_list l) : v(l) { std::cout << "constructed with a " << l.size() << "-element list\n"; } void append(std::initializer_list l) { v.insert(v.end(), l.begin(), l.end()); } std::pair c_arr() const { return {&v[0], v.size()}; // Copy the list initialization in the return statement // this does not use std :: initializer_list } }; template void templated_fn(T) {} int main() { S s = {1, 2, 3, 4, 5}; // copy initialization s.append({6, 7, 8}); // list initialization in function call std::cout << "The vector size is now " << s.c_arr().second << " ints:\n"; for (auto n : s.v) std::cout << n << ' '; std::cout << '\n'; std::cout << "Range-for over brace-init-list: \n"; for (int x : {-1, -2, -3}) // auto rules make this band for work std::cout << x << ' '; std::cout << '\n'; auto al = {10, 11, 12}; // special rules for auto std::cout << "The list bound to auto has size() = " << al.size() << '\n'; // templated_fn({1, 2, 3}); // Compile error! "{1, 2, 3}" is not an expression, // it has no type, so T cannot infer templated_fn>({1, 2, 3}); // OK templated_fn>({1, 2, 3}); // also OK } ``` ### Object-oriented Object-oriented programming (OOP) is a model of program programming with object concepts, and it is also an abstract approach to program development. ![Object-oriented features](https://raw.githubusercontent.com/huihut/interview/master/images/面向对象基本特征.png) Three Object-Oriented Features-Encapsulation, Inheritance, and Polymorphism ### Encapsulation Encapsulate objective things into abstract classes, and classes can only use their own data and methods for trusted classes or objects to operate, and hide untrusted information. Keywords: public, protected, private. Do not write defaults to private. * `public` members: can be accessed by any entity * `protected` members: only allowed to be accessed by subclasses and member functions of this class * `private` members: only accessible by member functions, friend classes, or friend functions of this class ### Inheritance * Base class (parent class) ——> derived class (subclass) ### Polymorphism * Polymorphism, that is, multiple states (morphology). In simple terms, we can define polymorphism as the ability of a message to be displayed in multiple forms. * Polymorphism is based on encapsulation and inheritance. * C polymorphism classification and implementation: 1. Ad-hoc Polymorphism (compile-time): function overload, operator overload 2. Subtype Polymorphism (runtime): virtual function 3. Parametric Polymorphism (compile-time): class template, function template 4. Coercion Polymorphism (compilation / runtime): basic type conversion, custom type conversion > [The Four Polymorphisms in C++](https://catonmat.net/cpp-polymorphism) #### Static polymorphism (compile time / early binding) Function overloading ```cpp class A { public: void do(int a); void do(int a, int b); }; ``` #### Dynamic polymorphism (runtime / late binding) * Virtual functions: decorate member functions with virtual to make them virtual * Dynamic binding: dynamic binding occurs when a virtual function is called using a reference or pointer to a base class **note:** * You can assign an object of a derived class to a pointer or reference of the base class, and not vice versa * Ordinary functions (non-class member functions) cannot be virtual functions * Static functions (static) cannot be virtual functions * The constructor cannot be a virtual function (because when the constructor is called, the virtual table pointer is not in the object's memory space, the virtual table pointer must be formed after the constructor is called) * An inline function cannot be a virtual function when it shows polymorphism. For an explanation, see: [Can a virtual function be an inline function? ](https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97) Dynamic polymorphic demo ```cpp class Shape // shape class { public: virtual double calcArea() { ... } virtual ~Shape(); }; class Circle : public Shape // circle class { public: virtual double calcArea(); ... }; class Rect : public Shape // rectangle class { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); Shape * shape2 = new Rect(5.0, 6.0); shape1->calcArea(); // call the method in the circular class shape2->calcArea(); // call the method in the rectangle class delete shape1; shape1 = nullptr; delete shape2; shape2 = nullptr; return 0; } ``` ### Virtual destructor The virtual destructor is to resolve the pointer of the base class to the derived class object, and delete the derived class object with the pointer of the base class. Virtual destructor demo ```cpp class Shape { public: Shape(); // Constructor cannot be virtual virtual double calcArea(); virtual ~Shape(); // virtual destructor }; class Circle : public Shape // circle class { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); shape1->calcArea(); delete shape1; // Because Shape has a virtual destructor, when delete deletes the memory, it first calls the subclass destructor and then the base class destructor to prevent memory leaks. shape1 = NULL; return 0; } ``` ### Pure virtual functions A pure virtual function is a special kind of virtual function. You cannot give a meaningful implementation to a virtual function in a base class. Instead, you declare it as a pure virtual function. Its implementation is left to the derived classes of the base class. ```cpp virtual int A() = 0; ``` ### Virtual functions, pure virtual functions * If a virtual function is declared in the class, this function is implemented, even if it is empty, its role is to allow this function to be overridden in its subclasses, so that the compiler can use Late binding to achieve polymorphism. A pure virtual function is just an interface. It is a function declaration. It must be left in a subclass to implement it. * Virtual functions can not be overridden in subclasses; but pure virtual functions must be implemented in subclasses to instantiate subclasses. * The virtual function class is used for "implementation inheritance". Inheriting the interface also inherits the implementation of the parent class. Pure virtual functions focus on the uniformity of the interface, and the implementation is done by subclasses. * A class with a pure virtual function is called an abstract class. This class cannot directly generate objects. It can only be used after being inherited and rewriting its virtual function. After an abstract class is inherited, subclasses can continue to be abstract or ordinary classes. * Virtual base class is the base class in virtual inheritance, see below for details. > [CSDN . C++ And connection of virtual functions and pure virtual functions](https://blog.csdn.net/u012260238/article/details/53610462) ### Virtual function pointer, virtual function table * Virtual function pointer: In an object containing a virtual function class, it points to a virtual function table, which is determined at runtime. * Virtual function table: in the program read-only data section (`.rodata section`, see: [object file storage structure](#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84)),Stores virtual function pointers. If the derived class implements a virtual function of the base class, the virtual function pointer of the original base class is overwritten in the virtual table, and is created according to the class declaration at compile time. > [C++ Function (table) implementation mechanism and simulation implementation in C language](https://blog.twofei.com/496/) ### Virtual inheritance Virtual inheritance is used to solve the problem of diamond inheritance under multiple inheritance conditions (wasting storage space and ambiguity). The underlying implementation principle is related to the compiler. It is generally implemented by ** virtual base class pointer ** and ** virtual base class table **. Each virtual inherited subclass has a virtual base class pointer (occupies the storage space of a pointer) , 4 bytes) and virtual base class table (does not occupy storage space of class objects) (It should be emphasized that the virtual base class will still have a copy in the subclass, but there is only one copy at most, not not in the subclass. Inside); when the subclass of virtual inheritance is inherited as the parent class, the pointer of the virtual base class will also be inherited. In fact, vbptr refers to a virtual base table pointer. This pointer points to a virtual base table. The virtual table records the offset address of the virtual base class and this class. Offset address, so that the virtual base class members are found, and virtual inheritance does not need to maintain two identical copies of the public base class (virtual base class) like ordinary multiple inheritance, saving storage space. ### Virtual inheritance, virtual functions * Similarities: Both use virtual pointers (both occupy class storage space) and virtual tables (both do not occupy class storage space) * the difference: * Virtual inheritance * The virtual base class still exists in the inherited class and only takes up storage space * The virtual base class table stores the offset of the virtual base class relative to the direct inherited class * Virtual function * Virtual functions do not take up storage space * The virtual function table stores the virtual function address ### Template classes, member templates, virtual functions * Virtual functions can be used in template classes * The member template of a class (whether it is a normal class or a class template) (it is a member function of the template) cannot be a virtual function ### Abstract class, interface class, aggregate class * Abstract class: a class containing pure virtual functions * Interface class: Abstract class containing only pure virtual functions * Aggregation classes: Users can directly access their members and have a special form of initialization syntax. Meet the following characteristics: * All members are public * No constructor is defined * No in-class initialization * No base class, no virtual function ### Memory allocation and management #### malloc, calloc, realloc, alloca 1. malloc: apply for a specified number of bytes of memory. The initial value in the requested memory is uncertain. 2. calloc: For an object of a specified length, allocate memory that can hold the specified number of objects. Each bit of the requested memory is initialized to 0. 3. realloc: change the previously allocated memory length (increase or decrease). When increasing the length, it may be necessary to move the contents of the previously allocated area to another sufficiently large area, while the initial value in the newly added area is uncertain. 4. alloca: apply for memory on the stack. When the program pops out of the stack, it will automatically release memory. However, it should be noted that alloca is not portable and difficult to implement on machines without traditional stacks. alloca should not be used in programs that must be widely ported. C99 supports variable-length arrays (VLAs) and can be used instead of alloca. #### malloc, free Used to allocate and release memory malloc, free use Apply for memory and confirm whether the application is successful ```cpp char *str = (char*) malloc(100); assert(str != nullptr); ``` Pointer is empty after freeing memory ```cpp free(p); p = nullptr; ``` #### new、delete 1. new / new []: Do two things, first call malloc at the bottom to allocate memory, and then call the constructor (create the object). 2. delete / delete []: also complete two things, first call the destructor (clean up resources), and then call free to free up space at the bottom. 3. new automatically calculates the number of bytes required when applying for memory, and malloc requires us to enter the number of bytes of the requested memory space ourselves. new, delete demo Apply for memory and confirm whether the application is successful ```cpp int main() { T* t = new T(); // memory allocation first, then constructor delete t; // destructor first, then release memory return 0; } ``` #### Positioning new Positioning new (placement new) allows us to pass additional address parameters to new to create objects in a pre-specified memory area. ```cpp new (place_address) type new (place_address) type (initializers) new (place_address) type [size] new (place_address) type [size] { braced initializer list } ``` * `place_address` is a pointer * `initializers` provides a (possibly empty) comma-separated list of initial values ### delete this - Is it legal? > [Is it legal (and moral) for a member function to say delete this?](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) Legal, but: 1. It must be guaranteed that this object is allocated via `new` (not `new[]`, not placement new, not on the stack, not global, not a member of other objects) 2. You must ensure that the member function that calls delete this is the last member function that calls this 3. You must ensure that the member function does not call this after `delete this` 4. Make sure no one uses it after delete this ### Smart pointer #### In the C ++ Standard Library (STL) header file:`#include ` #### C++ 98 ```cpp std::auto_ptr ps (new std::string(str)); ``` #### C++ 11 1. shared_ptr 2. unique_ptr 3. weak_ptr 4. auto_ptr (Deprecated by C ++ 11) * Class shared_ptr implements the concept of shared ownership. Multiple smart pointers point to the same object, and that object and its associated resources are released when the "last reference is destroyed". In order to perform the above work in more complex scenarios, the standard library provides auxiliary classes such as weak_ptr, bad_weak_ptr, and enable_shared_from_this. * Class unique_ptr implements the concept of exclusive ownership or strict ownership, ensuring that only one smart pointer can point to the object at a time. You can transfer ownership. It is especially useful for avoiding resource leaks, such as forgetting to delete after new. ##### shared_ptr Multiple smart pointers can share the same object, and the last one of the object has the responsibility to destroy the object and clean up all resources related to the object. * Support custom deleter, which can prevent Cross-DLL problems (object is created by new in dynamic link library (DLL), but deleted by another DLL), and the mutex is automatically released ##### weak_ptr weak_ptr allows you to share but does not own an object. Once the last smart pointer that owns the object loses ownership, any weak_ptr will automatically become empty. Therefore, in addition to the default and copy constructors, weak_ptr only provides a "accept a shared_ptr" constructor. * The problem of cycles of references (two objects that have not been used actually refer to each other, making them appear to be in the "used" state) ##### unique_ptr unique_ptr is a type that has only been available since C ++ 11, and is a smart pointer that can help avoid resource leaks during exceptions. Using exclusive ownership means that you can ensure that an object and its corresponding resource are owned by only one pointer at a time. Once you own the destroyed or programmed empty, or start owning another object, the previously owned object will be destroyed and any corresponding resources will be released. * unique_ptr is used instead of auto_ptr ##### auto_ptr Deprecated by c ++ 11 due to lack of language features such as `std::move` semantics "for construction and assignment", and other flaws. ##### auto_ptr compared to unique_ptr * auto_ptr can be assigned a copy, and ownership is transferred after copying; unqiue_ptr has no copy assignment semantics, but implements `move` semantics; * auto_ptr objects cannot manage arrays (destructive call `delete`), unique_ptr can manage arrays (destructive call` delete [] `); ### Casting Operator > [MSDN. Casting Operator](https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx) #### static_cast * Compile-time type conversion (no runtime checks) * Does not depend on RTTI | Conversion Type | Safety | Example | |--------------------------|--------------|--------------------------| | Numeric conversion | ✅ Safe | `float f=3.14; int i=static_cast(f);` | | Upcast (class hierarchy) | ✅ Safe | `Derived* d; Base* b=static_cast(d);` | | Downcast (class hierarchy)| ⚠️ Unsafe | `Base* b=new Base; Derived* d=static_cast(b);` | | Same-type conversion | ✅ Safe | `MyClass* p; MyClass* same=static_cast(p);` | | Explicit constructor call| ✅ Safe | `func(static_cast("text"));` | | Any type→void* | ✅ Safe | `int* p; void* vp=static_cast(p);` | | Enum↔Integer | ✅ Safe | `enum Color{RED}; int c=static_cast(RED);` | #### dynamic_cast * Runtime type checking (depends on RTTI) * Requires polymorphic type (base must have at least one virtual function) * Safe failure mechanism (nullptr or exception) | Conversion Type | Safety | Runtime Cost | Polymorphic Required | Failure Handling | Example | |---------------------|--------|--------------|----------------------|---------------------------|---------| | Upcast | ✅ Safe | None | ❌ Not required | Not applicable (always succeeds) | `Derived* d; Base* b = dynamic_cast(d);` | | Downcast | ✅ Safe | Yes | ✅ Required | Pointer→`nullptr`
Reference→`std::bad_cast` | `Base* b; Derived* d = dynamic_cast(b);` | | Cross-cast | ✅ Safe | Yes | ✅ Required | Pointer→`nullptr`
Reference→`std::bad_cast` | `B2* b2 = dynamic_cast(b1); // In diamond inheritance` | | Same-type conversion| ✅ Safe | None | ❌ Not required | Not applicable (always succeeds) | `Derived* d2 = dynamic_cast(d1);` | | Any type→void* | ✅ Safe | Yes | ✅ Required | `nullptr` | `void* p = dynamic_cast(obj);` | #### const_cast * Compile-time type modifier operation * Only modifies `const`/`volatile` attributes * Does not change underlying binary representation | Conversion Type | Safety | Example | |------------------------|--------------|----------------------------------| | Remove const | ⚠️ Risky | `const int* cp; int* p=const_cast(cp);` | | Remove volatile | ⚠️ Risky | `volatile int* vp; int* p=const_cast(vp);` | | Add const | ✅ Safe | `int* p; const int* cp=const_cast(p);` | | Legacy API compatibility | ⚠️ Necessary risk | `legacy_api(const_cast(str.c_str()));` | #### reinterpret_cast * No compile-time type safety checks * Binary bit-level reinterpretation * Most dangerous cast operator | Conversion Type | Safety | Example | |------------------------|--------------|----------------------------------| | Pointer↔Pointer | ❌ High risk | `MyClass* obj; void* p=reinterpret_cast(obj);` | | Pointer↔Integer | ❌ High risk | `intptr_t addr=reinterpret_cast(&obj);` | | Function pointer conversion | ❌ Extreme risk | Converting function pointers with different signatures | | Memory-mapped I/O | ⚠️ Systems programming | Hardware register access | #### bad_cast * The exception type for a failed reference conversion using `dynamic_cast`. bad_cast demo ```cpp try { Circle& ref_circle = dynamic_cast(ref_shape); } catch (bad_cast b) { cout << "Caught: " << b.what(); } ``` ### Runtime Type Information (RTTI) #### typeid * The typeid operator allows determining the type of an object at runtime * typeid returns a reference to a type\_info object * If you want to get the data type of the derived class through the pointer of the base class, the base class must have a virtual function * Can only get the actual type of the object #### type_info * The type_info class describes the type information generated by the compiler in the program. Objects of this class can effectively store pointers to the names of types. The type_info class can also store encoded values suitable for comparing whether two types are equal or comparing their permutation order. The encoding rules and permutation order for the types are unspecified and may vary from program to program. * Header file: `typeinfo` typeid, type_info demo ```cpp #include using namespace std; class Flyable // can fly { public: virtual void takeoff() = 0; // take off virtual void land() = 0; // land }; class Bird : public Flyable // bird { public: void foraging() {...} // foraging virtual void takeoff() {...} virtual void land() {...} virtual ~Bird(){} }; class Plane : public Flyable // airplane { public: void carry() {...} // carry virtual void takeoff() {...} virtual void land() {...} }; class type_info { public: const char* name() const; bool operator == (const type_info & rhs) const; bool operator != (const type_info & rhs) const; int before(const type_info & rhs) const; virtual ~type_info(); private: ... }; void doSomething(Flyable *obj) // do something { obj->takeoff(); cout << typeid(*obj).name() << endl; // output the type of the incoming object("class Bird" or "class Plane") if(typeid(*obj) == typeid(Bird)) // determine object type { Bird *bird = dynamic_cast(obj); // object conversion bird->foraging(); } obj->land(); } int main(){ Bird *b = new Bird(); doSomething(b); delete b; b = nullptr; return 0; } ``` ## ⭐️ Effective ### Effective C++ 1. Treat C ++ as a language federation (C, Object-Oriented C ++, Template C ++, STL) 2. It is better to replace the preprocessor with a compiler (try to replace `# define` with `const`, `enum`, `inline`) 3. Use const whenever possible 4. Make sure that the object has been initialized before being used (the copy constructor is more efficient than the default copy assignment after construction) 5. Understand what functions C ++ silently writes and calls (the compiler secretly creates a default constructor, copy constructor, copy assignment operator, destructor for class) 6. If you don't want to use the automatically generated function of the compiler, you should explicitly reject it (declare member functions that you don't want to use as private and do not implement them) 7. Declare a virtual destructor for the polymorphic base class (if the class has any virtual functions, it should have a virtual destructor) 8. Don't let the exception escape the destructor (the destructor should swallow and not propagate the exception, or end the program, instead of spitting out the exception; if you want to handle the exception, you should handle it in a non-destructed ordinary function) 9. Never call virtual functions during construction and destruction (because such calls never descend to the derived class) 10. Let `operator =` return a `reference to * this` (for chained assignment) 11. Handle "self-assignment" in `operator =` 12. When assigning an object, be sure to copy "all member variables within the object" and "all base class components" (call the base class copy constructor) 13. Manage resources by objects (resources are obtained in the constructor and released in the destructor. It is recommended to use smart pointers. The resource acquisition time is the initialization time (Resource Acquisition Is Initialization (RAII)). 14. Be careful with copying behavior in resource management classes (the general RAII class copying behavior is: inhibit copying, reference counting, deep copying, transfer of bottom resource ownership (similar to auto_ptr)) 15. Provide access to raw resources in the resource management class (access to raw resources may be explicitly or implicitly converted, in general, display conversion is safer, and implicit conversion is more convenient for customers) 16. Use the same form when using new and delete in pairs (`[]` in `new` then` delete []`, `new` without `[]` and `delete`) 17. Store the newed object in (into) a smart pointer in a separate statement (if you do not do this, it may cause undetectable resource leaks due to compiler optimizations) 18. Make interfaces easy to use correctly and not easy to be misused (Promote normal use: Consistency of interfaces, compatible behavior of built-in types; Prevent misuse: Create new types, Restrict operation on types, Constrain object values Eliminate the client's resource management responsibilities) 19. Designing a class is like designing a type, and you need to consider object creation, destruction, initialization, assignment, value passing, legal values, inheritance relationships, conversion, generalization, and so on. 20. Prefer pass-by-reference-to-const instead of pass-by-value (the former is usually more efficient and avoids slicing problems, but not applicable to built-in types, STL iterators, function objects) 21. When an object must be returned, do not deliberately return its reference (never return pointer or reference to a local stack object, or return reference to a heap-allocated object, or return pointer or reference to a local static object, which may be required at the same time. Multiple such objects.) 22. Declare member variables as private (for encapsulation, consistency, precise control over reads and writes, etc.) 23. Rather replace the member function with non-member and non-friend (which can increase packaging flexibility, packaging flexibility, and functional extensibility) 24. If all parameters (including the metaphoric parameter pointed to by this pointer) require type conversion, please use a non-member function for this 25. Consider writing a swap function that does not throw exceptions 26. Delay the appearance of variable definitions as much as possible (to increase program clarity and improve program efficiency) 27. Do as few transformations as possible (old: `(T) expression`,` T (expression) `; new:` const_cast (expression) `,` dynamic_cast (expression) `,` reinterpret_cast (expression) `,` static_cast (expression) `;; try to avoid transformation, pay attention to efficiency and avoid dynamic_casts, try to design so that no transformation is needed, the transformation can be encapsulated into a function, rather a new type of transformation is preferred) 28. Avoid using handles (including references, pointers, iterators) to point inside the object (to increase encapsulation, make const member functions behave more like const, and reduce "dangling handles" (such as dangling pointers, etc.) possibility) 29. It is worthwhile to work for "exception safety" (Exception-safe functions) will not leak resources or allow any data structure to break even if an exception occurs. There are three possible guarantees: basic, strong Type, do not throw abnormal type) 30. Thorough understanding of inlining inside and outside (inlining is a compile-time behavior in most C ++ programs; whether an inline function is really inline depends on the compiler; most compilers refuse to be too complex (such as with loops or recursion) ) Function inlining, and all calls to virtual functions (unless they are the most bland) will also cause inlining to fail; the code expansion caused by inline may cause a loss of efficiency; inline functions cannot be upgraded with the upgrade of the library) 31. Minimize the compilation dependencies between files (if you can use object references or object pointers to complete the task, do not use objects; if possible, try to replace class definitions with class declarations; provide differences between declarative and definitions Header file) 32. Make sure your public inheritance moulds out an is-a (is a) relationship (applicable to everything in base classes must apply to derived classes, because every derived class object is also a base class object ) 33. Avoid obscuring inherited names (you can use using declarative or forwarding functions to make the obscure names goodbye) 34. Distinguish between interface inheritance and implementation inheritance (under public inheritance, derived classes always inherit the interface of the base class; pure virtual functions only specify interface inheritance; non-pure virtual virtual functions specify interface inheritance and default implementation inheritance; non -virtual function specifies interface inheritance and mandatory implementation inheritance) 35. Consider alternatives to virtual functions (such as the non-virtual interface (NVI) approach of the Template Method design pattern, replacing virtual functions with "function pointer member variables" and replacing virtual functions with `tr1 :: function` member variables, Replace the virtual function in the inheritance system with another virtual function in the inheritance system) 36. Never redefine inherited non-virtual functions 37. Never redefine inherited default parameter values, because the default parameter value is statically bound (statically bound), but the virtual function is dynamically bound (dynamically bound) 38. Through compound molding has-a (one) or "implemented according to something" (in the application domain, compound means has-a (there is one); in the implementation domain, compound means (Is-implemented-in-terms-of) 39. Use private inheritance wisely and prudently (private inheritance means is-implemented-in-terms-of (implemented according to something)), use composite whenever possible, when the derived class needs to access the members of the protected base class, or needs to re- Use private inheritance when defining inherited virtual functions, or when empty base optimization is required) 40. Use multiple inheritance wisely and prudently (multiple inheritance is more complex than single inheritance, may lead to new ambiguities, and the need for virtual inheritance, but it does have a legitimate purpose, such as "public inheritance of an interface class" and "private inheritance A class that assists implementation "; virtual inheritance can solve the ambiguity of diamond inheritance under multiple inheritance, but it will increase the size, speed, complexity of initialization and assignment, etc.) 41. Understand implicit interfaces and compile-time polymorphism (both classes and templates support interfaces and polymorphism); class interfaces are explicit with signatures as the center, and polymorphism is through virtual Functions occur at runtime; the interface of a template is implicit based on valid expressions, and polymorphism occurs through comprehension of templates and function overloading resolution at compile time) 42. Understand the double meaning of typename (declared that the template type parameter is that the meaning of the prefix keywords class and typename are exactly the same; use the keyword typename to identify the nested dependent type name, but not in the base class lists or members Use it as a base class modifier in the member initialization list) 43. Learn to deal with the names in templated base classes (you can refer to the member names in base class templates via `this->` in derived class templates, or by a clearly written "base class qualification modifier" ) 44. Extract parameter-independent code from templates (code bloat caused by non-type template parameters can often be eliminated by replacing template parameters with function parameters or class member variables; because of type parameters The resulting code bloat can often be achieved by having implementation types with identical binary representations share implementation codes) 45. Use member function templates to accept all compatible types (please use member function templates to generate functions that "accept all compatible types"; declare member templates for "generalized copy construction" or "generalized assignment operation" Also need to declare the normal copy constructor and copy assignment operator) 46. ​​When you need type conversion, please define non-member functions for the template (when we write a class template, and the "relevant to this template" function provided by it supports "implicit type conversion of all parameters", please those functions Defined as "friend function inside class template") 47. Please use traits classes to represent type information (traits classes use templates and "templates specialization" to make "type-related information" available at compile time, and use overloading (overloading) to implement if ... else on types at compile time test) 48. Recognize template metaprogramming (TMP, template metaprogramming) ### More Effective c++ 1. Carefully distinguish between pointers and references (when you know that you need to point to something and never change to point to other things, or when you implement an operator whose syntax requirements cannot be met by pointers, you should choose references; At any other time, use pointers) 2. It is best to use C ++ cast operators (`static_cast`, `const_cast`, `dynamic_cast`, `reinterpret_cast`) 3. Never polymorphically treat arrays (polymorphism and pointer arithmetic cannot be mixed; array objects almost always involve pointer arithmetic, so arrays and polymorphism should not be mixed) 4. Don't provide default constructor if necessary (to avoid fields in the object being initialized meaninglessly) 5. Be alert to custom "type conversion functions" (single argument constructors can be avoided by simple methods (explicit keywords) or proxy classes); implicit type conversion operators can be changed to explicit Member function to avoid unexpected behavior) 6. Distinguish the prefix and postfix forms of the increment / decrement operator (pre-accumulate and take out and return a reference; post-preparation take and accumulate and return a const object; when processing user-defined types Should use pre-increment as much as possible; post-implementation should be based on its pre-brother) 7. Never overload the `&&`, `||`, and `,` operators (`&&` and `||` overloading will replace “sudden-semantic semantics” with “function call semantics”; `The overload of` does not guarantee that the left expression must be evaluated earlier than the right expression) 8. Understand the different meanings of new and delete ( `new operator`, `operator new`, `placement new`, `operator new[]` ; `delete operator`, `operator delete`, `destructor`, `operator delete[]` ) 9. Use destructors to avoid leaking resources (freeing resources when destructors can avoid resource leaks during exceptions) 10. Prevent resource leaks in constructors (because C ++ will only destruct objects that have already been constructed, the constructor can use try ... catch or auto_ptr (and similar classes) to handle resource leaks when exceptions occur) 11. Prohibit exceptions from flowing out of destructors (reason: first, avoid the terminate function being called in the stack-unwinding mechanism of exception propagation; second, help ensure that destructors complete everything they should do) 12. Understand the difference between "throwing an exception" and "passing a parameter" or "calling a virtual function" (first, exception objects are always copied (except by pointer), if you catch even by value It is copied twice, but the object passed to the function parameter does not necessarily have to be copied. Second, the object that is "thrown as exceptions" has fewer allowed type conversion actions than the object "passed to the function"; Third, the catch clause is checked by the compiler for its "order in which it appears in the source code". The first match succeeds and is executed, and a virtual function is called. Function " 13. Capture exceptions by by reference (to avoid the problem of object deletion and cutting of exception objects, retain the ability to catch standard exceptions, and restrict the number of times an exception object needs to be copied) 14. Use exception specifications wisely (exception specifications provide an excellent description of what kind of exceptions a function expects to throw; there are also some disadvantages, including that the compiler only checks them locally and is easy to inadvertently violate them. Prevent higher-level exception handlers from handling unexpected exceptions) 15. Understand the cost of exception handling (roughly estimated, if try block is used, the overall code will expand by about 5% -10%, and the execution speed will also decrease by this number; therefore, please limit your use of try block and exception specifications Must-use locations, and throw exceptions only in case of real exceptions) 16. Keep in mind the 80-20 rule (the overall performance of software is almost always determined by a small part of its constituent elements (codes), and the code that consumes resources can be identified using a program profiler) 17. Consider using lazy evaluation (can be applied to: Reference Counting to avoid unnecessary object copying, distinguish read and write actions of operator [] to do different things, Lazy Fetching (easy (Remove) to avoid unnecessary database reads, and Lazy Expression Evaluation (to avoid unnecessary numerical calculations) 18. Amortize the expected computational cost (when you must support certain operations whose structure is almost always required, or when the results are often required multiple times, over-eager evaluation can improve program efficiency ) ### Google C ++ Style Guide * English: [Google C ++ Style Guide](https://google.github.io/styleguide/cppguide.html) * Chinese: [C ++ Style Guide](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/) ### Other * [Bjarne Stroustrup FAQ](http://www.stroustrup.com/bs_faq.html) * [C ++ style and trick FAQ for Bjarne Stroustrup](http://www.stroustrup.com/bs_faq2.html) ## 📦 STL ### STL index [STL Method Meaning Index](https://github.com/huihut/interview/tree/master/STL) ### STL container Container | Underlying data structure | Time complexity | Unordered | Can not be repeated | Other ---|---|---|---|---|--- [array](https://github.com/huihut/interview/tree/master/STL#array) | array | random read and change O(1) | unordered | repeatable | support random access [vector](https://github.com/huihut/interview/tree/master/STL#vector) | Array | Random read, tail insertion, tail deletion O(1)
head insertion, head Delete O(n) | Unordered | Repeatable | Support random access [deque](https://github.com/huihut/interview/tree/master/STL#deque) | Dual-end queue | End-to-end insertion, end-to-end deletion O(1) | Unordered | Repeatable | One central control + Multiple buffers, support rapid addition and deletion at the beginning and end, support random access [forward_list](https://github.com/huihut/interview/tree/master/STL#forward_list) | One-way linked list | Insert and delete O(1) | Unordered | Repeatable | Random access is not supported [list](https://github.com/huihut/interview/tree/master/STL#list) | Doubly linked list | Insert / delete O(1) | Unordered | Repeatable | Does not support random access [stack](https://github.com/huihut/interview/tree/master/STL#stack) | deque / list | top insert, top delete O(1) | unordered | repeatable | deque or list closed head The end is open. The reason why the vector is not used should be that the capacity is limited, and the expansion takes time. [queue](https://github.com/huihut/interview/tree/master/STL#queue) | deque / list | tail insertion, head deletion O(1) | unordered | repeatable | deque or list closure The head end is open. The reason why the vector is not used should be that the capacity is limited, and the expansion takes time. [priority_queue](https://github.com/huihut/interview/tree/master/STL#priority_queue) | vector + max-heap | Insert, delete O(log2n) | Ordered | Repeatable | vector container + heap processing rules [set](https://github.com/huihut/interview/tree/master/STL#set) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Not repeatable | [multiset](https://github.com/huihut/interview/tree/master/STL#multiset) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Repeatable | [map](https://github.com/huihut/interview/tree/master/STL#map) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Not repeatable | [multimap](https://github.com/huihut/interview/tree/master/STL#multimap) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Repeatable | [unordered_set](https://github.com/huihut/interview/tree/master/STL#unordered_set) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Not Repeatable | [unordered_multiset](https://github.com/huihut/interview/tree/master/STL#unordered_multiset) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Repeatable | [unordered_map](https://github.com/huihut/interview/tree/master/STL#unordered_map) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Not Repeatable | [unordered_multimap](https://github.com/huihut/interview/tree/master/STL#unordered_multimap) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Repeatable | ### STL Algorithm Algorithm | Low-level algorithm | Time complexity | Can not be repeated --- | --- | --- | --- [find](http://www.cplusplus.com/reference/algorithm/find/) | Sequence search | O(n) | Repeatable [sort](https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808) | [Introspection sorting](https://en.wikipedia.org/wiki/Introsort) | O(n*log2n) | Repeatable ## 〽️ Data Structure ### Sequence structure #### Sequential stack(Sequence Stack) [SqStack.cpp](DataStructure/SqStack.cpp) Sequential stack data structures and pictures ```cpp typedef struct { ElemType *elem; int top; int size; int increment; } SqStack; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqStack.png) #### queue(Sequence Queue) Queue data structure ```cpp typedef struct { ElemType * elem; int front; int rear; int maxSize; }SqQueue; ``` ##### Acyclic queue Acyclic queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqQueue.png) `SqQueue.rear++` ##### Circular queue Circular queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqLoopStack.png) `SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize` #### Sequence table(Sequence List) [SqList.cpp](DataStructure/SqList.cpp) Sequence table data structure and pictures ```cpp typedef struct { ElemType *elem; int length; int size; int increment; } SqList; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqList.png) ### Chain structure [LinkList.cpp](DataStructure/LinkList.cpp) [LinkList_with_head.cpp](DataStructure/LinkList_with_head.cpp) Chained data structure ```cpp typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; ``` #### Chain queue(Link Queue) Chain queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkQueue.png) #### Chained representation of a linear list ##### Singly-Linked list(Link List) Singly-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkList.png) ##### Doubly linked list(Du-Link-List) Doubly-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/DuLinkList.png) ##### Circular linked list(Cir-Link-List) Circular-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/CirLinkList.png) ### Hash table [HashTable.cpp](DataStructure/HashTable.cpp) #### Concept Hash function:`H(key): K -> D , key ∈ K` #### Construction method * Direct Addressing * Divided remainder method * Digital analysis method * Folding method * The square method #### Collision resolution method * Chain address method: single linked list linked with the same key * Open Addressing * Linear detection method: same key-> put to the next position of key,`Hi = (H(key) + i) % m` * Secondary detection method: same key-> put to `Di = 1^2, -1^2, ..., ±(k)^2,(k<=m/2)` * Random detection:`H = (H(key) + Pseudo-random number) % m` #### Hash table data structure for linear probing Hash table data structure and pictures for linear detection ```cpp typedef char KeyType; typedef struct { KeyType key; }RcdType; typedef struct { RcdType *rcd; int size; int count; bool *tag; }HashTable; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/HashTable.png) ### Recursion #### Concept Function calls itself directly or indirectly #### Recursion and Divide * Divide and Conquer * Breakdown of the problem * Problem size decomposition * Half search (recursive) * Merge sort (recursive) * Quick sort (recursive) #### Recursion and Iteration * Iteration: use the old value of the variable repeatedly to push out the new value * Search in half (iterative) * Merge sort (iterative) #### Generalized table ##### Head and tail linked list storage representation Head and tail linked list storage representations and pictures for generalized tables ```cpp // Head-to-tail linked list storage representation of generalized list typedef enum {ATOM, LIST} ElemTag; // ATOM == 0: atom, LIST == 1: child table typedef struct GLNode { ElemTag tag; // public part, used to distinguish atomic nodes from table nodes union { // joint part of atomic node and table node AtomType atom; // atom is the range of atom nodes, AtomType is defined by the user struct { struct GLNode *hp, *tp; } ptr; /// ptr is the pointer field of the table node, prt.hp and ptr.tp point to the table header and table tail respectively } a; } *GList, GLNode; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList1.png) ##### Extended linear linked list storage representation Extended linear linked list storage representations and pictures ```cpp // Extended linear linked list storage representation of generalized tables typedef enum {ATOM, LIST} ElemTag; // ATOM == 0: atom, LIST == 1: child table typedef struct GLNode1 { ElemTag tag; // public part, used to distinguish atomic nodes from table nodes union { // joint part of atomic node and table node AtomType atom; // range of atom nodes struct GLNode1 *hp; // table pointer pointer } a; struct GLNode1 *tp; // Equivalent to next of a linear linked list, pointing to the next } *GList1, GLNode1; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList2.png) ### Binary tree [BinaryTree.cpp](DataStructure/BinaryTree.cpp) #### properties 1. Up to 2(i-1) nodes at the i-th level of a non-empty binary tree (i> = 1) 2. Binary tree with depth k up to 2k-1 node (k >= 1) 3. The number of nodes with degree 0 is n0, and the number of nodes with degree 2 is n2, then n0 = n2 + 1 4. Full binary tree depth with n nodes k = ⌊ log 2 (n) ⌋ + 1 5. For the node numbered i (1 <= i <= n) in a complete binary tree with n nodes 1. If i = 1, it is the root, otherwise the parents are ⌊ i / 2 ⌋ 2. If 2i > n, node i has no left child, otherwise the child number is 2i 3. If 2i + 1> n, the i node has no right child, otherwise the child number is 2i + 1 #### Storage structure Binary tree data structure ```cpp typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; ``` ##### Sequential storage Binary tree sequential storage pictures ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqBinaryTree.png) ##### Chained storage Binary tree chain store pictures ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkBinaryTree.png) #### Traversal * Sequence traversal * In-order traversal * Subsequent traversals * Hierarchical traversal #### Categories * Full Binary Tree * Complete binary tree (heap) * Big top heap: root> = left && root> = right * Small top heap: root <= left && root <= right * Binary search tree (binary sort tree): left The difference between B-tree and B + -tree comes from:[differences-between-b-trees-and-b-trees](https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees)、[Difference between B-tree and B + tree](https://www.cnblogs.com/ivictor/p/5849061.html) #### Octree Octree picture ![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png) An octree is a tree-like data structure used to describe three-dimensional space (dividing space). Each node of the octree represents a volume element of a cube, and each node has eight child nodes. The volume elements represented by the eight child nodes are added together to equal the volume of the parent node. The general center point is used as the bifurcation center of the node. ##### Purpose * 3D computer graphics * Nearest Search ## ⚡️ Algorithm ### Sort Sorting Algorithm | Average Time Complexity | Worst Time Complexity | Spatial Complexity | Data Object Stability ---|---|---|---|--- [Bubble Sort](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|Stable [Selection Sort](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|The array is unstable and the linked list is stable [Insert Sort](Algorithm/InsertSort.h) | O(n2)|O(n2)|O(1)|Stable [Quick Sort](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n) | Unstable [Heap Sort](Algorithm/HeapSort.cpp) | O(n*log2n)|O(n*log2n)|O(1)|Unstable [Merge Sort](Algorithm/MergeSort.h) | O(n*log2n) | O(n*log2n)|O(n)| Stable [Shell Sort](Algorithm/ShellSort.h) | O(n*log2n)|O(n2)|O(1)|Unstable [Count Sort](Algorithm/CountSort.cpp) | O(n+m)|O(n+m)|O(n+m)|Stable [Bucket Sort](Algorithm/BucketSort.cpp) | O(n)|O(n)|O(m)|Stable [Radix Sort](Algorithm/RadixSort.h) | O(k*n)|O(n2)| |Stable > * Are arranged in ascending order > * k: represents the number of "digits" in the value > * n: represents the size of the data > * m: represents the maximum value minus the minimum value of the data > * From: [wikipedia. Sorting Algorithm](https://en.wikipedia.org/wiki/Sorting_algorithm) ### Find Find Algorithm | Average Time Complexity | Spatial Complexity | Find Conditions ---|---|---|--- [SequentialSearch](Algorithm/SequentialSearch.h) | O(n) | O(1) | sorted or unsorted [Binary search (half search)](Algorithm/BinarySearch.h) | O(log2n)| O(1) | sorted [Insertion Search](Algorithm/InsertionSearch.h) | O(log2(log2n)) | O(1) | sorted [Fibonacci Search](Algorithm/FibonacciSearch.cpp) | O(log2n) | O(1) | soted [Hash Table](DataStructure/HashTable.cpp) | O(1) | O(n) | sorted or unsorted [Binary Search Tree (BST Search)](Algorithm/BSTSearch.h) |O(log2n) | | [Red Black Tree](DataStructure/RedBlackTree.cpp) |O(log2n) | | 2-3 Tree | O(log2n - log3n) | | B Tree/B+ Tree |O(log2n) | | ### Graph search algorithm Graph Search Algorithm | Data Structure | Traversal Time Complexity | Spatial Complexity ---|---|---|--- [BFS - Breadth First Search](https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|adjacency matrix
adjacency list |O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) [DFS - Depth-First Search](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)| adjacency matrix
adjacent linked list |O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) ### Other algorithms Algorithms | Ideas | Applications --- | --- | --- [Divide and Conquer](https://en.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95) | Divide a complex problem into two or more The same or similar sub-problems, until the last sub-problem can be simply and directly solved, the solution of the original problem is the combination of the solutions of the sub-problems | [loop schedule problem](https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem), sorting algorithms (quick sort, merge sort) [Dynamic Programming](https://en.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92) | By decomposing the original problem into relative Simple sub-question way to solve complex problems, suitable for problems with overlapping sub-problems and optimal sub-structure properties | [knapsack problem] KnapsackProblem), Fibonacci sequence [Greedy method](https://en.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95) | A kind of choice is taken in the current state in each step The best or optimal (that is, the most advantageous) choice, so that the result is the best or optimal algorithm | Travel Salesman Problem (Shortest Path Problem), Minimum Spanning Tree, Huffman Coding ## ❓ Problems ### Single Problem * [Chessboard Coverage Problem (Checkerboard coverage problem)](Problems/ChessboardCoverageProblem) * [Knapsack Problem](Problems/KnapsackProblem) * [Neumann Neighbor Problem](Problems/NeumannNeighborProblem) * [Round Robin Problem](Problems/RoundRobinProblem) * [Tubing Problem](Problems/TubingProblem) ### Leetcode Problems * [Github . haoel/leetcode](https://github.com/haoel/leetcode) * [Github . pezy/LeetCode](https://github.com/pezy/LeetCode) ### Sword Finger Offer * [Github . zhedahht/CodingInterviewChinese2](https://github.com/zhedahht/CodingInterviewChinese2) * [Github . gatieme/CodingInterviews](https://github.com/gatieme/CodingInterviews) ### Cracking the Coding Interview (Programmer Interview Gold) * [Github . careercup/ctci](https://github.com/careercup/ctci) * [Niu Ke. Programmer interview golden code](https://www.nowcoder.com/ta/cracking-the-coding-interview) ### Niu Ke * [Niu Ke . Online programming topics](https://www.nowcoder.com/activity/oj) ## 💻 Operating system ### Processes and threads For threaded systems: * Processes are independent units of resource allocation * Thread is an independent unit of resource scheduling For non-threaded systems: * Process is an independent unit of resource scheduling and allocation #### Communication between processes and advantages and disadvantages * Pipeline (PIPE) * Named pipes: A first-in-first-out communication method that allows communication between unrelated processes * Advantages: can achieve inter-process communication in any relationship * Disadvantages: 1. Long-term storage in the system, improper use is prone to errors Limited buffer * Anonymous pipes: A simplex first-in-first-out communication method that can only be used between processes with affinity (parent-child processes) * Advantages: simple and convenient * Disadvantages: Limited to one-way communication 2. Can only be created between its processes and their related processes 3. Limited buffer * Semaphore: a counter that can be used to control access to shared resources by multiple threads * Advantages: can synchronize processes * Disadvantage: limited semaphore * Signal (Signal): a more complex communication method used to notify the receiving process that an event has occurred * Message Queue: a linked list of messages, stored in the kernel and identified by the message queue identifier * Advantages: can achieve communication between any process, and achieve synchronization between message sending and receiving through system call functions, no need to consider synchronization issues, convenient * Disadvantages: Copying information requires additional CPU time, which is not suitable for situations with large amounts of information or frequent operations * Shared Memory: Maps a piece of memory that can be accessed by other processes. This shared memory is created by one process, but can be accessed by multiple processes. * Advantages: no need to copy, fast, large amount of information * Disadvantages: 1. Communication is achieved by directly attaching the shared space buffer to the virtual address space of the process, so the synchronization of read and write operations between processes 2. Use the memory buffer to directly exchange information. The entity of memory exists in the computer and can only be shared with many processes in a computer system, which is not convenient for network communication. * Socket (Socket): can be used for process communication between different computers * Advantages: 1. The transmission data is byte level, the transmission data can be customized, the data volume is small and the efficiency is high 2. Short data transmission time and high performance 3. Suitable for real-time information exchange between client and server 4. Can be encrypted, strong data security * Disadvantages: The transmitted data needs to be parsed and converted into application-level data. #### Communication between threads * Locking mechanism: including mutex, reader-writer lock, spin lock, and condition * Mutex (mutex): Provides an exclusive way to prevent data structures from being modified concurrently. * Reader-writer lock: Allow multiple threads to read shared data at the same time, and are mutually exclusive for write operations. * Spin locks are similar to mutex locks in order to protect shared resources. The mutex is when the resource is occupied and the applicant goes to sleep; the spin lock circularly detects whether the holder has released the lock. * Condition variable (condition): you can atomically block the process until a certain condition is true. Testing of conditions is performed under the protection of a mutex. Condition variables are always used with a mutex. * Semaphore mechanism * Unknown thread semaphore * Named thread semaphore * Signal mechanism (Signal): similar to signal processing between processes * Barrier: A barrier allows each thread to wait until all cooperating threads reach a certain point, and then continue execution from that point. The purpose of communication between threads is mainly for thread synchronization, so threads have no communication mechanism for data exchange like in process communication. > The communication methods between processes and their advantages and disadvantages come from: [Process thread interview question summary](http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977) #### Private and shared resources between processes * Private: address space, heap, global variables, stack, registers * Share: code snippet, public data, process directory, process ID #### Private and shared resources between threads * Private: thread stack, register, program counter * Shared: heap, address space, global variables, static variables #### Comparison, advantages and disadvantages of multi-process and multi-thread ##### Compared Contrast Dimensions | Multi-Process | Multi-Threaded | Summary --- | --- | --- | --- Data sharing and synchronization | Complex data sharing requires IPC; data is separate and easy to synchronize | Because process data is shared, data sharing is simple, but it is also because of this reason that synchronization is complicated | Each advantage Memory, CPU | Occupies more memory, complex switching, low CPU utilization | Less memory, simple switching, high CPU utilization | Threads dominate Create destroy, switch | create destroy, switch complex, slow speed | create destroy, switch simple, fast speed | thread dominant Programming, debugging | simple programming, simple debugging | complex programming, complex debugging | process dominates Reliability | Processes will not affect each other | Hanging a thread will cause the entire process to hang | Process dominance Distributed | Applicable to multi-core, multi-machine distributed; if one machine is not enough, it is relatively simple to expand to multiple machines | adapted to multi-core distributed | process dominant ##### Pros and cons Pros and cons | multi-process | multi-threaded --- | --- | --- Advantages | Simple programming and debugging, high reliability | Fast creation, destruction, switching, small memory and resource occupation Disadvantages | Creating, destroying, slow switching, large memory and resource occupation | Complex programming and debugging, poor reliability ##### Select * Need to frequently create and destroy priority threads * Priority threads that require a lot of calculations * Strongly related processing threads, weakly related processing processes * May be extended to multi-machine distributed processes, multi-core distributed threads * When all meet the needs, use the method you are most familiar with > The comparison, advantages and disadvantages of multi-process and multi-thread come from: [multi-thread or multi-process selection and difference](https://blog.csdn.net/lishenglong666/article/details/8557215) ### Linux kernel synchronization #### the reason In modern operating systems, there may be multiple kernel execution streams executing at the same time, so the kernel actually needs some synchronization mechanism to synchronize the access of the execution units to shared data like multi-process and multi-thread programming. Especially on multiprocessor systems, some synchronization mechanisms are needed to synchronize the access of the execution units on different processors to shared data. #### Synchronously * Atomic operations * Semaphore * Read and write semaphore (rw_semaphore) * Spinlock * Big Kernel Lock (BKL, Big Kernel Lock) * Read-write lock (rwlock) * Brlock-Big Reader Lock * Read-Copy Update (RCU, Read-Copy Update) * Sequence lock (seqlock) > From: [Linux kernel synchronization mechanism, Part 1](https://www.ibm.com/developerworks/cn/linux/l-synch/part1/), [Linux kernel synchronization mechanism, Part 2](https://www.ibm.com/developerworks/cn/linux/l-synch/part2/) ### Deadlock #### Reasons * Insufficient system resources * Improper allocation of resources * The progress order of the process operation is not suitable #### Generate conditions * Mutex * Request and hold * Not deprived * Loop #### Prevention * Breaking mutually exclusive conditions: transforming exclusive resources into virtual resources, most of which cannot be transformed. * Break the non-preemption condition: when a process occupies an exclusive resource and then applies for an exclusive resource that cannot be met, it will withdraw from the original resource. * Break possession and apply conditions: Adopt a resource pre-allocation strategy, that is, apply for all resources before the process runs, run if it meets, or wait, so that it will not be occupied and applied. * Break the loop waiting condition: implement the orderly allocation of resources, implement classification numbers for all devices, and all processes can only apply for resources in the form of increasing numbers. * Ordered resource allocation method * Banker Algorithm ### File system * Windows: FCB table + FAT + bitmap * Unix: inode + mixed index + group link ### Host byte order and network byte order #### Host Endianness (CPU Endianness) ##### Concept The host byte order is also called the CPU byte order. It is not determined by the operating system, but by the CPU instruction set architecture. There are two types of host byte order: * Big Endian: The high-order byte is stored in the low-order address, and the low-order byte is stored in the high-order address * Little Endian: The high-order byte is stored in the high-order address, and the low-order byte is stored in the low-order address. ##### Storage method The 32-bit integer `0x12345678` is stored from the address starting at` 0x00`, then: Memory address | 0x00 | 0x01 | 0x02 | 0x03 --- | --- | --- | --- | --- Big Endian | 12 | 34 | 56 | 78 Little Endian | 78 | 56 | 34 | 12 Big-endian little-endian pictures ![Big endian](https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Big-Endian.svg.png) ![Little endian](https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Little-Endian.svg.png) ##### Judging big-endian little-endian Judge big-endian little-endian You can determine whether your CPU byte order is big-endian or little-endian: ```cpp #include using namespace std; int main() { int i = 0x12345678; if (*((char*)&i) == 0x12) cout << "Big endian" << endl; else cout << "Little endian" << endl; return 0; } ``` ##### Byte order of each architecture processor * x86 (Intel, AMD), MOS Technology 6502, Z80, VAX, PDP-11 and other processors are little-endian; * Motorola 6800, Motorola 68000, PowerPC 970, System/370, SPARC (except V9) processors are big-endian; * The endianness of ARM (default little-endian), PowerPC (except PowerPC 970), DEC Alpha, SPARC V9, MIPS, PA-RISC, and IA64 are configurable. #### Network byte order Network byte order is a data representation format specified in TCP/IP. It has nothing to do with the specific CPU type, operating system, etc., so that it can ensure that the data can be correctly interpreted when transmitted between different hosts. The network byte order uses: Big Endian arrangement. ### Page replacement algorithm During the address mapping process, if a page to be accessed is found not to be in memory, a page fault interrupt is generated. When a page fault occurs, if there is no free page in the operating system memory, the operating system must select a page in memory to move it out of memory in order to make room for the page that is about to be paged in. The rules used to select which pages to eliminate are called page replacement algorithms. #### Categories * Global replacement: replacement in the entire memory space * Partial replacement: Replace in this process #### Algorithm Global: * Working set algorithm * Page rate replacement algorithm Partially: * Optimal Replacement Algorithm (OPT) * First in, first out replacement algorithm (FIFO) * Least Recently Used (LRU) Algorithm * Clock replacement algorithm ## ☁️ Computer Network > Some of the knowledge in this section comes from Computer Networking (7th Edition) Computer network architecture: ![Computer Network Architecture](https://raw.githubusercontent.com/huihut/interview/master/images/计算机网络体系结构.png) ### Roles and protocols of each layer Layered | role | protocol --- | --- | --- Physical layer | Transmission of bits through the medium, determination of mechanical and electrical specifications (bits) | RJ45, CLOCK, IEEE802.3 (repeaters, hubs) Data link layer | Assemble bits into frames and point-to-point delivery (Frame) | PPP, FR, HDLC, VLAN, MAC (bridge, switch) Network layer | Responsible for the transmission of data packets from source to sink and Internet interconnection (packets) | IP, ICMP, ARP, RARP, OSPF, IPX, RIP, IGRP (router) Transport layer | Provide end-to-end reliable message delivery and error recovery (Segment) | TCP, UDP, SPX Session layer | Establishing, managing, and terminating sessions (session protocol data unit SPDU) | NFS, SQL, NETBIOS, RPC Presentation Layer | Translate, Encrypt, and Compress Data (Representation Protocol Data Unit PPDU) | JPEG, MPEG, ASII Application layer | A means to allow access to the OSI environment (Application Protocol Data Unit APDU) | FTP, DNS, Telnet, SMTP, HTTP, WWW, NFS ### Physical layer * Unit of transmitted data: bit * Data transmission system: source system (source point, sender)-> transmission system-> destination system (receiver, destination) aisle: * Unidirectional channel (simplex channel): only one direction communication, no reverse direction interaction, such as broadcasting * Two-way alternate communication (half-duplex communication): both parties of the communication can send messages, but cannot send or receive at the same time * Two-way simultaneous communication (full-duplex communication): both parties of the communication can send and receive information at the same time Channel multiplexing technology: * Frequency division multiplexing (FDM, Frequency Division Multiplexing): different users in different frequency bands, users use different bandwidth resources at the same time * Time Division Multiplexing (TDM): different users in different time slices in the same time period, all users occupy the same bandwidth at different times * Wavelength Division Multiplexing (WDM): Frequency division multiplexing of light * Code Division Multiplexing (CDM): Different users use different codes and can use the same frequency band for communication at the same time ### data link layer Main channel: * Point-to-point channel * Broadcast channel #### Point-to-point channel * Data unit: frame Three basic questions: * Encapsulation: Encapsulate IP datagrams at the network layer into frames, `SOH-data part-EOT` * Transparent transmission: No matter what characters in the data part, it can be transmitted; it can be solved by byte padding method (escaping characters before conflicting characters) * Error detection: reduce bit error rate (BER), widely use cyclic redundancy check (CRC, Cyclic Redundancy Check) Point-to-Point Protocol: * Point-to-Point Protocol: The protocol used by the user computer to communicate with the ISP #### Broadcast Channel Broadcast communication: * Hardware address (physical address, MAC address) * Unicast frame (one-to-one): The MAC address of the received frame is the same as the hardware address of this station * Broadcast frame (one pair): frame sent to all stations on the local area network * Multicast frame (one-to-many): frame sent to some sites on the local area network ### Network layer * IP (Internet Protocol, Internet Protocol) is a protocol designed for computer networks to communicate with each other. * ARP (Address Resolution Protocol, Address Resolution Protocol) * ICMP (Internet Control Message Protocol, Internet Control Message Protocol) * IGMP (Internet Group Management Protocol, Internet Group Management Protocol) #### IP Internet Protocol IP address classification: * `IP address ::= (, )` IP address category | network number | network range | host number | IP address range --- | --- | --- | --- | --- Class A | 8bit, the first bit is fixed at 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255 Class B | 16bit, the first two bits are fixed at 10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255 Class C | 24bit, the first three bits are fixed at 110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255 Class D | The first four bits are fixed at 1110, followed by the multicast address Class E | The first five digits are fixed at 11110, the latter are reserved for future use IP datagram format: ![IP 数据报格式](https://raw.githubusercontent.com/huihut/interview/master/images/IP数据报格式.png) #### ICMP Internet Control Message Protocol ICMP message format: ![ICMP message format](https://raw.githubusercontent.com/huihut/interview/master/images/ICMP报文格式.png) application: * PING (Packet InterNet Groper) to test connectivity between two hosts * TTL (Time To Live, time to live) This field specifies the maximum number of network segments that IP packets are allowed to pass before being dropped by the router. #### Interior Gateway Protocol * RIP (Routing Information Protocol, Routing Information Protocol) * OSPF (Open Shortest Path First) #### External gateway protocol * BGP (Border Gateway Protocol, Border Gateway Protocol) #### IP multicast * IGMP (Internet Group Management Protocol, Internet Group Management Protocol) * Multicast routing protocol #### VPN and NAT * VPN (Virtual Private Network) * NAT (Network Address Translation, Network Address Translation) #### What does the routing table contain? 1. Network ID (Network ID): It is the network ID of the destination address. 2. Subnet mask: used to determine the network to which the IP belongs 3. Next hop address / interface: It is the address of the next stop of the data on the journey sent to the destination address. Where interface points to the next hop (that is, the next route). The route in an autonomous system (AS) should contain all subnets in the area, and the default gateway (Network id: `0.0.0.0`, Netmask:` 0.0.0.0`) points to the exit of the autonomous system. Depending on the application and implementation, the routing table may contain the following additional information: 1. Cost: It is the cost required to pass the path during data transmission. 2. Quality of service for routing 3. List of inbound / outbound connections to be filtered in the route ### Transport layer protocol: * TCP (Transmission Control Protocol) * UDP (User Datagram Protocol, User Datagram Protocol) port: Applications | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP --- | --- | --- | --- | --- | --- | --- | --- | --- Port number | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161 #### TCP * TCP (Transmission Control Protocol, Transmission Control Protocol) is a connection-oriented, reliable, byte stream-based transport layer communication protocol, and its transmission unit is a message segment. Feature: * Connection oriented * End-to-end communication * Reliable interaction * Full-duplex communication * Byte stream oriented How TCP guarantees reliable transmission: * Confirmation and timeout retransmission * Reasonable fragmentation and ordering of data * flow control * Congestion control * Data validation TCP message structure ![TCP Message](https://raw.githubusercontent.com/huihut/interview/master/images/TCP报文.png) TCP header ![TCP header](https://raw.githubusercontent.com/huihut/interview/master/images/TCP首部.png) TCP: Status Control Code (Code, Control Flag), which takes 6 bits and has the following meaning: * URG: Urgent. When `URG = 1` , it indicates that the urgent pointer field is valid, which means that the packet is an urgent packet. It tells the system that there is urgent data in this segment and it should be transmitted as soon as possible (equivalent to high-priority data), and the Urgent Pointer field in the above figure will also be enabled. * ACK: Acknowledge. The acknowledgment number field is valid only when `ACK = 1`, which means this packet is an acknowledgment packet. When `ACK = 0`, the confirmation number is invalid. * PSH: (Push function) If it is 1, the representative requires the other party to immediately transmit other corresponding packets in the buffer, without waiting for the buffer to be full before sending. * RST: Reset bit (Reset). When `RST = 1`, it indicates that a serious error occurred in the TCP connection (such as due to a host crash or other reasons). You must release the connection and then re-establish the transport connection. * SYN: Synchronous. When SYN is set to 1, it indicates that this is a connection request or connection acceptance message. Usually, the packet with the SYN flag indicates that "active" wants to connect to the other party. * FIN: Final bit, used to release a connection. When `FIN = 1`, it means that the data of the sending end of this segment has been sent, and it is required to release the transport connection. #### UDP * UDP (User Datagram Protocol, User Datagram Protocol) is a connectionless transport layer protocol in the OSI (Open System Interconnection) reference model, which provides a simple and unreliable information transfer service oriented to transactions, and its transmission unit Is a user datagram. feature: * no connection * Best efforts to deliver * Message-oriented * No congestion control * Supports one-to-one, one-to-many, many-to-one, and many-to-many interactive communication * Small overhead UDP message structure ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/UDP报文.png) UDP 首部 ![UDP 首部](https://raw.githubusercontent.com/huihut/interview/master/images/UDP首部.png) > TCP/UDP Pictures from #### Difference between TCP and UDP 1. TCP connection-oriented, UDP is connectionless; 2. TCP provides reliable service, that is, the data transmitted through the TCP connection is error-free, not lost, not duplicated, and arrives in order; UDP does its best to deliver, that is, it does not guarantee reliable delivery 3. TCP logical communication channel is a full-duplex reliable channel; UDP is an unreliable channel 5. Each TCP connection can only be point-to-point; UDP supports one-to-one, one-to-many, many-to-one and many-to-many interactive communication 6. TCP is oriented to byte streams (probably sticky packets may occur). In fact, TCP treats data as a series of unstructured byte streams; UDP is message-oriented (no sticky packets will occur) 7. UDP has no congestion control, so congestion on the network will not reduce the sending rate of the source host (useful for real-time applications, such as IP phones, real-time video conferencing, etc.) 8. TCP header overhead is 20 bytes; UDP header overhead is small, only 8 bytes #### TCP Sticky Packet ##### the reason TCP is a byte stream-based transmission service (UDP is message-based). "Stream" means that the data transmitted by TCP has no boundaries. So there may be situations where two data packets stick together. ##### Solve * Send fixed-length packets. If the size of each message is the same, the receiving peer only needs to accumulate the received data until the data is equal to a fixed-length value and treats it as a message. * Baotou plus package body length. The header is 4 bytes of fixed length, which indicates the length of the packet body. The receiving peer first receives the header length and receives the packet body according to the header length. * Set boundaries between packets, such as adding special symbols `\r\n` mark. The FTP protocol does just that. The problem is that if the data body also contains `\r\n` , it will be misjudged as the message boundary. * Use more complex application layer protocols. #### TCP Flow Control ##### Concept Flow control is to make the sending rate of the sender not too fast, but to allow the receiver to receive it. ##### Methods Variable window for flow control ![](https://raw.githubusercontent.com/huihut/interview/master/images/利用可变窗口进行流量控制举例.png) #### TCP congestion control ##### Concept Congestion control is to prevent excessive data from being injected into the network, so that the routers or links in the network will not be overloaded. ##### Methods * Slow start * Congestion avoidance * Fast retransmit * Fast recovery TCP Congestion control graph ![](https://raw.githubusercontent.com/huihut/interview/master/images/TCP拥塞窗口cwnd在拥塞控制时的变化情况.png) ![](https://raw.githubusercontent.com/huihut/interview/master/images/快重传示意图.png) ![](https://raw.githubusercontent.com/huihut/interview/master/images/TCP的拥塞控制流程图.png) #### TCP Transmission connection management > 因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述: ##### TCP 三次握手建立连接 ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/TCP三次握手建立连接.png) 【Explain the whole process of TCP connection establishment】 1. The client sends a SYN to the server, indicating that the client requests to establish a connection; 2. The server receives the SYN sent by the client and replies with SYN + ACK to the client (agreeing to establish a connection); 3. After receiving the SYN + ACK from the server, the client responds with an ACK to the server (indicating that the client received the consent message sent by the server); 4. The server receives the ACK from the client, the connection is established, and data transmission is possible. ##### Why does TCP have to shake hands three times? 【Answer One】 Because the channel is unreliable, and TCP wants to establish a reliable transmission on the unreliable channel, then three communications is the theoretical minimum. (And UDP does not need to establish a reliable transmission, so UDP does not require a three-way handshake.) > [Google Groups. Why is TCP a three-way handshake to establish a connection? {Technology} {Network Communication}](https://groups.google.com/forum/#!msg/pongba/kF6O7-MFxM0/5S7zIJ4yqKUJ) [Answer II] Because both parties need to confirm that the other party has received the serial number sent by themselves, the confirmation process requires at least three communications. > [Know. Why is TCP a three-way handshake instead of two or four? ](https://www.zhihu.com/question/24853633/answer/115173386) [Answer three] In order to prevent the invalid connection request segment from being sent to the server suddenly, an error is generated. > ["Computer Network (7th Edition) -Xie Xiren"](https://raw.githubusercontent.com/huihut/interview/master/images/TCP-transport-connection-management.png) ##### TCP Four waves to release the connection ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/TCP四次挥手释放连接.png) 【TCP Four waves to release the connection】 1. The client sends FIN to the server, indicating that the client does not need to send data to the server (request to release the connection from the client to the server); 2. The server receives the FIN sent by the client and replies ACK to the client (agreeing to release the connection from the client to the server); 3. The client receives the ACK from the server, and the connection from the client to the server has been released (but the connection from the server to the client has not been released, and the client can still receive data); 4. The server continues to send the unfinished data to the client; 5. The server sends FIN + ACK to the client, indicating that the server has sent the data (request to release the connection from the server to the client, even if no reply is received from the client, it will be automatically released after a certain period of time); 6. The client receives the FIN + ACK from the server and replies to the server with an ACK (agreeing to release the connection from the server to the client); 7. After receiving the ACK from the client, the server releases the connection from the server to the client. ##### Why does TCP have to wave four times? [Question 1] Why does TCP wave four times? / Why does TCP need three times to establish a connection and four times to release a connection? [Answer One] Because TCP is in full-duplex mode, after the client requests to close the connection, the connection from the client to the server is closed (one or two waves), and the server continues to transmit the data that has not been previously transmitted to the client (data transmission) , The connection from the server to the client is closed (waving three or four times). Therefore, when TCP releases the connection, the server's ACK and FIN are sent separately (with data transmission in between), and when the TCP establishes the connection, the server's ACK and SYN are sent together (second handshake), so TCP needs to establish the connection three times. It takes four times to release the connection. [Question 2] Why can ACK and SYN be sent together when TCP is connected, and ACK and FIN are sent separately when released? (ACK and FIN refer to the second and third wave) [Answer 2:] When the client requests the release, the server may still have data to transmit to the client, so the server must respond to the client FIN request (the server sends an ACK), and then the data is transmitted. After the transmission is completed, the server then Make a FIN request (the server sends FIN); there is no intermediate data transmission when connecting, so ACK and SYN can be sent together when connecting. [Question three] Why does the client need TIME-WAIT to wait for 2MSL at the end? 【Answer three】 1. In order to ensure that the last ACK message sent by the client can reach the server. If it fails to arrive, the server will retransmit the FIN + ACK segment overtime, and the client will retransmit the ACK and re-time. 2. Prevent invalid connection request segments from appearing in this connection. When TIME-WAIT lasts 2MSL, all the segments generated during the duration of this connection will disappear from the network, so that the old connection segments will not appear in the next connection. #### TCP finite state machine TCP finite state machine picture ![TCP Finite state machine](https://raw.githubusercontent.com/huihut/interview/master/images/TCP的有限状态机.png) ### Application layer #### DNS * DNS (Domain Name System) is a service of the Internet. It serves as a distributed database that maps domain names and IP addresses to each other, enabling people to access the Internet more conveniently. DNS uses TCP and UDP port 53. Currently, the limit for the length of each domain name is 63 characters, and the total length of the domain name cannot exceed 253 characters. domain name: * `Domain name :: = {..}`, for example: `blog.huihut.com` #### FTP * FTP (File Transfer Protocol, file transfer protocol) is a set of standard protocols for file transfer on the network, using the client / server model, using TCP datagrams, providing interactive access, two-way transmission. * TFTP (Trivial File Transfer Protocol) is a small and easy-to-implement file transfer protocol. It also uses the client-server method and uses UDP datagrams. It only supports file transfers and does not support interaction. User authentication #### TELNET * The TELNET protocol is a member of the TCP / IP protocol family and is the standard protocol and main method of the Internet remote login service. It provides users with the ability to complete remote host work on the local computer. * HTTP (HyperText Transfer Protocol) is a transfer protocol used to transfer hypertext from a WWW (World Wide Web, World Wide Web) server to a local browser. * SMTP (Simple Mail Transfer Protocol) is a set of rules for transferring mail from the source address to the destination address, which controls the transfer method of the letter. The SMTP protocol belongs to the TCP / IP protocol suite, which helps each computer find the next destination when sending or transferring letters. * Socket requires at least a pair of port numbers (Socket) to establish a network communication connection. Socket is essentially a programming interface (API), which encapsulates TCP / IP. TCP / IP also provides an interface that programmers can use for network development. This is the Socket programming interface. #### WWW * WWW (World Wide Web, World Wide Web, World Wide Web) is a system composed of many hypertext links to each other, accessed via the Internet ##### URL * URL (Uniform Resource Locator) is a standard resource address (Address) on the Internet standard format: * `Protocol type: [// server address [: port number]] [/ resource level UNIX file path] file name [? Query] [# Clip ID]` Complete format: * `Protocol type: [// [Credential information required to access resources @] server address [: port number]] [/ resource level UNIX file path] file name [? Query] [# segment ID]` > Among them [access credential information @ ;: port number ;? query; #fragment ID] are all optional items > For example: `https://github.com/huihut/interview#cc` ##### HTTP Hypertext Transfer Protocol (HTTP) is an application-layer protocol for distributed, collaborative, and hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. Request method Method | meaning --- | --- OPTIONS | Request some option information to allow clients to view server performance GET | Request the specified page information and return the entity body HEAD | Similar to a get request, except that there is no specific content in the returned response, which is used to get the header POST | Submits data to a specified resource for processing requests (such as submitting a form or uploading a file). The data is contained in the request body. POST requests may result in the creation of new resources and / or modification of existing resources PUT | Data transmitted from the client to the server replaces the contents of the specified document DELETE | request the server to delete the specified page TRACE | echo requests received by the server, mainly for testing or diagnostics Status Code * 1xx: indicates notification information, such as the request has been received or is being processed * 100 Continue: Continue, the client should continue its request * 101 Switching Protocols. The server switches protocols according to the client's request. Can only switch to higher-level protocols, for example, to new versions of HTTP * 2xx: indicates success, such as receiving or knowing * 200 OK: The request was successful * 3xx: indicates a redirect, and further action is required to complete the request * 301 Moved Permanently: Move permanently. The requested resource has been permanently moved to the new URL, the returned information will include the new URL, and the browser will automatically be directed to the new URL. Any future requests should use the new URL instead * 4xx: indicates a customer error, such as incorrect syntax in the request or inability to complete * 400 Bad Request: The client requested a syntax error that the server could not understand * 401 Unauthorized: request requires user authentication * 403 Forbidden: The server understood the request from the requesting client, but refused to execute the request (insufficient permissions) * 404 Not Found: The server cannot find the resource (web page) based on the client's request. With this code, the website designer can set a personality page for "the resource you requested could not be found" * 408 Request Timeout: The server waited too long for the request sent by the client, timed out * 5xx: indicates a server error, such as the server failing to complete the request * 500 Internal Server Error: Server internal error, unable to complete request * 503 Service Unavailable: Due to overload or system maintenance, the server cannot temporarily process the client's request. The length of the delay can be included in the server's Retry-After header * 504 Gateway Timeout: The server acting as a gateway or proxy, not receiving requests from the remote server in a timely manner >More status codes: [Rookie Tutorials. HTTP Status Codes](http://www.runoob.com/http/http-status-codes.html) ##### Other agreements * SMTP (Simple Main Transfer Protocol, Simple Mail Transfer Protocol) is a standard for transmitting Email over the Internet. It is a relatively simple text-based protocol. One or more recipients of a message are specified on top of it (confirmed to exist in most cases), and the message text is transmitted. You can easily test an SMTP server through a Telnet program. SMTP uses TCP port 25. * DHCP (Dynamic Host Configuration Protocol) is a network protocol for a local area network. It uses the UDP protocol to work and has two main purposes: * For internal network or network service provider to automatically assign IP addresses to users * For internal network administrators as a means of central management of all computers * SNMP (Simple Network Management Protocol) forms part of the Internet protocol family defined by the Internet Engineering Task Force (IETF). The protocol can support a network management system to monitor whether the devices connected to the network have any management concerns. ## 🌩 Network Programming ### Socket > [Linux Socket Programming (unlimited Linux)](https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html) ![Socket Client server communication](https://raw.githubusercontent.com/huihut/interview/master/images/socket客户端服务器通讯.jpg) #### Socket middle read()、write() function ```cpp ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); ``` ##### read() * The read function is responsible for reading content from fd. * When the read is successful, read returns the actual number of bytes read. * If the returned value is 0, it means that the end of the file has been read, and if it is less than 0, an error has occurred. * If the error is EINTR, the reading is caused by interruption; if it is ECONNREST, there is a problem with the network connection. ##### write () * The write function writes the contents of nbytes in buf to the file descriptor fd. * Returns the number of bytes written when successful. On failure, it returns -1 and sets the errno variable. * In network programs, there are two possibilities when we write to the socket file descriptor. * (1) The return value of write is greater than 0, indicating that part or all of the data has been written. * (2) The returned value is less than 0, and an error has occurred at this time. * If the error is EINTR, it indicates that an interruption error occurred during writing; if it is EPIPE, it indicates that there is a problem with the network connection (the other party has closed the connection). #### TCP three-way handshake in socket to establish connection We know that TCP establishes a connection by performing a "three-way handshake", that is, exchanging three packets. The general process is as follows: 1. The client sends a SYN J to the server 2. The server responds to the client with a SYN K, and confirms SYN J ACK J + 1 3. The client wants the server to send an acknowledgement ACK K + 1 Only the three-way handshake is finished, but what about the three-way handshake in the socket function? Please see the picture below: ![socket Sent in TCP Three handshake](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png) It can be seen from the figure: 1. When the client calls connect, a connection request is triggered and a SYN J packet is sent to the server. At this time, connect enters a blocking state; 2. The server listens to the connection request, that is, receives the SYN J packet, calls the accept function to receive the request and sends SYN K and ACK J + 1 to the client, then accept enters the blocking state; 3. After the client receives the SYN K of the server, ACK J + 1, connect returns at this time, and confirms the SYN K; 4. When the server receives ACK K + 1, accept returns, so that the three handshake is completed and the connection is established. #### TCP four-way handshake in socket to release connection The above describes the three-way handshake establishment process of TCP in socket and the socket functions involved. Now we introduce the process of releasing the connection by the four-way handshake in the socket, please see the following figure: ![socket TCP four-way handshake sent in](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png) The illustrated process is as follows: 1. An application process first calls close to actively close the connection, then TCP sends a FIN M; 2. After receiving the FIN M, the other end performs a passive close to confirm the FIN. Its reception is also passed to the application process as an end-of-file character, because the reception of FIN means that the application process can no longer receive additional data on the corresponding connection; 3. After a period of time, the application process that received the end-of-file character calls close to close its socket. This causes its TCP to also send a FIN N; 4. The source TCP that received the FIN confirms it. So there is a FIN and ACK in each direction. ## 💾 Database > Part of the knowledge in this section comes from "Introduction to Database Systems (5th Edition)" ### basic concepts * Data: Symbolic records describing things are called data. * Database (DB): It is a collection of a large amount of organized, shareable data stored in a computer for a long time, and has three basic characteristics of permanent storage, organization, and shareability. * Database Management System (DBMS): is a layer of data management software located between the user and the operating system. * Database system (DBS): is a system consisting of a database, a database management system (and its application development tools), applications, and a database administrator (DBA) to store, manage, process, and maintain data. * Entity: Objects that exist objectively and can be distinguished from each other are called entities. * Attribute: An attribute of an entity is called an attribute. * Key: The set of attributes that uniquely identify an entity is called a key. * Entity type: Use entity name and its attribute name set to abstract and characterize similar entities, called entity type. * Entity set: A set of the same entity type is called an entity set. * Relationship: The relationship between entities usually refers to the connection between different sets of entities. * Schema: A schema is also called a logical schema. It is a description of the logical structure and characteristics of the entire database data. It is a common data view for all users. * External schema: External schema is also called subschema or user schema. It is a description of the logical structure and characteristics of local data that database users (including application programmers and end users) can see and use. A database user's data view is a logical representation of data related to an application. * Internal schema: Internal schema is also called storage schema. A database has only one internal schema. It is a description of the physical structure and storage of the data, and the way the database is organized within the database. ### Common data models * Hierarchical model * Network model * Relational model * Relation (relation): a relation corresponds to a table * Tuple: a row in the table is a tuple * Attribute (attribute): a column in the table is an attribute * Key: A certain attribute group of a tuple can be uniquely identified in the table * Domain (domain): a set of values of the same data type * Component: an attribute value in a tuple * Relation mode: description of the relationship, generally expressed as `relation name (attribute 1, attribute 2, ..., attribute n)` * Object oriented data model * Object relational data model * Semistructured data model ### Common SQL Operations
Object type Object Type of operation
Database mode Mode CREATE SCHEMA
Basic table CREATE SCHEMA , ALTER TABLE
View CREATE VIEW
Index CREATE INDEX
Data Basic tables and views SELECT , INSERT , UPDATE , DELETE , REFERENCES , ALL PRIVILEGES
Attribute column SELECT , INSERT , UPDATE , REFERENCES , ALL PRIVILEGES
> SQL Grammar Tutorial: [runoob . SQL Tutorial](http://www.runoob.com/sql/sql-tutorial.html) ### Relational Database * Basic relation operations: query (selection, projection, connection (equivalent connection, natural connection, outer connection (left outer connection, right outer connection)), division, union, difference, intersection, Cartesian product, etc.), insertion, deletion ,modify * Three types of integrity constraints in the relationship model: entity integrity, referential integrity, user-defined integrity #### Index * Database index: sequential index, B + tree index, hash index * [Data structure and algorithm principle behind MySQL index](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) ### Database integrity * Database integrity refers to the correctness and compatibility of data. * Integrity: To prevent non-semantic (incorrect) data from the database. * Security: In order to protect the database from malicious damage and illegal access. * Trigger: is a special event-driven process defined by the user in the relational table. ### Relational data theory * Data dependency is a constraint relationship between the internal attributes and attributes of a relationship. It is a correlation between data that is reflected by the equality of the values ​​between attributes. * The most important data dependencies: function dependencies, multi-value dependencies. #### Paradigm * First Normal Form (1NF): The attribute (field) is the smallest unit and cannot be separated. * The second normal form (2NF): meet 1NF, each non-primary attribute completely depends on the primary key (eliminating the partial function dependence of 1NF non-primary attribute on the code). * The third normal form (3NF): 2NF is satisfied, and any non-primary attribute does not depend on other non-primary attributes (eliminating the 2NF non-primary attribute's transfer function dependence on the code). * Bowies-Kode Normal Form (BCNF): Satisfy 3NF, any non-primary attribute cannot depend on the subset of primary keys (eliminating the 3NF primary attribute's dependence on the code part and transfer function) * The fourth normal form (4NF): meet 3NF, there must be non-trivial and non-functional dependent multi-value dependencies between attributes (eliminating 3NF non-trivial and non-functional multi-value dependencies). ### Database Recovery * Transaction: It is a user-defined sequence of database operations. These operations are either all done or not done at all. It is an indivisible unit of work. * ACID characteristics of things: atomicity, consistency, isolation, persistence. * Implementation technology of recovery: establish redundant data-> use redundant data to implement database recovery. * Common techniques for establishing redundant data: data dumps (dynamic mass dumps, dynamic incremental dumps, static mass dumps, static incremental dumps), registration log files. ### Concurrency control * Transactions are the basic unit of concurrency control. * Data inconsistencies caused by concurrent operations include: lost modifications, non-repeatable reads, and "dirty" data reads. * The main technologies of concurrency control: blocking, timestamp, optimistic control method, multi-version concurrency control, etc. * Basic lock types: exclusive lock (X lock / write lock), shared lock (S lock / read lock). * Livelock deadlock: * Livelock: The transaction is always in a waiting state, which can be avoided through a first come, first served policy. * Deadlock: The transaction can never end * Prevention: one-time block method, sequential block method; * Diagnosis: timeout method, waiting graph method; * Cancel: Undo the transaction with the least deadlock cost and release all the locks of this transaction, so that other transactions can continue to run. * Serializable scheduling: The concurrent execution of multiple transactions is correct if and only if the result is the same as when the transactions are executed serially in a certain order. Guidelines for correct scheduling of concurrent transactions when serializable. ## 📏 Design pattern > Examples of major design patterns: [CSDN column. C ++ Design Patterns](https://blog.csdn.net/liang19890820/article/details/66974516) series of blog posts [Design Pattern Project Catalog](DesignPattern) ### Singleton mode [Singleton Pattern Example](DesignPattern/SingletonPattern) ### Abstract factory pattern [Abstract Factory Pattern Example](DesignPattern/AbstractFactoryPattern) ### Adapter Mode [Adapter pattern example](DesignPattern/AdapterPattern) ### Bridge mode [Bridge pattern example](DesignPattern/BridgePattern) ### Observer mode [Example of Observer Pattern](DesignPattern/ObserverPattern) ### Six Principles of Design Patterns * Single Responsibility Principle (SRP) * Liskov Substitution Principle (LSP) * Dependence Inversion Principle (DIP) * Interface Segregation Principle (ISP) * Law of Demeter (LoD) * Open Close Principle (OCP) ## ⚙️ Link Loading Library > Part of the knowledge in this section comes from "Programmer's Self-Cultivation-Link Loading Library" ### memory, stack, heap The general application memory space has the following areas: * Stack: Automatically allocated and released by the operating system, storing function parameter values, local variable values, etc., used to maintain the context of function calls * Heap: Generally allocated and released by the programmer. If the programmer does not release, it may be recycled by the operating system at the end of the program to accommodate the dynamically allocated memory area of ​​the application. * Executable image: Stores the image of the executable file in memory. Loading by the loader is to read or map the memory of the executable file here. * Reserved area: The reserved area is not a single memory area, but a general term for memory areas that are protected from access in the memory. For example, in C, the invalid pointer is assigned a value of 0 (NULL), so the 0 address is normal. Impossible to access data efficiently #### stack The stack stores the maintenance information required for a function call. It is often called a stack frame or an active record. It generally contains the following aspects: * Function return address and parameters * Temporary variables: including non-static local variables of functions and other temporary variables automatically generated by the compiler * Save context: including registers that need to remain unchanged before and after function calls #### Heap Heap allocation algorithm: * Free List * Bitmap * Object pool #### "Segment fault" or "Illegal operation, this memory address cannot be read / write" Errors caused by typical illegal pointer dereferencing. This error occurs when the pointer points to a memory address that is not allowed to read and write, and the program attempts to use the pointer to read or write that address. Common reasons: * Initialize the pointer to NULL, then start using the pointer without giving it a reasonable value * There is no need to initialize the pointer in the stack. The value of the pointer will generally be a random number. After that, the pointer is used directly. ### Compile and link #### File formats for each platform Platform | Executables | Objects | Dynamic Libraries / Shared Objects | Static Libraries ---|---|---|---|--- Windows|exe|obj|dll|lib Unix/Linux|ELF、out|o|so|a Mac|Mach-O|o|dylib、tbd、framework|a、framework #### Compile and link process 1. Pre-compilation (pre-compiler processes pre-compilation instructions such as `# include`,` # define` and generates `.i` or` .ii` files) 2. Compile (the compiler performs lexical analysis, syntax analysis, semantic analysis, intermediate code generation, object code generation, optimization, and generation of `.s` files) 3. Assembly (the assembler translates the assembly code into machine code and generates a `.o` file) 4. Linking (connectors perform address and space allocation, symbol resolution, relocation, and generate `.out` files) > The current version of GCC combines pre-compilation and compilation into one step, pre-compiled compiler cc1, assembler as, linker ld > MSVC compilation environment, compiler cl, linker link, executable file viewer dumpbin #### target document The files generated by the compiler after compiling the source code are called object files. The object file is structurally speaking, it is a compiled executable file format, but the linking process has not yet been performed, and some symbols or addresses may not be adjusted. > Executable files (`.exe` for Windows and` ELF` for Linux), dynamic link libraries (`.dll` for Windows and` .so` for Linux), static link libraries (`.lib` for Windows and Linux `.A`) are stored according to the executable file format (Windows according to PE-COFF, Linux according to ELF) ##### Object file format * Windows PE (Portable Executable), or PE-COFF, `.obj` format * Linux's ELF (Executable Linkable Format), `.o` format * Intel / Microsoft OMF (Object Module Format) * Unix `a.out` format * MS-DOS `.COM` format > PE and ELF are both COFF (Common File Format) variants ##### Object file storage structure Segment | Function --- | --- File Header | File header, describing the file attributes of the entire file (including whether the file is executable, is it statically linked or dynamically linked with the entry address, target hardware, target operating system, etc.) .text section | Code section, machine code compiled by the execution statement .data section | Data section, initialized global and local static variables .bss section | BSS section (Block Started by Symbol), uninitialized global and local static variables (because the default value is 0, so it is only reserved here and does not take up space) .rodata section | Read-only data section, which stores read-only data, generally read-only variables (such as const-modified variables) and string constants in the program .comment section | Comment section for compiler version information .note.GNU-stack section | Stack hint section > Other paragraphs omitted #### Linked Interface-Symbol In the link, the object files are actually a reference to the address between the object files, that is, a reference to the address of a function and a variable. We collectively refer to functions and variables as Symbols, and function names or variable names are Symbol Names. The following Symbol Table: Symbol (Symbol Name) | Symbol Value (Address) --- | --- main| 0x100 Add | 0x123 ... | ... ### Shared Library for Linux Shared libraries under Linux are ordinary ELF shared objects. The shared library version update should ensure the compatibility of the binary interface ABI (Application Binary Interface) #### Name `libname.so.x.y.z` * x: major version number, libraries with different major version numbers are not compatible and need to be recompiled * y: minor version number, higher version number is backward compatible with lower version number * z: release version number, no interface changes, fully compatible #### path Most open source systems, including Linux, follow the FHS (File Hierarchy Standard) standard. This standard specifies how system files are stored, including various directory structures, organizations, and roles. * `/lib`: Stores the most critical and basic shared libraries of the system, such as dynamic linker, C language runtime library, math library, etc. * `/usr/lib`: Stores key libraries required by non-system runtime, mainly development libraries * `/usr/local/lib`: Stores libraries that are not very relevant to the operating system itself, mainly libraries for some third-party applications > The dynamic linker looks for shared libraries in `/ lib`, `/usr/lib`, and specified by the `/etc/ld.so.conf` configuration file #### Environment variables * `LD_LIBRARY_PATH`: temporarily change the shared library search path of an application without affecting other applications * `LD_PRELOAD`: specify some shared libraries or even object files that are pre-loaded * `LD_DEBUG`: Turn on the debugging function of the dynamic linker #### So shared library writing Writing shared libraries with CLion Create a shared library named MySharedLib CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(MySharedLib) set(CMAKE_CXX_STANDARD 11) add_library(MySharedLib SHARED library.cpp library.h) ``` library.h ```cpp #ifndef MYSHAREDLIB_LIBRARY_H #define MYSHAREDLIB_LIBRARY_H // print Hello World! void hello(); // Sum using variable template parameters template T sum(T t) { return t; } template T sum(T first, Types ... rest) { return first + sum(rest...); } #endif ``` library.cpp ```cpp #include #include "library.h" void hello() { std::cout << "Hello, World!" << std::endl; } ``` #### use of so shared library (called by executable project) Use CLion to call a shared library Create an executable project named TestSharedLib CMakeLists.txt ```cmake cmake_minimum_required (VERSION 3.10) project (TestSharedLib) # C ++ 11 compile set (CMAKE_CXX_STANDARD 11) # Header file path set (INC_DIR / home / xx / code / clion / MySharedLib) # Library file path set (LIB_DIR / home / xx / code / clion / MySharedLib / cmake-build-debug) include_directories(${INC_DIR}) link_directories(${LIB_DIR}) link_libraries(MySharedLib) add_executable(TestSharedLib main.cpp) # link MySharedLib Library target_link_libraries(TestSharedLib MySharedLib) ``` main.cpp ```cpp #include #include "library.h" using std::cout; using std::endl; int main() { hello(); cout << "1 + 2 = " << sum(1,2) << endl; cout << "1 + 2 + 3 = " << sum(1,2,3) << endl; return 0; } ``` Results ``` Hello, World! 1 + 2 = 3 1 + 2 + 3 = 6 ``` ### Windows Application entry function * GUI(Graphical User Interface) application, linker options:`/SUBSYSTEM:WINDOWS` * CUI(Console User Interface) application, linker options:`/SUBSYSTEM:CONSOLE` _tWinMain versus _tmain Function declaration ```cpp Int WINAPI _tWinMain( HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int nCmdShow); int _tmain( int argc, TCHAR *argv[], TCHAR *envp[]); ``` Application Type | Entry Point Function | Embedded Startup Function --- | --- | --- GUI application for processing ANSI characters (strings) | _tWinMain (WinMain) | WinMainCRTSartup GUI application for handling Unicode characters (strings) | _tWinMain (wWinMain) | wWinMainCRTSartup CUI application that handles ANSI characters (strings) | _tmain (Main) | mainCRTSartup CUI application for handling Unicode characters (strings) | _tmain (wMain) | wmainCRTSartup Dynamic-Link Library | DllMain | _DllMainCRTStartup ### Dynamic-Link Library for Windows > Part of the knowledge comes from "Windows Core Programming (Fifth Edition)" #### Uses * Expanded application features * Simplified project management * Helps save memory * Promote the sharing of resources * Improved localization * Helps resolve differences between platforms * Can be used for special purposes #### Note * Creating a DLL is actually creating a function that can be called by an executable module * When a module provides a memory allocation function (malloc, new), it must also provide another memory release function (free, delete) * When mixing C and C++, use the extern "C" modifier * A DLL can export functions, variables (to avoid export), C++ classes (export and import need to be the same as the compiler, otherwise avoid export) * DLL module: __declspec (dllexport) in the cpp file is written before the include header file * Calling DLL's executable module: __declspec (dllimport) of the cpp file should not define MYLIBAPI before #### Search order for loading Windows programs 1. Directory containing executable files 2. The system directory of Windows can be obtained through GetSystemDirectory 3. 16-bit system directory, the System subdirectory in the Windows directory 4. The Windows directory can be obtained through GetWindowsDirectory 5. The current directory of the process 6. Directories listed in the PATH environment variable #### DLL Entry function DllMain function ```cpp BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: // Called when mapping a DLL to the process address space for the first time // The DLL is being mapped into the process' address space. break; case DLL_THREAD_ATTACH: // When the process creates a thread, it is used to tell the DLL to perform thread-related initialization (non-main thread execution) // A thread is bing created. break; case DLL_THREAD_DETACH: // The system calls ExitThread before the thread exits, the thread that is about to be terminated performs thread-related cleanup by telling the DLL // A thread is exiting cleanly. break; case DLL_PROCESS_DETACH: // Called when a DLL is removed from the process's address space // The DLL is being unmapped from the process' address space. break; } return (TRUE); // Used only for DLL_PROCESS_ATTACH } ``` #### Load and unload library LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread Function declaration ```cpp // load library HMODULE WINAPI LoadLibrary ( _In_ LPCTSTR lpFileName ); HMODULE LoadLibraryExA ( LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags ); // To load a Win32 DLL in a Universal Windows Platform (UWP) application, you need to call LoadPackagedLibrary instead of LoadLibrary or LoadLibraryEx HMODULE LoadPackagedLibrary ( LPCWSTR lpwLibFileName, DWORD Reserved ); // uninstall library BOOL WINAPI FreeLibrary ( _In_ HMODULE hModule ); // Unload the library and exit the thread VOID WINAPI FreeLibraryAndExitThread( _In_ HMODULE hModule, _In_ DWORD dwExitCode ); ``` #### Display link to export symbol GetProcAddress function declaration ```cpp FARPROC GetProcAddress ( HMODULE hInstDll, PCSTR pszSymbolName // Can only accept ANSI strings, not Unicode ); ``` #### DumpBin.exe View DLL information Use `DumpBin.exe` in the` VS Developer Command Prompt` to view the exported section of the DLL library (exported variables, functions, symbols of class names), relative virtual addresses (RVA). Such as: ``` DUMPBIN -exports D: \ mydll.dll ``` #### Flow chart of LoadLibrary and FreeLibrary LoadLibrary and FreeLibrary flowchart ##### LoadLibrary ![WindowsLoadLibrary](https://raw.githubusercontent.com/huihut/interview/master/images/WindowsLoadLibrary.png) ##### FreeLibrary ![WindowsFreeLibrary](https://raw.githubusercontent.com/huihut/interview/master/images/WindowsFreeLibrary.png) #### Writing of DLL library (export a DLL module) Writing DLL library (export a DLL module) DLL header file ```cpp // MyLib.h #ifdef MYLIBAPI // MYLIBAPI should be defined before include "Mylib.h" in all DLL source files // All functions / variables are being exported #else // This header file is included by an exe source code module, which means that all functions / variables are imported #define MYLIBAPI extern "C" __declspec (dllimport) #endif // Define any data structure and symbol here // Define exported variables (avoid exporting variables) MYLIBAPI int g_nResult; // Define the prototype of the exported function MYLIBAPI int Add(int nLeft, int nRight); ``` DLL source files ```cpp // MyLibFile1.cpp // Contains standard Windows and C runtime header files #include // Functions and variables exported from DLL source files #define MYLIBAPI extern "C" __declspec (dllexport) // Contains exported data structures, symbols, functions, variables #include "MyLib.h" // Put the code of this DLL source code file here int g_nResult; int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return g_nResult; } ``` #### Use of DLL library (dynamic link DLL at runtime) Use of DLL libraries (dynamically linking DLLs at runtime) ```cpp // A simple program that uses LoadLibrary and // GetProcAddress to access myPuts from Myputs.dll. #include #include typedef int (__cdecl *MYPROC)(LPWSTR); int main( void ) { HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; // Get a handle to the DLL module. hinstLib = LoadLibrary(TEXT("MyPuts.dll")); // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts"); // If the function address is valid, call the function. if (NULL != ProcAdd) { fRunTimeLinkSuccess = TRUE; (ProcAdd) (L"Message sent to the DLL function\n"); } // Free the DLL module. fFreeResult = FreeLibrary(hinstLib); } // If unable to call the DLL function, use an alternative. if (! fRunTimeLinkSuccess) printf("Message printed from executable\n"); return 0; } ``` ### Runtime(Runtime Library) #### Typical program operation steps 1. The operating system creates a process and passes control to the entry point of the program (often an entry function in the runtime library) 2. The entry function initializes the runtime library and program running environment (including heap, I / O, threads, global variable construction, etc.). 3. After the entry function is initialized, the main function is called to officially start executing the main part of the program. 4. After the main function finishes executing, return to the entry function for cleanup (including global variable destruction, heap destruction, closing I / O, etc.), and then make a system call to end the process. > I / O of a program refers to the interaction between the program and the outside world, including files, management programs, network, command line, signals, etc. More broadly, I / O refers to what the operating system understands as a "file." #### glibc entry `_start-> __libc_start_main-> exit-> _exit` The `main (argc, argv, __environ)` function is executed in `__libc_start_main`. #### MSVC CRT Entry `int mainCRTStartup (void)` Do the following: 1. Initialize global variables related to the OS version. 2. Initialize the heap. 3. Initialize I / O. 4. Get command line parameters and environment variables. 5. Initialize some data of the C library. 6. Call main and record the return value. 7. Check for errors and return the return value of main. #### C Language Runtime (CRT) It contains the following functions: * Start and exit: including entry functions and other functions on which entry functions depend. * Standard functions: There are functions implemented by the C language standard library specified by the C language standard. * I/O: Encapsulation and implementation of I / O functions. * Heap: Encapsulation and implementation of the heap. * Language implementation: implementation of some special functions in the language. * Debugging: Code that implements debugging functions. #### C Language Standard Library (ANSI C) contain: * Standard input and output (stdio.h) * File operations (stdio.h) * Character manipulation (ctype.h) * String operations (string.h) * Mathematical functions (math.h) * Resource Management (stdlib.h) * Format conversion (stdlib.h) * Time / Date (time.h) * Assertions (assert.h) * Constants on various types (limits.h & float.h) * Variable length parameter (stdarg.h) * Non-local jump (setjmp.h) ## 📚 Books > [huihut/CS-Books](https://github.com/huihut/CS-Books):📚 Computer Technology Books PDF ### Language * "C ++ Primer" * "Effective C ++" * "More Effective C ++" * "Exploring the C ++ Object Model" * "Understanding C ++ 11" * "Anatomy of STL Source Code" ### Algorithm * "Sword Finger Offer" * "Programming Pearls" * "Programmer Interview Collection" ### System * "Understanding Computer Systems" * "Windows Core Programming" * "Advanced Programming in the Unix Environment" ### The internet * "Unix Network Programming" * "Detailed Explanation of TCP / IP" ### Other * "The Self-Cultivation of Programmers" ## 🔱 C / C ++ Development Direction > The development direction of C / C ++ is very wide, including not limited to the following directions. The following are some of the requirements for recruiting positions of big factories and schools. ### Backend / Server [Backend Development] * Solid basic programming skills, master C / C ++ / JAVA and other development languages, commonly used algorithms and data structures; * Familiar with TCP / UDP network protocol and related programming, inter-process communication programming; * Understanding Python, Shell, Perl and other scripting languages; * Understand MYSQL, SQL language, programming, NoSQL, key-value storage principle; * Comprehensive and solid software knowledge structure, mastering professional knowledge of operating systems, software engineering, design patterns, data structures, database systems, and network security; * Understand the knowledge of distributed system design and development, load balancing technology, system disaster recovery design, and high availability systems. ### Desktop Client [PC client development] * Bachelor degree or above in computer software, majoring in programming, solid foundation, understanding of algorithms and data structure; * Familiar with memory management, file system, process thread scheduling of windows operating system; * Familiar with MFC / windows interface implementation mechanism, proficient in VC, proficient in C / C ++, proficient in STL, and network programming experience under Windows; * Familiar with Windows client development and debugging, Windows application software development experience is preferred; * Passionate about innovation and solving challenging problems, with good algorithm foundation and system analysis ability. ### Graphics / Game / VR / AR [Game Client Development] * Bachelor degree or above in computer science / engineering major, passion for programming, solid foundation, understanding of algorithms, data structure, software design related knowledge; * At least one programming language commonly used in game development, C ++ / C # programming experience is preferred; * Experience with game engines (such as Unity, Unreal) is preferred; * Knowledge of game client technology (such as graphics, audio, animation, physics, artificial intelligence, network synchronization) is preferred; * Passionate about innovation and solving challenging problems, strong learning ability, analytical and problem solving skills, and a good sense of teamwork; * Ability to read English technical documents; * Love games. ### Test Development [Test Development] * Bachelor degree or above in computer or related field; * One to two years of programming experience in C / C ++ / Python or other computer languages; * Ability to write test plans, test cases, and achieve performance and safety tests; * Ability to implement automated systems; * Ability to locate and investigate product defects and code-level debugging defects; * Work proactively, have a sense of responsibility, and have a good team spirit. ### Cyber Security / Reverse [safety technology] * Love the Internet, have a passionate pursuit of operating systems and network security, and professionalism is not limited; * Familiar with vulnerability mining, network security attack and defense technologies, and common hacking techniques; * Master basic development skills and be proficient in C / C ++ language; * Have a good grasp of database, operating system and network principles; * Experience in software reverse, network security attack and defense or security system development is preferred. ### Embedded / IoT [Embedded Application Development] * Have a good programming foundation, proficient in C / C ++ language; * Master the necessary knowledge for software development such as operating system and data structure; * Strong communication and understanding ability and good teamwork awareness; * Development experience on Linux / Android system platform is preferred. ### Audio / Video / Streaming / SDK [Audio and video codec] 1. Master's degree or above, computer, signal processing, mathematics, information and related majors and directions; 2. Solid video coding and decoding foundation, familiar with commonly used HEVC or H264, have a good digital signal processing foundation; 3. Master C / C ++, strong code ability, familiar with an assembly language is preferred; 4. Strong English literature reading ability; 5. Strong learning ability, teamwork spirit, and strong resistance to stress. ### Computer Vision / Machine Learning [Computer Vision Research] * Computer, applied mathematics, pattern recognition, artificial intelligence, automatic control, statistics, operations research, bioinformatics, physics / quantum computing, neuroscience, sociology / psychology and other majors, image processing, pattern recognition, machine learning related research Direction, undergraduate and above, doctoral degree is preferred; * Proficient in basic algorithms and applications related to computer vision and image processing; * Strong algorithm implementation ability, proficient in C / C ++ programming, familiar with at least one programming language of Shell / Python / Matlab; * Publication of papers in computer vision, pattern recognition and other academic conferences or journals, awards in relevant international competitions, and related patents are preferred. ## 💯 Review brush question website * [cplusplus](http://www.cplusplus.com/) * [cppreference](https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5) * [runoob](http://www.runoob.com/cplusplus/cpp-tutorial.html) * [leetcode](https://leetcode.com/) | [leetcode-cn](https://leetcode-cn.com/) * [lintcode](https://www.lintcode.com/) * [nowcoder](https://www.nowcoder.net/) ## 📝 Interview Question Experience * [Nowcoder.com's summary of the 2020 autumn tricks! (Post division)](https://www.nowcoder.com/discuss/205497) * [【Preparation for Autumn Moves】 Raiders for 2020 Autumn Moves](https://www.nowcoder.com/discuss/197116) * [2019 School Recruitment Summary! 【Daily Update】](https://www.nowcoder.com/discuss/90907) * [2019 School Recruitment Technology Posts Summary [Technology]](https://www.nowcoder.com/discuss/146655) * [2018 School Recruitment Questions Summary](https://www.nowcoder.com/discuss/68802) * [2017 Autumn Campus Recruitment Pen and Face Summaries](https://www.nowcoder.com/discuss/12805) * [The most complete collection of 2017 spring tricks in history!!](https://www.nowcoder.com/discuss/25268) * [Interview questions are here](https://www.nowcoder.com/discuss/57978) * [zhihu. On the Internet job search, what well-written and attentive face have you seen? It is best to share your own facial and mental journey. ](https://www.zhihu.com/question/29693016) * [zhihu. What are the most common interview algorithm questions for internet companies? ](https://www.zhihu.com/question/24964987) * [CSDN. C ++ Interview Questions Completely Organized](http://blog.csdn.net/ljzcome/article/details/574158) * [CSDN. Baidu R & D interview questions (C ++ direction)](http://blog.csdn.net/Xiongchao99/article/details/74524807?locationNum=6&fps=1) * [CSDN. C ++ 30 common interview questions](http://blog.csdn.net/fakine/article/details/51321544) * [CSDN. Tencent 2016 intern interview experience (already got offer)](http://blog.csdn.net/onever_say_love/article/details/51223886) * [cnblogs. C ++ Interview Collection (Questions Asked for Interview)](https://www.cnblogs.com/Y1Focus/p/6707121.html) * [cnblogs. C / C ++ written and interview questions summary](https://www.cnblogs.com/fangyukuan/archive/2010/09/18/1829871.html) * [cnblogs. Summary of common C ++ interview questions and basic knowledge points (1)](https://www.cnblogs.com/LUO77/p/5771237.html) * [segmentfault. Summary of common interview questions in C ++](https://segmentfault.com/a/1190000003745529) ## 📆 Recruiting time posts * [nowcoder . Enterprise Recruitment Schedule](https://www.nowcoder.com/school/schedule) ## 👍 Recommend * [Github. CyC2018 / Job-Recommend](https://github.com/CyC2018/Job-Recommend): 🔎 Internet internal push information (social recruitment, school recruitment, internship) * [Github. Amusi / AI-Job-Recommend](https://github.com/amusi/AI-Job-Recommend): direction of artificial intelligence of domestic companies (including machine learning, deep learning, computer vision and natural language processing) Job recruitment information (including full-time, internship and school recruitment) ## 👬 Contributor ## 📜 License This repository follows the CC BY-NC-SA 4.0 (signed-non-commercial use-shared in the same way) agreement, please indicate the source when reprinting, and should not be used for commercial purposes. [![CC BY-NC-SA 4.0](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)](https://github.com/huihut/interview/blob/master/LICENSE) ================================================ FILE: STL/README.md ================================================ [STL 详细文档](https://github.com/huihut/interview/blob/master/STL/STL.md) # 容器(containers) ## array array 是固定大小的顺序容器,它们保存了一个以严格的线性顺序排列的特定数量的元素。 方法|含义 ---|--- begin|返回指向数组容器中第一个元素的迭代器 end|返回指向数组容器中最后一个元素之后的理论元素的迭代器 rbegin|返回指向数组容器中最后一个元素的反向迭代器 rend|返回一个反向迭代器,指向数组中第一个元素之前的理论元素 cbegin|返回指向数组容器中第一个元素的常量迭代器(const_iterator) cend|返回指向数组容器中最后一个元素之后的理论元素的常量迭代器(const_iterator) crbegin|返回指向数组容器中最后一个元素的常量反向迭代器(const_reverse_iterator) crend|返回指向数组中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator) size|返回数组容器中元素的数量 max_size|返回数组容器可容纳的最大元素数 empty|返回一个布尔值,指示数组容器是否为空 operator[]|返回容器中第 n(参数)个位置的元素的引用 at|返回容器中第 n(参数)个位置的元素的引用 front|返回对容器中第一个元素的引用 back|返回对容器中最后一个元素的引用 data|返回指向容器中第一个元素的指针 fill|用 val(参数)填充数组所有元素 swap|通过 x(参数)的内容交换数组的内容 get(array)|形如 `std::get<0>(myarray)`;传入一个数组容器,返回指定位置元素的引用 relational operators (array)|形如 `arrayA > arrayB`;依此比较数组每个元素的大小关系 ## vector vector 是表示可以改变大小的数组的序列容器。 方法|含义 ---|--- vector|构造函数 ~vector|析构函数,销毁容器对象 operator=|将新内容分配给容器,替换其当前内容,并相应地修改其大小 begin|返回指向容器中第一个元素的迭代器 end|返回指向容器中最后一个元素之后的理论元素的迭代器 rbegin|返回指向容器中最后一个元素的反向迭代器 rend|返回一个反向迭代器,指向中第一个元素之前的理论元素 cbegin|返回指向容器中第一个元素的常量迭代器(const_iterator) cend|返回指向容器中最后一个元素之后的理论元素的常量迭代器(const_iterator) crbegin|返回指向容器中最后一个元素的常量反向迭代器(const_reverse_iterator) crend|返回指向容器中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator) size|返回容器中元素的数量 max_size|返回容器可容纳的最大元素数 resize|调整容器的大小,使其包含 n(参数)个元素 capacity|返回当前为 vector 分配的存储空间(容量)的大小 empty|返回 vector 是否为空 reserve|请求 vector 容量至少足以包含 n(参数)个元素 shrink_to_fit|要求容器减小其 capacity(容量)以适应其 size(元素数量) operator[]|返回容器中第 n(参数)个位置的元素的引用 at|返回容器中第 n(参数)个位置的元素的引用 front|返回对容器中第一个元素的引用 back|返回对容器中最后一个元素的引用 data|返回指向容器中第一个元素的指针 assign|将新内容分配给 vector,替换其当前内容,并相应地修改其 size push_back|在容器的最后一个元素之后添加一个新元素 pop_back|删除容器中的最后一个元素,有效地将容器 size 减少一个 insert|通过在指定位置的元素之前插入新元素来扩展该容器,通过插入元素的数量有效地增加容器大小 erase|从 vector 中删除单个元素(`position`)或一系列元素(`[first,last)`),这有效地减少了被去除的元素的数量,从而破坏了容器的大小 swap|通过 x(参数)的内容交换容器的内容,x 是另一个类型相同、size 可能不同的 vector 对象 clear|从 vector 中删除所有的元素(被销毁),留下 size 为 0 的容器 emplace|通过在 position(参数)位置处插入新元素 args(参数)来扩展容器 emplace_back|在 vector 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后 get_allocator|返回与vector关联的构造器对象的副本 swap(vector)|容器 x(参数)的内容与容器 y(参数)的内容交换。两个容器对象都必须是相同的类型(相同的模板参数),尽管大小可能不同 relational operators (vector)|形如 `vectorA > vectorB`;依此比较每个元素的大小关系 ## deque deque(['dek])(双端队列)是double-ended queue 的一个不规则缩写。deque是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。 方法|含义 ---|--- deque|构造函数 push_back|在当前的最后一个元素之后 ,在 deque 容器的末尾添加一个新元素 push_front|在 deque 容器的开始位置插入一个新的元素,位于当前的第一个元素之前 pop_back|删除 deque 容器中的最后一个元素,有效地将容器大小减少一个 pop_front|删除 deque 容器中的第一个元素,有效地减小其大小 emplace_front|在 deque 的开头插入一个新的元素,就在其当前的第一个元素之前 emplace_back|在 deque 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后 ## forward\_list forward_list(单向链表)是序列容器,允许在序列中的任何地方进行恒定的时间插入和擦除操作。 方法|含义 ---|--- forward\_list|返回指向容器中第一个元素之前的位置的迭代器 cbefore\_begin|返回指向容器中第一个元素之前的位置的 const_iterator ## list list,双向链表,是序列容器,允许在序列中的任何地方进行常数时间插入和擦除操作,并在两个方向上进行迭代。 ## stack stack 是一种容器适配器,用于在LIFO(后进先出)的操作,其中元素仅从容器的一端插入和提取。 ## queue queue 是一种容器适配器,用于在FIFO(先入先出)的操作,其中元素插入到容器的一端并从另一端提取。 ## priority_queue ## set set 是按照特定顺序存储唯一元素的容器。 ## multiset ## map map 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。 方法|含义 ---|--- map|构造函数 begin|返回引用容器中第一个元素的迭代器 key_comp|返回容器用于比较键的比较对象的副本 value_comp|返回可用于比较两个元素的比较对象,以获取第一个元素的键是否在第二个元素之前 find|在容器中搜索具有等于 k(参数)的键的元素,如果找到则返回一个迭代器,否则返回 map::end 的迭代器 count|在容器中搜索具有等于 k(参数)的键的元素,并返回匹配的数量 lower_bound|返回一个非递减序列 `[first, last)`(参数)中的第一个大于等于值 val(参数)的位置的迭代器 upper_bound|返回一个非递减序列 `[first, last)`(参数)中第一个大于 val(参数)的位置的迭代器 equal_range|获取相同元素的范围,返回包含容器中所有具有与 k(参数)等价的键的元素的范围边界(`pair< map::iterator, map::iterator >`) ## multimap ## unordered\_set ## unordered\_multiset ## unordered\_map ## unordered\_multimap ## tuple 元组是一个能够容纳元素集合的对象。每个元素可以是不同的类型。 ## pair 这个类把一对值(values)结合在一起,这些值可能是不同的类型(T1 和 T2)。每个值可以被公有的成员变量first、second访问。 # 算法(algorithms) ```cpp // 简单查找算法,要求输入迭代器(input iterator) find(beg, end, val); // 返回一个迭代器,指向输入序列中第一个等于 val 的元素,未找到返回 end find_if(beg, end, unaryPred); // 返回一个迭代器,指向第一个满足 unaryPred 的元素,未找到返回 end find_if_not(beg, end, unaryPred); // 返回一个迭代器,指向第一个令 unaryPred 为 false 的元素,未找到返回 end count(beg, end, val); // 返回一个计数器,指出 val 出现了多少次 count_if(beg, end, unaryPred); // 统计有多少个元素满足 unaryPred all_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都满足 unaryPred any_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否任意(存在)一个元素满足 unaryPred none_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都不满足 unaryPred // 查找重复值的算法,传入向前迭代器(forward iterator) adjacent_find(beg, end); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end adjacent_find(beg, end, binaryPred); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end search_n(beg, end, count, val); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end search_n(beg, end, count, val, binaryPred); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end // 查找子序列算法,除 find_first_of(前两个输入迭代器,后两个前向迭代器) 外,都要求两个前向迭代器 search(beg1, end1, beg2, end2); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1 search(beg1, end1, beg2, end2, binaryPred); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1 find_first_of(beg1, end1, beg2, end2); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1 find_first_of(beg1, end1, beg2, end2, binaryPred); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1 find_end(beg1, end1, beg2, end2); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1 find_end(beg1, end1, beg2, end2, binaryPred); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1 // 其他只读算法,传入输入迭代器 for_each(beg, end, unaryOp); // 对输入序列中的每个元素应用可调用对象 unaryOp,unaryOp 的返回值被忽略 mismatch(beg1, end1, beg2); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素 mismatch(beg1, end1, beg2, binaryPred); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素 equal(beg1, end1, beg2); // 比较每个元素,确定两个序列是否相等。 equal(beg1, end1, beg2, binaryPred); // 比较每个元素,确定两个序列是否相等。 // 二分搜索算法,传入前向迭代器或随机访问迭代器(random-access iterator),要求序列中的元素已经是有序的。通过小于运算符(<)或 comp 比较操作实现比较。 lower_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end lower_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end upper_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end upper_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end equal_range(beg, end, val); // 返回一个 pair,其 first 成员是 lower_bound 返回的迭代器,其 second 成员是 upper_bound 返回的迭代器 binary_search(beg, end, val); // 返回一个 bool 值,指出序列中是否包含等于 val 的元素。对于两个值 x 和 y,当 x 不小于 y 且 y 也不小于 x 时,认为它们相等。 // 只写不读算法,要求输出迭代器(output iterator) fill(beg, end, val); // 将 val 赋予每个元素,返回 void fill_n(beg, cnt, val); // 将 val 赋予 cnt 个元素,返回指向写入到输出序列最有一个元素之后位置的迭代器 genetate(beg, end, Gen); // 每次调用 Gen() 生成不同的值赋予每个序列,返回 void genetate_n(beg, cnt, Gen); // 每次调用 Gen() 生成不同的值赋予 cnt 个序列,返回指向写入到输出序列最有一个元素之后位置的迭代器 // 使用输入迭代器的写算法,读取一个输入序列,将值写入到一个输出序列(dest)中 copy(beg, end, dest); // 从输入范围将元素拷贝所有元素到 dest 指定定的目的序列 copy_if(beg, end, dest, unaryPred); // 从输入范围将元素拷贝满足 unaryPred 的元素到 dest 指定定的目的序列 copy_n(beg, n, dest); // 从输入范围将元素拷贝前 n 个元素到 dest 指定定的目的序列 move(beg, end, dest); // 对输入序列中的每个元素调用 std::move,将其移动到迭代器 dest 开始始的序列中 transform(beg, end, dest, unaryOp); // 调用给定操作(一元操作),并将结果写到dest中 transform(beg, end, beg2, dest, binaryOp); // 调用给定操作(二元操作),并将结果写到dest中 replace_copy(beg, end, dest, old_val, new_val); // 将每个元素拷贝到 dest,将等于 old_val 的的元素替换为 new_val replace_copy_if(beg, end, dest, unaryPred, new_val); // 将每个元素拷贝到 dest,将满足 unaryPred 的的元素替换为 new_val merge(beg1, end1, beg2, end2, dest); // 两个输入序列必须都是有序的,用 < 运算符将合并后的序列写入到 dest 中 merge(beg1, end1, beg2, end2, dest, comp); // 两个输入序列必须都是有序的,使用给定的比较操作(comp)将合并后的序列写入到 dest 中 // 使用前向迭代器的写算法,要求前向迭代器 iter_swap(iter1, iter2); // 交换 iter1 和 iter2 所表示的元素,返回 void swap_ranges(beg1, end1, beg2); // 将输入范围中所有元素与 beg2 开始的第二个序列中所有元素进行交换。返回递增后的的 beg2,指向最后一个交换元素之后的位置。 replace(beg, end, old_val, new_val); // 用 new_val 替换等于 old_val 的每个匹配元素 replace_if(beg, end, unaryPred, new_val); // 用 new_val 替换满足 unaryPred 的每个匹配元素 // 使用双向迭代器的写算法,要求双向选代器(bidirectional iterator) copy_backward(beg, end, dest); // 从输入范围中拷贝元素到指定目的位置。如果范围为空,则返回值为 dest;否则,返回值表示从 *beg 中拷贝或移动的元素。 move_backward(beg, end, dest); // 从输入范围中移动元素到指定目的位置。如果范围为空,则返回值为 dest;否则,返回值表示从 *beg 中拷贝或移动的元素。 inplace_merge(beg, mid, end); // 将同一个序列中的两个有序子序列合并为单一的有序序列。beg 到 mid 间的子序列和 mid 到 end 间的子序列被合并,并被写入到原序列中。使用 < 比较元素。 inplace_merge(beg, mid, end, comp); // 将同一个序列中的两个有序子序列合并为单一的有序序列。beg 到 mid 间的子序列和 mid 到 end 间的子序列被合并,并被写入到原序列中。使用给定的 comp 操作。 // 划分算法,要求双向选代器(bidirectional iterator) is_partitioned(beg, end, unaryPred); // 如果所有满足谓词 unaryPred 的元素都在不满足 unarypred 的元素之前,则返回 true。若序列为空,也返回 true partition_copy(beg, end, dest1, dest2, unaryPred); // 将满足 unaryPred 的元素拷贝到到 dest1,并将不满足 unaryPred 的元素拷贝到到 dest2。返回一个迭代器 pair,其 first 成员表示拷贝到 dest1 的的元素的末尾,second 表示拷贝到 dest2 的元素的末尾。 partitioned_point(beg, end, unaryPred); // 输入序列必须是已经用 unaryPred 划分过的。返回满足 unaryPred 的范围的尾后迭代器。如果返回的迭代器不是 end,则它指向的元素及其后的元素必须都不满足 unaryPred stable_partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg // 排序算法,要求随机访问迭代器(random-access iterator) sort(beg, end); // 排序整个范围 stable_sort(beg, end); // 排序整个范围(稳定排序) sort(beg, end, comp); // 排序整个范围 stable_sort(beg, end, comp); // 排序整个范围(稳定排序) is_sorted(beg, end); // 返回一个 bool 值,指出整个输入序列是否有序 is_sorted(beg, end, comp); // 返回一个 bool 值,指出整个输入序列是否有序 is_sorted_until(beg, end); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器 is_sorted_until(beg, end, comp); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器 partial_sort(beg, mid, end); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置 partial_sort(beg, mid, end, comp); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置 partial_sort_copy(beg, end, destBeg, destEnd); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中 partial_sort_copy(beg, end, destBeg, destEnd, comp); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中 nth_element(beg, nth, end); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它 nth_element(beg, nth, end, comp); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它 // 使用前向迭代器的重排算法。普通版本在输入序列自身内部重拍元素,_copy 版本完成重拍后写入到指定目的序列中,而不改变输入序列 remove(beg, end, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器 remove_if(beg, end, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器 remove_copy(beg, end, dest, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器 remove_copy_if(beg, end, dest, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器 unique(beg, end); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置 unique (beg, end, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置 unique_copy(beg, end, dest); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置 unique_copy_if(beg, end, dest, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置 rotate(beg, mid, end); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素 rotate_copy(beg, mid, end, dest); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素 // 使用双向迭代器的重排算法 reverse(beg, end); // 翻转序列中的元素,返回 void reverse_copy(beg, end, dest);; // 翻转序列中的元素,返回一个迭代器,指向拷贝到目的序列的元素的尾后位置 // 使用随机访问迭代器的重排算法 random_shuffle(beg, end); // 混洗输入序列中的元素,返回 void random_shuffle(beg, end, rand); // 混洗输入序列中的元素,rand 接受一个正整数的随机对象,返回 void shuffle(beg, end, Uniform_rand); // 混洗输入序列中的元素,Uniform_rand 必须满足均匀分布随机数生成器的要求,返回 void // 最小值和最大值,使用 < 运算符或给定的比较操作 comp 进行比较 min(val1, va12); // 返回 val1 和 val2 中的最小值,两个实参的类型必须完全一致。参数和返回类型都是 const的引引用,意味着对象不会被拷贝。下略 min(val1, val2, comp); min(init_list); min(init_list, comp); max(val1, val2); max(val1, val2, comp); max(init_list); max(init_list, comp); minmax(val1, val2); // 返回一个 pair,其 first 成员为提供的值中的较小者,second 成员为较大者。下略 minmax(vall, val2, comp); minmax(init_list); minmax(init_list, comp); min_element(beg, end); // 返回指向输入序列中最小元素的迭代器 min_element(beg, end, comp); // 返回指向输入序列中最小元素的迭代器 max_element(beg, end); // 返回指向输入序列中最大元素的迭代器 max_element(beg, end, comp); // 返回指向输入序列中最大元素的迭代器 minmax_element(beg, end); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素 minmax_element(beg, end, comp); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素 // 字典序比较,根据第一对不相等的元素的相对大小来返回结果。如果第一个序列在字典序中小于第二个序列,则返回 true。否则,返回 fa1se。如果个序列比另一个短,且所有元素都与较长序列的对应元素相等,则较短序列在字典序中更小。如果序列长度相等,且对应元素都相等,则在字典序中任何一个都不大于另外一个。 lexicographical_compare(beg1, end1, beg2, end2); lexicographical_compare(beg1, end1, beg2, end2, comp); ``` ================================================ FILE: STL/STL.md ================================================ # STL ## 网站 * [github . huihut/note/STL.md](https://github.com/huihut/note/blob/master/STL.md) * [cplusplus . stl](http://www.cplusplus.com/reference/stl/) * [cppreference . C++ 参考手册](http://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5) * [CSDN专栏:STL学习笔记](http://blog.csdn.net/column/details/geek-stl.html) ## 组成 * 容器(containers) * 算法(algorithms) * 迭代器(iterators) * 仿函数(functors) * 配接器(adapters) * 空间配置器(allocator) ## 容器(containers) * 序列式容器(sequence containers):元素都是可序(ordered),但未必是有序(sorted) * 关联式容器(associattive containers) ### array array是固定大小的顺序容器,它们保存了一个以严格的线性顺序排列的特定数量的元素。 在内部,一个数组除了它所包含的元素(甚至不是它的大小,它是一个模板参数,在编译时是固定的)以外不保存任何数据。存储大小与用语言括号语法([])声明的普通数组一样高效。这个类只是增加了一层成员函数和全局函数,所以数组可以作为标准容器使用。 与其他标准容器不同,数组具有固定的大小,并且不通过分配器管理其元素的分配:它们是封装固定大小数组元素的聚合类型。因此,他们不能动态地扩大或缩小。 零大小的数组是有效的,但是它们不应该被解除引用(成员的前面,后面和数据)。 与标准库中的其他容器不同,交换两个数组容器是一种线性操作,它涉及单独交换范围内的所有元素,这通常是相当低效的操作。另一方面,这允许迭代器在两个容器中的元素保持其原始容器关联。 数组容器的另一个独特特性是它们可以被当作元组对象来处理:array头部重载get函数来访问数组元素,就像它是一个元组,以及专门的tuple_size和tuple_element类型。 ```cpp template < class T, size_t N > class array; ``` ![](http://img.blog.csdn.net/20160405225541788) #### array::begin 返回指向数组容器中第一个元素的迭代器。 ![](https://i.stack.imgur.com/oa3EQ.png) ```cpp iterator begin() noexcept; const_iterator begin() const noexcept; ``` Example ```cpp #include #include int main() { std::array myarray = {2, 16, 77,34, 50}; std::cout << "myarray contains:"; for(auto it = myarray.begin(); it != myarray.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 2 16 77 34 50 ``` #### array::end 返回指向数组容器中最后一个元素之后的理论元素的迭代器。 ```cpp iterator end() noexcept; const_iterator end() const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = { 5, 19, 77, 34, 99 }; std::cout << "myarray contains:"; for ( auto it = myarray.begin(); it != myarray.end(); ++it ) std::cout << ' ' << *it; std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 5 19 77 34 99 ``` #### array::rbegin 返回指向数组容器中最后一个元素的反向迭代器。 ```cpp reverse_iterator rbegin()noexcept; const_reverse_iterator rbegin()const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = {4, 26, 80, 14} ; for(auto rit = myarray.rbegin(); rit < myarray.rend(); ++rit) std::cout << ' ' << *rit; std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 14 80 26 4 ``` #### array::rend 返回一个反向迭代器,指向数组中第一个元素之前的理论元素(这被认为是它的反向结束)。 ```cpp reverse_iterator rend() noexcept; const_reverse_iterator rend() const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = {4, 26, 80, 14}; std::cout << "myarray contains"; for(auto rit = myarray.rbegin(); rit < myarray.rend(); ++rit) std::cout << ' ' << *rit; std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 14 80 26 4 ``` #### array::cbegin 返回指向数组容器中第一个元素的常量迭代器(const_iterator);这个迭代器可以增加和减少,但是不能用来修改它指向的内容。 ```cpp const_iterator cbegin()const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = {2, 16, 77, 34, 50}; std::cout << "myarray contains:"; for ( auto it = myarray.cbegin(); it != myarray.cend(); ++it ) std::cout << ' ' << *it; // cannot modify *it std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 2 16 77 34 50 ``` #### array::cend 返回指向数组容器中最后一个元素之后的理论元素的常量迭代器(const_iterator)。这个迭代器可以增加和减少,但是不能用来修改它指向的内容。 ```cpp const_iterator cend() const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = { 15, 720, 801, 1002, 3502 }; std::cout << "myarray contains:"; for ( auto it = myarray.cbegin(); it != myarray.cend(); ++it ) std::cout << ' ' << *it; // cannot modify *it std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 2 16 77 34 50 ``` #### array::crbegin 返回指向数组容器中最后一个元素的常量反向迭代器(const_reverse_iterator) ```cpp const_reverse_iterator crbegin()const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = {10, 20, 30, 40, 50, 60} ; std::cout << "myarray backwards:"; for ( auto rit=myarray.crbegin() ; rit < myarray.crend(); ++rit ) std::cout << ' ' << *rit; // cannot modify *rit std::cout << '\n'; return 0; } ``` Output ``` myarray backwards: 60 50 40 30 20 10 ``` #### array::crend 返回指向数组中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator),它被认为是其反向结束。 ```cpp const_reverse_iterator crend() const noexcept; ``` Example ```cpp #include #include int main () { std::array myarray = {10, 20, 30, 40, 50, 60} ; std::cout << "myarray backwards:"; for ( auto rit=myarray.crbegin() ; rit < myarray.crend(); ++rit ) std::cout << ' ' << *rit; // cannot modify *rit std::cout << '\n'; return 0; } ``` Output ``` myarray backwards: 60 50 40 30 20 10 ``` #### array::size 返回数组容器中元素的数量。 ```cpp constexpr size_type size()noexcept; ``` Example ```cpp #include #include int main () { std::array myints; std::cout << "size of myints:" << myints.size() << std::endl; std::cout << "sizeof(myints):" << sizeof(myints) << std::endl; return 0; } ``` Possible Output ``` size of myints: 5 sizeof(myints): 20 ``` #### array::max_size 返回数组容器可容纳的最大元素数。数组对象的max_size与其size一样,始终等于用于实例化数组模板类的第二个模板参数。 ```cpp constexpr size_type max_size() noexcept; ``` Example ```cpp #include #include int main () { std::array myints; std::cout << "size of myints: " << myints.size() << '\n'; std::cout << "max_size of myints: " << myints.max_size() << '\n'; return 0; } ``` Output ``` size of myints: 10 max_size of myints: 10 ``` #### array::empty 返回一个布尔值,指示数组容器是否为空,即它的size()是否为0。 ```cpp constexpr bool empty() noexcept; ``` Example ```cpp #include #include int main () { std::array first; std::array second; std::cout << "first " << (first.empty() ? "is empty" : "is not empty") << '\n'; std::cout << "second " << (second.empty() ? "is empty" : "is not empty") << '\n'; return 0; } ``` Output: ``` first is empty second is not empt ``` #### array::operator[] 返回数组中第n个位置的元素的引用。与array::at相似,但array::at会检查数组边界并通过抛出一个out_of_range异常来判断n是否超出范围,而array::operator[]不检查边界。 ```cpp reference operator[] (size_type n); const_reference operator[] (size_type n) const; ``` Example ```cpp #include #include int main () { std::array myarray; unsigned int i; // assign some values: for(i=0; i<10; i++) myarray[i] = i; // print content std::cout << "myarray contains:"; for(i=0; i<10; i++) std::cout << ' ' << myarray[i]; std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 0 1 2 3 4 5 6 7 8 9 ``` #### array::at 返回数组中第n个位置的元素的引用。与array::operator[]相似,但array::at会检查数组边界并通过抛出一个out_of_range异常来判断n是否超出范围,而array::operator[]不检查边界。 ```cpp reference at ( size_type n ); const_reference at ( size_type n ) const; ``` Example ```cpp #include #include int main() { std::array myarray; unsigned int i; // assign some values: for (i = 0; i<10; i++) myarray[i] = i; // print content std::cout << "myarray contains:"; for (i = 0; i<10; i++) std::cout << ' ' << myarray.at(i); std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 0 1 2 3 4 5 6 7 8 9 ``` #### array::front 返回对数组容器中第一个元素的引用。array::begin返回的是迭代器,array::front返回的是直接引用。 在空容器上调用此函数会导致未定义的行为。 ```cpp reference front(); const_reference front() const; ``` Example ```cpp #include #include int main () { std::array myarray = {2, 16, 77}; std::cout << "front is: " << myarray.front() << std::endl; // 2 std::cout << "back is: " << myarray.back() << std::endl; // 77 myarray.front() = 100; std::cout << "myarray now contains:"; for ( int& x : myarray ) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` front is: 2 back is: 77 myarray now contains: 100 16 77 ``` #### array::back 返回对数组容器中最后一个元素的引用。array::end返回的是迭代器,array::back返回的是直接引用。 在空容器上调用此函数会导致未定义的行为。 ```cpp reference back(); const_reference back() const; ``` Example ```cpp #include #include int main () { std::array myarray = {5, 19, 77}; std::cout << "front is: " << myarray.front() << std::endl; // 5 std::cout << "back is: " << myarray.back() << std::endl; // 77 myarray.back() = 50; std::cout << "myarray now contains:"; for ( int& x : myarray ) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` front is: 5 back is: 77 myarray now contains: 5 19 50 ``` #### array::data 返回指向数组对象中第一个元素的指针。 由于数组中的元素存储在连续的存储位置,所以检索到的指针可以偏移以访问数组中的任何元素。 ```cpp value_type* data() noexcept; const value_type* data() const noexcept; ``` Example ```cpp #include #include #include int main () { const char* cstr = "Test string"; std::array charray; std::memcpy (charray.data(),cstr,12); std::cout << charray.data() << '\n'; return 0; } ``` Output ``` Test string ``` #### array::fill 用val填充数组所有元素,将val设置为数组对象中所有元素的值。 ```cpp void fill (const value_type& val); ``` Example ```cpp #include #include int main () { std::array myarray; myarray.fill(5); std::cout << "myarray contains:"; for ( int& x : myarray) { std::cout << ' ' << x; } std::cout << '\n'; return 0; } ``` Output ``` myarray contains: 5 5 5 5 5 5 ``` #### array::swap 通过x的内容交换数组的内容,这是另一个相同类型的数组对象(包括相同的大小)。 与其他容器的交换成员函数不同,此成员函数通过在各个元素之间执行与其大小相同的单独交换操作,以线性时间运行。 ```cpp void swap (array& x) noexcept(noexcept(swap(declval(),declval()))); ``` Example ```cpp #include #include int main () { std::array first = {10, 20, 30, 40, 50}; std::array second = {11, 22, 33, 44, 55}; first.swap (second); std::cout << "first:"; for (int& x : first) std::cout << ' ' << x; std::cout << '\n'; std::cout << "second:"; for (int& x : second) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` first: 11 22 33 44 55 second: 10 20 30 40 50 ``` #### get(array) 形如:std::get<0>(myarray);传入一个数组容器,返回指定位置元素的引用。 ```cpp template T&get(array &arr)noexcept; template T && get(array && arr)noexcept; template const T&get(const array &arr)noexcept; ``` Example ```cpp #include #include #include int main () { std::array myarray = {10, 20, 30}; std::tuple mytuple (10, 20, 30); std::tuple_element<0,decltype(myarray)>::type myelement; // int myelement myelement = std::get<2>(myarray); std::get<2>(myarray) = std::get<0>(myarray); std::get<0>(myarray) = myelement; std::cout << "first element in myarray: " << std::get<0>(myarray) << "\n"; std::cout << "first element in mytuple: " << std::get<0>(mytuple) << "\n"; return 0; } ``` Output ``` first element in myarray: 30 first element in mytuple: 10 ``` #### relational operators (array) 形如:arrayA != arrayB、arrayA > arrayB;依此比较数组每个元素的大小关系。 ```cpp (1) template bool operator ==(const array &lhs,const array &rhs); (2) template bool operator!=(const array &lhs,const array &rhs); (3) template bool operator <(const array &lhs,const array &rhs); (4) template bool operator <=(const array &lhs,const array &rhs); (5) template bool operator>(const array &lhs,const array &rhs); (6) template bool operator> =(const array &lhs,const array &rhs); ``` Example ```cpp #include #include int main () { std::array a = {10, 20, 30, 40, 50}; std::array b = {10, 20, 30, 40, 50}; std::array c = {50, 40, 30, 20, 10}; if (a==b) std::cout << "a and b are equal\n"; if (b!=c) std::cout << "b and c are not equal\n"; if (bb) std::cout << "c is greater than b\n"; if (a<=b) std::cout << "a is less than or equal to b\n"; if (a>=b) std::cout << "a is greater than or equal to b\n"; return 0; } ``` Output ``` a and b are equal b and c are not equal b is less than c c is greater than b a is less than or equal to b a is greater than or equal to b ``` ### vector vector是表示可以改变大小的数组的序列容器。 就像数组一样,vector为它们的元素使用连续的存储位置,这意味着它们的元素也可以使用到其元素的常规指针上的偏移来访问,而且和数组一样高效。但是与数组不同的是,它们的大小可以动态地改变,它们的存储由容器自动处理。 在内部,vector使用一个动态分配的数组来存储它们的元素。这个数组可能需要重新分配,以便在插入新元素时增加大小,这意味着分配一个新数组并将所有元素移动到其中。就处理时间而言,这是一个相对昂贵的任务,因此每次将元素添加到容器时矢量都不会重新分配。 相反,vector容器可以分配一些额外的存储以适应可能的增长,并且因此容器可以具有比严格需要包含其元素(即,其大小)的存储更大的实际容量。库可以实现不同的策略的增长到内存使用和重新分配之间的平衡,但在任何情况下,再分配应仅在对数生长的间隔发生尺寸,使得在所述载体的末端各个元件的插入可以与提供分期常量时间复杂性。 因此,与数组相比,载体消耗更多的内存来交换管理存储和以有效方式动态增长的能力。 与其他动态序列容器(deques,lists和 forward\_lists )相比,vector非常有效地访问其元素(就像数组一样),并相对有效地从元素末尾添加或移除元素。对于涉及插入或移除除了结尾之外的位置的元素的操作,它们执行比其他位置更差的操作,并且具有比列表和 forward\_lists 更不一致的迭代器和引用。 针对 vector 的各种常见操作的复杂度(效率)如下: * 随机访问 - 常数 O(1) * 在尾部增删元素 - 平摊(amortized)常数 O(1)}} * 增删元素 - 至 vector 尾部的线性距离 O(n)}} ```cpp template < class T, class Alloc = allocator > class vector; ``` ![](http://img.blog.csdn.net/20160406151211233) #### vector::vector (1)empty容器构造函数(默认构造函数) 构造一个空的容器,没有元素。 (2)fill构造函数 用n个元素构造一个容器。每个元素都是val的副本(如果提供)。 (3)范围(range)构造器 使用与[ range,first,last]范围内的元素相同的顺序构造一个容器,其中的每个元素都是emplace -从该范围内相应的元素构造而成。 (4)复制(copy)构造函数(并用分配器复制) 按照相同的顺序构造一个包含x中每个元素的副本的容器。 (5)移动(move)构造函数(和分配器移动) 构造一个获取x元素的容器。 如果指定了alloc并且与x的分配器不同,那么元素将被移动。否则,没有构建元素(他们的所有权直接转移)。 x保持未指定但有效的状态。 (6)初始化列表构造函数 构造一个容器中的每个元件中的一个拷贝的IL,以相同的顺序。 ```cpp default (1) explicit vector (const allocator_type& alloc = allocator_type()); fill (2) explicit vector (size_type n); vector (size_type n, const value_type& val, const allocator_type& alloc = allocator_type()); range (3) template vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()); copy (4) vector (const vector& x); vector (const vector& x, const allocator_type& alloc); move (5) vector (vector&& x); vector (vector&& x, const allocator_type& alloc); initializer list (6) vector (initializer_list il, const allocator_type& alloc = allocator_type()); ``` Example ```cpp #include #include int main () { // constructors used in the same order as described above: std::vector first; // empty vector of ints std::vector second(4, 100); // four ints with value 100 std::vector third(second.begin(), second.end());// iterating through second std::vector fourth(third); // a copy of third // the iterator constructor can also be used to construct from arrays: int myints[] = {16,2,77,29}; std::vector fifth(myints, myints + sizeof(myints) / sizeof(int)); std::cout << "The contents of fifth are:"; for(std::vector::iterator it = fifth.begin(); it != fifth.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; } ``` Output ``` The contents of fifth are: 16 2 77 29 ``` #### vector::~vector 销毁容器对象。这将在每个包含的元素上调用allocator_traits::destroy,并使用其分配器释放由矢量分配的所有存储容量。 ```cpp ~vector(); ``` #### vector::operator= 将新内容分配给容器,替换其当前内容,并相应地修改其大小。 ```cpp copy (1) vector& operator= (const vector& x); move (2) vector& operator= (vector&& x); initializer list (3) vector& operator= (initializer_list il); ``` Example ``` #include #include int main () { std::vector foo (3,0); std::vector bar (5,0); bar = foo; foo = std::vector(); std::cout << "Size of foo: " << int(foo.size()) << '\n'; std::cout << "Size of bar: " << int(bar.size()) << '\n'; return 0; } ``` Output ``` Size of foo: 0 Size of bar: 3 ``` #### vector::begin #### vector::end #### vector::rbegin #### vector::rend #### vector::cbegin #### vector::cend #### vector::rcbegin #### vector::rcend #### vector::size 返回vector中元素的数量。 这是vector中保存的实际对象的数量,不一定等于其存储容量。 ```cpp size_type size() const noexcept; ``` Example ```cpp #include #include int main () { std::vector myints; std::cout << "0. size: " << myints.size() << '\n'; for (int i=0; i<10; i++) myints.push_back(i); std::cout << "1. size: " << myints.size() << '\n'; myints.insert (myints.end(),10,100); std::cout << "2. size: " << myints.size() << '\n'; myints.pop_back(); std::cout << "3. size: " << myints.size() << '\n'; return 0; } ``` Output ``` 0. size: 0 1. size: 10 2. size: 20 3. size: 19 ``` #### vector::max_size 返回该vector可容纳的元素的最大数量。由于已知的系统或库实现限制, 这是容器可以达到的最大潜在大小,但容器无法保证能够达到该大小:在达到该大小之前的任何时间,仍然无法分配存储。 ```cpp size_type max_size() const noexcept; ``` Example ```cpp #include #include int main () { std::vector myvector; // set some content in the vector: for (int i=0; i<100; i++) myvector.push_back(i); std::cout << "size: " << myvector.size() << "\n"; std::cout << "capacity: " << myvector.capacity() << "\n"; std::cout << "max_size: " << myvector.max_size() << "\n"; return 0; } ``` A possible output for this program could be: ``` size: 100 capacity: 128 max_size: 1073741823 ``` #### vector::resize 调整容器的大小,使其包含n个元素。 如果n小于当前的容器size,内容将被缩小到前n个元素,将其删除(并销毁它们)。 如果n大于当前容器size,则通过在末尾插入尽可能多的元素以达到大小n来扩展内容。如果指定了val,则新元素将初始化为val的副本,否则将进行值初始化。 如果n也大于当前的容器的capacity(容量),分配的存储空间将自动重新分配。 注意这个函数通过插入或者删除元素的内容来改变容器的实际内容。 ```cpp void resize (size_type n); void resize (size_type n, const value_type& val); ``` Example ```cpp #include #include int main () { std::vector myvector; // set some initial content: for (int i=1;i<10;i++) myvector.push_back(i); myvector.resize(5); myvector.resize(8,100); myvector.resize(12); std::cout << "myvector contains:"; for (int i=0;i #include int main () { std::vector myvector; // set some content in the vector: for (int i=0; i<100; i++) myvector.push_back(i); std::cout << "size: " << (int) myvector.size() << '\n'; std::cout << "capacity: " << (int) myvector.capacity() << '\n'; std::cout << "max_size: " << (int) myvector.max_size() << '\n'; return 0; } ``` A possible output for this program could be: ``` size: 100 capacity: 128 max_size: 1073741823 ``` #### vector::empty 返回vector是否为空(即,它的size是否为0) ```cpp bool empty() const noexcept; ``` Example ```cpp #include #include int main () { std::vector myvector; int sum (0); for (int i=1;i<=10;i++) myvector.push_back(i); while (!myvector.empty()) { sum += myvector.back(); myvector.pop_back(); } std::cout << "total: " << sum << '\n'; return 0; } ``` Output ``` total: 55 ``` #### vector::reserve 请求vector容量至少足以包含n个元素。 如果n大于当前vector容量,则该函数使容器重新分配其存储容量,从而将其容量增加到n(或更大)。 在所有其他情况下,函数调用不会导致重新分配,并且vector容量不受影响。 这个函数对vector大小没有影响,也不能改变它的元素。 ```cpp void reserve (size_type n); ``` Example ```cpp #include #include int main () { std::vector::size_type sz; std::vector foo; sz = foo.capacity(); std::cout << "making foo grow:\n"; for (int i=0; i<100; ++i) { foo.push_back(i); if (sz!=foo.capacity()) { sz = foo.capacity(); std::cout << "capacity changed: " << sz << '\n'; } } std::vector bar; sz = bar.capacity(); bar.reserve(100); // this is the only difference with foo above std::cout << "making bar grow:\n"; for (int i=0; i<100; ++i) { bar.push_back(i); if (sz!=bar.capacity()) { sz = bar.capacity(); std::cout << "capacity changed: " << sz << '\n'; } } return 0; } ``` Possible output ``` making foo grow: capacity changed: 1 capacity changed: 2 capacity changed: 4 capacity changed: 8 capacity changed: 16 capacity changed: 32 capacity changed: 64 capacity changed: 128 making bar grow: capacity changed: 100 ``` #### vector::shrink_to_fit 要求容器减小其capacity(容量)以适应其尺寸。 该请求是非绑定的,并且容器实现可以自由地进行优化,并且保持capacity大于其size的vector。 这可能导致重新分配,但对矢量大小没有影响,并且不能改变其元素。 ```cpp void shrink_to_fit(); ``` Example ```cpp #include #include int main () { std::vector myvector (100); std::cout << "1. capacity of myvector: " << myvector.capacity() << '\n'; myvector.resize(10); std::cout << "2. capacity of myvector: " << myvector.capacity() << '\n'; myvector.shrink_to_fit(); std::cout << "3. capacity of myvector: " << myvector.capacity() << '\n'; return 0; } ``` Possible output ``` 1. capacity of myvector: 100 2. capacity of myvector: 100 3. capacity of myvector: 10 ``` #### vector::operator[] #### vector::at #### vector::front #### vector::back #### vector::data #### vector::assign 将新内容分配给vector,替换其当前内容,并相应地修改其大小。 在范围版本(1)中,新内容是从第一个和最后一个范围内的每个元素按相同顺序构造的元素。 在填充版本(2)中,新内容是n个元素,每个元素都被初始化为一个val的副本。 在初始化列表版本(3)中,新内容是以相同顺序作为初始化列表传递的值的副本。 所述内部分配器被用于(通过其性状),以分配和解除分配存储器如果重新分配发生。它也习惯于摧毁所有现有的元素,并构建新的元素。 ```cpp range (1) template void assign (InputIterator first, InputIterator last); fill (2) void assign (size_type n, const value_type& val); initializer list (3) void assign (initializer_list il); ``` Example ```cpp #include #include int main () { std::vector first; std::vector second; std::vector third; first.assign (7,100); // 7 ints with a value of 100 std::vector::iterator it; it=first.begin()+1; second.assign (it,first.end()-1); // the 5 central values of first int myints[] = {1776,7,4}; third.assign (myints,myints+3); // assigning from array. std::cout << "Size of first: " << int (first.size()) << '\n'; std::cout << "Size of second: " << int (second.size()) << '\n'; std::cout << "Size of third: " << int (third.size()) << '\n'; return 0; } ``` Output ``` Size of first: 7 Size of second: 5 Size of third: 3 ``` 补充:vector::assign 与 vector::operator= 的区别: 1. vector::assign 实现源码 ```cpp void assign(size_type __n, const _Tp& __val) { _M_fill_assign(__n, __val); } template void vector<_Tp, _Alloc>::_M_fill_assign(size_t __n, const value_type& __val) { if (__n > capacity()) { vector<_Tp, _Alloc> __tmp(__n, __val, get_allocator()); __tmp.swap(*this); } else if (__n > size()) { fill(begin(), end(), __val); _M_finish = uninitialized_fill_n(_M_finish, __n - size(), __val); } else erase(fill_n(begin(), __n, __val), end()); } ``` 2. vector::operator= 实现源码 ```cpp template vector<_Tp,_Alloc>& vector<_Tp,_Alloc>::operator=(const vector<_Tp, _Alloc>& __x) { if (&__x != this) { const size_type __xlen = __x.size(); if (__xlen > capacity()) { iterator __tmp = _M_allocate_and_copy(__xlen, __x.begin(), __x.end()); destroy(_M_start, _M_finish); _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = __tmp; _M_end_of_storage = _M_start + __xlen; } else if (size() >= __xlen) { iterator __i = copy(__x.begin(), __x.end(), begin()); destroy(__i, _M_finish); } else { copy(__x.begin(), __x.begin() + size(), _M_start); uninitialized_copy(__x.begin() + size(), __x.end(), _M_finish); } _M_finish = _M_start + __xlen; } return *this; } ``` #### vector::push_back 在vector的最后一个元素之后添加一个新元素。val的内容被复制(或移动)到新的元素。 这有效地将容器size增加了一个,如果新的矢量size超过了当前vector的capacity,则导致所分配的存储空间自动重新分配。 ```cpp void push_back (const value_type& val); void push_back (value_type&& val); ``` Example ```cpp #include #include int main () { std::vector myvector; int myint; std::cout << "Please enter some integers (enter 0 to end):\n"; do { std::cin >> myint; myvector.push_back (myint); } while (myint); std::cout << "myvector stores " << int(myvector.size()) << " numbers.\n"; return 0; } ``` #### vector::pop_back 删除vector中的最后一个元素,有效地将容器size减少一个。 这破坏了被删除的元素。 ```cpp void pop_back(); ``` Example ```cpp #include #include int main () { std::vector myvector; int sum (0); myvector.push_back (100); myvector.push_back (200); myvector.push_back (300); while (!myvector.empty()) { sum+=myvector.back(); myvector.pop_back(); } std::cout << "The elements of myvector add up to " << sum << '\n'; return 0; } ``` Output ``` The elements of myvector add up to 600 ``` #### vector::insert 通过在指定位置的元素之前插入新元素来扩展该vector,通过插入元素的数量有效地增加容器大小。 这会导致分配的存储空间自动重新分配,只有在新的vector的size超过当前的vector的capacity的情况下。 由于vector使用数组作为其基础存储,因此除了将元素插入到vector末尾之后,或vector的begin之前,其他位置会导致容器重新定位位置之后的所有元素到他们的新位置。与其他种类的序列容器(例如list或forward_list)执行相同操作的操作相比,这通常是低效的操作。 ```cpp single element (1) iterator insert (const_iterator position, const value_type& val); fill (2) iterator insert (const_iterator position, size_type n, const value_type& val); range (3) template iterator insert (const_iterator position, InputIterator first, InputIterator last); move (4) iterator insert (const_iterator position, value_type&& val); initializer list (5) iterator insert (const_iterator position, initializer_list il); ``` Example ```cpp #include #include int main () { std::vector myvector (3,100); std::vector::iterator it; it = myvector.begin(); it = myvector.insert ( it , 200 ); myvector.insert (it,2,300); // "it" no longer valid, get a new one: it = myvector.begin(); std::vector anothervector (2,400); myvector.insert (it+2,anothervector.begin(),anothervector.end()); int myarray [] = { 501,502,503 }; myvector.insert (myvector.begin(), myarray, myarray+3); std::cout << "myvector contains:"; for (it=myvector.begin(); it v(5, 0); std::vector::iterator vi; // 获取vector第一个元素的迭代器 vi = v.begin(); // push_back 插入元素之后可能会因为 push_back 的骚操作(创建一个新vector把旧vector的值复制到新vector),导致vector迭代器iterator的指针变成野指针,而导致insert出错 v.push_back(10); v.insert(vi, 2, 300); return 0; } ``` 改正:应该把`vi = v.begin();`放到`v.push_back(10);`后面 #### vector::erase 从vector中删除单个元素(position)或一系列元素([first,last))。 这有效地减少了被去除的元素的数量,从而破坏了容器的大小。 由于vector使用一个数组作为其底层存储,所以删除除vector结束位置之后,或vector的begin之前的元素外,将导致容器将段被擦除后的所有元素重新定位到新的位置。与其他种类的序列容器(例如list或forward_list)执行相同操作的操作相比,这通常是低效的操作。 ```cpp iterator erase (const_iterator position); iterator erase (const_iterator first, const_iterator last); ``` Example ```cpp #include #include int main () { std::vector myvector; // set some values (from 1 to 10) for (int i=1; i<=10; i++) myvector.push_back(i); // erase the 6th element myvector.erase (myvector.begin()+5); // erase the first 3 elements: myvector.erase (myvector.begin(),myvector.begin()+3); std::cout << "myvector contains:"; for (unsigned i=0; i #include int main () { std::vector foo (3,100); // three ints with a value of 100 std::vector bar (5,200); // five ints with a value of 200 foo.swap(bar); std::cout << "foo contains:"; for (unsigned i=0; i().swap(x); // clear x reallocating ` ```cpp void clear() noexcept; ``` Example ```cpp #include #include void printVector(const std::vector &v) { for (auto it = v.begin(); it != v.end(); ++it) { std::cout << *it << ' '; } std::cout << std::endl; } int main() { std::vector v1(5, 50); printVector(v1); std::cout << "v1 size = " << v1.size() << std::endl; std::cout << "v1 capacity = " << v1.capacity() << std::endl; v1.clear(); printVector(v1); std::cout << "v1 size = " << v1.size() << std::endl; std::cout << "v1 capacity = " << v1.capacity() << std::endl; v1.push_back(11); v1.push_back(22); printVector(v1); std::cout << "v1 size = " << v1.size() << std::endl; std::cout << "v1 capacity = " << v1.capacity() << std::endl; return 0; } ``` Output ``` 50 50 50 50 50 v1 size = 5 v1 capacity = 5 v1 size = 0 v1 capacity = 5 11 22 v1 size = 2 v1 capacity = 5 ``` #### vector::emplace 通过在position位置处插入新元素args来扩展容器。这个新元素是用args作为构建的参数来构建的。 这有效地增加了一个容器的大小。 分配存储空间的自动重新分配发生在新的vector的size超过当前向量容量的情况下。 由于vector使用数组作为其基础存储,因此除了将元素插入到vector末尾之后,或vector的begin之前,其他位置会导致容器重新定位位置之后的所有元素到他们的新位置。与其他种类的序列容器(例如list或forward_list)执行相同操作的操作相比,这通常是低效的操作。 该元素是通过调用allocator_traits::construct来转换args来创建的。插入一个类似的成员函数,将现有对象复制或移动到容器中。 ```cpp template iterator emplace (const_iterator position, Args&&... args); ``` Example ```cpp #include #include int main () { std::vector myvector = {10,20,30}; auto it = myvector.emplace ( myvector.begin()+1, 100 ); myvector.emplace ( it, 200 ); myvector.emplace ( myvector.end(), 300 ); std::cout << "myvector contains:"; for (auto& x: myvector) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` myvector contains: 10 200 100 20 30 300 ``` #### vector::emplace_back 在vector的末尾插入一个新的元素,紧跟在当前的最后一个元素之后。这个新元素是用args作为构造函数的参数来构造的。 这有效地将容器大小增加了一个,如果新的矢量大小超过了当前的vector容量,则导致所分配的存储空间自动重新分配。 该元素是通过调用allocator_traits :: construct来转换args来创建的。 与push\_back相比,emplace\_back可以避免额外的复制和移动操作。 ```cpp template void emplace_back (Args&&... args); ``` Example ```cpp #include #include #include struct President { std::string name; std::string country; int year; President(std::string p_name, std::string p_country, int p_year) : name(std::move(p_name)), country(std::move(p_country)), year(p_year) { std::cout << "I am being constructed.\n"; } President(President&& other) : name(std::move(other.name)), country(std::move(other.country)), year(other.year) { std::cout << "I am being moved.\n"; } President& operator=(const President& other) = default; }; int main() { std::vector elections; std::cout << "emplace_back:\n"; elections.emplace_back("Nelson Mandela", "South Africa", 1994); std::vector reElections; std::cout << "\npush_back:\n"; reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936)); std::cout << "\nContents:\n"; for (President const& president: elections) { std::cout << president.name << " was elected president of " << president.country << " in " << president.year << ".\n"; } for (President const& president: reElections) { std::cout << president.name << " was re-elected president of " << president.country << " in " << president.year << ".\n"; } } ``` Output ``` emplace_back: I am being constructed. push_back: I am being constructed. I am being moved. Contents: Nelson Mandela was elected president of South Africa in 1994. Franklin Delano Roosevelt was re-elected president of the USA in 1936. ``` #### vector::get_allocator 返回与vector关联的构造器对象的副本。 ```cpp allocator_type get_allocator() const noexcept; ``` Example ```cpp #include #include int main () { std::vector myvector; int * p; unsigned int i; // allocate an array with space for 5 elements using vector's allocator: p = myvector.get_allocator().allocate(5); // construct values in-place on the array: for (i=0; i<5; i++) myvector.get_allocator().construct(&p[i],i); std::cout << "The allocated array contains:"; for (i=0; i<5; i++) std::cout << ' ' << p[i]; std::cout << '\n'; // destroy and deallocate: for (i=0; i<5; i++) myvector.get_allocator().destroy(&p[i]); myvector.get_allocator().deallocate(p,5); return 0; } ``` Output ``` The allocated array contains: 0 1 2 3 4 ``` 注意:deallocate和destory的关系: deallocate实现的源码: template inline void _deallocate(T* buffer) { ::operator delete(buffer); //为什么不用 delete [] ? ,operator delete 区别于 delete //operator delete 是一个底层操作符 } destory: template inline void _destory(T *ptr) { ptr->~T(); } destory负责调用类型的析构函数,销毁相应内存上的内容(但销毁后内存地址仍保留) deallocate负责释放内存(此时相应内存中的值在此之前应调用destory销毁,将内存地址返回给系统,代表这部分地址使用引用-1) #### relational operators (vector) #### swap (vector) #### vector ### deque deque(['dek])(双端队列)是double-ended queue 的一个不规则缩写。deque是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。 特定的库可以以不同的方式实现deques,通常作为某种形式的动态数组。但是在任何情况下,它们都允许通过随机访问迭代器直接访问各个元素,通过根据需要扩展和收缩容器来自动处理存储。 因此,它们提供了类似于vector的功能,但是在序列的开始部分也可以高效地插入和删除元素,而不仅仅是在结尾。但是,与vector不同,deques并不保证将其所有元素存储在连续的存储位置:deque通过偏移指向另一个元素的指针访问元素会导致未定义的行为。 两个vector和deques提供了一个非常相似的接口,可以用于类似的目的,但内部工作方式完全不同:虽然vector使用单个数组需要偶尔重新分配以增长,但是deque的元素可以分散在不同的块的容器,容器在内部保存必要的信息以提供对其任何元素的持续时间和统一的顺序接口(通过迭代器)的直接访问。因此,deques在内部比vector更复杂一点,但是这使得他们在某些情况下更有效地增长,尤其是在重新分配变得更加昂贵的很长序列的情况下。 对于频繁插入或删除开始或结束位置以外的元素的操作,deques表现得更差,并且与列表和转发列表相比,迭代器和引用的一致性更低。 deque上常见操作的复杂性(效率)如下: * 随机访问 - 常数O(1) * 在结尾或开头插入或移除元素 - 摊销不变O(1) * 插入或移除元素 - 线性O(n) ```cpp template < class T, class Alloc = allocator > class deque; ``` ![](http://img.blog.csdn.net/20170727225856144?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRlg2Nzc1ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ![](https://images0.cnblogs.com/blog/559453/201401/092150340824.png) #### deque::deque 构造一个deque容器对象,根据所使用的构造函数版本初始化它的内容: Example ```cpp #include #include int main () { unsigned int i; // constructors used in the same order as described above: std::deque first; // empty deque of ints std::deque second (4,100); // four ints with value 100 std::deque third (second.begin(),second.end()); // iterating through second std::deque fourth (third); // a copy of third // the iterator constructor can be used to copy arrays: int myints[] = {16,2,77,29}; std::deque fifth (myints, myints + sizeof(myints) / sizeof(int) ); std::cout << "The contents of fifth are:"; for (std::deque::iterator it = fifth.begin(); it!=fifth.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; } ``` Output ``` The contents of fifth are: 16 2 77 29 ``` #### deque::push_back 在当前的最后一个元素之后 ,在deque容器的末尾添加一个新元素。val的内容被复制(或移动)到新的元素。 这有效地增加了一个容器的大小。 ```cpp void push_back (const value_type& val); void push_back (value_type&& val); ``` Example ```cpp #include #include int main () { std::deque mydeque; int myint; std::cout << "Please enter some integers (enter 0 to end):\n"; do { std::cin >> myint; mydeque.push_back (myint); } while (myint); std::cout << "mydeque stores " << (int) mydeque.size() << " numbers.\n"; return 0; } ``` #### deque::push_front 在deque容器的开始位置插入一个新的元素,位于当前的第一个元素之前。val的内容被复制(或移动)到插入的元素。 这有效地增加了一个容器的大小。 ```cpp void push_front (const value_type& val); void push_front (value_type&& val); ``` Example ```cpp #include #include int main () { std::deque mydeque (2,100); // two ints with a value of 100 mydeque.push_front (200); mydeque.push_front (300); std::cout << "mydeque contains:"; for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; } ``` Output ``` 300 200 100 100 ``` #### deque::pop_back 删除deque容器中的最后一个元素,有效地将容器大小减少一个。 这破坏了被删除的元素。 ```cpp void pop_back(); ``` Example ```cpp #include #include int main () { std::deque mydeque; int sum (0); mydeque.push_back (10); mydeque.push_back (20); mydeque.push_back (30); while (!mydeque.empty()) { sum+=mydeque.back(); mydeque.pop_back(); } std::cout << "The elements of mydeque add up to " << sum << '\n'; return 0; } ``` Output ``` The elements of mydeque add up to 60 ``` #### deque::pop_front 删除deque容器中的第一个元素,有效地减小其大小。 这破坏了被删除的元素。 ```cpp void pop_front(); ``` Example ```cpp #include #include int main () { std::deque mydeque; mydeque.push_back (100); mydeque.push_back (200); mydeque.push_back (300); std::cout << "Popping out the elements in mydeque:"; while (!mydeque.empty()) { std::cout << ' ' << mydeque.front(); mydeque.pop_front(); } std::cout << "\nThe final size of mydeque is " << int(mydeque.size()) << '\n'; return 0; } ``` Output ``` Popping out the elements in mydeque: 100 200 300 The final size of mydeque is 0 ``` #### deque::emplace_front 在deque的开头插入一个新的元素,就在其当前的第一个元素之前。这个新的元素是用args作为构建的参数来构建的。 这有效地增加了一个容器的大小。 该元素是通过调用allocator_traits::construct来转换args来创建的。 存在一个类似的成员函数push_front,它可以将现有对象复制或移动到容器中。 ```cpp template void emplace_front (Args&&... args); ``` Example ```cpp #include #include int main () { std::deque mydeque = {10,20,30}; mydeque.emplace_front (111); mydeque.emplace_front (222); std::cout << "mydeque contains:"; for (auto& x: mydeque) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` mydeque contains: 222 111 10 20 30 ``` #### deque::emplace_back 在deque的末尾插入一个新的元素,紧跟在当前的最后一个元素之后。这个新的元素是用args作为构建的参数来构建的。 这有效地增加了一个容器的大小。 该元素是通过调用allocator_traits::construct来转换args来创建的。 存在一个类似的成员函数push_back,它可以将现有对象复制或移动到容器中 ```cpp template void emplace_back (Args&&... args); ``` Example ```cpp #include #include int main () { std::deque mydeque = {10,20,30}; mydeque.emplace_back (100); mydeque.emplace_back (200); std::cout << "mydeque contains:"; for (auto& x: mydeque) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` mydeque contains: 10 20 30 100 200 ``` ### forward\_list forward_list(单向链表)是序列容器,允许在序列中的任何地方进行恒定的时间插入和擦除操作。 forward\_list(单向链表)被实现为单链表; 单链表可以将它们包含的每个元素存储在不同和不相关的存储位置中。通过关联到序列中下一个元素的链接的每个元素来保留排序。forward\_list容器和列表 之间的主要设计区别容器是第一个内部只保留一个到下一个元素的链接,而后者每个元素保留两个链接:一个指向下一个元素,一个指向前一个元素,允许在两个方向上有效的迭代,但是每个元素消耗额外的存储空间并且插入和移除元件的时间开销略高。因此,forward_list对象比列表对象更有效率,尽管它们只能向前迭代。 与其他基本的标准序列容器(array,vector和deque),forward_list通常在插入,提取和移动容器内任何位置的元素方面效果更好,因此也适用于密集使用这些元素的算法,如排序算法。 的主要缺点修饰符Modifiers S和列表相比这些其它序列容器s是说,他们缺乏可以通过位置的元素的直接访问; 例如,要访问forward_list中的第六个元素,必须从开始位置迭代到该位置,这需要在这些位置之间的线性时间。它们还消耗一些额外的内存来保持与每个元素相关联的链接信息(这可能是大型小元素列表的重要因素)。 该修饰符Modifiersclass模板的设计考虑到效率:按照设计,它与简单的手写C型单链表一样高效,实际上是唯一的标准容器,为了效率的考虑故意缺少尺寸成员函数:由于其性质作为一个链表,具有一个需要一定时间的大小的成员将需要它保持一个内部计数器的大小(如列表所示)。这会消耗一些额外的存储空间,并使插入和删除操作效率稍低。要获取forward_list对象的大小,可以使用距离算法的开始和结束,这是一个需要线性时间的操作。 ![](http://img.blog.csdn.net/20160407212133266) #### forward\_list::forward\_list ```cpp default (1) explicit forward_list (const allocator_type& alloc = allocator_type()); fill (2) explicit forward_list (size_type n); explicit forward_list (size_type n, const value_type& val, const allocator_type& alloc = allocator_type()); range (3) template forward_list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()); copy (4) forward_list (const forward_list& fwdlst); forward_list (const forward_list& fwdlst, const allocator_type& alloc); move (5) forward_list (forward_list&& fwdlst); forward_list (forward_list&& fwdlst, const allocator_type& alloc); initializer list (6) forward_list (initializer_list il, const allocator_type& alloc = allocator_type()); ``` Example ```cpp #include #include int main () { // constructors used in the same order as described above: std::forward_list first; // default: empty std::forward_list second (3,77); // fill: 3 seventy-sevens std::forward_list third (second.begin(), second.end()); // range initialization std::forward_list fourth (third); // copy constructor std::forward_list fifth (std::move(fourth)); // move ctor. (fourth wasted) std::forward_list sixth = {3, 52, 25, 90}; // initializer_list constructor std::cout << "first:" ; for (int& x: first) std::cout << " " << x; std::cout << '\n'; std::cout << "second:"; for (int& x: second) std::cout << " " << x; std::cout << '\n'; std::cout << "third:"; for (int& x: third) std::cout << " " << x; std::cout << '\n'; std::cout << "fourth:"; for (int& x: fourth) std::cout << " " << x; std::cout << '\n'; std::cout << "fifth:"; for (int& x: fifth) std::cout << " " << x; std::cout << '\n'; std::cout << "sixth:"; for (int& x: sixth) std::cout << " " << x; std::cout << '\n'; return 0; } ``` Possible output ``` forward_list constructor examples: first: second: 77 77 77 third: 77 77 77 fourth: fifth: 77 77 77 sixth: 3 52 25 90 ``` #### forward\_list::~forward\_list #### forward\_list::before\_begin 返回指向容器中第一个元素之前的位置的迭代器。 返回的迭代器不应被解除引用:它是为了用作成员函数的参数emplace\_after,insert\_after,erase\_after或splice\_after,指定序列,其中执行该动作的位置的开始位置。 ```cpp iterator before_begin() noexcept; const_iterator before_begin() const noexcept; ``` Example ```cpp #include #include int main () { std::forward_list mylist = {20, 30, 40, 50}; mylist.insert_after ( mylist.before_begin(), 11 ); std::cout << "mylist contains:"; for ( int& x: mylist ) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` mylist contains: 11 20 30 40 50 ``` #### forward\_list::cbefore\_begin 返回指向容器中第一个元素之前的位置的const_iterator。 一个常量性是指向常量内容的迭代器。这个迭代器可以增加和减少(除非它本身也是const),就像forward\_list::before\_begin返回的迭代器一样,但不能用来修改它指向的内容。 返回的价值不得解除引用。 ```cpp const_iterator cbefore_begin() const noexcept; ``` Example ```cpp #include #include int main () { std::forward_list mylist = {77, 2, 16}; mylist.insert_after ( mylist.cbefore_begin(), 19 ); std::cout << "mylist contains:"; for ( int& x: mylist ) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ``` mylist contains: 19 77 2 16 ``` ### list list是一个双向链表容器,它存储元素的顺序由元素被插入的顺序决定,而不是由元素的值决定。list 不对元素进行排序,也不提供基于键的快速访问。 #### list::front,list::back 返回第一个与最后一个元素的引用,如果容器为空,则调用该函数会导致未定义行为。 ```cpp reference front(); const_reference front() const; reference back(); const_reference back() const; ``` Example ```cpp #include #include int main() { std::list mylist = {20, 30, 40, 50}; mylist.front() = 10; mylist.back() = 99; std::cout << "mylist contains:"; for (auto& x : mylist) std::cout << ' ' << x; std::cout << '\n'; return 0; } ``` Output ```cpp mylist contains: 10 30 40 99 ``` #### list::unique 移除容器中所有连续重复的元素,只保留一个。如果提供了比较函数,则使用该函数来判断元素是否相等。否则使用默认的比较函数operator==。 ```cpp template void unique(BinaryPredicate pred); ``` Example ```cpp #include #include int main() { std::list mylist = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4}; mylist.unique(); std::cout << "mylist contains:"; for (int& x : mylist) std::cout << ' ' << x; return 0; } ``` Output ```cpp mylist contains: 1 2 3 4 ``` #### list::merge 将两个已排序的list合并为一个有序的list。如果this和other中的元素都按非递减顺序排序,则merge会将other中的所有元素合并到this中,合并后的this仍然保持非递减顺序,other清空元素。如果this和other指向同一个list,则行为未定义。 ```cpp template void merge(std::list& other, Compare comp); ``` Example ```cpp #include #include int main() { std::list first = {1, 3, 5, 7}; std::list second = {2, 4, 6, 8}; first.merge(second); std::cout << "first contains:"; for (int& x : first) std::cout << ' ' << x; std::cout << '\n'; std::cout << "second contains:"; for (int& x : second) std::cout << ' ' << x; return 0; } ``` Output ```cpp first contains: 1 2 3 4 5 6 7 8 second contains: ``` #### list::splice 移除other中指定区间的元素,并插入到this中。 ```cpp void splice(iterator it, list& x, iterator first); ``` Example ```cpp #include #include int main() { std::list list1 = {1, 3, 5, 7}; std::list list2 = {2, 4, 6, 8}; auto it = list1.begin(); std::advance(it, 2);//Not support + for iterator auto first = list2.begin(); std::advance(first, 1); list1.splice(it, list2, first); for (int value : list1) { std::cout << value << " "; } std::cout << std::endl; for (int value : list2) { std::cout << value << " "; } std::cout << std::endl; return 0; } ``` Output: ```cpp 1 3 4 5 7 2 6 8 ``` #### list::reverse 反转容器中的元素。 ```cpp void reverse() noexcept; ``` Example: ```cpp #include #include int main() { std::list mylist = {1, 2, 3, 4, 5}; mylist.reverse(); std::cout << "mylist contains:"; for (int& x : mylist) std::cout << ' ' << x; return 0; } ``` Output: ```cpp mylist contains:1 2 3 4 5 ``` ### stack 后进先出(LIFO),基于底层容器(deque或list或vector)实现。 #### stack::emplace 在栈顶构造一个新元素,减少了不必要的对象构造和销毁. ```cpp template void emplace(Args&&... args); ``` Example: ```cpp #include #include int main() { std::stack mystack; mystack.emplace(10); mystack.emplace(20); std::cout << "mystack contains:"; while (!mystack.empty()) { std::cout << ' ' << mystack.top(); mystack.pop(); } return 0; } ``` Output: ```cpp mystack contains: 20 10 ``` #### stack::push_range(c++23) 将范围中的每个元素插入到栈的末尾 ```cpp template void push_range(InputIterator first, InputIterator last); ``` Example: ```cpp #include #include #include int main() { std::stack st; std::vector vec = {1, 2, 3, 4}; st.push_range(vec); while (!st.empty()) { std::cout << st.top() <<" "; st.pop(); } } ``` Output: ```cpp 4 3 2 1 ``` ### queue 先进先出(FIFO),基于底层容器(deque或list)实现。 ### priority_queue 提供了一种基于优先级的队列,通常基于堆实现,默认使用最大堆。 #### lambda自定义函数比较 ```cpp #include #include #include int main() { std::priority_queue maxHeap; maxHeap.push(10); maxHeap.push(20); maxHeap.push(15); std::cout << "Top element (max): " << maxHeap.top() << std::endl; maxHeap.pop(); std::cout << "Top element after pop: " << maxHeap.top()< b; };//lambda std::priority_queue, decltype(cmp)> minHeap(cmp); minHeap.push(10); minHeap.push(20); minHeap.push(15); std::cout << "Top element (min): " << minHeap.top() << std::endl; return 0; } ``` Output: ```cpp Top element (max): 20 Top element after pop: 15 Top element (min): 10 ``` ### set 存储唯一的元素,并且这些元素会自动按照特定的顺序排列. #### set::upper_bound 返回指向第一个大于指定值的元素的迭代器。 ```cpp template iterator upper_bound(const K& x); template const_iterator upper_bound(const K& x) const; ``` Example: ```cpp #include #include #include int main() { std::set mySet = {"apple", "banana", "cherry", "date"}; auto it = mySet.upper_bound("banana"); if (it != mySet.end()) { std::cout << "The first element greater than 'banana' is: " << *it; } else { std::cout << "No element greater than 'banana' found."; } return 0; } ``` Output: ```cpp The first element greater than 'banana' is: cherry ``` #### set::contains 判断元素是否存在。 ```cpp bool contains(const key_type& key) const; ``` Example: ```cpp #include #include int main() { std::set mySet = {1, 2, 3, 4, 5}; int valueToFind = 3; if (mySet.contains(valueToFind)) { std::cout << valueToFind << " is in the set."; } else { std::cout << valueToFind << " is not in the set."; } return 0; } ``` Output: ```cpp 3 is in the set. ``` #### set::value_comp 获取一个函数对象,用于比较两个键值。 ```cpp key_compare value_comp() const; ``` Example: ```cpp #include #include int main() { std::set mySet = {5, 3, 9, 1, 7}; auto comp = mySet.value_comp(); int a = 3; int b = 5; if (comp(a, b)) { std::cout << a << " is less than " << b << " according to the set's comparison function."; } else { std::cout << a << " is not less than " << b << " according to the set's comparison function."; } return 0; } ``` Output: ```cpp 3 is less than 5 according to the set's comparison function. ``` #### set::extract 从set中提取指定的元素节点,并返回一个节点句柄。 ```cpp template node_type extract(K&& x); ``` Example: ```cpp #include #include int main() { std::set mySet = {1, 2, 3, 4, 5}; auto nh = mySet.extract(3); if (!nh.empty()) { std::cout << "Extracted element: " << nh.value(); } else { std::cout << "Element not found."; } std::cout< #include int main() { std::multiset ms = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4}; auto range = ms.equal_range(3); for (auto it = range.first; it != range.second; ++it) { std::cout << *it << " "; } return 0; } ``` Output: ```cpp 3 3 3 ``` #### multset::find 返回一个迭代器,指向给定键的元素。如果找不到给定键,则返回一个指向 multiset 的末尾的迭代器。 Example: ```cpp #include #include int main() { std::multiset myMultiset = {1, 2, 2, 3, 4, 4, 4}; auto it = myMultiset.find(2); if (it != myMultiset.end()) { std::cout << "Found element with value " << *it << std::endl; } else { std::cout << "Element not found" << std::endl; } return 0; } ``` Output: ```cpp Found element with value 2 ``` #### multiset::emplace_hint 通过提供一个位置,帮助优化插入操作的性能。 ```cpp template iterator emplace_hint(const_iterator hint, Args&&... args); ``` Example: ```cpp #include #include int main() { std::multiset myMultiset{1, 2, 4, 5}; auto it = myMultiset.find(2); myMultiset.emplace_hint(it, 3);//Given position of 2 for (const auto& elem : myMultiset) { std::cout << elem << " "; } return 0; } ``` Output: ```cpp 1 2 3 4 5 ``` ### map map 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。 在映射中,键值通常用于对元素进行排序和唯一标识,而映射的值存储与此键关联的内容。该类型的键和映射的值可能不同,并且在部件类型被分组在一起VALUE_TYPE,这是一种对类型结合两种: ```cpp typedef pair value_type; ``` 在内部,映射中的元素总是按照由其内部比较对象(比较类型)指示的特定的严格弱排序标准按键排序。映射容器通常比unordered_map容器慢,以通过它们的键来访问各个元素,但是它们允许基于它们的顺序对子集进行直接迭代。 在该映射值地图可以直接通过使用其相应的键来访问括号运算符((操作符[] )。 映射通常如实施 ```cpp template < class Key, // map::key_type class T, // map::mapped_type class Compare = less, // map::key_compare class Alloc = allocator > // map::allocator_type > class map; ``` #### map::map 构造一个映射容器对象,根据所使用的构造器版本初始化其内容: (1)空容器构造函数(默认构造函数) 构造一个空的容器,没有元素。 (2)范围构造函数 构造具有一样多的元素的范围内的容器[第一,最后一个),其中每个元件布设构造的从在该范围内它的相应的元件。 (3)复制构造函数(并用分配器复制) 使用x中的每个元素的副本构造一个容器。 (4)移动构造函数(并与分配器一起移动) 构造一个获取x元素的容器。 如果指定了alloc并且与x的分配器不同,那么元素将被移动。否则,没有构建元素(他们的所有权直接转移)。 x保持未指定但有效的状态。 (5)初始化列表构造函数 用il中的每个元素的副本构造一个容器。 ```cpp empty (1) explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); explicit map (const allocator_type& alloc); range (2) template map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); copy (3) map (const map& x); map (const map& x, const allocator_type& alloc); move (4) map (map&& x); map (map&& x, const allocator_type& alloc); initializer list (5) map (initializer_list il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); ``` Example ```cpp #include #include bool fncomp (char lhs, char rhs) {return lhs first; first['a']=10; first['b']=30; first['c']=50; first['d']=70; std::map second (first.begin(),first.end()); std::map third (second); std::map fourth; // class as Compare bool(*fn_pt)(char,char) = fncomp; std::map fifth (fn_pt); // function pointer as Compare return 0; } ``` #### map::begin 返回引用map容器中第一个元素的迭代器。 由于map容器始终保持其元素的顺序,所以开始指向遵循容器排序标准的元素。 如果容器是空的,则返回的迭代器值不应被解除引用。 ```cpp iterator begin() noexcept; const_iterator begin() const noexcept; ``` Example ```cpp #include #include int main () { std::map mymap; mymap['b'] = 100; mymap['a'] = 200; mymap['c'] = 300; // show content: for (std::map::iterator it=mymap.begin(); it!=mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n'; return 0; } ``` Output ``` a => 200 b => 100 c => 300 ``` #### map::key_comp 返回容器用于比较键的比较对象的副本。 ```cpp key_compare key_comp() const; ``` Example ```cpp #include #include int main () { std::map mymap; std::map::key_compare mycomp = mymap.key_comp(); mymap['a']=100; mymap['b']=200; mymap['c']=300; std::cout << "mymap contains:\n"; char highest = mymap.rbegin()->first; // key value of last element std::map::iterator it = mymap.begin(); do { std::cout << it->first << " => " << it->second << '\n'; } while ( mycomp((*it++).first, highest) ); std::cout << '\n'; return 0; } ``` Output ``` mymap contains: a => 100 b => 200 c => 300 ``` #### map::value_comp 返回可用于比较两个元素的比较对象,以获取第一个元素的键是否在第二个元素之前。 ```cpp value_compare value_comp() const; ``` Example ```cpp #include #include int main () { std::map mymap; mymap['x']=1001; mymap['y']=2002; mymap['z']=3003; std::cout << "mymap contains:\n"; std::pair highest = *mymap.rbegin(); // last element std::map::iterator it = mymap.begin(); do { std::cout << it->first << " => " << it->second << '\n'; } while ( mymap.value_comp()(*it++, highest) ); return 0; } ``` Output ``` mymap contains: x => 1001 y => 2002 z => 3003 ``` #### map::find 在容器中搜索具有等于k的键的元素,如果找到则返回一个迭代器,否则返回map::end的迭代器。 如果容器的比较对象自反地返回假(即,不管元素作为参数传递的顺序),则两个key被认为是等同的。 另一个成员函数map::count可以用来检查一个特定的键是否存在。 ```cpp iterator find (const key_type& k); const_iterator find (const key_type& k) const; ``` Example ```cpp #include #include int main () { std::map mymap; std::map::iterator it; mymap['a']=50; mymap['b']=100; mymap['c']=150; mymap['d']=200; it = mymap.find('b'); if (it != mymap.end()) mymap.erase (it); // print content: std::cout << "elements in mymap:" << '\n'; std::cout << "a => " << mymap.find('a')->second << '\n'; std::cout << "c => " << mymap.find('c')->second << '\n'; std::cout << "d => " << mymap.find('d')->second << '\n'; return 0; } ``` Output ``` elements in mymap: a => 50 c => 150 d => 200 ``` #### map::count 在容器中搜索具有等于k的键的元素,并返回匹配的数量。 由于地图容器中的所有元素都是唯一的,因此该函数只能返回1(如果找到该元素)或返回零(否则)。 如果容器的比较对象自反地返回错误(即,不管按键作为参数传递的顺序),则两个键被认为是等同的。 ```cpp size_type count (const key_type& k) const; ``` Example ```cpp #include #include int main () { std::map mymap; char c; mymap ['a']=101; mymap ['c']=202; mymap ['f']=303; for (c='a'; c<'h'; c++) { std::cout << c; if (mymap.count(c)>0) std::cout << " is an element of mymap.\n"; else std::cout << " is not an element of mymap.\n"; } return 0; } ``` Output ``` a is an element of mymap. b is not an element of mymap. c is an element of mymap. d is not an element of mymap. e is not an element of mymap. f is an element of mymap. g is not an element of mymap. ``` #### map::lower_bound 将迭代器返回到下限 返回指向容器中第一个元素的迭代器,该元素的键不会在k之前出现(即,它是等价的或者在其后)。 该函数使用其内部比较对象(key\_comp)来确定这一点,将迭代器返回到key\_comp(element\_key,k)将返回false的第一个元素。 如果map类用默认的比较类型(less)实例化,则函数返回一个迭代器到第一个元素,其键不小于k。 一个类似的成员函数upper\_bound具有相同的行为lower\_bound,除非映射包含一个key值等于k的元素:在这种情况下,lower\_bound返回指向该元素的迭代器,而upper\_bound返回指向下一个元素的迭代器。 ```cpp iterator lower_bound (const key_type& k); const_iterator lower_bound (const key_type& k) const; ``` Example ```cpp #include #include int main () { std::map mymap; std::map::iterator itlow,itup; mymap['a']=20; mymap['b']=40; mymap['c']=60; mymap['d']=80; mymap['e']=100; itlow=mymap.lower_bound ('b'); // itlow points to b itup=mymap.upper_bound ('d'); // itup points to e (not d!) mymap.erase(itlow,itup); // erases [itlow,itup) // print content: for (std::map::iterator it=mymap.begin(); it!=mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n'; return 0; } ``` Output ``` a => 20 e => 100 ``` #### map::upper_bound 将迭代器返回到上限 返回一个指向容器中第一个元素的迭代器,它的关键字被认为是在k之后。 该函数使用其内部比较对象(key\_comp)来确定这一点,将迭代器返回到key\_comp(k,element\_key)将返回true的第一个元素。 如果map类用默认的比较类型(less)实例化,则函数返回一个迭代器到第一个元素,其键大于k。 类似的成员函数lower\_bound具有与upper\_bound相同的行为,除了map包含一个元素,其键值等于k:在这种情况下,lower\_bound返回指向该元素的迭代器,而upper\_bound返回指向下一个元素的迭代器。 ```cpp iterator upper_bound (const key_type& k); const_iterator upper_bound (const key_type& k) const; ``` Example ```cpp #include #include int main () { std::map mymap; std::map::iterator itlow,itup; mymap['a']=20; mymap['b']=40; mymap['c']=60; mymap['d']=80; mymap['e']=100; itlow=mymap.lower_bound ('b'); // itlow points to b itup=mymap.upper_bound ('d'); // itup points to e (not d!) mymap.erase(itlow,itup); // erases [itlow,itup) // print content: for (std::map::iterator it=mymap.begin(); it!=mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n'; return 0; } ``` Output ``` a => 20 e => 100 ``` #### map::equal_range 获取相同元素的范围 返回包含容器中所有具有与k等价的键的元素的范围边界。 由于地图容器中的元素具有唯一键,所以返回的范围最多只包含一个元素。 如果没有找到匹配,则返回的范围具有零的长度,与两个迭代器指向具有考虑去后一个密钥对所述第一元件ķ根据容器的内部比较对象(key\_comp)。如果容器的比较对象返回false,则两个键被认为是等价的。 ```cpp pair equal_range (const key_type& k) const; pair equal_range (const key_type& k); ``` Example ```cpp #include #include int main () { std::map mymap; mymap['a']=10; mymap['b']=20; mymap['c']=30; std::pair::iterator,std::map::iterator> ret; ret = mymap.equal_range('b'); std::cout << "lower bound points to: "; std::cout << ret.first->first << " => " << ret.first->second << '\n'; std::cout << "upper bound points to: "; std::cout << ret.second->first << " => " << ret.second->second << '\n'; return 0; } ``` Output ``` lower bound points to: 'b' => 20 upper bound points to: 'c' => 30 ``` ### multimap 在map的基础上,允许重复元素。 ### tuple 元组是一个能够容纳元素集合的对象。每个元素可以是不同的类型。 ```cpp template class tuple; ``` Example ```cpp #include // std::cout #include // std::tuple, std::get, std::tie, std::ignore int main () { std::tuple foo (10,'x'); auto bar = std::make_tuple ("test", 3.1, 14, 'y'); std::get<2>(bar) = 100; // access element int myint; char mychar; std::tie (myint, mychar) = foo; // unpack elements std::tie (std::ignore, std::ignore, myint, mychar) = bar; // unpack (with ignore) mychar = std::get<3>(bar); std::get<0>(foo) = std::get<2>(bar); std::get<1>(foo) = mychar; std::cout << "foo contains: "; std::cout << std::get<0>(foo) << ' '; std::cout << std::get<1>(foo) << '\n'; return 0; } ``` Output ``` foo contains: 100 y ``` #### tuple::tuple 构建一个 tuple(元组)对象。 这涉及单独构建其元素,初始化取决于调用的构造函数形式: (1)默认的构造函数 构建一个 元组对象的元素值初始化。 (2)复制/移动构造函数 该对象使用tpl的内容进行初始化 元组目的。tpl 的相应元素被传递给每个元素的构造函数。 (3)隐式转换构造函数 同上。tpl中的 所有类型都可以隐含地转换为构造中它们各自元素的类型元组 目的。 (4)初始化构造函数 用elems中的相应元素初始化每个元素。elems 的相应元素被传递给每个元素的构造函数。 (5)对转换构造函数 该对象有两个对应于pr.first和的元素pr.second。PR中的所有类型都应该隐含地转换为其中各自元素的类型元组 目的。 (6)分配器版本 和上面的版本一样,除了每个元素都是使用allocator alloc构造的。 ```cpp default (1) constexpr tuple(); copy / move (2) tuple (const tuple& tpl) = default; tuple (tuple&& tpl) = default; implicit conversion (3) template tuple (const tuple& tpl); template tuple (tuple&& tpl); initialization (4) explicit tuple (const Types&... elems); template explicit tuple (UTypes&&... elems); conversion from pair (5) template tuple (const pair& pr); template tuple (pair&& pr); allocator (6) template tuple (allocator_arg_t aa, const Alloc& alloc); template tuple (allocator_arg_t aa, const Alloc& alloc, const tuple& tpl); template tuple (allocator_arg_t aa, const Alloc& alloc, tuple&& tpl); template tuple (allocator_arg_t aa, const Alloc& alloc, const tuple& tpl); template tuple (allocator_arg_t aa, const Alloc& alloc, tuple&& tpl); template tuple (allocator_arg_t aa, const Alloc& alloc, const Types&... elems); template tuple (allocator_arg_t aa, const Alloc& alloc, UTypes&&... elems); template tuple (allocator_arg_t aa, const Alloc& alloc, const pair& pr); template tuple (allocator_arg_t aa, const Alloc& alloc, pair&& pr); ``` Example ```cpp #include // std::cout #include // std::make_pair #include // std::tuple, std::make_tuple, std::get int main () { std::tuple first; // default std::tuple second (first); // copy std::tuple third (std::make_tuple(20,'b')); // move std::tuple fourth (third); // implicit conversion std::tuple fifth (10,'a'); // initialization std::tuple sixth (std::make_pair(30,'c')); // from pair / move std::cout << "sixth contains: " << std::get<0>(sixth); std::cout << " and " << std::get<1>(sixth) << '\n'; return 0; } ``` Output ``` sixth contains: 30 and c ``` #### tuple::tie 将元组中的值解包到多个变量中. ```cpp template constexpr tuple tie(Types&... args); ``` Example: ```cpp #include #include int main() { std::tuple myTuple(42, 3.14, "Hello"); int a; double b; std::string c; std::tie(a, b, c) = myTuple; std::cout << "a: " << a << "\n"; std::cout << "b: " << b << "\n"; std::cout << "c: " << c << "\n"; return 0; } ``` Output: ```cpp a: 42 b: 3.14 c: Hello ``` #### tuple::apply(c++23) 将函数 F 应用于 tuple t ```cpp template constexpr decltype(auto) apply(F&& f, Tuple&& t); ``` Example: ```cpp #include #include void print(int a, double b, const std::string& c) { std::cout << "a: " << a << ", b: " << b << ", c: " << c << "\n"; } int main() { std::tuple myTuple(42, 3.14, "Hello"); std::apply(print, myTuple); return 0; } ``` Output: ```cpp a: 42, b: 3.14, c: Hello ``` #### tuple::ignore 解包操作中忽略某个值。 ```cpp namespace std { struct _Ignored {}; _Ignored const ignore; } ``` Example: ```cpp #include #include int main() { std::tuple t(1, 2.5, "hello", 'a'); int a; std::string c; std::tie(a, std::ignore, c, std::ignore) = t; std::cout << "a: " << a << ", c: " << c << std::endl; return 0; } ``` Output: ```cpp a: 1, c: hello ``` ### pair 这个类把一对值(values)结合在一起,这些值可能是不同的类型(T1 和 T2)。每个值可以被公有的成员变量first、second访问。 pair是tuple(元组)的一个特例。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。 应用: * 可以将两个类型数据组合成一个如map * 当某个函数需要两个返回值时 ```cpp template struct pair; ``` #### pair::pair 构建一个pair对象。 这涉及到单独构建它的两个组件对象,初始化依赖于调用的构造器形式: (1)默认的构造函数 构建一个 对对象的元素值初始化。 (2)复制/移动构造函数(和隐式转换) 该对象被初始化为pr的内容 对目的。pr 的相应成员被传递给每个成员的构造函数。 (3)初始化构造函数 会员 第一是由一个和成员构建的第二与b。 (4)分段构造 构造成员 first 和 second 到位,传递元素first\_args 作为参数的构造函数 first,和元素 second\_args 到的构造函数 second 。 ```cpp default (1) constexpr pair(); copy / move (2) template pair (const pair& pr); template pair (pair&& pr); pair (const pair& pr) = default; pair (pair&& pr) = default; initialization (3) pair (const first_type& a, const second_type& b); template pair (U&& a, V&& b); piecewise (4) template pair (piecewise_construct_t pwc, tuple first_args, tuple second_args); ``` Example ```cpp #include // std::pair, std::make_pair #include // std::string #include // std::cout int main () { std::pair product1; // default constructor std::pair product2 ("tomatoes",2.30); // value init std::pair product3 (product2); // copy constructor product1 = std::make_pair(std::string("lightbulbs"),0.99); // using make_pair (move) product2.first = "shoes"; // the type of first is string product2.second = 39.90; // the type of second is double std::cout << "The price of " << product1.first << " is $" << product1.second << '\n'; std::cout << "The price of " << product2.first << " is $" << product2.second << '\n'; std::cout << "The price of " << product3.first << " is $" << product3.second << '\n'; return 0; } ``` Output ``` The price of lightbulbs is $0.99 The price of shoes is $39.9 The price of tomatoes is $2.3 ``` ### span 提供了一种方便的方式来引用连续的内存块。span不复制数据,使用方式类似于指针或引用,提供了更高的性能。 #### span::first 获取一个包含span前count个元素的子视图。 ```cpp template constexpr std::span first() const; ``` Example: ```cpp #include #include #include int main() { std::vector vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::span span(vec); auto first3 = span.first<3>(); std::cout << "First 3 elements: "; for (int i : first3) { std::cout << i << " "; } return 0; } ``` Output: ```cpp First 3 elements: 1 2 3 ``` #### span::subspan 获取一个包含span中从offset开始的count个元素的子视图。 ```cpp constexpr std::span subspan(std::size_t offset, std::size_t count) const; ``` Example: ```cpp #include #include #include int main() { std::vector vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::span span(vec); auto subspan1 = span.subspan(2, 3); std::cout << "Subspan : "; for (int i : subspan1) { std::cout << i << " "; } return 0; } ``` Output: ```cpp Subspan : 3 4 5 ``` ### 无序容器(基于哈希表实现) ![](http://img.blog.csdn.net/20160410123436394) #### unordered_set 存储唯一的元素,不允许重复,元素的顺序是无序的。 #### unordered_multiset 存储元素,允许重复,元素的顺序是无序的。 #### unordered_map 存储键值对,键是唯一的,元素的顺序是无序的。 #### unordered_multimap 存储键值对,允许键重复,元素的顺序是无序的。 ### 平坦容器(C++23) 使用连续的存储空间,速度优化。 ![](http://img.blog.csdn.net/20160410123436394) #### flat_set #### flat_multiset #### flat_map #### flat_multimap ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/CNAME ================================================ interview.huihut.com ================================================ FILE: docs/README.md ================================================
📖 Github    |    📚 Docsify

💡 关于 📚 本仓库是面向 C/C++ 技术方向校招求职者、初学者的基础知识总结,包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。 💡 侧边目录支持方式:[📚 Docsify 文档](https://interview.huihut.com)、[Github + TOC 导航](https://github.com/jawil/GayHub)([TOC预览.png](https://gitee.com/huihut/interview/raw/master/images/TOC预览.png)) 📄 保存为 PDF 方式:使用 Chrome 浏览器打开 📚 Docsify 文档 页面,缩起左侧目录-右键 - 打印 - 选择目标打印机是另存为PDF - 保存([打印预览.png](https://gitee.com/huihut/interview/raw/master/images/打印预览.png)) 🙏 仓库内容如有错误或改进欢迎 issue 或 pr,建议或讨论可在 [#12](https://github.com/huihut/interview/issues/12) 提出。由于本人水平有限,仓库中的知识点有来自本人原创、读书笔记、书籍、博文等,非原创均已标明出处,如有遗漏,请 issue 提出。本仓库遵循 [CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享)](https://github.com/huihut/interview/blob/master/LICENSE) 协议,转载请注明出处,不得用于商业目的。
## 📑 目录 * [➕ C/C++](#cc) * [⭐️ Effective](#effective) * [📦 STL](#stl) * [〽️ 数据结构](#data-structure) * [⚡️ 算法](#algorithm) * [❓ Problems](#problems) * [💻 操作系统](#os) * [☁️ 计算机网络](#computer-network) * [🌩 网络编程](#network-programming) * [💾 数据库](#database) * [📏 设计模式](#design-pattern) * [⚙️ 链接装载库](#link-loading-library) * [📚 书籍](#books) * [🔱 C/C++ 发展方向](#cc-development-direction) * [💯 复习刷题网站](#review-of-brush-questions-website) * [📝 面试题目经验](#interview-questions-experience) * [📆 招聘时间岗位](#recruitment-time-post) * [👍 内推](#recommend) * [👬 贡献者](#contributor) * [📜 License](#license) ## ➕ C/C++ ### const #### 作用 1. 修饰变量,说明该变量不可以被改变; 2. 修饰指针,分为指向常量的指针(pointer to const)和自身是常量的指针(常量指针,const pointer); 3. 修饰引用,指向常量的引用(reference to const),用于形参类型,即避免了拷贝,又避免了函数对值的修改; 4. 修饰成员函数,说明该成员函数内不能修改成员变量。 #### const 的指针与引用 * 指针 * 指向常量的指针(pointer to const) * 自身是常量的指针(常量指针,const pointer) * 引用 * 指向常量的引用(reference to const) * 没有 const reference,因为引用只是对象的别名,引用不是对象,不能用 const 修饰 > (为了方便记忆可以想成)被 const 修饰(在 const 后面)的值不可改变,如下文使用例子中的 `p2`、`p3` #### 使用 const 使用 ```cpp // 类 class A { private: const int a; // 常对象成员,可以使用初始化列表或者类内初始化 public: // 构造函数 A() : a(0) { }; A(int x) : a(x) { }; // 初始化列表 // const可用于对重载函数的区分 int getValue(); // 普通成员函数 int getValue() const; // 常成员函数,不得修改类中的任何数据成员的值 }; void function() { // 对象 A b; // 普通对象,可以调用全部成员函数 const A a; // 常对象,只能调用常成员函数 const A *p = &a; // 指针变量,指向常对象 const A &q = a; // 指向常对象的引用 // 指针 char greeting[] = "Hello"; char* p1 = greeting; // 指针变量,指向字符数组变量 const char* p2 = greeting; // 指针变量,指向字符数组常量(const 后面是 char,说明指向的字符(char)不可改变) char* const p3 = greeting; // 自身是常量的指针,指向字符数组变量(const 后面是 p3,说明 p3 指针自身不可改变) const char* const p4 = greeting; // 自身是常量的指针,指向字符数组常量 } // 函数 void function1(const int Var); // 传递过来的参数在函数内不可变 void function2(const char* Var); // 参数指针所指内容为常量 void function3(char* const Var); // 参数指针为常量 void function4(const int& Var); // 引用参数在函数内为常量 // 函数返回值 const int function5(); // 返回一个常数 const int* function6(); // 返回一个指向常量的指针变量,使用:const int *p = function6(); int* const function7(); // 返回一个指向变量的常指针,使用:int* const p = function7(); ``` #### 宏定义 #define 和 const 常量 宏定义 #define|const 常量 ---|--- 宏定义,相当于字符替换|常量声明 预处理器处理|编译器处理 无类型安全检查|有类型安全检查 不分配内存|要分配内存 存储在代码段|存储在数据段 可通过 `#undef` 取消|不可取消 ### static #### 作用 1. 修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。 2. 修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。 3. 修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。 4. 修饰成员函数,修饰成员函数使得不需要生成对象就可以访问该函数,但是在 static 函数内不能访问非静态成员。 ### this 指针 1. `this` 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。 2. 当对一个对象调用成员函数时,编译程序先将对象的地址赋给 `this` 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 `this` 指针。 3. 当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。 4. `this` 指针被隐含地声明为: `ClassName *const this`,这意味着不能给 `this` 指针赋值;在 `ClassName` 类的 `const` 成员函数中,`this` 指针的类型为:`const ClassName* const`,这说明不能对 `this` 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作); 5. `this` 并不是一个常规变量,而是个右值,所以不能取得 `this` 的地址(不能 `&this`)。 6. 在以下场景中,经常需要显式引用 `this` 指针: 1. 为实现对象的链式引用; 2. 为避免对同一对象进行赋值操作; 3. 在实现一些数据结构时,如 `list`。 ### inline 内联函数 #### 特征 * 相当于把内联函数里面的内容写在调用内联函数处; * 相当于不用执行进入函数的步骤,直接执行函数体; * 相当于宏,却比宏多了类型检查,真正具有函数特性; * 编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数; * 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。 #### 使用 inline 使用 ```cpp // 声明1(加 inline,建议使用) inline int functionName(int first, int second,...); // 声明2(不加 inline) int functionName(int first, int second,...); // 定义 inline int functionName(int first, int second,...) {/****/}; // 类内定义,隐式内联 class A { int doA() { return 0; } // 隐式内联 } // 类外定义,需要显式内联 class A { int doA(); } inline int A::doA() { return 0; } // 需要显式内联 ``` #### 编译器对 inline 函数的处理步骤 1. 将 inline 函数体复制到 inline 函数调用点处; 2. 为所用 inline 函数中的局部变量分配内存空间; 3. 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中; 4. 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。 #### 优缺点 优点 1. 内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。 2. 内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。 3. 在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。 4. 内联函数在运行时可调试,而宏定义不可以。 缺点 1. 代码膨胀。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。 2. inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译,不像 non-inline 可以直接链接。 3. 是否内联,程序员不可控。内联函数只是对编译器的建议,是否对函数内联,决定权在于编译器。 #### 虚函数(virtual)可以是内联函数(inline)吗? > [Are "inline virtual" member functions ever actually "inlined"?](http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html) * 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。 * 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。 * `inline virtual` 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 `Base::who()`),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。 虚函数内联使用 ```cpp #include using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // 不写inline时隐式内联 { cout << "I am Derived\n"; } }; int main() { // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 Base b; b.who(); // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。 Base *ptr = new Derived(); ptr->who(); // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 delete ptr; ptr = nullptr; system("pause"); return 0; } ``` ### volatile ```cpp volatile int i = 10; ``` * volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。 * volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值) * const 可以是 volatile (如只读的状态寄存器) * 指针可以是 volatile ### assert() 断言,是宏,而非函数。assert 宏的原型定义在 ``(C)、``(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 `NDEBUG` 来关闭 assert,但是需要在源代码的开头,`include ` 之前。 assert() 使用 ```cpp #define NDEBUG // 加上这行,则 assert 不可用 #include assert( p != NULL ); // assert 不可用 ``` ### sizeof() * sizeof 对数组,得到整个数组所占空间大小。 * sizeof 对指针,得到指针本身所占空间大小。 ### 编译器扩展与标准对齐控制 * 编译器扩展:`#pragma pack(n)`,将随后定义的 `struct`/`class`/`union` 的成员最大对齐限制为 n 字节。 * 标准对齐控制: * `alignas(k)`,要求类型或变量至少按 k 字节对齐(向上取整到 ≥ 自然对齐)。 * `alignof(T)`,获取类型 T 的自然对齐要求(编译时常量)。 特性 |#pragma pack|alignas --------|--------------|------------ 标准化 |❌ 编译器扩展 | ✅ C++11标准 对齐方向|⬇️ 只能减小对齐| ⬆️ 只能增大对齐 可移植性|❌ 编译器依赖 | ✅ 跨平台 作用范围|🔄 影响整个结构| 🎯 可针对单个成员 性能影响|⚠️ 可能降低内存访问速度| ⚠️ 过度对齐浪费空间 使用 ```cpp #include #include #pragma pack(push, 1) // 最大对齐 1 字节,紧凑布局 struct PackedHeader { uint16_t len; // offset 0 uint32_t id; // offset 2 }; #pragma pack(pop) struct alignas(8) Align8 { double value; // offset 0, 占 8 字节 int flag; // offset 8 }; int main() { std::cout << "PackedHeader size: " << sizeof(PackedHeader) << "\n"; // 6 std::cout << "Align8 size: " << sizeof(Align8) << "\n"; // 16 } ``` ### 位域 ```cpp Bit mode: 2; // mode 占 2 位 ``` 类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。 * 位域在内存中的布局是与机器有关的 * 位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定 * 取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域 ### extern 与 extern "C" * `extern` 是存储类说明符(storage-class-specifier),用于声明变量或函数具有外部链接,表示实体的定义可能在其他翻译单元中。 * `extern "C"` 是链接指示(linkage directive),它指定函数或变量使用 C 语言链接(不影响编译规则)。 1. 禁止 C++ 名称修饰。确保符号名称与该平台下 C 编译器生成的名称一致,避免链接时因名称修饰导致的未定义符号错误,但不保证平台 ABI(应用二进制接口)一致性。 2. 实现 C/C++ 互操作。允许 C++ 函数被 C 代码调用(或反之)。 `extern "C"` 使用 ```cpp #ifdef __cplusplus extern "C" { #endif void *memset(void *, int, size_t); #ifdef __cplusplus } #endif ``` ### struct 和 typedef struct #### C 中 ```c // c typedef struct Student { int age; } S; ``` 等价于 ```c // c struct Student { int age; }; typedef struct Student S; ``` 此时 `S` 等价于 `struct Student`,但两个标识符名称空间不相同。 另外还可以定义与 `struct Student` 不冲突的 `void Student() {}`。 #### C++ 中 由于编译器定位符号的规则(搜索规则)改变,导致不同于C语言。 一、如果在类标识符空间定义了 `struct Student {...};`,使用 `Student me;` 时,编译器将搜索全局标识符表,`Student` 未找到,则在类标识符内搜索。 即表现为可以使用 `Student` 也可以使用 `struct Student`,如下: ```cpp // cpp struct Student { int age; }; void f( Student me ); // 正确,"struct" 关键字可省略 ``` 二、若定义了与 `Student` 同名函数之后,则 `Student` 只代表函数,不代表结构体,如下: ```cpp typedef struct Student { int age; } S; void Student() {} // 正确,定义后 "Student" 只代表此函数 //void S() {} // 错误,符号 "S" 已经被定义为一个 "struct Student" 的别名 int main() { Student(); struct Student me; // 或者 "S me"; return 0; } ``` ### C++ 中 struct 和 class 总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。 #### 区别 * 最本质的一个区别就是默认的访问控制 1. 默认的继承访问权限。struct 是 public 的,class 是 private 的。 2. struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。 ### union 联合 联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点: * 默认访问控制符为 public * 可以含有构造函数、析构函数 * 不能含有引用类型的成员 * 不能继承自其他类,不能作为基类 * 不能含有虚函数 * 匿名 union 在定义所在作用域可直接访问 union 成员 * 匿名 union 不能包含 protected 成员或 private 成员 * 全局匿名联合必须是静态(static)的 union 使用 ```cpp #include union UnionTest { UnionTest() : i(10) {}; int i; double d; }; static union { int i; double d; }; int main() { UnionTest u; union { int i; double d; }; std::cout << u.i << std::endl; // 输出 UnionTest 联合的 10 ::i = 20; std::cout << ::i << std::endl; // 输出全局静态匿名联合的 20 i = 30; std::cout << i << std::endl; // 输出局部匿名联合的 30 return 0; } ``` ### C 实现 C++ 类 C 实现 C++ 的面向对象特性(封装、继承、多态) * 封装:使用函数指针把属性与方法封装到结构体中 * 继承:结构体嵌套 * 多态:父类与子类方法的函数指针不同 > [Can you write object-oriented code in C? [closed]](https://stackoverflow.com/a/351745) ### explicit(显式)关键字 * explicit 修饰构造函数时,可以防止隐式转换和复制初始化 * explicit 修饰转换函数时,可以防止隐式转换,但 [按语境转换](https://zh.cppreference.com/w/cpp/language/implicit_conversion) 除外 explicit 使用 ```cpp struct A { A(int) { } operator bool() const { return true; } }; struct B { explicit B(int) {} explicit operator bool() const { return true; } }; void doA(A a) {} void doB(B b) {} int main() { A a1(1); // OK:直接初始化 A a2 = 1; // OK:复制初始化 A a3{ 1 }; // OK:直接列表初始化 A a4 = { 1 }; // OK:复制列表初始化 A a5 = (A)1; // OK:允许 static_cast 的显式转换 doA(1); // OK:允许从 int 到 A 的隐式转换 if (a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a8 = static_cast(a1); // OK :static_cast 进行直接初始化 B b1(1); // OK:直接初始化 B b2 = 1; // 错误:被 explicit 修饰构造函数的对象不可以复制初始化 B b3{ 1 }; // OK:直接列表初始化 B b4 = { 1 }; // 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化 B b5 = (B)1; // OK:允许 static_cast 的显式转换 doB(1); // 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换 if (b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 bool b6(b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 bool b7 = b1; // 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换 bool b8 = static_cast(b1); // OK:static_cast 进行直接初始化 return 0; } ``` ### friend 友元类和友元函数 * 能访问私有成员 * 破坏封装性 * 友元关系不可传递 * 友元关系的单向性 * 友元声明的形式及数量不受限制 ### using #### using 声明 一条 `using 声明` 语句一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字。如: ```cpp using namespace_name::name; ``` #### 构造函数的 using 声明 在 C++11 中,派生类能够重用其直接基类定义的构造函数。 ```cpp class Derived : Base { public: using Base::Base; /* ... */ }; ``` 如上 using 声明,对于基类的每个构造函数,编译器都生成一个与之对应(形参列表完全相同)的派生类构造函数。生成如下类型构造函数: ```cpp Derived(parms) : Base(args) { } ``` #### using 指示 `using 指示` 使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。如: ```cpp using namespace namespace_name; ``` #### 尽量少使用 `using 指示` 污染命名空间 > 一般说来,使用 using 命令比使用 using 编译命令更安全,这是由于它**只导入了指定的名称**。如果该名称与局部名称发生冲突,编译器将**发出指示**。using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则**局部名称将覆盖名称空间版本**,而编译器**并不会发出警告**。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。 using 使用 尽量少使用 `using 指示` ```cpp using namespace std; ``` 应该多使用 `using 声明` ```cpp int x; std::cin >> x ; std::cout << x << std::endl; ``` 或者 ```cpp using std::cin; using std::cout; using std::endl; int x; cin >> x; cout << x << endl; ``` ### :: 范围解析运算符 #### 分类 1. 全局作用域符(`::name`):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间 2. 类作用域符(`class::name`):用于表示指定类型的作用域范围是具体某个类的 3. 命名空间作用域符(`namespace::name`):用于表示指定类型的作用域范围是具体某个命名空间的 :: 使用 ```cpp int count = 11; // 全局(::)的 count class A { public: static int count; // 类 A 的 count(A::count) }; int A::count = 21; void fun() { int count = 31; // 初始化局部的 count 为 31 count = 32; // 设置局部的 count 的值为 32 } int main() { ::count = 12; // 测试 1:设置全局的 count 的值为 12 A::count = 22; // 测试 2:设置类 A 的 count 为 22 fun(); // 测试 3 return 0; } ``` ### enum 枚举类型 #### 限定作用域的枚举类型 ```cpp enum class open_modes { input, output, append }; ``` #### 不限定作用域的枚举类型 ```cpp enum color { red, yellow, green }; enum { floatPrec = 6, doublePrec = 10 }; ``` ### decltype decltype 关键字用于检查实体的声明类型或表达式的类型及值分类。语法: ```cpp decltype ( expression ) ``` decltype 使用 ```cpp // 尾置返回允许我们在参数列表之后声明返回类型 template auto fcn(It beg, It end) -> decltype(*beg) { // 处理序列 return *beg; // 返回序列中一个元素的引用 } // 为了使用模板参数成员,必须用 typename template auto fcn2(It beg, It end) -> typename remove_reference::type { // 处理序列 return *beg; // 返回序列中一个元素的拷贝 } ``` ### 引用 #### 左值引用 常规引用,一般表示对象的身份。 #### 右值引用 右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。 右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面: * 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。 * 能够更简洁明确地定义泛型函数。 #### 引用折叠 * `X& &`、`X& &&`、`X&& &` 可折叠成 `X&` * `X&& &&` 可折叠成 `X&&` ### 宏 * 宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对 “参数” 进行的是一对一的替换。 ### 成员初始化列表 好处 * 更高效:少了一次调用默认构造函数的过程。 * 有些场合必须要用初始化列表: 1. 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面 2. 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面 3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化 ### initializer_list 列表初始化 用花括号初始化器列表初始化一个对象,其中对应构造函数接受一个 `std::initializer_list` 参数. initializer_list 使用 ```cpp #include #include #include template struct S { std::vector v; S(std::initializer_list l) : v(l) { std::cout << "constructed with a " << l.size() << "-element list\n"; } void append(std::initializer_list l) { v.insert(v.end(), l.begin(), l.end()); } std::pair c_arr() const { return {&v[0], v.size()}; // 在 return 语句中复制列表初始化 // 这不使用 std::initializer_list } }; template void templated_fn(T) {} int main() { S s = {1, 2, 3, 4, 5}; // 复制初始化 s.append({6, 7, 8}); // 函数调用中的列表初始化 std::cout << "The vector size is now " << s.c_arr().second << " ints:\n"; for (auto n : s.v) std::cout << n << ' '; std::cout << '\n'; std::cout << "Range-for over brace-init-list: \n"; for (int x : {-1, -2, -3}) // auto 的规则令此带范围 for 工作 std::cout << x << ' '; std::cout << '\n'; auto al = {10, 11, 12}; // auto 的特殊规则 std::cout << "The list bound to auto has size() = " << al.size() << '\n'; // templated_fn({1, 2, 3}); // 编译错误!“ {1, 2, 3} ”不是表达式, // 它无类型,故 T 无法推导 templated_fn>({1, 2, 3}); // OK templated_fn>({1, 2, 3}); // 也 OK } ``` ### 面向对象 面向对象程序设计(Object-oriented programming,OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。 ![面向对象特征](https://gitee.com/huihut/interview/raw/master/images/面向对象基本特征.png) 面向对象三大特征 —— 封装、继承、多态 ### 封装 把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。关键字:public, protected, private。不写默认为 private。 * `public` 成员:可以被任意实体访问 * `protected` 成员:只允许被子类及本类的成员函数访问 * `private` 成员:只允许被本类的成员函数、友元类或友元函数访问 ### 继承 * 基类(父类)——> 派生类(子类) ### 多态 * 多态,即多种状态(形态)。简单来说,我们可以将多态定义为消息以多种形式显示的能力。 * 多态是以封装和继承为基础的。 * C++ 多态分类及实现: 1. 重载多态(Ad-hoc Polymorphism,编译期):函数重载、运算符重载 2. 子类型多态(Subtype Polymorphism,运行期):虚函数 3. 参数多态性(Parametric Polymorphism,编译期):类模板、函数模板 4. 强制多态(Coercion Polymorphism,编译期/运行期):基本类型转换、自定义类型转换 > [The Four Polymorphisms in C++](https://catonmat.net/cpp-polymorphism) #### 静态多态(编译期/早绑定) 函数重载 ```cpp class A { public: void do(int a); void do(int a, int b); }; ``` #### 动态多态(运行期/晚绑定) * 虚函数:用 virtual 修饰成员函数,使其成为虚函数 * 动态绑定:当使用基类的引用或指针调用一个虚函数时将发生动态绑定 **注意:** * 可以将派生类的对象赋值给基类的指针或引用,反之不可 * 普通函数(非类成员函数)不能是虚函数 * 静态函数(static)不能是虚函数 * 构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间中,必须要构造函数调用完成后才会形成虚表指针) * 内联函数不能是表现多态性时的虚函数,解释见:[虚函数(virtual)可以是内联函数(inline)吗?](https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97) 动态多态使用 ```cpp class Shape // 形状类 { public: virtual double calcArea() { ... } virtual ~Shape(); }; class Circle : public Shape // 圆形类 { public: virtual double calcArea(); ... }; class Rect : public Shape // 矩形类 { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); Shape * shape2 = new Rect(5.0, 6.0); shape1->calcArea(); // 调用圆形类里面的方法 shape2->calcArea(); // 调用矩形类里面的方法 delete shape1; shape1 = nullptr; delete shape2; shape2 = nullptr; return 0; } ``` ### 虚析构函数 虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。 虚析构函数使用 ```cpp class Shape { public: Shape(); // 构造函数不能是虚函数 virtual double calcArea(); virtual ~Shape(); // 虚析构函数 }; class Circle : public Shape // 圆形类 { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); shape1->calcArea(); delete shape1; // 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。 shape1 = NULL; return 0; } ``` ### 纯虚函数 纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。 ```cpp virtual int A() = 0; ``` ### 虚函数、纯虚函数 * 类里如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。 * 虚函数在子类里面可以不重写;但纯虚函数必须在子类实现才可以实例化子类。 * 虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。 * 带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。 * 虚基类是虚继承中的基类,具体见下文虚继承。 > [CSDN . C++ 中的虚函数、纯虚函数区别和联系](https://blog.csdn.net/u012260238/article/details/53610462) ### 虚函数指针、虚函数表 * 虚函数指针:在含有虚函数类的对象中,指向虚函数表,在运行时确定。 * 虚函数表:在程序只读数据段(`.rodata section`,见:[目标文件存储结构](#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84)),存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建。 > [C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现](https://blog.twofei.com/496/) ### 虚继承 虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。 底层实现原理与编译器相关,一般通过**虚基类指针**和**虚基类表**实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。 实际上,vbptr 指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。 ### 虚继承、虚函数 * 相同之处:都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间) * 不同之处: * 虚继承 * 虚基类依旧存在继承类中,只占用存储空间 * 虚基类表存储的是虚基类相对直接继承类的偏移 * 虚函数 * 虚函数不占用存储空间 * 虚函数表存储的是虚函数地址 ### 类模板、成员模板、虚函数 * 类模板中可以使用虚函数 * 一个类(无论是普通类还是类模板)的成员模板(本身是模板的成员函数)不能是虚函数 ### 抽象类、接口类、聚合类 * 抽象类:含有纯虚函数的类 * 接口类:仅含有纯虚函数的抽象类 * 聚合类:用户可以直接访问其成员,并且具有特殊的初始化语法形式。满足如下特点: * 所有成员都是 public * 没有定义任何构造函数 * 没有类内初始化 * 没有基类,也没有 virtual 函数 ### 内存分配和管理 #### malloc、calloc、realloc、alloca 1. malloc:申请指定字节数的内存。申请到的内存中的初始值不确定。 2. calloc:为指定长度的对象,分配能容纳其指定个数的内存。申请到的内存的每一位(bit)都初始化为 0。 3. realloc:更改以前分配的内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定。 4. alloca:在栈上申请内存。程序在出栈的时候,会自动释放内存。但是需要注意的是,alloca 不具可移植性, 而且在没有传统堆栈的机器上很难实现。alloca 不宜使用在必须广泛移植的程序中。C99 中支持变长数组 (VLA),可以用来替代 alloca。 #### malloc、free 用于分配、释放内存 malloc、free 使用 申请内存,确认是否申请成功 ```cpp char *str = (char*) malloc(100); assert(str != nullptr); ``` 释放内存后指针置空 ```cpp free(p); p = nullptr; ``` #### new、delete 1. new / new[]:完成两件事,先底层调用 malloc 分配了内存,然后调用构造函数(创建对象)。 2. delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用 free 释放空间。 3. new 在申请内存时会自动计算所需字节数,而 malloc 则需我们自己输入申请内存空间的字节数。 new、delete 使用 申请内存,确认是否申请成功 ```cpp int main() { T* t = new T(); // 先内存分配 ,再构造函数 delete t; // 先析构函数,再内存释放 return 0; } ``` #### 定位 new 定位 new(placement new)允许我们向 new 传递额外的地址参数,从而在预先指定的内存区域创建对象。 ```cpp new (place_address) type new (place_address) type (initializers) new (place_address) type [size] new (place_address) type [size] { braced initializer list } ``` * `place_address` 是个指针 * `initializers` 提供一个(可能为空的)以逗号分隔的初始值列表 ### delete this 合法吗? > [Is it legal (and moral) for a member function to say delete this?](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) 合法,但: 1. 必须保证 this 对象是通过 `new`(不是 `new[]`、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的 2. 必须保证调用 `delete this` 的成员函数是最后一个调用 this 的成员函数 3. 必须保证成员函数的 `delete this ` 后面没有调用 this 了 4. 必须保证 `delete this` 后没有人使用了 ### 智能指针 #### C++ 标准库(STL)中 头文件:`#include ` #### C++ 98 ```cpp std::auto_ptr ps (new std::string(str)); ``` #### C++ 11 1. shared_ptr 2. unique_ptr 3. weak_ptr 4. auto_ptr(被 C++11 弃用) * Class shared_ptr 实现共享式拥有(shared ownership)概念。多个智能指针指向相同对象,该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作,标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。 * Class unique_ptr 实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏(resource leak)——如 new 后忘记 delete ——特别有用。 ##### shared_ptr 多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。 * 支持定制型删除器(custom deleter),可防范 Cross-DLL 问题(对象在动态链接库(DLL)中被 new 创建,却在另一个 DLL 内被 delete 销毁)、自动解除互斥锁 ##### weak_ptr weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。 * 可打破环状引用(cycles of references,两个其实已经没有被使用的对象彼此互指,使之看似还在 “被使用” 的状态)的问题 ##### unique_ptr unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有者被销毁或变成空或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。 * unique_ptr 用于取代 auto_ptr ##### auto_ptr 被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 `std::move` 语义,以及其他瑕疵。 ##### auto_ptr 与 unique_ptr 比较 * auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了`move` 语义; * auto_ptr 对象不能管理数组(析构调用 `delete`),unique_ptr 可以管理数组(析构调用 `delete[]` ); ### 强制类型转换运算符 > [MSDN . 强制转换运算符](https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx) #### static_cast * 编译时类型转换(无运行时检查) * 不依赖 RTTI | 转换类型 | 安全性 | 示例 | |--------------------------|--------------|--------------------------| | 数值类型转换 | ✅ 安全 | `float f=3.14; int i=static_cast(f);` | | 类向上转换 | ✅ 安全 | `Derived* d; Base* b=static_cast(d);` | | 类向下转换 | ⚠️ 不安全 | `Base* b=new Base; Derived* d=static_cast(b);` | | 类不变转换 | ✅ 安全 | `MyClass* p; MyClass* same=static_cast(p);` | | 显式构造函数调用 | ✅ 安全 | `func(static_cast("text"));` | | 任意→void* | ✅ 安全 | `int* p; void* vp=static_cast(p);` | | 枚举↔整型 | ✅ 安全 | `enum Color{RED}; int c=static_cast(RED);` | #### dynamic_cast * 运行时类型检查(依赖 RTTI) * 多态类型要求(基类需至少一个虚函数) * 安全失败机制(nullptr 或异常) | 转换类型 | 安全性 | 运行时开销 | 多态要求 | 失败处理 | 示例 | |--------------|--------|------------|----------|------------------------|------| | 类向上转换 | ✅ 安全 | 无 | ❌ 不需要 | 不适用(总是成功) | `Derived* d; Base* b = dynamic_cast(d);` | | 类向下转换 | ✅ 安全 | 有 | ✅ 需要 | 指针→`nullptr`
引用→`std::bad_cast` | `Base* b=new Base; Derived* d = dynamic_cast(b);` | | 类旁支转换 | ✅ 安全 | 有 | ✅ 需要 | 指针→`nullptr`
引用→`std::bad_cast` | `B2* b2 = dynamic_cast(b1); // 菱形继承中 B1*→B2*` | | 类不变转换 | ✅ 安全 | 无 | ❌ 不需要 | 不适用(总是成功) | `Derived* d2 = dynamic_cast(d1);` | | 任意→void* | ✅ 安全 | 有 | ✅ 需要 | `nullptr` | `void* p = dynamic_cast(obj);` | #### const_cast * 编译时类型修饰符操作 * 仅修改 `const`/`volatile` 属性 * 不改变底层二进制表示 | 转换类型 | 安全性 | 示例 | |------------------------|--------------|-------------------------------| | 移除 const | ⚠️ 风险 | `const int* cp; int* p=const_cast(cp);` | | 移除 volatile | ⚠️ 风险 | `volatile int* vp; int* p=const_cast(vp);` | | 添加 const | ✅ 安全 | `int* p; const int* cp=const_cast(p);` | | 兼容旧式 API | ⚠️ 必要风险 | `legacy_api(const_cast(str.c_str()));` | #### reinterpret_cast * 无编译时类型安全检查 * 二进制位级重新解释 * 最危险的转换操作符 | 转换类型 | 安全性 | 示例 | |------------------------|--------------|-------------------------------| | 指针↔指针 | ❌ 高危 | `MyClass* obj; void* p=reinterpret_cast(obj);` | | 指针↔整型 | ❌ 高危 | `intptr_t addr=reinterpret_cast(&obj);` | | 函数指针转换 | ❌ 极高危 | 不同签名函数指针转换 | | 内存映射 I/O | ⚠️ 系统编程 | 硬件寄存器访问 | #### bad_cast * `dynamic_cast` 引用转换失败的异常类型。 bad_cast 使用 ```cpp try { Circle& ref_circle = dynamic_cast(ref_shape); } catch (bad_cast b) { cout << "Caught: " << b.what(); } ``` ### 运行时类型信息 (RTTI) #### typeid * typeid 运算符允许在运行时确定对象的类型 * typeid 返回一个 type\_info 对象的引用 * 如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数 * 只能获取对象的实际类型 #### type_info * type_info 类描述编译器在程序中生成的类型信息。 此类的对象可以有效存储指向类型的名称的指针。 type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。 类型的编码规则和排列顺序是未指定的,并且可能因程序而异。 * 头文件:`typeinfo` typeid、type_info 使用 ```cpp #include using namespace std; class Flyable // 能飞的 { public: virtual void takeoff() = 0; // 起飞 virtual void land() = 0; // 降落 }; class Bird : public Flyable // 鸟 { public: void foraging() {...} // 觅食 virtual void takeoff() {...} virtual void land() {...} virtual ~Bird(){} }; class Plane : public Flyable // 飞机 { public: void carry() {...} // 运输 virtual void takeoff() {...} virtual void land() {...} }; class type_info { public: const char* name() const; bool operator == (const type_info & rhs) const; bool operator != (const type_info & rhs) const; int before(const type_info & rhs) const; virtual ~type_info(); private: ... }; void doSomething(Flyable *obj) // 做些事情 { obj->takeoff(); cout << typeid(*obj).name() << endl; // 输出传入对象类型("class Bird" or "class Plane") if(typeid(*obj) == typeid(Bird)) // 判断对象类型 { Bird *bird = dynamic_cast(obj); // 对象转化 bird->foraging(); } obj->land(); } int main(){ Bird *b = new Bird(); doSomething(b); delete b; b = nullptr; return 0; } ``` ## ⭐️ Effective ### Effective C++ 1. 视 C++ 为一个语言联邦(C、Object-Oriented C++、Template C++、STL) 2. 宁可以编译器替换预处理器(尽量以 `const`、`enum`、`inline` 替换 `#define`) 3. 尽可能使用 const 4. 确定对象被使用前已先被初始化(构造时赋值(copy 构造函数)比 default 构造后赋值(copy assignment)效率高) 5. 了解 C++ 默默编写并调用哪些函数(编译器暗自为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符、析构函数) 6. 若不想使用编译器自动生成的函数,就应该明确拒绝(将不想使用的成员函数声明为 private,并且不予实现) 7. 为多态基类声明 virtual 析构函数(如果 class 带有任何 virtual 函数,它就应该拥有一个 virtual 析构函数) 8. 别让异常逃离析构函数(析构函数应该吞下不传播异常,或者结束程序,而不是吐出异常;如果要处理异常应该在非析构的普通函数处理) 9. 绝不在构造和析构过程中调用 virtual 函数(因为这类调用从不下降至 derived class) 10. 令 `operator=` 返回一个 `reference to *this` (用于连锁赋值) 11. 在 `operator=` 中处理 “自我赋值” 12. 赋值对象时应确保复制 “对象内的所有成员变量” 及 “所有 base class 成分”(调用基类复制构造函数) 13. 以对象管理资源(资源在构造函数获得,在析构函数释放,建议使用智能指针,资源取得时机便是初始化时机(Resource Acquisition Is Initialization,RAII)) 14. 在资源管理类中小心 copying 行为(普遍的 RAII class copying 行为是:抑制 copying、引用计数、深度拷贝、转移底部资源拥有权(类似 auto_ptr)) 15. 在资源管理类中提供对原始资源(raw resources)的访问(对原始资源的访问可能经过显式转换或隐式转换,一般而言显示转换比较安全,隐式转换对客户比较方便) 16. 成对使用 new 和 delete 时要采取相同形式(`new` 中使用 `[]` 则 `delete []`,`new` 中不使用 `[]` 则 `delete`) 17. 以独立语句将 newed 对象存储于(置入)智能指针(如果不这样做,可能会因为编译器优化,导致难以察觉的资源泄漏) 18. 让接口容易被正确使用,不易被误用(促进正常使用的办法:接口的一致性、内置类型的行为兼容;阻止误用的办法:建立新类型,限制类型上的操作,约束对象值、消除客户的资源管理责任) 19. 设计 class 犹如设计 type,需要考虑对象创建、销毁、初始化、赋值、值传递、合法值、继承关系、转换、一般化等等。 20. 宁以 pass-by-reference-to-const 替换 pass-by-value (前者通常更高效、避免切割问题(slicing problem),但不适用于内置类型、STL迭代器、函数对象) 21. 必须返回对象时,别妄想返回其 reference(绝不返回 pointer 或 reference 指向一个 local stack 对象,或返回 reference 指向一个 heap-allocated 对象,或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象。) 22. 将成员变量声明为 private(为了封装、一致性、对其读写精确控制等) 23. 宁以 non-member、non-friend 替换 member 函数(可增加封装性、包裹弹性(packaging flexibility)、机能扩充性) 24. 若所有参数(包括被this指针所指的那个隐喻参数)皆须要类型转换,请为此采用 non-member 函数 25. 考虑写一个不抛异常的 swap 函数 26. 尽可能延后变量定义式的出现时间(可增加程序清晰度并改善程序效率) 27. 尽量少做转型动作(旧式:`(T)expression`、`T(expression)`;新式:`const_cast(expression)`、`dynamic_cast(expression)`、`reinterpret_cast(expression)`、`static_cast(expression)`、;尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型) 28. 避免使用 handles(包括 引用、指针、迭代器)指向对象内部(以增加封装性、使 const 成员函数的行为更像 const、降低 “虚吊号码牌”(dangling handles,如悬空指针等)的可能性) 29. 为 “异常安全” 而努力是值得的(异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏,分为三种可能的保证:基本型、强列型、不抛异常型) 30. 透彻了解 inlining 的里里外外(inlining 在大多数 C++ 程序中是编译期的行为;inline 函数是否真正 inline,取决于编译器;大部分编译器拒绝太过复杂(如带有循环或递归)的函数 inlining,而所有对 virtual 函数的调用(除非是最平淡无奇的)也都会使 inlining 落空;inline 造成的代码膨胀可能带来效率损失;inline 函数无法随着程序库的升级而升级) 31. 将文件间的编译依存关系降至最低(如果使用 object references 或 object pointers 可以完成任务,就不要使用 objects;如果能够,尽量以 class 声明式替换 class 定义式;为声明式和定义式提供不同的头文件) 32. 确定你的 public 继承塑模出 is-a(是一种)关系(适用于 base classes 身上的每一件事情一定适用于 derived classes 身上,因为每一个 derived class 对象也都是一个 base class 对象) 33. 避免遮掩继承而来的名字(可使用 using 声明式或转交函数(forwarding functions)来让被遮掩的名字再见天日) 34. 区分接口继承和实现继承(在 public 继承之下,derived classes 总是继承 base class 的接口;pure virtual 函数只具体指定接口继承;非纯 impure virtual 函数具体指定接口继承及缺省实现继承;non-virtual 函数具体指定接口继承以及强制性实现继承) 35. 考虑 virtual 函数以外的其他选择(如 Template Method 设计模式的 non-virtual interface(NVI)手法,将 virtual 函数替换为 “函数指针成员变量”,以 `tr1::function` 成员变量替换 virtual 函数,将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数) 36. 绝不重新定义继承而来的 non-virtual 函数 37. 绝不重新定义继承而来的缺省参数值,因为缺省参数值是静态绑定(statically bound),而 virtual 函数却是动态绑定(dynamically bound) 38. 通过复合塑模 has-a(有一个)或 “根据某物实现出”(在应用域(application domain),复合意味 has-a(有一个);在实现域(implementation domain),复合意味着 is-implemented-in-terms-of(根据某物实现出)) 39. 明智而审慎地使用 private 继承(private 继承意味着 is-implemented-in-terms-of(根据某物实现出),尽可能使用复合,当 derived class 需要访问 protected base class 的成员,或需要重新定义继承而来的时候 virtual 函数,或需要 empty base 最优化时,才使用 private 继承) 40. 明智而审慎地使用多重继承(多继承比单一继承复杂,可能导致新的歧义性,以及对 virtual 继承的需要,但确有正当用途,如 “public 继承某个 interface class” 和 “private 继承某个协助实现的 class”;virtual 继承可解决多继承下菱形继承的二义性问题,但会增加大小、速度、初始化及赋值的复杂度等等成本) 41. 了解隐式接口和编译期多态(class 和 templates 都支持接口(interfaces)和多态(polymorphism);class 的接口是以签名为中心的显式的(explicit),多态则是通过 virtual 函数发生于运行期;template 的接口是奠基于有效表达式的隐式的(implicit),多态则是通过 template 具现化和函数重载解析(function overloading resolution)发生于编译期) 42. 了解 typename 的双重意义(声明 template 类型参数是,前缀关键字 class 和 typename 的意义完全相同;请使用关键字 typename 标识嵌套从属类型名称,但不得在基类列(base class lists)或成员初值列(member initialization list)内以它作为 base class 修饰符) 43. 学习处理模板化基类内的名称(可在 derived class templates 内通过 `this->` 指涉 base class templates 内的成员名称,或藉由一个明白写出的 “base class 资格修饰符” 完成) 44. 将与参数无关的代码抽离 templates(因类型模板参数(non-type template parameters)而造成代码膨胀往往可以通过函数参数或 class 成员变量替换 template 参数来消除;因类型参数(type parameters)而造成的代码膨胀往往可以通过让带有完全相同二进制表述(binary representations)的实现类型(instantiation types)共享实现码) 45. 运用成员函数模板接受所有兼容类型(请使用成员函数模板(member function templates)生成 “可接受所有兼容类型” 的函数;声明 member templates 用于 “泛化 copy 构造” 或 “泛化 assignment 操作” 时还需要声明正常的 copy 构造函数和 copy assignment 操作符) 46. 需要类型转换时请为模板定义非成员函数(当我们编写一个 class template,而它所提供之 “与此 template 相关的” 函数支持 “所有参数之隐式类型转换” 时,请将那些函数定义为 “class template 内部的 friend 函数”) 47. 请使用 traits classes 表现类型信息(traits classes 通过 templates 和 “templates 特化” 使得 “类型相关信息” 在编译期可用,通过重载技术(overloading)实现在编译期对类型执行 if...else 测试) 48. 认识 template 元编程(模板元编程(TMP,template metaprogramming)可将工作由运行期移往编译期,因此得以实现早期错误侦测和更高的执行效率;TMP 可被用来生成 “给予政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码) 49. 了解 new-handler 的行为(set\_new\_handler 允许客户指定一个在内存分配无法获得满足时被调用的函数;nothrow new 是一个颇具局限的工具,因为它只适用于内存分配(operator new),后继的构造函数调用还是可能抛出异常) 50. 了解 new 和 delete 的合理替换时机(为了检测运用错误、收集动态分配内存之使用统计信息、增加分配和归还速度、降低缺省内存管理器带来的空间额外开销、弥补缺省分配器中的非最佳齐位、将相关对象成簇集中、获得非传统的行为) 51. 编写 new 和 delete 时需固守常规(operator new 应该内涵一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就应该调用 new-handler,它也应该有能力处理 0 bytes 申请,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”;operator delete 应该在收到 null 指针时不做任何事,class 专属版本则还应该处理 “比正确大小更大的(错误)申请”) 52. 写了 placement new 也要写 placement delete(当你写一个 placement operator new,请确定也写出了对应的 placement operator delete,否则可能会发生隐微而时断时续的内存泄漏;当你声明 placement new 和 placement delete,请确定不要无意识(非故意)地遮掩了它们地正常版本) 53. 不要轻忽编译器的警告 54. 让自己熟悉包括 TR1 在内的标准程序库(TR1,C++ Technical Report 1,C++11 标准的草稿文件) 55. 让自己熟悉 Boost(准标准库) ### More Effective c++ 1. 仔细区别 pointers 和 references(当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由 pointers 达成,你就应该选择 references;任何其他时候,请采用 pointers) 2. 最好使用 C++ 转型操作符(`static_cast`、`const_cast`、`dynamic_cast`、`reinterpret_cast`) 3. 绝不要以多态(polymorphically)方式处理数组(多态(polymorphism)和指针算术不能混用;数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用) 4. 非必要不提供 default constructor(避免对象中的字段被无意义地初始化) 5. 对定制的 “类型转换函数” 保持警觉(单自变量 constructors 可通过简易法(explicit 关键字)或代理类(proxy classes)来避免编译器误用;隐式类型转换操作符可改为显式的 member function 来避免非预期行为) 6. 区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式(前置式累加后取出,返回一个 reference;后置式取出后累加,返回一个 const 对象;处理用户定制类型时,应该尽可能使用前置式 increment;后置式的实现应以其前置式兄弟为基础) 7. 千万不要重载 `&&`,`||` 和 `,` 操作符(`&&` 与 `||` 的重载会用 “函数调用语义” 取代 “骤死式语义”;`,` 的重载导致不能保证左侧表达式一定比右侧表达式更早被评估) 8. 了解各种不同意义的 new 和 delete(`new operator`、`operator new`、`placement new`、`operator new[]`;`delete operator`、`operator delete`、`destructor`、`operator delete[]`) 9. 利用 destructors 避免泄漏资源(在 destructors 释放资源可以避免异常时的资源泄漏) 10. 在 constructors 内阻止资源泄漏(由于 C++ 只会析构已构造完成的对象,因此在构造函数可以使用 try...catch 或者 auto_ptr(以及与之相似的 classes) 处理异常时资源泄露问题) 11. 禁止异常流出 destructors 之外(原因:一、避免 terminate 函数在 exception 传播过程的栈展开(stack-unwinding)机制种被调用;二、协助确保 destructors 完成其应该完成的所有事情) 12. 了解 “抛出一个 exception” 与 “传递一个参数” 或 “调用一个虚函数” 之间的差异(第一,exception objects 总是会被复制(by pointer 除外),如果以 by value 方式捕捉甚至被复制两次,而传递给函数参数的对象则不一定得复制;第二,“被抛出成为 exceptions” 的对象,其被允许的类型转换动作比 “被传递到函数去” 的对象少;第三,catch 子句以其 “出现于源代码的顺序” 被编译器检验对比,其中第一个匹配成功者便执行,而调用一个虚函数,被选中执行的是那个 “与对象类型最佳吻合” 的函数) 13. 以 by reference 方式捕获 exceptions(可避免对象删除问题、exception objects 的切割问题,可保留捕捉标准 exceptions 的能力,可约束 exception object 需要复制的次数) 14. 明智运用 exception specifications(exception specifications 对 “函数希望抛出什么样的 exceptions” 提供了卓越的说明;也有一些缺点,包括编译器只对它们做局部性检验而很容易不经意地违反,与可能会妨碍更上层的 exception 处理函数处理未预期的 exceptions) 15. 了解异常处理的成本(粗略估计,如果使用 try 语句块,代码大约整体膨胀 5%-10%,执行速度亦大约下降这个数;因此请将你对 try 语句块和 exception specifications 的使用限制于非用不可的地点,并且在真正异常的情况下才抛出 exceptions) 16. 谨记 80-20 法则(软件的整体性能几乎总是由其构成要素(代码)的一小部分决定的,可使用程序分析器(program profiler)识别出消耗资源的代码) 17. 考虑使用 lazy evaluation(缓式评估)(可应用于:Reference Counting(引用计数)来避免非必要的对象复制、区分 operator[] 的读和写动作来做不同的事情、Lazy Fetching(缓式取出)来避免非必要的数据库读取动作、Lazy Expression Evaluation(表达式缓评估)来避免非必要的数值计算动作) 18. 分期摊还预期的计算成本(当你必须支持某些运算而其结构几乎总是被需要,或其结果常常被多次需要的时候,over-eager evaluation(超急评估)可以改善程序效率) ### Google C++ Style Guide * 英文:[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) * 中文:[C++ 风格指南](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/) ### 其他 * [Bjarne Stroustrup 的常见问题](http://www.stroustrup.com/bs_faq.html) * [Bjarne Stroustrup 的 C++ 风格和技巧常见问题](http://www.stroustrup.com/bs_faq2.html) ## 📦 STL ### STL 索引 [STL 方法含义索引](https://github.com/huihut/interview/tree/master/STL) ### STL 容器 容器 | 底层数据结构 | 时间复杂度 | 有无序 | 可不可重复 | 其他 ---|---|---|---|---|--- [array](https://github.com/huihut/interview/tree/master/STL#array)|数组|随机读改 O(1)|无序|可重复|支持随机访问 [vector](https://github.com/huihut/interview/tree/master/STL#vector)|数组|随机读改、尾部插入、尾部删除 O(1)
头部插入、头部删除 O(n)|无序|可重复|支持随机访问 [deque](https://github.com/huihut/interview/tree/master/STL#deque)|双端队列|头尾插入、头尾删除 O(1)|无序|可重复|一个中央控制器 + 多个缓冲区,支持首尾快速增删,支持随机访问 [forward_list](https://github.com/huihut/interview/tree/master/STL#forward_list)|单向链表|插入、删除 O(1)|无序|可重复|不支持随机访问 [list](https://github.com/huihut/interview/tree/master/STL#list)|双向链表|插入、删除 O(1)|无序|可重复|不支持随机访问 [stack](https://github.com/huihut/interview/tree/master/STL#stack)|deque / list|顶部插入、顶部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 [queue](https://github.com/huihut/interview/tree/master/STL#queue)|deque / list|尾部插入、头部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 [priority_queue](https://github.com/huihut/interview/tree/master/STL#priority_queue)|vector + max-heap|插入、删除 O(log2n)|有序|可重复|vector容器+heap处理规则 [set](https://github.com/huihut/interview/tree/master/STL#set)|红黑树|插入、删除、查找 O(log2n)|有序|不可重复| [multiset](https://github.com/huihut/interview/tree/master/STL#multiset)|红黑树|插入、删除、查找 O(log2n)|有序|可重复| [map](https://github.com/huihut/interview/tree/master/STL#map)|红黑树|插入、删除、查找 O(log2n)|有序|不可重复| [multimap](https://github.com/huihut/interview/tree/master/STL#multimap)|红黑树|插入、删除、查找 O(log2n)|有序|可重复| [unordered_set](https://github.com/huihut/interview/tree/master/STL#unordered_set)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复| [unordered_multiset](https://github.com/huihut/interview/tree/master/STL#unordered_multiset)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复| [unordered_map](https://github.com/huihut/interview/tree/master/STL#unordered_map)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复| [unordered_multimap](https://github.com/huihut/interview/tree/master/STL#unordered_multimap)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复| ### STL 算法 算法 | 底层算法 | 时间复杂度 | 可不可重复 ---|---|---|--- [find](http://www.cplusplus.com/reference/algorithm/find/)|顺序查找|O(n)|可重复 [sort](https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808)|[内省排序](https://en.wikipedia.org/wiki/Introsort)|O(n*log2n)|可重复 ## 〽️ 数据结构 ### 顺序结构 #### 顺序栈(Sequence Stack) [SqStack.cpp](DataStructure/SqStack.cpp) 顺序栈数据结构和图片 ```cpp typedef struct { ElemType *elem; int top; int size; int increment; } SqStack; ``` ![](https://gitee.com/huihut/interview/raw/master/images/SqStack.png) #### 队列(Sequence Queue) 队列数据结构 ```cpp typedef struct { ElemType * elem; int front; int rear; int maxSize; }SqQueue; ``` ##### 非循环队列 非循环队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqQueue.png) `SqQueue.rear++` ##### 循环队列 循环队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqLoopStack.png) `SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize` #### 顺序表(Sequence List) [SqList.cpp](DataStructure/SqList.cpp) 顺序表数据结构和图片 ```cpp typedef struct { ElemType *elem; int length; int size; int increment; } SqList; ``` ![](https://gitee.com/huihut/interview/raw/master/images/SqList.png) ### 链式结构 [LinkList.cpp](DataStructure/LinkList.cpp) [LinkList_with_head.cpp](DataStructure/LinkList_with_head.cpp) 链式数据结构 ```cpp typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; ``` #### 链队列(Link Queue) 链队列图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkQueue.png) #### 线性表的链式表示 ##### 单链表(Link List) 单链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkList.png) ##### 双向链表(Du-Link-List) 双向链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/DuLinkList.png) ##### 循环链表(Cir-Link-List) 循环链表图片 ![](https://gitee.com/huihut/interview/raw/master/images/CirLinkList.png) ### 哈希表 [HashTable.cpp](DataStructure/HashTable.cpp) #### 概念 哈希函数:`H(key): K -> D , key ∈ K` #### 构造方法 * 直接定址法 * 除留余数法 * 数字分析法 * 折叠法 * 平方取中法 #### 冲突处理方法 * 链地址法:key 相同的用单链表链接 * 开放定址法 * 线性探测法:key 相同 -> 放到 key 的下一个位置,`Hi = (H(key) + i) % m` * 二次探测法:key 相同 -> 放到 `Di = 1^2, -1^2, ..., ±(k)^2,(k<=m/2)` * 随机探测法:`H = (H(key) + 伪随机数) % m` #### 线性探测的哈希表数据结构 线性探测的哈希表数据结构和图片 ```cpp typedef char KeyType; typedef struct { KeyType key; }RcdType; typedef struct { RcdType *rcd; int size; int count; bool *tag; }HashTable; ``` ![](https://gitee.com/huihut/interview/raw/master/images/HashTable.png) ### 递归 #### 概念 函数直接或间接地调用自身 #### 递归与分治 * 分治法 * 问题的分解 * 问题规模的分解 * 折半查找(递归) * 归并排序(递归) * 快速排序(递归) #### 递归与迭代 * 迭代:反复利用变量旧值推出新值 * 折半查找(迭代) * 归并排序(迭代) #### 广义表 ##### 头尾链表存储表示 广义表的头尾链表存储表示和图片 ```cpp // 广义表的头尾链表存储表示 typedef enum {ATOM, LIST} ElemTag; // ATOM==0:原子,LIST==1:子表 typedef struct GLNode { ElemTag tag; // 公共部分,用于区分原子结点和表结点 union { // 原子结点和表结点的联合部分 AtomType atom; // atom 是原子结点的值域,AtomType 由用户定义 struct { struct GLNode *hp, *tp; } ptr; // ptr 是表结点的指针域,prt.hp 和 ptr.tp 分别指向表头和表尾 } a; } *GList, GLNode; ``` ![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList1.png) ##### 扩展线性链表存储表示 扩展线性链表存储表示和图片 ```cpp // 广义表的扩展线性链表存储表示 typedef enum {ATOM, LIST} ElemTag; // ATOM==0:原子,LIST==1:子表 typedef struct GLNode1 { ElemTag tag; // 公共部分,用于区分原子结点和表结点 union { // 原子结点和表结点的联合部分 AtomType atom; // 原子结点的值域 struct GLNode1 *hp; // 表结点的表头指针 } a; struct GLNode1 *tp; // 相当于线性链表的 next,指向下一个元素结点 } *GList1, GLNode1; ``` ![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList2.png) ### 二叉树 [BinaryTree.cpp](DataStructure/BinaryTree.cpp) #### 性质 1. 非空二叉树第 i 层最多 2(i-1) 个结点 (i >= 1) 2. 深度为 k 的二叉树最多 2k - 1 个结点 (k >= 1) 3. 度为 0 的结点数为 n0,度为 2 的结点数为 n2,则 n0 = n2 + 1 4. 有 n 个结点的完全二叉树深度 k = ⌊ log2(n) ⌋ + 1 5. 对于含 n 个结点的完全二叉树中编号为 i (1 <= i <= n) 的结点 1. 若 i = 1,为根,否则双亲为 ⌊ i / 2 ⌋ 2. 若 2i > n,则 i 结点没有左孩子,否则孩子编号为 2i 3. 若 2i + 1 > n,则 i 结点没有右孩子,否则孩子编号为 2i + 1 #### 存储结构 二叉树数据结构 ```cpp typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; ``` ##### 顺序存储 二叉树顺序存储图片 ![](https://gitee.com/huihut/interview/raw/master/images/SqBinaryTree.png) ##### 链式存储 二叉树链式存储图片 ![](https://gitee.com/huihut/interview/raw/master/images/LinkBinaryTree.png) #### 遍历方式 * 先序遍历 * 中序遍历 * 后续遍历 * 层次遍历 #### 分类 * 满二叉树 * 完全二叉树(堆) * 大顶堆:根 >= 左 && 根 >= 右 * 小顶堆:根 <= 左 && 根 <= 右 * 二叉查找树(二叉排序树):左 < 根 < 右 * 平衡二叉树(AVL树):| 左子树树高 - 右子树树高 | <= 1 * 最小失衡树:平衡二叉树插入新结点导致失衡的子树:调整: * LL型:根的左孩子右旋 * RR型:根的右孩子左旋 * LR型:根的左孩子左旋,再右旋 * RL型:右孩子的左子树,先右旋,再左旋 ### 其他树及森林 #### 树的存储结构 * 双亲表示法 * 双亲孩子表示法 * 孩子兄弟表示法 #### 并查集 一种不相交的子集所构成的集合 S = {S1, S2, ..., Sn} #### 平衡二叉树(AVL树) ##### 性质 * | 左子树树高 - 右子树树高 | <= 1 * 平衡二叉树必定是二叉搜索树,反之则不一定 * 最小二叉平衡树的节点的公式:`F(n)=F(n-1)+F(n-2)+1` (1 是根节点,F(n-1) 是左子树的节点数量,F(n-2) 是右子树的节点数量) 平衡二叉树图片 ![](https://gitee.com/huihut/interview/raw/master/images/Self-balancingBinarySearchTree.png) ##### 最小失衡树 平衡二叉树插入新结点导致失衡的子树 调整: * LL 型:根的左孩子右旋 * RR 型:根的右孩子左旋 * LR 型:根的左孩子左旋,再右旋 * RL 型:右孩子的左子树,先右旋,再左旋 #### 红黑树 [RedBlackTree.cpp](DataStructure/RedBlackTree.cpp) ##### 红黑树的特征是什么? 1. 节点是红色或黑色。 2. 根是黑色。 3. 所有叶子都是黑色(叶子是 NIL 节点)。 4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)(新增节点的父节点必须相同) 5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。(新增节点必须为红) ##### 调整 1. 变色 2. 左旋 3. 右旋 ##### 应用 * 关联数组:如 STL 中的 map、set ##### 红黑树、B 树、B+ 树的区别? * 红黑树的深度比较大,而 B 树和 B+ 树的深度则相对要小一些 * B+ 树则将数据都保存在叶子节点,同时通过链表的形式将他们连接在一起。 #### B 树(B-tree)、B+ 树(B+-tree) B 树、B+ 树图片 ![B 树(B-tree)、B+ 树(B+-tree)](https://i.stack.imgur.com/l6UyF.png) ##### 特点 * 一般化的二叉查找树(binary search tree) * “矮胖”,内部(非叶子)节点可以拥有可变数量的子节点(数量范围预先定义好) ##### 应用 * 大部分文件系统、数据库系统都采用B树、B+树作为索引结构 ##### 区别 * B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中。 * B+树中所有叶子节点都是通过指针连接在一起,而B树不会。 ##### B树的优点 对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。 ##### B+树的优点 * 非叶子节点不会带上 ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。 * 叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。 > B 树、B+ 树区别来自:[differences-between-b-trees-and-b-trees](https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees)、[B树和B+树的区别](https://www.cnblogs.com/ivictor/p/5849061.html) #### 八叉树 八叉树图片 ![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png) 八叉树(octree),或称八元树,是一种用于描述三维空间(划分空间)的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。 ##### 用途 * 三维计算机图形 * 最邻近搜索 ## ⚡️ 算法 ### 排序 排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 ---|---|---|---|--- [冒泡排序](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|稳定 [选择排序](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|数组不稳定、链表稳定 [插入排序](Algorithm/InsertSort.h) | O(n2)|O(n2)|O(1)|稳定 [快速排序](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n) | 不稳定 [堆排序](Algorithm/HeapSort.cpp) | O(n*log2n)|O(n*log2n)|O(1)|不稳定 [归并排序](Algorithm/MergeSort.h) | O(n*log2n) | O(n*log2n)|O(n)|稳定 [希尔排序](Algorithm/ShellSort.h) | O(n*log2n)|O(n2)|O(1)|不稳定 [计数排序](Algorithm/CountSort.cpp) | O(n+m)|O(n+m)|O(n+m)|稳定 [桶排序](Algorithm/BucketSort.cpp) | O(n)|O(n)|O(m)|稳定 [基数排序](Algorithm/RadixSort.h) | O(k*n)|O(n2)| |稳定 > * 均按从小到大排列 > * k:代表数值中的 “数位” 个数 > * n:代表数据规模 > * m:代表数据的最大值减最小值 > * 来自:[wikipedia . 排序算法](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95) ### 查找 查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件 ---|---|---|--- [顺序查找](Algorithm/SequentialSearch.h) | O(n) | O(1) | 无序或有序 [二分查找(折半查找)](Algorithm/BinarySearch.h) | O(log2n)| O(1) | 有序 [插值查找](Algorithm/InsertionSearch.h) | O(log2(log2n)) | O(1) | 有序 [斐波那契查找](Algorithm/FibonacciSearch.cpp) | O(log2n) | O(1) | 有序 [哈希查找](DataStructure/HashTable.cpp) | O(1) | O(n) | 无序或有序 [二叉查找树(二叉搜索树查找)](Algorithm/BSTSearch.h) |O(log2n) | | [红黑树](DataStructure/RedBlackTree.cpp) |O(log2n) | | 2-3树 | O(log2n - log3n) | | B树/B+树 |O(log2n) | | ### 图搜索算法 图搜索算法 |数据结构| 遍历时间复杂度 | 空间复杂度 ---|---|---|--- [BFS广度优先搜索](https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵
邻接链表|O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) [DFS深度优先搜索](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵
邻接链表|O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) ### 其他算法 算法 |思想| 应用 ---|---|--- [分治法](https://zh.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95)|把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并|[循环赛日程安排问题](https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem)、排序算法(快速排序、归并排序) [动态规划](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92)|通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,适用于有重叠子问题和最优子结构性质的问题|[背包问题](https://github.com/huihut/interview/tree/master/Problems/KnapsackProblem)、斐波那契数列 [贪心法](https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95)|一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法|旅行推销员问题(最短路径问题)、最小生成树、哈夫曼编码 ## ❓ Problems ### Single Problem * [Chessboard Coverage Problem(棋盘覆盖问题)](Problems/ChessboardCoverageProblem) * [Knapsack Problem(背包问题)](Problems/KnapsackProblem) * [Neumann Neighbor Problem(冯诺依曼邻居问题)](Problems/NeumannNeighborProblem) * [Round Robin Problem(循环赛日程安排问题)](Problems/RoundRobinProblem) * [Tubing Problem(输油管道问题)](Problems/TubingProblem) ### Leetcode Problems * [Github . haoel/leetcode](https://github.com/haoel/leetcode) * [Github . pezy/LeetCode](https://github.com/pezy/LeetCode) ### 剑指 Offer * [Github . zhedahht/CodingInterviewChinese2](https://github.com/zhedahht/CodingInterviewChinese2) * [Github . gatieme/CodingInterviews](https://github.com/gatieme/CodingInterviews) ### Cracking the Coding Interview 程序员面试金典 * [Github . careercup/ctci](https://github.com/careercup/ctci) * [牛客网 . 程序员面试金典](https://www.nowcoder.com/ta/cracking-the-coding-interview) ### 牛客网 * [牛客网 . 在线编程专题](https://www.nowcoder.com/activity/oj) ## 💻 操作系统 ### 进程与线程 对于有线程系统: * 进程是资源分配的独立单位 * 线程是资源调度的独立单位 对于无线程系统: * 进程是资源调度、分配的独立单位 #### 进程之间的通信方式以及优缺点 * 管道(PIPE) * 有名管道(命名管道):一种先进先出的通信方式,它允许无亲缘关系进程间的通信 * 优点:可以实现任意关系的进程间的通信 * 缺点: 1. 长期存于系统中,使用不当容易出错 2. 缓冲区有限 * 无名管道(匿名管道):一种单工先进先出的通信方式,只能在具有亲缘关系的进程间使用(父子进程) * 优点:简单方便 * 缺点: 1. 局限于单向通信 2. 只能创建在它的进程以及其有亲缘关系的进程之间 3. 缓冲区有限 * 信号量(Semaphore):一个计数器,可以用来控制多个线程对共享资源的访问 * 优点:可以同步进程 * 缺点:信号量有限 * 信号(Signal):一种比较复杂的通信方式,用于通知接收进程某个事件已经发生 * 消息队列(Message Queue):是消息的链表,存放在内核中并由消息队列标识符标识 * 优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便 * 缺点:信息的复制需要额外消耗 CPU 的时间,不适宜于信息量大或操作频繁的场合 * 共享内存(Shared Memory):映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问 * 优点:无须复制,快捷,信息量大 * 缺点: 1. 通信是通过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,因此进程间的读写操作的同步问题 2. 利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信 * 套接字(Socket):可用于不同计算机间的进程通信 * 优点: 1. 传输数据为字节级,传输数据可自定义,数据量小效率高 2. 传输数据时间短,性能高 3. 适合于客户端和服务器端之间信息实时交互 4. 可以加密,数据安全性强 * 缺点:需对传输的数据进行解析,转化成应用级的数据。 #### 线程之间的通信方式 * 锁机制:包括互斥锁/量(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)、条件变量(condition) * 互斥锁/量(mutex):提供了以排他方式防止数据结构被并发修改的方法。 * 读写锁(reader-writer lock):允许多个线程同时读共享数据,而对写操作是互斥的。 * 自旋锁(spin lock)与互斥锁类似,都是为了保护共享资源。互斥锁是当资源被占用,申请者进入睡眠状态;而自旋锁则循环检测保持者是否已经释放锁。 * 条件变量(condition):可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。 * 信号量机制(Semaphore) * 无名线程信号量 * 命名线程信号量 * 信号机制(Signal):类似进程间的信号处理 * 屏障(barrier):屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。 线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制 > 进程之间的通信方式以及优缺点来源于:[进程线程面试题总结](http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977) #### 进程之间私有和共享的资源 * 私有:地址空间、堆、全局变量、栈、寄存器 * 共享:代码段,公共数据,进程目录,进程 ID #### 线程之间私有和共享的资源 * 私有:线程栈,寄存器,程序计数器 * 共享:堆,地址空间,全局变量,静态变量 #### 多进程与多线程间的对比、优劣与选择 ##### 对比 对比维度 | 多进程 | 多线程 | 总结 ---|---|---|--- 数据共享、同步|数据共享复杂,需要用 IPC;数据是分开的,同步简单|因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂|各有优势 内存、CPU|占用内存多,切换复杂,CPU 利用率低|占用内存少,切换简单,CPU 利用率高|线程占优 创建销毁、切换|创建销毁、切换复杂,速度慢|创建销毁、切换简单,速度很快|线程占优 编程、调试|编程简单,调试简单|编程复杂,调试复杂|进程占优 可靠性|进程间不会互相影响|一个线程挂掉将导致整个进程挂掉|进程占优 分布式|适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单|适应于多核分布式|进程占优 ##### 优劣 优劣|多进程|多线程 ---|---|--- 优点|编程、调试简单,可靠性较高|创建、销毁、切换速度快,内存、资源占用小 缺点|创建、销毁、切换速度慢,内存、资源占用大|编程、调试复杂,可靠性较差 ##### 选择 * 需要频繁创建销毁的优先用线程 * 需要进行大量计算的优先使用线程 * 强相关的处理用线程,弱相关的处理用进程 * 可能要扩展到多机分布的用进程,多核分布的用线程 * 都满足需求的情况下,用你最熟悉、最拿手的方式 > 多进程与多线程间的对比、优劣与选择来自:[多线程还是多进程的选择及区别](https://blog.csdn.net/lishenglong666/article/details/8557215) ### Linux 内核的同步方式 #### 原因 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。 #### 同步方式 * 原子操作 * 信号量(semaphore) * 读写信号量(rw_semaphore) * 自旋锁(spinlock) * 大内核锁(BKL,Big Kernel Lock) * 读写锁(rwlock) * 大读者锁(brlock-Big Reader Lock) * 读-拷贝修改(RCU,Read-Copy Update) * 顺序锁(seqlock) > 来自:[Linux 内核的同步机制,第 1 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part1/)、[Linux 内核的同步机制,第 2 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part2/) ### 死锁 #### 原因 * 系统资源不足 * 资源分配不当 * 进程运行推进顺序不合适 #### 产生条件 * 互斥 * 请求和保持 * 不剥夺 * 环路 #### 预防 * 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。 * 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。 * 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。 * 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。 * 有序资源分配法 * 银行家算法 ### 文件系统 * Windows:FCB 表 + FAT + 位图 * Unix:inode + 混合索引 + 成组链接 ### 主机字节序与网络字节序 #### 主机字节序(CPU 字节序) ##### 概念 主机字节序又叫 CPU 字节序,其不是由操作系统决定的,而是由 CPU 指令集架构决定的。主机字节序分为两种: * 大端字节序(Big Endian):高序字节存储在低位地址,低序字节存储在高位地址 * 小端字节序(Little Endian):高序字节存储在高位地址,低序字节存储在低位地址 ##### 存储方式 32 位整数 `0x12345678` 是从起始位置为 `0x00` 的地址开始存放,则: 内存地址 | 0x00 | 0x01 | 0x02 | 0x03 ---|---|---|---|--- 大端|12|34|56|78 小端|78|56|34|12 大端小端图片 ![大端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Big-Endian.svg.png) ![小端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Little-Endian.svg.png) ##### 判断大端小端 判断大端小端 可以这样判断自己 CPU 字节序是大端还是小端: ```cpp #include using namespace std; int main() { int i = 0x12345678; if (*((char*)&i) == 0x12) cout << "大端" << endl; else cout << "小端" << endl; return 0; } ``` ##### 各架构处理器的字节序 * x86(Intel、AMD)、MOS Technology 6502、Z80、VAX、PDP-11 等处理器为小端序; * Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除 V9 外)等处理器为大端序; * ARM(默认小端序)、PowerPC(除 PowerPC 970 外)、DEC Alpha、SPARC V9、MIPS、PA-RISC 及 IA64 的字节序是可配置的。 #### 网络字节序 网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。 网络字节顺序采用:大端(Big Endian)排列方式。 ### 页面置换算法 在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。 #### 分类 * 全局置换:在整个内存空间置换 * 局部置换:在本进程中进行置换 #### 算法 全局: * 工作集算法 * 缺页率置换算法 局部: * 最佳置换算法(OPT) * 先进先出置换算法(FIFO) * 最近最久未使用(LRU)算法 * 时钟(Clock)置换算法 ## ☁️ 计算机网络 > 本节部分知识点来自《计算机网络(第 7 版)》 计算机网络体系结构: ![计算机网络体系结构](https://gitee.com/huihut/interview/raw/master/images/计算机网络体系结构.png) ### 各层作用及协议 分层 | 作用 | 协议 ---|---|--- 物理层 | 通过媒介传输比特,确定机械及电气规范(比特 Bit) | RJ45、CLOCK、IEEE802.3(中继器,集线器) 数据链路层|将比特组装成帧和点到点的传递(帧 Frame)| PPP、FR、HDLC、VLAN、MAC(网桥,交换机) 网络层|负责数据包从源到宿的传递和网际互连(包 Packet)|IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP(路由器) 运输层|提供端到端的可靠报文传递和错误恢复( 段Segment)|TCP、UDP、SPX 会话层|建立、管理和终止会话(会话协议数据单元 SPDU)|NFS、SQL、NETBIOS、RPC 表示层|对数据进行翻译、加密和压缩(表示协议数据单元 PPDU)|JPEG、MPEG、ASII 应用层|允许访问OSI环境的手段(应用协议数据单元 APDU)|FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS ### 物理层 * 传输数据的单位:比特 * 数据传输系统:源系统(源点、发送器) --> 传输系统 --> 目的系统(接收器、终点) 通道: * 单向通道(单工通道):只有一个方向通信,没有反方向交互,如广播 * 双向交替通信(半双工通信):通信双方都可发消息,但不能同时发送或接收 * 双向同时通信(全双工通信):通信双方可以同时发送和接收信息 通道复用技术: * 频分复用(FDM,Frequency Division Multiplexing):不同用户在不同频带,所用用户在同样时间占用不同带宽资源 * 时分复用(TDM,Time Division Multiplexing):不同用户在同一时间段的不同时间片,所有用户在不同时间占用同样的频带宽度 * 波分复用(WDM,Wavelength Division Multiplexing):光的频分复用 * 码分复用(CDM,Code Division Multiplexing):不同用户使用不同的码,可以在同样时间使用同样频带通信 ### 数据链路层 主要信道: * 点对点信道 * 广播信道 #### 点对点信道 * 数据单元:帧 三个基本问题: * 封装成帧:把网络层的 IP 数据报封装成帧,`SOH - 数据部分 - EOT` * 透明传输:不管数据部分什么字符,都能传输出去;可以通过字节填充方法解决(冲突字符前加转义字符) * 差错检测:降低误码率(BER,Bit Error Rate),广泛使用循环冗余检测(CRC,Cyclic Redundancy Check) 点对点协议(Point-to-Point Protocol): * 点对点协议(Point-to-Point Protocol):用户计算机和 ISP 通信时所使用的协议 #### 广播信道 广播通信: * 硬件地址(物理地址、MAC 地址) * 单播(unicast)帧(一对一):收到的帧的 MAC 地址与本站的硬件地址相同 * 广播(broadcast)帧(一对全体):发送给本局域网上所有站点的帧 * 多播(multicast)帧(一对多):发送给本局域网上一部分站点的帧 ### 网络层 * IP(Internet Protocol,网际协议)是为计算机网络相互连接进行通信而设计的协议。 * ARP(Address Resolution Protocol,地址解析协议) * ICMP(Internet Control Message Protocol,网际控制报文协议) * IGMP(Internet Group Management Protocol,网际组管理协议) #### IP 网际协议 IP 地址分类: * `IP 地址 ::= {<网络号>,<主机号>}` IP 地址类别 | 网络号 | 网络范围 | 主机号 | IP 地址范围 ---|---|---|---|--- A 类 | 8bit,第一位固定为 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255 B 类 | 16bit,前两位固定为 10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255 C 类 | 24bit,前三位固定为 110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255 D 类 | 前四位固定为 1110,后面为多播地址 E 类 | 前五位固定为 11110,后面保留为今后所用 IP 数据报格式: ![IP 数据报格式](https://gitee.com/huihut/interview/raw/master/images/IP数据报格式.png) #### ICMP 网际控制报文协议 ICMP 报文格式: ![ICMP 报文格式](https://gitee.com/huihut/interview/raw/master/images/ICMP报文格式.png) 应用: * PING(Packet InterNet Groper,分组网间探测)测试两个主机之间的连通性 * TTL(Time To Live,生存时间)该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量 #### 内部网关协议 * RIP(Routing Information Protocol,路由信息协议) * OSPF(Open Shortest Path First,开放最短路径优先) #### 外部网关协议 * BGP(Border Gateway Protocol,边界网关协议) #### IP多播 * IGMP(Internet Group Management Protocol,网际组管理协议) * 多播路由选择协议 #### VPN 和 NAT * VPN(Virtual Private Network,虚拟专用网) * NAT(Network Address Translation,网络地址转换) #### 路由表包含什么? 1. 网络 ID(Network ID, Network number):就是目标地址的网络 ID。 2. 子网掩码(subnet mask):用来判断 IP 所属网络 3. 下一跳地址/接口(Next hop / interface):就是数据在发送到目标地址的旅途中下一站的地址。其中 interface 指向 next hop(即为下一个 route)。一个自治系统(AS, Autonomous system)中的 route 应该包含区域内所有的子网络,而默认网关(Network id: `0.0.0.0`, Netmask: `0.0.0.0`)指向自治系统的出口。 根据应用和执行的不同,路由表可能含有如下附加信息: 1. 花费(Cost):就是数据发送过程中通过路径所需要的花费。 2. 路由的服务质量 3. 路由中需要过滤的出/入连接列表 ### 运输层 协议: * TCP(Transmission Control Protocol,传输控制协议) * UDP(User Datagram Protocol,用户数据报协议) 端口: 应用程序 | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP --- | --- | --- |--- |--- |--- |--- |--- |--- 端口号 | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161 #### TCP * TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,其传输的单位是报文段。 特征: * 面向连接 * 端到端通信 * 可靠交互 * 全双工通信 * 面向字节流 TCP 如何保证可靠传输: * 确认和超时重传 * 数据合理分片和排序 * 流量控制 * 拥塞控制 * 数据校验 TCP 报文结构 ![TCP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP报文.png) TCP 首部 ![TCP 首部](https://gitee.com/huihut/interview/raw/master/images/TCP首部.png) TCP:状态控制码(Code,Control Flag),占 6 比特,含义如下: * URG:紧急比特(urgent),当 `URG=1` 时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。 * ACK:确认比特(Acknowledge)。只有当 `ACK=1` 时确认号字段才有效,代表这个封包为确认封包。当 `ACK=0` 时,确认号无效。 * PSH:(Push function)若为 1 时,代表要求对方立即传送缓冲区内的其他对应封包,而无需等缓冲满了才送。 * RST:复位比特(Reset),当 `RST=1` 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。 * SYN:同步比特(Synchronous),SYN 置为 1,就表示这是一个连接请求或连接接受报文,通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。 * FIN:终止比特(Final),用来释放一个连接。当 `FIN=1` 时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。 #### UDP * UDP(User Datagram Protocol,用户数据报协议)是 OSI(Open System Interconnection 开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,其传输的单位是用户数据报。 特征: * 无连接 * 尽最大努力交付 * 面向报文 * 没有拥塞控制 * 支持一对一、一对多、多对一、多对多的交互通信 * 首部开销小 UDP 报文结构 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/UDP报文.png) UDP 首部 ![UDP 首部](https://gitee.com/huihut/interview/raw/master/images/UDP首部.png) > TCP/UDP 图片来源于: #### TCP 与 UDP 的区别 1. TCP 面向连接,UDP 是无连接的; 2. TCP 提供可靠的服务,也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付 3. TCP 的逻辑通信信道是全双工的可靠信道;UDP 则是不可靠信道 5. 每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信 6. TCP 面向字节流(可能出现黏包问题),实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的(不会出现黏包问题) 7. UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等) 8. TCP 首部开销20字节;UDP 的首部开销小,只有 8 个字节 #### TCP 黏包问题 ##### 原因 TCP 是一个基于字节流的传输服务(UDP 基于报文的),“流” 意味着 TCP 所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况。 ##### 解决 * 发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。 * 包头加上包体长度。包头是定长的 4 个字节,说明了包体的长度。接收对等方先接收包头长度,依据包头长度来接收包体。 * 在数据包之间设置边界,如添加特殊符号 `\r\n` 标记。FTP 协议正是这么做的。但问题在于如果数据正文中也含有 `\r\n`,则会误判为消息的边界。 * 使用更加复杂的应用层协议。 #### TCP 流量控制 ##### 概念 流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。 ##### 方法 利用可变窗口进行流量控制 ![](https://gitee.com/huihut/interview/raw/master/images/利用可变窗口进行流量控制举例.png) #### TCP 拥塞控制 ##### 概念 拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。 ##### 方法 * 慢开始( slow-start ) * 拥塞避免( congestion avoidance ) * 快重传( fast retransmit ) * 快恢复( fast recovery ) TCP的拥塞控制图 ![](https://gitee.com/huihut/interview/raw/master/images/TCP拥塞窗口cwnd在拥塞控制时的变化情况.png) ![](https://gitee.com/huihut/interview/raw/master/images/快重传示意图.png) ![](https://gitee.com/huihut/interview/raw/master/images/TCP的拥塞控制流程图.png) #### TCP 传输连接管理 > 因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述: ##### TCP 三次握手建立连接 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP三次握手建立连接.png) 【TCP 建立连接全过程解释】 1. 客户端发送 SYN 给服务器,说明客户端请求建立连接; 2. 服务端收到客户端发的 SYN,并回复 SYN+ACK 给客户端(同意建立连接); 3. 客户端收到服务端的 SYN+ACK 后,回复 ACK 给服务端(表示客户端收到了服务端发的同意报文); 4. 服务端收到客户端的 ACK,连接已建立,可以数据传输。 ##### TCP 为什么要进行三次握手? 【答案一】因为信道不可靠,而 TCP 想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值。(而 UDP 则不需建立可靠传输,因此 UDP 不需要三次握手。) > [Google Groups . TCP 建立连接为什么是三次握手?{技术}{网络通信}](https://groups.google.com/forum/#!msg/pongba/kF6O7-MFxM0/5S7zIJ4yqKUJ) 【答案二】因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信。 > [知乎 . TCP 为什么是三次握手,而不是两次或四次?](https://www.zhihu.com/question/24853633/answer/115173386) 【答案三】为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。 > [《计算机网络(第 7 版)-谢希仁》](https://gitee.com/huihut/interview/raw/master/images/TCP-transport-connection-management.png) ##### TCP 四次挥手释放连接 ![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP四次挥手释放连接.png) 【TCP 释放连接全过程解释】 1. 客户端发送 FIN 给服务器,说明客户端不必发送数据给服务器了(请求释放从客户端到服务器的连接); 2. 服务器接收到客户端发的 FIN,并回复 ACK 给客户端(同意释放从客户端到服务器的连接); 3. 客户端收到服务端回复的 ACK,此时从客户端到服务器的连接已释放(但服务端到客户端的连接还未释放,并且客户端还可以接收数据); 4. 服务端继续发送之前没发完的数据给客户端; 5. 服务端发送 FIN+ACK 给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放); 6. 客户端收到服务端的 FIN+ACK,并回复 ACK 给服务端(同意释放从服务端到客户端的连接); 7. 服务端收到客户端的 ACK 后,释放从服务端到客户端的连接。 ##### TCP 为什么要进行四次挥手? 【问题一】TCP 为什么要进行四次挥手? / 为什么 TCP 建立连接需要三次,而释放连接则需要四次? 【答案一】因为 TCP 是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向客户端的连接关闭(三四次挥手)。所以 TCP 释放连接时服务器的 ACK 和 FIN 是分开发送的(中间隔着数据传输),而 TCP 建立连接时服务器的 ACK 和 SYN 是一起发送的(第二次握手),所以 TCP 建立连接需要三次,而释放连接则需要四次。 【问题二】为什么 TCP 连接时可以 ACK 和 SYN 一起发送,而释放时则 ACK 和 FIN 分开发送呢?(ACK 和 FIN 分开是指第二次和第三次挥手) 【答案二】因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端 FIN 请求(服务端发送 ACK),然后数据传输,传输完成后,服务端再提出 FIN 请求(服务端发送 FIN);而连接时则没有中间的数据传输,因此连接时可以 ACK 和 SYN 一起发送。 【问题三】为什么客户端释放最后需要 TIME-WAIT 等待 2MSL 呢? 【答案三】 1. 为了保证客户端发送的最后一个 ACK 报文能够到达服务端。若未成功到达,则服务端超时重传 FIN+ACK 报文段,客户端再重传 ACK,并重新计时。 2. 防止已失效的连接请求报文段出现在本连接中。TIME-WAIT 持续 2MSL 可使本连接持续的时间内所产生的所有报文段都从网络中消失,这样可使下次连接中不会出现旧的连接报文段。 #### TCP 有限状态机 TCP 有限状态机图片 ![TCP 的有限状态机](https://gitee.com/huihut/interview/raw/master/images/TCP的有限状态机.png) ### 应用层 #### DNS * DNS(Domain Name System,域名系统)是互联网的一项服务。它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS 使用 TCP 和 UDP 端口 53。当前,对于每一级域名长度的限制是 63 个字符,域名总长度则不能超过 253 个字符。 域名: * `域名 ::= {<三级域名>.<二级域名>.<顶级域名>}`,如:`blog.huihut.com` #### FTP * FTP(File Transfer Protocol,文件传输协议)是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式,使用 TCP 数据报,提供交互式访问,双向传输。 * TFTP(Trivial File Transfer Protocol,简单文件传输协议)一个小且易实现的文件传输协议,也使用客户-服务器方式,使用UDP数据报,只支持文件传输而不支持交互,没有列目录,不能对用户进行身份鉴定 #### TELNET * TELNET 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。 * HTTP(HyperText Transfer Protocol,超文本传输协议)是用于从 WWW(World Wide Web,万维网)服务器传输超文本到本地浏览器的传送协议。 * SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 * Socket 建立网络通信连接至少要一对端口号(Socket)。Socket 本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。 #### WWW * WWW(World Wide Web,环球信息网,万维网)是一个由许多互相链接的超文本组成的系统,通过互联网访问 ##### URL * URL(Uniform Resource Locator,统一资源定位符)是因特网上标准的资源的地址(Address) 标准格式: * `协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]` 完整格式: * `协议类型:[//[访问资源需要的凭证信息@]服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]` > 其中【访问凭证信息@;:端口号;?查询;#片段ID】都属于选填项 > 如:`https://github.com/huihut/interview#cc` ##### HTTP HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。 请求方法 方法 | 意义 --- | --- OPTIONS | 请求一些选项信息,允许客户端查看服务器的性能 GET | 请求指定的页面信息,并返回实体主体 HEAD | 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改 PUT | 从客户端向服务器传送的数据取代指定的文档的内容 DELETE | 请求服务器删除指定的页面 TRACE | 回显服务器收到的请求,主要用于测试或诊断 状态码(Status-Code) * 1xx:表示通知信息,如请求收到了或正在进行处理 * 100 Continue:继续,客户端应继续其请求 * 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议 * 2xx:表示成功,如接收或知道了 * 200 OK: 请求成功 * 3xx:表示重定向,如要完成请求还必须采取进一步的行动 * 301 Moved Permanently: 永久移动。请求的资源已被永久的移动到新 URL,返回信息会包括新的 URL,浏览器会自动定向到新 URL。今后任何新的请求都应使用新的 URL 代替 * 4xx:表示客户的差错,如请求中有错误的语法或不能完成 * 400 Bad Request: 客户端请求的语法错误,服务器无法理解 * 401 Unauthorized: 请求要求用户的身份认证 * 403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求(权限不够) * 404 Not Found: 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 “您所请求的资源无法找到” 的个性页面 * 408 Request Timeout: 服务器等待客户端发送的请求时间过长,超时 * 5xx:表示服务器的差错,如服务器失效无法完成请求 * 500 Internal Server Error: 服务器内部错误,无法完成请求 * 503 Service Unavailable: 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中 * 504 Gateway Timeout: 充当网关或代理的服务器,未及时从远端服务器获取请求 > 更多状态码:[菜鸟教程 . HTTP状态码](http://www.runoob.com/http/http-status-codes.html) ##### 其他协议 * SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是在 Internet 传输 Email 的标准,是一个相对简单的基于文本的协议。在其之上指定了一条消息的一个或多个接收者(在大多数情况下被确认是存在的),然后消息文本会被传输。可以很简单地通过 Telnet 程序来测试一个 SMTP 服务器。SMTP 使用 TCP 端口 25。 * DHCP(Dynamic Host Configuration Protocol,动态主机设置协议)是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途: * 用于内部网络或网络服务供应商自动分配 IP 地址给用户 * 用于内部网络管理员作为对所有电脑作中央管理的手段 * SNMP(Simple Network Management Protocol,简单网络管理协议)构成了互联网工程工作小组(IETF,Internet Engineering Task Force)定义的 Internet 协议族的一部分。该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。 ## 🌩 网络编程 ### Socket > [Linux Socket 编程(不限 Linux)](https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html) ![Socket 客户端服务器通讯](https://gitee.com/huihut/interview/raw/master/images/socket客户端服务器通讯.jpg) #### Socket 中的 read()、write() 函数 ```cpp ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); ``` ##### read() * read 函数是负责从 fd 中读取内容。 * 当读成功时,read 返回实际所读的字节数。 * 如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。 * 如果错误为 EINTR 说明读是由中断引起的;如果是 ECONNREST 表示网络连接出了问题。 ##### write() * write 函数将 buf 中的 nbytes 字节内容写入文件描述符 fd。 * 成功时返回写的字节数。失败时返回 -1,并设置 errno 变量。 * 在网络程序中,当我们向套接字文件描述符写时有俩种可能。 * (1)write 的返回值大于 0,表示写了部分或者是全部的数据。 * (2)返回的值小于 0,此时出现了错误。 * 如果错误为 EINTR 表示在写的时候出现了中断错误;如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。 #### Socket 中 TCP 的三次握手建立连接 我们知道 TCP 建立连接要进行 “三次握手”,即交换三个分组。大致流程如下: 1. 客户端向服务器发送一个 SYN J 2. 服务器向客户端响应一个 SYN K,并对 SYN J 进行确认 ACK J+1 3. 客户端再想服务器发一个确认 ACK K+1 只有就完了三次握手,但是这个三次握手发生在 Socket 的那几个函数中呢?请看下图: ![socket 中发送的 TCP 三次握手](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png) 从图中可以看出: 1. 当客户端调用 connect 时,触发了连接请求,向服务器发送了 SYN J 包,这时 connect 进入阻塞状态; 2. 服务器监听到连接请求,即收到 SYN J 包,调用 accept 函数接收请求向客户端发送 SYN K ,ACK J+1,这时 accept 进入阻塞状态; 3. 客户端收到服务器的 SYN K ,ACK J+1 之后,这时 connect 返回,并对 SYN K 进行确认; 4. 服务器收到 ACK K+1 时,accept 返回,至此三次握手完毕,连接建立。 #### Socket 中 TCP 的四次握手释放连接 上面介绍了 socket 中 TCP 的三次握手建立过程,及其涉及的 socket 函数。现在我们介绍 socket 中的四次握手释放连接的过程,请看下图: ![socket 中发送的 TCP 四次握手](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png) 图示过程如下: 1. 某个应用进程首先调用 close 主动关闭连接,这时 TCP 发送一个 FIN M; 2. 另一端接收到 FIN M 之后,执行被动关闭,对这个 FIN 进行确认。它的接收也作为文件结束符传递给应用进程,因为 FIN 的接收意味着应用进程在相应的连接上再也接收不到额外数据; 3. 一段时间之后,接收到文件结束符的应用进程调用 close 关闭它的 socket。这导致它的 TCP 也发送一个 FIN N; 4. 接收到这个 FIN 的源发送端 TCP 对它进行确认。 这样每个方向上都有一个 FIN 和 ACK。 ## 💾 数据库 > 本节部分知识点来自《数据库系统概论(第 5 版)》 ### 基本概念 * 数据(data):描述事物的符号记录称为数据。 * 数据库(DataBase,DB):是长期存储在计算机内、有组织的、可共享的大量数据的集合,具有永久存储、有组织、可共享三个基本特点。 * 数据库管理系统(DataBase Management System,DBMS):是位于用户与操作系统之间的一层数据管理软件。 * 数据库系统(DataBase System,DBS):是有数据库、数据库管理系统(及其应用开发工具)、应用程序和数据库管理员(DataBase Administrator DBA)组成的存储、管理、处理和维护数据的系统。 * 实体(entity):客观存在并可相互区别的事物称为实体。 * 属性(attribute):实体所具有的某一特性称为属性。 * 码(key):唯一标识实体的属性集称为码。 * 实体型(entity type):用实体名及其属性名集合来抽象和刻画同类实体,称为实体型。 * 实体集(entity set):同一实体型的集合称为实体集。 * 联系(relationship):实体之间的联系通常是指不同实体集之间的联系。 * 模式(schema):模式也称逻辑模式,是数据库全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。 * 外模式(external schema):外模式也称子模式(subschema)或用户模式,它是数据库用户(包括应用程序员和最终用户)能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图,是与某一应用有关的数据的逻辑表示。 * 内模式(internal schema):内模式也称为存储模式(storage schema),一个数据库只有一个内模式。他是数据物理结构和存储方式的描述,是数据库在数据库内部的组织方式。 ### 常用数据模型 * 层次模型(hierarchical model) * 网状模型(network model) * 关系模型(relational model) * 关系(relation):一个关系对应通常说的一张表 * 元组(tuple):表中的一行即为一个元组 * 属性(attribute):表中的一列即为一个属性 * 码(key):表中可以唯一确定一个元组的某个属性组 * 域(domain):一组具有相同数据类型的值的集合 * 分量:元组中的一个属性值 * 关系模式:对关系的描述,一般表示为 `关系名(属性1, 属性2, ..., 属性n)` * 面向对象数据模型(object oriented data model) * 对象关系数据模型(object relational data model) * 半结构化数据模型(semistructure data model) ### 常用 SQL 操作
对象类型 对象 操作类型
数据库模式 模式 CREATE SCHEMA
基本表 CREATE SCHEMAALTER TABLE
视图 CREATE VIEW
索引 CREATE INDEX
数据 基本表和视图 SELECTINSERTUPDATEDELETEREFERENCESALL PRIVILEGES
属性列 SELECTINSERTUPDATEREFERENCESALL PRIVILEGES
> SQL 语法教程:[runoob . SQL 教程](http://www.runoob.com/sql/sql-tutorial.html) ### 关系型数据库 * 基本关系操作:查询(选择、投影、连接(等值连接、自然连接、外连接(左外连接、右外连接))、除、并、差、交、笛卡尔积等)、插入、删除、修改 * 关系模型中的三类完整性约束:实体完整性、参照完整性、用户定义的完整性 #### 索引 * 数据库索引:顺序索引、B+ 树索引、hash 索引 * [MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) ### 数据库完整性 * 数据库的完整性是指数据的正确性和相容性。 * 完整性:为了防止数据库中存在不符合语义(不正确)的数据。 * 安全性:为了保护数据库防止恶意破坏和非法存取。 * 触发器:是用户定义在关系表中的一类由事件驱动的特殊过程。 ### 关系数据理论 * 数据依赖是一个关系内部属性与属性之间的一种约束关系,是通过属性间值的相等与否体现出来的数据间相关联系。 * 最重要的数据依赖:函数依赖、多值依赖。 #### 范式 * 第一范式(1NF):属性(字段)是最小单位不可再分。 * 第二范式(2NF):满足 1NF,每个非主属性完全依赖于主键(消除 1NF 非主属性对码的部分函数依赖)。 * 第三范式(3NF):满足 2NF,任何非主属性不依赖于其他非主属性(消除 2NF 非主属性对码的传递函数依赖)。 * 鲍依斯-科得范式(BCNF):满足 3NF,任何非主属性不能对主键子集依赖(消除 3NF 主属性对码的部分和传递函数依赖)。 * 第四范式(4NF):满足 3NF,属性之间不能有非平凡且非函数依赖的多值依赖(消除 3NF 非平凡且非函数依赖的多值依赖)。 ### 数据库恢复 * 事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。 * 事物的 ACID 特性:原子性、一致性、隔离性、持续性。 * 恢复的实现技术:建立冗余数据 -> 利用冗余数据实施数据库恢复。 * 建立冗余数据常用技术:数据转储(动态海量转储、动态增量转储、静态海量转储、静态增量转储)、登记日志文件。 ### 并发控制 * 事务是并发控制的基本单位。 * 并发操作带来的数据不一致性包括:丢失修改、不可重复读、读 “脏” 数据。 * 并发控制主要技术:封锁、时间戳、乐观控制法、多版本并发控制等。 * 基本封锁类型:排他锁(X 锁 / 写锁)、共享锁(S 锁 / 读锁)。 * 活锁死锁: * 活锁:事务永远处于等待状态,可通过先来先服务的策略避免。 * 死锁:事务永远不能结束 * 预防:一次封锁法、顺序封锁法; * 诊断:超时法、等待图法; * 解除:撤销处理死锁代价最小的事务,并释放此事务的所有的锁,使其他事务得以继续运行下去。 * 可串行化调度:多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同。可串行性时并发事务正确调度的准则。 ## 📏 设计模式 > 各大设计模式例子参考:[CSDN专栏 . C++ 设计模式](https://blog.csdn.net/liang19890820/article/details/66974516) 系列博文 [设计模式工程目录](DesignPattern) ### 单例模式 [单例模式例子](DesignPattern/SingletonPattern) ### 抽象工厂模式 [抽象工厂模式例子](DesignPattern/AbstractFactoryPattern) ### 适配器模式 [适配器模式例子](DesignPattern/AdapterPattern) ### 桥接模式 [桥接模式例子](DesignPattern/BridgePattern) ### 观察者模式 [观察者模式例子](DesignPattern/ObserverPattern) ### 设计模式的六大原则 * 单一职责原则(SRP,Single Responsibility Principle) * 里氏替换原则(LSP,Liskov Substitution Principle) * 依赖倒置原则(DIP,Dependence Inversion Principle) * 接口隔离原则(ISP,Interface Segregation Principle) * 迪米特法则(LoD,Law of Demeter) * 开放封闭原则(OCP,Open Close Principle) ## ⚙️ 链接装载库 > 本节部分知识点来自《程序员的自我修养——链接装载库》 ### 内存、栈、堆 一般应用程序内存空间有如下区域: * 栈:由操作系统自动分配释放,存放函数的参数值、局部变量等的值,用于维护函数调用的上下文 * 堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收,用来容纳应用程序动态分配的内存区域 * 可执行文件映像:存储着可执行文件在内存中的映像,由装载器装载是将可执行文件的内存读取或映射到这里 * 保留区:保留区并不是一个单一的内存区域,而是对内存中受到保护而禁止访问的内存区域的总称,如通常 C 语言讲无效指针赋值为 0(NULL),因此 0 地址正常情况下不可能有效的访问数据 #### 栈 栈保存了一个函数调用所需要的维护信息,常被称为堆栈帧(Stack Frame)或活动记录(Activate Record),一般包含以下几方面: * 函数的返回地址和参数 * 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量 * 保存上下文:包括函数调用前后需要保持不变的寄存器 #### 堆 堆分配算法: * 空闲链表(Free List) * 位图(Bitmap) * 对象池 #### “段错误(segment fault)” 或 “非法操作,该内存地址不能 read/write” 典型的非法指针解引用造成的错误。当指针指向一个不允许读写的内存地址,而程序却试图利用指针来读或写该地址时,会出现这个错误。 普遍原因: * 将指针初始化为 NULL,之后没有给它一个合理的值就开始使用指针 * 没用初始化栈中的指针,指针的值一般会是随机数,之后就直接开始使用指针 ### 编译链接 #### 各平台文件格式 平台 | 可执行文件 | 目标文件 | 动态库/共享对象 | 静态库 ---|---|---|---|--- Windows|exe|obj|dll|lib Unix/Linux|ELF、out|o|so|a Mac|Mach-O|o|dylib、tbd、framework|a、framework #### 编译链接过程 1. 预编译(预编译器处理如 `#include`、`#define` 等预编译指令,生成 `.i` 或 `.ii` 文件) 2. 编译(编译器进行词法分析、语法分析、语义分析、中间代码生成、目标代码生成、优化,生成 `.s` 文件) 3. 汇编(汇编器把汇编码翻译成机器码,生成 `.o` 文件) 4. 链接(连接器进行地址和空间分配、符号决议、重定位,生成 `.out` 文件) > 现在版本 GCC 把预编译和编译合成一步,预编译编译程序 cc1、汇编器 as、连接器 ld > MSVC 编译环境,编译器 cl、连接器 link、可执行文件查看器 dumpbin #### 目标文件 编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。 > 可执行文件(Windows 的 `.exe` 和 Linux 的 `ELF`)、动态链接库(Windows 的 `.dll` 和 Linux 的 `.so`)、静态链接库(Windows 的 `.lib` 和 Linux 的 `.a`)都是按照可执行文件格式存储(Windows 按照 PE-COFF,Linux 按照 ELF) ##### 目标文件格式 * Windows 的 PE(Portable Executable),或称为 PE-COFF,`.obj` 格式 * Linux 的 ELF(Executable Linkable Format),`.o` 格式 * Intel/Microsoft 的 OMF(Object Module Format) * Unix 的 `a.out` 格式 * MS-DOS 的 `.COM` 格式 > PE 和 ELF 都是 COFF(Common File Format)的变种 ##### 目标文件存储结构 段 | 功能 --- | --- File Header | 文件头,描述整个文件的文件属性(包括文件是否可执行、是静态链接或动态连接及入口地址、目标硬件、目标操作系统等) .text section | 代码段,执行语句编译成的机器代码 .data section | 数据段,已初始化的全局变量和局部静态变量 .bss section | BSS 段(Block Started by Symbol),未初始化的全局变量和局部静态变量(因为默认值为 0,所以只是在此预留位置,不占空间) .rodata section | 只读数据段,存放只读数据,一般是程序里面的只读变量(如 const 修饰的变量)和字符串常量 .comment section | 注释信息段,存放编译器版本信息 .note.GNU-stack section | 堆栈提示段 > 其他段略 #### 链接的接口————符号 在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。 如下符号表(Symbol Table): Symbol(符号名) | Symbol Value (地址) --- | --- main| 0x100 Add | 0x123 ... | ... ### Linux 的共享库(Shared Library) Linux 下的共享库就是普通的 ELF 共享对象。 共享库版本更新应该保证二进制接口 ABI(Application Binary Interface)的兼容 #### 命名 `libname.so.x.y.z` * x:主版本号,不同主版本号的库之间不兼容,需要重新编译 * y:次版本号,高版本号向后兼容低版本号 * z:发布版本号,不对接口进行更改,完全兼容 #### 路径 大部分包括 Linux 在内的开源系统遵循 FHS(File Hierarchy Standard)的标准,这标准规定了系统文件如何存放,包括各个目录结构、组织和作用。 * `/lib`:存放系统最关键和最基础的共享库,如动态链接器、C 语言运行库、数学库等 * `/usr/lib`:存放非系统运行时所需要的关键性的库,主要是开发库 * `/usr/local/lib`:存放跟操作系统本身并不十分相关的库,主要是一些第三方应用程序的库 > 动态链接器会在 `/lib`、`/usr/lib` 和由 `/etc/ld.so.conf` 配置文件指定的,目录中查找共享库 #### 环境变量 * `LD_LIBRARY_PATH`:临时改变某个应用程序的共享库查找路径,而不会影响其他应用程序 * `LD_PRELOAD`:指定预先装载的一些共享库甚至是目标文件 * `LD_DEBUG`:打开动态链接器的调试功能 #### so 共享库的编写 使用 CLion 编写共享库 创建一个名为 MySharedLib 的共享库 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(MySharedLib) set(CMAKE_CXX_STANDARD 11) add_library(MySharedLib SHARED library.cpp library.h) ``` library.h ```cpp #ifndef MYSHAREDLIB_LIBRARY_H #define MYSHAREDLIB_LIBRARY_H // 打印 Hello World! void hello(); // 使用可变模版参数求和 template T sum(T t) { return t; } template T sum(T first, Types ... rest) { return first + sum(rest...); } #endif ``` library.cpp ```cpp #include #include "library.h" void hello() { std::cout << "Hello, World!" << std::endl; } ``` #### so 共享库的使用(被可执行项目调用) 使用 CLion 调用共享库 创建一个名为 TestSharedLib 的可执行项目 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(TestSharedLib) # C++11 编译 set(CMAKE_CXX_STANDARD 11) # 头文件路径 set(INC_DIR /home/xx/code/clion/MySharedLib) # 库文件路径 set(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug) include_directories(${INC_DIR}) link_directories(${LIB_DIR}) link_libraries(MySharedLib) add_executable(TestSharedLib main.cpp) # 链接 MySharedLib 库 target_link_libraries(TestSharedLib MySharedLib) ``` main.cpp ```cpp #include #include "library.h" using std::cout; using std::endl; int main() { hello(); cout << "1 + 2 = " << sum(1,2) << endl; cout << "1 + 2 + 3 = " << sum(1,2,3) << endl; return 0; } ``` 执行结果 ``` Hello, World! 1 + 2 = 3 1 + 2 + 3 = 6 ``` ### Windows 应用程序入口函数 * GUI(Graphical User Interface)应用,链接器选项:`/SUBSYSTEM:WINDOWS` * CUI(Console User Interface)应用,链接器选项:`/SUBSYSTEM:CONSOLE` _tWinMain 与 _tmain 函数声明 ```cpp Int WINAPI _tWinMain( HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int nCmdShow); int _tmain( int argc, TCHAR *argv[], TCHAR *envp[]); ``` 应用程序类型|入口点函数|嵌入可执行文件的启动函数 ---|---|--- 处理ANSI字符(串)的GUI应用程序|_tWinMain(WinMain)|WinMainCRTSartup 处理Unicode字符(串)的GUI应用程序|_tWinMain(wWinMain)|wWinMainCRTSartup 处理ANSI字符(串)的CUI应用程序|_tmain(Main)|mainCRTSartup 处理Unicode字符(串)的CUI应用程序|_tmain(wMain)|wmainCRTSartup 动态链接库(Dynamic-Link Library)|DllMain|_DllMainCRTStartup ### Windows 的动态链接库(Dynamic-Link Library) > 部分知识点来自《Windows 核心编程(第五版)》 #### 用处 * 扩展了应用程序的特性 * 简化了项目管理 * 有助于节省内存 * 促进了资源的共享 * 促进了本地化 * 有助于解决平台间的差异 * 可以用于特殊目的 #### 注意 * 创建 DLL,事实上是在创建可供一个可执行模块调用的函数 * 当一个模块提供一个内存分配函数(malloc、new)的时候,它必须同时提供另一个内存释放函数(free、delete) * 在使用 C 和 C++ 混编的时候,要使用 extern "C" 修饰符 * 一个 DLL 可以导出函数、变量(避免导出)、C++ 类(导出导入需要同编译器,否则避免导出) * DLL 模块:cpp 文件中的 __declspec(dllexport) 写在 include 头文件之前 * 调用 DLL 的可执行模块:cpp 文件的 __declspec(dllimport) 之前不应该定义 MYLIBAPI #### 加载 Windows 程序的搜索顺序 1. 包含可执行文件的目录 2. Windows 的系统目录,可以通过 GetSystemDirectory 得到 3. 16 位的系统目录,即 Windows 目录中的 System 子目录 4. Windows 目录,可以通过 GetWindowsDirectory 得到 5. 进程的当前目录 6. PATH 环境变量中所列出的目录 #### DLL 入口函数 DllMain 函数 ```cpp BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: // 第一次将一个DLL映射到进程地址空间时调用 // The DLL is being mapped into the process' address space. break; case DLL_THREAD_ATTACH: // 当进程创建一个线程的时候,用于告诉DLL执行与线程相关的初始化(非主线程执行) // A thread is bing created. break; case DLL_THREAD_DETACH: // 系统调用 ExitThread 线程退出前,即将终止的线程通过告诉DLL执行与线程相关的清理 // A thread is exiting cleanly. break; case DLL_PROCESS_DETACH: // 将一个DLL从进程的地址空间时调用 // The DLL is being unmapped from the process' address space. break; } return (TRUE); // Used only for DLL_PROCESS_ATTACH } ``` #### 载入卸载库 LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread 函数声明 ```cpp // 载入库 HMODULE WINAPI LoadLibrary( _In_ LPCTSTR lpFileName ); HMODULE LoadLibraryExA( LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags ); // 若要在通用 Windows 平台(UWP)应用中加载 Win32 DLL,需要调用 LoadPackagedLibrary,而不是 LoadLibrary 或 LoadLibraryEx HMODULE LoadPackagedLibrary( LPCWSTR lpwLibFileName, DWORD Reserved ); // 卸载库 BOOL WINAPI FreeLibrary( _In_ HMODULE hModule ); // 卸载库和退出线程 VOID WINAPI FreeLibraryAndExitThread( _In_ HMODULE hModule, _In_ DWORD dwExitCode ); ``` #### 显示地链接到导出符号 GetProcAddress 函数声明 ```cpp FARPROC GetProcAddress( HMODULE hInstDll, PCSTR pszSymbolName // 只能接受 ANSI 字符串,不能是 Unicode ); ``` #### DumpBin.exe 查看 DLL 信息 在 `VS 的开发人员命令提示符` 使用 `DumpBin.exe` 可查看 DLL 库的导出段(导出的变量、函数、类名的符号)、相对虚拟地址(RVA,relative virtual address)。如: ``` DUMPBIN -exports D:\mydll.dll ``` #### LoadLibrary 与 FreeLibrary 流程图 LoadLibrary 与 FreeLibrary 流程图 ##### LoadLibrary ![WindowsLoadLibrary](https://gitee.com/huihut/interview/raw/master/images/WindowsLoadLibrary.png) ##### FreeLibrary ![WindowsFreeLibrary](https://gitee.com/huihut/interview/raw/master/images/WindowsFreeLibrary.png) #### DLL 库的编写(导出一个 DLL 模块) DLL 库的编写(导出一个 DLL 模块) DLL 头文件 ```cpp // MyLib.h #ifdef MYLIBAPI // MYLIBAPI 应该在全部 DLL 源文件的 include "Mylib.h" 之前被定义 // 全部函数/变量正在被导出 #else // 这个头文件被一个exe源代码模块包含,意味着全部函数/变量被导入 #define MYLIBAPI extern "C" __declspec(dllimport) #endif // 这里定义任何的数据结构和符号 // 定义导出的变量(避免导出变量) MYLIBAPI int g_nResult; // 定义导出函数原型 MYLIBAPI int Add(int nLeft, int nRight); ``` DLL 源文件 ```cpp // MyLibFile1.cpp // 包含标准Windows和C运行时头文件 #include // DLL源码文件导出的函数和变量 #define MYLIBAPI extern "C" __declspec(dllexport) // 包含导出的数据结构、符号、函数、变量 #include "MyLib.h" // 将此DLL源代码文件的代码放在此处 int g_nResult; int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return g_nResult; } ``` #### DLL 库的使用(运行时动态链接 DLL) DLL 库的使用(运行时动态链接 DLL) ```cpp // A simple program that uses LoadLibrary and // GetProcAddress to access myPuts from Myputs.dll. #include #include typedef int (__cdecl *MYPROC)(LPWSTR); int main( void ) { HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; // Get a handle to the DLL module. hinstLib = LoadLibrary(TEXT("MyPuts.dll")); // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts"); // If the function address is valid, call the function. if (NULL != ProcAdd) { fRunTimeLinkSuccess = TRUE; (ProcAdd) (L"Message sent to the DLL function\n"); } // Free the DLL module. fFreeResult = FreeLibrary(hinstLib); } // If unable to call the DLL function, use an alternative. if (! fRunTimeLinkSuccess) printf("Message printed from executable\n"); return 0; } ``` ### 运行库(Runtime Library) #### 典型程序运行步骤 1. 操作系统创建进程,把控制权交给程序的入口(往往是运行库中的某个入口函数) 2. 入口函数对运行库和程序运行环境进行初始化(包括堆、I/O、线程、全局变量构造等等)。 3. 入口函数初始化后,调用 main 函数,正式开始执行程序主体部分。 4. main 函数执行完毕后,返回到入口函数进行清理工作(包括全局变量析构、堆销毁、关闭I/O等),然后进行系统调用结束进程。 > 一个程序的 I/O 指代程序与外界的交互,包括文件、管程、网络、命令行、信号等。更广义地讲,I/O 指代操作系统理解为 “文件” 的事物。 #### glibc 入口 `_start -> __libc_start_main -> exit -> _exit` 其中 `main(argc, argv, __environ)` 函数在 `__libc_start_main` 里执行。 #### MSVC CRT 入口 `int mainCRTStartup(void)` 执行如下操作: 1. 初始化和 OS 版本有关的全局变量。 2. 初始化堆。 3. 初始化 I/O。 4. 获取命令行参数和环境变量。 5. 初始化 C 库的一些数据。 6. 调用 main 并记录返回值。 7. 检查错误并将 main 的返回值返回。 #### C 语言运行库(CRT) 大致包含如下功能: * 启动与退出:包括入口函数及入口函数所依赖的其他函数等。 * 标准函数:有 C 语言标准规定的C语言标准库所拥有的函数实现。 * I/O:I/O 功能的封装和实现。 * 堆:堆的封装和实现。 * 语言实现:语言中一些特殊功能的实现。 * 调试:实现调试功能的代码。 #### C语言标准库(ANSI C) 包含: * 标准输入输出(stdio.h) * 文件操作(stdio.h) * 字符操作(ctype.h) * 字符串操作(string.h) * 数学函数(math.h) * 资源管理(stdlib.h) * 格式转换(stdlib.h) * 时间/日期(time.h) * 断言(assert.h) * 各种类型上的常数(limits.h & float.h) * 变长参数(stdarg.h) * 非局部跳转(setjmp.h) ## 📚 书籍 > [huihut/CS-Books](https://github.com/huihut/CS-Books):📚 Computer Science Books 计算机技术类书籍 PDF ### 语言 * 《C++ Primer》 * 《Effective C++》 * 《More Effective C++》 * 《深度探索 C++ 对象模型》 * 《深入理解 C++11》 * 《STL 源码剖析》 ### 算法 * 《剑指 Offer》 * 《编程珠玑》 * 《程序员面试宝典》 ### 系统 * 《深入理解计算机系统》 * 《Windows 核心编程》 * 《Unix 环境高级编程》 ### 网络 * 《Unix 网络编程》 * 《TCP/IP 详解》 ### 其他 * 《程序员的自我修养》 ## 🔱 C/C++ 发展方向 > C/C++ 发展方向甚广,包括不限于以下方向, 以下列举一些大厂校招岗位要求。 ### 后台/服务器 【后台开发】 * 编程基本功扎实,掌握 C/C++/JAVA 等开发语言、常用算法和数据结构; * 熟悉 TCP/UDP 网络协议及相关编程、进程间通讯编程; * 了解 Python、Shell、Perl 等脚本语言; * 了解 MYSQL 及 SQL 语言、编程,了解 NoSQL, key-value 存储原理; * 全面、扎实的软件知识结构,掌握操作系统、软件工程、设计模式、数据结构、数据库系统、网络安全等专业知识; * 了解分布式系统设计与开发、负载均衡技术,系统容灾设计,高可用系统等知识。 ### 桌面客户端 【PC 客户端开发】 * 计算机软件相关专业本科或以上学历,热爱编程,基础扎实,理解算法和数据结构相关知识; * 熟悉 windows 操作系统的内存管理、文件系统、进程线程调度; * 熟悉 MFC/windows 界面实现机制,熟练使用 VC,精通 C/C++,熟练使用 STL,以及 Windows 下网络编程经验; * 熟练掌握 Windows 客户端开发、调试,有 Windows 应用软件开发经验优先; * 对于创新及解决具有挑战性的问题充满激情,具有良好的算法基础及系统分析能力。 ### 图形学/游戏/VR/AR 【游戏客户端开发】 * 计算机科学/工程相关专业本科或以上学历,热爱编程,基础扎实,理解算法、数据结构、软件设计相关知识; * 至少掌握一种游戏开发常用的编程语言,具 C++/C# 编程经验优先; * 具游戏引擎(如 Unity、Unreal)使用经验者优先; * 了解某方面的游戏客户端技术(如图形、音频、动画、物理、人工智能、网络同步)者优先考虑; * 对于创新及解决具有挑战性的问题充满激情,有较强的学习能力、分析及解决问题能力,具备良好的团队合作意识; * 具阅读英文技术文档能力; * 热爱游戏。 ### 测试开发 【测试开发】 * 计算机或相关专业本科及以上学历; * 一至两年的 C/C++/Python 或其他计算机语言的编程经验; * 具备撰写测试计划、测试用例、以及实现性能和安全等测试的能力; * 具备实现自动化系统的能力; * 具备定位调查产品缺陷能力、以及代码级别调试缺陷的能力; * 工作主动积极,有责任心,具有良好的团队合作精神。 ### 网络安全/逆向 【安全技术】 * 热爱互联网,对操作系统和网络安全有狂热的追求,专业不限; * 熟悉漏洞挖掘、网络安全攻防技术,了解常见黑客攻击手法; * 掌握基本开发能力,熟练使用 C/C++ 语言; * 对数据库、操作系统、网络原理有较好掌握; * 具有软件逆向,网络安全攻防或安全系统开发经验者优先。 ### 嵌入式/物联网 【嵌入式应用开发】 * 有良好的编程基础,熟练掌握 C/C++ 语言; * 掌握操作系统、数据结构等软件开发必备知识; * 具备较强的沟通理解能力及良好的团队合作意识; * 有 Linux/Android 系统平台的开发经验者优先。 ### 音视频/流媒体/SDK 【音视频编解码】 1. 硕士及以上学历,计算机、信号处理、数学、信息类及相关专业和方向; 2. 视频编解码基础扎实,熟常用的 HEVC 或 H264,有较好的数字信号处理基础; 3. 掌握 C/C++,代码能力强, 熟悉一种汇编语言尤佳; 4. 较强的英文文献阅读能力; 5. 学习能力强,具有团队协作精神,有较强的抗压能力。 ### 计算机视觉/机器学习 【计算机视觉研究】 * 计算机、应用数学、模式识别、人工智能、自控、统计学、运筹学、生物信息、物理学/量子计算、神经科学、社会学/心理学等专业,图像处理、模式识别、机器学习相关研究方向,本科及以上,博士优先; * 熟练掌握计算机视觉和图像处理相关的基本算法及应用; * 较强的算法实现能力,熟练掌握 C/C++ 编程,熟悉 Shell/Python/Matlab 至少一种编程语言; * 在计算机视觉、模式识别等学术会议或者期刊上发表论文、相关国际比赛获奖、及有相关专利者优先。 ## 💯 复习刷题网站 * [cplusplus](http://www.cplusplus.com/) * [cppreference](https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5) * [runoob](http://www.runoob.com/cplusplus/cpp-tutorial.html) * [leetcode](https://leetcode.com/) | [leetcode-cn](https://leetcode-cn.com/) * [lintcode](https://www.lintcode.com/) * [nowcoder](https://www.nowcoder.net/) ## 📝 面试题目经验 * [牛客网 . 2020秋招面经大汇总!(岗位划分)](https://www.nowcoder.com/discuss/205497) * [牛客网 . 【备战秋招】2020届秋招备战攻略](https://www.nowcoder.com/discuss/197116) * [牛客网 . 2019校招面经大汇总!【每日更新中】](https://www.nowcoder.com/discuss/90907) * [牛客网 . 2019校招技术类岗位面经汇总【技术类】](https://www.nowcoder.com/discuss/146655) * [牛客网 . 2018校招笔试真题汇总](https://www.nowcoder.com/discuss/68802) * [牛客网 . 2017秋季校园招聘笔经面经专题汇总](https://www.nowcoder.com/discuss/12805) * [牛客网 . 史上最全2017春招面经大合集!!](https://www.nowcoder.com/discuss/25268) * [牛客网 . 面试题干货在此](https://www.nowcoder.com/discuss/57978) * [知乎 . 互联网求职路上,你见过哪些写得很好、很用心的面经?最好能分享自己的面经、心路历程。](https://www.zhihu.com/question/29693016) * [知乎 . 互联网公司最常见的面试算法题有哪些?](https://www.zhihu.com/question/24964987) * [CSDN . 全面整理的C++面试题](http://blog.csdn.net/ljzcome/article/details/574158) * [CSDN . 百度研发类面试题(C++方向)](http://blog.csdn.net/Xiongchao99/article/details/74524807?locationNum=6&fps=1) * [CSDN . c++常见面试题30道](http://blog.csdn.net/fakine/article/details/51321544) * [CSDN . 腾讯2016实习生面试经验(已经拿到offer)](http://blog.csdn.net/onever_say_love/article/details/51223886) * [cnblogs . C++面试集锦( 面试被问到的问题 )](https://www.cnblogs.com/Y1Focus/p/6707121.html) * [cnblogs . C/C++ 笔试、面试题目大汇总](https://www.cnblogs.com/fangyukuan/archive/2010/09/18/1829871.html) * [cnblogs . 常见C++面试题及基本知识点总结(一)](https://www.cnblogs.com/LUO77/p/5771237.html) * [segmentfault . C++常见面试问题总结](https://segmentfault.com/a/1190000003745529) ## 📆 招聘时间岗位 * [牛客网 . 名企校招日程](https://www.nowcoder.com/school/schedule) ## 👍 内推 * [Github . CyC2018/Job-Recommend](https://github.com/CyC2018/Job-Recommend):🔎 互联网内推信息(社招、校招、实习) * [Github . amusi/AI-Job-Recommend](https://github.com/amusi/AI-Job-Recommend):国内公司人工智能方向(含机器学习、深度学习、计算机视觉和自然语言处理)岗位的招聘信息(含全职、实习和校招) ## 👬 贡献者 ## 📜 License 本仓库遵循 CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享) 协议,转载请注明出处,不得用于商业目的。 [![CC BY-NC-SA 4.0](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)](https://github.com/huihut/interview/blob/master/LICENSE) ================================================ FILE: docs/_navbar.md ================================================ * [简体中文](/) * [English](/en) ================================================ FILE: docs/en.md ================================================
📖 Github    |    📚 Docsify

💡 ON 📚 This repository is a summary of the basic knowledge of recruiting job seekers and beginners in the direction of C / C ++ technology, including language, program library, data structure, algorithm, system, network, link loading library and other knowledge and interview experience, recruitment, internal push, etc. information. 💡 Side directory support methods:[📚 Docsify Doc](https://interview.huihut.com)、[Github + TOC navigation](https://github.com/jawil/GayHub)([TOC preview.png](https://raw.githubusercontent.com/huihut/interview/master/images/TOC预览.png)) 📄 Save as PDF: Use the Chrome browser to open the 📚 Docsify document page, shrink the left directory-right click-print-select the target printer is Save as PDF-Save ( [Print Preview.png](https://raw.githubusercontent.com/huihut/interview/master/images/PrintPreview.png) ) 🙏 If there are any errors or improvements in the contents of the warehouse, issues or pr are welcome. Suggestions or discussions can be submitted at [# 12](https://github.com/huihut/interview/issues/12). Due to my limited level, the knowledge points in the warehouse are from my original, reading notes, books, blog posts, etc. Non-original has been marked with the source, if there is any omission, please issue an issue. This warehouse follows the [CC BY-NC-SA 4.0](https://github.com/huihut/interview/blob/master/LICENSE) agreement, please indicate the source for the reprint, and may not be used for commercial purposes.
## 📑 Table of contents * [➕ C/C++](#cc) * [⭐️ Effective](#effective) * [📦 STL](#stl) * [〽️ Data Structure](#data-structure) * [⚡️ Algorithm](#algorithm) * [❓ Problems](#problems) * [💻 OS](#os) * [☁️ Computer Network](#computer-network) * [🌩 Network Programming](#network-programming) * [💾 Database](#database) * [📏 Design Pattern](#design-pattern) * [⚙️ Link Loading Library](#link-loading-library) * [📚 Books](#books) * [🔱 C/C++ development direction](#cc-development-direction) * [💯 Review of Brush Questions Website](#review-of-brush-questions-website) * [📝 Interview Questions Experience](#interview-questions-experience) * [📆 Recruitment time post](#recruitment-time-post) * [👍 Recommend](#recommend) * [👬 Contributor](#contributor) * [📜 License](#license) ## ➕ C/C++ ### const #### Function 1. Modify the variable, indicating that the variable cannot be changed; 2. Modified pointers, divided into pointers to const (pointer to const) and pointers that are constants themselves (const pointer, const pointer); 3. Modified references, references to constants (reference to const), are used for formal parameter types, which avoids copying and function modification of values; 4. Decorate a member function, stating that member variables cannot be modified within the member function. #### const Pointers and references * Pointer * Pointer to const * A pointer to a constant itself (const pointer) * Reference * Reference to const * There is no const reference because the reference is an alias of an object, the reference is not an object > (Think of it for convenience) The value modified by const (after const) cannot be changed, such as `p2`, `p3` in the usage example below #### use const use ```cpp // class class A { private: const int a; // constant object member, can use initialization list or in-class initializer public: // Constructor A() : a(0) { }; A(int x) : a(x) { }; // initialize list // const can be used to distinguish between overloaded functions int getValue(); // ordinary member function int getValue() const; // constant member function, must not modify the value of any data member in the class }; void function() { // object A b; // ordinary object, can call all member functions const A a; // constant object, can only call constant member functions const A *p = &a; // pointer variable, point to a constant object const A &q = a; // reference to constant object // pointer char greeting[] = "Hello"; char* p1 = greeting; // pointer variable, pointing to a character array variable const char* p2 = greeting; // pointer variable, pointing to a character array constant (char followed by const, indicating that the character pointed to (char) cannot be changed) char* const p3 = greeting; // itself is a constant pointer to a character array variable (const followed by p3, indicating that the p3 pointer itself cannot be changed) const char* const p4 = greeting; // a pointer to a constant itself, pointing to a character array constant } // function void function1(const int Var); // the passed parameters are immutable within the function void function2(const char* Var); // The content pointed to by the parameter pointer is constant void function3(char* const Var); // parameter pointer is constant void function4(const int& Var); // the reference parameter is constant inside the function // function return value const int function5(); // returns a constant const int* function6(); // returns a pointer variable to a constant, use: const int * p = function6 (); int* const function7(); // returns a constant pointer to a variable, use: int * const p = function7 (); ``` #### #define and const constants #define|const constants ---|--- Macro definitions, equivalent to character substitution|constant declarations preprocessor processing|compiler processing without type safety checking|with type safety checking no memory allocation|memory allocation required stored in code segment|stored in data segment Can be canceled by `#undef`|Not cancelable ### static #### Function 1. Modify ordinary variables, modify the storage area and life cycle of the variables, make the variables stored in the static area, allocate space before the `main` function runs, if there is an initial value, initialize it with the initial value, if there is no initial value, the system uses the default Value to initialize it. 2. Modify ordinary functions to indicate the scope of the function, which can only be used in the file where the function is defined. When developing a project with multiple people, in order to prevent duplicate names from functions in other people's namespaces, you can position functions as static. 3. Decorate member variables. Decorate member variables so that all objects hold only one of the variable, and you can access the member without generating an object. 4. Decorate member functions. Decorate member functions so that they can be accessed without generating objects, but non-static members cannot be accessed within static functions. ### this pointer 1. `this` pointer is a special pointer hidden in every non-static member function. It points to the object that called the member function. 2. When calling a member function on an object, the compiler first assigns the address of the object to the `this` pointer, and then calls the member function. Each time the member function accesses a data member, the `this` pointer is implicitly used. 3. When a member function is called, it is automatically passed an implicit parameter, which is a pointer to the object where the member function is located. 4. The `this` pointer is implicitly declared as: `ClassName * const this`, which means that the `this` pointer cannot be assigned; in the `const` member function of the `ClassName` class, the type of the `this` pointer For: `const ClassName * const`, this means that the object pointed to by the `this` pointer cannot be modified (that is, the data members of such objects cannot be assigned); 5. `this` is not a regular variable, but an rvalue, so you cannot get the address of `this` (you can't `& this`). 6. It is often necessary to explicitly reference the `this` pointer in the following scenarios: 1. To implement a chained reference to an object; 2. To avoid performing assignments on the same object; 3. When implementing some data structures, such as `list`. ### inline function #### Features * Equivalent to writing the contents of the inline function at the call of the inline function; * It is equivalent to directly execute the function body without executing the steps of entering the function; * Equivalent to a macro, but with more type checking than a macro, it really has function characteristics; * The compiler generally does not inline inline functions that include complex operations such as loops, recursion, and switch; * Functions defined in class declarations, other than virtual functions, are automatically implicitly treated as inline functions. #### use inline use ```cpp // Statement 1 (plus inline, recommended) inline int functionName(int first, int second,...); // statement 2 (without inline) int functionName(int first, int second,...); // definition inline int functionName(int first, int second,...) {/****/}; // inside class definition, implicitly inline class A { int doA() { return 0; } // implicit inlining } // definition outside the class, need to be explicitly inlined class A { int doA(); } inline int A::doA() { return 0; } // requires explicit inlining ``` #### Compiler processing steps for inline functions 1. Copy the inline function body to the `inline` function call point; 2. Allocate memory space for local variables in the used `inline` function; 3. Map the input parameters and return values of the `inline` function to the local variable space of the calling method; 4. If the `inline` function has multiple return points, turn it into a branch at the end of the inline function code block (using `GOTO`). #### Advantages and disadvantages Advantages 1. Inline functions, like macro functions, perform code expansion at the callee's place, eliminating the need to push parameters on the stack, open and recover stack frames, and return results, etc., thereby improving program execution speed. 2. Compared to macro functions, inline functions do security checks or automatic type conversions (as with normal functions) when code is expanded, while macro definitions do not. 3. Declaring a member function that is also defined in a class automatically converts it into an inline function, so inline functions can access class member variables, while macro definitions cannot. 4. Inline functions are debuggable at runtime, while macro definitions are not. Disadvantages 1. Code bloat. Inlining is at the expense of code bloat (copy), eliminating the overhead of function calls. If the time to execute the code in the function body is greater than the cost of the function call, then the efficiency gain will be small. On the other hand, copying the code for each inline function call will increase the total code size of the program and consume more memory space. 2. The inline function cannot be upgraded with the function library upgrade. Changes to the inline function require recompilation, unlike non-inline, which can be linked directly. 3. Whether it is inline or not is beyond the programmer's control. Inline functions are just suggestions to the compiler. The decision whether to inline functions is up to the compiler. #### Can a virtual function be an inline function? > [Are "inline virtual" member functions ever actually "inlined"?](Http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html) * A virtual function can be an inline function. Inline can modify a virtual function, but it cannot be inlined when the virtual function exhibits polymorphism. * Inlining is recommended by the compiler, and the polymorphism of virtual functions is at runtime. The compiler cannot know which code is called at runtime, so virtual functions cannot be inlined at runtime (runtime). . * `inline virtual` The only time it can be inlined is: the compiler knows which class the object is called (such as `Base::who()` ), only if the compiler has an actual object instead of a pointer or reference to the object Will happen. Virtual function inline use ```cpp #include using namespace std; class Base { public: inline virtual void who() { cout << "I am Base\n"; } virtual ~Base() {} }; class Derived : public Base { public: inline void who() // Implicit inlining when not writing inline { cout << "I am Derived\n"; } }; int main() { // The virtual function who () here is called through the concrete object (b) of the class (Base), which can be determined during compilation, so it can be inlined, but whether it is inlined depends on the compilation Device. Base b; b.who(); // The virtual function here is called through a pointer, which is polymorphic and needs to be determined during runtime, so it cannot be inlined. Base *ptr = new Derived(); ptr->who(); // Because Base has a virtual destructor (virtual ~ Base () {}), when deleting, the Derived destructor is called first, and then the Base destructor is called to prevent memory leaks. delete ptr; ptr = nullptr; system("pause"); return 0; } ``` ### volatile ```cpp volatile int i = 10; ``` * The volatile keyword is a type modifier, and the type variable declared with it indicates that it can be changed by some factors unknown to the compiler (operating system, hardware, other threads, etc.). So using volatile tells the compiler that such objects should not be optimized. * Variables declared by the volatile keyword must be fetched from memory each time they are accessed (variables that are not modified by volatile may be fetched from CPU registers due to compiler optimizations) * const can be volatile (such as a read-only status register) * Pointer can be volatile ### assert() Assertions are macros, not functions. The prototype of the assert macro is defined in `` (C), `` (C ++), and its role is to terminate program execution if its condition returns an error. You can turn off assert by defining `NDEBUG`, but it needs to be at the beginning of the source code, before `include ` . assert () uses ```cpp #define NDEBUG // Add this line, assert is not available #include assert( p != NULL ); // assert is not available ``` ### sizeof() * sizeof For arrays - get the size of the entire array. * sizeof For pointers - get the size of the space occupied by the pointer itself. ### Compiler Extensions vs Standard Alignment Control * Compiler Extension `#pragma pack(n)`, restricts the maximum alignment of members in subsequently defined `struct`/`class`/`union` to n bytes. * Standard Alignment Control: * `alignas(k)`, requires types or variables to be aligned to at least k bytes (rounds up to ≥ natural alignment). * `alignof(T)`, gets the natural alignment requirement of type T (compile-time constant). Feature | `#pragma pack` | `alignas` ----------------|-------------------------|--------------------- Standardization | ❌ Compiler Extension | ✅ C++11 Standard Alignment Direction | ⬇️ Only decreases alignment | ⬆️ Only increases alignment Portability | ❌ Compiler Dependent | ✅ Cross-platform Scope | 🔄 Affects entire struct | 🎯 Per-member control Performance Impact | ⚠️ May reduce memory access speed | ⚠️ Over-alignment wastes space #### Usage Examples ```cpp #include #include #pragma pack(push, 1) // Max alignment 1 byte (compact layout) struct PackedHeader { uint16_t len; // offset 0 uint32_t id; // offset 2 }; #pragma pack(pop) struct alignas(8) Align8 { // Force 8-byte alignment double value; // offset 0 (8 bytes) int flag; // offset 8 }; int main() { std::cout << "PackedHeader size: " << sizeof(PackedHeader) << "\n"; // Output: 6 std::cout << "Align8 size: " << sizeof(Align8) << "\n"; // Output: 16 } ### Bit field ```cpp Bit mode: 2; // mode is 2 digits ``` A class can define its (non-static) data members as bit-fields, which contain a certain number of binary bits in a bit-field. When a program needs to transfer binary data to other programs or hardware devices, the bit field is usually used. * The layout of the bit field in memory is machine-dependent * The type of the bit field must be an integer or enumerated type. The behavior of the bit field in a signed type will depend on the implementation. * The fetch operator (&) cannot be applied to the bit field, and no pointer can point to the bit field of the class ### `extern` vs `extern "C"` * `extern` is a **storage-class specifier** used to declare that a variable or function has **external linkage**, indicating that the entity's definition may reside in another translation unit. * `extern "C"` is a **linkage directive** that specifies functions or variables should use **C language linkage** (without affecting compilation rules). 1. **Suppresses C++ name mangling**: Ensures symbol names match those generated by the C compiler _on that specific platform_, preventing undefined symbol errors during linking due to name decoration. **Does not guarantee platform ABI (Application Binary Interface) consistency**. 2. **Enables C/C++ interoperability**: Allows C++ functions to be called from C code (and vice versa). `extern "C"` demo ```cpp #ifdef __cplusplus extern "C" { #endif void *memset(void *, int, size_t); #ifdef __cplusplus } #endif ``` ### struct with typedef struct #### In C ```c // c typedef struct Student { int age; } S; ``` Equivalent to ```c // c struct Student { int age; }; typedef struct Student S; ``` At this time `S` is equivalent to` struct Student`, but the two identifier namespaces are different. You can also define `void Student(){}` that does not conflict with `struct Student` . #### In C++ Because the compiler's rules for positioning symbols (search rules) have changed, it is different from the C language. First, if `struct Student {...};` is defined in the class identifier space, when `Student me;` is used, the compiler will search the global identifier table. If `Student` is not found, it will be in the class identifier. search for. That means it can use `Student` or `struct Student`, as follows: ```cpp // cpp struct Student { int age; }; void f( Student me ); // correct, the "struct" keyword can be omitted ``` If a function with the same name as `Student` is defined, `Student` only represents the function, not the structure, as follows: ```cpp typedef struct Student { int age; } S; void Student() {} //Correct, "Student" only represents this function after definition //void S() {} // Error, symbol "S" has been defined as an alias for "struct Student" int main() { Student(); struct Student me; // Or "S me"; return 0; } ``` ### struct and class in C ++ In general, struct is more suitable as an implementation of a data structure, and class is more suitable as an implementation of an object. #### The difference * The most essential difference is the default access control 1. Default inherited access rights. struct is public and class is private. 2. struct as the data structure implementation body, its default data access control is public, and class as the object implementation body, its default member variable access control is private. ### union union Union is a special class that saves space. A union can have multiple data members, but only one data member can have a value at any time. When a member is assigned, other members become undefined. Union has the following characteristics: * The default access control character is public * May contain constructors and destructors * Cannot contain members of reference type * Cannot inherit from other classes and cannot be used as a base class * Cannot contain virtual functions * Anonymous union can directly access union members in the scope where it is defined * Anonymous union cannot contain protected members or private members * Global anonymous union must be static union demo ```cpp #include union UnionTest { UnionTest() : i(10) {}; int i; double d; }; static union { int i; double d; }; int main() { UnionTest u; union { int i; double d; }; std::cout << u.i << std::endl; // Output UnionTest union 10 ::i = 20; std::cout << ::i << std::endl; // Output global static anonymous union 20 i = 30; std::cout << i << std::endl; // Output of locally anonymous union of 30 return 0; } ``` ### C implements C ++ classes C implements object-oriented features of C ++ (encapsulation, inheritance, polymorphism) * Encapsulation: Use function pointers to encapsulate properties and methods into structures * Inheritance: structure nesting * Polymorphism: function pointers of parent and child methods are different > [Can you write object-oriented code in C? [closed]](https://stackoverflow.com/a/351745) ### explicit (keyword) * explicit constructor modification prevents implicit conversion and copy initialization * explicit conversions can prevent implicit conversions, except [conversion by context](https://en.cppreference.com/w/cpp/language/implicit_conversion) explicit demo ```cpp struct A { A(int) { } operator bool() const { return true; } }; struct B { explicit B(int) {} explicit operator bool() const { return true; } }; void doA(A a) {} void doB(B b) {} int main() { A a1(1); // OK:direct initialization A a2 = 1; // OK:copy initialization A a3{ 1 }; // OK:direct list initialization A a4 = { 1 }; // OK:copy list initialization A a5 = (A)1; // OK:Allow explicit conversion of static_cast doA(1); // OK:Allow implicit conversion from int to A if (a1); // OK: implicit conversion from A to bool using conversion function A ::operator bool() bool a6(a1); // OK: implicit conversion from A to bool using conversion function A::operator bool() bool a7 = a1; // OK: implicit conversion from A to bool using conversion function A::operator bool() bool a8 = static_cast(a1); // OK: static_cast for direct initialization B b1(1); // OK:direct initialization B b2 = 1; // Error: Object modified by explicit constructor cannot be initialized by copying B b3{ 1 }; // OK:direct list initialization B b4 = { 1 }; // Error: Object modified by explicit constructor cannot copy list initialization B b5 = (B)1; // OK: Allow explicit conversion of static_cast doB(1); // Error: Objects whose constructor is explicitly modified cannot be implicitly converted from int to B if (b1); // OK: objects modified by explicit conversion function B::operator bool() can be converted from B to bool by context bool b6(b1); // OK: Explicitly modified conversion function B::operator The object of bool() can be converted from B to bool by context bool b7 = b1; // Error: Objects modified by explicit conversion function B :: operator bool () cannot be implicitly converted bool b8 = static_cast(b1); // OK: static_cast performs direct initialization return 0; } ``` ### friend - friend class and friend function * Access to private members * Destruction of encapsulation * Friendship is not transitive * One-way friendship * There are no restrictions on the form and number of friend declarations ### using #### using statement A `using declaration` introduces only one member of a namespace at a time. It allows us to know exactly which name is referenced in the program. Such as: ```cpp using namespace_name::name; ``` #### Using declaration of constructor In C ++ 11, a derived class can reuse the constructor defined by its direct base class. ```cpp class Derived : Base { public: using Base::Base; /* ... */ }; ``` As above using statement, for each constructor of the base class, the compiler generates a derived class constructor corresponding to it (the parameter list is exactly the same). Generates the following type constructor: ```cpp Derived (parms): Base (args) {} ``` #### using instructions The `using directive` makes all names in a particular namespace visible, so we don't need to add any prefix qualifiers to them. Such as: ```cpp using namespace namespace_name; ``` #### Minimize `using directives` to pollute namespaces > Generally speaking, it is safer to use the using command than the using compile command, because it** imports only the specified name**. If the name conflicts with a local name, the compiler will** issue instructions**. The using compile command imports all names, including names that may not be needed. If there is a conflict with a local name, the local name will override the namespace version, and the compiler will not issue a warning. In addition, the openness of the namespace means that the names of the namespace may be scattered in multiple places, which makes it difficult to know exactly which names have been added. using demo Minimize `using directives` ```cpp using namespace std; ``` You should use `using declarations` more often ```cpp int x; std::cin >> x ; std::cout << x << std::endl; ``` or ```cpp using std::cin; using std::cout; using std::endl; int x; cin >> x; cout << x << endl; ``` ### :: scope resolution operator #### classification 1. Global scope (`:: name`): used before type names (classes, class members, member functions, variables, etc.) to indicate that the scope is a global namespace 2. Class scope character (`class :: name`): used to indicate that the scope of the specified type is specific to a class 3. Namespace scope (`namespace :: name`): used to indicate that the scope of the specified type is specific to a namespace :: demo ```cpp int count = 11; // Global (: :) count class A { public: static int count; // Count (A::count) of class A }; int A::count = 21; void fun() { int count = 31; // Initialize the local count to 31 count = 32; // Set the local count to 32 } int main() { ::count = 12; // Test 1: Set the global count to 12 A::count = 22; // Test 2: Set the count of class A to 22 fun(); // Test 3 return 0; } ``` ### enum - enum type #### Scoped Enumeration Type ```cpp enum class open_modes { input, output, append }; ``` #### Unscoped enumeration type ```cpp enum color { red, yellow, green }; enum { floatPrec = 6, doublePrec = 10 }; ``` ### decltype decltype keyword is used to check the declared type or expression type and value classification of an entity. grammar: ```cpp decltype ( expression ) ``` decltype demo ```cpp // Tail return allows us to declare the return type after the parameter list template auto fcn(It beg, It end) -> decltype(*beg) { / process the sequence return *beg; // return a reference to an element in the sequence } // In order to use template parameter members, you must use typename template auto fcn2(It beg, It end) -> typename remove_reference::type { // process the sequence return * beg; // return a copy of an element in the sequence } ``` ### reference #### lvalue reference Regular reference, which generally represents the identity of the object. #### rvalue reference An rvalue reference is a reference that must be bound to an rvalue (a temporary object, an object to be destroyed) and generally represents the value of the object. An rvalue reference implements Move Sementics and Perfect Forwarding. Its main purpose is twofold: * Eliminate unnecessary copying of objects when two objects interact, saving computing storage resources and improving efficiency. * Ability to define generic functions more concisely. #### Reference Collapse * `X & &`, `X & &&`, `X && &` can be folded into `X &` * `X && &&` can be folded into `X &&` ### Macro * A macro definition can implement a function similar to a function, but it is not a function after all, and the "parameters" in the brackets in the macro definition are not real parameters. The "parameters" are replaced one-to-one when the macro is expanded . ### Member initialization list benefit * More efficient: There is no need to call the default constructor once. * In some cases it is necessary to use the initialization list: 1. Constant members, because constants can only be initialized and cannot be assigned, so they must be placed in the initialization list 2. Reference types. References must be initialized at the time of definition and cannot be reassigned, so they must also be written in the initialization list. 3. There is no class type for the default constructor, because the initialization list can be used to initialize without having to call the default constructor. ### initializer_list list initialization Initialize an object with a curly brace initializer list, where the corresponding constructor accepts a `std :: initializer_list` parameter. initializer_list uses ```cpp #include #include #include template struct S { std::vector v; S(std::initializer_list l) : v(l) { std::cout << "constructed with a " << l.size() << "-element list\n"; } void append(std::initializer_list l) { v.insert(v.end(), l.begin(), l.end()); } std::pair c_arr() const { return {&v[0], v.size()}; // Copy the list initialization in the return statement // this does not use std :: initializer_list } }; template void templated_fn(T) {} int main() { S s = {1, 2, 3, 4, 5}; // copy initialization s.append({6, 7, 8}); // list initialization in function call std::cout << "The vector size is now " << s.c_arr().second << " ints:\n"; for (auto n : s.v) std::cout << n << ' '; std::cout << '\n'; std::cout << "Range-for over brace-init-list: \n"; for (int x : {-1, -2, -3}) // auto rules make this band for work std::cout << x << ' '; std::cout << '\n'; auto al = {10, 11, 12}; // special rules for auto std::cout << "The list bound to auto has size() = " << al.size() << '\n'; // templated_fn({1, 2, 3}); // Compile error! "{1, 2, 3}" is not an expression, // it has no type, so T cannot infer templated_fn>({1, 2, 3}); // OK templated_fn>({1, 2, 3}); // also OK } ``` ### Object-oriented Object-oriented programming (OOP) is a model of program programming with object concepts, and it is also an abstract approach to program development. ![Object-oriented features](https://raw.githubusercontent.com/huihut/interview/master/images/面向对象基本特征.png) Three Object-Oriented Features-Encapsulation, Inheritance, and Polymorphism ### Encapsulation Encapsulate objective things into abstract classes, and classes can only use their own data and methods for trusted classes or objects to operate, and hide untrusted information. Keywords: public, protected, private. Do not write defaults to private. * `public` members: can be accessed by any entity * `protected` members: only allowed to be accessed by subclasses and member functions of this class * `private` members: only accessible by member functions, friend classes, or friend functions of this class ### Inheritance * Base class (parent class) ——> derived class (subclass) ### Polymorphism * Polymorphism, that is, multiple states (morphology). In simple terms, we can define polymorphism as the ability of a message to be displayed in multiple forms. * Polymorphism is based on encapsulation and inheritance. * C polymorphism classification and implementation: 1. Ad-hoc Polymorphism (compile-time): function overload, operator overload 2. Subtype Polymorphism (runtime): virtual function 3. Parametric Polymorphism (compile-time): class template, function template 4. Coercion Polymorphism (compilation / runtime): basic type conversion, custom type conversion > [The Four Polymorphisms in C++](https://catonmat.net/cpp-polymorphism) #### Static polymorphism (compile time / early binding) Function overloading ```cpp class A { public: void do(int a); void do(int a, int b); }; ``` #### Dynamic polymorphism (runtime / late binding) * Virtual functions: decorate member functions with virtual to make them virtual * Dynamic binding: dynamic binding occurs when a virtual function is called using a reference or pointer to a base class **note:** * You can assign an object of a derived class to a pointer or reference of the base class, and not vice versa * Ordinary functions (non-class member functions) cannot be virtual functions * Static functions (static) cannot be virtual functions * The constructor cannot be a virtual function (because when the constructor is called, the virtual table pointer is not in the object's memory space, the virtual table pointer must be formed after the constructor is called) * An inline function cannot be a virtual function when it shows polymorphism. For an explanation, see: [Can a virtual function be an inline function? ](https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97) Dynamic polymorphic demo ```cpp class Shape // shape class { public: virtual double calcArea() { ... } virtual ~Shape(); }; class Circle : public Shape // circle class { public: virtual double calcArea(); ... }; class Rect : public Shape // rectangle class { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); Shape * shape2 = new Rect(5.0, 6.0); shape1->calcArea(); // call the method in the circular class shape2->calcArea(); // call the method in the rectangle class delete shape1; shape1 = nullptr; delete shape2; shape2 = nullptr; return 0; } ``` ### Virtual destructor The virtual destructor is to resolve the pointer of the base class to the derived class object, and delete the derived class object with the pointer of the base class. Virtual destructor demo ```cpp class Shape { public: Shape(); // Constructor cannot be virtual virtual double calcArea(); virtual ~Shape(); // virtual destructor }; class Circle : public Shape // circle class { public: virtual double calcArea(); ... }; int main() { Shape * shape1 = new Circle(4.0); shape1->calcArea(); delete shape1; // Because Shape has a virtual destructor, when delete deletes the memory, it first calls the subclass destructor and then the base class destructor to prevent memory leaks. shape1 = NULL; return 0; } ``` ### Pure virtual functions A pure virtual function is a special kind of virtual function. You cannot give a meaningful implementation to a virtual function in a base class. Instead, you declare it as a pure virtual function. Its implementation is left to the derived classes of the base class. ```cpp virtual int A() = 0; ``` ### Virtual functions, pure virtual functions * If a virtual function is declared in the class, this function is implemented, even if it is empty, its role is to allow this function to be overridden in its subclasses, so that the compiler can use Late binding to achieve polymorphism. A pure virtual function is just an interface. It is a function declaration. It must be left in a subclass to implement it. * Virtual functions can not be overridden in subclasses; but pure virtual functions must be implemented in subclasses to instantiate subclasses. * The virtual function class is used for "implementation inheritance". Inheriting the interface also inherits the implementation of the parent class. Pure virtual functions focus on the uniformity of the interface, and the implementation is done by subclasses. * A class with a pure virtual function is called an abstract class. This class cannot directly generate objects. It can only be used after being inherited and rewriting its virtual function. After an abstract class is inherited, subclasses can continue to be abstract or ordinary classes. * Virtual base class is the base class in virtual inheritance, see below for details. > [CSDN . C++ And connection of virtual functions and pure virtual functions](https://blog.csdn.net/u012260238/article/details/53610462) ### Virtual function pointer, virtual function table * Virtual function pointer: In an object containing a virtual function class, it points to a virtual function table, which is determined at runtime. * Virtual function table: in the program read-only data section (`.rodata section`, see: [object file storage structure](#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84)),Stores virtual function pointers. If the derived class implements a virtual function of the base class, the virtual function pointer of the original base class is overwritten in the virtual table, and is created according to the class declaration at compile time. > [C++ Function (table) implementation mechanism and simulation implementation in C language](https://blog.twofei.com/496/) ### Virtual inheritance Virtual inheritance is used to solve the problem of diamond inheritance under multiple inheritance conditions (wasting storage space and ambiguity). The underlying implementation principle is related to the compiler. It is generally implemented by ** virtual base class pointer ** and ** virtual base class table **. Each virtual inherited subclass has a virtual base class pointer (occupies the storage space of a pointer) , 4 bytes) and virtual base class table (does not occupy storage space of class objects) (It should be emphasized that the virtual base class will still have a copy in the subclass, but there is only one copy at most, not not in the subclass. Inside); when the subclass of virtual inheritance is inherited as the parent class, the pointer of the virtual base class will also be inherited. In fact, vbptr refers to a virtual base table pointer. This pointer points to a virtual base table. The virtual table records the offset address of the virtual base class and this class. Offset address, so that the virtual base class members are found, and virtual inheritance does not need to maintain two identical copies of the public base class (virtual base class) like ordinary multiple inheritance, saving storage space. ### Virtual inheritance, virtual functions * Similarities: Both use virtual pointers (both occupy class storage space) and virtual tables (both do not occupy class storage space) * the difference: * Virtual inheritance * The virtual base class still exists in the inherited class and only takes up storage space * The virtual base class table stores the offset of the virtual base class relative to the direct inherited class * Virtual function * Virtual functions do not take up storage space * The virtual function table stores the virtual function address ### Template classes, member templates, virtual functions * Virtual functions can be used in template classes * The member template of a class (whether it is a normal class or a class template) (it is a member function of the template) cannot be a virtual function ### Abstract class, interface class, aggregate class * Abstract class: a class containing pure virtual functions * Interface class: Abstract class containing only pure virtual functions * Aggregation classes: Users can directly access their members and have a special form of initialization syntax. Meet the following characteristics: * All members are public * No constructor is defined * No in-class initialization * No base class, no virtual function ### Memory allocation and management #### malloc, calloc, realloc, alloca 1. malloc: apply for a specified number of bytes of memory. The initial value in the requested memory is uncertain. 2. calloc: For an object of a specified length, allocate memory that can hold the specified number of objects. Each bit of the requested memory is initialized to 0. 3. realloc: change the previously allocated memory length (increase or decrease). When increasing the length, it may be necessary to move the contents of the previously allocated area to another sufficiently large area, while the initial value in the newly added area is uncertain. 4. alloca: apply for memory on the stack. When the program pops out of the stack, it will automatically release memory. However, it should be noted that alloca is not portable and difficult to implement on machines without traditional stacks. alloca should not be used in programs that must be widely ported. C99 supports variable-length arrays (VLAs) and can be used instead of alloca. #### malloc, free Used to allocate and release memory malloc, free use Apply for memory and confirm whether the application is successful ```cpp char *str = (char*) malloc(100); assert(str != nullptr); ``` Pointer is empty after freeing memory ```cpp free(p); p = nullptr; ``` #### new、delete 1. new / new []: Do two things, first call malloc at the bottom to allocate memory, and then call the constructor (create the object). 2. delete / delete []: also complete two things, first call the destructor (clean up resources), and then call free to free up space at the bottom. 3. new automatically calculates the number of bytes required when applying for memory, and malloc requires us to enter the number of bytes of the requested memory space ourselves. new, delete demo Apply for memory and confirm whether the application is successful ```cpp int main() { T* t = new T(); // memory allocation first, then constructor delete t; // destructor first, then release memory return 0; } ``` #### Positioning new Positioning new (placement new) allows us to pass additional address parameters to new to create objects in a pre-specified memory area. ```cpp new (place_address) type new (place_address) type (initializers) new (place_address) type [size] new (place_address) type [size] { braced initializer list } ``` * `place_address` is a pointer * `initializers` provides a (possibly empty) comma-separated list of initial values ### delete this - Is it legal? > [Is it legal (and moral) for a member function to say delete this?](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) Legal, but: 1. It must be guaranteed that this object is allocated via `new` (not `new[]`, not placement new, not on the stack, not global, not a member of other objects) 2. You must ensure that the member function that calls delete this is the last member function that calls this 3. You must ensure that the member function does not call this after `delete this` 4. Make sure no one uses it after delete this ### Smart pointer #### In the C ++ Standard Library (STL) header file:`#include ` #### C++ 98 ```cpp std::auto_ptr ps (new std::string(str)); ``` #### C++ 11 1. shared_ptr 2. unique_ptr 3. weak_ptr 4. auto_ptr (Deprecated by C ++ 11) * Class shared_ptr implements the concept of shared ownership. Multiple smart pointers point to the same object, and that object and its associated resources are released when the "last reference is destroyed". In order to perform the above work in more complex scenarios, the standard library provides auxiliary classes such as weak_ptr, bad_weak_ptr, and enable_shared_from_this. * Class unique_ptr implements the concept of exclusive ownership or strict ownership, ensuring that only one smart pointer can point to the object at a time. You can transfer ownership. It is especially useful for avoiding resource leaks, such as forgetting to delete after new. ##### shared_ptr Multiple smart pointers can share the same object, and the last one of the object has the responsibility to destroy the object and clean up all resources related to the object. * Support custom deleter, which can prevent Cross-DLL problems (object is created by new in dynamic link library (DLL), but deleted by another DLL), and the mutex is automatically released ##### weak_ptr weak_ptr allows you to share but does not own an object. Once the last smart pointer that owns the object loses ownership, any weak_ptr will automatically become empty. Therefore, in addition to the default and copy constructors, weak_ptr only provides a "accept a shared_ptr" constructor. * The problem of cycles of references (two objects that have not been used actually refer to each other, making them appear to be in the "used" state) ##### unique_ptr unique_ptr is a type that has only been available since C ++ 11, and is a smart pointer that can help avoid resource leaks during exceptions. Using exclusive ownership means that you can ensure that an object and its corresponding resource are owned by only one pointer at a time. Once you own the destroyed or programmed empty, or start owning another object, the previously owned object will be destroyed and any corresponding resources will be released. * unique_ptr is used instead of auto_ptr ##### auto_ptr Deprecated by c ++ 11 due to lack of language features such as `std::move` semantics "for construction and assignment", and other flaws. ##### auto_ptr compared to unique_ptr * auto_ptr can be assigned a copy, and ownership is transferred after copying; unqiue_ptr has no copy assignment semantics, but implements `move` semantics; * auto_ptr objects cannot manage arrays (destructive call `delete`), unique_ptr can manage arrays (destructive call` delete [] `); ### Casting Operator > [MSDN. Casting Operator](https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx) #### static_cast * Compile-time type conversion (no runtime checks) * Does not depend on RTTI | Conversion Type | Safety | Example | |--------------------------|--------------|--------------------------| | Numeric conversion | ✅ Safe | `float f=3.14; int i=static_cast(f);` | | Upcast (class hierarchy) | ✅ Safe | `Derived* d; Base* b=static_cast(d);` | | Downcast (class hierarchy)| ⚠️ Unsafe | `Base* b=new Base; Derived* d=static_cast(b);` | | Same-type conversion | ✅ Safe | `MyClass* p; MyClass* same=static_cast(p);` | | Explicit constructor call| ✅ Safe | `func(static_cast("text"));` | | Any type→void* | ✅ Safe | `int* p; void* vp=static_cast(p);` | | Enum↔Integer | ✅ Safe | `enum Color{RED}; int c=static_cast(RED);` | #### dynamic_cast * Runtime type checking (depends on RTTI) * Requires polymorphic type (base must have at least one virtual function) * Safe failure mechanism (nullptr or exception) | Conversion Type | Safety | Runtime Cost | Polymorphic Required | Failure Handling | Example | |---------------------|--------|--------------|----------------------|---------------------------|---------| | Upcast | ✅ Safe | None | ❌ Not required | Not applicable (always succeeds) | `Derived* d; Base* b = dynamic_cast(d);` | | Downcast | ✅ Safe | Yes | ✅ Required | Pointer→`nullptr`
Reference→`std::bad_cast` | `Base* b; Derived* d = dynamic_cast(b);` | | Cross-cast | ✅ Safe | Yes | ✅ Required | Pointer→`nullptr`
Reference→`std::bad_cast` | `B2* b2 = dynamic_cast(b1); // In diamond inheritance` | | Same-type conversion| ✅ Safe | None | ❌ Not required | Not applicable (always succeeds) | `Derived* d2 = dynamic_cast(d1);` | | Any type→void* | ✅ Safe | Yes | ✅ Required | `nullptr` | `void* p = dynamic_cast(obj);` | #### const_cast * Compile-time type modifier operation * Only modifies `const`/`volatile` attributes * Does not change underlying binary representation | Conversion Type | Safety | Example | |------------------------|--------------|----------------------------------| | Remove const | ⚠️ Risky | `const int* cp; int* p=const_cast(cp);` | | Remove volatile | ⚠️ Risky | `volatile int* vp; int* p=const_cast(vp);` | | Add const | ✅ Safe | `int* p; const int* cp=const_cast(p);` | | Legacy API compatibility | ⚠️ Necessary risk | `legacy_api(const_cast(str.c_str()));` | #### reinterpret_cast * No compile-time type safety checks * Binary bit-level reinterpretation * Most dangerous cast operator | Conversion Type | Safety | Example | |------------------------|--------------|----------------------------------| | Pointer↔Pointer | ❌ High risk | `MyClass* obj; void* p=reinterpret_cast(obj);` | | Pointer↔Integer | ❌ High risk | `intptr_t addr=reinterpret_cast(&obj);` | | Function pointer conversion | ❌ Extreme risk | Converting function pointers with different signatures | | Memory-mapped I/O | ⚠️ Systems programming | Hardware register access | #### bad_cast * The exception type for a failed reference conversion using `dynamic_cast`. bad_cast demo ```cpp try { Circle& ref_circle = dynamic_cast(ref_shape); } catch (bad_cast b) { cout << "Caught: " << b.what(); } ``` ### Runtime Type Information (RTTI) #### typeid * The typeid operator allows determining the type of an object at runtime * typeid returns a reference to a type\_info object * If you want to get the data type of the derived class through the pointer of the base class, the base class must have a virtual function * Can only get the actual type of the object #### type_info * The type_info class describes the type information generated by the compiler in the program. Objects of this class can effectively store pointers to the names of types. The type_info class can also store encoded values suitable for comparing whether two types are equal or comparing their permutation order. The encoding rules and permutation order for the types are unspecified and may vary from program to program. * Header file: `typeinfo` typeid, type_info demo ```cpp #include using namespace std; class Flyable // can fly { public: virtual void takeoff() = 0; // take off virtual void land() = 0; // land }; class Bird : public Flyable // bird { public: void foraging() {...} // foraging virtual void takeoff() {...} virtual void land() {...} virtual ~Bird(){} }; class Plane : public Flyable // airplane { public: void carry() {...} // carry virtual void takeoff() {...} virtual void land() {...} }; class type_info { public: const char* name() const; bool operator == (const type_info & rhs) const; bool operator != (const type_info & rhs) const; int before(const type_info & rhs) const; virtual ~type_info(); private: ... }; void doSomething(Flyable *obj) // do something { obj->takeoff(); cout << typeid(*obj).name() << endl; // output the type of the incoming object("class Bird" or "class Plane") if(typeid(*obj) == typeid(Bird)) // determine object type { Bird *bird = dynamic_cast(obj); // object conversion bird->foraging(); } obj->land(); } int main(){ Bird *b = new Bird(); doSomething(b); delete b; b = nullptr; return 0; } ``` ## ⭐️ Effective ### Effective C++ 1. Treat C ++ as a language federation (C, Object-Oriented C ++, Template C ++, STL) 2. It is better to replace the preprocessor with a compiler (try to replace `# define` with `const`, `enum`, `inline`) 3. Use const whenever possible 4. Make sure that the object has been initialized before being used (the copy constructor is more efficient than the default copy assignment after construction) 5. Understand what functions C ++ silently writes and calls (the compiler secretly creates a default constructor, copy constructor, copy assignment operator, destructor for class) 6. If you don't want to use the automatically generated function of the compiler, you should explicitly reject it (declare member functions that you don't want to use as private and do not implement them) 7. Declare a virtual destructor for the polymorphic base class (if the class has any virtual functions, it should have a virtual destructor) 8. Don't let the exception escape the destructor (the destructor should swallow and not propagate the exception, or end the program, instead of spitting out the exception; if you want to handle the exception, you should handle it in a non-destructed ordinary function) 9. Never call virtual functions during construction and destruction (because such calls never descend to the derived class) 10. Let `operator =` return a `reference to * this` (for chained assignment) 11. Handle "self-assignment" in `operator =` 12. When assigning an object, be sure to copy "all member variables within the object" and "all base class components" (call the base class copy constructor) 13. Manage resources by objects (resources are obtained in the constructor and released in the destructor. It is recommended to use smart pointers. The resource acquisition time is the initialization time (Resource Acquisition Is Initialization (RAII)). 14. Be careful with copying behavior in resource management classes (the general RAII class copying behavior is: inhibit copying, reference counting, deep copying, transfer of bottom resource ownership (similar to auto_ptr)) 15. Provide access to raw resources in the resource management class (access to raw resources may be explicitly or implicitly converted, in general, display conversion is safer, and implicit conversion is more convenient for customers) 16. Use the same form when using new and delete in pairs (`[]` in `new` then` delete []`, `new` without `[]` and `delete`) 17. Store the newed object in (into) a smart pointer in a separate statement (if you do not do this, it may cause undetectable resource leaks due to compiler optimizations) 18. Make interfaces easy to use correctly and not easy to be misused (Promote normal use: Consistency of interfaces, compatible behavior of built-in types; Prevent misuse: Create new types, Restrict operation on types, Constrain object values Eliminate the client's resource management responsibilities) 19. Designing a class is like designing a type, and you need to consider object creation, destruction, initialization, assignment, value passing, legal values, inheritance relationships, conversion, generalization, and so on. 20. Prefer pass-by-reference-to-const instead of pass-by-value (the former is usually more efficient and avoids slicing problems, but not applicable to built-in types, STL iterators, function objects) 21. When an object must be returned, do not deliberately return its reference (never return pointer or reference to a local stack object, or return reference to a heap-allocated object, or return pointer or reference to a local static object, which may be required at the same time. Multiple such objects.) 22. Declare member variables as private (for encapsulation, consistency, precise control over reads and writes, etc.) 23. Rather replace the member function with non-member and non-friend (which can increase packaging flexibility, packaging flexibility, and functional extensibility) 24. If all parameters (including the metaphoric parameter pointed to by this pointer) require type conversion, please use a non-member function for this 25. Consider writing a swap function that does not throw exceptions 26. Delay the appearance of variable definitions as much as possible (to increase program clarity and improve program efficiency) 27. Do as few transformations as possible (old: `(T) expression`,` T (expression) `; new:` const_cast (expression) `,` dynamic_cast (expression) `,` reinterpret_cast (expression) `,` static_cast (expression) `;; try to avoid transformation, pay attention to efficiency and avoid dynamic_casts, try to design so that no transformation is needed, the transformation can be encapsulated into a function, rather a new type of transformation is preferred) 28. Avoid using handles (including references, pointers, iterators) to point inside the object (to increase encapsulation, make const member functions behave more like const, and reduce "dangling handles" (such as dangling pointers, etc.) possibility) 29. It is worthwhile to work for "exception safety" (Exception-safe functions) will not leak resources or allow any data structure to break even if an exception occurs. There are three possible guarantees: basic, strong Type, do not throw abnormal type) 30. Thorough understanding of inlining inside and outside (inlining is a compile-time behavior in most C ++ programs; whether an inline function is really inline depends on the compiler; most compilers refuse to be too complex (such as with loops or recursion) ) Function inlining, and all calls to virtual functions (unless they are the most bland) will also cause inlining to fail; the code expansion caused by inline may cause a loss of efficiency; inline functions cannot be upgraded with the upgrade of the library) 31. Minimize the compilation dependencies between files (if you can use object references or object pointers to complete the task, do not use objects; if possible, try to replace class definitions with class declarations; provide differences between declarative and definitions Header file) 32. Make sure your public inheritance moulds out an is-a (is a) relationship (applicable to everything in base classes must apply to derived classes, because every derived class object is also a base class object ) 33. Avoid obscuring inherited names (you can use using declarative or forwarding functions to make the obscure names goodbye) 34. Distinguish between interface inheritance and implementation inheritance (under public inheritance, derived classes always inherit the interface of the base class; pure virtual functions only specify interface inheritance; non-pure virtual virtual functions specify interface inheritance and default implementation inheritance; non -virtual function specifies interface inheritance and mandatory implementation inheritance) 35. Consider alternatives to virtual functions (such as the non-virtual interface (NVI) approach of the Template Method design pattern, replacing virtual functions with "function pointer member variables" and replacing virtual functions with `tr1 :: function` member variables, Replace the virtual function in the inheritance system with another virtual function in the inheritance system) 36. Never redefine inherited non-virtual functions 37. Never redefine inherited default parameter values, because the default parameter value is statically bound (statically bound), but the virtual function is dynamically bound (dynamically bound) 38. Through compound molding has-a (one) or "implemented according to something" (in the application domain, compound means has-a (there is one); in the implementation domain, compound means (Is-implemented-in-terms-of) 39. Use private inheritance wisely and prudently (private inheritance means is-implemented-in-terms-of (implemented according to something)), use composite whenever possible, when the derived class needs to access the members of the protected base class, or needs to re- Use private inheritance when defining inherited virtual functions, or when empty base optimization is required) 40. Use multiple inheritance wisely and prudently (multiple inheritance is more complex than single inheritance, may lead to new ambiguities, and the need for virtual inheritance, but it does have a legitimate purpose, such as "public inheritance of an interface class" and "private inheritance A class that assists implementation "; virtual inheritance can solve the ambiguity of diamond inheritance under multiple inheritance, but it will increase the size, speed, complexity of initialization and assignment, etc.) 41. Understand implicit interfaces and compile-time polymorphism (both classes and templates support interfaces and polymorphism); class interfaces are explicit with signatures as the center, and polymorphism is through virtual Functions occur at runtime; the interface of a template is implicit based on valid expressions, and polymorphism occurs through comprehension of templates and function overloading resolution at compile time) 42. Understand the double meaning of typename (declared that the template type parameter is that the meaning of the prefix keywords class and typename are exactly the same; use the keyword typename to identify the nested dependent type name, but not in the base class lists or members Use it as a base class modifier in the member initialization list) 43. Learn to deal with the names in templated base classes (you can refer to the member names in base class templates via `this->` in derived class templates, or by a clearly written "base class qualification modifier" ) 44. Extract parameter-independent code from templates (code bloat caused by non-type template parameters can often be eliminated by replacing template parameters with function parameters or class member variables; because of type parameters The resulting code bloat can often be achieved by having implementation types with identical binary representations share implementation codes) 45. Use member function templates to accept all compatible types (please use member function templates to generate functions that "accept all compatible types"; declare member templates for "generalized copy construction" or "generalized assignment operation" Also need to declare the normal copy constructor and copy assignment operator) 46. ​​When you need type conversion, please define non-member functions for the template (when we write a class template, and the "relevant to this template" function provided by it supports "implicit type conversion of all parameters", please those functions Defined as "friend function inside class template") 47. Please use traits classes to represent type information (traits classes use templates and "templates specialization" to make "type-related information" available at compile time, and use overloading (overloading) to implement if ... else on types at compile time test) 48. Recognize template metaprogramming (TMP, template metaprogramming) ### More Effective c++ 1. Carefully distinguish between pointers and references (when you know that you need to point to something and never change to point to other things, or when you implement an operator whose syntax requirements cannot be met by pointers, you should choose references; At any other time, use pointers) 2. It is best to use C ++ cast operators (`static_cast`, `const_cast`, `dynamic_cast`, `reinterpret_cast`) 3. Never polymorphically treat arrays (polymorphism and pointer arithmetic cannot be mixed; array objects almost always involve pointer arithmetic, so arrays and polymorphism should not be mixed) 4. Don't provide default constructor if necessary (to avoid fields in the object being initialized meaninglessly) 5. Be alert to custom "type conversion functions" (single argument constructors can be avoided by simple methods (explicit keywords) or proxy classes); implicit type conversion operators can be changed to explicit Member function to avoid unexpected behavior) 6. Distinguish the prefix and postfix forms of the increment / decrement operator (pre-accumulate and take out and return a reference; post-preparation take and accumulate and return a const object; when processing user-defined types Should use pre-increment as much as possible; post-implementation should be based on its pre-brother) 7. Never overload the `&&`, `||`, and `,` operators (`&&` and `||` overloading will replace “sudden-semantic semantics” with “function call semantics”; `The overload of` does not guarantee that the left expression must be evaluated earlier than the right expression) 8. Understand the different meanings of new and delete ( `new operator`, `operator new`, `placement new`, `operator new[]` ; `delete operator`, `operator delete`, `destructor`, `operator delete[]` ) 9. Use destructors to avoid leaking resources (freeing resources when destructors can avoid resource leaks during exceptions) 10. Prevent resource leaks in constructors (because C ++ will only destruct objects that have already been constructed, the constructor can use try ... catch or auto_ptr (and similar classes) to handle resource leaks when exceptions occur) 11. Prohibit exceptions from flowing out of destructors (reason: first, avoid the terminate function being called in the stack-unwinding mechanism of exception propagation; second, help ensure that destructors complete everything they should do) 12. Understand the difference between "throwing an exception" and "passing a parameter" or "calling a virtual function" (first, exception objects are always copied (except by pointer), if you catch even by value It is copied twice, but the object passed to the function parameter does not necessarily have to be copied. Second, the object that is "thrown as exceptions" has fewer allowed type conversion actions than the object "passed to the function"; Third, the catch clause is checked by the compiler for its "order in which it appears in the source code". The first match succeeds and is executed, and a virtual function is called. Function " 13. Capture exceptions by by reference (to avoid the problem of object deletion and cutting of exception objects, retain the ability to catch standard exceptions, and restrict the number of times an exception object needs to be copied) 14. Use exception specifications wisely (exception specifications provide an excellent description of what kind of exceptions a function expects to throw; there are also some disadvantages, including that the compiler only checks them locally and is easy to inadvertently violate them. Prevent higher-level exception handlers from handling unexpected exceptions) 15. Understand the cost of exception handling (roughly estimated, if try block is used, the overall code will expand by about 5% -10%, and the execution speed will also decrease by this number; therefore, please limit your use of try block and exception specifications Must-use locations, and throw exceptions only in case of real exceptions) 16. Keep in mind the 80-20 rule (the overall performance of software is almost always determined by a small part of its constituent elements (codes), and the code that consumes resources can be identified using a program profiler) 17. Consider using lazy evaluation (can be applied to: Reference Counting to avoid unnecessary object copying, distinguish read and write actions of operator [] to do different things, Lazy Fetching (easy (Remove) to avoid unnecessary database reads, and Lazy Expression Evaluation (to avoid unnecessary numerical calculations) 18. Amortize the expected computational cost (when you must support certain operations whose structure is almost always required, or when the results are often required multiple times, over-eager evaluation can improve program efficiency ) ### Google C ++ Style Guide * English: [Google C ++ Style Guide](https://google.github.io/styleguide/cppguide.html) * Chinese: [C ++ Style Guide](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/) ### Other * [Bjarne Stroustrup FAQ](http://www.stroustrup.com/bs_faq.html) * [C ++ style and trick FAQ for Bjarne Stroustrup](http://www.stroustrup.com/bs_faq2.html) ## 📦 STL ### STL index [STL Method Meaning Index](https://github.com/huihut/interview/tree/master/STL) ### STL container Container | Underlying data structure | Time complexity | Unordered | Can not be repeated | Other ---|---|---|---|---|--- [array](https://github.com/huihut/interview/tree/master/STL#array) | array | random read and change O(1) | unordered | repeatable | support random access [vector](https://github.com/huihut/interview/tree/master/STL#vector) | Array | Random read, tail insertion, tail deletion O(1)
head insertion, head Delete O(n) | Unordered | Repeatable | Support random access [deque](https://github.com/huihut/interview/tree/master/STL#deque) | Dual-end queue | End-to-end insertion, end-to-end deletion O(1) | Unordered | Repeatable | One central control + Multiple buffers, support rapid addition and deletion at the beginning and end, support random access [forward_list](https://github.com/huihut/interview/tree/master/STL#forward_list) | One-way linked list | Insert and delete O(1) | Unordered | Repeatable | Random access is not supported [list](https://github.com/huihut/interview/tree/master/STL#list) | Doubly linked list | Insert / delete O(1) | Unordered | Repeatable | Does not support random access [stack](https://github.com/huihut/interview/tree/master/STL#stack) | deque / list | top insert, top delete O(1) | unordered | repeatable | deque or list closed head The end is open. The reason why the vector is not used should be that the capacity is limited, and the expansion takes time. [queue](https://github.com/huihut/interview/tree/master/STL#queue) | deque / list | tail insertion, head deletion O(1) | unordered | repeatable | deque or list closure The head end is open. The reason why the vector is not used should be that the capacity is limited, and the expansion takes time. [priority_queue](https://github.com/huihut/interview/tree/master/STL#priority_queue) | vector + max-heap | Insert, delete O(log2n) | Ordered | Repeatable | vector container + heap processing rules [set](https://github.com/huihut/interview/tree/master/STL#set) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Not repeatable | [multiset](https://github.com/huihut/interview/tree/master/STL#multiset) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Repeatable | [map](https://github.com/huihut/interview/tree/master/STL#map) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Not repeatable | [multimap](https://github.com/huihut/interview/tree/master/STL#multimap) | Red and Black Tree | Insert, delete, find O(log2n) | Ordered | Repeatable | [unordered_set](https://github.com/huihut/interview/tree/master/STL#unordered_set) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Not Repeatable | [unordered_multiset](https://github.com/huihut/interview/tree/master/STL#unordered_multiset) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Repeatable | [unordered_map](https://github.com/huihut/interview/tree/master/STL#unordered_map) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Not Repeatable | [unordered_multimap](https://github.com/huihut/interview/tree/master/STL#unordered_multimap) | Hash Table | Insert, Delete, Find O(1) Worst O(n) | Unordered | Repeatable | ### STL Algorithm Algorithm | Low-level algorithm | Time complexity | Can not be repeated --- | --- | --- | --- [find](http://www.cplusplus.com/reference/algorithm/find/) | Sequence search | O(n) | Repeatable [sort](https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808) | [Introspection sorting](https://en.wikipedia.org/wiki/Introsort) | O(n*log2n) | Repeatable ## 〽️ Data Structure ### Sequence structure #### Sequential stack(Sequence Stack) [SqStack.cpp](DataStructure/SqStack.cpp) Sequential stack data structures and pictures ```cpp typedef struct { ElemType *elem; int top; int size; int increment; } SqStack; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqStack.png) #### queue(Sequence Queue) Queue data structure ```cpp typedef struct { ElemType * elem; int front; int rear; int maxSize; }SqQueue; ``` ##### Acyclic queue Acyclic queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqQueue.png) `SqQueue.rear++` ##### Circular queue Circular queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqLoopStack.png) `SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize` #### Sequence table(Sequence List) [SqList.cpp](DataStructure/SqList.cpp) Sequence table data structure and pictures ```cpp typedef struct { ElemType *elem; int length; int size; int increment; } SqList; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqList.png) ### Chain structure [LinkList.cpp](DataStructure/LinkList.cpp) [LinkList_with_head.cpp](DataStructure/LinkList_with_head.cpp) Chained data structure ```cpp typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkList; ``` #### Chain queue(Link Queue) Chain queue picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkQueue.png) #### Chained representation of a linear list ##### Singly-Linked list(Link List) Singly-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkList.png) ##### Doubly linked list(Du-Link-List) Doubly-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/DuLinkList.png) ##### Circular linked list(Cir-Link-List) Circular-linked list picture ![](https://raw.githubusercontent.com/huihut/interview/master/images/CirLinkList.png) ### Hash table [HashTable.cpp](DataStructure/HashTable.cpp) #### Concept Hash function:`H(key): K -> D , key ∈ K` #### Construction method * Direct Addressing * Divided remainder method * Digital analysis method * Folding method * The square method #### Collision resolution method * Chain address method: single linked list linked with the same key * Open Addressing * Linear detection method: same key-> put to the next position of key,`Hi = (H(key) + i) % m` * Secondary detection method: same key-> put to `Di = 1^2, -1^2, ..., ±(k)^2,(k<=m/2)` * Random detection:`H = (H(key) + Pseudo-random number) % m` #### Hash table data structure for linear probing Hash table data structure and pictures for linear detection ```cpp typedef char KeyType; typedef struct { KeyType key; }RcdType; typedef struct { RcdType *rcd; int size; int count; bool *tag; }HashTable; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/HashTable.png) ### Recursion #### Concept Function calls itself directly or indirectly #### Recursion and Divide * Divide and Conquer * Breakdown of the problem * Problem size decomposition * Half search (recursive) * Merge sort (recursive) * Quick sort (recursive) #### Recursion and Iteration * Iteration: use the old value of the variable repeatedly to push out the new value * Search in half (iterative) * Merge sort (iterative) #### Generalized table ##### Head and tail linked list storage representation Head and tail linked list storage representations and pictures for generalized tables ```cpp // Head-to-tail linked list storage representation of generalized list typedef enum {ATOM, LIST} ElemTag; // ATOM == 0: atom, LIST == 1: child table typedef struct GLNode { ElemTag tag; // public part, used to distinguish atomic nodes from table nodes union { // joint part of atomic node and table node AtomType atom; // atom is the range of atom nodes, AtomType is defined by the user struct { struct GLNode *hp, *tp; } ptr; /// ptr is the pointer field of the table node, prt.hp and ptr.tp point to the table header and table tail respectively } a; } *GList, GLNode; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList1.png) ##### Extended linear linked list storage representation Extended linear linked list storage representations and pictures ```cpp // Extended linear linked list storage representation of generalized tables typedef enum {ATOM, LIST} ElemTag; // ATOM == 0: atom, LIST == 1: child table typedef struct GLNode1 { ElemTag tag; // public part, used to distinguish atomic nodes from table nodes union { // joint part of atomic node and table node AtomType atom; // range of atom nodes struct GLNode1 *hp; // table pointer pointer } a; struct GLNode1 *tp; // Equivalent to next of a linear linked list, pointing to the next } *GList1, GLNode1; ``` ![](https://raw.githubusercontent.com/huihut/interview/master/images/GeneralizedList2.png) ### Binary tree [BinaryTree.cpp](DataStructure/BinaryTree.cpp) #### properties 1. Up to 2(i-1) nodes at the i-th level of a non-empty binary tree (i> = 1) 2. Binary tree with depth k up to 2k-1 node (k >= 1) 3. The number of nodes with degree 0 is n0, and the number of nodes with degree 2 is n2, then n0 = n2 + 1 4. Full binary tree depth with n nodes k = ⌊ log 2 (n) ⌋ + 1 5. For the node numbered i (1 <= i <= n) in a complete binary tree with n nodes 1. If i = 1, it is the root, otherwise the parents are ⌊ i / 2 ⌋ 2. If 2i > n, node i has no left child, otherwise the child number is 2i 3. If 2i + 1> n, the i node has no right child, otherwise the child number is 2i + 1 #### Storage structure Binary tree data structure ```cpp typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; ``` ##### Sequential storage Binary tree sequential storage pictures ![](https://raw.githubusercontent.com/huihut/interview/master/images/SqBinaryTree.png) ##### Chained storage Binary tree chain store pictures ![](https://raw.githubusercontent.com/huihut/interview/master/images/LinkBinaryTree.png) #### Traversal * Sequence traversal * In-order traversal * Subsequent traversals * Hierarchical traversal #### Categories * Full Binary Tree * Complete binary tree (heap) * Big top heap: root> = left && root> = right * Small top heap: root <= left && root <= right * Binary search tree (binary sort tree): left The difference between B-tree and B + -tree comes from:[differences-between-b-trees-and-b-trees](https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees)、[Difference between B-tree and B + tree](https://www.cnblogs.com/ivictor/p/5849061.html) #### Octree Octree picture ![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png) An octree is a tree-like data structure used to describe three-dimensional space (dividing space). Each node of the octree represents a volume element of a cube, and each node has eight child nodes. The volume elements represented by the eight child nodes are added together to equal the volume of the parent node. The general center point is used as the bifurcation center of the node. ##### Purpose * 3D computer graphics * Nearest Search ## ⚡️ Algorithm ### Sort Sorting Algorithm | Average Time Complexity | Worst Time Complexity | Spatial Complexity | Data Object Stability ---|---|---|---|--- [Bubble Sort](Algorithm/BubbleSort.h) | O(n2)|O(n2)|O(1)|Stable [Selection Sort](Algorithm/SelectionSort.h) | O(n2)|O(n2)|O(1)|The array is unstable and the linked list is stable [Insert Sort](Algorithm/InsertSort.h) | O(n2)|O(n2)|O(1)|Stable [Quick Sort](Algorithm/QuickSort.h) | O(n*log2n) | O(n2) | O(log2n) | Unstable [Heap Sort](Algorithm/HeapSort.cpp) | O(n*log2n)|O(n*log2n)|O(1)|Unstable [Merge Sort](Algorithm/MergeSort.h) | O(n*log2n) | O(n*log2n)|O(n)| Stable [Shell Sort](Algorithm/ShellSort.h) | O(n*log2n)|O(n2)|O(1)|Unstable [Count Sort](Algorithm/CountSort.cpp) | O(n+m)|O(n+m)|O(n+m)|Stable [Bucket Sort](Algorithm/BucketSort.cpp) | O(n)|O(n)|O(m)|Stable [Radix Sort](Algorithm/RadixSort.h) | O(k*n)|O(n2)| |Stable > * Are arranged in ascending order > * k: represents the number of "digits" in the value > * n: represents the size of the data > * m: represents the maximum value minus the minimum value of the data > * From: [wikipedia. Sorting Algorithm](https://en.wikipedia.org/wiki/Sorting_algorithm) ### Find Find Algorithm | Average Time Complexity | Spatial Complexity | Find Conditions ---|---|---|--- [SequentialSearch](Algorithm/SequentialSearch.h) | O(n) | O(1) | sorted or unsorted [Binary search (half search)](Algorithm/BinarySearch.h) | O(log2n)| O(1) | sorted [Insertion Search](Algorithm/InsertionSearch.h) | O(log2(log2n)) | O(1) | sorted [Fibonacci Search](Algorithm/FibonacciSearch.cpp) | O(log2n) | O(1) | soted [Hash Table](DataStructure/HashTable.cpp) | O(1) | O(n) | sorted or unsorted [Binary Search Tree (BST Search)](Algorithm/BSTSearch.h) |O(log2n) | | [Red Black Tree](DataStructure/RedBlackTree.cpp) |O(log2n) | | 2-3 Tree | O(log2n - log3n) | | B Tree/B+ Tree |O(log2n) | | ### Graph search algorithm Graph Search Algorithm | Data Structure | Traversal Time Complexity | Spatial Complexity ---|---|---|--- [BFS - Breadth First Search](https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|adjacency matrix
adjacency list |O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) [DFS - Depth-First Search](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)| adjacency matrix
adjacent linked list |O(\|v\|2)
O(\|v\|+\|E\|)|O(\|v\|2)
O(\|v\|+\|E\|) ### Other algorithms Algorithms | Ideas | Applications --- | --- | --- [Divide and Conquer](https://en.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95) | Divide a complex problem into two or more The same or similar sub-problems, until the last sub-problem can be simply and directly solved, the solution of the original problem is the combination of the solutions of the sub-problems | [loop schedule problem](https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem), sorting algorithms (quick sort, merge sort) [Dynamic Programming](https://en.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92) | By decomposing the original problem into relative Simple sub-question way to solve complex problems, suitable for problems with overlapping sub-problems and optimal sub-structure properties | [knapsack problem] KnapsackProblem), Fibonacci sequence [Greedy method](https://en.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95) | A kind of choice is taken in the current state in each step The best or optimal (that is, the most advantageous) choice, so that the result is the best or optimal algorithm | Travel Salesman Problem (Shortest Path Problem), Minimum Spanning Tree, Huffman Coding ## ❓ Problems ### Single Problem * [Chessboard Coverage Problem (Checkerboard coverage problem)](Problems/ChessboardCoverageProblem) * [Knapsack Problem](Problems/KnapsackProblem) * [Neumann Neighbor Problem](Problems/NeumannNeighborProblem) * [Round Robin Problem](Problems/RoundRobinProblem) * [Tubing Problem](Problems/TubingProblem) ### Leetcode Problems * [Github . haoel/leetcode](https://github.com/haoel/leetcode) * [Github . pezy/LeetCode](https://github.com/pezy/LeetCode) ### Sword Finger Offer * [Github . zhedahht/CodingInterviewChinese2](https://github.com/zhedahht/CodingInterviewChinese2) * [Github . gatieme/CodingInterviews](https://github.com/gatieme/CodingInterviews) ### Cracking the Coding Interview (Programmer Interview Gold) * [Github . careercup/ctci](https://github.com/careercup/ctci) * [Niu Ke. Programmer interview golden code](https://www.nowcoder.com/ta/cracking-the-coding-interview) ### Niu Ke * [Niu Ke . Online programming topics](https://www.nowcoder.com/activity/oj) ## 💻 Operating system ### Processes and threads For threaded systems: * Processes are independent units of resource allocation * Thread is an independent unit of resource scheduling For non-threaded systems: * Process is an independent unit of resource scheduling and allocation #### Communication between processes and advantages and disadvantages * Pipeline (PIPE) * Named pipes: A first-in-first-out communication method that allows communication between unrelated processes * Advantages: can achieve inter-process communication in any relationship * Disadvantages: 1. Long-term storage in the system, improper use is prone to errors Limited buffer * Anonymous pipes: A simplex first-in-first-out communication method that can only be used between processes with affinity (parent-child processes) * Advantages: simple and convenient * Disadvantages: Limited to one-way communication 2. Can only be created between its processes and their related processes 3. Limited buffer * Semaphore: a counter that can be used to control access to shared resources by multiple threads * Advantages: can synchronize processes * Disadvantage: limited semaphore * Signal (Signal): a more complex communication method used to notify the receiving process that an event has occurred * Message Queue: a linked list of messages, stored in the kernel and identified by the message queue identifier * Advantages: can achieve communication between any process, and achieve synchronization between message sending and receiving through system call functions, no need to consider synchronization issues, convenient * Disadvantages: Copying information requires additional CPU time, which is not suitable for situations with large amounts of information or frequent operations * Shared Memory: Maps a piece of memory that can be accessed by other processes. This shared memory is created by one process, but can be accessed by multiple processes. * Advantages: no need to copy, fast, large amount of information * Disadvantages: 1. Communication is achieved by directly attaching the shared space buffer to the virtual address space of the process, so the synchronization of read and write operations between processes 2. Use the memory buffer to directly exchange information. The entity of memory exists in the computer and can only be shared with many processes in a computer system, which is not convenient for network communication. * Socket (Socket): can be used for process communication between different computers * Advantages: 1. The transmission data is byte level, the transmission data can be customized, the data volume is small and the efficiency is high 2. Short data transmission time and high performance 3. Suitable for real-time information exchange between client and server 4. Can be encrypted, strong data security * Disadvantages: The transmitted data needs to be parsed and converted into application-level data. #### Communication between threads * Locking mechanism: including mutex, reader-writer lock, spin lock, and condition * Mutex (mutex): Provides an exclusive way to prevent data structures from being modified concurrently. * Reader-writer lock: Allow multiple threads to read shared data at the same time, and are mutually exclusive for write operations. * Spin locks are similar to mutex locks in order to protect shared resources. The mutex is when the resource is occupied and the applicant goes to sleep; the spin lock circularly detects whether the holder has released the lock. * Condition variable (condition): you can atomically block the process until a certain condition is true. Testing of conditions is performed under the protection of a mutex. Condition variables are always used with a mutex. * Semaphore mechanism * Unknown thread semaphore * Named thread semaphore * Signal mechanism (Signal): similar to signal processing between processes * Barrier: A barrier allows each thread to wait until all cooperating threads reach a certain point, and then continue execution from that point. The purpose of communication between threads is mainly for thread synchronization, so threads have no communication mechanism for data exchange like in process communication. > The communication methods between processes and their advantages and disadvantages come from: [Process thread interview question summary](http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977) #### Private and shared resources between processes * Private: address space, heap, global variables, stack, registers * Share: code snippet, public data, process directory, process ID #### Private and shared resources between threads * Private: thread stack, register, program counter * Shared: heap, address space, global variables, static variables #### Comparison, advantages and disadvantages of multi-process and multi-thread ##### Compared Contrast Dimensions | Multi-Process | Multi-Threaded | Summary --- | --- | --- | --- Data sharing and synchronization | Complex data sharing requires IPC; data is separate and easy to synchronize | Because process data is shared, data sharing is simple, but it is also because of this reason that synchronization is complicated | Each advantage Memory, CPU | Occupies more memory, complex switching, low CPU utilization | Less memory, simple switching, high CPU utilization | Threads dominate Create destroy, switch | create destroy, switch complex, slow speed | create destroy, switch simple, fast speed | thread dominant Programming, debugging | simple programming, simple debugging | complex programming, complex debugging | process dominates Reliability | Processes will not affect each other | Hanging a thread will cause the entire process to hang | Process dominance Distributed | Applicable to multi-core, multi-machine distributed; if one machine is not enough, it is relatively simple to expand to multiple machines | adapted to multi-core distributed | process dominant ##### Pros and cons Pros and cons | multi-process | multi-threaded --- | --- | --- Advantages | Simple programming and debugging, high reliability | Fast creation, destruction, switching, small memory and resource occupation Disadvantages | Creating, destroying, slow switching, large memory and resource occupation | Complex programming and debugging, poor reliability ##### Select * Need to frequently create and destroy priority threads * Priority threads that require a lot of calculations * Strongly related processing threads, weakly related processing processes * May be extended to multi-machine distributed processes, multi-core distributed threads * When all meet the needs, use the method you are most familiar with > The comparison, advantages and disadvantages of multi-process and multi-thread come from: [multi-thread or multi-process selection and difference](https://blog.csdn.net/lishenglong666/article/details/8557215) ### Linux kernel synchronization #### the reason In modern operating systems, there may be multiple kernel execution streams executing at the same time, so the kernel actually needs some synchronization mechanism to synchronize the access of the execution units to shared data like multi-process and multi-thread programming. Especially on multiprocessor systems, some synchronization mechanisms are needed to synchronize the access of the execution units on different processors to shared data. #### Synchronously * Atomic operations * Semaphore * Read and write semaphore (rw_semaphore) * Spinlock * Big Kernel Lock (BKL, Big Kernel Lock) * Read-write lock (rwlock) * Brlock-Big Reader Lock * Read-Copy Update (RCU, Read-Copy Update) * Sequence lock (seqlock) > From: [Linux kernel synchronization mechanism, Part 1](https://www.ibm.com/developerworks/cn/linux/l-synch/part1/), [Linux kernel synchronization mechanism, Part 2](https://www.ibm.com/developerworks/cn/linux/l-synch/part2/) ### Deadlock #### Reasons * Insufficient system resources * Improper allocation of resources * The progress order of the process operation is not suitable #### Generate conditions * Mutex * Request and hold * Not deprived * Loop #### Prevention * Breaking mutually exclusive conditions: transforming exclusive resources into virtual resources, most of which cannot be transformed. * Break the non-preemption condition: when a process occupies an exclusive resource and then applies for an exclusive resource that cannot be met, it will withdraw from the original resource. * Break possession and apply conditions: Adopt a resource pre-allocation strategy, that is, apply for all resources before the process runs, run if it meets, or wait, so that it will not be occupied and applied. * Break the loop waiting condition: implement the orderly allocation of resources, implement classification numbers for all devices, and all processes can only apply for resources in the form of increasing numbers. * Ordered resource allocation method * Banker Algorithm ### File system * Windows: FCB table + FAT + bitmap * Unix: inode + mixed index + group link ### Host byte order and network byte order #### Host Endianness (CPU Endianness) ##### Concept The host byte order is also called the CPU byte order. It is not determined by the operating system, but by the CPU instruction set architecture. There are two types of host byte order: * Big Endian: The high-order byte is stored in the low-order address, and the low-order byte is stored in the high-order address * Little Endian: The high-order byte is stored in the high-order address, and the low-order byte is stored in the low-order address. ##### Storage method The 32-bit integer `0x12345678` is stored from the address starting at` 0x00`, then: Memory address | 0x00 | 0x01 | 0x02 | 0x03 --- | --- | --- | --- | --- Big Endian | 12 | 34 | 56 | 78 Little Endian | 78 | 56 | 34 | 12 Big-endian little-endian pictures ![Big endian](https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Big-Endian.svg.png) ![Little endian](https://raw.githubusercontent.com/huihut/interview/master/images/CPU-Little-Endian.svg.png) ##### Judging big-endian little-endian Judge big-endian little-endian You can determine whether your CPU byte order is big-endian or little-endian: ```cpp #include using namespace std; int main() { int i = 0x12345678; if (*((char*)&i) == 0x12) cout << "Big endian" << endl; else cout << "Little endian" << endl; return 0; } ``` ##### Byte order of each architecture processor * x86 (Intel, AMD), MOS Technology 6502, Z80, VAX, PDP-11 and other processors are little-endian; * Motorola 6800, Motorola 68000, PowerPC 970, System/370, SPARC (except V9) processors are big-endian; * The endianness of ARM (default little-endian), PowerPC (except PowerPC 970), DEC Alpha, SPARC V9, MIPS, PA-RISC, and IA64 are configurable. #### Network byte order Network byte order is a data representation format specified in TCP/IP. It has nothing to do with the specific CPU type, operating system, etc., so that it can ensure that the data can be correctly interpreted when transmitted between different hosts. The network byte order uses: Big Endian arrangement. ### Page replacement algorithm During the address mapping process, if a page to be accessed is found not to be in memory, a page fault interrupt is generated. When a page fault occurs, if there is no free page in the operating system memory, the operating system must select a page in memory to move it out of memory in order to make room for the page that is about to be paged in. The rules used to select which pages to eliminate are called page replacement algorithms. #### Categories * Global replacement: replacement in the entire memory space * Partial replacement: Replace in this process #### Algorithm Global: * Working set algorithm * Page rate replacement algorithm Partially: * Optimal Replacement Algorithm (OPT) * First in, first out replacement algorithm (FIFO) * Least Recently Used (LRU) Algorithm * Clock replacement algorithm ## ☁️ Computer Network > Some of the knowledge in this section comes from Computer Networking (7th Edition) Computer network architecture: ![Computer Network Architecture](https://raw.githubusercontent.com/huihut/interview/master/images/计算机网络体系结构.png) ### Roles and protocols of each layer Layered | role | protocol --- | --- | --- Physical layer | Transmission of bits through the medium, determination of mechanical and electrical specifications (bits) | RJ45, CLOCK, IEEE802.3 (repeaters, hubs) Data link layer | Assemble bits into frames and point-to-point delivery (Frame) | PPP, FR, HDLC, VLAN, MAC (bridge, switch) Network layer | Responsible for the transmission of data packets from source to sink and Internet interconnection (packets) | IP, ICMP, ARP, RARP, OSPF, IPX, RIP, IGRP (router) Transport layer | Provide end-to-end reliable message delivery and error recovery (Segment) | TCP, UDP, SPX Session layer | Establishing, managing, and terminating sessions (session protocol data unit SPDU) | NFS, SQL, NETBIOS, RPC Presentation Layer | Translate, Encrypt, and Compress Data (Representation Protocol Data Unit PPDU) | JPEG, MPEG, ASII Application layer | A means to allow access to the OSI environment (Application Protocol Data Unit APDU) | FTP, DNS, Telnet, SMTP, HTTP, WWW, NFS ### Physical layer * Unit of transmitted data: bit * Data transmission system: source system (source point, sender)-> transmission system-> destination system (receiver, destination) aisle: * Unidirectional channel (simplex channel): only one direction communication, no reverse direction interaction, such as broadcasting * Two-way alternate communication (half-duplex communication): both parties of the communication can send messages, but cannot send or receive at the same time * Two-way simultaneous communication (full-duplex communication): both parties of the communication can send and receive information at the same time Channel multiplexing technology: * Frequency division multiplexing (FDM, Frequency Division Multiplexing): different users in different frequency bands, users use different bandwidth resources at the same time * Time Division Multiplexing (TDM): different users in different time slices in the same time period, all users occupy the same bandwidth at different times * Wavelength Division Multiplexing (WDM): Frequency division multiplexing of light * Code Division Multiplexing (CDM): Different users use different codes and can use the same frequency band for communication at the same time ### data link layer Main channel: * Point-to-point channel * Broadcast channel #### Point-to-point channel * Data unit: frame Three basic questions: * Encapsulation: Encapsulate IP datagrams at the network layer into frames, `SOH-data part-EOT` * Transparent transmission: No matter what characters in the data part, it can be transmitted; it can be solved by byte padding method (escaping characters before conflicting characters) * Error detection: reduce bit error rate (BER), widely use cyclic redundancy check (CRC, Cyclic Redundancy Check) Point-to-Point Protocol: * Point-to-Point Protocol: The protocol used by the user computer to communicate with the ISP #### Broadcast Channel Broadcast communication: * Hardware address (physical address, MAC address) * Unicast frame (one-to-one): The MAC address of the received frame is the same as the hardware address of this station * Broadcast frame (one pair): frame sent to all stations on the local area network * Multicast frame (one-to-many): frame sent to some sites on the local area network ### Network layer * IP (Internet Protocol, Internet Protocol) is a protocol designed for computer networks to communicate with each other. * ARP (Address Resolution Protocol, Address Resolution Protocol) * ICMP (Internet Control Message Protocol, Internet Control Message Protocol) * IGMP (Internet Group Management Protocol, Internet Group Management Protocol) #### IP Internet Protocol IP address classification: * `IP address ::= (, )` IP address category | network number | network range | host number | IP address range --- | --- | --- | --- | --- Class A | 8bit, the first bit is fixed at 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255 Class B | 16bit, the first two bits are fixed at 10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255 Class C | 24bit, the first three bits are fixed at 110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255 Class D | The first four bits are fixed at 1110, followed by the multicast address Class E | The first five digits are fixed at 11110, the latter are reserved for future use IP datagram format: ![IP 数据报格式](https://raw.githubusercontent.com/huihut/interview/master/images/IP数据报格式.png) #### ICMP Internet Control Message Protocol ICMP message format: ![ICMP message format](https://raw.githubusercontent.com/huihut/interview/master/images/ICMP报文格式.png) application: * PING (Packet InterNet Groper) to test connectivity between two hosts * TTL (Time To Live, time to live) This field specifies the maximum number of network segments that IP packets are allowed to pass before being dropped by the router. #### Interior Gateway Protocol * RIP (Routing Information Protocol, Routing Information Protocol) * OSPF (Open Shortest Path First) #### External gateway protocol * BGP (Border Gateway Protocol, Border Gateway Protocol) #### IP multicast * IGMP (Internet Group Management Protocol, Internet Group Management Protocol) * Multicast routing protocol #### VPN and NAT * VPN (Virtual Private Network) * NAT (Network Address Translation, Network Address Translation) #### What does the routing table contain? 1. Network ID (Network ID): It is the network ID of the destination address. 2. Subnet mask: used to determine the network to which the IP belongs 3. Next hop address / interface: It is the address of the next stop of the data on the journey sent to the destination address. Where interface points to the next hop (that is, the next route). The route in an autonomous system (AS) should contain all subnets in the area, and the default gateway (Network id: `0.0.0.0`, Netmask:` 0.0.0.0`) points to the exit of the autonomous system. Depending on the application and implementation, the routing table may contain the following additional information: 1. Cost: It is the cost required to pass the path during data transmission. 2. Quality of service for routing 3. List of inbound / outbound connections to be filtered in the route ### Transport layer protocol: * TCP (Transmission Control Protocol) * UDP (User Datagram Protocol, User Datagram Protocol) port: Applications | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP --- | --- | --- | --- | --- | --- | --- | --- | --- Port number | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161 #### TCP * TCP (Transmission Control Protocol, Transmission Control Protocol) is a connection-oriented, reliable, byte stream-based transport layer communication protocol, and its transmission unit is a message segment. Feature: * Connection oriented * End-to-end communication * Reliable interaction * Full-duplex communication * Byte stream oriented How TCP guarantees reliable transmission: * Confirmation and timeout retransmission * Reasonable fragmentation and ordering of data * flow control * Congestion control * Data validation TCP message structure ![TCP Message](https://raw.githubusercontent.com/huihut/interview/master/images/TCP报文.png) TCP header ![TCP header](https://raw.githubusercontent.com/huihut/interview/master/images/TCP首部.png) TCP: Status Control Code (Code, Control Flag), which takes 6 bits and has the following meaning: * URG: Urgent. When `URG = 1` , it indicates that the urgent pointer field is valid, which means that the packet is an urgent packet. It tells the system that there is urgent data in this segment and it should be transmitted as soon as possible (equivalent to high-priority data), and the Urgent Pointer field in the above figure will also be enabled. * ACK: Acknowledge. The acknowledgment number field is valid only when `ACK = 1`, which means this packet is an acknowledgment packet. When `ACK = 0`, the confirmation number is invalid. * PSH: (Push function) If it is 1, the representative requires the other party to immediately transmit other corresponding packets in the buffer, without waiting for the buffer to be full before sending. * RST: Reset bit (Reset). When `RST = 1`, it indicates that a serious error occurred in the TCP connection (such as due to a host crash or other reasons). You must release the connection and then re-establish the transport connection. * SYN: Synchronous. When SYN is set to 1, it indicates that this is a connection request or connection acceptance message. Usually, the packet with the SYN flag indicates that "active" wants to connect to the other party. * FIN: Final bit, used to release a connection. When `FIN = 1`, it means that the data of the sending end of this segment has been sent, and it is required to release the transport connection. #### UDP * UDP (User Datagram Protocol, User Datagram Protocol) is a connectionless transport layer protocol in the OSI (Open System Interconnection) reference model, which provides a simple and unreliable information transfer service oriented to transactions, and its transmission unit Is a user datagram. feature: * no connection * Best efforts to deliver * Message-oriented * No congestion control * Supports one-to-one, one-to-many, many-to-one, and many-to-many interactive communication * Small overhead UDP message structure ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/UDP报文.png) UDP 首部 ![UDP 首部](https://raw.githubusercontent.com/huihut/interview/master/images/UDP首部.png) > TCP/UDP Pictures from #### Difference between TCP and UDP 1. TCP connection-oriented, UDP is connectionless; 2. TCP provides reliable service, that is, the data transmitted through the TCP connection is error-free, not lost, not duplicated, and arrives in order; UDP does its best to deliver, that is, it does not guarantee reliable delivery 3. TCP logical communication channel is a full-duplex reliable channel; UDP is an unreliable channel 5. Each TCP connection can only be point-to-point; UDP supports one-to-one, one-to-many, many-to-one and many-to-many interactive communication 6. TCP is oriented to byte streams (probably sticky packets may occur). In fact, TCP treats data as a series of unstructured byte streams; UDP is message-oriented (no sticky packets will occur) 7. UDP has no congestion control, so congestion on the network will not reduce the sending rate of the source host (useful for real-time applications, such as IP phones, real-time video conferencing, etc.) 8. TCP header overhead is 20 bytes; UDP header overhead is small, only 8 bytes #### TCP Sticky Packet ##### the reason TCP is a byte stream-based transmission service (UDP is message-based). "Stream" means that the data transmitted by TCP has no boundaries. So there may be situations where two data packets stick together. ##### Solve * Send fixed-length packets. If the size of each message is the same, the receiving peer only needs to accumulate the received data until the data is equal to a fixed-length value and treats it as a message. * Baotou plus package body length. The header is 4 bytes of fixed length, which indicates the length of the packet body. The receiving peer first receives the header length and receives the packet body according to the header length. * Set boundaries between packets, such as adding special symbols `\r\n` mark. The FTP protocol does just that. The problem is that if the data body also contains `\r\n` , it will be misjudged as the message boundary. * Use more complex application layer protocols. #### TCP Flow Control ##### Concept Flow control is to make the sending rate of the sender not too fast, but to allow the receiver to receive it. ##### Methods Variable window for flow control ![](https://raw.githubusercontent.com/huihut/interview/master/images/利用可变窗口进行流量控制举例.png) #### TCP congestion control ##### Concept Congestion control is to prevent excessive data from being injected into the network, so that the routers or links in the network will not be overloaded. ##### Methods * Slow start * Congestion avoidance * Fast retransmit * Fast recovery TCP Congestion control graph ![](https://raw.githubusercontent.com/huihut/interview/master/images/TCP拥塞窗口cwnd在拥塞控制时的变化情况.png) ![](https://raw.githubusercontent.com/huihut/interview/master/images/快重传示意图.png) ![](https://raw.githubusercontent.com/huihut/interview/master/images/TCP的拥塞控制流程图.png) #### TCP Transmission connection management > 因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述: ##### TCP 三次握手建立连接 ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/TCP三次握手建立连接.png) 【Explain the whole process of TCP connection establishment】 1. The client sends a SYN to the server, indicating that the client requests to establish a connection; 2. The server receives the SYN sent by the client and replies with SYN + ACK to the client (agreeing to establish a connection); 3. After receiving the SYN + ACK from the server, the client responds with an ACK to the server (indicating that the client received the consent message sent by the server); 4. The server receives the ACK from the client, the connection is established, and data transmission is possible. ##### Why does TCP have to shake hands three times? 【Answer One】 Because the channel is unreliable, and TCP wants to establish a reliable transmission on the unreliable channel, then three communications is the theoretical minimum. (And UDP does not need to establish a reliable transmission, so UDP does not require a three-way handshake.) > [Google Groups. Why is TCP a three-way handshake to establish a connection? {Technology} {Network Communication}](https://groups.google.com/forum/#!msg/pongba/kF6O7-MFxM0/5S7zIJ4yqKUJ) [Answer II] Because both parties need to confirm that the other party has received the serial number sent by themselves, the confirmation process requires at least three communications. > [Know. Why is TCP a three-way handshake instead of two or four? ](https://www.zhihu.com/question/24853633/answer/115173386) [Answer three] In order to prevent the invalid connection request segment from being sent to the server suddenly, an error is generated. > ["Computer Network (7th Edition) -Xie Xiren"](https://raw.githubusercontent.com/huihut/interview/master/images/TCP-transport-connection-management.png) ##### TCP Four waves to release the connection ![UDP 报文](https://raw.githubusercontent.com/huihut/interview/master/images/TCP四次挥手释放连接.png) 【TCP Four waves to release the connection】 1. The client sends FIN to the server, indicating that the client does not need to send data to the server (request to release the connection from the client to the server); 2. The server receives the FIN sent by the client and replies ACK to the client (agreeing to release the connection from the client to the server); 3. The client receives the ACK from the server, and the connection from the client to the server has been released (but the connection from the server to the client has not been released, and the client can still receive data); 4. The server continues to send the unfinished data to the client; 5. The server sends FIN + ACK to the client, indicating that the server has sent the data (request to release the connection from the server to the client, even if no reply is received from the client, it will be automatically released after a certain period of time); 6. The client receives the FIN + ACK from the server and replies to the server with an ACK (agreeing to release the connection from the server to the client); 7. After receiving the ACK from the client, the server releases the connection from the server to the client. ##### Why does TCP have to wave four times? [Question 1] Why does TCP wave four times? / Why does TCP need three times to establish a connection and four times to release a connection? [Answer One] Because TCP is in full-duplex mode, after the client requests to close the connection, the connection from the client to the server is closed (one or two waves), and the server continues to transmit the data that has not been previously transmitted to the client (data transmission) , The connection from the server to the client is closed (waving three or four times). Therefore, when TCP releases the connection, the server's ACK and FIN are sent separately (with data transmission in between), and when the TCP establishes the connection, the server's ACK and SYN are sent together (second handshake), so TCP needs to establish the connection three times. It takes four times to release the connection. [Question 2] Why can ACK and SYN be sent together when TCP is connected, and ACK and FIN are sent separately when released? (ACK and FIN refer to the second and third wave) [Answer 2:] When the client requests the release, the server may still have data to transmit to the client, so the server must respond to the client FIN request (the server sends an ACK), and then the data is transmitted. After the transmission is completed, the server then Make a FIN request (the server sends FIN); there is no intermediate data transmission when connecting, so ACK and SYN can be sent together when connecting. [Question three] Why does the client need TIME-WAIT to wait for 2MSL at the end? 【Answer three】 1. In order to ensure that the last ACK message sent by the client can reach the server. If it fails to arrive, the server will retransmit the FIN + ACK segment overtime, and the client will retransmit the ACK and re-time. 2. Prevent invalid connection request segments from appearing in this connection. When TIME-WAIT lasts 2MSL, all the segments generated during the duration of this connection will disappear from the network, so that the old connection segments will not appear in the next connection. #### TCP finite state machine TCP finite state machine picture ![TCP Finite state machine](https://raw.githubusercontent.com/huihut/interview/master/images/TCP的有限状态机.png) ### Application layer #### DNS * DNS (Domain Name System) is a service of the Internet. It serves as a distributed database that maps domain names and IP addresses to each other, enabling people to access the Internet more conveniently. DNS uses TCP and UDP port 53. Currently, the limit for the length of each domain name is 63 characters, and the total length of the domain name cannot exceed 253 characters. domain name: * `Domain name :: = {..}`, for example: `blog.huihut.com` #### FTP * FTP (File Transfer Protocol, file transfer protocol) is a set of standard protocols for file transfer on the network, using the client / server model, using TCP datagrams, providing interactive access, two-way transmission. * TFTP (Trivial File Transfer Protocol) is a small and easy-to-implement file transfer protocol. It also uses the client-server method and uses UDP datagrams. It only supports file transfers and does not support interaction. User authentication #### TELNET * The TELNET protocol is a member of the TCP / IP protocol family and is the standard protocol and main method of the Internet remote login service. It provides users with the ability to complete remote host work on the local computer. * HTTP (HyperText Transfer Protocol) is a transfer protocol used to transfer hypertext from a WWW (World Wide Web, World Wide Web) server to a local browser. * SMTP (Simple Mail Transfer Protocol) is a set of rules for transferring mail from the source address to the destination address, which controls the transfer method of the letter. The SMTP protocol belongs to the TCP / IP protocol suite, which helps each computer find the next destination when sending or transferring letters. * Socket requires at least a pair of port numbers (Socket) to establish a network communication connection. Socket is essentially a programming interface (API), which encapsulates TCP / IP. TCP / IP also provides an interface that programmers can use for network development. This is the Socket programming interface. #### WWW * WWW (World Wide Web, World Wide Web, World Wide Web) is a system composed of many hypertext links to each other, accessed via the Internet ##### URL * URL (Uniform Resource Locator) is a standard resource address (Address) on the Internet standard format: * `Protocol type: [// server address [: port number]] [/ resource level UNIX file path] file name [? Query] [# Clip ID]` Complete format: * `Protocol type: [// [Credential information required to access resources @] server address [: port number]] [/ resource level UNIX file path] file name [? Query] [# segment ID]` > Among them [access credential information @ ;: port number ;? query; #fragment ID] are all optional items > For example: `https://github.com/huihut/interview#cc` ##### HTTP Hypertext Transfer Protocol (HTTP) is an application-layer protocol for distributed, collaborative, and hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. Request method Method | meaning --- | --- OPTIONS | Request some option information to allow clients to view server performance GET | Request the specified page information and return the entity body HEAD | Similar to a get request, except that there is no specific content in the returned response, which is used to get the header POST | Submits data to a specified resource for processing requests (such as submitting a form or uploading a file). The data is contained in the request body. POST requests may result in the creation of new resources and / or modification of existing resources PUT | Data transmitted from the client to the server replaces the contents of the specified document DELETE | request the server to delete the specified page TRACE | echo requests received by the server, mainly for testing or diagnostics Status Code * 1xx: indicates notification information, such as the request has been received or is being processed * 100 Continue: Continue, the client should continue its request * 101 Switching Protocols. The server switches protocols according to the client's request. Can only switch to higher-level protocols, for example, to new versions of HTTP * 2xx: indicates success, such as receiving or knowing * 200 OK: The request was successful * 3xx: indicates a redirect, and further action is required to complete the request * 301 Moved Permanently: Move permanently. The requested resource has been permanently moved to the new URL, the returned information will include the new URL, and the browser will automatically be directed to the new URL. Any future requests should use the new URL instead * 4xx: indicates a customer error, such as incorrect syntax in the request or inability to complete * 400 Bad Request: The client requested a syntax error that the server could not understand * 401 Unauthorized: request requires user authentication * 403 Forbidden: The server understood the request from the requesting client, but refused to execute the request (insufficient permissions) * 404 Not Found: The server cannot find the resource (web page) based on the client's request. With this code, the website designer can set a personality page for "the resource you requested could not be found" * 408 Request Timeout: The server waited too long for the request sent by the client, timed out * 5xx: indicates a server error, such as the server failing to complete the request * 500 Internal Server Error: Server internal error, unable to complete request * 503 Service Unavailable: Due to overload or system maintenance, the server cannot temporarily process the client's request. The length of the delay can be included in the server's Retry-After header * 504 Gateway Timeout: The server acting as a gateway or proxy, not receiving requests from the remote server in a timely manner >More status codes: [Rookie Tutorials. HTTP Status Codes](http://www.runoob.com/http/http-status-codes.html) ##### Other agreements * SMTP (Simple Main Transfer Protocol, Simple Mail Transfer Protocol) is a standard for transmitting Email over the Internet. It is a relatively simple text-based protocol. One or more recipients of a message are specified on top of it (confirmed to exist in most cases), and the message text is transmitted. You can easily test an SMTP server through a Telnet program. SMTP uses TCP port 25. * DHCP (Dynamic Host Configuration Protocol) is a network protocol for a local area network. It uses the UDP protocol to work and has two main purposes: * For internal network or network service provider to automatically assign IP addresses to users * For internal network administrators as a means of central management of all computers * SNMP (Simple Network Management Protocol) forms part of the Internet protocol family defined by the Internet Engineering Task Force (IETF). The protocol can support a network management system to monitor whether the devices connected to the network have any management concerns. ## 🌩 Network Programming ### Socket > [Linux Socket Programming (unlimited Linux)](https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html) ![Socket Client server communication](https://raw.githubusercontent.com/huihut/interview/master/images/socket客户端服务器通讯.jpg) #### Socket middle read()、write() function ```cpp ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); ``` ##### read() * The read function is responsible for reading content from fd. * When the read is successful, read returns the actual number of bytes read. * If the returned value is 0, it means that the end of the file has been read, and if it is less than 0, an error has occurred. * If the error is EINTR, the reading is caused by interruption; if it is ECONNREST, there is a problem with the network connection. ##### write () * The write function writes the contents of nbytes in buf to the file descriptor fd. * Returns the number of bytes written when successful. On failure, it returns -1 and sets the errno variable. * In network programs, there are two possibilities when we write to the socket file descriptor. * (1) The return value of write is greater than 0, indicating that part or all of the data has been written. * (2) The returned value is less than 0, and an error has occurred at this time. * If the error is EINTR, it indicates that an interruption error occurred during writing; if it is EPIPE, it indicates that there is a problem with the network connection (the other party has closed the connection). #### TCP three-way handshake in socket to establish connection We know that TCP establishes a connection by performing a "three-way handshake", that is, exchanging three packets. The general process is as follows: 1. The client sends a SYN J to the server 2. The server responds to the client with a SYN K, and confirms SYN J ACK J + 1 3. The client wants the server to send an acknowledgement ACK K + 1 Only the three-way handshake is finished, but what about the three-way handshake in the socket function? Please see the picture below: ![socket Sent in TCP Three handshake](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png) It can be seen from the figure: 1. When the client calls connect, a connection request is triggered and a SYN J packet is sent to the server. At this time, connect enters a blocking state; 2. The server listens to the connection request, that is, receives the SYN J packet, calls the accept function to receive the request and sends SYN K and ACK J + 1 to the client, then accept enters the blocking state; 3. After the client receives the SYN K of the server, ACK J + 1, connect returns at this time, and confirms the SYN K; 4. When the server receives ACK K + 1, accept returns, so that the three handshake is completed and the connection is established. #### TCP four-way handshake in socket to release connection The above describes the three-way handshake establishment process of TCP in socket and the socket functions involved. Now we introduce the process of releasing the connection by the four-way handshake in the socket, please see the following figure: ![socket TCP four-way handshake sent in](http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png) The illustrated process is as follows: 1. An application process first calls close to actively close the connection, then TCP sends a FIN M; 2. After receiving the FIN M, the other end performs a passive close to confirm the FIN. Its reception is also passed to the application process as an end-of-file character, because the reception of FIN means that the application process can no longer receive additional data on the corresponding connection; 3. After a period of time, the application process that received the end-of-file character calls close to close its socket. This causes its TCP to also send a FIN N; 4. The source TCP that received the FIN confirms it. So there is a FIN and ACK in each direction. ## 💾 Database > Part of the knowledge in this section comes from "Introduction to Database Systems (5th Edition)" ### basic concepts * Data: Symbolic records describing things are called data. * Database (DB): It is a collection of a large amount of organized, shareable data stored in a computer for a long time, and has three basic characteristics of permanent storage, organization, and shareability. * Database Management System (DBMS): is a layer of data management software located between the user and the operating system. * Database system (DBS): is a system consisting of a database, a database management system (and its application development tools), applications, and a database administrator (DBA) to store, manage, process, and maintain data. * Entity: Objects that exist objectively and can be distinguished from each other are called entities. * Attribute: An attribute of an entity is called an attribute. * Key: The set of attributes that uniquely identify an entity is called a key. * Entity type: Use entity name and its attribute name set to abstract and characterize similar entities, called entity type. * Entity set: A set of the same entity type is called an entity set. * Relationship: The relationship between entities usually refers to the connection between different sets of entities. * Schema: A schema is also called a logical schema. It is a description of the logical structure and characteristics of the entire database data. It is a common data view for all users. * External schema: External schema is also called subschema or user schema. It is a description of the logical structure and characteristics of local data that database users (including application programmers and end users) can see and use. A database user's data view is a logical representation of data related to an application. * Internal schema: Internal schema is also called storage schema. A database has only one internal schema. It is a description of the physical structure and storage of the data, and the way the database is organized within the database. ### Common data models * Hierarchical model * Network model * Relational model * Relation (relation): a relation corresponds to a table * Tuple: a row in the table is a tuple * Attribute (attribute): a column in the table is an attribute * Key: A certain attribute group of a tuple can be uniquely identified in the table * Domain (domain): a set of values of the same data type * Component: an attribute value in a tuple * Relation mode: description of the relationship, generally expressed as `relation name (attribute 1, attribute 2, ..., attribute n)` * Object oriented data model * Object relational data model * Semistructured data model ### Common SQL Operations
Object type Object Type of operation
Database mode Mode CREATE SCHEMA
Basic table CREATE SCHEMA , ALTER TABLE
View CREATE VIEW
Index CREATE INDEX
Data Basic tables and views SELECT , INSERT , UPDATE , DELETE , REFERENCES , ALL PRIVILEGES
Attribute column SELECT , INSERT , UPDATE , REFERENCES , ALL PRIVILEGES
> SQL Grammar Tutorial: [runoob . SQL Tutorial](http://www.runoob.com/sql/sql-tutorial.html) ### Relational Database * Basic relation operations: query (selection, projection, connection (equivalent connection, natural connection, outer connection (left outer connection, right outer connection)), division, union, difference, intersection, Cartesian product, etc.), insertion, deletion ,modify * Three types of integrity constraints in the relationship model: entity integrity, referential integrity, user-defined integrity #### Index * Database index: sequential index, B + tree index, hash index * [Data structure and algorithm principle behind MySQL index](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) ### Database integrity * Database integrity refers to the correctness and compatibility of data. * Integrity: To prevent non-semantic (incorrect) data from the database. * Security: In order to protect the database from malicious damage and illegal access. * Trigger: is a special event-driven process defined by the user in the relational table. ### Relational data theory * Data dependency is a constraint relationship between the internal attributes and attributes of a relationship. It is a correlation between data that is reflected by the equality of the values ​​between attributes. * The most important data dependencies: function dependencies, multi-value dependencies. #### Paradigm * First Normal Form (1NF): The attribute (field) is the smallest unit and cannot be separated. * The second normal form (2NF): meet 1NF, each non-primary attribute completely depends on the primary key (eliminating the partial function dependence of 1NF non-primary attribute on the code). * The third normal form (3NF): 2NF is satisfied, and any non-primary attribute does not depend on other non-primary attributes (eliminating the 2NF non-primary attribute's transfer function dependence on the code). * Bowies-Kode Normal Form (BCNF): Satisfy 3NF, any non-primary attribute cannot depend on the subset of primary keys (eliminating the 3NF primary attribute's dependence on the code part and transfer function) * The fourth normal form (4NF): meet 3NF, there must be non-trivial and non-functional dependent multi-value dependencies between attributes (eliminating 3NF non-trivial and non-functional multi-value dependencies). ### Database Recovery * Transaction: It is a user-defined sequence of database operations. These operations are either all done or not done at all. It is an indivisible unit of work. * ACID characteristics of things: atomicity, consistency, isolation, persistence. * Implementation technology of recovery: establish redundant data-> use redundant data to implement database recovery. * Common techniques for establishing redundant data: data dumps (dynamic mass dumps, dynamic incremental dumps, static mass dumps, static incremental dumps), registration log files. ### Concurrency control * Transactions are the basic unit of concurrency control. * Data inconsistencies caused by concurrent operations include: lost modifications, non-repeatable reads, and "dirty" data reads. * The main technologies of concurrency control: blocking, timestamp, optimistic control method, multi-version concurrency control, etc. * Basic lock types: exclusive lock (X lock / write lock), shared lock (S lock / read lock). * Livelock deadlock: * Livelock: The transaction is always in a waiting state, which can be avoided through a first come, first served policy. * Deadlock: The transaction can never end * Prevention: one-time block method, sequential block method; * Diagnosis: timeout method, waiting graph method; * Cancel: Undo the transaction with the least deadlock cost and release all the locks of this transaction, so that other transactions can continue to run. * Serializable scheduling: The concurrent execution of multiple transactions is correct if and only if the result is the same as when the transactions are executed serially in a certain order. Guidelines for correct scheduling of concurrent transactions when serializable. ## 📏 Design pattern > Examples of major design patterns: [CSDN column. C ++ Design Patterns](https://blog.csdn.net/liang19890820/article/details/66974516) series of blog posts [Design Pattern Project Catalog](DesignPattern) ### Singleton mode [Singleton Pattern Example](DesignPattern/SingletonPattern) ### Abstract factory pattern [Abstract Factory Pattern Example](DesignPattern/AbstractFactoryPattern) ### Adapter Mode [Adapter pattern example](DesignPattern/AdapterPattern) ### Bridge mode [Bridge pattern example](DesignPattern/BridgePattern) ### Observer mode [Example of Observer Pattern](DesignPattern/ObserverPattern) ### Six Principles of Design Patterns * Single Responsibility Principle (SRP) * Liskov Substitution Principle (LSP) * Dependence Inversion Principle (DIP) * Interface Segregation Principle (ISP) * Law of Demeter (LoD) * Open Close Principle (OCP) ## ⚙️ Link Loading Library > Part of the knowledge in this section comes from "Programmer's Self-Cultivation-Link Loading Library" ### memory, stack, heap The general application memory space has the following areas: * Stack: Automatically allocated and released by the operating system, storing function parameter values, local variable values, etc., used to maintain the context of function calls * Heap: Generally allocated and released by the programmer. If the programmer does not release, it may be recycled by the operating system at the end of the program to accommodate the dynamically allocated memory area of ​​the application. * Executable image: Stores the image of the executable file in memory. Loading by the loader is to read or map the memory of the executable file here. * Reserved area: The reserved area is not a single memory area, but a general term for memory areas that are protected from access in the memory. For example, in C, the invalid pointer is assigned a value of 0 (NULL), so the 0 address is normal. Impossible to access data efficiently #### stack The stack stores the maintenance information required for a function call. It is often called a stack frame or an active record. It generally contains the following aspects: * Function return address and parameters * Temporary variables: including non-static local variables of functions and other temporary variables automatically generated by the compiler * Save context: including registers that need to remain unchanged before and after function calls #### Heap Heap allocation algorithm: * Free List * Bitmap * Object pool #### "Segment fault" or "Illegal operation, this memory address cannot be read / write" Errors caused by typical illegal pointer dereferencing. This error occurs when the pointer points to a memory address that is not allowed to read and write, and the program attempts to use the pointer to read or write that address. Common reasons: * Initialize the pointer to NULL, then start using the pointer without giving it a reasonable value * There is no need to initialize the pointer in the stack. The value of the pointer will generally be a random number. After that, the pointer is used directly. ### Compile and link #### File formats for each platform Platform | Executables | Objects | Dynamic Libraries / Shared Objects | Static Libraries ---|---|---|---|--- Windows|exe|obj|dll|lib Unix/Linux|ELF、out|o|so|a Mac|Mach-O|o|dylib、tbd、framework|a、framework #### Compile and link process 1. Pre-compilation (pre-compiler processes pre-compilation instructions such as `# include`,` # define` and generates `.i` or` .ii` files) 2. Compile (the compiler performs lexical analysis, syntax analysis, semantic analysis, intermediate code generation, object code generation, optimization, and generation of `.s` files) 3. Assembly (the assembler translates the assembly code into machine code and generates a `.o` file) 4. Linking (connectors perform address and space allocation, symbol resolution, relocation, and generate `.out` files) > The current version of GCC combines pre-compilation and compilation into one step, pre-compiled compiler cc1, assembler as, linker ld > MSVC compilation environment, compiler cl, linker link, executable file viewer dumpbin #### target document The files generated by the compiler after compiling the source code are called object files. The object file is structurally speaking, it is a compiled executable file format, but the linking process has not yet been performed, and some symbols or addresses may not be adjusted. > Executable files (`.exe` for Windows and` ELF` for Linux), dynamic link libraries (`.dll` for Windows and` .so` for Linux), static link libraries (`.lib` for Windows and Linux `.A`) are stored according to the executable file format (Windows according to PE-COFF, Linux according to ELF) ##### Object file format * Windows PE (Portable Executable), or PE-COFF, `.obj` format * Linux's ELF (Executable Linkable Format), `.o` format * Intel / Microsoft OMF (Object Module Format) * Unix `a.out` format * MS-DOS `.COM` format > PE and ELF are both COFF (Common File Format) variants ##### Object file storage structure Segment | Function --- | --- File Header | File header, describing the file attributes of the entire file (including whether the file is executable, is it statically linked or dynamically linked with the entry address, target hardware, target operating system, etc.) .text section | Code section, machine code compiled by the execution statement .data section | Data section, initialized global and local static variables .bss section | BSS section (Block Started by Symbol), uninitialized global and local static variables (because the default value is 0, so it is only reserved here and does not take up space) .rodata section | Read-only data section, which stores read-only data, generally read-only variables (such as const-modified variables) and string constants in the program .comment section | Comment section for compiler version information .note.GNU-stack section | Stack hint section > Other paragraphs omitted #### Linked Interface-Symbol In the link, the object files are actually a reference to the address between the object files, that is, a reference to the address of a function and a variable. We collectively refer to functions and variables as Symbols, and function names or variable names are Symbol Names. The following Symbol Table: Symbol (Symbol Name) | Symbol Value (Address) --- | --- main| 0x100 Add | 0x123 ... | ... ### Shared Library for Linux Shared libraries under Linux are ordinary ELF shared objects. The shared library version update should ensure the compatibility of the binary interface ABI (Application Binary Interface) #### Name `libname.so.x.y.z` * x: major version number, libraries with different major version numbers are not compatible and need to be recompiled * y: minor version number, higher version number is backward compatible with lower version number * z: release version number, no interface changes, fully compatible #### path Most open source systems, including Linux, follow the FHS (File Hierarchy Standard) standard. This standard specifies how system files are stored, including various directory structures, organizations, and roles. * `/lib`: Stores the most critical and basic shared libraries of the system, such as dynamic linker, C language runtime library, math library, etc. * `/usr/lib`: Stores key libraries required by non-system runtime, mainly development libraries * `/usr/local/lib`: Stores libraries that are not very relevant to the operating system itself, mainly libraries for some third-party applications > The dynamic linker looks for shared libraries in `/ lib`, `/usr/lib`, and specified by the `/etc/ld.so.conf` configuration file #### Environment variables * `LD_LIBRARY_PATH`: temporarily change the shared library search path of an application without affecting other applications * `LD_PRELOAD`: specify some shared libraries or even object files that are pre-loaded * `LD_DEBUG`: Turn on the debugging function of the dynamic linker #### So shared library writing Writing shared libraries with CLion Create a shared library named MySharedLib CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(MySharedLib) set(CMAKE_CXX_STANDARD 11) add_library(MySharedLib SHARED library.cpp library.h) ``` library.h ```cpp #ifndef MYSHAREDLIB_LIBRARY_H #define MYSHAREDLIB_LIBRARY_H // print Hello World! void hello(); // Sum using variable template parameters template T sum(T t) { return t; } template T sum(T first, Types ... rest) { return first + sum(rest...); } #endif ``` library.cpp ```cpp #include #include "library.h" void hello() { std::cout << "Hello, World!" << std::endl; } ``` #### use of so shared library (called by executable project) Use CLion to call a shared library Create an executable project named TestSharedLib CMakeLists.txt ```cmake cmake_minimum_required (VERSION 3.10) project (TestSharedLib) # C ++ 11 compile set (CMAKE_CXX_STANDARD 11) # Header file path set (INC_DIR / home / xx / code / clion / MySharedLib) # Library file path set (LIB_DIR / home / xx / code / clion / MySharedLib / cmake-build-debug) include_directories(${INC_DIR}) link_directories(${LIB_DIR}) link_libraries(MySharedLib) add_executable(TestSharedLib main.cpp) # link MySharedLib Library target_link_libraries(TestSharedLib MySharedLib) ``` main.cpp ```cpp #include #include "library.h" using std::cout; using std::endl; int main() { hello(); cout << "1 + 2 = " << sum(1,2) << endl; cout << "1 + 2 + 3 = " << sum(1,2,3) << endl; return 0; } ``` Results ``` Hello, World! 1 + 2 = 3 1 + 2 + 3 = 6 ``` ### Windows Application entry function * GUI(Graphical User Interface) application, linker options:`/SUBSYSTEM:WINDOWS` * CUI(Console User Interface) application, linker options:`/SUBSYSTEM:CONSOLE` _tWinMain versus _tmain Function declaration ```cpp Int WINAPI _tWinMain( HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int nCmdShow); int _tmain( int argc, TCHAR *argv[], TCHAR *envp[]); ``` Application Type | Entry Point Function | Embedded Startup Function --- | --- | --- GUI application for processing ANSI characters (strings) | _tWinMain (WinMain) | WinMainCRTSartup GUI application for handling Unicode characters (strings) | _tWinMain (wWinMain) | wWinMainCRTSartup CUI application that handles ANSI characters (strings) | _tmain (Main) | mainCRTSartup CUI application for handling Unicode characters (strings) | _tmain (wMain) | wmainCRTSartup Dynamic-Link Library | DllMain | _DllMainCRTStartup ### Dynamic-Link Library for Windows > Part of the knowledge comes from "Windows Core Programming (Fifth Edition)" #### Uses * Expanded application features * Simplified project management * Helps save memory * Promote the sharing of resources * Improved localization * Helps resolve differences between platforms * Can be used for special purposes #### Note * Creating a DLL is actually creating a function that can be called by an executable module * When a module provides a memory allocation function (malloc, new), it must also provide another memory release function (free, delete) * When mixing C and C++, use the extern "C" modifier * A DLL can export functions, variables (to avoid export), C++ classes (export and import need to be the same as the compiler, otherwise avoid export) * DLL module: __declspec (dllexport) in the cpp file is written before the include header file * Calling DLL's executable module: __declspec (dllimport) of the cpp file should not define MYLIBAPI before #### Search order for loading Windows programs 1. Directory containing executable files 2. The system directory of Windows can be obtained through GetSystemDirectory 3. 16-bit system directory, the System subdirectory in the Windows directory 4. The Windows directory can be obtained through GetWindowsDirectory 5. The current directory of the process 6. Directories listed in the PATH environment variable #### DLL Entry function DllMain function ```cpp BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: // Called when mapping a DLL to the process address space for the first time // The DLL is being mapped into the process' address space. break; case DLL_THREAD_ATTACH: // When the process creates a thread, it is used to tell the DLL to perform thread-related initialization (non-main thread execution) // A thread is bing created. break; case DLL_THREAD_DETACH: // The system calls ExitThread before the thread exits, the thread that is about to be terminated performs thread-related cleanup by telling the DLL // A thread is exiting cleanly. break; case DLL_PROCESS_DETACH: // Called when a DLL is removed from the process's address space // The DLL is being unmapped from the process' address space. break; } return (TRUE); // Used only for DLL_PROCESS_ATTACH } ``` #### Load and unload library LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread Function declaration ```cpp // load library HMODULE WINAPI LoadLibrary ( _In_ LPCTSTR lpFileName ); HMODULE LoadLibraryExA ( LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags ); // To load a Win32 DLL in a Universal Windows Platform (UWP) application, you need to call LoadPackagedLibrary instead of LoadLibrary or LoadLibraryEx HMODULE LoadPackagedLibrary ( LPCWSTR lpwLibFileName, DWORD Reserved ); // uninstall library BOOL WINAPI FreeLibrary ( _In_ HMODULE hModule ); // Unload the library and exit the thread VOID WINAPI FreeLibraryAndExitThread( _In_ HMODULE hModule, _In_ DWORD dwExitCode ); ``` #### Display link to export symbol GetProcAddress function declaration ```cpp FARPROC GetProcAddress ( HMODULE hInstDll, PCSTR pszSymbolName // Can only accept ANSI strings, not Unicode ); ``` #### DumpBin.exe View DLL information Use `DumpBin.exe` in the` VS Developer Command Prompt` to view the exported section of the DLL library (exported variables, functions, symbols of class names), relative virtual addresses (RVA). Such as: ``` DUMPBIN -exports D: \ mydll.dll ``` #### Flow chart of LoadLibrary and FreeLibrary LoadLibrary and FreeLibrary flowchart ##### LoadLibrary ![WindowsLoadLibrary](https://raw.githubusercontent.com/huihut/interview/master/images/WindowsLoadLibrary.png) ##### FreeLibrary ![WindowsFreeLibrary](https://raw.githubusercontent.com/huihut/interview/master/images/WindowsFreeLibrary.png) #### Writing of DLL library (export a DLL module) Writing DLL library (export a DLL module) DLL header file ```cpp // MyLib.h #ifdef MYLIBAPI // MYLIBAPI should be defined before include "Mylib.h" in all DLL source files // All functions / variables are being exported #else // This header file is included by an exe source code module, which means that all functions / variables are imported #define MYLIBAPI extern "C" __declspec (dllimport) #endif // Define any data structure and symbol here // Define exported variables (avoid exporting variables) MYLIBAPI int g_nResult; // Define the prototype of the exported function MYLIBAPI int Add(int nLeft, int nRight); ``` DLL source files ```cpp // MyLibFile1.cpp // Contains standard Windows and C runtime header files #include // Functions and variables exported from DLL source files #define MYLIBAPI extern "C" __declspec (dllexport) // Contains exported data structures, symbols, functions, variables #include "MyLib.h" // Put the code of this DLL source code file here int g_nResult; int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return g_nResult; } ``` #### Use of DLL library (dynamic link DLL at runtime) Use of DLL libraries (dynamically linking DLLs at runtime) ```cpp // A simple program that uses LoadLibrary and // GetProcAddress to access myPuts from Myputs.dll. #include #include typedef int (__cdecl *MYPROC)(LPWSTR); int main( void ) { HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; // Get a handle to the DLL module. hinstLib = LoadLibrary(TEXT("MyPuts.dll")); // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts"); // If the function address is valid, call the function. if (NULL != ProcAdd) { fRunTimeLinkSuccess = TRUE; (ProcAdd) (L"Message sent to the DLL function\n"); } // Free the DLL module. fFreeResult = FreeLibrary(hinstLib); } // If unable to call the DLL function, use an alternative. if (! fRunTimeLinkSuccess) printf("Message printed from executable\n"); return 0; } ``` ### Runtime(Runtime Library) #### Typical program operation steps 1. The operating system creates a process and passes control to the entry point of the program (often an entry function in the runtime library) 2. The entry function initializes the runtime library and program running environment (including heap, I / O, threads, global variable construction, etc.). 3. After the entry function is initialized, the main function is called to officially start executing the main part of the program. 4. After the main function finishes executing, return to the entry function for cleanup (including global variable destruction, heap destruction, closing I / O, etc.), and then make a system call to end the process. > I / O of a program refers to the interaction between the program and the outside world, including files, management programs, network, command line, signals, etc. More broadly, I / O refers to what the operating system understands as a "file." #### glibc entry `_start-> __libc_start_main-> exit-> _exit` The `main (argc, argv, __environ)` function is executed in `__libc_start_main`. #### MSVC CRT Entry `int mainCRTStartup (void)` Do the following: 1. Initialize global variables related to the OS version. 2. Initialize the heap. 3. Initialize I / O. 4. Get command line parameters and environment variables. 5. Initialize some data of the C library. 6. Call main and record the return value. 7. Check for errors and return the return value of main. #### C Language Runtime (CRT) It contains the following functions: * Start and exit: including entry functions and other functions on which entry functions depend. * Standard functions: There are functions implemented by the C language standard library specified by the C language standard. * I/O: Encapsulation and implementation of I / O functions. * Heap: Encapsulation and implementation of the heap. * Language implementation: implementation of some special functions in the language. * Debugging: Code that implements debugging functions. #### C Language Standard Library (ANSI C) contain: * Standard input and output (stdio.h) * File operations (stdio.h) * Character manipulation (ctype.h) * String operations (string.h) * Mathematical functions (math.h) * Resource Management (stdlib.h) * Format conversion (stdlib.h) * Time / Date (time.h) * Assertions (assert.h) * Constants on various types (limits.h & float.h) * Variable length parameter (stdarg.h) * Non-local jump (setjmp.h) ## 📚 Books > [huihut/CS-Books](https://github.com/huihut/CS-Books):📚 Computer Technology Books PDF ### Language * "C ++ Primer" * "Effective C ++" * "More Effective C ++" * "Exploring the C ++ Object Model" * "Understanding C ++ 11" * "Anatomy of STL Source Code" ### Algorithm * "Sword Finger Offer" * "Programming Pearls" * "Programmer Interview Collection" ### System * "Understanding Computer Systems" * "Windows Core Programming" * "Advanced Programming in the Unix Environment" ### The internet * "Unix Network Programming" * "Detailed Explanation of TCP / IP" ### Other * "The Self-Cultivation of Programmers" ## 🔱 C / C ++ Development Direction > The development direction of C / C ++ is very wide, including not limited to the following directions. The following are some of the requirements for recruiting positions of big factories and schools. ### Backend / Server [Backend Development] * Solid basic programming skills, master C / C ++ / JAVA and other development languages, commonly used algorithms and data structures; * Familiar with TCP / UDP network protocol and related programming, inter-process communication programming; * Understanding Python, Shell, Perl and other scripting languages; * Understand MYSQL, SQL language, programming, NoSQL, key-value storage principle; * Comprehensive and solid software knowledge structure, mastering professional knowledge of operating systems, software engineering, design patterns, data structures, database systems, and network security; * Understand the knowledge of distributed system design and development, load balancing technology, system disaster recovery design, and high availability systems. ### Desktop Client [PC client development] * Bachelor degree or above in computer software, majoring in programming, solid foundation, understanding of algorithms and data structure; * Familiar with memory management, file system, process thread scheduling of windows operating system; * Familiar with MFC / windows interface implementation mechanism, proficient in VC, proficient in C / C ++, proficient in STL, and network programming experience under Windows; * Familiar with Windows client development and debugging, Windows application software development experience is preferred; * Passionate about innovation and solving challenging problems, with good algorithm foundation and system analysis ability. ### Graphics / Game / VR / AR [Game Client Development] * Bachelor degree or above in computer science / engineering major, passion for programming, solid foundation, understanding of algorithms, data structure, software design related knowledge; * At least one programming language commonly used in game development, C ++ / C # programming experience is preferred; * Experience with game engines (such as Unity, Unreal) is preferred; * Knowledge of game client technology (such as graphics, audio, animation, physics, artificial intelligence, network synchronization) is preferred; * Passionate about innovation and solving challenging problems, strong learning ability, analytical and problem solving skills, and a good sense of teamwork; * Ability to read English technical documents; * Love games. ### Test Development [Test Development] * Bachelor degree or above in computer or related field; * One to two years of programming experience in C / C ++ / Python or other computer languages; * Ability to write test plans, test cases, and achieve performance and safety tests; * Ability to implement automated systems; * Ability to locate and investigate product defects and code-level debugging defects; * Work proactively, have a sense of responsibility, and have a good team spirit. ### Cyber Security / Reverse [safety technology] * Love the Internet, have a passionate pursuit of operating systems and network security, and professionalism is not limited; * Familiar with vulnerability mining, network security attack and defense technologies, and common hacking techniques; * Master basic development skills and be proficient in C / C ++ language; * Have a good grasp of database, operating system and network principles; * Experience in software reverse, network security attack and defense or security system development is preferred. ### Embedded / IoT [Embedded Application Development] * Have a good programming foundation, proficient in C / C ++ language; * Master the necessary knowledge for software development such as operating system and data structure; * Strong communication and understanding ability and good teamwork awareness; * Development experience on Linux / Android system platform is preferred. ### Audio / Video / Streaming / SDK [Audio and video codec] 1. Master's degree or above, computer, signal processing, mathematics, information and related majors and directions; 2. Solid video coding and decoding foundation, familiar with commonly used HEVC or H264, have a good digital signal processing foundation; 3. Master C / C ++, strong code ability, familiar with an assembly language is preferred; 4. Strong English literature reading ability; 5. Strong learning ability, teamwork spirit, and strong resistance to stress. ### Computer Vision / Machine Learning [Computer Vision Research] * Computer, applied mathematics, pattern recognition, artificial intelligence, automatic control, statistics, operations research, bioinformatics, physics / quantum computing, neuroscience, sociology / psychology and other majors, image processing, pattern recognition, machine learning related research Direction, undergraduate and above, doctoral degree is preferred; * Proficient in basic algorithms and applications related to computer vision and image processing; * Strong algorithm implementation ability, proficient in C / C ++ programming, familiar with at least one programming language of Shell / Python / Matlab; * Publication of papers in computer vision, pattern recognition and other academic conferences or journals, awards in relevant international competitions, and related patents are preferred. ## 💯 Review brush question website * [cplusplus](http://www.cplusplus.com/) * [cppreference](https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5) * [runoob](http://www.runoob.com/cplusplus/cpp-tutorial.html) * [leetcode](https://leetcode.com/) | [leetcode-cn](https://leetcode-cn.com/) * [lintcode](https://www.lintcode.com/) * [nowcoder](https://www.nowcoder.net/) ## 📝 Interview Question Experience * [Nowcoder.com's summary of the 2020 autumn tricks! (Post division)](https://www.nowcoder.com/discuss/205497) * [【Preparation for Autumn Moves】 Raiders for 2020 Autumn Moves](https://www.nowcoder.com/discuss/197116) * [2019 School Recruitment Summary! 【Daily Update】](https://www.nowcoder.com/discuss/90907) * [2019 School Recruitment Technology Posts Summary [Technology]](https://www.nowcoder.com/discuss/146655) * [2018 School Recruitment Questions Summary](https://www.nowcoder.com/discuss/68802) * [2017 Autumn Campus Recruitment Pen and Face Summaries](https://www.nowcoder.com/discuss/12805) * [The most complete collection of 2017 spring tricks in history!!](https://www.nowcoder.com/discuss/25268) * [Interview questions are here](https://www.nowcoder.com/discuss/57978) * [zhihu. On the Internet job search, what well-written and attentive face have you seen? It is best to share your own facial and mental journey. ](https://www.zhihu.com/question/29693016) * [zhihu. What are the most common interview algorithm questions for internet companies? ](https://www.zhihu.com/question/24964987) * [CSDN. C ++ Interview Questions Completely Organized](http://blog.csdn.net/ljzcome/article/details/574158) * [CSDN. Baidu R & D interview questions (C ++ direction)](http://blog.csdn.net/Xiongchao99/article/details/74524807?locationNum=6&fps=1) * [CSDN. C ++ 30 common interview questions](http://blog.csdn.net/fakine/article/details/51321544) * [CSDN. Tencent 2016 intern interview experience (already got offer)](http://blog.csdn.net/onever_say_love/article/details/51223886) * [cnblogs. C ++ Interview Collection (Questions Asked for Interview)](https://www.cnblogs.com/Y1Focus/p/6707121.html) * [cnblogs. C / C ++ written and interview questions summary](https://www.cnblogs.com/fangyukuan/archive/2010/09/18/1829871.html) * [cnblogs. Summary of common C ++ interview questions and basic knowledge points (1)](https://www.cnblogs.com/LUO77/p/5771237.html) * [segmentfault. Summary of common interview questions in C ++](https://segmentfault.com/a/1190000003745529) ## 📆 Recruiting time posts * [nowcoder . Enterprise Recruitment Schedule](https://www.nowcoder.com/school/schedule) ## 👍 Recommend * [Github. CyC2018 / Job-Recommend](https://github.com/CyC2018/Job-Recommend): 🔎 Internet internal push information (social recruitment, school recruitment, internship) * [Github. Amusi / AI-Job-Recommend](https://github.com/amusi/AI-Job-Recommend): direction of artificial intelligence of domestic companies (including machine learning, deep learning, computer vision and natural language processing) Job recruitment information (including full-time, internship and school recruitment) ## 👬 Contributor ## 📜 License This repository follows the CC BY-NC-SA 4.0 (signed-non-commercial use-shared in the same way) agreement, please indicate the source when reprinting, and should not be used for commercial purposes. [![CC BY-NC-SA 4.0](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)](https://github.com/huihut/interview/blob/master/LICENSE) ================================================ FILE: docs/index.html ================================================ interview