[{"data":1,"prerenderedAt":14459},["ShallowReactive",2],{"posts":3},[4,303,757,774,990,1177,1907,2103,4302,4683,7976,11116,12151],{"id":5,"title":6,"body":7,"date":293,"description":294,"extension":295,"meta":296,"navigation":143,"path":297,"seo":298,"stem":299,"tags":300,"__hash__":302},"blog\u002Fblog\u002Falgorithm-learning-004-list.md","链表",{"type":8,"value":9,"toc":287},"minimark",[10,52,56,116,119,255,258,276,279,283],[11,12,17],"pre",{"className":13,"code":14,"language":15,"meta":16,"style":16},"language-cpp shiki shiki-themes github-light github-dark","struct ListNode {\n  int val;\n  ListNode *next;\n  ListNode(int x): val(x), next(nullptr) {}\n};\n","cpp","",[18,19,20,28,34,40,46],"code",{"__ignoreMap":16},[21,22,25],"span",{"class":23,"line":24},"line",1,[21,26,27],{},"struct ListNode {\n",[21,29,31],{"class":23,"line":30},2,[21,32,33],{},"  int val;\n",[21,35,37],{"class":23,"line":36},3,[21,38,39],{},"  ListNode *next;\n",[21,41,43],{"class":23,"line":42},4,[21,44,45],{},"  ListNode(int x): val(x), next(nullptr) {}\n",[21,47,49],{"class":23,"line":48},5,[21,50,51],{},"};\n",[53,54,55],"h2",{"id":55},"链表反转",[11,57,59],{"className":13,"code":58,"language":15,"meta":16,"style":16},"ListNode* reverse_list(ListNode *head) {\n  ListNode *pre = nullptr, *curr = head, *next = nullptr;\n  while(curr) {\n    next = curr->next;\n    curr->next = pre;\n    pre = curr;\n    curr = next;\n  }\n  return pre;\n}\n",[18,60,61,66,71,76,81,86,92,98,104,110],{"__ignoreMap":16},[21,62,63],{"class":23,"line":24},[21,64,65],{},"ListNode* reverse_list(ListNode *head) {\n",[21,67,68],{"class":23,"line":30},[21,69,70],{},"  ListNode *pre = nullptr, *curr = head, *next = nullptr;\n",[21,72,73],{"class":23,"line":36},[21,74,75],{},"  while(curr) {\n",[21,77,78],{"class":23,"line":42},[21,79,80],{},"    next = curr->next;\n",[21,82,83],{"class":23,"line":48},[21,84,85],{},"    curr->next = pre;\n",[21,87,89],{"class":23,"line":88},6,[21,90,91],{},"    pre = curr;\n",[21,93,95],{"class":23,"line":94},7,[21,96,97],{},"    curr = next;\n",[21,99,101],{"class":23,"line":100},8,[21,102,103],{},"  }\n",[21,105,107],{"class":23,"line":106},9,[21,108,109],{},"  return pre;\n",[21,111,113],{"class":23,"line":112},10,[21,114,115],{},"}\n",[53,117,118],{"id":118},"合并两个有序链表",[11,120,122],{"className":13,"code":121,"language":15,"meta":16,"style":16},"ListNode* merge_list(ListNode* l1, ListNode* l2) {\n  if(l1 == nullptr)  return l2;\n  if(l2 == nullptr)  return l1;\n\n  ListNode* head = (l1->val \u003C= l2->val) ? l1 : l2;\n  ListNode* pre = head;\n  ListNode* curr1 = head->next;\n  ListNode* curr2 = (head == l2) ? l1 : l2;\n\n  while(curr1 != nullptr && curr2 != nullptr) {\n    if(curr1->val \u003C= curr2->val) {\n      pre->next = curr1;\n      curr1 = curr1->next;\n    } else {\n      pre->next = curr2;\n      curr2 = curr2->next;\n    }\n    pre = pre->next;\n  }\n\n  if(curr1 == nullptr) pre->next = curr2;\n  if(curr2 == nullptr) pre->next = curr1;\n  return head;\n}\n",[18,123,124,129,134,139,145,150,155,160,165,169,174,180,186,192,198,204,210,216,222,227,232,238,244,250],{"__ignoreMap":16},[21,125,126],{"class":23,"line":24},[21,127,128],{},"ListNode* merge_list(ListNode* l1, ListNode* l2) {\n",[21,130,131],{"class":23,"line":30},[21,132,133],{},"  if(l1 == nullptr)  return l2;\n",[21,135,136],{"class":23,"line":36},[21,137,138],{},"  if(l2 == nullptr)  return l1;\n",[21,140,141],{"class":23,"line":42},[21,142,144],{"emptyLinePlaceholder":143},true,"\n",[21,146,147],{"class":23,"line":48},[21,148,149],{},"  ListNode* head = (l1->val \u003C= l2->val) ? l1 : l2;\n",[21,151,152],{"class":23,"line":88},[21,153,154],{},"  ListNode* pre = head;\n",[21,156,157],{"class":23,"line":94},[21,158,159],{},"  ListNode* curr1 = head->next;\n",[21,161,162],{"class":23,"line":100},[21,163,164],{},"  ListNode* curr2 = (head == l2) ? l1 : l2;\n",[21,166,167],{"class":23,"line":106},[21,168,144],{"emptyLinePlaceholder":143},[21,170,171],{"class":23,"line":112},[21,172,173],{},"  while(curr1 != nullptr && curr2 != nullptr) {\n",[21,175,177],{"class":23,"line":176},11,[21,178,179],{},"    if(curr1->val \u003C= curr2->val) {\n",[21,181,183],{"class":23,"line":182},12,[21,184,185],{},"      pre->next = curr1;\n",[21,187,189],{"class":23,"line":188},13,[21,190,191],{},"      curr1 = curr1->next;\n",[21,193,195],{"class":23,"line":194},14,[21,196,197],{},"    } else {\n",[21,199,201],{"class":23,"line":200},15,[21,202,203],{},"      pre->next = curr2;\n",[21,205,207],{"class":23,"line":206},16,[21,208,209],{},"      curr2 = curr2->next;\n",[21,211,213],{"class":23,"line":212},17,[21,214,215],{},"    }\n",[21,217,219],{"class":23,"line":218},18,[21,220,221],{},"    pre = pre->next;\n",[21,223,225],{"class":23,"line":224},19,[21,226,103],{},[21,228,230],{"class":23,"line":229},20,[21,231,144],{"emptyLinePlaceholder":143},[21,233,235],{"class":23,"line":234},21,[21,236,237],{},"  if(curr1 == nullptr) pre->next = curr2;\n",[21,239,241],{"class":23,"line":240},22,[21,242,243],{},"  if(curr2 == nullptr) pre->next = curr1;\n",[21,245,247],{"class":23,"line":246},23,[21,248,249],{},"  return head;\n",[21,251,253],{"class":23,"line":252},24,[21,254,115],{},[53,256,257],{"id":257},"链表相加",[11,259,261],{"className":13,"code":260,"language":15,"meta":16,"style":16},"ListNode* add_two_number(ListNode* l1, ListNode* l2) {\n\n}\n",[18,262,263,268,272],{"__ignoreMap":16},[21,264,265],{"class":23,"line":24},[21,266,267],{},"ListNode* add_two_number(ListNode* l1, ListNode* l2) {\n",[21,269,270],{"class":23,"line":30},[21,271,144],{"emptyLinePlaceholder":143},[21,273,274],{"class":23,"line":36},[21,275,115],{},[53,277,278],{"id":278},"划分链表",[280,281,282],"p",{},"待续",[284,285,286],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":16,"searchDepth":30,"depth":30,"links":288},[289,290,291,292],{"id":55,"depth":30,"text":55},{"id":118,"depth":30,"text":118},{"id":257,"depth":30,"text":257},{"id":278,"depth":30,"text":278},"2025-11-28","笔记","md",{},"\u002Fblog\u002Falgorithm-learning-004-list",{"title":6,"description":294},"blog\u002Falgorithm-learning-004-list",[301],"算法","aZmvJ9xzwGXFXYihOPNgHQuyxlPJh5suWEOjrJ91Wv0",{"id":304,"title":305,"body":306,"date":750,"description":294,"extension":295,"meta":751,"navigation":143,"path":752,"seo":753,"stem":754,"tags":755,"__hash__":756},"blog\u002Fblog\u002Falgorithm-learning-003-binarysearch.md","二分搜索",{"type":8,"value":307,"toc":743},[308,311,369,373,451,455,525,528,537,646,649,728,735,738,741],[53,309,310],{"id":310},"在有序数组中确定num的存在",[11,312,314],{"className":13,"code":313,"language":15,"meta":16,"style":16},"bool binary_search(std::vector\u003Cint> &nums, int target) {\n  int l = 0, r = nums.size() - 1;\n  int m = 0;\n  while(l \u003C= r) {\n    m = l + (r - l) \u002F 2;\n    if(nums[m] == target) return true;\n    else if(nums[m] > target ) r = m - 1;\n    else l = m + 1;\n  }\n  return false;\n}\n",[18,315,316,321,326,331,336,341,346,351,356,360,365],{"__ignoreMap":16},[21,317,318],{"class":23,"line":24},[21,319,320],{},"bool binary_search(std::vector\u003Cint> &nums, int target) {\n",[21,322,323],{"class":23,"line":30},[21,324,325],{},"  int l = 0, r = nums.size() - 1;\n",[21,327,328],{"class":23,"line":36},[21,329,330],{},"  int m = 0;\n",[21,332,333],{"class":23,"line":42},[21,334,335],{},"  while(l \u003C= r) {\n",[21,337,338],{"class":23,"line":48},[21,339,340],{},"    m = l + (r - l) \u002F 2;\n",[21,342,343],{"class":23,"line":88},[21,344,345],{},"    if(nums[m] == target) return true;\n",[21,347,348],{"class":23,"line":94},[21,349,350],{},"    else if(nums[m] > target ) r = m - 1;\n",[21,352,353],{"class":23,"line":100},[21,354,355],{},"    else l = m + 1;\n",[21,357,358],{"class":23,"line":106},[21,359,103],{},[21,361,362],{"class":23,"line":112},[21,363,364],{},"  return false;\n",[21,366,367],{"class":23,"line":176},[21,368,115],{},[53,370,372],{"id":371},"在有序数组中找num的最左位置","在有序数组中找>=num的最左位置",[11,374,376],{"className":13,"code":375,"language":15,"meta":16,"style":16},"int binary_search(std::vector\u003Cint> &nums, int target) {\n  int l = 0, r = nums.size() - 1;\n  int m = 0;\n  int index = -1;\n  while(l \u003C= r) {\n    m = 1 + (r - l) \u002F 2;\n    if(nums[m] >= target) {\n      index = m;\n      r = m - 1;\n    }\n    else {\n      l = m + 1;\n    }\n  }\n  return index;\n}\n",[18,377,378,383,387,391,396,400,405,410,415,420,424,429,434,438,442,447],{"__ignoreMap":16},[21,379,380],{"class":23,"line":24},[21,381,382],{},"int binary_search(std::vector\u003Cint> &nums, int target) {\n",[21,384,385],{"class":23,"line":30},[21,386,325],{},[21,388,389],{"class":23,"line":36},[21,390,330],{},[21,392,393],{"class":23,"line":42},[21,394,395],{},"  int index = -1;\n",[21,397,398],{"class":23,"line":48},[21,399,335],{},[21,401,402],{"class":23,"line":88},[21,403,404],{},"    m = 1 + (r - l) \u002F 2;\n",[21,406,407],{"class":23,"line":94},[21,408,409],{},"    if(nums[m] >= target) {\n",[21,411,412],{"class":23,"line":100},[21,413,414],{},"      index = m;\n",[21,416,417],{"class":23,"line":106},[21,418,419],{},"      r = m - 1;\n",[21,421,422],{"class":23,"line":112},[21,423,215],{},[21,425,426],{"class":23,"line":176},[21,427,428],{},"    else {\n",[21,430,431],{"class":23,"line":182},[21,432,433],{},"      l = m + 1;\n",[21,435,436],{"class":23,"line":188},[21,437,215],{},[21,439,440],{"class":23,"line":194},[21,441,103],{},[21,443,444],{"class":23,"line":200},[21,445,446],{},"  return index;\n",[21,448,449],{"class":23,"line":206},[21,450,115],{},[53,452,454],{"id":453},"在有序数组中找num的最右位置","在有序数组中找\u003C=num的最右位置",[11,456,458],{"className":13,"code":457,"language":15,"meta":16,"style":16},"int binary_search(std::vector\u003Cint> &nums, int target) {\n  int l = 0, r = nums.size() - 1;\n  int m = 0;\n  int index = -1;\n  while(l \u003C= r) {\n    m = 1 + (r - l) \u002F 2;\n    if(nums[m] \u003C= target) {\n      index = m;\n      l = m + 1;\n    }\n    else {\n      r = m - 1;\n    }\n  }\n  return index;\n}\n",[18,459,460,464,468,472,476,480,484,489,493,497,501,505,509,513,517,521],{"__ignoreMap":16},[21,461,462],{"class":23,"line":24},[21,463,382],{},[21,465,466],{"class":23,"line":30},[21,467,325],{},[21,469,470],{"class":23,"line":36},[21,471,330],{},[21,473,474],{"class":23,"line":42},[21,475,395],{},[21,477,478],{"class":23,"line":48},[21,479,335],{},[21,481,482],{"class":23,"line":88},[21,483,404],{},[21,485,486],{"class":23,"line":94},[21,487,488],{},"    if(nums[m] \u003C= target) {\n",[21,490,491],{"class":23,"line":100},[21,492,414],{},[21,494,495],{"class":23,"line":106},[21,496,433],{},[21,498,499],{"class":23,"line":112},[21,500,215],{},[21,502,503],{"class":23,"line":176},[21,504,428],{},[21,506,507],{"class":23,"line":182},[21,508,419],{},[21,510,511],{"class":23,"line":188},[21,512,215],{},[21,514,515],{"class":23,"line":194},[21,516,103],{},[21,518,519],{"class":23,"line":200},[21,520,446],{},[21,522,523],{"class":23,"line":206},[21,524,115],{},[53,526,527],{"id":527},"二分搜索不一定在有序数组上",[280,529,530],{},[531,532,536],"a",{"href":533,"rel":534},"https:\u002F\u002Fleetcode.cn\u002Fproblems\u002Ffind-peak-element\u002Fdescription\u002F",[535],"nofollow","leetcode 162.寻找峰值",[11,538,540],{"className":13,"code":539,"language":15,"meta":16,"style":16},"int find_peak(std::vector\u003Cint> &nums) {\n  int n = nums.size();\n  if(n == 1) return 0;\n  if(nums[0] > nums[1]) return 0;\n  if(nums[n-1] > nums[n-2]) return n-1;\n\n  int l = 1, r = n - 2, m = 0, ans = -1;\n  while(l \u003C= r) {\n    m = l + (r - l) \u002F 2;\n    \u002F\u002F 小于左边的数，那么极值点一定在左边\n    \u002F\u002F 右边可能也有但只要求返回一个极值点，不管右边\n    if(nums[m] \u003C nums[m-1]) r = m - 1;\n    \u002F\u002F 大于左边的数，小于右边的数，极值点一定在右边\n    else if(nums[m] \u003C nums[m+1]) l = m + 1;\n    \u002F\u002F 大于左边又大于右边，返回极值点\n    else{\n      ans = m;\n      break;\n    }\n  }\n  return ans;\n}\n",[18,541,542,547,552,557,562,567,571,576,580,584,589,594,599,604,609,614,619,624,629,633,637,642],{"__ignoreMap":16},[21,543,544],{"class":23,"line":24},[21,545,546],{},"int find_peak(std::vector\u003Cint> &nums) {\n",[21,548,549],{"class":23,"line":30},[21,550,551],{},"  int n = nums.size();\n",[21,553,554],{"class":23,"line":36},[21,555,556],{},"  if(n == 1) return 0;\n",[21,558,559],{"class":23,"line":42},[21,560,561],{},"  if(nums[0] > nums[1]) return 0;\n",[21,563,564],{"class":23,"line":48},[21,565,566],{},"  if(nums[n-1] > nums[n-2]) return n-1;\n",[21,568,569],{"class":23,"line":88},[21,570,144],{"emptyLinePlaceholder":143},[21,572,573],{"class":23,"line":94},[21,574,575],{},"  int l = 1, r = n - 2, m = 0, ans = -1;\n",[21,577,578],{"class":23,"line":100},[21,579,335],{},[21,581,582],{"class":23,"line":106},[21,583,340],{},[21,585,586],{"class":23,"line":112},[21,587,588],{},"    \u002F\u002F 小于左边的数，那么极值点一定在左边\n",[21,590,591],{"class":23,"line":176},[21,592,593],{},"    \u002F\u002F 右边可能也有但只要求返回一个极值点，不管右边\n",[21,595,596],{"class":23,"line":182},[21,597,598],{},"    if(nums[m] \u003C nums[m-1]) r = m - 1;\n",[21,600,601],{"class":23,"line":188},[21,602,603],{},"    \u002F\u002F 大于左边的数，小于右边的数，极值点一定在右边\n",[21,605,606],{"class":23,"line":194},[21,607,608],{},"    else if(nums[m] \u003C nums[m+1]) l = m + 1;\n",[21,610,611],{"class":23,"line":200},[21,612,613],{},"    \u002F\u002F 大于左边又大于右边，返回极值点\n",[21,615,616],{"class":23,"line":206},[21,617,618],{},"    else{\n",[21,620,621],{"class":23,"line":212},[21,622,623],{},"      ans = m;\n",[21,625,626],{"class":23,"line":218},[21,627,628],{},"      break;\n",[21,630,631],{"class":23,"line":224},[21,632,215],{},[21,634,635],{"class":23,"line":229},[21,636,103],{},[21,638,639],{"class":23,"line":234},[21,640,641],{},"  return ans;\n",[21,643,644],{"class":23,"line":240},[21,645,115],{},[280,647,648],{},"另一种if判断",[11,650,652],{"className":13,"code":651,"language":15,"meta":16,"style":16},"int find_peak(std::vector\u003Cint> &nums) {\n  int n = nums.size();\n  if(n == 1) return 0;\n  if(nums[0] > nums[1]) return 0;\n  if(nums[n-1] > nums[n-2]) return n-1;\n\n  int l = 1, r = n - 2, m = 0, ans = -1;\n  while(l \u003C= r) {\n    m = l + (r - l) \u002F 2;\n    if(nums[m] > nums[m-1] && nums[m] > nums[m+1]){\n      ans = m;\n      break;\n    }\n    else if(nums[m] \u003C nums[m-1]) r = m - 1;\n    else l = m + 1;\n  }\n  return ans;\n}\n",[18,653,654,658,662,666,670,674,678,682,686,690,695,699,703,707,712,716,720,724],{"__ignoreMap":16},[21,655,656],{"class":23,"line":24},[21,657,546],{},[21,659,660],{"class":23,"line":30},[21,661,551],{},[21,663,664],{"class":23,"line":36},[21,665,556],{},[21,667,668],{"class":23,"line":42},[21,669,561],{},[21,671,672],{"class":23,"line":48},[21,673,566],{},[21,675,676],{"class":23,"line":88},[21,677,144],{"emptyLinePlaceholder":143},[21,679,680],{"class":23,"line":94},[21,681,575],{},[21,683,684],{"class":23,"line":100},[21,685,335],{},[21,687,688],{"class":23,"line":106},[21,689,340],{},[21,691,692],{"class":23,"line":112},[21,693,694],{},"    if(nums[m] > nums[m-1] && nums[m] > nums[m+1]){\n",[21,696,697],{"class":23,"line":176},[21,698,623],{},[21,700,701],{"class":23,"line":182},[21,702,628],{},[21,704,705],{"class":23,"line":188},[21,706,215],{},[21,708,709],{"class":23,"line":194},[21,710,711],{},"    else if(nums[m] \u003C nums[m-1]) r = m - 1;\n",[21,713,714],{"class":23,"line":200},[21,715,355],{},[21,717,718],{"class":23,"line":206},[21,719,103],{},[21,721,722],{"class":23,"line":212},[21,723,641],{},[21,725,726],{"class":23,"line":218},[21,727,115],{},[280,729,730,731,734],{},"注意初始l，r的值，如果",[18,732,733],{},"int l = 0, r = n - 1","，当m为0或者n-1时，m-1或m+1会越界",[53,736,737],{"id":737},"二分答案法",[280,739,740],{},"待续...",[284,742,286],{},{"title":16,"searchDepth":30,"depth":30,"links":744},[745,746,747,748,749],{"id":310,"depth":30,"text":310},{"id":371,"depth":30,"text":372},{"id":453,"depth":30,"text":454},{"id":527,"depth":30,"text":527},{"id":737,"depth":30,"text":737},"2025-11-27",{},"\u002Fblog\u002Falgorithm-learning-003-binarysearch",{"title":305,"description":294},"blog\u002Falgorithm-learning-003-binarysearch",[301],"clwtVRZgjl2NkybguK28mv2cdlBhgwyRHyvlg4NAKGQ",{"id":758,"title":759,"body":760,"date":767,"description":294,"extension":295,"meta":768,"navigation":143,"path":769,"seo":770,"stem":771,"tags":772,"__hash__":773},"blog\u002Fblog\u002Falgorithm-learning-001-bitwise.md","位运算",{"type":8,"value":761,"toc":765},[762],[280,763,764],{},"待更新...",{"title":16,"searchDepth":30,"depth":30,"links":766},[],"2025-11-26",{},"\u002Fblog\u002Falgorithm-learning-001-bitwise",{"title":759,"description":294},"blog\u002Falgorithm-learning-001-bitwise",[301],"aFOXDDpcE-guiu1ZoriIQ1zm8fabcfDBg0uSepbCumk",{"id":775,"title":776,"body":777,"date":767,"description":294,"extension":295,"meta":984,"navigation":143,"path":985,"seo":986,"stem":987,"tags":988,"__hash__":989},"blog\u002Fblog\u002Falgorithm-learning-002-sort.md","排序",{"type":8,"value":778,"toc":979},[779,782,795,856,859,870,920,923,934,975,977],[53,780,781],{"id":781},"选择排序",[783,784,785,789,792],"ul",{},[786,787,788],"li",{},"0到n-1，选最小值放0位置",[786,790,791],{},"1到n-1，选最小值放1位置",[786,793,794],{},"i到n-1，选最小值放i位置",[11,796,798],{"className":13,"code":797,"language":15,"meta":16,"style":16},"void select_sort(std::vector\u003Cint> &nums) {\n  int n = nums.size();\n  for(int i = 0; i \u003C n - 1; ++i) {\n    int k = i;\n    for(int j = i + 1; j \u003C n; ++j) {\n      if(nums[j] \u003C nums[k]) {\n        k = j;\n      }\n    }\n    std::swap(nums[i], nums[k]);\n  }\n}\n",[18,799,800,805,809,814,819,824,829,834,839,843,848,852],{"__ignoreMap":16},[21,801,802],{"class":23,"line":24},[21,803,804],{},"void select_sort(std::vector\u003Cint> &nums) {\n",[21,806,807],{"class":23,"line":30},[21,808,551],{},[21,810,811],{"class":23,"line":36},[21,812,813],{},"  for(int i = 0; i \u003C n - 1; ++i) {\n",[21,815,816],{"class":23,"line":42},[21,817,818],{},"    int k = i;\n",[21,820,821],{"class":23,"line":48},[21,822,823],{},"    for(int j = i + 1; j \u003C n; ++j) {\n",[21,825,826],{"class":23,"line":88},[21,827,828],{},"      if(nums[j] \u003C nums[k]) {\n",[21,830,831],{"class":23,"line":94},[21,832,833],{},"        k = j;\n",[21,835,836],{"class":23,"line":100},[21,837,838],{},"      }\n",[21,840,841],{"class":23,"line":106},[21,842,215],{},[21,844,845],{"class":23,"line":112},[21,846,847],{},"    std::swap(nums[i], nums[k]);\n",[21,849,850],{"class":23,"line":176},[21,851,103],{},[21,853,854],{"class":23,"line":182},[21,855,115],{},[53,857,858],{"id":858},"冒泡排序",[783,860,861,864,867],{},[786,862,863],{},"0到n-1，最大值冒到n-1",[786,865,866],{},"0到n-2，最大值冒到n-2",[786,868,869],{},"0到i，最大值冒到i",[11,871,873],{"className":13,"code":872,"language":15,"meta":16,"style":16},"void bubble_sort(std::vector\u003Cint> &nums) {\n  int n = nums.size();\n  for(int i = n - 1; i > 0; --i) {\n    for(int j = 0; j \u003C i; ++j) {\n      if(nums[j] > nums[j+1]) {\n        std::swap(nums[j], nums[j+1]);\n      }\n    }\n  }\n}\n",[18,874,875,880,884,889,894,899,904,908,912,916],{"__ignoreMap":16},[21,876,877],{"class":23,"line":24},[21,878,879],{},"void bubble_sort(std::vector\u003Cint> &nums) {\n",[21,881,882],{"class":23,"line":30},[21,883,551],{},[21,885,886],{"class":23,"line":36},[21,887,888],{},"  for(int i = n - 1; i > 0; --i) {\n",[21,890,891],{"class":23,"line":42},[21,892,893],{},"    for(int j = 0; j \u003C i; ++j) {\n",[21,895,896],{"class":23,"line":48},[21,897,898],{},"      if(nums[j] > nums[j+1]) {\n",[21,900,901],{"class":23,"line":88},[21,902,903],{},"        std::swap(nums[j], nums[j+1]);\n",[21,905,906],{"class":23,"line":94},[21,907,838],{},[21,909,910],{"class":23,"line":100},[21,911,215],{},[21,913,914],{"class":23,"line":106},[21,915,103],{},[21,917,918],{"class":23,"line":112},[21,919,115],{},[53,921,922],{"id":922},"插入排序",[783,924,925,928,931],{},[786,926,927],{},"0到0有序，1位置往左插入",[786,929,930],{},"0到1有序，2位置往左插入",[786,932,933],{},"0到i-1有序，i位置往左插入",[11,935,937],{"className":13,"code":936,"language":15,"meta":16,"style":16},"void insert_sort(std::vector\u003Cint> &nums) {\n  int n = nums.size();\n  for(int i = 1; i \u003C n; ++i) {\n    for(int j = i - 1; j >= 0 && nums[j] > nums[j+1]; --j) {\n      std::swap(nums[j], nums[j+1]);\n    }\n  }\n}\n",[18,938,939,944,948,953,958,963,967,971],{"__ignoreMap":16},[21,940,941],{"class":23,"line":24},[21,942,943],{},"void insert_sort(std::vector\u003Cint> &nums) {\n",[21,945,946],{"class":23,"line":30},[21,947,551],{},[21,949,950],{"class":23,"line":36},[21,951,952],{},"  for(int i = 1; i \u003C n; ++i) {\n",[21,954,955],{"class":23,"line":42},[21,956,957],{},"    for(int j = i - 1; j >= 0 && nums[j] > nums[j+1]; --j) {\n",[21,959,960],{"class":23,"line":48},[21,961,962],{},"      std::swap(nums[j], nums[j+1]);\n",[21,964,965],{"class":23,"line":88},[21,966,215],{},[21,968,969],{"class":23,"line":94},[21,970,103],{},[21,972,973],{"class":23,"line":100},[21,974,115],{},[280,976,740],{},[284,978,286],{},{"title":16,"searchDepth":30,"depth":30,"links":980},[981,982,983],{"id":781,"depth":30,"text":781},{"id":858,"depth":30,"text":858},{"id":922,"depth":30,"text":922},{},"\u002Fblog\u002Falgorithm-learning-002-sort",{"title":776,"description":294},"blog\u002Falgorithm-learning-002-sort",[301],"DTZFXPn4WahM1DB2nkLICcRHxnWMamd5L57ftEOdLjY",{"id":991,"title":992,"body":993,"date":1169,"description":294,"extension":295,"meta":1170,"navigation":143,"path":1171,"seo":1172,"stem":1173,"tags":1174,"__hash__":1176},"blog\u002Fblog\u002Fescape-sequence.md","C++的`\\?`",{"type":8,"value":994,"toc":1165},[995,998,1004,1011,1021,1025,1028,1031,1158],[53,996,997],{"id":997},"引言",[280,999,1000,1001],{},"在重读《C++ Primer》2.1 基本数据类型的时候，发现在C++的转义序列里面有个之前没注意到的细节",[18,1002,1003],{},"\\?",[280,1005,1006],{},[1007,1008],"img",{"alt":1009,"src":1010},"escape sequence","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fcplusplus_escape_sequence.jpg",[280,1012,1013,1014,1016,1017,1020],{},"遂打开vscode发现",[18,1015,1003],{},"与",[18,1018,1019],{},"?","并没有什么区别",[53,1022,1024],{"id":1023},"trigraph","Trigraph",[280,1026,1027],{},"那么为什么问号需要转义字符来表示它本身，显然它确实有特殊含义",[280,1029,1030],{},"trigraph三字符组，最早设计出来解决某些键盘不能输入某些编程必须的字符问题",[1032,1033,1034,1046],"table",{},[1035,1036,1037],"thead",{},[1038,1039,1040,1043],"tr",{},[1041,1042,1024],"th",{},[1041,1044,1045],{},"Equivalent",[1047,1048,1049,1062,1074,1086,1098,1110,1122,1134,1146],"tbody",{},[1038,1050,1051,1057],{},[1052,1053,1054],"td",{},[18,1055,1056],{},"??=",[1052,1058,1059],{},[18,1060,1061],{},"#",[1038,1063,1064,1069],{},[1052,1065,1066],{},[18,1067,1068],{},"??\u002F",[1052,1070,1071],{},[18,1072,1073],{},"\\",[1038,1075,1076,1081],{},[1052,1077,1078],{},[18,1079,1080],{},"??` ",[1052,1082,1083],{},[18,1084,1085],{},"^",[1038,1087,1088,1093],{},[1052,1089,1090],{},[18,1091,1092],{},"??(",[1052,1094,1095],{},[18,1096,1097],{},"[",[1038,1099,1100,1105],{},[1052,1101,1102],{},[18,1103,1104],{},"??)",[1052,1106,1107],{},[18,1108,1109],{},"]",[1038,1111,1112,1117],{},[1052,1113,1114],{},[18,1115,1116],{},"??!",[1052,1118,1119],{},[18,1120,1121],{},"|",[1038,1123,1124,1129],{},[1052,1125,1126],{},[18,1127,1128],{},"??\u003C",[1052,1130,1131],{},[18,1132,1133],{},"{",[1038,1135,1136,1141],{},[1052,1137,1138],{},[18,1139,1140],{},"??>",[1052,1142,1143],{},[18,1144,1145],{},"}",[1038,1147,1148,1153],{},[1052,1149,1150],{},[18,1151,1152],{},"??-",[1052,1154,1155],{},[18,1156,1157],{},"~",[280,1159,1160,1161,1164],{},"及当同时使用",[18,1162,1163],{},"??","时，需要对问号进行转义，避免编译器将其误解释为一个trigraph",{"title":16,"searchDepth":30,"depth":30,"links":1166},[1167,1168],{"id":997,"depth":30,"text":997},{"id":1023,"depth":30,"text":1024},"2025-05-11",{},"\u002Fblog\u002Fescape-sequence",{"title":992,"description":294},"blog\u002Fescape-sequence",[1175],"C++","JSByrGOUS0sRfv7X1XMwjlDCB_HYTnRcrFxkjR3PzzU",{"id":1178,"title":1179,"body":1180,"date":1899,"description":294,"extension":295,"meta":1900,"navigation":143,"path":1901,"seo":1902,"stem":1903,"tags":1904,"__hash__":1906},"blog\u002Fblog\u002Flock-free-stack.md","无锁的线程安全栈",{"type":8,"value":1181,"toc":1890},[1182,1188,1191,1283,1296,1300,1303,1386,1397,1400,1403,1411,1416,1419,1450,1457,1460,1477,1544,1547,1556,1880,1883,1888],[1183,1184,1185],"blockquote",{},[280,1186,1187],{},"选自《C++ Concurrency In Action》7.2.4",[53,1189,1190],{"id":1190},"数据结构",[11,1192,1194],{"className":13,"code":1193,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nclass LockFreeStack {\n private:\n  struct Node;\n  struct CountNodePtr {\n    int external_count;\n    Node* ptr;\n  };\n  struct Node {\n    std::shared_ptr\u003CT> data;\n    std::atomic\u003Cint> internal_count;\n    CountNodePtr next;\n\n    Node(const T& value)\n        : data(std::make_shared\u003CT>(value)), internal_count(0) {}\n  };\n  std::atomic\u003CCountNodePtr> head;\n};\n",[18,1195,1196,1201,1206,1211,1216,1221,1226,1231,1236,1241,1246,1251,1256,1260,1265,1270,1274,1279],{"__ignoreMap":16},[21,1197,1198],{"class":23,"line":24},[21,1199,1200],{},"template \u003Ctypename T>\n",[21,1202,1203],{"class":23,"line":30},[21,1204,1205],{},"class LockFreeStack {\n",[21,1207,1208],{"class":23,"line":36},[21,1209,1210],{}," private:\n",[21,1212,1213],{"class":23,"line":42},[21,1214,1215],{},"  struct Node;\n",[21,1217,1218],{"class":23,"line":48},[21,1219,1220],{},"  struct CountNodePtr {\n",[21,1222,1223],{"class":23,"line":88},[21,1224,1225],{},"    int external_count;\n",[21,1227,1228],{"class":23,"line":94},[21,1229,1230],{},"    Node* ptr;\n",[21,1232,1233],{"class":23,"line":100},[21,1234,1235],{},"  };\n",[21,1237,1238],{"class":23,"line":106},[21,1239,1240],{},"  struct Node {\n",[21,1242,1243],{"class":23,"line":112},[21,1244,1245],{},"    std::shared_ptr\u003CT> data;\n",[21,1247,1248],{"class":23,"line":176},[21,1249,1250],{},"    std::atomic\u003Cint> internal_count;\n",[21,1252,1253],{"class":23,"line":182},[21,1254,1255],{},"    CountNodePtr next;\n",[21,1257,1258],{"class":23,"line":188},[21,1259,144],{"emptyLinePlaceholder":143},[21,1261,1262],{"class":23,"line":194},[21,1263,1264],{},"    Node(const T& value)\n",[21,1266,1267],{"class":23,"line":200},[21,1268,1269],{},"        : data(std::make_shared\u003CT>(value)), internal_count(0) {}\n",[21,1271,1272],{"class":23,"line":206},[21,1273,1235],{},[21,1275,1276],{"class":23,"line":212},[21,1277,1278],{},"  std::atomic\u003CCountNodePtr> head;\n",[21,1280,1281],{"class":23,"line":218},[21,1282,51],{},[1183,1284,1285],{},[280,1286,1287,1290,1291,1295],{},[18,1288,1289],{},"std::atomic\u003CCountNodePtr>","超过8字节，",[1292,1293,1294],"del",{},"可能","并不是无锁的，主要学习如何通过计数避免资源过早释放",[1297,1298,1299],"h3",{"id":1299},"为什么引用计数",[280,1301,1302],{},"正常通过链表实现的无锁栈",[11,1304,1306],{"className":13,"code":1305,"language":15,"meta":16,"style":16},"class LockFreeStack {\nprivate:\n  struct Node {\n    std::shared_ptr\u003CT> data;\n    Node* next;\n  };\n  std::atomic\u003CNode*> head;\n\npublic:\n  std::shared_ptr\u003CT> pop() {\n    Node* old_head = head.load(); \u002F\u002F 1\n    while(old_head && !head.compare_exchange_weak(old_head, old_head->next)); \u002F\u002F 2\n    std::shared_ptr\u003CT> res = old_head ? old_head->data : std::make_shared\u003CT>();\n    delete old_head;\n    return res;\n  }\n};\n",[18,1307,1308,1312,1317,1321,1325,1330,1334,1339,1343,1348,1353,1358,1363,1368,1373,1378,1382],{"__ignoreMap":16},[21,1309,1310],{"class":23,"line":24},[21,1311,1205],{},[21,1313,1314],{"class":23,"line":30},[21,1315,1316],{},"private:\n",[21,1318,1319],{"class":23,"line":36},[21,1320,1240],{},[21,1322,1323],{"class":23,"line":42},[21,1324,1245],{},[21,1326,1327],{"class":23,"line":48},[21,1328,1329],{},"    Node* next;\n",[21,1331,1332],{"class":23,"line":88},[21,1333,1235],{},[21,1335,1336],{"class":23,"line":94},[21,1337,1338],{},"  std::atomic\u003CNode*> head;\n",[21,1340,1341],{"class":23,"line":100},[21,1342,144],{"emptyLinePlaceholder":143},[21,1344,1345],{"class":23,"line":106},[21,1346,1347],{},"public:\n",[21,1349,1350],{"class":23,"line":112},[21,1351,1352],{},"  std::shared_ptr\u003CT> pop() {\n",[21,1354,1355],{"class":23,"line":176},[21,1356,1357],{},"    Node* old_head = head.load(); \u002F\u002F 1\n",[21,1359,1360],{"class":23,"line":182},[21,1361,1362],{},"    while(old_head && !head.compare_exchange_weak(old_head, old_head->next)); \u002F\u002F 2\n",[21,1364,1365],{"class":23,"line":188},[21,1366,1367],{},"    std::shared_ptr\u003CT> res = old_head ? old_head->data : std::make_shared\u003CT>();\n",[21,1369,1370],{"class":23,"line":194},[21,1371,1372],{},"    delete old_head;\n",[21,1374,1375],{"class":23,"line":200},[21,1376,1377],{},"    return res;\n",[21,1379,1380],{"class":23,"line":206},[21,1381,103],{},[21,1383,1384],{"class":23,"line":212},[21,1385,51],{},[280,1387,1388,1389,1392,1393,1396],{},"主要基于出栈时，线程a运行完步骤1后，线程b删除了该节点。因此线程a中的",[18,1390,1391],{},"old_head","变成了\n悬空指针(并不是nullptr，但指向的空间被释放)，而步骤2中，调用CAS前需要计算实参",[18,1394,1395],{},"old_head->next","的值",[280,1398,1399],{},"因此在确保没有线程使用该结点前，不能释放该结点，引用计数可以解决该问题",[1297,1401,1402],{"id":1402},"为什么拆分引用计数",[1183,1404,1405],{},[280,1406,1407],{},[531,1408,1409],{"href":1409,"rel":1410},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F67371033\u002Fhow-does-the-split-reference-counting-work-in-a-lock-free-stack",[535],[280,1412,1413],{},[1292,1414,1415],{},"还是看stackoverflow里老哥的回答吧，我也不明白为什么这么设计",[280,1417,1418],{},"将计数分为两个阶段",[1420,1421,1422,1443],"ol",{},[786,1423,1424,1425,1016,1428,1431,1432],{},"计数器被拆分(",[18,1426,1427],{},"external_count",[18,1429,1430],{},"internal_count",")，两个的和才是结点的真正计数",[783,1433,1434,1437],{},[786,1435,1436],{},"引用结点时，外部计数加一，结束访问时，内部计数减一",[786,1438,1439,1442],{},[18,1440,1441],{},"external_count == -internal_count","时，引用为0，删除结点",[786,1444,1445,1446,1449],{},"将外部计数加入到内部计数中，此时内部计数表示真正的计数",[1447,1448],"br",{},"内部计数为0时，删除结点",[280,1451,1452,1453,1456],{},"当其他线程引用结点时，外部计数加一。如果没有内部计数，在不再引用时，会递减计数，此时就需要\n与递增时一样(",[18,1454,1455],{},"IncreaseHeadCount()",")循环调用CAS",[53,1458,1459],{"id":1459},"入栈",[1420,1461,1462,1472],{},[786,1463,1464,1465,1468,1469],{},"将当前结点的",[18,1466,1467],{},"next","指向栈顶",[18,1470,1471],{},"head",[786,1473,1474,1475],{},"将当前结点设置为",[18,1476,1471],{},[11,1478,1480],{"className":13,"code":1479,"language":15,"meta":16,"style":16},"void push(const T& value) {\n  \u002F\u002F 1. 创建结点，并计数\n  CountNodePtr new_node;\n  new_node.ptr = new Node(value);\n  new_node.external_count = 1;\n\n  \u002F\u002F 2. 将新结点的next指向head\n  new_node.ptr->next = head;\n\n  \u002F\u002F 3. 确保新结点的next指向的是最新的head\n  \u002F\u002F 并将新节点设为head\n  while(!head.compare_exchange_weak(new_node.ptr->next, new_node));\n}\n",[18,1481,1482,1487,1492,1497,1502,1507,1511,1516,1521,1525,1530,1535,1540],{"__ignoreMap":16},[21,1483,1484],{"class":23,"line":24},[21,1485,1486],{},"void push(const T& value) {\n",[21,1488,1489],{"class":23,"line":30},[21,1490,1491],{},"  \u002F\u002F 1. 创建结点，并计数\n",[21,1493,1494],{"class":23,"line":36},[21,1495,1496],{},"  CountNodePtr new_node;\n",[21,1498,1499],{"class":23,"line":42},[21,1500,1501],{},"  new_node.ptr = new Node(value);\n",[21,1503,1504],{"class":23,"line":48},[21,1505,1506],{},"  new_node.external_count = 1;\n",[21,1508,1509],{"class":23,"line":88},[21,1510,144],{"emptyLinePlaceholder":143},[21,1512,1513],{"class":23,"line":94},[21,1514,1515],{},"  \u002F\u002F 2. 将新结点的next指向head\n",[21,1517,1518],{"class":23,"line":100},[21,1519,1520],{},"  new_node.ptr->next = head;\n",[21,1522,1523],{"class":23,"line":106},[21,1524,144],{"emptyLinePlaceholder":143},[21,1526,1527],{"class":23,"line":112},[21,1528,1529],{},"  \u002F\u002F 3. 确保新结点的next指向的是最新的head\n",[21,1531,1532],{"class":23,"line":176},[21,1533,1534],{},"  \u002F\u002F 并将新节点设为head\n",[21,1536,1537],{"class":23,"line":182},[21,1538,1539],{},"  while(!head.compare_exchange_weak(new_node.ptr->next, new_node));\n",[21,1541,1542],{"class":23,"line":188},[21,1543,115],{},[53,1545,1546],{"id":1546},"出栈",[280,1548,1549,1550,1552,1553,1555],{},"出栈就是将栈顶(",[18,1551,1471],{},")的",[18,1554,1467],{},"设为栈顶，然后删除旧的栈顶",[11,1557,1559],{"className":13,"code":1558,"language":15,"meta":16,"style":16},"void IncreaseHeadCount(CountNodePtr& old_counter) {\n  \u002F\u002F new_counter只是个临时量\n  counted_node_ptr new_counter;\n  do {\n    new_counter = old_counter;\n    ++new_counter.external_count;\n    \u002F\u002F 如果head没变，head的外部计数加一\n    \u002F\u002F 如果head改变，old_counter拷贝head，然后head的外部计数加一\n  } while(!head.compare_exchange_strong(old_counter, new_counter));\n  old_counter.external_count = new_counter.external_count;\n}\n\nstd::shared_ptr\u003CT> pop() {\n  CountNodePtr old_head = head.load();\n  while (1) {\n    \u002F\u002F 获取头节点以及其外部计数\n    IncreaseHeadCount(old_head);\n    node* old_head_ptr = old_head.ptr;\n    if (!old_head_ptr) return std::shared_ptr\u003CT>();\n\n    \u002F\u002F 某个线程(线程a)进入后，其他线程就无法获取到当前结点了\n    \u002F\u002F 其他线程就两种情况:\n    \u002F\u002F 1. 刚进入pop，还没有IncreaseHeadCount()，那就与当前节点无关了，加载的是另外的结点\n    \u002F\u002F 2. 经过了这个IncreaseHeadCount()，引用的是与线程a相同的结点，此时只能结束对当前结点的引用\n    \u002F\u002F 内部计数减一，即执行else if的内容\n    if (head.compare_exchange_strong(old_head, old_head_ptr->next)) {\n\n      std::shared_ptr\u003CT> res;\n      res.swap(old_head_ptr->data);\n\n      \u002F\u002F head不再引用该结点，因此减一\n      \u002F\u002F 当前的old_head也将要超出作用域，再次减一\n      \u002F\u002F 该代码好像有些误导，需要看下面的部分才能明白此处的含义\n      const int count_increase = old_head.external_count - 2;\n\n      \u002F\u002F 当前线程退出引用后，计数的第一阶段结束\n      \u002F\u002F 此时外部计数为external_count，而内部计数为internal_count - 2\n      \u002F\u002F 判断总计数 = external_count + (internal_count - 2)是否为0\n      \u002F\u002F 即判断internal_count == -(external_count - 2)\n      \u002F\u002F 此处解释了上面count_increase的定义，以及下面的if判断\n\n      \u002F\u002F 计数进入第二阶段，将外部计数加入到内部计数\n      \u002F\u002F internal_count = (internal_count - 2) + external_count\n      \u002F\u002F 也就是internal_count.fetch_add(external_count - 2)\n      if (old_head_ptr->internal_count.fetch_add(count_increase) ==\n          -count_increase) {\n        delete old_head_ptr;\n      }\n      return res;\n    } else if (old_head_ptr->internal_count.fetch_sub(1) == 1) {\n      \u002F\u002F internal_count.fetch_sub(-1)很好理解，其他线程退出对当前结点的引用，详情见if(CAS)处的注释\n      \u002F\u002F 对于internal_count == 1的判断\n      \u002F\u002F 一般来说internal_count肯定为0或者负数，即线程退出结点的引用而递减\n      \u002F\u002F 当internal_count为正数时，表示当前节点的计数由内部计数来表达(计数的第二阶段)\n      \u002F\u002F internal_count为1，表示只有当前线程持有该节点，因此退出引用，删除节点\n      delete old_head_ptr;\n    }\n  }\n}\n",[18,1560,1561,1566,1571,1576,1581,1586,1591,1596,1601,1606,1611,1615,1619,1624,1629,1634,1639,1644,1649,1654,1658,1663,1668,1673,1678,1684,1690,1695,1701,1707,1712,1718,1724,1730,1736,1741,1747,1753,1759,1765,1771,1776,1782,1788,1794,1800,1806,1812,1817,1823,1829,1835,1841,1847,1853,1859,1865,1870,1875],{"__ignoreMap":16},[21,1562,1563],{"class":23,"line":24},[21,1564,1565],{},"void IncreaseHeadCount(CountNodePtr& old_counter) {\n",[21,1567,1568],{"class":23,"line":30},[21,1569,1570],{},"  \u002F\u002F new_counter只是个临时量\n",[21,1572,1573],{"class":23,"line":36},[21,1574,1575],{},"  counted_node_ptr new_counter;\n",[21,1577,1578],{"class":23,"line":42},[21,1579,1580],{},"  do {\n",[21,1582,1583],{"class":23,"line":48},[21,1584,1585],{},"    new_counter = old_counter;\n",[21,1587,1588],{"class":23,"line":88},[21,1589,1590],{},"    ++new_counter.external_count;\n",[21,1592,1593],{"class":23,"line":94},[21,1594,1595],{},"    \u002F\u002F 如果head没变，head的外部计数加一\n",[21,1597,1598],{"class":23,"line":100},[21,1599,1600],{},"    \u002F\u002F 如果head改变，old_counter拷贝head，然后head的外部计数加一\n",[21,1602,1603],{"class":23,"line":106},[21,1604,1605],{},"  } while(!head.compare_exchange_strong(old_counter, new_counter));\n",[21,1607,1608],{"class":23,"line":112},[21,1609,1610],{},"  old_counter.external_count = new_counter.external_count;\n",[21,1612,1613],{"class":23,"line":176},[21,1614,115],{},[21,1616,1617],{"class":23,"line":182},[21,1618,144],{"emptyLinePlaceholder":143},[21,1620,1621],{"class":23,"line":188},[21,1622,1623],{},"std::shared_ptr\u003CT> pop() {\n",[21,1625,1626],{"class":23,"line":194},[21,1627,1628],{},"  CountNodePtr old_head = head.load();\n",[21,1630,1631],{"class":23,"line":200},[21,1632,1633],{},"  while (1) {\n",[21,1635,1636],{"class":23,"line":206},[21,1637,1638],{},"    \u002F\u002F 获取头节点以及其外部计数\n",[21,1640,1641],{"class":23,"line":212},[21,1642,1643],{},"    IncreaseHeadCount(old_head);\n",[21,1645,1646],{"class":23,"line":218},[21,1647,1648],{},"    node* old_head_ptr = old_head.ptr;\n",[21,1650,1651],{"class":23,"line":224},[21,1652,1653],{},"    if (!old_head_ptr) return std::shared_ptr\u003CT>();\n",[21,1655,1656],{"class":23,"line":229},[21,1657,144],{"emptyLinePlaceholder":143},[21,1659,1660],{"class":23,"line":234},[21,1661,1662],{},"    \u002F\u002F 某个线程(线程a)进入后，其他线程就无法获取到当前结点了\n",[21,1664,1665],{"class":23,"line":240},[21,1666,1667],{},"    \u002F\u002F 其他线程就两种情况:\n",[21,1669,1670],{"class":23,"line":246},[21,1671,1672],{},"    \u002F\u002F 1. 刚进入pop，还没有IncreaseHeadCount()，那就与当前节点无关了，加载的是另外的结点\n",[21,1674,1675],{"class":23,"line":252},[21,1676,1677],{},"    \u002F\u002F 2. 经过了这个IncreaseHeadCount()，引用的是与线程a相同的结点，此时只能结束对当前结点的引用\n",[21,1679,1681],{"class":23,"line":1680},25,[21,1682,1683],{},"    \u002F\u002F 内部计数减一，即执行else if的内容\n",[21,1685,1687],{"class":23,"line":1686},26,[21,1688,1689],{},"    if (head.compare_exchange_strong(old_head, old_head_ptr->next)) {\n",[21,1691,1693],{"class":23,"line":1692},27,[21,1694,144],{"emptyLinePlaceholder":143},[21,1696,1698],{"class":23,"line":1697},28,[21,1699,1700],{},"      std::shared_ptr\u003CT> res;\n",[21,1702,1704],{"class":23,"line":1703},29,[21,1705,1706],{},"      res.swap(old_head_ptr->data);\n",[21,1708,1710],{"class":23,"line":1709},30,[21,1711,144],{"emptyLinePlaceholder":143},[21,1713,1715],{"class":23,"line":1714},31,[21,1716,1717],{},"      \u002F\u002F head不再引用该结点，因此减一\n",[21,1719,1721],{"class":23,"line":1720},32,[21,1722,1723],{},"      \u002F\u002F 当前的old_head也将要超出作用域，再次减一\n",[21,1725,1727],{"class":23,"line":1726},33,[21,1728,1729],{},"      \u002F\u002F 该代码好像有些误导，需要看下面的部分才能明白此处的含义\n",[21,1731,1733],{"class":23,"line":1732},34,[21,1734,1735],{},"      const int count_increase = old_head.external_count - 2;\n",[21,1737,1739],{"class":23,"line":1738},35,[21,1740,144],{"emptyLinePlaceholder":143},[21,1742,1744],{"class":23,"line":1743},36,[21,1745,1746],{},"      \u002F\u002F 当前线程退出引用后，计数的第一阶段结束\n",[21,1748,1750],{"class":23,"line":1749},37,[21,1751,1752],{},"      \u002F\u002F 此时外部计数为external_count，而内部计数为internal_count - 2\n",[21,1754,1756],{"class":23,"line":1755},38,[21,1757,1758],{},"      \u002F\u002F 判断总计数 = external_count + (internal_count - 2)是否为0\n",[21,1760,1762],{"class":23,"line":1761},39,[21,1763,1764],{},"      \u002F\u002F 即判断internal_count == -(external_count - 2)\n",[21,1766,1768],{"class":23,"line":1767},40,[21,1769,1770],{},"      \u002F\u002F 此处解释了上面count_increase的定义，以及下面的if判断\n",[21,1772,1774],{"class":23,"line":1773},41,[21,1775,144],{"emptyLinePlaceholder":143},[21,1777,1779],{"class":23,"line":1778},42,[21,1780,1781],{},"      \u002F\u002F 计数进入第二阶段，将外部计数加入到内部计数\n",[21,1783,1785],{"class":23,"line":1784},43,[21,1786,1787],{},"      \u002F\u002F internal_count = (internal_count - 2) + external_count\n",[21,1789,1791],{"class":23,"line":1790},44,[21,1792,1793],{},"      \u002F\u002F 也就是internal_count.fetch_add(external_count - 2)\n",[21,1795,1797],{"class":23,"line":1796},45,[21,1798,1799],{},"      if (old_head_ptr->internal_count.fetch_add(count_increase) ==\n",[21,1801,1803],{"class":23,"line":1802},46,[21,1804,1805],{},"          -count_increase) {\n",[21,1807,1809],{"class":23,"line":1808},47,[21,1810,1811],{},"        delete old_head_ptr;\n",[21,1813,1815],{"class":23,"line":1814},48,[21,1816,838],{},[21,1818,1820],{"class":23,"line":1819},49,[21,1821,1822],{},"      return res;\n",[21,1824,1826],{"class":23,"line":1825},50,[21,1827,1828],{},"    } else if (old_head_ptr->internal_count.fetch_sub(1) == 1) {\n",[21,1830,1832],{"class":23,"line":1831},51,[21,1833,1834],{},"      \u002F\u002F internal_count.fetch_sub(-1)很好理解，其他线程退出对当前结点的引用，详情见if(CAS)处的注释\n",[21,1836,1838],{"class":23,"line":1837},52,[21,1839,1840],{},"      \u002F\u002F 对于internal_count == 1的判断\n",[21,1842,1844],{"class":23,"line":1843},53,[21,1845,1846],{},"      \u002F\u002F 一般来说internal_count肯定为0或者负数，即线程退出结点的引用而递减\n",[21,1848,1850],{"class":23,"line":1849},54,[21,1851,1852],{},"      \u002F\u002F 当internal_count为正数时，表示当前节点的计数由内部计数来表达(计数的第二阶段)\n",[21,1854,1856],{"class":23,"line":1855},55,[21,1857,1858],{},"      \u002F\u002F internal_count为1，表示只有当前线程持有该节点，因此退出引用，删除节点\n",[21,1860,1862],{"class":23,"line":1861},56,[21,1863,1864],{},"      delete old_head_ptr;\n",[21,1866,1868],{"class":23,"line":1867},57,[21,1869,215],{},[21,1871,1873],{"class":23,"line":1872},58,[21,1874,103],{},[21,1876,1878],{"class":23,"line":1877},59,[21,1879,115],{},[53,1881,1882],{"id":1882},"内存模型",[280,1884,1885],{},[1292,1886,1887],{},"这部分更是重量级，个人水平有限",[284,1889,286],{},{"title":16,"searchDepth":30,"depth":30,"links":1891},[1892,1896,1897,1898],{"id":1190,"depth":30,"text":1190,"children":1893},[1894,1895],{"id":1299,"depth":36,"text":1299},{"id":1402,"depth":36,"text":1402},{"id":1459,"depth":30,"text":1459},{"id":1546,"depth":30,"text":1546},{"id":1882,"depth":30,"text":1882},"2023-08-13",{},"\u002Fblog\u002Flock-free-stack",{"title":1179,"description":294},"blog\u002Flock-free-stack",[1175,1905],"并发编程","8vUSAAA8Y1Zl1ZQxBd_eJfSc4-atxvH-UR1IzQ3VA6I",{"id":1908,"title":1909,"body":1910,"date":2096,"description":294,"extension":295,"meta":2097,"navigation":143,"path":2098,"seo":2099,"stem":2100,"tags":2101,"__hash__":2102},"blog\u002Fblog\u002Fmesi.md","MESI协议以及内存屏障",{"type":8,"value":1911,"toc":2087},[1912,1915,1918,1923,1929,1933,1947,1953,1958,1961,2030,2034,2037,2040,2044,2047,2050,2053,2056,2062,2070,2073,2076,2079],[53,1913,1914],{"id":1914},"缓存一致性",[280,1916,1917],{},"由于L1\u002FL2 Cache是多个核心各自独有的，会带来缓存一致性的问题",[1183,1919,1920],{},[280,1921,1922],{},"CPU Cache是由多个Cache Line组成，Cache Line也是CPU从内存读取数据的基本单位",[280,1924,1925],{},[1007,1926],{"alt":1927,"src":1928},"cpu","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fcpu.png",[53,1930,1932],{"id":1931},"mesi协议","MESI协议",[783,1934,1935,1938,1941,1944],{},[786,1936,1937],{},"Modified，已修改",[786,1939,1940],{},"Exclusive，独占",[786,1942,1943],{},"Shared，共享",[786,1945,1946],{},"Invalidated，已失效",[280,1948,1949],{},[1007,1950],{"alt":1951,"src":1952},"cpu memory1","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fmemory1.png",[1183,1954,1955],{},[280,1956,1957],{},"CPU要从主存读写数据，需要向总线发起事务(读事务或写事务)来从主存读取或者写入数据",[280,1959,1960],{},"假设有两个CPU，A与B，内存中n的值为1",[1032,1962,1963,1978],{},[1035,1964,1965],{},[1038,1966,1967,1971,1975],{},[1041,1968,1970],{"align":1969},"left","操作",[1041,1972,1974],{"align":1973},"center","CPU A Cache 中数据的状态",[1041,1976,1977],{"align":1973},"CPU B Cache 中的数据的状态",[1047,1979,1980,1991,2001,2011,2021],{},[1038,1981,1982,1985,1988],{},[1052,1983,1984],{"align":1969},"1. B读取n时，此时Cache B中没有，从主存中读取",[1052,1986,1987],{"align":1973},"-",[1052,1989,1990],{"align":1973},"n变为独占状态",[1038,1992,1993,1996,1999],{},[1052,1994,1995],{"align":1969},"2. A获取n时，此时Cache A中没有，而B收到总线通知，将n拷贝到Cache A",[1052,1997,1998],{"align":1973},"n变为共享状态",[1052,2000,1998],{"align":1973},[1038,2002,2003,2006,2008],{},[1052,2004,2005],{"align":1969},"3. A要修改n的值时，通过总线发送失效指令，A收到B的ACK后才能进行修改",[1052,2007,1990],{"align":1973},[1052,2009,2010],{"align":1973},"n变为失效状态",[1038,2012,2013,2016,2019],{},[1052,2014,2015],{"align":1969},"4. A修改完毕后，如令n=2",[1052,2017,2018],{"align":1973},"n变为已修改状态",[1052,2020,2010],{"align":1973},[1038,2022,2023,2026,2028],{},[1052,2024,2025],{"align":1969},"5. 当B再次获取n时，发现自己是失效的，需要向A请求",[1052,2027,1998],{"align":1973},[1052,2029,1998],{"align":1973},[1297,2031,2033],{"id":2032},"store-buffer","Store Buffer",[280,2035,2036],{},"通过MESI保证了缓存一致性，即保证A与B缓存中的数据一致。但A的每次修改都需要等待B的ACK，会占用CPU的\n利用率，因此引用Store Buffer",[280,2038,2039],{},"当CPU发送失效指令后，将修改的数据放入Store Buffer，然后执行其他命令。当其他CPU都响应了ACK后，\nCPU Cache再从Store Buffer读取数据",[1297,2041,2043],{"id":2042},"store-forward","Store Forward",[280,2045,2046],{},"当修改后的数据还在Store Buffer，Cache中的数据仍是旧值时，CPU如果接到读取指令，会从Cache中读取到旧值。\n因此，当CPU读取数据时，会先查看Store Buffer中有没有，如果有则直接读取Store Buffer里的值，如果没有才\n读取Cache中的数据，这便是Store Forward",[1297,2048,2049],{"id":2049},"失效队列",[280,2051,2052],{},"所有的数据的修改都会存在Store Buffer中，而当Store Buffer满了后，CPU仍然需要等待ACK后才能进行修改。\n当其他CPU收到失效指令后，要先将数据置为失效状态，然后再响应ACK，而此时CPU可能很忙，不能及时处理",[280,2054,2055],{},"因此加入失效队列，当CPU收到失效指令时，将其放入失效队列中，并直接返回ACK，等到空闲时再处理队列中的消息",[280,2057,2058],{},[1007,2059],{"alt":2060,"src":2061},"cpu memory2","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fmemory2.png",[1183,2063,2064,2067],{},[280,2065,2066],{},"CPU的指令乱序执行是由Store Buffer以及失效队列造成的",[280,2068,2069],{},"假设缓存A，B中都有a，b。当CPU A先修改a后修改b时，可能会先收到b的ACK再收到a的ACK，即先修改b再修改a",[53,2071,2072],{"id":2072},"内存屏障",[280,2074,2075],{},"Store Buffer不能保证最新的修改更新到主存，而CPU没有及时的读取失效队列的消息导致\n缓存数据没能及时变为失效状态，而直接被读取",[280,2077,2078],{},"内存屏障简单的说就是禁用Store Buffer以及失效队列",[783,2080,2081,2084],{},[786,2082,2083],{},"在写入数据时，保证所有写入指令都执行完毕(Store Buffer都写入到Cache)",[786,2085,2086],{},"在读取数据时，保证所有失效队列的消息已经完成",{"title":16,"searchDepth":30,"depth":30,"links":2088},[2089,2090,2095],{"id":1914,"depth":30,"text":1914},{"id":1931,"depth":30,"text":1932,"children":2091},[2092,2093,2094],{"id":2032,"depth":36,"text":2033},{"id":2042,"depth":36,"text":2043},{"id":2049,"depth":36,"text":2049},{"id":2072,"depth":30,"text":2072},"2023-08-10",{},"\u002Fblog\u002Fmesi",{"title":1909,"description":294},"blog\u002Fmesi",[1905],"s1oKyeYgJ-MS8sfNom9CTPc5fd0k76xYPgRSI0-exT8",{"id":2104,"title":2105,"body":2106,"date":4295,"description":294,"extension":295,"meta":4296,"navigation":143,"path":4297,"seo":4298,"stem":4299,"tags":4300,"__hash__":4301},"blog\u002Fblog\u002Fcplusplus-concurrency.md","C++并发",{"type":8,"value":2107,"toc":4278},[2108,2111,2125,2128,2196,2201,2204,2207,2212,2229,2233,2262,2265,2268,2374,2377,2406,2410,2417,2431,2434,2461,2465,2471,2485,2488,2704,2708,2716,2740,2743,2746,2751,2812,2823,2826,2909,2912,2931,2998,3008,3017,3020,3030,3047,3050,3053,3062,3141,3158,3204,3207,3213,3217,3234,3254,3265,3299,3304,3324,3328,3335,3364,3367,3411,3415,3418,3473,3477,3484,3503,3517,3531,3534,3538,3560,3564,3630,3633,3636,3650,3657,3661,3666,3675,3762,3766,3778,3808,3816,3826,3836,3841,3873,3886,3901,3905,3915,3944,3948,3951,3983,3988,4004,4007,4010,4018,4021,4127,4130,4135,4143,4158,4161,4164,4167,4180,4186,4192,4237,4243,4246,4251,4254,4259,4262,4267,4270,4276],[280,2109,2110],{},"参考自《C++ Concurrency In Action》",[783,2112,2113,2116],{},[786,2114,2115],{},"作者：Anthony Williams",[786,2117,2118,2119],{},"译者：",[531,2120,2124],{"href":2121,"rel":2122,"title":2123},"https:\u002F\u002Fgithub.com\u002FxiaoweiChen",[535],"github","xiaoweiChen",[53,2126,2127],{"id":2127},"线程管理",[783,2129,2130,2168,2182,2193],{},[786,2131,2132,2133],{},"启动新线程",[11,2134,2136],{"className":13,"code":2135,"language":15,"meta":16,"style":16},"void Func1();\nvoid Func2(int);\nvoid Foo::Func3(int);\nstd::thread t1(Func1);\nstd::thread t2(Func1, 0);\nstd::thread t3(Func1, &Foo{}, 0);\n",[18,2137,2138,2143,2148,2153,2158,2163],{"__ignoreMap":16},[21,2139,2140],{"class":23,"line":24},[21,2141,2142],{},"void Func1();\n",[21,2144,2145],{"class":23,"line":30},[21,2146,2147],{},"void Func2(int);\n",[21,2149,2150],{"class":23,"line":36},[21,2151,2152],{},"void Foo::Func3(int);\n",[21,2154,2155],{"class":23,"line":42},[21,2156,2157],{},"std::thread t1(Func1);\n",[21,2159,2160],{"class":23,"line":48},[21,2161,2162],{},"std::thread t2(Func1, 0);\n",[21,2164,2165],{"class":23,"line":88},[21,2166,2167],{},"std::thread t3(Func1, &Foo{}, 0);\n",[786,2169,2170,2171],{},"等待与分离",[783,2172,2173,2176,2179],{},[786,2174,2175],{},"obj.join()",[786,2177,2178],{},"obj.detach()",[786,2180,2181],{},"obj.joinable()",[786,2183,2184,2185],{},"唯一标识符",[783,2186,2187,2190],{},[786,2188,2189],{},"obj.get_id()",[786,2191,2192],{},"std::this_thread::get_id()",[786,2194,2195],{},"其他",[280,2197,2198],{},[18,2199,2200],{},"std::thread::hardware_concurrency()",[53,2202,2203],{"id":2203},"共享数据",[1297,2205,2206],{"id":2206},"使用互斥保护数据",[2208,2209,2211],"h4",{"id":2210},"stdmutex","std::mutex",[783,2213,2214,2219,2224],{},[786,2215,2216],{},[18,2217,2218],{},"lock()",[786,2220,2221],{},[18,2222,2223],{},"try_lock()",[786,2225,2226],{},[18,2227,2228],{},"unlock()",[2230,2231,2232],"h5",{"id":2232},"基本用法",[11,2234,2236],{"className":13,"code":2235,"language":15,"meta":16,"style":16},"std::mutex m;\n\nm.lock();\n...\nm.unlock();\n",[18,2237,2238,2243,2247,2252,2257],{"__ignoreMap":16},[21,2239,2240],{"class":23,"line":24},[21,2241,2242],{},"std::mutex m;\n",[21,2244,2245],{"class":23,"line":30},[21,2246,144],{"emptyLinePlaceholder":143},[21,2248,2249],{"class":23,"line":36},[21,2250,2251],{},"m.lock();\n",[21,2253,2254],{"class":23,"line":42},[21,2255,2256],{},"...\n",[21,2258,2259],{"class":23,"line":48},[21,2260,2261],{},"m.unlock();\n",[2230,2263,2264],{"id":2264},"避免死锁",[280,2266,2267],{},"多个互斥量锁住时，在所有地方应该以相同的顺序上锁，否则可能会造成死锁",[11,2269,2271],{"className":13,"code":2270,"language":15,"meta":16,"style":16},"std::mutex m1;\nstd::mutex m2;\n\nvoid Func1()\n{\n  m1.lock();\n  m2.lock();\n  ...\n  m2.unlock();\n  m1.unlock();\n}\nvoid Func2()\n{\n  m2.lock();\n  m1.lock();\n  \u002F\u002F 应该和Func1顺序相同\n  \u002F\u002F m1.lock();\n  \u002F\u002F m2.lock();\n  ...\n  m1.unlock();\n  m2.unlock();\n}\n",[18,2272,2273,2278,2283,2287,2292,2297,2302,2307,2312,2317,2322,2326,2331,2335,2339,2343,2348,2353,2358,2362,2366,2370],{"__ignoreMap":16},[21,2274,2275],{"class":23,"line":24},[21,2276,2277],{},"std::mutex m1;\n",[21,2279,2280],{"class":23,"line":30},[21,2281,2282],{},"std::mutex m2;\n",[21,2284,2285],{"class":23,"line":36},[21,2286,144],{"emptyLinePlaceholder":143},[21,2288,2289],{"class":23,"line":42},[21,2290,2291],{},"void Func1()\n",[21,2293,2294],{"class":23,"line":48},[21,2295,2296],{},"{\n",[21,2298,2299],{"class":23,"line":88},[21,2300,2301],{},"  m1.lock();\n",[21,2303,2304],{"class":23,"line":94},[21,2305,2306],{},"  m2.lock();\n",[21,2308,2309],{"class":23,"line":100},[21,2310,2311],{},"  ...\n",[21,2313,2314],{"class":23,"line":106},[21,2315,2316],{},"  m2.unlock();\n",[21,2318,2319],{"class":23,"line":112},[21,2320,2321],{},"  m1.unlock();\n",[21,2323,2324],{"class":23,"line":176},[21,2325,115],{},[21,2327,2328],{"class":23,"line":182},[21,2329,2330],{},"void Func2()\n",[21,2332,2333],{"class":23,"line":188},[21,2334,2296],{},[21,2336,2337],{"class":23,"line":194},[21,2338,2306],{},[21,2340,2341],{"class":23,"line":200},[21,2342,2301],{},[21,2344,2345],{"class":23,"line":206},[21,2346,2347],{},"  \u002F\u002F 应该和Func1顺序相同\n",[21,2349,2350],{"class":23,"line":212},[21,2351,2352],{},"  \u002F\u002F m1.lock();\n",[21,2354,2355],{"class":23,"line":218},[21,2356,2357],{},"  \u002F\u002F m2.lock();\n",[21,2359,2360],{"class":23,"line":224},[21,2361,2311],{},[21,2363,2364],{"class":23,"line":229},[21,2365,2321],{},[21,2367,2368],{"class":23,"line":234},[21,2369,2316],{},[21,2371,2372],{"class":23,"line":240},[21,2373,115],{},[280,2375,2376],{},"或者一次锁住多个互斥量",[11,2378,2380],{"className":13,"code":2379,"language":15,"meta":16,"style":16},"std::mutex m1, m2;\nstd::lock(m1, m2);\n...\nm1.unlock();\nm2.unlock();\n",[18,2381,2382,2387,2392,2396,2401],{"__ignoreMap":16},[21,2383,2384],{"class":23,"line":24},[21,2385,2386],{},"std::mutex m1, m2;\n",[21,2388,2389],{"class":23,"line":30},[21,2390,2391],{},"std::lock(m1, m2);\n",[21,2393,2394],{"class":23,"line":36},[21,2395,2256],{},[21,2397,2398],{"class":23,"line":42},[21,2399,2400],{},"m1.unlock();\n",[21,2402,2403],{"class":23,"line":48},[21,2404,2405],{},"m2.unlock();\n",[2208,2407,2409],{"id":2408},"stdlock_guard","std::lock_guard\u003C>",[280,2411,2412,2413,1016,2415],{},"互斥量的RAII模板类，不用手动",[18,2414,2218],{},[18,2416,2228],{},[11,2418,2420],{"className":13,"code":2419,"language":15,"meta":16,"style":16},"std::mutex m;\nstd::lock_guard\u003Cstd::mutex> lg(m);\n",[18,2421,2422,2426],{"__ignoreMap":16},[21,2423,2424],{"class":23,"line":24},[21,2425,2242],{},[21,2427,2428],{"class":23,"line":30},[21,2429,2430],{},"std::lock_guard\u003Cstd::mutex> lg(m);\n",[280,2432,2433],{},"同时，可以获取并接管锁",[11,2435,2437],{"className":13,"code":2436,"language":15,"meta":16,"style":16},"std::mutex m1, m2;\nstd::lock(m1, m2);\n\nstd::lock_guard\u003Cstd::mutex> lg1(m1, std::adopt_lock);\nstd::lock_guard\u003Cstd::mutex> lg2(m2, std::adopt_lock);\n",[18,2438,2439,2443,2447,2451,2456],{"__ignoreMap":16},[21,2440,2441],{"class":23,"line":24},[21,2442,2386],{},[21,2444,2445],{"class":23,"line":30},[21,2446,2391],{},[21,2448,2449],{"class":23,"line":36},[21,2450,144],{"emptyLinePlaceholder":143},[21,2452,2453],{"class":23,"line":42},[21,2454,2455],{},"std::lock_guard\u003Cstd::mutex> lg1(m1, std::adopt_lock);\n",[21,2457,2458],{"class":23,"line":48},[21,2459,2460],{},"std::lock_guard\u003Cstd::mutex> lg2(m2, std::adopt_lock);\n",[2208,2462,2464],{"id":2463},"stdscoped_lock","std::scoped_lock\u003C>",[280,2466,2467,2468,2470],{},"C++17中的新的RAII模板，与",[18,2469,2409],{},"相同，不过可以接受多个互斥量，对其上锁",[11,2472,2474],{"className":13,"code":2473,"language":15,"meta":16,"style":16},"std::mutex m1, m2;\nstd::scoped_lock\u003Cstd::mutex> sl(m1, m2);\n",[18,2475,2476,2480],{"__ignoreMap":16},[21,2477,2478],{"class":23,"line":24},[21,2479,2386],{},[21,2481,2482],{"class":23,"line":30},[21,2483,2484],{},"std::scoped_lock\u003Cstd::mutex> sl(m1, m2);\n",[2208,2486,2487],{"id":2487},"层级锁的实现",[11,2489,2491],{"className":13,"code":2490,"language":15,"meta":16,"style":16},"#include \u003Cclimits>\n#include \u003Cmutex>\n#include \u003Cstdexcept>\n\nclass hierarchical_mutex {\npublic:\n  hierarchical_mutex(unsigned long value)\n      : hierarchy_value(value), previous_hierarchy_value(0) {}\n  void lock() {\n    check_hieratchy(); \u002F\u002F 当前互斥量的层级与当前线程的层级相比较\n    internal_mutex.lock();\n    update_hierarchy(); \u002F\u002F 将线程层级与当前层级同步\n  }\n  void unlock() {\n    if (this_hierarchy_value != hierarchy_value)\n      throw std::logic_error(\"mutex hierarchy violated\");\n    this_hierarchy_value = previous_hierarchy_value;\n    internal_mutex.unlock();\n  }\n  bool try_lock() {\n    check_hieratchy();\n    if (!internal_mutex.try_lock())\n      return false;\n    update_hierarchy();\n    return true;\n  }\n\nprivate:\n  void check_hieratchy() {\n    if (hierarchy_value >= this_hierarchy_value)\n      throw std::logic_error(\"mutex hierarchy violated\");\n  }\n  void update_hierarchy() {\n    previous_hierarchy_value = this_hierarchy_value;\n    this_hierarchy_value = hierarchy_value;\n  }\n\nprivate:\n  std::mutex internal_mutex;\n  const unsigned long hierarchy_value;    \u002F\u002F 当前锁的层级\n  unsigned long previous_hierarchy_value; \u002F\u002F 当前锁的上一层的层级\n  static thread_local unsigned long this_hierarchy_value; \u002F\u002F 线程的层级\n};\n\nthread_local unsigned long hierarchical_mutex::this_hierarchy_value{ULONG_MAX};\n",[18,2492,2493,2498,2503,2508,2512,2517,2521,2526,2531,2536,2541,2546,2551,2555,2560,2565,2570,2575,2580,2584,2589,2594,2599,2604,2609,2614,2618,2622,2626,2631,2636,2640,2644,2649,2654,2659,2663,2667,2671,2676,2681,2686,2691,2695,2699],{"__ignoreMap":16},[21,2494,2495],{"class":23,"line":24},[21,2496,2497],{},"#include \u003Cclimits>\n",[21,2499,2500],{"class":23,"line":30},[21,2501,2502],{},"#include \u003Cmutex>\n",[21,2504,2505],{"class":23,"line":36},[21,2506,2507],{},"#include \u003Cstdexcept>\n",[21,2509,2510],{"class":23,"line":42},[21,2511,144],{"emptyLinePlaceholder":143},[21,2513,2514],{"class":23,"line":48},[21,2515,2516],{},"class hierarchical_mutex {\n",[21,2518,2519],{"class":23,"line":88},[21,2520,1347],{},[21,2522,2523],{"class":23,"line":94},[21,2524,2525],{},"  hierarchical_mutex(unsigned long value)\n",[21,2527,2528],{"class":23,"line":100},[21,2529,2530],{},"      : hierarchy_value(value), previous_hierarchy_value(0) {}\n",[21,2532,2533],{"class":23,"line":106},[21,2534,2535],{},"  void lock() {\n",[21,2537,2538],{"class":23,"line":112},[21,2539,2540],{},"    check_hieratchy(); \u002F\u002F 当前互斥量的层级与当前线程的层级相比较\n",[21,2542,2543],{"class":23,"line":176},[21,2544,2545],{},"    internal_mutex.lock();\n",[21,2547,2548],{"class":23,"line":182},[21,2549,2550],{},"    update_hierarchy(); \u002F\u002F 将线程层级与当前层级同步\n",[21,2552,2553],{"class":23,"line":188},[21,2554,103],{},[21,2556,2557],{"class":23,"line":194},[21,2558,2559],{},"  void unlock() {\n",[21,2561,2562],{"class":23,"line":200},[21,2563,2564],{},"    if (this_hierarchy_value != hierarchy_value)\n",[21,2566,2567],{"class":23,"line":206},[21,2568,2569],{},"      throw std::logic_error(\"mutex hierarchy violated\");\n",[21,2571,2572],{"class":23,"line":212},[21,2573,2574],{},"    this_hierarchy_value = previous_hierarchy_value;\n",[21,2576,2577],{"class":23,"line":218},[21,2578,2579],{},"    internal_mutex.unlock();\n",[21,2581,2582],{"class":23,"line":224},[21,2583,103],{},[21,2585,2586],{"class":23,"line":229},[21,2587,2588],{},"  bool try_lock() {\n",[21,2590,2591],{"class":23,"line":234},[21,2592,2593],{},"    check_hieratchy();\n",[21,2595,2596],{"class":23,"line":240},[21,2597,2598],{},"    if (!internal_mutex.try_lock())\n",[21,2600,2601],{"class":23,"line":246},[21,2602,2603],{},"      return false;\n",[21,2605,2606],{"class":23,"line":252},[21,2607,2608],{},"    update_hierarchy();\n",[21,2610,2611],{"class":23,"line":1680},[21,2612,2613],{},"    return true;\n",[21,2615,2616],{"class":23,"line":1686},[21,2617,103],{},[21,2619,2620],{"class":23,"line":1692},[21,2621,144],{"emptyLinePlaceholder":143},[21,2623,2624],{"class":23,"line":1697},[21,2625,1316],{},[21,2627,2628],{"class":23,"line":1703},[21,2629,2630],{},"  void check_hieratchy() {\n",[21,2632,2633],{"class":23,"line":1709},[21,2634,2635],{},"    if (hierarchy_value >= this_hierarchy_value)\n",[21,2637,2638],{"class":23,"line":1714},[21,2639,2569],{},[21,2641,2642],{"class":23,"line":1720},[21,2643,103],{},[21,2645,2646],{"class":23,"line":1726},[21,2647,2648],{},"  void update_hierarchy() {\n",[21,2650,2651],{"class":23,"line":1732},[21,2652,2653],{},"    previous_hierarchy_value = this_hierarchy_value;\n",[21,2655,2656],{"class":23,"line":1738},[21,2657,2658],{},"    this_hierarchy_value = hierarchy_value;\n",[21,2660,2661],{"class":23,"line":1743},[21,2662,103],{},[21,2664,2665],{"class":23,"line":1749},[21,2666,144],{"emptyLinePlaceholder":143},[21,2668,2669],{"class":23,"line":1755},[21,2670,1316],{},[21,2672,2673],{"class":23,"line":1761},[21,2674,2675],{},"  std::mutex internal_mutex;\n",[21,2677,2678],{"class":23,"line":1767},[21,2679,2680],{},"  const unsigned long hierarchy_value;    \u002F\u002F 当前锁的层级\n",[21,2682,2683],{"class":23,"line":1773},[21,2684,2685],{},"  unsigned long previous_hierarchy_value; \u002F\u002F 当前锁的上一层的层级\n",[21,2687,2688],{"class":23,"line":1778},[21,2689,2690],{},"  static thread_local unsigned long this_hierarchy_value; \u002F\u002F 线程的层级\n",[21,2692,2693],{"class":23,"line":1784},[21,2694,51],{},[21,2696,2697],{"class":23,"line":1790},[21,2698,144],{"emptyLinePlaceholder":143},[21,2700,2701],{"class":23,"line":1796},[21,2702,2703],{},"thread_local unsigned long hierarchical_mutex::this_hierarchy_value{ULONG_MAX};\n",[2208,2705,2707],{"id":2706},"stdunique_lock","std::unique_lock\u003C>",[280,2709,1016,2710,2712,2713,2715],{},[18,2711,2409],{},"相似，不过前者只提供了析构接口，但",[18,2714,2707],{},"可手动上锁，解锁更为灵活",[11,2717,2719],{"className":13,"code":2718,"language":15,"meta":16,"style":16},"std::mutex m1, m2;\nstd::unique_lock\u003Cstd::mutex> ul1(m1, std::defer_lock); \u002F\u002F std::defer_lock表示，不对mutex上锁\nstd::unique_lock\u003Cstd::mutex> ul2(m2) \u002F\u002F m2已自动上锁\nul1.lock(); \u002F\u002F 手动上锁\n",[18,2720,2721,2725,2730,2735],{"__ignoreMap":16},[21,2722,2723],{"class":23,"line":24},[21,2724,2386],{},[21,2726,2727],{"class":23,"line":30},[21,2728,2729],{},"std::unique_lock\u003Cstd::mutex> ul1(m1, std::defer_lock); \u002F\u002F std::defer_lock表示，不对mutex上锁\n",[21,2731,2732],{"class":23,"line":36},[21,2733,2734],{},"std::unique_lock\u003Cstd::mutex> ul2(m2) \u002F\u002F m2已自动上锁\n",[21,2736,2737],{"class":23,"line":42},[21,2738,2739],{},"ul1.lock(); \u002F\u002F 手动上锁\n",[1297,2741,2742],{"id":2742},"保护数据的替代方案",[2208,2744,2745],{"id":2745},"保护共享数据的初始化过程",[783,2747,2748],{},[786,2749,2750],{},"使用互斥量",[11,2752,2754],{"className":13,"code":2753,"language":15,"meta":16,"style":16},"std::mutex m;\nstd::shared_ptr\u003CWidget> sp;\nvoid Foo()\n{\n  std::unique_lock\u003Cstd::mutex> ul(m);\n  if(!sp)\n  {\n    sp.reset(new Widget);\n  }\n  ul.unlock();\n  sp->do_something();\n}\n",[18,2755,2756,2760,2765,2770,2774,2779,2784,2789,2794,2798,2803,2808],{"__ignoreMap":16},[21,2757,2758],{"class":23,"line":24},[21,2759,2242],{},[21,2761,2762],{"class":23,"line":30},[21,2763,2764],{},"std::shared_ptr\u003CWidget> sp;\n",[21,2766,2767],{"class":23,"line":36},[21,2768,2769],{},"void Foo()\n",[21,2771,2772],{"class":23,"line":42},[21,2773,2296],{},[21,2775,2776],{"class":23,"line":48},[21,2777,2778],{},"  std::unique_lock\u003Cstd::mutex> ul(m);\n",[21,2780,2781],{"class":23,"line":88},[21,2782,2783],{},"  if(!sp)\n",[21,2785,2786],{"class":23,"line":94},[21,2787,2788],{},"  {\n",[21,2790,2791],{"class":23,"line":100},[21,2792,2793],{},"    sp.reset(new Widget);\n",[21,2795,2796],{"class":23,"line":106},[21,2797,103],{},[21,2799,2800],{"class":23,"line":112},[21,2801,2802],{},"  ul.unlock();\n",[21,2804,2805],{"class":23,"line":176},[21,2806,2807],{},"  sp->do_something();\n",[21,2809,2810],{"class":23,"line":182},[21,2811,115],{},[783,2813,2814],{},[786,2815,2816,2819,2820],{},[18,2817,2818],{},"std::once_flag","和",[18,2821,2822],{},"std::call_once",[280,2824,2825],{},"比用互斥量消耗的资源更少",[11,2827,2829],{"className":13,"code":2828,"language":15,"meta":16,"style":16},"std::shared_ptr\u003CWidget> sp;\nstd::once_flag flag;\nvoid Init()\n{\n  sp.reset(new Widget);\n}\nvoid Foo\n{\n  std::call_once(flag, Init);\n  sp->do_something();\n}\n\n\u002F\u002F Init()只调用了一次\nstd::thread t1(Foo);\nstd::thread t2(Foo);\nt1.join();\nt2.join();\n",[18,2830,2831,2835,2840,2845,2849,2854,2858,2863,2867,2872,2876,2880,2884,2889,2894,2899,2904],{"__ignoreMap":16},[21,2832,2833],{"class":23,"line":24},[21,2834,2764],{},[21,2836,2837],{"class":23,"line":30},[21,2838,2839],{},"std::once_flag flag;\n",[21,2841,2842],{"class":23,"line":36},[21,2843,2844],{},"void Init()\n",[21,2846,2847],{"class":23,"line":42},[21,2848,2296],{},[21,2850,2851],{"class":23,"line":48},[21,2852,2853],{},"  sp.reset(new Widget);\n",[21,2855,2856],{"class":23,"line":88},[21,2857,115],{},[21,2859,2860],{"class":23,"line":94},[21,2861,2862],{},"void Foo\n",[21,2864,2865],{"class":23,"line":100},[21,2866,2296],{},[21,2868,2869],{"class":23,"line":106},[21,2870,2871],{},"  std::call_once(flag, Init);\n",[21,2873,2874],{"class":23,"line":112},[21,2875,2807],{},[21,2877,2878],{"class":23,"line":176},[21,2879,115],{},[21,2881,2882],{"class":23,"line":182},[21,2883,144],{"emptyLinePlaceholder":143},[21,2885,2886],{"class":23,"line":188},[21,2887,2888],{},"\u002F\u002F Init()只调用了一次\n",[21,2890,2891],{"class":23,"line":194},[21,2892,2893],{},"std::thread t1(Foo);\n",[21,2895,2896],{"class":23,"line":200},[21,2897,2898],{},"std::thread t2(Foo);\n",[21,2900,2901],{"class":23,"line":206},[21,2902,2903],{},"t1.join();\n",[21,2905,2906],{"class":23,"line":212},[21,2907,2908],{},"t2.join();\n",[2208,2910,2911],{"id":2911},"保护不常更新的数据结构",[280,2913,2914,2915,2819,2918,2921,2922,2924,2925,2927,2928,2930],{},"C++17提供",[18,2916,2917],{},"std::shared_mutex",[18,2919,2920],{},"std::shared_timed_mutex","，C++14只提供",[18,2923,2920],{},",\n而C++11并未提供。",[18,2926,2920],{},"更多操作方式，",[18,2929,2917],{},"有更高的性能",[11,2932,2934],{"className":13,"code":2933,"language":15,"meta":16,"style":16},"std::shared_mutex sm;\n\nvoid Foo()\n{\n  \u002F\u002F 其他线程加锁时，不会阻塞\n  std::shared_lock\u003Cstd::shared_mutex> sl(sm);\n  ...\n}\nvoid Foo2()\n{\n  \u002F\u002F 其他线程尝试加锁时，会阻塞\n  std::lock_guard\u003Cstd::shared_mutex> lg(sm);\n  ...\n}\n",[18,2935,2936,2941,2945,2949,2953,2958,2963,2967,2971,2976,2980,2985,2990,2994],{"__ignoreMap":16},[21,2937,2938],{"class":23,"line":24},[21,2939,2940],{},"std::shared_mutex sm;\n",[21,2942,2943],{"class":23,"line":30},[21,2944,144],{"emptyLinePlaceholder":143},[21,2946,2947],{"class":23,"line":36},[21,2948,2769],{},[21,2950,2951],{"class":23,"line":42},[21,2952,2296],{},[21,2954,2955],{"class":23,"line":48},[21,2956,2957],{},"  \u002F\u002F 其他线程加锁时，不会阻塞\n",[21,2959,2960],{"class":23,"line":88},[21,2961,2962],{},"  std::shared_lock\u003Cstd::shared_mutex> sl(sm);\n",[21,2964,2965],{"class":23,"line":94},[21,2966,2311],{},[21,2968,2969],{"class":23,"line":100},[21,2970,115],{},[21,2972,2973],{"class":23,"line":106},[21,2974,2975],{},"void Foo2()\n",[21,2977,2978],{"class":23,"line":112},[21,2979,2296],{},[21,2981,2982],{"class":23,"line":176},[21,2983,2984],{},"  \u002F\u002F 其他线程尝试加锁时，会阻塞\n",[21,2986,2987],{"class":23,"line":182},[21,2988,2989],{},"  std::lock_guard\u003Cstd::shared_mutex> lg(sm);\n",[21,2991,2992],{"class":23,"line":188},[21,2993,2311],{},[21,2995,2996],{"class":23,"line":194},[21,2997,115],{},[280,2999,3000,3001,3004,3005,3007],{},"并行访问数据时，使用",[18,3002,3003],{},"std::shared_lock\u003C>","上锁，所有线程都可对数据进行访问，而修改数据时，\n使用",[18,3006,2409],{},"上锁，只有一个线程可以进行修改",[280,3009,3010,3011,3013,3014,3016],{},"限制：当有线程有共享锁(",[18,3012,3003],{},"上锁)时，独占锁(",[18,3015,2409],{},")会阻塞，\n而当有线程有独占锁时，其他独占和所有共享锁都会阻塞，直到独占锁解锁",[2208,3018,3019],{"id":3019},"嵌套锁",[280,3021,3022,3023,3025,3026,3029],{},"一个线程中",[18,3024,2211],{},"已经上锁后，再次上锁是错误的。而",[18,3027,3028],{},"std::recursive_mutex","，在同一线程可多次上锁",[280,3031,3032,3033,3035,3036,3038,3039,3042,3043,3046],{},"只不过，如果调用",[18,3034,2218],{},"三次，就需要",[18,3037,2228],{},"三次，不过可以使用",[18,3040,3041],{},"std::lock_guard\u003Cstd::recursive_mutex>","，或者\n",[18,3044,3045],{},"std::unique_lock\u003Cstd::recursive_mutex>","来管理",[53,3048,3049],{"id":3049},"同步操作",[1297,3051,3052],{"id":3052},"条件变量",[280,3054,3055,3058,3059],{},[18,3056,3057],{},"std::condition_variable","或",[18,3060,3061],{},"std::condition_variable_any",[11,3063,3065],{"className":13,"code":3064,"language":15,"meta":16,"style":16},"static std::condition_variable cond;\nstatic bool flag = false;\nstatic std::mutex m1;\n\nvoid Prepare()\n{\n  std::lock_guard\u003Cstd::mutex> lg(m1);\n  flag = true;\n  cond.notify_one();\n}\n\nvoid Process()\n{\n  std::unique_lock\u003Cstd::mutex> ul(m1);\n  cond.wait(ul, [](){return flag;});\n}\n",[18,3066,3067,3072,3077,3082,3086,3091,3095,3100,3105,3110,3114,3118,3123,3127,3132,3137],{"__ignoreMap":16},[21,3068,3069],{"class":23,"line":24},[21,3070,3071],{},"static std::condition_variable cond;\n",[21,3073,3074],{"class":23,"line":30},[21,3075,3076],{},"static bool flag = false;\n",[21,3078,3079],{"class":23,"line":36},[21,3080,3081],{},"static std::mutex m1;\n",[21,3083,3084],{"class":23,"line":42},[21,3085,144],{"emptyLinePlaceholder":143},[21,3087,3088],{"class":23,"line":48},[21,3089,3090],{},"void Prepare()\n",[21,3092,3093],{"class":23,"line":88},[21,3094,2296],{},[21,3096,3097],{"class":23,"line":94},[21,3098,3099],{},"  std::lock_guard\u003Cstd::mutex> lg(m1);\n",[21,3101,3102],{"class":23,"line":100},[21,3103,3104],{},"  flag = true;\n",[21,3106,3107],{"class":23,"line":106},[21,3108,3109],{},"  cond.notify_one();\n",[21,3111,3112],{"class":23,"line":112},[21,3113,115],{},[21,3115,3116],{"class":23,"line":176},[21,3117,144],{"emptyLinePlaceholder":143},[21,3119,3120],{"class":23,"line":182},[21,3121,3122],{},"void Process()\n",[21,3124,3125],{"class":23,"line":188},[21,3126,2296],{},[21,3128,3129],{"class":23,"line":194},[21,3130,3131],{},"  std::unique_lock\u003Cstd::mutex> ul(m1);\n",[21,3133,3134],{"class":23,"line":200},[21,3135,3136],{},"  cond.wait(ul, [](){return flag;});\n",[21,3138,3139],{"class":23,"line":206},[21,3140,115],{},[280,3142,3143,3146,3147,3150,3151,3153,3154,3157],{},[18,3144,3145],{},"Process()","中，如果",[18,3148,3149],{},"cond.wait()","的第二个参数为false，会解锁ul，并令线程阻塞，等待",[18,3152,3145],{},"中\n的",[18,3155,3156],{},"cond.notify_one()","唤醒，唤醒后ul上锁，重新判断第二个参数的值，如果仍为false，就继续阻塞",[11,3159,3161],{"className":13,"code":3160,"language":15,"meta":16,"style":16},"template\u003Ctypename Predicate>\nvoid minimal_wait(std::unique_lock\u003Cstd::mutex>& lk, Predicate pred)\n{\n  while(!pred())\n  {\n    lk.unlock();\n    lk.lock();\n  }\n}\n",[18,3162,3163,3168,3173,3177,3182,3186,3191,3196,3200],{"__ignoreMap":16},[21,3164,3165],{"class":23,"line":24},[21,3166,3167],{},"template\u003Ctypename Predicate>\n",[21,3169,3170],{"class":23,"line":30},[21,3171,3172],{},"void minimal_wait(std::unique_lock\u003Cstd::mutex>& lk, Predicate pred)\n",[21,3174,3175],{"class":23,"line":36},[21,3176,2296],{},[21,3178,3179],{"class":23,"line":42},[21,3180,3181],{},"  while(!pred())\n",[21,3183,3184],{"class":23,"line":48},[21,3185,2788],{},[21,3187,3188],{"class":23,"line":88},[21,3189,3190],{},"    lk.unlock();\n",[21,3192,3193],{"class":23,"line":94},[21,3194,3195],{},"    lk.lock();\n",[21,3197,3198],{"class":23,"line":100},[21,3199,103],{},[21,3201,3202],{"class":23,"line":106},[21,3203,115],{},[1297,3205,3206],{"id":3206},"future",[280,3208,3209,3212],{},[18,3210,3211],{},"std::future\u003C>","只移动，所有权在不同实例中互相传递",[2208,3214,3216],{"id":3215},"stdasync","std::async",[280,3218,3219,3220,3223,3224,3226,3227,3058,3230,3233],{},"启动一个异步任务与",[18,3221,3222],{},"std::tread","相似，返回一个",[18,3225,3211],{},"对象。当使用",[18,3228,3229],{},"get()",[18,3231,3232],{},"wait()","函数时，会阻塞线程，直到future就绪即std::async\n完成为止",[11,3235,3237],{"className":13,"code":3236,"language":15,"meta":16,"style":16},"int Foo();\nstd::future\u003Cint> result = std::async(Foo);\nstd::cout \u003C\u003C result.get() \u003C\u003C std::endl;\n",[18,3238,3239,3244,3249],{"__ignoreMap":16},[21,3240,3241],{"class":23,"line":24},[21,3242,3243],{},"int Foo();\n",[21,3245,3246],{"class":23,"line":30},[21,3247,3248],{},"std::future\u003Cint> result = std::async(Foo);\n",[21,3250,3251],{"class":23,"line":36},[21,3252,3253],{},"std::cout \u003C\u003C result.get() \u003C\u003C std::endl;\n",[280,3255,3256,3258,3259,1016,3262],{},[18,3257,3216],{},"的第一个参数有",[18,3260,3261],{},"std::launch::deferred",[18,3263,3264],{},"std::launch::async",[11,3266,3268],{"className":13,"code":3267,"language":15,"meta":16,"style":16},"std::future\u003Cint> result1 = std::async(std::launch::deferred, Foo);  \u002F\u002F 在当前线程同步运行，直到get或wait时，才调用函数\nstd::future\u003Cint> result2 = std::async(std::launch::async, Foo);     \u002F\u002F 创建新线程异步运行，表示函数必须在独立线程上执行\nstd::future\u003Cint> result3 = std::async(std::launc::deferred | std::launch::async, Foo); \u002F\u002F 由系统决定\n\nresult1.wait();\nresult3.wait();\n",[18,3269,3270,3275,3280,3285,3289,3294],{"__ignoreMap":16},[21,3271,3272],{"class":23,"line":24},[21,3273,3274],{},"std::future\u003Cint> result1 = std::async(std::launch::deferred, Foo);  \u002F\u002F 在当前线程同步运行，直到get或wait时，才调用函数\n",[21,3276,3277],{"class":23,"line":30},[21,3278,3279],{},"std::future\u003Cint> result2 = std::async(std::launch::async, Foo);     \u002F\u002F 创建新线程异步运行，表示函数必须在独立线程上执行\n",[21,3281,3282],{"class":23,"line":36},[21,3283,3284],{},"std::future\u003Cint> result3 = std::async(std::launc::deferred | std::launch::async, Foo); \u002F\u002F 由系统决定\n",[21,3286,3287],{"class":23,"line":42},[21,3288,144],{"emptyLinePlaceholder":143},[21,3290,3291],{"class":23,"line":48},[21,3292,3293],{},"result1.wait();\n",[21,3295,3296],{"class":23,"line":88},[21,3297,3298],{},"result3.wait();\n",[280,3300,3301,3303],{},[18,3302,3216],{},"析构时，会阻塞线程，相当于同步执行",[11,3305,3307],{"className":13,"code":3306,"language":15,"meta":16,"style":16},"\u002F\u002F 临时变量，用完后会析构，因此do_something会等到异步任务执行完才会执行\nstd::async([]{std::cout \u003C\u003C \"hello\" \u003C\u003C std::endl;});\ndo_something();\n",[18,3308,3309,3314,3319],{"__ignoreMap":16},[21,3310,3311],{"class":23,"line":24},[21,3312,3313],{},"\u002F\u002F 临时变量，用完后会析构，因此do_something会等到异步任务执行完才会执行\n",[21,3315,3316],{"class":23,"line":30},[21,3317,3318],{},"std::async([]{std::cout \u003C\u003C \"hello\" \u003C\u003C std::endl;});\n",[21,3320,3321],{"class":23,"line":36},[21,3322,3323],{},"do_something();\n",[2208,3325,3327],{"id":3326},"stdpackaged_task","std::packaged_task\u003C>",[280,3329,3330,3331,3334],{},"只是将可调用对象与future绑定，调用",[18,3332,3333],{},"std::packaged_task","对象会调用绑定的可调用对象",[11,3336,3338],{"className":13,"code":3337,"language":15,"meta":16,"style":16},"int Foo();\nstd::packaged_task\u003Cint()> task {Foo};\nstd::future\u003Cint> f = task.get_future();\ntask(); \u002F\u002F 相当于执行Foo()，运行结束后f状态为就绪，即之后f.get()或f.wait()不会阻塞\nstd::cout \u003C\u003C f.get() \u003C\u003C std::endl;\n",[18,3339,3340,3344,3349,3354,3359],{"__ignoreMap":16},[21,3341,3342],{"class":23,"line":24},[21,3343,3243],{},[21,3345,3346],{"class":23,"line":30},[21,3347,3348],{},"std::packaged_task\u003Cint()> task {Foo};\n",[21,3350,3351],{"class":23,"line":36},[21,3352,3353],{},"std::future\u003Cint> f = task.get_future();\n",[21,3355,3356],{"class":23,"line":42},[21,3357,3358],{},"task(); \u002F\u002F 相当于执行Foo()，运行结束后f状态为就绪，即之后f.get()或f.wait()不会阻塞\n",[21,3360,3361],{"class":23,"line":48},[21,3362,3363],{},"std::cout \u003C\u003C f.get() \u003C\u003C std::endl;\n",[280,3365,3366],{},"可用于线程当中",[11,3368,3370],{"className":13,"code":3369,"language":15,"meta":16,"style":16},"int Foo();\nstd::packaged_task\u003Cint()> task {Foo};\nstd::future\u003Cint> f = task.get_future();\n\nstd::thread t(task);\n...\nf.wait(); \u002F\u002F 阻塞线程，直到f就绪\n...\nt.join();\n",[18,3371,3372,3376,3380,3384,3388,3393,3397,3402,3406],{"__ignoreMap":16},[21,3373,3374],{"class":23,"line":24},[21,3375,3243],{},[21,3377,3378],{"class":23,"line":30},[21,3379,3348],{},[21,3381,3382],{"class":23,"line":36},[21,3383,3353],{},[21,3385,3386],{"class":23,"line":42},[21,3387,144],{"emptyLinePlaceholder":143},[21,3389,3390],{"class":23,"line":48},[21,3391,3392],{},"std::thread t(task);\n",[21,3394,3395],{"class":23,"line":88},[21,3396,2256],{},[21,3398,3399],{"class":23,"line":94},[21,3400,3401],{},"f.wait(); \u002F\u002F 阻塞线程，直到f就绪\n",[21,3403,3404],{"class":23,"line":100},[21,3405,2256],{},[21,3407,3408],{"class":23,"line":106},[21,3409,3410],{},"t.join();\n",[2208,3412,3414],{"id":3413},"stdpromise","std::promise\u003C>",[280,3416,3417],{},"可以将一个值传递给一个新线程",[11,3419,3421],{"className":13,"code":3420,"language":15,"meta":16,"style":16},"auto task = [](std::future\u003Cint> f) {\n    std::cout \u003C\u003C f.get() \u003C\u003C std::flush; \u002F\u002F 阻塞，直到 p.set_value() 被调用\n};\n\nstd::promise\u003Cint> p;\nstd::thread t{ task, p.get_future() };\n\nstd::this_thread::sleep_for(std::chrono::seconds(5));\np.set_value(5);\n\nt.join();\n",[18,3422,3423,3428,3433,3437,3441,3446,3451,3455,3460,3465,3469],{"__ignoreMap":16},[21,3424,3425],{"class":23,"line":24},[21,3426,3427],{},"auto task = [](std::future\u003Cint> f) {\n",[21,3429,3430],{"class":23,"line":30},[21,3431,3432],{},"    std::cout \u003C\u003C f.get() \u003C\u003C std::flush; \u002F\u002F 阻塞，直到 p.set_value() 被调用\n",[21,3434,3435],{"class":23,"line":36},[21,3436,51],{},[21,3438,3439],{"class":23,"line":42},[21,3440,144],{"emptyLinePlaceholder":143},[21,3442,3443],{"class":23,"line":48},[21,3444,3445],{},"std::promise\u003Cint> p;\n",[21,3447,3448],{"class":23,"line":88},[21,3449,3450],{},"std::thread t{ task, p.get_future() };\n",[21,3452,3453],{"class":23,"line":94},[21,3454,144],{"emptyLinePlaceholder":143},[21,3456,3457],{"class":23,"line":100},[21,3458,3459],{},"std::this_thread::sleep_for(std::chrono::seconds(5));\n",[21,3461,3462],{"class":23,"line":106},[21,3463,3464],{},"p.set_value(5);\n",[21,3466,3467],{"class":23,"line":112},[21,3468,144],{"emptyLinePlaceholder":143},[21,3470,3471],{"class":23,"line":176},[21,3472,3410],{},[2208,3474,3476],{"id":3475},"stdshared_future","std::shared_future\u003C>",[280,3478,3479,3480,3483],{},"构造",[18,3481,3482],{},"shared_future","的方法",[11,3485,3487],{"className":13,"code":3486,"language":15,"meta":16,"style":16},"std::promise\u003Cint> p;\nstd::future f(p.get_future());\nstd::shared_future\u003Cint> sf(std::move(f));\n",[18,3488,3489,3493,3498],{"__ignoreMap":16},[21,3490,3491],{"class":23,"line":24},[21,3492,3445],{},[21,3494,3495],{"class":23,"line":30},[21,3496,3497],{},"std::future f(p.get_future());\n",[21,3499,3500],{"class":23,"line":36},[21,3501,3502],{},"std::shared_future\u003Cint> sf(std::move(f));\n",[11,3504,3506],{"className":13,"code":3505,"language":15,"meta":16,"style":16},"std::promise\u003Cint> p;\nstd::shared_future\u003Cint> sf(p.get_future());\n",[18,3507,3508,3512],{"__ignoreMap":16},[21,3509,3510],{"class":23,"line":24},[21,3511,3445],{},[21,3513,3514],{"class":23,"line":30},[21,3515,3516],{},"std::shared_future\u003Cint> sf(p.get_future());\n",[11,3518,3520],{"className":13,"code":3519,"language":15,"meta":16,"style":16},"std::promise\u003Cint> p;\nauto sf = p.get_future().share();\n",[18,3521,3522,3526],{"__ignoreMap":16},[21,3523,3524],{"class":23,"line":24},[21,3525,3445],{},[21,3527,3528],{"class":23,"line":30},[21,3529,3530],{},"auto sf = p.get_future().share();\n",[1297,3532,3533],{"id":3533},"锁存器和栅栏",[2208,3535,3537],{"id":3536},"stdlatch","std::latch",[783,3539,3540,3546,3555],{},[786,3541,3542,3543],{},"计数器作为构造函数的唯一参数",[18,3544,3545],{},"std::latch la(3)",[786,3547,3548,1016,3551,3554],{},[18,3549,3550],{},"count_down()",[18,3552,3553],{},"arrive_and_wait()"," 令计数器减一，而后者会阻塞线程直到计数器为0",[786,3556,3557,3559],{},[18,3558,3232],{},"阻塞线程，直到计数器为0",[2208,3561,3563],{"id":3562},"stdbarrier","std::barrier",[783,3565,3566,3599,3604,3619],{},[786,3567,3568,3569],{},"计数器作为第一个参数，可调用对象(必须是noexcept)作为第二个参数(可选)，在barrier就绪(计数器为0)时，\n其中一个线程调用。同时，返回值指定下一次的计数",[11,3570,3572],{"className":13,"code":3571,"language":15,"meta":16,"style":16},"std::barrier b1(3);\nstd::barrier b2(3, []() noexcept {\n  std::cout \u003C\u003C std::this_thread::get_id() \u003C\u003C std::endl;\n  return -1; \u002F\u002F -1表示下一次计数不变\n});\n",[18,3573,3574,3579,3584,3589,3594],{"__ignoreMap":16},[21,3575,3576],{"class":23,"line":24},[21,3577,3578],{},"std::barrier b1(3);\n",[21,3580,3581],{"class":23,"line":30},[21,3582,3583],{},"std::barrier b2(3, []() noexcept {\n",[21,3585,3586],{"class":23,"line":36},[21,3587,3588],{},"  std::cout \u003C\u003C std::this_thread::get_id() \u003C\u003C std::endl;\n",[21,3590,3591],{"class":23,"line":42},[21,3592,3593],{},"  return -1; \u002F\u002F -1表示下一次计数不变\n",[21,3595,3596],{"class":23,"line":48},[21,3597,3598],{},"});\n",[786,3600,3601,3603],{},[18,3602,3553],{},"令计数器减一，并且阻塞线程",[786,3605,3606,1016,3609,3611,3612,1016,3615,3618],{},[18,3607,3608],{},"arrive()",[18,3610,3232],{},"，",[18,3613,3614],{},"b.arrive(b.wait())",[18,3616,3617],{},"b.arrive_and_wait()","等价",[786,3620,3621,3624,3625],{},[18,3622,3623],{},"arrive_and_drop()","，当前计数与下次barrier计数减一",[1183,3626,3627],{},[280,3628,3629],{},"std::barrier可多次使用",[53,3631,3632],{"id":3632},"内存模型和原子操作",[1297,3634,3635],{"id":3635},"atomic",[280,3637,3638,3639,3642,3643,3646,3647,3649],{},"atomic的操作都是原子的，有的是使用原子指令，有的使用互斥锁模拟原子操作，使用",[18,3640,3641],{},"x.is_lock_free()","\n函数查询原子指令(",[18,3644,3645],{},"is_lock_free()","返回true)还是使用锁(",[18,3648,3645],{},"返回false)",[280,3651,3652,3653,3656],{},"同时C++17中，所有原子类型有一个static constexpr成员变量",[18,3654,3655],{},"X::is_always_lock_free","，值为true\n表示无锁，false表示有锁",[2208,3658,3660],{"id":3659},"stdatomic_flag","std::atomic_flag",[1183,3662,3663],{},[280,3664,3665],{},"唯一确保为无锁的类型",[280,3667,3668,3670,3671,3674],{},[18,3669,3660],{},"对象必须被",[18,3672,3673],{},"ATOMIC_FLAG_INIT","初始化。初始化标志位为清除状态即false",[11,3676,3678],{"className":13,"code":3677,"language":15,"meta":16,"style":16},"class spinlock_mutex\n{\nprivate:\n  std::atomic_flag flag;\npublic:\n  spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}\n  lock()\n  {\n    \u002F\u002F test_and_set()设置标志位为true，并返回旧的标志位\n    \u002F\u002F 第一次调用或着clear()后，才会返回false，从而调出循环\n    while(flag.test_and_set(std::memory_order_acquire));\n  }\n  unlock()\n  {\n    \u002F\u002F 设置标志位为false\n    flag.clear(std::memory_order_release);\n  }\n};\n",[18,3679,3680,3685,3689,3693,3698,3702,3707,3712,3716,3721,3726,3731,3735,3740,3744,3749,3754,3758],{"__ignoreMap":16},[21,3681,3682],{"class":23,"line":24},[21,3683,3684],{},"class spinlock_mutex\n",[21,3686,3687],{"class":23,"line":30},[21,3688,2296],{},[21,3690,3691],{"class":23,"line":36},[21,3692,1316],{},[21,3694,3695],{"class":23,"line":42},[21,3696,3697],{},"  std::atomic_flag flag;\n",[21,3699,3700],{"class":23,"line":48},[21,3701,1347],{},[21,3703,3704],{"class":23,"line":88},[21,3705,3706],{},"  spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}\n",[21,3708,3709],{"class":23,"line":94},[21,3710,3711],{},"  lock()\n",[21,3713,3714],{"class":23,"line":100},[21,3715,2788],{},[21,3717,3718],{"class":23,"line":106},[21,3719,3720],{},"    \u002F\u002F test_and_set()设置标志位为true，并返回旧的标志位\n",[21,3722,3723],{"class":23,"line":112},[21,3724,3725],{},"    \u002F\u002F 第一次调用或着clear()后，才会返回false，从而调出循环\n",[21,3727,3728],{"class":23,"line":176},[21,3729,3730],{},"    while(flag.test_and_set(std::memory_order_acquire));\n",[21,3732,3733],{"class":23,"line":182},[21,3734,103],{},[21,3736,3737],{"class":23,"line":188},[21,3738,3739],{},"  unlock()\n",[21,3741,3742],{"class":23,"line":194},[21,3743,2788],{},[21,3745,3746],{"class":23,"line":200},[21,3747,3748],{},"    \u002F\u002F 设置标志位为false\n",[21,3750,3751],{"class":23,"line":206},[21,3752,3753],{},"    flag.clear(std::memory_order_release);\n",[21,3755,3756],{"class":23,"line":212},[21,3757,103],{},[21,3759,3760],{"class":23,"line":218},[21,3761,51],{},[2208,3763,3765],{"id":3764},"stdatomicbool","std::atomic\u003Cbool>",[280,3767,3768,3771,3772,1016,3775],{},[18,3769,3770],{},"load()",", ",[18,3773,3774],{},"store()",[18,3776,3777],{},"exchange()",[11,3779,3781],{"className":13,"code":3780,"language":15,"meta":16,"style":16},"atd::atomic\u003Cbool> b;\nbool x = b.load(std::memory_order_acquire);\nb.store(true);\n\u002F\u002F exchange()会返回旧值\nx = b.exchange(true, std::memory_order_acq_rel);\n",[18,3782,3783,3788,3793,3798,3803],{"__ignoreMap":16},[21,3784,3785],{"class":23,"line":24},[21,3786,3787],{},"atd::atomic\u003Cbool> b;\n",[21,3789,3790],{"class":23,"line":30},[21,3791,3792],{},"bool x = b.load(std::memory_order_acquire);\n",[21,3794,3795],{"class":23,"line":36},[21,3796,3797],{},"b.store(true);\n",[21,3799,3800],{"class":23,"line":42},[21,3801,3802],{},"\u002F\u002F exchange()会返回旧值\n",[21,3804,3805],{"class":23,"line":48},[21,3806,3807],{},"x = b.exchange(true, std::memory_order_acq_rel);\n",[280,3809,3810,2819,3813],{},[18,3811,3812],{},"compare_exchange_weak()",[18,3814,3815],{},"compare_exchange_strong()",[1183,3817,3818],{},[280,3819,3820,3821,1016,3823,3825],{},"CAS即Compare And Swap，",[18,3822,3812],{},[18,3824,3815],{},"是C++对CAS的实现",[280,3827,3828,3831,3832,3835],{},[18,3829,3830],{},"x.compare_exchange_strong(expected, desired)","，如果x的原始值(",[18,3833,3834],{},"*this",")与期望值(expected)相同，\n则令x的值为desired，并返回ture，如果不同，则x的值不变，并将值赋给expected，返回false",[1183,3837,3838],{},[280,3839,3840],{},"返回值true或false表示x的值是否变化，与期望值相同则改变，不同则没变",[11,3842,3844],{"className":13,"code":3843,"language":15,"meta":16,"style":16},"bool expected = false;\nextern std::atomic\u003Cbool> b;\nif(b.compare_exchange_strong(expected, true))\n{\n  ...\n}\n",[18,3845,3846,3851,3856,3861,3865,3869],{"__ignoreMap":16},[21,3847,3848],{"class":23,"line":24},[21,3849,3850],{},"bool expected = false;\n",[21,3852,3853],{"class":23,"line":30},[21,3854,3855],{},"extern std::atomic\u003Cbool> b;\n",[21,3857,3858],{"class":23,"line":36},[21,3859,3860],{},"if(b.compare_exchange_strong(expected, true))\n",[21,3862,3863],{"class":23,"line":42},[21,3864,2296],{},[21,3866,3867],{"class":23,"line":48},[21,3868,2311],{},[21,3870,3871],{"class":23,"line":88},[21,3872,115],{},[280,3874,3875,3876,3878,3879,3882,3883,3885],{},"对于",[18,3877,3812],{},"来说，可能会出现\"伪失败\"，即",[18,3880,3881],{},"x.compare_exchange_weak(y, z)","，在\nx与y相等时，仍然返回false，且将x的值赋给y。所以通常在使用",[18,3884,3812],{},"时，都需要一个\n循环",[11,3887,3889],{"className":13,"code":3888,"language":15,"meta":16,"style":16},"\u002F\u002F x与expected相等时，如果伪失败，将x的值赋给expected后，再进行一次CAS\nwhile(!x.compare_exchange_weak(expected, desired));\n",[18,3890,3891,3896],{"__ignoreMap":16},[21,3892,3893],{"class":23,"line":24},[21,3894,3895],{},"\u002F\u002F x与expected相等时，如果伪失败，将x的值赋给expected后，再进行一次CAS\n",[21,3897,3898],{"class":23,"line":30},[21,3899,3900],{},"while(!x.compare_exchange_weak(expected, desired));\n",[2208,3902,3904],{"id":3903},"stdatomict","std::atomic\u003CT*>",[280,3906,3907,3908,1016,3911,3914],{},"提供+=、-=、++、--操作，同时",[18,3909,3910],{},"fetch_add()",[18,3912,3913],{},"fetch_sub()","在加、减的基础上返回原来的值，称为\n\"交换-相加\"",[11,3916,3918],{"className":13,"code":3917,"language":15,"meta":16,"style":16},"class Widget {};\nWidget a[3];\nstd::atmoic\u003CWidget*> p {a};\n\nWidget* w1 = p.fetch_add(1); \u002F\u002F p加1，而w1是p的原始值\n",[18,3919,3920,3925,3930,3935,3939],{"__ignoreMap":16},[21,3921,3922],{"class":23,"line":24},[21,3923,3924],{},"class Widget {};\n",[21,3926,3927],{"class":23,"line":30},[21,3928,3929],{},"Widget a[3];\n",[21,3931,3932],{"class":23,"line":36},[21,3933,3934],{},"std::atmoic\u003CWidget*> p {a};\n",[21,3936,3937],{"class":23,"line":42},[21,3938,144],{"emptyLinePlaceholder":143},[21,3940,3941],{"class":23,"line":48},[21,3942,3943],{},"Widget* w1 = p.fetch_add(1); \u002F\u002F p加1，而w1是p的原始值\n",[2208,3945,3947],{"id":3946},"stdatomic","std::atomic\u003C>",[1297,3949,3950],{"id":3950},"原子操作的内存序",[1420,3952,3953,3958,3963,3968,3973,3978],{},[786,3954,3955],{},[18,3956,3957],{},"memory_order_relaxed",[786,3959,3960],{},[18,3961,3962],{},"memory_order_consume",[786,3964,3965],{},[18,3966,3967],{},"memory_order_acquire",[786,3969,3970],{},[18,3971,3972],{},"memory_order_release",[786,3974,3975],{},[18,3976,3977],{},"memory_order_acq_rel",[786,3979,3980],{},[18,3981,3982],{},"memory_order_seq_cst",[280,3984,3985,3986],{},"除非指定一个选项，不然默认都是",[18,3987,3982],{},[280,3989,3990,3991,3771,3993,3995,3996,2819,3998,4000,4001,4003],{},"6种选项代表三种内存模型：顺序一致性，获取-释放序(",[18,3992,3962],{},[18,3994,3967],{},",\n",[18,3997,3972],{},[18,3999,3977],{},")和自由序(",[18,4002,3957],{},")",[280,4005,4006],{},"memory order针对的是共享变量，可以是atomic也可以是non-atomic的，但一定是共享的，通过\nmemory order约定CPU操作变量的顺序",[2208,4008,4009],{"id":4009},"顺序一致性",[1420,4011,4012,4015],{},[786,4013,4014],{},"操作不重排，以源码的顺序执行",[786,4016,4017],{},"当前线程的操作顺序，对于其他线程可见",[280,4019,4020],{},"producer的线程中的代码顺序不会改变，即3先行于4，该顺序对consumer可见。因此在运行1\n时，知道先运行3再运行4",[11,4022,4024],{"className":13,"code":4023,"language":15,"meta":16,"style":16},"std::atomic\u003Cbool> ready { false };\nstd::string work = \" \";\n\nvoid consumer()\n{\n  while(!ready.load());           \u002F\u002F 1\n  std::cout \u003C\u003C work \u003C\u003C std::endl; \u002F\u002F 2\n}\n\nvoid producer()\n{\n  work = \"done\";     \u002F\u002F 3\n  ready.store(true); \u002F\u002F 4\n}\n\nint main()\n{\n  std::thread t1(producer);\n  std::thread t2(consumer);\n  t1.join();\n  t2.join();\n}\n",[18,4025,4026,4031,4036,4040,4045,4049,4054,4059,4063,4067,4072,4076,4081,4086,4090,4094,4099,4103,4108,4113,4118,4123],{"__ignoreMap":16},[21,4027,4028],{"class":23,"line":24},[21,4029,4030],{},"std::atomic\u003Cbool> ready { false };\n",[21,4032,4033],{"class":23,"line":30},[21,4034,4035],{},"std::string work = \" \";\n",[21,4037,4038],{"class":23,"line":36},[21,4039,144],{"emptyLinePlaceholder":143},[21,4041,4042],{"class":23,"line":42},[21,4043,4044],{},"void consumer()\n",[21,4046,4047],{"class":23,"line":48},[21,4048,2296],{},[21,4050,4051],{"class":23,"line":88},[21,4052,4053],{},"  while(!ready.load());           \u002F\u002F 1\n",[21,4055,4056],{"class":23,"line":94},[21,4057,4058],{},"  std::cout \u003C\u003C work \u003C\u003C std::endl; \u002F\u002F 2\n",[21,4060,4061],{"class":23,"line":100},[21,4062,115],{},[21,4064,4065],{"class":23,"line":106},[21,4066,144],{"emptyLinePlaceholder":143},[21,4068,4069],{"class":23,"line":112},[21,4070,4071],{},"void producer()\n",[21,4073,4074],{"class":23,"line":176},[21,4075,2296],{},[21,4077,4078],{"class":23,"line":182},[21,4079,4080],{},"  work = \"done\";     \u002F\u002F 3\n",[21,4082,4083],{"class":23,"line":188},[21,4084,4085],{},"  ready.store(true); \u002F\u002F 4\n",[21,4087,4088],{"class":23,"line":194},[21,4089,115],{},[21,4091,4092],{"class":23,"line":200},[21,4093,144],{"emptyLinePlaceholder":143},[21,4095,4096],{"class":23,"line":206},[21,4097,4098],{},"int main()\n",[21,4100,4101],{"class":23,"line":212},[21,4102,2296],{},[21,4104,4105],{"class":23,"line":218},[21,4106,4107],{},"  std::thread t1(producer);\n",[21,4109,4110],{"class":23,"line":224},[21,4111,4112],{},"  std::thread t2(consumer);\n",[21,4114,4115],{"class":23,"line":229},[21,4116,4117],{},"  t1.join();\n",[21,4119,4120],{"class":23,"line":234},[21,4121,4122],{},"  t2.join();\n",[21,4124,4125],{"class":23,"line":240},[21,4126,115],{},[2208,4128,4129],{"id":4129},"获取-释放序",[1183,4131,4132],{},[280,4133,4134],{},"在线程A上一个原子存储是释放操作，在线程B上对相同变量的原子加载时获得操作，且\n线程B上的加载读取由线程A上的存储写入的值，则线程A上的存储Synchronizes-with(同步发生)线程B上的加载",[783,4136,4137,4140],{},[786,4138,4139],{},"不许acquire之后的操作重排到acquire之前，其他release同一原子变量的线程的所有\n写入对当前线程可见",[786,4141,4142],{},"不许release之前的操作重排到release之后，当前线程的所有写入，可见于获得该同一\n原子变量的其他线程",[280,4144,4145,4146,4148,4149,4151,4152,4154,4155],{},"关于",[18,4147,3962],{},"，与",[18,4150,3967],{},"一样，必须与",[18,4153,3972],{},"一起使用，\n",[1292,4156,4157],{},"然后就看不懂了，后续再补充",[2208,4159,4160],{"id":4160},"自由序",[280,4162,4163],{},"没有任何同步和重排限制",[1297,4165,4166],{"id":4166},"栅栏",[280,4168,3875,4169,1016,4171,4173,4174,4176,4177,4179],{},[18,4170,2218],{},[18,4172,2228],{},"，可以看作两个单方向的屏障，",[18,4175,2218],{},"只允许向下方移动，\n",[18,4178,2228],{},"只允许向上方移动",[280,4181,4182],{},[1007,4183],{"alt":4184,"src":4185},"move-out","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fmove_out.png",[280,4187,4188],{},[1007,4189],{"alt":4190,"src":4191},"move-in","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fmove_in.png",[1032,4193,4194,4207],{},[1035,4195,4196],{},[1038,4197,4198,4201,4204],{},[1041,4199,4200],{"align":1969},"full fence",[1041,4202,4203],{"align":1969},"acquire fence",[1041,4205,4206],{"align":1969},"release fence",[1047,4208,4209,4226],{},[1038,4210,4211,4216,4221],{},[1052,4212,4213],{"align":1969},[18,4214,4215],{},"std::atomic_thread_fence()",[1052,4217,4218],{"align":1969},[18,4219,4220],{},"std::atomic_thread_fence(std::memory_order_acquire)",[1052,4222,4223],{"align":1969},[18,4224,4225],{},"std::atomic_thread_fence(std::memory_order_release)",[1038,4227,4228,4231,4234],{},[1052,4229,4230],{"align":1969},"避免重排(Store-Load除外)",[1052,4232,4233],{"align":1969},"避免栅栏前的读操作，被栅栏后的操作重排",[1052,4235,4236],{"align":1969},"避免栅栏前的写擦欧总，被栅栏前的操作重排",[280,4238,4239],{},[1007,4240],{"alt":4241,"src":4242},"fences","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Ffences.png",[2208,4244,4200],{"id":4245},"full-fence",[280,4247,4248],{},[1007,4249],{"alt":4245,"src":4250},"https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Ffull_fence.png",[2208,4252,4203],{"id":4253},"acquire-fence",[280,4255,4256],{},[1007,4257],{"alt":4253,"src":4258},"https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Facquire_fence.png",[2208,4260,4206],{"id":4261},"release-fence",[280,4263,4264],{},[1007,4265],{"alt":4261,"src":4266},"https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Frelease_fence.png",[4268,4269],"hr",{},[280,4271,4272,4273],{},"待续。。。",[1292,4274,4275],{},"有时间再看后面的",[284,4277,286],{},{"title":16,"searchDepth":30,"depth":30,"links":4279},[4280,4281,4285,4290],{"id":2127,"depth":30,"text":2127},{"id":2203,"depth":30,"text":2203,"children":4282},[4283,4284],{"id":2206,"depth":36,"text":2206},{"id":2742,"depth":36,"text":2742},{"id":3049,"depth":30,"text":3049,"children":4286},[4287,4288,4289],{"id":3052,"depth":36,"text":3052},{"id":3206,"depth":36,"text":3206},{"id":3533,"depth":36,"text":3533},{"id":3632,"depth":30,"text":3632,"children":4291},[4292,4293,4294],{"id":3635,"depth":36,"text":3635},{"id":3950,"depth":36,"text":3950},{"id":4166,"depth":36,"text":4166},"2023-07-24",{},"\u002Fblog\u002Fcplusplus-concurrency",{"title":2105,"description":294},"blog\u002Fcplusplus-concurrency",[1175,1905],"JAvcJa3vDr3fIa9rUig4beZFZOMpUW2pmGaKHQpjw-o",{"id":4303,"title":4304,"body":4305,"date":4674,"description":4675,"extension":295,"meta":4676,"navigation":143,"path":4677,"seo":4678,"stem":4679,"tags":4680,"__hash__":4682},"blog\u002Fblog\u002Fmemorandum-nvim.md","nvim学习记录",{"type":8,"value":4306,"toc":4668},[4307,4310,4602,4605,4608,4613,4620,4628,4635,4641,4644,4650,4653,4659,4662],[53,4308,4309],{"id":4309},"快捷键",[1032,4311,4312,4321],{},[1035,4313,4314],{},[1038,4315,4316,4318],{},[1041,4317,1970],{"align":1973},[1041,4319,4320],{"align":1969},"描述",[1047,4322,4323,4333,4343,4353,4363,4373,4383,4393,4406,4416,4426,4436,4446,4456,4465,4474,4484,4494,4511,4521,4531,4549,4559,4569,4582,4592],{},[1038,4324,4325,4330],{},[1052,4326,4327],{"align":1973},[18,4328,4329],{},"\u003CC-f>",[1052,4331,4332],{"align":1969},"下一页",[1038,4334,4335,4340],{},[1052,4336,4337],{"align":1973},[18,4338,4339],{},"\u003CC-b>",[1052,4341,4342],{"align":1969},"上一页",[1038,4344,4345,4350],{},[1052,4346,4347],{"align":1973},[18,4348,4349],{},"\u003CC-d>",[1052,4351,4352],{"align":1969},"下半页",[1038,4354,4355,4360],{},[1052,4356,4357],{"align":1973},[18,4358,4359],{},"\u003CC-u>",[1052,4361,4362],{"align":1969},"上半页",[1038,4364,4365,4370],{},[1052,4366,4367],{"align":1973},[18,4368,4369],{},"{n}G",[1052,4371,4372],{"align":1969},"跳转到第n行",[1038,4374,4375,4380],{},[1052,4376,4377],{"align":1973},[18,4378,4379],{},":jumps",[1052,4381,4382],{"align":1969},"查看跳转列表",[1038,4384,4385,4390],{},[1052,4386,4387],{"align":1973},[18,4388,4389],{},":marks",[1052,4391,4392],{"align":1969},"查看标记列表",[1038,4394,4395,4400],{},[1052,4396,4397],{"align":1973},[18,4398,4399],{},"m{a~z}",[1052,4401,4402,4403],{"align":1969},"标记光标当前位置，标记名为",[18,4404,4405],{},"{a~z}",[1038,4407,4408,4413],{},[1052,4409,4410],{"align":1973},[18,4411,4412],{},"m{A~Z}",[1052,4414,4415],{"align":1969},"跨缓冲区标记",[1038,4417,4418,4423],{},[1052,4419,4420],{"align":1973},[18,4421,4422],{}," `{a~Z}",[1052,4424,4425],{"align":1969},"移动到标记位置",[1038,4427,4428,4433],{},[1052,4429,4430],{"align":1973},[18,4431,4432],{},"'{a~Z}",[1052,4434,4435],{"align":1969},"移动到标记行首",[1038,4437,4438,4443],{},[1052,4439,4440],{"align":1973},[18,4441,4442],{},"\u003CC-i>",[1052,4444,4445],{"align":1969},"跳转到下一个位置",[1038,4447,4448,4453],{},[1052,4449,4450],{"align":1973},[18,4451,4452],{},"\u003CC-o>",[1052,4454,4455],{"align":1969},"跳转到上一个位置",[1038,4457,4458,4462],{},[1052,4459,4460],{"align":1973},[18,4461,1133],{},[1052,4463,4464],{"align":1969},"跳转上一段开头",[1038,4466,4467,4471],{},[1052,4468,4469],{"align":1973},[18,4470,1145],{},[1052,4472,4473],{"align":1969},"跳转下一段开头",[1038,4475,4476,4481],{},[1052,4477,4478],{"align":1973},[18,4479,4480],{},"[[",[1052,4482,4483],{"align":1969},"跳转上一个函数(代码中函数的'{'必须单独占一行)",[1038,4485,4486,4491],{},[1052,4487,4488],{"align":1973},[18,4489,4490],{},"]]",[1052,4492,4493],{"align":1969},"跳转下一个函数",[1038,4495,4496,4501],{},[1052,4497,4498],{"align":1973},[18,4499,4500],{},":s\u002F{old}\u002F{new}",[1052,4502,4503,4504,4507,4508],{"align":1969},"替换当前行的第一个",[18,4505,4506],{},"{old}","为",[18,4509,4510],{},"{new}",[1038,4512,4513,4518],{},[1052,4514,4515],{"align":1973},[18,4516,4517],{},":s\u002F{old}\u002F{new}\u002Fg",[1052,4519,4520],{"align":1969},"替换当前行的所有",[1038,4522,4523,4528],{},[1052,4524,4525],{"align":1973},[18,4526,4527],{},":s\u002F{old}\u002F{new}\u002Fgc",[1052,4529,4530],{"align":1969},"请求确认",[1038,4532,4533,4538],{},[1052,4534,4535],{"align":1973},[18,4536,4537],{},":{n1},{n2}s\u002F{old}\u002F{new}\u002Fg",[1052,4539,4540,4541,4544,4545,4548],{"align":1969},"从",[18,4542,4543],{},"{n1}","到",[18,4546,4547],{},"{n2}","行",[1038,4550,4551,4556],{},[1052,4552,4553],{"align":1973},[18,4554,4555],{},":%s\u002F{old}\u002F{new}\u002Fg",[1052,4557,4558],{"align":1969},"从第一行到最后一行",[1038,4560,4561,4566],{},[1052,4562,4563],{"align":1973},[18,4564,4565],{},".",[1052,4567,4568],{"align":1969},"重复上一个动作",[1038,4570,4571,4576],{},[1052,4572,4573],{"align":1973},[18,4574,4575],{},"R",[1052,4577,4578,4579],{"align":1969},"一直取代直到",[18,4580,4581],{},"\u003CESC>",[1038,4583,4584,4589],{},[1052,4585,4586],{"align":1973},[18,4587,4588],{},"\u003CC-a>",[1052,4590,4591],{"align":1969},"光标下数字加一",[1038,4593,4594,4599],{},[1052,4595,4596],{"align":1973},[18,4597,4598],{},"\u003CC-x>",[1052,4600,4601],{"align":1969},"光标下数字减一",[53,4603,4604],{"id":4604},"实用功能",[1297,4606,4607],{"id":4607},"读写文件",[1183,4609,4610],{},[280,4611,4612],{},"取自nvim tutor的Lesson 5.3与5.4",[280,4614,4615,4616,4619],{},"选择需要写入到新文件的内容，按",[18,4617,4618],{},":","后会显示",[11,4621,4626],{"className":4622,"code":4624,"language":4625},[4623],"language-text",":'\u003C,'>\n","text",[18,4627,4624],{"__ignoreMap":16},[280,4629,4630,4631,4634],{},"然后输入",[18,4632,4633],{},"w { filename }","将选择的内容保存到另一个文件中，就像这样",[11,4636,4639],{"className":4637,"code":4638,"language":4625},[4623],":'\u003C,'>w TEST\n",[18,4640,4638],{"__ignoreMap":16},[280,4642,4643],{},"NOTE: 这里写入的是一个新文件，如果已经存在该文件，会显示",[11,4645,4648],{"className":4646,"code":4647,"language":4625},[4623],"E13: File exists (add ! to override)\n",[18,4649,4647],{"__ignoreMap":16},[280,4651,4652],{},"同时，也可将另一文件的内容写入到当前文件",[11,4654,4657],{"className":4655,"code":4656,"language":4625},[4623],":r { filename }\n",[18,4658,4656],{"__ignoreMap":16},[280,4660,4661],{},"NOTE: 也可输入命令",[11,4663,4666],{"className":4664,"code":4665,"language":4625},[4623],":r !ls\n",[18,4667,4665],{"__ignoreMap":16},{"title":16,"searchDepth":30,"depth":30,"links":4669},[4670,4671],{"id":4309,"depth":30,"text":4309},{"id":4604,"depth":30,"text":4604,"children":4672},[4673],{"id":4607,"depth":36,"text":4607},"2023-06-22","备忘录",{},"\u002Fblog\u002Fmemorandum-nvim",{"title":4304,"description":4675},"blog\u002Fmemorandum-nvim",[4681],"nvim","q5zGD4cB4cKpkxw7MAYtWXvQ2H6yWWVozjoEivAvk6E",{"id":4684,"title":4685,"body":4686,"date":7969,"description":294,"extension":295,"meta":7970,"navigation":143,"path":7971,"seo":7972,"stem":7973,"tags":7974,"__hash__":7975},"blog\u002Fblog\u002Feffective-modern-cplusplus.md","Effective Modern C++",{"type":8,"value":4687,"toc":7909},[4688,4692,4696,4725,4729,4734,4742,4795,4799,4807,4811,4867,4912,4915,4942,4951,4972,4979,5007,5010,5013,5059,5073,5077,5142,5146,5185,5189,5193,5197,5201,5289,5293,5297,5371,5375,5379,5475,5479,5483,5491,5495,5499,5503,5507,5543,5547,5551,5555,5559,5591,5595,5608,5614,5621,5655,5667,5696,5732,5736,5740,5752,5780,5794,5801,5844,5850,5890,5894,5897,5980,6069,6072,6083,6090,6093,6100,6104,6117,6121,6148,6175,6178,6205,6208,6234,6238,6290,6294,6297,6300,6333,6336,6367,6370,6375,6378,6420,6423,6430,6433,6464,6468,6539,6581,6584,6588,6592,6595,6640,6643,6646,6650,6654,6658,6722,6726,6787,6791,6798,6807,6811,6815,6819,6847,6851,6871,6879,6894,6898,6901,6937,6943,6948,6951,6980,6999,7002,7006,7010,7013,7061,7071,7099,7102,7122,7125,7129,7132,7152,7155,7246,7250,7278,7282,7290,7294,7298,7314,7318,7328,7362,7365,7402,7406,7509,7524,7528,7537,7556,7560,7563,7718,7722,7736,7740,7744,7860,7865,7876,7880,7885,7907],[53,4689,4691],{"id":4690},"chapter-1-deducing-types","CHAPTER 1 Deducing Types",[1297,4693,4695],{"id":4694},"条款1-理解模板类型推导","条款1 理解模板类型推导",[11,4697,4699],{"className":13,"code":4698,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\n\u002F\u002F 此处ParamType泛指param的类型，可以是T，T&，const T&以及T&&\nvoid f(ParamType param);\n\nf(expr)\n",[18,4700,4701,4706,4711,4716,4720],{"__ignoreMap":16},[21,4702,4703],{"class":23,"line":24},[21,4704,4705],{},"template\u003Ctypename T>\n",[21,4707,4708],{"class":23,"line":30},[21,4709,4710],{},"\u002F\u002F 此处ParamType泛指param的类型，可以是T，T&，const T&以及T&&\n",[21,4712,4713],{"class":23,"line":36},[21,4714,4715],{},"void f(ParamType param);\n",[21,4717,4718],{"class":23,"line":42},[21,4719,144],{"emptyLinePlaceholder":143},[21,4721,4722],{"class":23,"line":48},[21,4723,4724],{},"f(expr)\n",[2208,4726,4728],{"id":4727},"paramtype为指针或引用但不是通用引用","ParamType为指针或引用但不是通用引用",[1183,4730,4731],{},[280,4732,4733],{},"通用引用为T&&",[1420,4735,4736,4739],{},[786,4737,4738],{},"如果expr为引用，则忽略引用部分",[786,4740,4741],{},"剩下的部分决定T，然后T与形参匹配得出ParamType",[11,4743,4745],{"className":13,"code":4744,"language":15,"meta":16,"style":16},"Template\u003Ctypename T>\nvoid f(T& param);\n\nint x = 1;\nconst int cx = 1;\nconst int& rx = cx;\n\nf(x);  \u002F\u002F T为int，param的类型为int&\nf(cx); \u002F\u002F T为const int，param类型为const int&\nf(rx); \u002F\u002F T为const int，param类型为const int&\n",[18,4746,4747,4752,4757,4761,4766,4771,4776,4780,4785,4790],{"__ignoreMap":16},[21,4748,4749],{"class":23,"line":24},[21,4750,4751],{},"Template\u003Ctypename T>\n",[21,4753,4754],{"class":23,"line":30},[21,4755,4756],{},"void f(T& param);\n",[21,4758,4759],{"class":23,"line":36},[21,4760,144],{"emptyLinePlaceholder":143},[21,4762,4763],{"class":23,"line":42},[21,4764,4765],{},"int x = 1;\n",[21,4767,4768],{"class":23,"line":48},[21,4769,4770],{},"const int cx = 1;\n",[21,4772,4773],{"class":23,"line":88},[21,4774,4775],{},"const int& rx = cx;\n",[21,4777,4778],{"class":23,"line":94},[21,4779,144],{"emptyLinePlaceholder":143},[21,4781,4782],{"class":23,"line":100},[21,4783,4784],{},"f(x);  \u002F\u002F T为int，param的类型为int&\n",[21,4786,4787],{"class":23,"line":106},[21,4788,4789],{},"f(cx); \u002F\u002F T为const int，param类型为const int&\n",[21,4791,4792],{"class":23,"line":112},[21,4793,4794],{},"f(rx); \u002F\u002F T为const int，param类型为const int&\n",[2208,4796,4798],{"id":4797},"paramtype是通用引用","ParamType是通用引用",[783,4800,4801,4804],{},[786,4802,4803],{},"expr是左值，T和ParamType都是左值引用",[786,4805,4806],{},"expr是右值，T为expr类型，ParamType为右值引用",[2208,4808,4810],{"id":4809},"paramtype既不是指针也不是引用","ParamType既不是指针也不是引用",[11,4812,4814],{"className":13,"code":4813,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid f(T param);\n\nint x = 27;\nconst int cx = x;\nconst int& rx = cx;\n\n\u002F\u002F T和param都是int\nf(x);\nf(cx);\nf(rx)\n",[18,4815,4816,4820,4825,4829,4834,4839,4843,4847,4852,4857,4862],{"__ignoreMap":16},[21,4817,4818],{"class":23,"line":24},[21,4819,4705],{},[21,4821,4822],{"class":23,"line":30},[21,4823,4824],{},"void f(T param);\n",[21,4826,4827],{"class":23,"line":36},[21,4828,144],{"emptyLinePlaceholder":143},[21,4830,4831],{"class":23,"line":42},[21,4832,4833],{},"int x = 27;\n",[21,4835,4836],{"class":23,"line":48},[21,4837,4838],{},"const int cx = x;\n",[21,4840,4841],{"class":23,"line":88},[21,4842,4775],{},[21,4844,4845],{"class":23,"line":94},[21,4846,144],{"emptyLinePlaceholder":143},[21,4848,4849],{"class":23,"line":100},[21,4850,4851],{},"\u002F\u002F T和param都是int\n",[21,4853,4854],{"class":23,"line":106},[21,4855,4856],{},"f(x);\n",[21,4858,4859],{"class":23,"line":112},[21,4860,4861],{},"f(cx);\n",[21,4863,4864],{"class":23,"line":176},[21,4865,4866],{},"f(rx)\n",[1420,4868,4869,4872],{},[786,4870,4871],{},"如果expr是引用，忽略引用部分",[786,4873,4874,4875,4877,4878,4883,4905,4907,4908,4911],{},"如果忽略引用后是const或volatile，它们也会被忽略",[1447,4876],{},"param只是expr的拷贝，expr不可修改，不代表param也一样",[1183,4879,4880],{},[280,4881,4882],{},"函数实参传递给形参时，会忽略实参的顶层const",[11,4884,4886],{"className":13,"code":4885,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid f(T param);\n\nconst char* const ptr = \"Fun with pointers\";\n",[18,4887,4888,4892,4896,4900],{"__ignoreMap":16},[21,4889,4890],{"class":23,"line":24},[21,4891,4705],{},[21,4893,4894],{"class":23,"line":30},[21,4895,4824],{},[21,4897,4898],{"class":23,"line":36},[21,4899,144],{"emptyLinePlaceholder":143},[21,4901,4902],{"class":23,"line":42},[21,4903,4904],{},"const char* const ptr = \"Fun with pointers\";\n",[1447,4906],{},"即T为",[18,4909,4910],{},"const char*","类型",[2208,4913,4914],{"id":4914},"数组实参",[11,4916,4918],{"className":13,"code":4917,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid f(T param);\n\nconst char name[] = \"Effective\";\nf(name);\n",[18,4919,4920,4924,4928,4932,4937],{"__ignoreMap":16},[21,4921,4922],{"class":23,"line":24},[21,4923,4705],{},[21,4925,4926],{"class":23,"line":30},[21,4927,4824],{},[21,4929,4930],{"class":23,"line":36},[21,4931,144],{"emptyLinePlaceholder":143},[21,4933,4934],{"class":23,"line":42},[21,4935,4936],{},"const char name[] = \"Effective\";\n",[21,4938,4939],{"class":23,"line":48},[21,4940,4941],{},"f(name);\n",[280,4943,4944,4945,4947,4948,4950],{},"此时，name会由数组退化成",[18,4946,4910],{},"指针即T为",[18,4949,4910],{},"类型。但同时也可用引用来表示数组",[11,4952,4954],{"className":13,"code":4953,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid f(T& param);\n\nf(name);\n",[18,4955,4956,4960,4964,4968],{"__ignoreMap":16},[21,4957,4958],{"class":23,"line":24},[21,4959,4705],{},[21,4961,4962],{"class":23,"line":30},[21,4963,4756],{},[21,4965,4966],{"class":23,"line":36},[21,4967,144],{"emptyLinePlaceholder":143},[21,4969,4970],{"class":23,"line":42},[21,4971,4941],{},[280,4973,4974,4975,4978],{},"此时的T为数组引用类型，即",[18,4976,4977],{},"const char(&)[10]","，同时我们可以在模板函数中推出数组大小",[11,4980,4982],{"className":13,"code":4981,"language":15,"meta":16,"style":16},"template\u003Ctypename T, std::size_t N>\nconstexpr std::size_t arraySize(T (&)[N]) noexcept\n{\n  return N;\n}\n",[18,4983,4984,4989,4994,4998,5003],{"__ignoreMap":16},[21,4985,4986],{"class":23,"line":24},[21,4987,4988],{},"template\u003Ctypename T, std::size_t N>\n",[21,4990,4991],{"class":23,"line":30},[21,4992,4993],{},"constexpr std::size_t arraySize(T (&)[N]) noexcept\n",[21,4995,4996],{"class":23,"line":36},[21,4997,2296],{},[21,4999,5000],{"class":23,"line":42},[21,5001,5002],{},"  return N;\n",[21,5004,5005],{"class":23,"line":48},[21,5006,115],{},[2208,5008,5009],{"id":5009},"函数实参",[280,5011,5012],{},"与数组一样，函数类型也会退化为函数指针",[11,5014,5016],{"className":13,"code":5015,"language":15,"meta":16,"style":16},"void someFunc(int, double);\ntemplate\u003Ctypename T>\nvoid f1(T param);\n\ntemplate\u003Ctypename T>\nvoid f2(T& param);\n\nf1(someFunc);  \u002F\u002F ParamType为void(*)(int, double)\nf2(someFunc);  \u002F\u002F ParamType为void(&)(int, double)\n",[18,5017,5018,5023,5027,5032,5036,5040,5045,5049,5054],{"__ignoreMap":16},[21,5019,5020],{"class":23,"line":24},[21,5021,5022],{},"void someFunc(int, double);\n",[21,5024,5025],{"class":23,"line":30},[21,5026,4705],{},[21,5028,5029],{"class":23,"line":36},[21,5030,5031],{},"void f1(T param);\n",[21,5033,5034],{"class":23,"line":42},[21,5035,144],{"emptyLinePlaceholder":143},[21,5037,5038],{"class":23,"line":48},[21,5039,4705],{},[21,5041,5042],{"class":23,"line":88},[21,5043,5044],{},"void f2(T& param);\n",[21,5046,5047],{"class":23,"line":94},[21,5048,144],{"emptyLinePlaceholder":143},[21,5050,5051],{"class":23,"line":100},[21,5052,5053],{},"f1(someFunc);  \u002F\u002F ParamType为void(*)(int, double)\n",[21,5055,5056],{"class":23,"line":106},[21,5057,5058],{},"f2(someFunc);  \u002F\u002F ParamType为void(&)(int, double)\n",[783,5060,5061,5064,5067,5070],{},[786,5062,5063],{},"在模板类型推导时，引用会被忽略",[786,5065,5066],{},"对于通用引用的推导，左值实参会被特殊对待",[786,5068,5069],{},"对于传值类型的推导，实参的常量性和易变性会被忽略",[786,5071,5072],{},"在模板类型推导时，数组或函数会退化为指针，除非被用于初始化引用",[1297,5074,5076],{"id":5075},"条款2-理解auto类型推导-item2","条款2 理解auto类型推导 {#item2}",[783,5078,5079,5139],{},[786,5080,5081,5082,5085,5086],{},"auto类型拖到通常和模板类型推导相同，但auto类型推导假定花括号初始化代表\n",[18,5083,5084],{},"std::initializer_list","而模板类型推导不这样做",[11,5087,5089],{"className":13,"code":5088,"language":15,"meta":16,"style":16},"auto x = {1, 2, 3, 4}; \u002F\u002F x的类型为std::initializer_list\u003Cint>\n\ntemplate\u003Ctypename T>\nvoid f(T param);\n\nf({1, 2, 3, 4});  \u002F\u002F 不能推导出\n\ntemplate\u003Ctypename T>\nvoid f(std::initializer_list\u003CT> param);\n\nf({1, 2, 3, 4});  \u002F\u002F 此时可推出\n",[18,5090,5091,5096,5100,5104,5108,5112,5117,5121,5125,5130,5134],{"__ignoreMap":16},[21,5092,5093],{"class":23,"line":24},[21,5094,5095],{},"auto x = {1, 2, 3, 4}; \u002F\u002F x的类型为std::initializer_list\u003Cint>\n",[21,5097,5098],{"class":23,"line":30},[21,5099,144],{"emptyLinePlaceholder":143},[21,5101,5102],{"class":23,"line":36},[21,5103,4705],{},[21,5105,5106],{"class":23,"line":42},[21,5107,4824],{},[21,5109,5110],{"class":23,"line":48},[21,5111,144],{"emptyLinePlaceholder":143},[21,5113,5114],{"class":23,"line":88},[21,5115,5116],{},"f({1, 2, 3, 4});  \u002F\u002F 不能推导出\n",[21,5118,5119],{"class":23,"line":94},[21,5120,144],{"emptyLinePlaceholder":143},[21,5122,5123],{"class":23,"line":100},[21,5124,4705],{},[21,5126,5127],{"class":23,"line":106},[21,5128,5129],{},"void f(std::initializer_list\u003CT> param);\n",[21,5131,5132],{"class":23,"line":112},[21,5133,144],{"emptyLinePlaceholder":143},[21,5135,5136],{"class":23,"line":176},[21,5137,5138],{},"f({1, 2, 3, 4});  \u002F\u002F 此时可推出\n",[786,5140,5141],{},"在C++14中允许出现在函数返回值或者lambda函数形参中，但它的工作机制是模板类型推导的方案",[1297,5143,5145],{"id":5144},"条款3-理解decltype","条款3 理解decltype",[783,5147,5148,5151,5174],{},[786,5149,5150],{},"decltype总是不加修改的产生变量或表达式的类型",[786,5152,5153,5154],{},"对于T类型的左值表达式，decltype总是产出T的引用即T&",[11,5155,5157],{"className":13,"code":5156,"language":15,"meta":16,"style":16},"int a = 1;\ndecltype(a);    \u002F\u002F int\ndecltype((a));  \u002F\u002F int&\n",[18,5158,5159,5164,5169],{"__ignoreMap":16},[21,5160,5161],{"class":23,"line":24},[21,5162,5163],{},"int a = 1;\n",[21,5165,5166],{"class":23,"line":30},[21,5167,5168],{},"decltype(a);    \u002F\u002F int\n",[21,5170,5171],{"class":23,"line":36},[21,5172,5173],{},"decltype((a));  \u002F\u002F int&\n",[786,5175,5176,5177],{},"C++14支持decltype(auto)，就像auto一样，推导出类型，但它使用自己独特规则进行推导",[1183,5178,5179,5182],{},[280,5180,5181],{},"单纯的auto与模板参数推导一样，会忽略引用",[280,5183,5184],{},"而在decltype(auto)中，auto说明类型会被推导，decltype说明会按decltype的规则推导",[1297,5186,5188],{"id":5187},"条款4-学会查看类型推导结果","条款4 学会查看类型推导结果",[53,5190,5192],{"id":5191},"chapter-2-auto","CHAPTER 2 auto",[1297,5194,5196],{"id":5195},"条款5-优先考虑auto而非显示类型声明","条款5 优先考虑auto而非显示类型声明",[1297,5198,5200],{"id":5199},"条款6-auto推导若非己愿使用显示类型初始化惯用法","条款6 auto推导若非己愿，使用显示类型初始化惯用法",[783,5202,5203,5277],{},[786,5204,5205,5206,5253,5255,5256,5271,5273,5274],{},"不可见的代理类可能会使auto从表达式中推导出错误的类型",[11,5207,5209],{"className":13,"code":5208,"language":15,"meta":16,"style":16},"namespace std {\n  template\u003Cclass Allocator>\n  class vector\u003Cbool, Allocator>{\n    public:\n    class reference {...};\n\n    reference operator[](size_type n);\n  }\n}\n",[18,5210,5211,5216,5221,5226,5231,5236,5240,5245,5249],{"__ignoreMap":16},[21,5212,5213],{"class":23,"line":24},[21,5214,5215],{},"namespace std {\n",[21,5217,5218],{"class":23,"line":30},[21,5219,5220],{},"  template\u003Cclass Allocator>\n",[21,5222,5223],{"class":23,"line":36},[21,5224,5225],{},"  class vector\u003Cbool, Allocator>{\n",[21,5227,5228],{"class":23,"line":42},[21,5229,5230],{},"    public:\n",[21,5232,5233],{"class":23,"line":48},[21,5234,5235],{},"    class reference {...};\n",[21,5237,5238],{"class":23,"line":88},[21,5239,144],{"emptyLinePlaceholder":143},[21,5241,5242],{"class":23,"line":94},[21,5243,5244],{},"    reference operator[](size_type n);\n",[21,5246,5247],{"class":23,"line":100},[21,5248,103],{},[21,5250,5251],{"class":23,"line":106},[21,5252,115],{},[1447,5254],{},"reference就是vector的代理类，当使用",[11,5257,5259],{"className":13,"code":5258,"language":15,"meta":16,"style":16},"std::vector\u003Cbool> feature(const Widget&);\nauto highPriority = feature(w)[5];\n",[18,5260,5261,5266],{"__ignoreMap":16},[21,5262,5263],{"class":23,"line":24},[21,5264,5265],{},"std::vector\u003Cbool> feature(const Widget&);\n",[21,5267,5268],{"class":23,"line":30},[21,5269,5270],{},"auto highPriority = feature(w)[5];\n",[1447,5272],{},"此时期望的是highPriority为bool类型，但实际上auto推导的是",[18,5275,5276],{},"std::vector\u003Cbool>::reference",[786,5278,5279,5280],{},"显式类型初始器惯用强制auto推导出想要的结果",[11,5281,5283],{"className":13,"code":5282,"language":15,"meta":16,"style":16},"auto highPriority = static_cast\u003Cbool>(feature(w)[5]);\n",[18,5284,5285],{"__ignoreMap":16},[21,5286,5287],{"class":23,"line":24},[21,5288,5282],{},[53,5290,5292],{"id":5291},"chapter-3-moving-to-modern-c","CHAPTER 3 Moving to Modern C++",[1297,5294,5296],{"id":5295},"条款7-区别使用和创建对象","条款7 区别使用()和{}创建对象",[783,5298,5299,5307,5353],{},[786,5300,5301,5302],{},"括号初始化可防止变窄转换",[1183,5303,5304],{},[280,5305,5306],{},"括号初始化指的是大括号",[786,5308,5309,5310,5312,5313,5350,5352],{},"在构造函数重载中，括号初始化会与",[18,5311,5084],{},"参数匹配，即使其他构造函数时更好的选择",[11,5314,5316],{"className":13,"code":5315,"language":15,"meta":16,"style":16},"class Widget\n{\npublic:\n  Widget(int i, double d);\n  Widget(std::initializer_list\u003Cbool> il);\n};\nWidget w{10, 5.0};\n",[18,5317,5318,5323,5327,5331,5336,5341,5345],{"__ignoreMap":16},[21,5319,5320],{"class":23,"line":24},[21,5321,5322],{},"class Widget\n",[21,5324,5325],{"class":23,"line":30},[21,5326,2296],{},[21,5328,5329],{"class":23,"line":36},[21,5330,1347],{},[21,5332,5333],{"class":23,"line":42},[21,5334,5335],{},"  Widget(int i, double d);\n",[21,5337,5338],{"class":23,"line":48},[21,5339,5340],{},"  Widget(std::initializer_list\u003Cbool> il);\n",[21,5342,5343],{"class":23,"line":88},[21,5344,51],{},[21,5346,5347],{"class":23,"line":94},[21,5348,5349],{},"Widget w{10, 5.0};\n",[1447,5351],{},"上述代码会匹配initializer_list参数的构造函数，而同时由于括号初始化禁止变窄转换，编译会失败",[786,5354,5355,5356],{},"在模板类中选择使用小括号初始化或花括号初始化创建对象是一个挑战",[11,5357,5359],{"className":13,"code":5358,"language":15,"meta":16,"style":16},"std::vector\u003Cint> a(10, 20); \u002F\u002F 10个20\nstd::vector\u003Cint> b{10, 20}; \u002F\u002F 10和20\n",[18,5360,5361,5366],{"__ignoreMap":16},[21,5362,5363],{"class":23,"line":24},[21,5364,5365],{},"std::vector\u003Cint> a(10, 20); \u002F\u002F 10个20\n",[21,5367,5368],{"class":23,"line":30},[21,5369,5370],{},"std::vector\u003Cint> b{10, 20}; \u002F\u002F 10和20\n",[1297,5372,5374],{"id":5373},"条款8-优先考虑nullptr而非0和null","条款8 优先考虑nullptr而非0和NULL",[1297,5376,5378],{"id":5377},"条款9-优先考虑别名声明而非typedefs","条款9 优先考虑别名声明而非typedefs",[783,5380,5381,5384],{},[786,5382,5383],{},"typedef不支持模板化，但是别名声明支持",[786,5385,5386,5387,5449,5451,5452],{},"别名模板避免了使用::type后缀，同时也就省去了typename的声明",[11,5388,5390],{"className":13,"code":5389,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nstruct MyAllocList\n{\n  typedef std::list\u003CT, MyAlloc\u003CT>> type;\n};\nMyAllocList\u003CWidget>::type lw;\n\n\u002F\u002F 在模板中还需要加上typename\ntemplate\u003Ctypename T>\nclass Widget\n{\n  typename MyAllocList\u003CT>::type list;\n};\n",[18,5391,5392,5396,5401,5405,5410,5414,5419,5423,5428,5432,5436,5440,5445],{"__ignoreMap":16},[21,5393,5394],{"class":23,"line":24},[21,5395,4705],{},[21,5397,5398],{"class":23,"line":30},[21,5399,5400],{},"struct MyAllocList\n",[21,5402,5403],{"class":23,"line":36},[21,5404,2296],{},[21,5406,5407],{"class":23,"line":42},[21,5408,5409],{},"  typedef std::list\u003CT, MyAlloc\u003CT>> type;\n",[21,5411,5412],{"class":23,"line":48},[21,5413,51],{},[21,5415,5416],{"class":23,"line":88},[21,5417,5418],{},"MyAllocList\u003CWidget>::type lw;\n",[21,5420,5421],{"class":23,"line":94},[21,5422,144],{"emptyLinePlaceholder":143},[21,5424,5425],{"class":23,"line":100},[21,5426,5427],{},"\u002F\u002F 在模板中还需要加上typename\n",[21,5429,5430],{"class":23,"line":106},[21,5431,4705],{},[21,5433,5434],{"class":23,"line":112},[21,5435,5322],{},[21,5437,5438],{"class":23,"line":176},[21,5439,2296],{},[21,5441,5442],{"class":23,"line":182},[21,5443,5444],{},"  typename MyAllocList\u003CT>::type list;\n",[21,5446,5447],{"class":23,"line":188},[21,5448,51],{},[1447,5450],{},"可直接使用using",[11,5453,5455],{"className":13,"code":5454,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nusing MyAllocList = std::list\u003CT, MyAlloc\u003CT>>;\n\nMyAllocList\u003CWidget> lw;\n",[18,5456,5457,5461,5466,5470],{"__ignoreMap":16},[21,5458,5459],{"class":23,"line":24},[21,5460,4705],{},[21,5462,5463],{"class":23,"line":30},[21,5464,5465],{},"using MyAllocList = std::list\u003CT, MyAlloc\u003CT>>;\n",[21,5467,5468],{"class":23,"line":36},[21,5469,144],{"emptyLinePlaceholder":143},[21,5471,5472],{"class":23,"line":42},[21,5473,5474],{},"MyAllocList\u003CWidget> lw;\n",[1297,5476,5478],{"id":5477},"条款10-优先考虑限域枚举而非未限域枚举","条款10 优先考虑限域枚举而非未限域枚举",[1297,5480,5482],{"id":5481},"条款11-优先考虑使用deleted函数而非使用未定义的私有声明","条款11 优先考虑使用deleted函数而非使用未定义的私有声明",[783,5484,5485,5488],{},[786,5486,5487],{},"比起声明函数private但不定义，使用deleted函数更好",[786,5489,5490],{},"任何函数都能delete，包括非成员函数和模板示例",[1297,5492,5494],{"id":5493},"条例12-使用override声明重载函数","条例12 使用override声明重载函数",[1297,5496,5498],{"id":5497},"条款13-优先考虑const_iterator而非iterator","条款13 优先考虑const_iterator而非iterator",[1297,5500,5502],{"id":5501},"条款14-如果函数不抛出异常请使用noexcept","条款14 如果函数不抛出异常请使用noexcept",[1297,5504,5506],{"id":5505},"条款15-尽可能的使用constexpr","条款15 尽可能的使用constexpr",[783,5508,5509,5532],{},[786,5510,5511,5512],{},"constexpr对象是const，它的值在编译期可知。但不是所有const对象都是constexpr",[11,5513,5515],{"className":13,"code":5514,"language":15,"meta":16,"style":16},"int a;\nconst int b = a;\nconstexpr int c = b; \u002F\u002F 错误，b的值编译期不可知\n",[18,5516,5517,5522,5527],{"__ignoreMap":16},[21,5518,5519],{"class":23,"line":24},[21,5520,5521],{},"int a;\n",[21,5523,5524],{"class":23,"line":30},[21,5525,5526],{},"const int b = a;\n",[21,5528,5529],{"class":23,"line":36},[21,5530,5531],{},"constexpr int c = b; \u002F\u002F 错误，b的值编译期不可知\n",[786,5533,5534,5535],{},"当传递编译期可知的值时，constexpr函数可以产出编译期可知的结果",[1183,5536,5537,5540],{},[280,5538,5539],{},"constexpr函数的实参在编译期可知时，其结果将在编译期计算",[280,5541,5542],{},"constexpr函数被编译期不可知值调用时，他就像普通函数一样，在运行时计算",[1297,5544,5546],{"id":5545},"条款16-让const成员函数线程安全","条款16 让const成员函数线程安全",[1297,5548,5550],{"id":5549},"条款17-理解特殊成员函数的生成","条款17 理解特殊成员函数的生成",[53,5552,5554],{"id":5553},"chapter-4-smart-pointers","CHAPTER 4 Smart pointers",[1297,5556,5558],{"id":5557},"条款18-对于独占资源使用stdunique_ptr","条款18 对于独占资源使用std::unique_ptr",[783,5560,5561,5581],{},[786,5562,5563,5564,5567,5568],{},"默认情况，资源销毁通过delete，但支持自定义的删除函数。而有状态的删除器和函数指针会增加\n",[18,5565,5566],{},"std::unique_ptr","的大小",[1183,5569,5570,5573,5576],{},[280,5571,5572],{},"有状态是指有状态对象，就是有数据对象，可以保持数据，是非线程安全的",[280,5574,5575],{},"无状态就是一次操作，不能保存数据",[280,5577,5578],{},[1292,5579,5580],{},"可能是这样的",[786,5582,5583,5584,5586,5587,5590],{},"将",[18,5585,5566],{},"转化为",[18,5588,5589],{},"std::shared_ptr","是简单的",[1297,5592,5594],{"id":5593},"条款19-对于共享资源使用stdshared_ptr","条款19 对于共享资源使用std::shared_ptr",[783,5596,5597,5602,5605],{},[786,5598,5599,5601],{},[18,5600,5589],{},"大小是原始指针的两倍，内部包含一个指向资源的原始指针，一个指向引用计数\n的指针(指向控制块，控制块包含引用计数)",[786,5603,5604],{},"引用计数必须动态分配",[786,5606,5607],{},"递增递减引用计数必须是原子性的",[280,5609,5610],{},[1007,5611],{"alt":5612,"src":5613},"share_ptr","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fshared_ptr.png",[280,5615,5616,5617,5620],{},"如果通过原始指针构造",[18,5618,5619],{},"shared_ptr","，需要直接传递new的结果",[11,5622,5624],{"className":13,"code":5623,"language":15,"meta":16,"style":16},"std::shared_ptr\u003CWidget> sp(new Widget);\n\nWidget* pw = new Widget;\n\u002F\u002F 会创建两个控制块，当spw1释放后，spw2会重复释放\nstd::shared_ptr\u003CWidget> spw1(pw);\nstd::shared_ptr\u003CWidget> spw2(pw);\n",[18,5625,5626,5631,5635,5640,5645,5650],{"__ignoreMap":16},[21,5627,5628],{"class":23,"line":24},[21,5629,5630],{},"std::shared_ptr\u003CWidget> sp(new Widget);\n",[21,5632,5633],{"class":23,"line":30},[21,5634,144],{"emptyLinePlaceholder":143},[21,5636,5637],{"class":23,"line":36},[21,5638,5639],{},"Widget* pw = new Widget;\n",[21,5641,5642],{"class":23,"line":42},[21,5643,5644],{},"\u002F\u002F 会创建两个控制块，当spw1释放后，spw2会重复释放\n",[21,5646,5647],{"class":23,"line":48},[21,5648,5649],{},"std::shared_ptr\u003CWidget> spw1(pw);\n",[21,5651,5652],{"class":23,"line":88},[21,5653,5654],{},"std::shared_ptr\u003CWidget> spw2(pw);\n",[280,5656,5657,5658,3479,5661,5663,5664],{},"当需要在类内部通过",[18,5659,5660],{},"this",[18,5662,5619],{},"时，需要使用",[18,5665,5666],{},"share_from_this()",[11,5668,5670],{"className":13,"code":5669,"language":15,"meta":16,"style":16},"std::vector\u003Cstd::shared_ptr\u003CWidget>> widgets;\nvoid Widget::process() {\n  \u002F\u002F 会创建单独的控制块，造成重复释放\n  widgets.emplace_back(this);\n}\n",[18,5671,5672,5677,5682,5687,5692],{"__ignoreMap":16},[21,5673,5674],{"class":23,"line":24},[21,5675,5676],{},"std::vector\u003Cstd::shared_ptr\u003CWidget>> widgets;\n",[21,5678,5679],{"class":23,"line":30},[21,5680,5681],{},"void Widget::process() {\n",[21,5683,5684],{"class":23,"line":36},[21,5685,5686],{},"  \u002F\u002F 会创建单独的控制块，造成重复释放\n",[21,5688,5689],{"class":23,"line":42},[21,5690,5691],{},"  widgets.emplace_back(this);\n",[21,5693,5694],{"class":23,"line":48},[21,5695,115],{},[11,5697,5699],{"className":13,"code":5698,"language":15,"meta":16,"style":16},"class Widget: public std::enable_shared_from_this {\npublic:\n  void process();\n};\nvoid Widget::process() {\n  widgets.emplace_back(share_from_this());\n}\n",[18,5700,5701,5706,5710,5715,5719,5723,5728],{"__ignoreMap":16},[21,5702,5703],{"class":23,"line":24},[21,5704,5705],{},"class Widget: public std::enable_shared_from_this {\n",[21,5707,5708],{"class":23,"line":30},[21,5709,1347],{},[21,5711,5712],{"class":23,"line":36},[21,5713,5714],{},"  void process();\n",[21,5716,5717],{"class":23,"line":42},[21,5718,51],{},[21,5720,5721],{"class":23,"line":48},[21,5722,5681],{},[21,5724,5725],{"class":23,"line":88},[21,5726,5727],{},"  widgets.emplace_back(share_from_this());\n",[21,5729,5730],{"class":23,"line":94},[21,5731,115],{},[1297,5733,5735],{"id":5734},"条款20-当stdshared_ptr可能悬空时使用stdweak_ptr","条款20 当std::shared_ptr可能悬空时使用std::weak_ptr",[1297,5737,5739],{"id":5738},"条款21-优先考虑使用stdmake_unique和stdmake_shared而非new","条款21 优先考虑使用std::make_unique和std::make_shared而非new",[280,5741,5742,5745,5746,5749,5750],{},[18,5743,5744],{},"std::make_shared","是C++11标准，但",[18,5747,5748],{},"std::make_unique","在C++14，但可自己实现基础版本的",[18,5751,5748],{},[11,5753,5755],{"className":13,"code":5754,"language":15,"meta":16,"style":16},"template\u003Ctypename T, typename... Ts>\nstd::unique_ptr\u003CT> make_unique(Ts&&... params)\n{\n  return std::unique_ptr\u003CT>(new T(std::forward\u003CTs>(params)...));\n}\n",[18,5756,5757,5762,5767,5771,5776],{"__ignoreMap":16},[21,5758,5759],{"class":23,"line":24},[21,5760,5761],{},"template\u003Ctypename T, typename... Ts>\n",[21,5763,5764],{"class":23,"line":30},[21,5765,5766],{},"std::unique_ptr\u003CT> make_unique(Ts&&... params)\n",[21,5768,5769],{"class":23,"line":36},[21,5770,2296],{},[21,5772,5773],{"class":23,"line":42},[21,5774,5775],{},"  return std::unique_ptr\u003CT>(new T(std::forward\u003CTs>(params)...));\n",[21,5777,5778],{"class":23,"line":48},[21,5779,115],{},[280,5781,5782,5783,5786,5787,5790,5791,5793],{},"控制块还有第二个引用计数",[18,5784,5785],{},"weak_count","，只要",[18,5788,5789],{},"std::weak_ptr","引用一个控制块即",[18,5792,5785],{},"大于零，\n该控制块就必须存在。",[280,5795,5796,5797,5800],{},"使用",[18,5798,5799],{},"make_shared","创建对象时，对象销毁和释放内存之间会出现延迟",[11,5802,5804],{"className":13,"code":5803,"language":15,"meta":16,"style":16},"auto pBig = std::make_shared\u003CBigType>();\n... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n\n\u002F\u002F 此时，先前分配给对象以及控制块的内存还未释放\n\n... \u002F\u002F 最后一个std::weak_ptr销毁\n\u002F\u002F 控制块和对象内存释放\n",[18,5805,5806,5811,5816,5821,5825,5830,5834,5839],{"__ignoreMap":16},[21,5807,5808],{"class":23,"line":24},[21,5809,5810],{},"auto pBig = std::make_shared\u003CBigType>();\n",[21,5812,5813],{"class":23,"line":30},[21,5814,5815],{},"... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n",[21,5817,5818],{"class":23,"line":36},[21,5819,5820],{},"... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n",[21,5822,5823],{"class":23,"line":42},[21,5824,144],{"emptyLinePlaceholder":143},[21,5826,5827],{"class":23,"line":48},[21,5828,5829],{},"\u002F\u002F 此时，先前分配给对象以及控制块的内存还未释放\n",[21,5831,5832],{"class":23,"line":88},[21,5833,144],{"emptyLinePlaceholder":143},[21,5835,5836],{"class":23,"line":94},[21,5837,5838],{},"... \u002F\u002F 最后一个std::weak_ptr销毁\n",[21,5840,5841],{"class":23,"line":100},[21,5842,5843],{},"\u002F\u002F 控制块和对象内存释放\n",[280,5845,5846,5847,5849],{},"直接使用new，一旦最后一个",[18,5848,5619],{},"被销毁，对象的内存就会释放",[11,5851,5853],{"className":13,"code":5852,"language":15,"meta":16,"style":16},"auto pBig = std::shared_ptr\u003CBigType>(new BigType);\n... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n\n\u002F\u002F 此时，对象销毁，分配给对象的内存释放\n\n... \u002F\u002F 最后一个std::weak_ptr销毁\n\u002F\u002F 控制块的内存释放\n",[18,5854,5855,5860,5864,5868,5872,5877,5881,5885],{"__ignoreMap":16},[21,5856,5857],{"class":23,"line":24},[21,5858,5859],{},"auto pBig = std::shared_ptr\u003CBigType>(new BigType);\n",[21,5861,5862],{"class":23,"line":30},[21,5863,5815],{},[21,5865,5866],{"class":23,"line":36},[21,5867,5820],{},[21,5869,5870],{"class":23,"line":42},[21,5871,144],{"emptyLinePlaceholder":143},[21,5873,5874],{"class":23,"line":48},[21,5875,5876],{},"\u002F\u002F 此时，对象销毁，分配给对象的内存释放\n",[21,5878,5879],{"class":23,"line":88},[21,5880,144],{"emptyLinePlaceholder":143},[21,5882,5883],{"class":23,"line":94},[21,5884,5838],{},[21,5886,5887],{"class":23,"line":100},[21,5888,5889],{},"\u002F\u002F 控制块的内存释放\n",[1297,5891,5893],{"id":5892},"条款22-当使用pimpl惯用法请在实现文件中定义特殊成员函数","条款22 当使用Pimpl惯用法，请在实现文件中定义特殊成员函数",[280,5895,5896],{},"Pimpl是\"指向实现的指针\"，通过将类的实现细节放在一个单独的实现类当中，类通过private指针类来访问实现类",[11,5898,5900],{"className":13,"code":5899,"language":15,"meta":16,"style":16},"\u002F\u002F widget.h\n#include \u003Cmemory>\n\nclass Widget\n{\npublic:\n  Widget();\n  ~Widget();\n  Widget(const Widget&);\n  Widget& operator=(const Widget&);\n  Widget(Widget&&);\n  Widget& operator=(Widget&&);\n\nprivate:\n  struct Impl;\n  std::unique_ptr\u003CImpl> pImpl;\n};\n",[18,5901,5902,5907,5912,5916,5920,5924,5928,5933,5938,5943,5948,5953,5958,5962,5966,5971,5976],{"__ignoreMap":16},[21,5903,5904],{"class":23,"line":24},[21,5905,5906],{},"\u002F\u002F widget.h\n",[21,5908,5909],{"class":23,"line":30},[21,5910,5911],{},"#include \u003Cmemory>\n",[21,5913,5914],{"class":23,"line":36},[21,5915,144],{"emptyLinePlaceholder":143},[21,5917,5918],{"class":23,"line":42},[21,5919,5322],{},[21,5921,5922],{"class":23,"line":48},[21,5923,2296],{},[21,5925,5926],{"class":23,"line":88},[21,5927,1347],{},[21,5929,5930],{"class":23,"line":94},[21,5931,5932],{},"  Widget();\n",[21,5934,5935],{"class":23,"line":100},[21,5936,5937],{},"  ~Widget();\n",[21,5939,5940],{"class":23,"line":106},[21,5941,5942],{},"  Widget(const Widget&);\n",[21,5944,5945],{"class":23,"line":112},[21,5946,5947],{},"  Widget& operator=(const Widget&);\n",[21,5949,5950],{"class":23,"line":176},[21,5951,5952],{},"  Widget(Widget&&);\n",[21,5954,5955],{"class":23,"line":182},[21,5956,5957],{},"  Widget& operator=(Widget&&);\n",[21,5959,5960],{"class":23,"line":188},[21,5961,144],{"emptyLinePlaceholder":143},[21,5963,5964],{"class":23,"line":194},[21,5965,1316],{},[21,5967,5968],{"class":23,"line":200},[21,5969,5970],{},"  struct Impl;\n",[21,5972,5973],{"class":23,"line":206},[21,5974,5975],{},"  std::unique_ptr\u003CImpl> pImpl;\n",[21,5977,5978],{"class":23,"line":212},[21,5979,51],{},[11,5981,5983],{"className":13,"code":5982,"language":15,"meta":16,"style":16},"\u002F\u002F widget.cpp\n#include \"widget.h\"\n\nstruct Widget::Impl\n{\n  int x, y;\n};\n\nWidget::Widget(): pImpl(std::make_unique\u003CImpl>()) {}\nWidget::~Widget() = default;\nWidget::Widget(const Widget& rhs): pImpl(std::make_unique\u003CImpl>(*rhs.pImpl)) {}\nWidget& Widget::operator=(const Widget& rhs)\n{\n  *pImpl = *rhs.pImpl;\n  return *this;\n}\nWidget(Widget&& rhs) = default;\nWidget& operator=(Widget&& rhs) = default;\n",[18,5984,5985,5990,5995,5999,6004,6008,6013,6017,6021,6026,6031,6036,6041,6045,6050,6055,6059,6064],{"__ignoreMap":16},[21,5986,5987],{"class":23,"line":24},[21,5988,5989],{},"\u002F\u002F widget.cpp\n",[21,5991,5992],{"class":23,"line":30},[21,5993,5994],{},"#include \"widget.h\"\n",[21,5996,5997],{"class":23,"line":36},[21,5998,144],{"emptyLinePlaceholder":143},[21,6000,6001],{"class":23,"line":42},[21,6002,6003],{},"struct Widget::Impl\n",[21,6005,6006],{"class":23,"line":48},[21,6007,2296],{},[21,6009,6010],{"class":23,"line":88},[21,6011,6012],{},"  int x, y;\n",[21,6014,6015],{"class":23,"line":94},[21,6016,51],{},[21,6018,6019],{"class":23,"line":100},[21,6020,144],{"emptyLinePlaceholder":143},[21,6022,6023],{"class":23,"line":106},[21,6024,6025],{},"Widget::Widget(): pImpl(std::make_unique\u003CImpl>()) {}\n",[21,6027,6028],{"class":23,"line":112},[21,6029,6030],{},"Widget::~Widget() = default;\n",[21,6032,6033],{"class":23,"line":176},[21,6034,6035],{},"Widget::Widget(const Widget& rhs): pImpl(std::make_unique\u003CImpl>(*rhs.pImpl)) {}\n",[21,6037,6038],{"class":23,"line":182},[21,6039,6040],{},"Widget& Widget::operator=(const Widget& rhs)\n",[21,6042,6043],{"class":23,"line":188},[21,6044,2296],{},[21,6046,6047],{"class":23,"line":194},[21,6048,6049],{},"  *pImpl = *rhs.pImpl;\n",[21,6051,6052],{"class":23,"line":200},[21,6053,6054],{},"  return *this;\n",[21,6056,6057],{"class":23,"line":206},[21,6058,115],{},[21,6060,6061],{"class":23,"line":212},[21,6062,6063],{},"Widget(Widget&& rhs) = default;\n",[21,6065,6066],{"class":23,"line":218},[21,6067,6068],{},"Widget& operator=(Widget&& rhs) = default;\n",[280,6070,6071],{},"当一个Widget对象销毁时",[1420,6073,6074,6077,6080],{},[786,6075,6076],{},"会调用析构函数销毁pImpl",[786,6078,6079],{},"pImpl调用默认删除器",[786,6081,6082],{},"默认删除器使用delete释放原始指针所指向的空间",[280,6084,6085,6086,6089],{},"在默认删除器中，delete之前会调用",[18,6087,6088],{},"static_assert","来确保原始指针指向的类型不是一个未完成类型，\n因此应该在调用析构函数之前，让Impl为完整类型。即，将析构函数的定义写在Impl的定义下方",[280,6091,6092],{},"对于移动操作，需要销毁原来的对象，因此与析构相同",[1183,6094,6095],{},[280,6096,3875,6097,6099],{},[18,6098,5589],{},"来说，删除器的类型不是智能指针的一部分，在特殊函数(析构，移动)调用时，\n不需要指向的对象是完成类型",[53,6101,6103],{"id":6102},"chapter-5-rvalue-references-move-semantics-and-perfect-forwarding","CHAPTER 5 RValue References, Move Semantics and Perfect Forwarding",[1183,6105,6106],{},[280,6107,6108,6112,6113,6116],{},[6109,6110,6111],"strong",{},"参数","(parameter)永远是",[6109,6114,6115],{},"左值","(LValue)，即便它的类型是一个右值引用",[1297,6118,6120],{"id":6119},"条款23-理解stdmove和stdforward","条款23 理解std::move和std::forward",[11,6122,6124],{"className":13,"code":6123,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\ntypename remove_reference\u003CT>::type&& move(T&& param)\n{\n  return static_cast\u003Cremove_reference\u003CT>::type&&>(param);\n}\n",[18,6125,6126,6130,6135,6139,6144],{"__ignoreMap":16},[21,6127,6128],{"class":23,"line":24},[21,6129,4705],{},[21,6131,6132],{"class":23,"line":30},[21,6133,6134],{},"typename remove_reference\u003CT>::type&& move(T&& param)\n",[21,6136,6137],{"class":23,"line":36},[21,6138,2296],{},[21,6140,6141],{"class":23,"line":42},[21,6142,6143],{},"  return static_cast\u003Cremove_reference\u003CT>::type&&>(param);\n",[21,6145,6146],{"class":23,"line":48},[21,6147,115],{},[11,6149,6151],{"className":13,"code":6150,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid Foo(T&& param)\n{\n  Test(std::forward\u003CT>(param))\n}\n",[18,6152,6153,6157,6162,6166,6171],{"__ignoreMap":16},[21,6154,6155],{"class":23,"line":24},[21,6156,4705],{},[21,6158,6159],{"class":23,"line":30},[21,6160,6161],{},"void Foo(T&& param)\n",[21,6163,6164],{"class":23,"line":36},[21,6165,2296],{},[21,6167,6168],{"class":23,"line":42},[21,6169,6170],{},"  Test(std::forward\u003CT>(param))\n",[21,6172,6173],{"class":23,"line":48},[21,6174,115],{},[280,6176,6177],{},"在对通用引用转发时，实参无论是左值还是右值，都会被T&&接受，但当接收右值时，param的类型为\n右值引用，但此时param为一个左值，因此forward匹配的仍然是形参为左值引用的重载",[11,6179,6181],{"className":13,"code":6180,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nT&& forward(remove_reference\u003CT>::type& param)\n{\n  return static_cast\u003CT&&>(param);\n}\n",[18,6182,6183,6187,6192,6196,6201],{"__ignoreMap":16},[21,6184,6185],{"class":23,"line":24},[21,6186,1200],{},[21,6188,6189],{"class":23,"line":30},[21,6190,6191],{},"T&& forward(remove_reference\u003CT>::type& param)\n",[21,6193,6194],{"class":23,"line":36},[21,6195,2296],{},[21,6197,6198],{"class":23,"line":42},[21,6199,6200],{},"  return static_cast\u003CT&&>(param);\n",[21,6202,6203],{"class":23,"line":48},[21,6204,115],{},[280,6206,6207],{},"该重载只有在传入右值的时候才会匹配，并且返回右值",[11,6209,6211],{"className":13,"code":6210,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nT&& forward(remove_reference\u003CT>::type&& param)\n{\n  return static_cast\u003CT&&>(param);\n}\n",[18,6212,6213,6217,6222,6226,6230],{"__ignoreMap":16},[21,6214,6215],{"class":23,"line":24},[21,6216,1200],{},[21,6218,6219],{"class":23,"line":30},[21,6220,6221],{},"T&& forward(remove_reference\u003CT>::type&& param)\n",[21,6223,6224],{"class":23,"line":36},[21,6225,2296],{},[21,6227,6228],{"class":23,"line":42},[21,6229,6200],{},[21,6231,6232],{"class":23,"line":48},[21,6233,115],{},[1297,6235,6237],{"id":6236},"条款24-区分通用引用与右值引用","条款24 区分通用引用与右值引用",[783,6239,6240,6243],{},[786,6241,6242],{},"如果一个函数模板参数的类型为T&&，并且T需要被推导得知，或者如果一个对象被声明为auto&&，这个\n参数或者对象就是一个通用引用",[786,6244,6245,6246,6251,6287,6289],{},"如果类型声明的形式不是标准的type&&，或者如果类型推导没有发生，那么type&&代表一个右值引用",[1183,6247,6248],{},[280,6249,6250],{},"模板里面的函数参数类型为T&&，并不一定会发生类型推导",[11,6252,6254],{"className":13,"code":6253,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nclass vector\n{\npublic:\n  void push_back(T&& param);\n};\nstd::vector\u003CWidget> v;\n",[18,6255,6256,6260,6265,6269,6273,6278,6282],{"__ignoreMap":16},[21,6257,6258],{"class":23,"line":24},[21,6259,1200],{},[21,6261,6262],{"class":23,"line":30},[21,6263,6264],{},"class vector\n",[21,6266,6267],{"class":23,"line":36},[21,6268,2296],{},[21,6270,6271],{"class":23,"line":42},[21,6272,1347],{},[21,6274,6275],{"class":23,"line":48},[21,6276,6277],{},"  void push_back(T&& param);\n",[21,6279,6280],{"class":23,"line":88},[21,6281,51],{},[21,6283,6284],{"class":23,"line":94},[21,6285,6286],{},"std::vector\u003CWidget> v;\n",[1447,6288],{},"实例化vector时就确定了push_back的声明",[1297,6291,6293],{"id":6292},"条款25-对右值引用使用stdmove对通用引用使用stdforward","条款25 对右值引用使用std::move，对通用引用使用std::forward",[280,6295,6296],{},"按值返回的函数，并且返回值绑定到右值引用或通用引用上，需要对返回值的引用使用std::move或者\nstd::forward",[280,6298,6299],{},"lhs为左值，返回lhs会拷贝到返回值的内存空间，而如果lhs支持移动，使用std::move效率更高",[11,6301,6303],{"className":13,"code":6302,"language":15,"meta":16,"style":16},"Matrix operator+(Matrix&& lhs, const Matrix& rhs)\n{\n  lhs += rhs;\n  \u002F\u002F return lhs;\n  return std::move(lhs);\n}\n",[18,6304,6305,6310,6314,6319,6324,6329],{"__ignoreMap":16},[21,6306,6307],{"class":23,"line":24},[21,6308,6309],{},"Matrix operator+(Matrix&& lhs, const Matrix& rhs)\n",[21,6311,6312],{"class":23,"line":30},[21,6313,2296],{},[21,6315,6316],{"class":23,"line":36},[21,6317,6318],{},"  lhs += rhs;\n",[21,6320,6321],{"class":23,"line":42},[21,6322,6323],{},"  \u002F\u002F return lhs;\n",[21,6325,6326],{"class":23,"line":48},[21,6327,6328],{},"  return std::move(lhs);\n",[21,6330,6331],{"class":23,"line":88},[21,6332,115],{},[280,6334,6335],{},"如果不带std::forward，frac类型为右值引用时，frac仍然为左值，return仍需要拷贝",[11,6337,6339],{"className":13,"code":6338,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nFraction reduceAndCopy(T&& frac)\n{\n  ...\n  return std::forward\u003CT>(frac);\n}\n",[18,6340,6341,6345,6350,6354,6358,6363],{"__ignoreMap":16},[21,6342,6343],{"class":23,"line":24},[21,6344,1200],{},[21,6346,6347],{"class":23,"line":30},[21,6348,6349],{},"Fraction reduceAndCopy(T&& frac)\n",[21,6351,6352],{"class":23,"line":36},[21,6353,2296],{},[21,6355,6356],{"class":23,"line":42},[21,6357,2311],{},[21,6359,6360],{"class":23,"line":48},[21,6361,6362],{},"  return std::forward\u003CT>(frac);\n",[21,6364,6365],{"class":23,"line":88},[21,6366,115],{},[280,6368,6369],{},"C++标准存在返回值优化(RVO)，即直接在返回值的内存中构造，来避免复制，也称为Copy elision",[1183,6371,6372],{},[280,6373,6374],{},"具名返回值优化NRVO，返回具名的局部变量",[280,6376,6377],{},"返回值优化的条件：1.局部变量与返回值类型相同；2.局部变量就是返回值",[11,6379,6381],{"className":13,"code":6380,"language":15,"meta":16,"style":16},"Widget makeWidget()\n{\n  Widget w;\n  ...\n  \u002F\u002F 不满足要求2，std::move是对w的引用，而非局部变量，无法优化\n  \u002F\u002F return std::move(w);\n  return w;\n}\n",[18,6382,6383,6388,6392,6397,6401,6406,6411,6416],{"__ignoreMap":16},[21,6384,6385],{"class":23,"line":24},[21,6386,6387],{},"Widget makeWidget()\n",[21,6389,6390],{"class":23,"line":30},[21,6391,2296],{},[21,6393,6394],{"class":23,"line":36},[21,6395,6396],{},"  Widget w;\n",[21,6398,6399],{"class":23,"line":42},[21,6400,2311],{},[21,6402,6403],{"class":23,"line":48},[21,6404,6405],{},"  \u002F\u002F 不满足要求2，std::move是对w的引用，而非局部变量，无法优化\n",[21,6407,6408],{"class":23,"line":88},[21,6409,6410],{},"  \u002F\u002F return std::move(w);\n",[21,6412,6413],{"class":23,"line":94},[21,6414,6415],{},"  return w;\n",[21,6417,6418],{"class":23,"line":100},[21,6419,115],{},[280,6421,6422],{},"如果满足RVO的条件，但编译器选择不执行复制忽略，则必须将返回对象视为右值。标准要求RVO，\n忽略复制或者将std::move隐式应用于返回的本地对象",[280,6424,6425,6426,6429],{},"因此上述代码中，",[18,6427,6428],{},"return w;","如果不执行复制忽略的优化，就会自动将std::move隐式执行",[280,6431,6432],{},"按值传递参数的情况于此类似，它们没有RVO的资格，但是如果作为返回值，编译器会将其视为右值",[11,6434,6436],{"className":13,"code":6435,"language":15,"meta":16,"style":16},"Widget makeWidget(Widget w)\n{\n  return w;\n  \u002F\u002F 实际上，编译器的代码为\n  \u002F\u002F return std::move(w);\n}\n",[18,6437,6438,6443,6447,6451,6456,6460],{"__ignoreMap":16},[21,6439,6440],{"class":23,"line":24},[21,6441,6442],{},"Widget makeWidget(Widget w)\n",[21,6444,6445],{"class":23,"line":30},[21,6446,2296],{},[21,6448,6449],{"class":23,"line":36},[21,6450,6415],{},[21,6452,6453],{"class":23,"line":42},[21,6454,6455],{},"  \u002F\u002F 实际上，编译器的代码为\n",[21,6457,6458],{"class":23,"line":48},[21,6459,6410],{},[21,6461,6462],{"class":23,"line":88},[21,6463,115],{},[1297,6465,6467],{"id":6466},"条款26-避免在通用引用上重载","条款26 避免在通用引用上重载",[11,6469,6471],{"className":13,"code":6470,"language":15,"meta":16,"style":16},"class Person\n{\npublic:\n  template\u003Ctypename T>\n  Person(T&& n) : name(std::forward\u003CT>(n)) {} \u002F\u002F 通过名字构造\n  Person(int id);                             \u002F\u002F 通过id构造\n  Person(const Person&);\n  Person(Person&&);\n};\n\nPerson p1(\"Nancy\");\nPerson p2(p1);  \u002F\u002F 调用的是通用引用的构造函数，而非拷贝构造\nconst Person p3(\"aaa\");\nPerson p4(p3);  \u002F\u002F 正常匹配拷贝构造\n",[18,6472,6473,6478,6482,6486,6491,6496,6501,6506,6511,6515,6519,6524,6529,6534],{"__ignoreMap":16},[21,6474,6475],{"class":23,"line":24},[21,6476,6477],{},"class Person\n",[21,6479,6480],{"class":23,"line":30},[21,6481,2296],{},[21,6483,6484],{"class":23,"line":36},[21,6485,1347],{},[21,6487,6488],{"class":23,"line":42},[21,6489,6490],{},"  template\u003Ctypename T>\n",[21,6492,6493],{"class":23,"line":48},[21,6494,6495],{},"  Person(T&& n) : name(std::forward\u003CT>(n)) {} \u002F\u002F 通过名字构造\n",[21,6497,6498],{"class":23,"line":88},[21,6499,6500],{},"  Person(int id);                             \u002F\u002F 通过id构造\n",[21,6502,6503],{"class":23,"line":94},[21,6504,6505],{},"  Person(const Person&);\n",[21,6507,6508],{"class":23,"line":100},[21,6509,6510],{},"  Person(Person&&);\n",[21,6512,6513],{"class":23,"line":106},[21,6514,51],{},[21,6516,6517],{"class":23,"line":112},[21,6518,144],{"emptyLinePlaceholder":143},[21,6520,6521],{"class":23,"line":176},[21,6522,6523],{},"Person p1(\"Nancy\");\n",[21,6525,6526],{"class":23,"line":182},[21,6527,6528],{},"Person p2(p1);  \u002F\u002F 调用的是通用引用的构造函数，而非拷贝构造\n",[21,6530,6531],{"class":23,"line":188},[21,6532,6533],{},"const Person p3(\"aaa\");\n",[21,6535,6536],{"class":23,"line":194},[21,6537,6538],{},"Person p4(p3);  \u002F\u002F 正常匹配拷贝构造\n",[11,6540,6542],{"className":13,"code":6541,"language":15,"meta":16,"style":16},"class Person;\nclass SpecialPerson : public Person\n{\npublic:\n  \u002F\u002F 调用的都是Person的通用引用的构造函数\n  SpecialPerson(const SpecialPerson& rhs) : Person(rhs) {}\n  SpecialPerson(SpecialPerson&& rhs) : Person(std::move(rhs)) {}\n};\n",[18,6543,6544,6549,6554,6558,6562,6567,6572,6577],{"__ignoreMap":16},[21,6545,6546],{"class":23,"line":24},[21,6547,6548],{},"class Person;\n",[21,6550,6551],{"class":23,"line":30},[21,6552,6553],{},"class SpecialPerson : public Person\n",[21,6555,6556],{"class":23,"line":36},[21,6557,2296],{},[21,6559,6560],{"class":23,"line":42},[21,6561,1347],{},[21,6563,6564],{"class":23,"line":48},[21,6565,6566],{},"  \u002F\u002F 调用的都是Person的通用引用的构造函数\n",[21,6568,6569],{"class":23,"line":88},[21,6570,6571],{},"  SpecialPerson(const SpecialPerson& rhs) : Person(rhs) {}\n",[21,6573,6574],{"class":23,"line":94},[21,6575,6576],{},"  SpecialPerson(SpecialPerson&& rhs) : Person(std::move(rhs)) {}\n",[21,6578,6579],{"class":23,"line":100},[21,6580,51],{},[280,6582,6583],{},"完美转发构造函数是糟糕的实现，因为对于non-const左值不会调用构造函数而是完美转发构造，\n而且会劫持派生类对于基类的拷贝和移动构造",[1297,6585,6587],{"id":6586},"条款27-熟悉通用引用重载的替代方法","条款27 熟悉通用引用重载的替代方法",[2208,6589,6591],{"id":6590},"abandon-overloading","Abandon overloading",[280,6593,6594],{},"重载虽然可以区分std::string与int，而采用不同的处理方式，但如果需要类型转换时，可能不能\n起到期望的结果",[11,6596,6598],{"className":13,"code":6597,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid LogAndAdd(T&& name)\n{\n  names.emplace(std::forward\u003CT>(name));\n}\nvoid LogAndAdd(int id)\n{\n  names.emplace(GetNameById(id));\n}\n",[18,6599,6600,6604,6609,6613,6618,6622,6627,6631,6636],{"__ignoreMap":16},[21,6601,6602],{"class":23,"line":24},[21,6603,4705],{},[21,6605,6606],{"class":23,"line":30},[21,6607,6608],{},"void LogAndAdd(T&& name)\n",[21,6610,6611],{"class":23,"line":36},[21,6612,2296],{},[21,6614,6615],{"class":23,"line":42},[21,6616,6617],{},"  names.emplace(std::forward\u003CT>(name));\n",[21,6619,6620],{"class":23,"line":48},[21,6621,115],{},[21,6623,6624],{"class":23,"line":88},[21,6625,6626],{},"void LogAndAdd(int id)\n",[21,6628,6629],{"class":23,"line":94},[21,6630,2296],{},[21,6632,6633],{"class":23,"line":100},[21,6634,6635],{},"  names.emplace(GetNameById(id));\n",[21,6637,6638],{"class":23,"line":106},[21,6639,115],{},[280,6641,6642],{},"此时如果实参类型为short，模板函数的优先级大于形参为int的重载",[280,6644,6645],{},"因此可以不使用重载，分别改函数名为logAndAddName和logAndAddId，但是如果是构造函数的话，\n就无法使用该方法",[2208,6647,6649],{"id":6648},"pass-by-const-t","Pass by const T&",[2208,6651,6653],{"id":6652},"pass-by-value","Pass by value",[2208,6655,6657],{"id":6656},"use-tag-dispatch","Use Tag dispatch",[11,6659,6661],{"className":13,"code":6660,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid LogAndAdd(T&& name)\n{\n  LogAndAddImpl(std::forward\u003CT>(name), std::is_integral\u003Cstd::remove_reference_t\u003CT>>());\n}\ntemplate\u003Ctypename T>\nvoid LogAndAddImpl(T&& name, std::false_type)\n{\n  ...\n}\nvoid LogAndAddImpl(int id, std::true_type)\n{\n  ...\n}\n",[18,6662,6663,6667,6671,6675,6680,6684,6688,6693,6697,6701,6705,6710,6714,6718],{"__ignoreMap":16},[21,6664,6665],{"class":23,"line":24},[21,6666,4705],{},[21,6668,6669],{"class":23,"line":30},[21,6670,6608],{},[21,6672,6673],{"class":23,"line":36},[21,6674,2296],{},[21,6676,6677],{"class":23,"line":42},[21,6678,6679],{},"  LogAndAddImpl(std::forward\u003CT>(name), std::is_integral\u003Cstd::remove_reference_t\u003CT>>());\n",[21,6681,6682],{"class":23,"line":48},[21,6683,115],{},[21,6685,6686],{"class":23,"line":88},[21,6687,4705],{},[21,6689,6690],{"class":23,"line":94},[21,6691,6692],{},"void LogAndAddImpl(T&& name, std::false_type)\n",[21,6694,6695],{"class":23,"line":100},[21,6696,2296],{},[21,6698,6699],{"class":23,"line":106},[21,6700,2311],{},[21,6702,6703],{"class":23,"line":112},[21,6704,115],{},[21,6706,6707],{"class":23,"line":176},[21,6708,6709],{},"void LogAndAddImpl(int id, std::true_type)\n",[21,6711,6712],{"class":23,"line":182},[21,6713,2296],{},[21,6715,6716],{"class":23,"line":188},[21,6717,2311],{},[21,6719,6720],{"class":23,"line":194},[21,6721,115],{},[2208,6723,6725],{"id":6724},"constraining-templates-that-take-universal-references","Constraining templates that take universal references",[11,6727,6729],{"className":13,"code":6728,"language":15,"meta":16,"style":16},"class Person\n{\npublic:\n  template\u003Ctypename T, typename = std::enable_if_t\u003C\n    !std::is_base_of_v\u003CPerson, std::decay_t\u003CT>>\n    &&\n    !std::is_integral_v\u003Cstd::remove_reference\u003CT>>\n    >\n  >\n  Person(T&& n): name(std::forward\u003CT>(n)) {...}\n  Person(int id): name(GetNameById(id)) {...}\n};\n",[18,6730,6731,6735,6739,6743,6748,6753,6758,6763,6768,6773,6778,6783],{"__ignoreMap":16},[21,6732,6733],{"class":23,"line":24},[21,6734,6477],{},[21,6736,6737],{"class":23,"line":30},[21,6738,2296],{},[21,6740,6741],{"class":23,"line":36},[21,6742,1347],{},[21,6744,6745],{"class":23,"line":42},[21,6746,6747],{},"  template\u003Ctypename T, typename = std::enable_if_t\u003C\n",[21,6749,6750],{"class":23,"line":48},[21,6751,6752],{},"    !std::is_base_of_v\u003CPerson, std::decay_t\u003CT>>\n",[21,6754,6755],{"class":23,"line":88},[21,6756,6757],{},"    &&\n",[21,6759,6760],{"class":23,"line":94},[21,6761,6762],{},"    !std::is_integral_v\u003Cstd::remove_reference\u003CT>>\n",[21,6764,6765],{"class":23,"line":100},[21,6766,6767],{},"    >\n",[21,6769,6770],{"class":23,"line":106},[21,6771,6772],{},"  >\n",[21,6774,6775],{"class":23,"line":112},[21,6776,6777],{},"  Person(T&& n): name(std::forward\u003CT>(n)) {...}\n",[21,6779,6780],{"class":23,"line":176},[21,6781,6782],{},"  Person(int id): name(GetNameById(id)) {...}\n",[21,6784,6785],{"class":23,"line":182},[21,6786,51],{},[2208,6788,6790],{"id":6789},"trade-offs","Trade-offs",[280,6792,6793,6794,6797],{},"使用Person(u\"hello\")，其中实参为",[18,6795,6796],{},"const char16_t","，而不是char，此时调用的是通用引用的构造函数，\n但其无法转换为std::string，因此需要提示错误信息",[11,6799,6801],{"className":13,"code":6800,"language":15,"meta":16,"style":16},"static_assert(std::is_constructible\u003Cstd::string, T>::value, \"message\");\n",[18,6802,6803],{"__ignoreMap":16},[21,6804,6805],{"class":23,"line":24},[21,6806,6800],{},[1297,6808,6810],{"id":6809},"条款28-理解引用折叠","条款28 理解引用折叠",[1297,6812,6814],{"id":6813},"条款29-移动语义的缺点","条款29 移动语义的缺点",[1297,6816,6818],{"id":6817},"条款30-熟悉完美转发的失败情况","条款30 熟悉完美转发的失败情况",[11,6820,6822],{"className":13,"code":6821,"language":15,"meta":16,"style":16},"template\u003Ctypename... Ts>\nvoid fwd(Ts&&... params)\n{\n  f(std::forward\u003CTs>(params)...);\n}\n",[18,6823,6824,6829,6834,6838,6843],{"__ignoreMap":16},[21,6825,6826],{"class":23,"line":24},[21,6827,6828],{},"template\u003Ctypename... Ts>\n",[21,6830,6831],{"class":23,"line":30},[21,6832,6833],{},"void fwd(Ts&&... params)\n",[21,6835,6836],{"class":23,"line":36},[21,6837,2296],{},[21,6839,6840],{"class":23,"line":42},[21,6841,6842],{},"  f(std::forward\u003CTs>(params)...);\n",[21,6844,6845],{"class":23,"line":48},[21,6846,115],{},[2208,6848,6850],{"id":6849},"braced-initializers","Braced initializers",[11,6852,6854],{"className":13,"code":6853,"language":15,"meta":16,"style":16},"void f(const std::vector\u003Cint>& v);\nf({1, 2 , 3});  \u002F\u002F 隐式转换为std::vector\u003Cint>\nfwd({1, 2, 3}); \u002F\u002F 无法编译\n",[18,6855,6856,6861,6866],{"__ignoreMap":16},[21,6857,6858],{"class":23,"line":24},[21,6859,6860],{},"void f(const std::vector\u003Cint>& v);\n",[21,6862,6863],{"class":23,"line":30},[21,6864,6865],{},"f({1, 2 , 3});  \u002F\u002F 隐式转换为std::vector\u003Cint>\n",[21,6867,6868],{"class":23,"line":36},[21,6869,6870],{},"fwd({1, 2, 3}); \u002F\u002F 无法编译\n",[280,6872,6873,6874,6876,6877],{},"条款2中提到，在对fwd的调用中的{1, 2, 3}进行类型推导时，由于fwd的参数没有声明为",[18,6875,5084],{},"，\n无法匹配。但auto却可以通过braced initializer推导出",[18,6878,5084],{},[11,6880,6882],{"className":13,"code":6881,"language":15,"meta":16,"style":16},"auto il = {1, 2, 3}; \u002F\u002F il为std::initializer_list\u003Cint>类型\nfwd(il);\n",[18,6883,6884,6889],{"__ignoreMap":16},[21,6885,6886],{"class":23,"line":24},[21,6887,6888],{},"auto il = {1, 2, 3}; \u002F\u002F il为std::initializer_list\u003Cint>类型\n",[21,6890,6891],{"class":23,"line":30},[21,6892,6893],{},"fwd(il);\n",[2208,6895,6897],{"id":6896},"_0或者null作为空指针","0或者NULL作为空指针",[2208,6899,6900],{"id":6900},"仅声明的整数静态const数据成员",[11,6902,6904],{"className":13,"code":6903,"language":15,"meta":16,"style":16},"class Widget\n{\npublic:\n  static const std::size_t Minvals = 28;\n};\nvoid f(std::size_t val);\nfwd(Widget::Minvals);  \u002F\u002F 通过编译，但由于Minvals没有定义，无法链接\n",[18,6905,6906,6910,6914,6918,6923,6927,6932],{"__ignoreMap":16},[21,6907,6908],{"class":23,"line":24},[21,6909,5322],{},[21,6911,6912],{"class":23,"line":30},[21,6913,2296],{},[21,6915,6916],{"class":23,"line":36},[21,6917,1347],{},[21,6919,6920],{"class":23,"line":42},[21,6921,6922],{},"  static const std::size_t Minvals = 28;\n",[21,6924,6925],{"class":23,"line":48},[21,6926,51],{},[21,6928,6929],{"class":23,"line":88},[21,6930,6931],{},"void f(std::size_t val);\n",[21,6933,6934],{"class":23,"line":94},[21,6935,6936],{},"fwd(Widget::Minvals);  \u002F\u002F 通过编译，但由于Minvals没有定义，无法链接\n",[280,6938,6939,6940,6942],{},"fwd的参数是通用引用，底层中，引用与指针是一样的，即通过引用传递Minvals实际上与使用指针转递\nMinvals一样。但是static const在类中仅仅只是声明，而声明是不会分配内存的，即无法被指针指向。",[1447,6941],{},"\n从而，通过引用传递整型static const数据成员，必须定义它们",[1183,6944,6945],{},[280,6946,6947],{},"只是要求定义，并不是强制，因为有的编译器允许未定义的情况",[2208,6949,6950],{"id":6950},"重载的函数名称和模板名称",[11,6952,6954],{"className":13,"code":6953,"language":15,"meta":16,"style":16},"int Foo(int x);\nint Foo(int x, int y);\n\nf(int (*)(int));\nfwd(Foo); \u002F\u002F 无法判断选择哪个Foo\n",[18,6955,6956,6961,6966,6970,6975],{"__ignoreMap":16},[21,6957,6958],{"class":23,"line":24},[21,6959,6960],{},"int Foo(int x);\n",[21,6962,6963],{"class":23,"line":30},[21,6964,6965],{},"int Foo(int x, int y);\n",[21,6967,6968],{"class":23,"line":36},[21,6969,144],{"emptyLinePlaceholder":143},[21,6971,6972],{"class":23,"line":42},[21,6973,6974],{},"f(int (*)(int));\n",[21,6976,6977],{"class":23,"line":48},[21,6978,6979],{},"fwd(Foo); \u002F\u002F 无法判断选择哪个Foo\n",[11,6981,6983],{"className":13,"code":6982,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nT Foo(T param) {...}\nfwd(Foo) \u002F\u002F 无法判断那个Foo\n",[18,6984,6985,6989,6994],{"__ignoreMap":16},[21,6986,6987],{"class":23,"line":24},[21,6988,4705],{},[21,6990,6991],{"class":23,"line":30},[21,6992,6993],{},"T Foo(T param) {...}\n",[21,6995,6996],{"class":23,"line":36},[21,6997,6998],{},"fwd(Foo) \u002F\u002F 无法判断那个Foo\n",[2208,7000,7001],{"id":7001},"位域",[53,7003,7005],{"id":7004},"chapter-6-lambda表达式","CHAPTER 6 Lambda表达式",[1297,7007,7009],{"id":7008},"条款31-避免使用默认捕获模式","条款31 避免使用默认捕获模式",[280,7011,7012],{},"闭包只会对lambda被创建时所在的作用域里的非静态局部变量生效，因此不能捕获成员变量",[11,7014,7016],{"className":13,"code":7015,"language":15,"meta":16,"style":16},"class Widget\n{\nprivate:\n  int m_value;\npublic:\n  void Test()\n  {\n    auto f = [=]() {return m_value;};\n  }\n};\n",[18,7017,7018,7022,7026,7030,7035,7039,7044,7048,7053,7057],{"__ignoreMap":16},[21,7019,7020],{"class":23,"line":24},[21,7021,5322],{},[21,7023,7024],{"class":23,"line":30},[21,7025,2296],{},[21,7027,7028],{"class":23,"line":36},[21,7029,1316],{},[21,7031,7032],{"class":23,"line":42},[21,7033,7034],{},"  int m_value;\n",[21,7036,7037],{"class":23,"line":48},[21,7038,1347],{},[21,7040,7041],{"class":23,"line":88},[21,7042,7043],{},"  void Test()\n",[21,7045,7046],{"class":23,"line":94},[21,7047,2788],{},[21,7049,7050],{"class":23,"line":100},[21,7051,7052],{},"    auto f = [=]() {return m_value;};\n",[21,7054,7055],{"class":23,"line":106},[21,7056,103],{},[21,7058,7059],{"class":23,"line":112},[21,7060,51],{},[280,7062,7063,7064,7067,7068],{},"在成员函数类，相当于隐式捕获this，lambda中的",[18,7065,7066],{},"m_value","是",[18,7069,7070],{},"this->m_value",[11,7072,7074],{"className":13,"code":7073,"language":15,"meta":16,"style":16},"void Widget::Test()\n{\n  auto current_object_ptr = this;\n  auto f = [current_object_ptr](){return current_object_ptr->m_value;};\n}\n",[18,7075,7076,7081,7085,7090,7095],{"__ignoreMap":16},[21,7077,7078],{"class":23,"line":24},[21,7079,7080],{},"void Widget::Test()\n",[21,7082,7083],{"class":23,"line":30},[21,7084,2296],{},[21,7086,7087],{"class":23,"line":36},[21,7088,7089],{},"  auto current_object_ptr = this;\n",[21,7091,7092],{"class":23,"line":42},[21,7093,7094],{},"  auto f = [current_object_ptr](){return current_object_ptr->m_value;};\n",[21,7096,7097],{"class":23,"line":48},[21,7098,115],{},[280,7100,7101],{},"定义在全局空间或者指定命名空间的全局变量，或者是一个声明为static的类内或文件内的成员。\n这些对象也能在lambda中使用，但它们不能被捕获",[11,7103,7105],{"className":13,"code":7104,"language":15,"meta":16,"style":16},"static int a = 1;\nauto f = [=](){return a;};\n++a;\n",[18,7106,7107,7112,7117],{"__ignoreMap":16},[21,7108,7109],{"class":23,"line":24},[21,7110,7111],{},"static int a = 1;\n",[21,7113,7114],{"class":23,"line":30},[21,7115,7116],{},"auto f = [=](){return a;};\n",[21,7118,7119],{"class":23,"line":36},[21,7120,7121],{},"++a;\n",[280,7123,7124],{},"虽然按值捕获，但并不能捕获a，在调用f()时，返回的是++a后的值，相当于是按引用捕获。\n因此，在开始时就应该避免使用默认的按值捕获模式，以免误解",[1297,7126,7128],{"id":7127},"条款32-使用初始化捕获来移动对象到闭包中","条款32 使用初始化捕获来移动对象到闭包中",[280,7130,7131],{},"C++ 14中可使用初始化捕获",[11,7133,7135],{"className":13,"code":7134,"language":15,"meta":16,"style":16},"class Widget；\nautp pw = std::make_unique\u003CWidget>();\nauto func = [pw = std::move(pw)] {...};\n",[18,7136,7137,7142,7147],{"__ignoreMap":16},[21,7138,7139],{"class":23,"line":24},[21,7140,7141],{},"class Widget；\n",[21,7143,7144],{"class":23,"line":30},[21,7145,7146],{},"autp pw = std::make_unique\u003CWidget>();\n",[21,7148,7149],{"class":23,"line":36},[21,7150,7151],{},"auto func = [pw = std::move(pw)] {...};\n",[280,7153,7154],{},"C++ 11中可替代方法",[783,7156,7157,7223],{},[786,7158,7159,7160],{},"闭包类",[11,7161,7163],{"className":13,"code":7162,"language":15,"meta":16,"style":16},"class Entity\n{\npublic:\n  explicit Entity(std::unique_ptr\u003CWidget>&& ptr) : pw(std::move(ptr)) {}\n  bool operator()() const\n  {\n    ...\n  }\nprivate:\n  std::unique_ptr pw;\n};\n\nauto func = Entity(std::make_unique\u003CWidget>());\n",[18,7164,7165,7170,7174,7178,7183,7188,7192,7197,7201,7205,7210,7214,7218],{"__ignoreMap":16},[21,7166,7167],{"class":23,"line":24},[21,7168,7169],{},"class Entity\n",[21,7171,7172],{"class":23,"line":30},[21,7173,2296],{},[21,7175,7176],{"class":23,"line":36},[21,7177,1347],{},[21,7179,7180],{"class":23,"line":42},[21,7181,7182],{},"  explicit Entity(std::unique_ptr\u003CWidget>&& ptr) : pw(std::move(ptr)) {}\n",[21,7184,7185],{"class":23,"line":48},[21,7186,7187],{},"  bool operator()() const\n",[21,7189,7190],{"class":23,"line":88},[21,7191,2788],{},[21,7193,7194],{"class":23,"line":94},[21,7195,7196],{},"    ...\n",[21,7198,7199],{"class":23,"line":100},[21,7200,103],{},[21,7202,7203],{"class":23,"line":106},[21,7204,1316],{},[21,7206,7207],{"class":23,"line":112},[21,7208,7209],{},"  std::unique_ptr pw;\n",[21,7211,7212],{"class":23,"line":176},[21,7213,51],{},[21,7215,7216],{"class":23,"line":182},[21,7217,144],{"emptyLinePlaceholder":143},[21,7219,7220],{"class":23,"line":188},[21,7221,7222],{},"auto func = Entity(std::make_unique\u003CWidget>());\n",[786,7224,7225,7226],{},"std::bind",[11,7227,7229],{"className":13,"code":7228,"language":15,"meta":16,"style":16},"using UniquePtr = std::unique_ptr\u003CWidget>;\nUniquePtr pw = std::make_unique\u003CWidget>();\nauto func = std::bind([](const UniquePtr& ptr){...}, pw);\n",[18,7230,7231,7236,7241],{"__ignoreMap":16},[21,7232,7233],{"class":23,"line":24},[21,7234,7235],{},"using UniquePtr = std::unique_ptr\u003CWidget>;\n",[21,7237,7238],{"class":23,"line":30},[21,7239,7240],{},"UniquePtr pw = std::make_unique\u003CWidget>();\n",[21,7242,7243],{"class":23,"line":36},[21,7244,7245],{},"auto func = std::bind([](const UniquePtr& ptr){...}, pw);\n",[1297,7247,7249],{"id":7248},"条款33-对于stdforward的auto形参使用decltype","条款33 对于std::forward的auto&&形参使用decltype",[11,7251,7253],{"className":13,"code":7252,"language":15,"meta":16,"style":16},"auto f =\n  [](auto&&... params)\n  {\n    return func(normailized(std::forward\u003Cdecltype(params)>(params)...));\n  }\n",[18,7254,7255,7260,7265,7269,7274],{"__ignoreMap":16},[21,7256,7257],{"class":23,"line":24},[21,7258,7259],{},"auto f =\n",[21,7261,7262],{"class":23,"line":30},[21,7263,7264],{},"  [](auto&&... params)\n",[21,7266,7267],{"class":23,"line":36},[21,7268,2788],{},[21,7270,7271],{"class":23,"line":42},[21,7272,7273],{},"    return func(normailized(std::forward\u003Cdecltype(params)>(params)...));\n",[21,7275,7276],{"class":23,"line":48},[21,7277,103],{},[1297,7279,7281],{"id":7280},"条款34-考虑lambda表达式而非stdbind","条款34 考虑lambda表达式而非std::bind",[783,7283,7284,7287],{},[786,7285,7286],{},"与使用std::bind相比，Lambda更易读，更具有表达力并且可能更高效",[786,7288,7289],{},"只有在C++11中，std::bind可能对实现移动捕获或使用模板化函数调用运算符来绑定对象时会很有用",[53,7291,7293],{"id":7292},"chapter-7-并发api","CHAPTER 7 并发API",[1297,7295,7297],{"id":7296},"条款35-优先基于任务编程而不是基于线程","条款35 优先基于任务编程而不是基于线程",[783,7299,7300,7306,7309],{},[786,7301,7302,7305],{},[18,7303,7304],{},"std::thread","不能直接访问异步执行的结果，如果执行函数有异常抛出，代码会终止执行",[786,7307,7308],{},"基于线程的编程方式关于解决资源超限，负载均衡的方案移植性不佳",[786,7310,7311,7313],{},[18,7312,3216],{},"会默认解决上面问题",[1297,7315,7317],{"id":7316},"条款36-确保在异步为必须时才指定stdlaunchasync","条款36 确保在异步为必须时，才指定std::launch::async",[280,7319,5796,7320,7323,7324,7327],{},[18,7321,7322],{},"wait_for()"," or ",[18,7325,7326],{},"wait_until()","时考虑deferred状态",[11,7329,7331],{"className":13,"code":7330,"language":15,"meta":16,"style":16},"void Foo();\nauto f = std::async(Foo);\n\u002F\u002F 当std::async使用std::launch::deferred时，会死循环\nwhile(f.wait_for(1s) != std::future_status::ready) {\n  \u002F\u002F ...\n}\n",[18,7332,7333,7338,7343,7348,7353,7358],{"__ignoreMap":16},[21,7334,7335],{"class":23,"line":24},[21,7336,7337],{},"void Foo();\n",[21,7339,7340],{"class":23,"line":30},[21,7341,7342],{},"auto f = std::async(Foo);\n",[21,7344,7345],{"class":23,"line":36},[21,7346,7347],{},"\u002F\u002F 当std::async使用std::launch::deferred时，会死循环\n",[21,7349,7350],{"class":23,"line":42},[21,7351,7352],{},"while(f.wait_for(1s) != std::future_status::ready) {\n",[21,7354,7355],{"class":23,"line":48},[21,7356,7357],{},"  \u002F\u002F ...\n",[21,7359,7360],{"class":23,"line":88},[21,7361,115],{},[280,7363,7364],{},"可以先判断一下是否为deferred",[11,7366,7368],{"className":13,"code":7367,"language":15,"meta":16,"style":16},"if(f.wait_for(0s) == std::future_status::deferred) {\n  \u002F\u002F ...\n} else {\n while(f.wait_for(1s) != std::future_status::ready) {\n  \u002F\u002F ...\n }\n}\n",[18,7369,7370,7375,7379,7384,7389,7393,7398],{"__ignoreMap":16},[21,7371,7372],{"class":23,"line":24},[21,7373,7374],{},"if(f.wait_for(0s) == std::future_status::deferred) {\n",[21,7376,7377],{"class":23,"line":30},[21,7378,7357],{},[21,7380,7381],{"class":23,"line":36},[21,7382,7383],{},"} else {\n",[21,7385,7386],{"class":23,"line":42},[21,7387,7388],{}," while(f.wait_for(1s) != std::future_status::ready) {\n",[21,7390,7391],{"class":23,"line":48},[21,7392,7357],{},[21,7394,7395],{"class":23,"line":88},[21,7396,7397],{}," }\n",[21,7399,7400],{"class":23,"line":94},[21,7401,115],{},[1297,7403,7405],{"id":7404},"条款37-从各个方面使得stdthreads-unjoinable","条款37: 从各个方面使得std::threads unjoinable",[11,7407,7409],{"className":13,"code":7408,"language":15,"meta":16,"style":16},"class ThreadRAII {\npublic:\n  enum class DtorAction{ join, detch };\n  ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}\n  ~ThreadRAII() {\n    if(t.joinable()) {\n      if(action == DtorAction::join) {\n        t.join();\n      } else {\n        t.detach();\n      }\n    }\n  }\n\n  ThreadRAII(ThreadRAII&&) = default;\n  ThreadRAII& operator=(ThreadRAII&&) = default;\n  std::thread& get() { return t; }\nprivate:\n  DtorAction action;\n  std::thread t;\n};\n",[18,7410,7411,7416,7420,7425,7430,7435,7440,7445,7450,7455,7460,7464,7468,7472,7476,7481,7486,7491,7495,7500,7505],{"__ignoreMap":16},[21,7412,7413],{"class":23,"line":24},[21,7414,7415],{},"class ThreadRAII {\n",[21,7417,7418],{"class":23,"line":30},[21,7419,1347],{},[21,7421,7422],{"class":23,"line":36},[21,7423,7424],{},"  enum class DtorAction{ join, detch };\n",[21,7426,7427],{"class":23,"line":42},[21,7428,7429],{},"  ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}\n",[21,7431,7432],{"class":23,"line":48},[21,7433,7434],{},"  ~ThreadRAII() {\n",[21,7436,7437],{"class":23,"line":88},[21,7438,7439],{},"    if(t.joinable()) {\n",[21,7441,7442],{"class":23,"line":94},[21,7443,7444],{},"      if(action == DtorAction::join) {\n",[21,7446,7447],{"class":23,"line":100},[21,7448,7449],{},"        t.join();\n",[21,7451,7452],{"class":23,"line":106},[21,7453,7454],{},"      } else {\n",[21,7456,7457],{"class":23,"line":112},[21,7458,7459],{},"        t.detach();\n",[21,7461,7462],{"class":23,"line":176},[21,7463,838],{},[21,7465,7466],{"class":23,"line":182},[21,7467,215],{},[21,7469,7470],{"class":23,"line":188},[21,7471,103],{},[21,7473,7474],{"class":23,"line":194},[21,7475,144],{"emptyLinePlaceholder":143},[21,7477,7478],{"class":23,"line":200},[21,7479,7480],{},"  ThreadRAII(ThreadRAII&&) = default;\n",[21,7482,7483],{"class":23,"line":206},[21,7484,7485],{},"  ThreadRAII& operator=(ThreadRAII&&) = default;\n",[21,7487,7488],{"class":23,"line":212},[21,7489,7490],{},"  std::thread& get() { return t; }\n",[21,7492,7493],{"class":23,"line":218},[21,7494,1316],{},[21,7496,7497],{"class":23,"line":224},[21,7498,7499],{},"  DtorAction action;\n",[21,7501,7502],{"class":23,"line":229},[21,7503,7504],{},"  std::thread t;\n",[21,7506,7507],{"class":23,"line":234},[21,7508,51],{},[280,7510,7511,7512,7515,7516,7519,7520,7523],{},"析构时执行",[18,7513,7514],{},"join()","可能导致性能异常，执行",[18,7517,7518],{},"detach()","可能导致bug(",[6109,7521,7522],{},"详情见原书",")，\n适当的解决方案是中断线程，详情见《C++ Concurrency in Action》9.2部分",[1297,7525,7527],{"id":7526},"条款38-关注不同线程句柄析构行为","条款38: 关注不同线程句柄析构行为",[1183,7529,7530],{},[280,7531,7532,7533,7536],{},"promise搭配future使用时，结果存储在",[6109,7534,7535],{},"共享状态","*(shared state)中，而共享状态通常\n是基于堆的对象",[783,7538,7539,7547],{},[786,7540,7541,7543,7544,7546],{},[18,7542,3206],{},"的正常析构行为就是销毁",[18,7545,3206],{},"本身的成员数据",[786,7548,7549,7550,7552,7553,7555],{},"最后一个引用",[18,7551,3216],{},"创建共享状态的",[18,7554,3206],{},"析构函数会在任务结束前block",[1297,7557,7559],{"id":7558},"条款39-对于一次性事件通讯考虑使用无返回-future","条款39: 对于一次性事件通讯考虑使用无返回 future",[280,7561,7562],{},"一个任务通知另一个异步任务执行的方法",[783,7564,7565,7581,7640],{},[786,7566,3052,7567,3052,7569,7571,7572],{},[1447,7568],{},[18,7570,3232],{},"语句存在虚假唤醒，即使条件变量没有被通知，也可能被唤醒，\n可使用Lambda解决",[11,7573,7575],{"className":13,"code":7574,"language":15,"meta":16,"style":16},"cv.wait(lk, []{return whether the event has occurred; });\n",[18,7576,7577],{"__ignoreMap":16},[21,7578,7579],{"class":23,"line":24},[21,7580,7574],{},[786,7582,7583,7584,7633,7635,7636,7639],{},"共享的boolean标志",[11,7585,7587],{"className":13,"code":7586,"language":15,"meta":16,"style":16},"void Foo1() {\n  std::atomic\u003Cbool> flag(false);\n  \u002F\u002F ...\n  flag = true;\n}\nvoid Foo2() {\n  \u002F\u002F ...\n  while(!false);\n  \u002F\u002F ...\n}\n",[18,7588,7589,7594,7599,7603,7607,7611,7616,7620,7625,7629],{"__ignoreMap":16},[21,7590,7591],{"class":23,"line":24},[21,7592,7593],{},"void Foo1() {\n",[21,7595,7596],{"class":23,"line":30},[21,7597,7598],{},"  std::atomic\u003Cbool> flag(false);\n",[21,7600,7601],{"class":23,"line":36},[21,7602,7357],{},[21,7604,7605],{"class":23,"line":42},[21,7606,3104],{},[21,7608,7609],{"class":23,"line":48},[21,7610,115],{},[21,7612,7613],{"class":23,"line":88},[21,7614,7615],{},"void Foo2() {\n",[21,7617,7618],{"class":23,"line":94},[21,7619,7357],{},[21,7621,7622],{"class":23,"line":100},[21,7623,7624],{},"  while(!false);\n",[21,7626,7627],{"class":23,"line":106},[21,7628,7357],{},[21,7630,7631],{"class":23,"line":112},[21,7632,115],{},[1447,7634],{},"无锁以及没有虚假唤醒，但是",[18,7637,7638],{},"Foo2()","会一直占用CPU",[786,7641,7642,7643,7699,7701,7702,1016,7705,7708,7709,7711,7712,7714,7715,7717],{},"promise与future",[11,7644,7646],{"className":13,"code":7645,"language":15,"meta":16,"style":16},"std::promise\u003Cvoid> p;\n\nvoid Foo1() {\n  \u002F\u002F ...\n  p.set_value();\n}\n\nvoid Foo2() {\n  \u002F\u002F ...\n  p.get_future().wait();\n  \u002F\u002F ...\n}\n",[18,7647,7648,7653,7657,7661,7665,7670,7674,7678,7682,7686,7691,7695],{"__ignoreMap":16},[21,7649,7650],{"class":23,"line":24},[21,7651,7652],{},"std::promise\u003Cvoid> p;\n",[21,7654,7655],{"class":23,"line":30},[21,7656,144],{"emptyLinePlaceholder":143},[21,7658,7659],{"class":23,"line":36},[21,7660,7593],{},[21,7662,7663],{"class":23,"line":42},[21,7664,7357],{},[21,7666,7667],{"class":23,"line":48},[21,7668,7669],{},"  p.set_value();\n",[21,7671,7672],{"class":23,"line":88},[21,7673,115],{},[21,7675,7676],{"class":23,"line":94},[21,7677,144],{"emptyLinePlaceholder":143},[21,7679,7680],{"class":23,"line":100},[21,7681,7615],{},[21,7683,7684],{"class":23,"line":106},[21,7685,7357],{},[21,7687,7688],{"class":23,"line":112},[21,7689,7690],{},"  p.get_future().wait();\n",[21,7692,7693],{"class":23,"line":176},[21,7694,7357],{},[21,7696,7697],{"class":23,"line":182},[21,7698,115],{},[1447,7700],{},"无锁，没有虚假唤醒，不会一直占用CPU，但",[18,7703,7704],{},"std::promise",[18,7706,7707],{},"std::future","间有共享\n状态，并且共享状态是动态分配的，会有分配与释放的开销",[1447,7710],{},"同时，",[18,7713,7704],{},"只能设置一次，即与",[18,7716,7707],{},"之间的通信是一次性的",[1297,7719,7721],{"id":7720},"条款40-对于并发请使用stdatomicvolatile用于特殊内存区","条款40: 对于并发请使用std::atomic、volatile用于特殊内存区",[783,7723,7724,7730],{},[786,7725,7726,7729],{},[18,7727,7728],{},"std::atomic","用在并发程序中",[786,7731,7732,7735],{},[18,7733,7734],{},"volatile","用于特殊内存的场景中，避免被编译器优化内存",[53,7737,7739],{"id":7738},"chapter-8-微调","CHAPTER 8 微调",[1297,7741,7743],{"id":7742},"条款41-如果参数可拷贝并且移动操作开销很低总是考虑直接按值传递","条款41 如果参数可拷贝并且移动操作开销很低，总是考虑直接按值传递",[11,7745,7747],{"className":13,"code":7746,"language":15,"meta":16,"style":16},"std::vector\u003Cstd::string> names;\n\n\u002F\u002F 1.重载 左值会由一次拷贝，右值由一次移动\nvoid AddName(const std::string& newName)\n{\n  names.push_back(newName);\n}\nvoid AddName(std::string&& newName)\n{\n  names.push_back(std::move(newName));\n}\n\n\u002F\u002F 2.通用模板 与重载开销相同\ntemplate\u003Ctypename T>\nvoid AddName(T&& newName)\n{\n  names.push_back(std::forward\u003CT>(newName));\n}\n\n\n\u002F\u002F 3.按值传递 左值参数，一次拷贝一次移动，右值参数，两次拷贝\nvoid AddName(std::string newName)\n{\n  names.push_back(std::move(newName));\n}\n",[18,7748,7749,7754,7758,7763,7768,7772,7777,7781,7786,7790,7795,7799,7803,7808,7812,7817,7821,7826,7830,7834,7838,7843,7848,7852,7856],{"__ignoreMap":16},[21,7750,7751],{"class":23,"line":24},[21,7752,7753],{},"std::vector\u003Cstd::string> names;\n",[21,7755,7756],{"class":23,"line":30},[21,7757,144],{"emptyLinePlaceholder":143},[21,7759,7760],{"class":23,"line":36},[21,7761,7762],{},"\u002F\u002F 1.重载 左值会由一次拷贝，右值由一次移动\n",[21,7764,7765],{"class":23,"line":42},[21,7766,7767],{},"void AddName(const std::string& newName)\n",[21,7769,7770],{"class":23,"line":48},[21,7771,2296],{},[21,7773,7774],{"class":23,"line":88},[21,7775,7776],{},"  names.push_back(newName);\n",[21,7778,7779],{"class":23,"line":94},[21,7780,115],{},[21,7782,7783],{"class":23,"line":100},[21,7784,7785],{},"void AddName(std::string&& newName)\n",[21,7787,7788],{"class":23,"line":106},[21,7789,2296],{},[21,7791,7792],{"class":23,"line":112},[21,7793,7794],{},"  names.push_back(std::move(newName));\n",[21,7796,7797],{"class":23,"line":176},[21,7798,115],{},[21,7800,7801],{"class":23,"line":182},[21,7802,144],{"emptyLinePlaceholder":143},[21,7804,7805],{"class":23,"line":188},[21,7806,7807],{},"\u002F\u002F 2.通用模板 与重载开销相同\n",[21,7809,7810],{"class":23,"line":194},[21,7811,4705],{},[21,7813,7814],{"class":23,"line":200},[21,7815,7816],{},"void AddName(T&& newName)\n",[21,7818,7819],{"class":23,"line":206},[21,7820,2296],{},[21,7822,7823],{"class":23,"line":212},[21,7824,7825],{},"  names.push_back(std::forward\u003CT>(newName));\n",[21,7827,7828],{"class":23,"line":218},[21,7829,115],{},[21,7831,7832],{"class":23,"line":224},[21,7833,144],{"emptyLinePlaceholder":143},[21,7835,7836],{"class":23,"line":229},[21,7837,144],{"emptyLinePlaceholder":143},[21,7839,7840],{"class":23,"line":234},[21,7841,7842],{},"\u002F\u002F 3.按值传递 左值参数，一次拷贝一次移动，右值参数，两次拷贝\n",[21,7844,7845],{"class":23,"line":240},[21,7846,7847],{},"void AddName(std::string newName)\n",[21,7849,7850],{"class":23,"line":246},[21,7851,2296],{},[21,7853,7854],{"class":23,"line":252},[21,7855,7794],{},[21,7857,7858],{"class":23,"line":1680},[21,7859,115],{},[280,7861,7862],{},[1292,7863,7864],{},"后面没看懂。。。",[783,7866,7867,7870,7873],{},[786,7868,7869],{},"对于可复制，移动开销低，而且无条件复制的参数，按值传递效率基本与按引用传递效率一致，而且\n易于实现，生成更少的目标代码",[786,7871,7872],{},"通过拷贝构造拷贝参数，可能比通过赋值拷贝开销大的多",[786,7874,7875],{},"按值转递会引起切片问题，所说不适合基类类型的参数",[1297,7877,7879],{"id":7878},"条款42-考虑使用emplacement代替insertion","条款42 考虑使用emplacement代替insertion",[280,7881,7882],{},[1292,7883,7884],{},"也没怎么看懂",[783,7886,7887,7890,7904],{},[786,7888,7889],{},"原则上，emplacement函数有时会比insertion函数高效，并且不会更差",[786,7891,7892,7893],{},"实际上，当执行如下操作时，emplacement函数更快",[1420,7894,7895,7898,7901],{},[786,7896,7897],{},"值被构造到容器中，而不是直接赋值",[786,7899,7900],{},"传入的类型与容器类型不一致",[786,7902,7903],{},"容器不拒绝已经存在的重复值",[786,7905,7906],{},"emplacement函数可能执行insertion函数拒绝的显示构造",[284,7908,286],{},{"title":16,"searchDepth":30,"depth":30,"links":7910},[7911,7917,7921,7934,7941,7951,7957,7965],{"id":4690,"depth":30,"text":4691,"children":7912},[7913,7914,7915,7916],{"id":4694,"depth":36,"text":4695},{"id":5075,"depth":36,"text":5076},{"id":5144,"depth":36,"text":5145},{"id":5187,"depth":36,"text":5188},{"id":5191,"depth":30,"text":5192,"children":7918},[7919,7920],{"id":5195,"depth":36,"text":5196},{"id":5199,"depth":36,"text":5200},{"id":5291,"depth":30,"text":5292,"children":7922},[7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933],{"id":5295,"depth":36,"text":5296},{"id":5373,"depth":36,"text":5374},{"id":5377,"depth":36,"text":5378},{"id":5477,"depth":36,"text":5478},{"id":5481,"depth":36,"text":5482},{"id":5493,"depth":36,"text":5494},{"id":5497,"depth":36,"text":5498},{"id":5501,"depth":36,"text":5502},{"id":5505,"depth":36,"text":5506},{"id":5545,"depth":36,"text":5546},{"id":5549,"depth":36,"text":5550},{"id":5553,"depth":30,"text":5554,"children":7935},[7936,7937,7938,7939,7940],{"id":5557,"depth":36,"text":5558},{"id":5593,"depth":36,"text":5594},{"id":5734,"depth":36,"text":5735},{"id":5738,"depth":36,"text":5739},{"id":5892,"depth":36,"text":5893},{"id":6102,"depth":30,"text":6103,"children":7942},[7943,7944,7945,7946,7947,7948,7949,7950],{"id":6119,"depth":36,"text":6120},{"id":6236,"depth":36,"text":6237},{"id":6292,"depth":36,"text":6293},{"id":6466,"depth":36,"text":6467},{"id":6586,"depth":36,"text":6587},{"id":6809,"depth":36,"text":6810},{"id":6813,"depth":36,"text":6814},{"id":6817,"depth":36,"text":6818},{"id":7004,"depth":30,"text":7005,"children":7952},[7953,7954,7955,7956],{"id":7008,"depth":36,"text":7009},{"id":7127,"depth":36,"text":7128},{"id":7248,"depth":36,"text":7249},{"id":7280,"depth":36,"text":7281},{"id":7292,"depth":30,"text":7293,"children":7958},[7959,7960,7961,7962,7963,7964],{"id":7296,"depth":36,"text":7297},{"id":7316,"depth":36,"text":7317},{"id":7404,"depth":36,"text":7405},{"id":7526,"depth":36,"text":7527},{"id":7558,"depth":36,"text":7559},{"id":7720,"depth":36,"text":7721},{"id":7738,"depth":30,"text":7739,"children":7966},[7967,7968],{"id":7742,"depth":36,"text":7743},{"id":7878,"depth":36,"text":7879},"2023-06-18",{},"\u002Fblog\u002Feffective-modern-cplusplus",{"title":4685,"description":294},"blog\u002Feffective-modern-cplusplus",[1175],"ADN43sXTlznATJOFVLq0JYFUzN-Uw74bs7lkPz59rwQ",{"id":7977,"title":7978,"body":7979,"date":11109,"description":294,"extension":295,"meta":11110,"navigation":143,"path":11111,"seo":11112,"stem":11113,"tags":11114,"__hash__":11115},"blog\u002Fblog\u002Feffective-cplusplus.md","Effective C++",{"type":8,"value":7980,"toc":11039},[7981,7985,7989,7992,8006,8010,8013,8053,8060,8090,8092,8121,8124,8151,8159,8163,8166,8240,8243,8266,8270,8286,8290,8294,8298,8302,8315,8319,8327,8331,8335,8339,8374,8540,8544,8552,8555,8559,8569,8573,8614,8661,8665,8682,8686,8690,8714,8720,8735,8738,8742,8746,8750,8774,8778,8782,8785,8788,8792,8795,8799,8857,8860,8888,8892,9159,9162,9166,9170,9174,9179,9183,9187,9191,9194,9198,9204,9208,9211,9270,9274,9278,9294,9298,9302,9370,9376,9380,9450,9454,9518,9522,9525,9529,9623,9626,9672,9679,9686,9690,9698,9702,9780,9804,9808,9812,9816,9881,9893,9908,9920,9970,9979,10104,10108,10243,10247,10250,10254,10260,10287,10292,10346,10349,10410,10416,10426,10503,10510,10578,10581,10609,10615,10625,10689,10696,10711,10716,10720,10724,10935,10939,11037],[53,7982,7984],{"id":7983},"习惯c","习惯C++",[1297,7986,7988],{"id":7987},"条款01视c为一个语言联邦","条款01：视C++为一个语言联邦",[280,7990,7991],{},"C++高效编程守则视状况而变化，取决使用的是C++的哪一部分",[783,7993,7994,7997,8000,8003],{},[786,7995,7996],{},"C",[786,7998,7999],{},"Object-Oriented C++",[786,8001,8002],{},"Template C++",[786,8004,8005],{},"STL",[1297,8007,8009],{"id":8008},"条款02-尽量以const-enum-inline替换-define","条款02 尽量以const, enum, inline替换 #define",[280,8011,8012],{},"当编译器不允许在class中为static常量赋初值，但又需要一个初值时",[11,8014,8016],{"className":13,"code":8015,"language":15,"meta":16,"style":16},"class Entity\n{\nprivate:\n  static const int Num = 1;\n  int m_Array[Num];\n};\n\nconst int Entity::Num;\n",[18,8017,8018,8022,8026,8030,8035,8040,8044,8048],{"__ignoreMap":16},[21,8019,8020],{"class":23,"line":24},[21,8021,7169],{},[21,8023,8024],{"class":23,"line":30},[21,8025,2296],{},[21,8027,8028],{"class":23,"line":36},[21,8029,1316],{},[21,8031,8032],{"class":23,"line":42},[21,8033,8034],{},"  static const int Num = 1;\n",[21,8036,8037],{"class":23,"line":48},[21,8038,8039],{},"  int m_Array[Num];\n",[21,8041,8042],{"class":23,"line":88},[21,8043,51],{},[21,8045,8046],{"class":23,"line":94},[21,8047,144],{"emptyLinePlaceholder":143},[21,8049,8050],{"class":23,"line":100},[21,8051,8052],{},"const int Entity::Num;\n",[280,8054,8055,8056,4003],{},"可使用\"the enum hack\"补偿做法(",[8057,8058,8059],"em",{},"在模板元编程常用到",[11,8061,8063],{"className":13,"code":8062,"language":15,"meta":16,"style":16},"class Entity\n{\nprivate:\n  enum { Num = 1 };\n  int m_Array[Num];\n};\n",[18,8064,8065,8069,8073,8077,8082,8086],{"__ignoreMap":16},[21,8066,8067],{"class":23,"line":24},[21,8068,7169],{},[21,8070,8071],{"class":23,"line":30},[21,8072,2296],{},[21,8074,8075],{"class":23,"line":36},[21,8076,1316],{},[21,8078,8079],{"class":23,"line":42},[21,8080,8081],{},"  enum { Num = 1 };\n",[21,8083,8084],{"class":23,"line":48},[21,8085,8039],{},[21,8087,8088],{"class":23,"line":88},[21,8089,51],{},[280,8091,3875],{},[11,8093,8095],{"className":13,"code":8094,"language":15,"meta":16,"style":16},"#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a): (b))\n\nint a = 5, b = 0;\nCALL_WITH_MAX(++a, b);       \u002F\u002F a累加两次\nCALL_WITH_MAX(++a, b + 10);  \u002F\u002F a累加一次\n",[18,8096,8097,8102,8106,8111,8116],{"__ignoreMap":16},[21,8098,8099],{"class":23,"line":24},[21,8100,8101],{},"#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a): (b))\n",[21,8103,8104],{"class":23,"line":30},[21,8105,144],{"emptyLinePlaceholder":143},[21,8107,8108],{"class":23,"line":36},[21,8109,8110],{},"int a = 5, b = 0;\n",[21,8112,8113],{"class":23,"line":42},[21,8114,8115],{},"CALL_WITH_MAX(++a, b);       \u002F\u002F a累加两次\n",[21,8117,8118],{"class":23,"line":48},[21,8119,8120],{},"CALL_WITH_MAX(++a, b + 10);  \u002F\u002F a累加一次\n",[280,8122,8123],{},"使用如下替代",[11,8125,8127],{"className":13,"code":8126,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\ninline void callWithMax(const T& a, const T& b)\n{\n  f(a > b ? a : b)\n}\n",[18,8128,8129,8133,8138,8142,8147],{"__ignoreMap":16},[21,8130,8131],{"class":23,"line":24},[21,8132,4705],{},[21,8134,8135],{"class":23,"line":30},[21,8136,8137],{},"inline void callWithMax(const T& a, const T& b)\n",[21,8139,8140],{"class":23,"line":36},[21,8141,2296],{},[21,8143,8144],{"class":23,"line":42},[21,8145,8146],{},"  f(a > b ? a : b)\n",[21,8148,8149],{"class":23,"line":48},[21,8150,115],{},[783,8152,8153,8156],{},[786,8154,8155],{},"对于单纯常量，最好以const对象或enum替换#define",[786,8157,8158],{},"对于形似函数的宏，最好改用inline函数替换#define",[1297,8160,8162],{"id":8161},"条款03-尽可能使用const","条款03 尽可能使用const",[280,8164,8165],{},"在class中定义const成员函数时，如果出现const版本与非const版本内容过长并且重复时",[11,8167,8169],{"className":13,"code":8168,"language":15,"meta":16,"style":16},"class TextBlock\n{\npublic:\n  const char& operator[](std::size_t position) const\n  {\n    ...\n    return text[position]\n  }\n  char& operator[](std::size_t position)\n  {\n    ...\n    return text[position]\n  }\nprivate:\n std::string text;\n};\n",[18,8170,8171,8176,8180,8184,8189,8193,8197,8202,8206,8211,8215,8219,8223,8227,8231,8236],{"__ignoreMap":16},[21,8172,8173],{"class":23,"line":24},[21,8174,8175],{},"class TextBlock\n",[21,8177,8178],{"class":23,"line":30},[21,8179,2296],{},[21,8181,8182],{"class":23,"line":36},[21,8183,1347],{},[21,8185,8186],{"class":23,"line":42},[21,8187,8188],{},"  const char& operator[](std::size_t position) const\n",[21,8190,8191],{"class":23,"line":48},[21,8192,2788],{},[21,8194,8195],{"class":23,"line":88},[21,8196,7196],{},[21,8198,8199],{"class":23,"line":94},[21,8200,8201],{},"    return text[position]\n",[21,8203,8204],{"class":23,"line":100},[21,8205,103],{},[21,8207,8208],{"class":23,"line":106},[21,8209,8210],{},"  char& operator[](std::size_t position)\n",[21,8212,8213],{"class":23,"line":112},[21,8214,2788],{},[21,8216,8217],{"class":23,"line":176},[21,8218,7196],{},[21,8220,8221],{"class":23,"line":182},[21,8222,8201],{},[21,8224,8225],{"class":23,"line":188},[21,8226,103],{},[21,8228,8229],{"class":23,"line":194},[21,8230,1316],{},[21,8232,8233],{"class":23,"line":200},[21,8234,8235],{}," std::string text;\n",[21,8237,8238],{"class":23,"line":206},[21,8239,51],{},[280,8241,8242],{},"可使用非const版本调用const版本，避免重复",[11,8244,8246],{"className":13,"code":8245,"language":15,"meta":16,"style":16},"char& operator[](std::size_t position)\n{\n  return const_cast\u003Cchar&>(static_cast\u003Cconst TextBlock>(*this)[position]);\n}\n",[18,8247,8248,8253,8257,8262],{"__ignoreMap":16},[21,8249,8250],{"class":23,"line":24},[21,8251,8252],{},"char& operator[](std::size_t position)\n",[21,8254,8255],{"class":23,"line":30},[21,8256,2296],{},[21,8258,8259],{"class":23,"line":36},[21,8260,8261],{},"  return const_cast\u003Cchar&>(static_cast\u003Cconst TextBlock>(*this)[position]);\n",[21,8263,8264],{"class":23,"line":42},[21,8265,115],{},[1297,8267,8269],{"id":8268},"条款04-确定对象被使用前已先被初始化","条款04 确定对象被使用前已先被初始化",[783,8271,8272,8275,8278],{},[786,8273,8274],{},"为内置型对象进行手工初始化，因为C++不保证初始化他们",[786,8276,8277],{},"构造函数最好使用成员初始化列表，而不要在构造函数内使用赋值操作",[786,8279,8280,8281],{},"为避免\"跨编译单元的初始化次序\"问题，以local static对象代替non-local static对象",[1183,8282,8283],{},[280,8284,8285],{},"local static对象指的是函数内的static对象",[53,8287,8289],{"id":8288},"构造析构赋值运算","构造\u002F析构\u002F赋值运算",[1297,8291,8293],{"id":8292},"条款05-了解c合成并调用了哪些函数","条款05 了解C++合成并调用了哪些函数",[1297,8295,8297],{"id":8296},"条款06-不想使用编译器自动生成的函数就应该明确拒接","条款06 不想使用编译器自动生成的函数，就应该明确拒接",[1297,8299,8301],{"id":8300},"条款07-为多态基类声明virtual析构","条款07 为多态基类声明virtual析构",[783,8303,8304,8307],{},[786,8305,8306],{},"带多态时，基类应该声明一个虚析构。如果类有任何虚函数，它就应该有一个虚析构函数",[786,8308,8309,8310],{},"如果类的设计不是作为基类使用，或不是为了多态，就不应该声明虚析构函数",[1183,8311,8312],{},[280,8313,8314],{},"虚函数会生成虚表指针，增加类的大小",[1297,8316,8318],{"id":8317},"条款08-别让异常逃离析构函数","条款08 别让异常逃离析构函数",[783,8320,8321,8324],{},[786,8322,8323],{},"析构函数不要吐出异常，如果一个被析构函数调用的函数抛出异常，析构函数应该捕捉任何异常，\n然后吞下它们或结束程序",[786,8325,8326],{},"如果需要对某个操作函数运行期间抛出的异常做出反应，那么class应该提供一个普通函数执行该操作\n而不是在析构函数中执行操作",[1297,8328,8330],{"id":8329},"条款09-不在构造和析构函数过程中调用虚函数","条款09 不在构造和析构函数过程中调用虚函数",[1297,8332,8334],{"id":8333},"条款10-令operator-返回一个reference-to-this","条款10 令operator= 返回一个reference to *this",[1297,8336,8338],{"id":8337},"条款11-在operator-中处理自赋值","条款11 在operator= 中处理自赋值",[11,8340,8342],{"className":13,"code":8341,"language":15,"meta":16,"style":16},"class Bitmap { ... };\nclass Widget\n{\n  ...\nprivate:\n  Bitmap* pb;\n};\n",[18,8343,8344,8349,8353,8357,8361,8365,8370],{"__ignoreMap":16},[21,8345,8346],{"class":23,"line":24},[21,8347,8348],{},"class Bitmap { ... };\n",[21,8350,8351],{"class":23,"line":30},[21,8352,5322],{},[21,8354,8355],{"class":23,"line":36},[21,8356,2296],{},[21,8358,8359],{"class":23,"line":42},[21,8360,2311],{},[21,8362,8363],{"class":23,"line":48},[21,8364,1316],{},[21,8366,8367],{"class":23,"line":88},[21,8368,8369],{},"  Bitmap* pb;\n",[21,8371,8372],{"class":23,"line":94},[21,8373,51],{},[783,8375,8376,8419,8457],{},[786,8377,8378,8379],{},"认同测试检验自赋值，但不具备异常安全性。当new Bitmap异常时，pd指向一块被删除的空间",[11,8380,8382],{"className":13,"code":8381,"language":15,"meta":16,"style":16},"Widget& Widget::operator=(const Widget& rhs)\n{\n  if (this == &rhs) return *this;\n\n  delete pb;\n  pd = new Bitmap(*rhs.pb)\n  return *this;\n}\n",[18,8383,8384,8388,8392,8397,8401,8406,8411,8415],{"__ignoreMap":16},[21,8385,8386],{"class":23,"line":24},[21,8387,6040],{},[21,8389,8390],{"class":23,"line":30},[21,8391,2296],{},[21,8393,8394],{"class":23,"line":36},[21,8395,8396],{},"  if (this == &rhs) return *this;\n",[21,8398,8399],{"class":23,"line":42},[21,8400,144],{"emptyLinePlaceholder":143},[21,8402,8403],{"class":23,"line":48},[21,8404,8405],{},"  delete pb;\n",[21,8407,8408],{"class":23,"line":88},[21,8409,8410],{},"  pd = new Bitmap(*rhs.pb)\n",[21,8412,8413],{"class":23,"line":94},[21,8414,6054],{},[21,8416,8417],{"class":23,"line":100},[21,8418,115],{},[786,8420,8421,8422],{},"在复制pb所指内容之前，不删除pb",[11,8423,8425],{"className":13,"code":8424,"language":15,"meta":16,"style":16},"Widget& Widget::operator=(const Widget& rhs)\n{\n  Bitmap* pOrign = pb;\n  pd = new Bitmap(*rhs.pb)\n  delete pOrign;\n  return *this;\n}\n",[18,8426,8427,8431,8435,8440,8444,8449,8453],{"__ignoreMap":16},[21,8428,8429],{"class":23,"line":24},[21,8430,6040],{},[21,8432,8433],{"class":23,"line":30},[21,8434,2296],{},[21,8436,8437],{"class":23,"line":36},[21,8438,8439],{},"  Bitmap* pOrign = pb;\n",[21,8441,8442],{"class":23,"line":42},[21,8443,8410],{},[21,8445,8446],{"class":23,"line":48},[21,8447,8448],{},"  delete pOrign;\n",[21,8450,8451],{"class":23,"line":88},[21,8452,6054],{},[21,8454,8455],{"class":23,"line":94},[21,8456,115],{},[786,8458,8459,8460],{},"copy and swap",[11,8461,8463],{"className":13,"code":8462,"language":15,"meta":16,"style":16},"void swap(Widget& lhs, Widget& rhs)\n{\n  \u002F\u002F 交换lhs与rhs的数据\n}\nWidget& Widget::operator=(const Widget& rhs)\n{\n  Widget temp(rhs);\n  swap(temp, *this);\n  return *this;\n}\n\n\u002F* 或者直接在实参传递时copy *\u002F\nWidget& Widget::operator=(Widget rhs)\n{\n  swap(rhs, *this);\n  return *this;\n}\n",[18,8464,8465,8470,8474,8479,8483,8487,8491,8496,8501,8505,8509,8513,8518,8523,8527,8532,8536],{"__ignoreMap":16},[21,8466,8467],{"class":23,"line":24},[21,8468,8469],{},"void swap(Widget& lhs, Widget& rhs)\n",[21,8471,8472],{"class":23,"line":30},[21,8473,2296],{},[21,8475,8476],{"class":23,"line":36},[21,8477,8478],{},"  \u002F\u002F 交换lhs与rhs的数据\n",[21,8480,8481],{"class":23,"line":42},[21,8482,115],{},[21,8484,8485],{"class":23,"line":48},[21,8486,6040],{},[21,8488,8489],{"class":23,"line":88},[21,8490,2296],{},[21,8492,8493],{"class":23,"line":94},[21,8494,8495],{},"  Widget temp(rhs);\n",[21,8497,8498],{"class":23,"line":100},[21,8499,8500],{},"  swap(temp, *this);\n",[21,8502,8503],{"class":23,"line":106},[21,8504,6054],{},[21,8506,8507],{"class":23,"line":112},[21,8508,115],{},[21,8510,8511],{"class":23,"line":176},[21,8512,144],{"emptyLinePlaceholder":143},[21,8514,8515],{"class":23,"line":182},[21,8516,8517],{},"\u002F* 或者直接在实参传递时copy *\u002F\n",[21,8519,8520],{"class":23,"line":188},[21,8521,8522],{},"Widget& Widget::operator=(Widget rhs)\n",[21,8524,8525],{"class":23,"line":194},[21,8526,2296],{},[21,8528,8529],{"class":23,"line":200},[21,8530,8531],{},"  swap(rhs, *this);\n",[21,8533,8534],{"class":23,"line":206},[21,8535,6054],{},[21,8537,8538],{"class":23,"line":212},[21,8539,115],{},[1297,8541,8543],{"id":8542},"条款12-复制对象时不要遗漏成员","条款12 复制对象时不要遗漏成员",[783,8545,8546,8549],{},[786,8547,8548],{},"拷贝函数应该确保复制\"对象的所有成员变量\"以及\"所有base class部分\"",[786,8550,8551],{},"拷贝构造与拷贝运算符不要相互调用，避免代码重复的话可共同调用第三个函数如init()",[53,8553,8554],{"id":8554},"资源管理",[1297,8556,8558],{"id":8557},"条款13-以对象管理资源","条款13 以对象管理资源",[783,8560,8561],{},[786,8562,8563,8564],{},"为防止资源泄露，请使用RAII对象，他们在构造函数中获得资源并在析构函数中释放资源",[1183,8565,8566],{},[280,8567,8568],{},"RAII即Resource Acquisition Is Initalization，资源获取即初始化",[1297,8570,8572],{"id":8571},"条款14-在资源管理类中小心拷贝行为","条款14 在资源管理类中小心拷贝行为",[11,8574,8576],{"className":13,"code":8575,"language":15,"meta":16,"style":16},"class Lock\n{\npublic:\n  explicit Lock(Mutex* pm) : mutexPtr(pm) { lock(mutexPtr); }\n  ~Lock() { unlock(mutexPtr); }\nprivate:\n  Mutex* mutexPtr;\n};\n",[18,8577,8578,8583,8587,8591,8596,8601,8605,8610],{"__ignoreMap":16},[21,8579,8580],{"class":23,"line":24},[21,8581,8582],{},"class Lock\n",[21,8584,8585],{"class":23,"line":30},[21,8586,2296],{},[21,8588,8589],{"class":23,"line":36},[21,8590,1347],{},[21,8592,8593],{"class":23,"line":42},[21,8594,8595],{},"  explicit Lock(Mutex* pm) : mutexPtr(pm) { lock(mutexPtr); }\n",[21,8597,8598],{"class":23,"line":48},[21,8599,8600],{},"  ~Lock() { unlock(mutexPtr); }\n",[21,8602,8603],{"class":23,"line":88},[21,8604,1316],{},[21,8606,8607],{"class":23,"line":94},[21,8608,8609],{},"  Mutex* mutexPtr;\n",[21,8611,8612],{"class":23,"line":100},[21,8613,51],{},[783,8615,8616,8619],{},[786,8617,8618],{},"禁止复制",[786,8620,8621,8622],{},"引用计数",[11,8623,8625],{"className":13,"code":8624,"language":15,"meta":16,"style":16},"class Lock\n{\npublic:\n  explicit Lock(Mutex* pm) : mutexPtr(pm, unlock) { lock(mutexPtr.get()); }\n\nprivate:\n  std::shared_ptr\u003CMutex> mutexPtr;\n};\n",[18,8626,8627,8631,8635,8639,8644,8648,8652,8657],{"__ignoreMap":16},[21,8628,8629],{"class":23,"line":24},[21,8630,8582],{},[21,8632,8633],{"class":23,"line":30},[21,8634,2296],{},[21,8636,8637],{"class":23,"line":36},[21,8638,1347],{},[21,8640,8641],{"class":23,"line":42},[21,8642,8643],{},"  explicit Lock(Mutex* pm) : mutexPtr(pm, unlock) { lock(mutexPtr.get()); }\n",[21,8645,8646],{"class":23,"line":48},[21,8647,144],{"emptyLinePlaceholder":143},[21,8649,8650],{"class":23,"line":88},[21,8651,1316],{},[21,8653,8654],{"class":23,"line":94},[21,8655,8656],{},"  std::shared_ptr\u003CMutex> mutexPtr;\n",[21,8658,8659],{"class":23,"line":100},[21,8660,51],{},[1297,8662,8664],{"id":8663},"条款15-在资源管理类中提供对原始资源的访问","条款15 在资源管理类中提供对原始资源的访问",[783,8666,8667],{},[786,8668,8669,8670],{},"每个RAII class应该提供一个取得所管理资源的办法\n",[783,8671,8672,8675],{},[786,8673,8674],{},"显示转换：定义get()",[786,8676,8677,8678,8681],{},"隐式转换：类型转换运算符operator ",[8057,8679,8680],{},"type","() const;",[1297,8683,8685],{"id":8684},"条款16-成对使用new和delete时采取相同形式","条款16 成对使用new和delete时采取相同形式",[1297,8687,8689],{"id":8688},"条款17-以独立语句将newed对象置入智能指针","条款17 以独立语句将newed对象置入智能指针",[11,8691,8693],{"className":13,"code":8692,"language":15,"meta":16,"style":16},"int priority();\nvoid processWidget(std::shared_ptr\u003CWidget>, int);\n\nprocessWidget(std::shared_ptr\u003CWidget>(new Widget), priority());\n",[18,8694,8695,8700,8705,8709],{"__ignoreMap":16},[21,8696,8697],{"class":23,"line":24},[21,8698,8699],{},"int priority();\n",[21,8701,8702],{"class":23,"line":30},[21,8703,8704],{},"void processWidget(std::shared_ptr\u003CWidget>, int);\n",[21,8706,8707],{"class":23,"line":36},[21,8708,144],{"emptyLinePlaceholder":143},[21,8710,8711],{"class":23,"line":42},[21,8712,8713],{},"processWidget(std::shared_ptr\u003CWidget>(new Widget), priority());\n",[280,8715,8716,8717,8719],{},"实参的运行次序是不定的，当priority()在new Widget与",[18,8718,5619],{},"构造函数之间运行时，如果导致异常new Widget返回的\n指针将会遗失，造成资源泄露。因此应该使用分离语句",[11,8721,8723],{"className":13,"code":8722,"language":15,"meta":16,"style":16},"std::shared_ptr\u003CWidget> pw(new Widget);\nprocessWidget(pw, priority());\n",[18,8724,8725,8730],{"__ignoreMap":16},[21,8726,8727],{"class":23,"line":24},[21,8728,8729],{},"std::shared_ptr\u003CWidget> pw(new Widget);\n",[21,8731,8732],{"class":23,"line":30},[21,8733,8734],{},"processWidget(pw, priority());\n",[53,8736,8737],{"id":8737},"设计与声明",[1297,8739,8741],{"id":8740},"条款18-让接口容易被正确使用不易被误用","条款18 让接口容易被正确使用，不易被误用",[1297,8743,8745],{"id":8744},"条款19-设计class犹如type","条款19 设计class犹如type",[1297,8747,8749],{"id":8748},"条款20-传const引用替换传值","条款20 传const引用替换传值",[783,8751,8752,8768],{},[786,8753,8754,8755,8758,8759,8762,8763],{},"尽量以",[8057,8756,8757],{},"pass-py-reference-to-const","替换",[8057,8760,8761],{},"pass-py-value","，前者通常比较高效，并可避免切割问题",[1183,8764,8765],{},[280,8766,8767],{},"切割问题指在动态类型中，只有子类指针与引用具备多态，其他情况下用子类表示基类会导致子类的特殊部分\n被切割",[786,8769,8770,8771,8773],{},"以上规则并不适用于内置类型，以及STL的迭代器和函数对象。对他们而言，",[8057,8772,8761],{},"往往比较适当",[1297,8775,8777],{"id":8776},"条款21-必须返回对象时不要返回引用","条款21 必须返回对象时不要返回引用",[1297,8779,8781],{"id":8780},"条款22-将成员变量声明为private","条款22 将成员变量声明为private",[280,8783,8784],{},"protected并不比public更有封装性。当一个public变量被取消时，会破坏所有使用它的代码，protected同理，\n会破坏所有子类中使用它的代码。",[280,8786,8787],{},"因此只有两种访问权限：private(提供封装)和其他(不提供封装)",[1297,8789,8791],{"id":8790},"条框23-以non-membernon-friend替代member函数","条框23 以non-member、non-friend替代member函数",[280,8793,8794],{},"可增加封装性、包裹弹性以及机能扩充性",[1297,8796,8798],{"id":8797},"条框24-若所有参数都需要类型转换请采用non-member函数","条框24 若所有参数都需要类型转换，请采用non-member函数",[11,8800,8802],{"className":13,"code":8801,"language":15,"meta":16,"style":16},"class Rational\n{\npublic:\n  const Rational operator*(const Rational& rhs)\n  {\n    ...\n  }\n};\n\nRational test, result;\nresult = test * 2;\nresult = 2 * test;  \u002F\u002F 并不能通过编译\n",[18,8803,8804,8809,8813,8817,8822,8826,8830,8834,8838,8842,8847,8852],{"__ignoreMap":16},[21,8805,8806],{"class":23,"line":24},[21,8807,8808],{},"class Rational\n",[21,8810,8811],{"class":23,"line":30},[21,8812,2296],{},[21,8814,8815],{"class":23,"line":36},[21,8816,1347],{},[21,8818,8819],{"class":23,"line":42},[21,8820,8821],{},"  const Rational operator*(const Rational& rhs)\n",[21,8823,8824],{"class":23,"line":48},[21,8825,2788],{},[21,8827,8828],{"class":23,"line":88},[21,8829,7196],{},[21,8831,8832],{"class":23,"line":94},[21,8833,103],{},[21,8835,8836],{"class":23,"line":100},[21,8837,51],{},[21,8839,8840],{"class":23,"line":106},[21,8841,144],{"emptyLinePlaceholder":143},[21,8843,8844],{"class":23,"line":112},[21,8845,8846],{},"Rational test, result;\n",[21,8848,8849],{"class":23,"line":176},[21,8850,8851],{},"result = test * 2;\n",[21,8853,8854],{"class":23,"line":182},[21,8855,8856],{},"result = 2 * test;  \u002F\u002F 并不能通过编译\n",[280,8858,8859],{},"需要为函数的所有参数进行类型转换时，这个函数必须为non-member",[11,8861,8863],{"className":13,"code":8862,"language":15,"meta":16,"style":16},"class Rational;\nconst Rational operator*(const Rational& lhs, const Rational& rhs)\n{\n ...\n}\n",[18,8864,8865,8870,8875,8879,8884],{"__ignoreMap":16},[21,8866,8867],{"class":23,"line":24},[21,8868,8869],{},"class Rational;\n",[21,8871,8872],{"class":23,"line":30},[21,8873,8874],{},"const Rational operator*(const Rational& lhs, const Rational& rhs)\n",[21,8876,8877],{"class":23,"line":36},[21,8878,2296],{},[21,8880,8881],{"class":23,"line":42},[21,8882,8883],{}," ...\n",[21,8885,8886],{"class":23,"line":48},[21,8887,115],{},[1297,8889,8891],{"id":8890},"条款25-考虑写个不抛异常的swap函数","条款25 考虑写个不抛异常的swap函数",[783,8893,8894,8897,9054,9061],{},[786,8895,8896],{},"当std::swap对自定义类型效率不高时，提供一个swap成员函数，并确定这个函数不抛出异常",[786,8898,8899,8900,8984,8986,8987],{},"如果提供了一个member swap，也该提供一个non-member swap调用前者。对于非模板类也请特化std::swap",[11,8901,8903],{"className":13,"code":8902,"language":15,"meta":16,"style":16},"class Widget\n{\npublic:\n  void swap(Widget& other)\n  {\n    using std::swap;\n    swap(xxx, other.xxx);\n  }\n}\n\nnamespace std\n{\n  template\u003C>\n  void swap\u003CWidget>(Widget& a, Widget& b)\n  {\n    a.swap(b);\n  }\n}\n",[18,8904,8905,8909,8913,8917,8922,8926,8931,8936,8940,8944,8948,8953,8957,8962,8967,8971,8976,8980],{"__ignoreMap":16},[21,8906,8907],{"class":23,"line":24},[21,8908,5322],{},[21,8910,8911],{"class":23,"line":30},[21,8912,2296],{},[21,8914,8915],{"class":23,"line":36},[21,8916,1347],{},[21,8918,8919],{"class":23,"line":42},[21,8920,8921],{},"  void swap(Widget& other)\n",[21,8923,8924],{"class":23,"line":48},[21,8925,2788],{},[21,8927,8928],{"class":23,"line":88},[21,8929,8930],{},"    using std::swap;\n",[21,8932,8933],{"class":23,"line":94},[21,8934,8935],{},"    swap(xxx, other.xxx);\n",[21,8937,8938],{"class":23,"line":100},[21,8939,103],{},[21,8941,8942],{"class":23,"line":106},[21,8943,115],{},[21,8945,8946],{"class":23,"line":112},[21,8947,144],{"emptyLinePlaceholder":143},[21,8949,8950],{"class":23,"line":176},[21,8951,8952],{},"namespace std\n",[21,8954,8955],{"class":23,"line":182},[21,8956,2296],{},[21,8958,8959],{"class":23,"line":188},[21,8960,8961],{},"  template\u003C>\n",[21,8963,8964],{"class":23,"line":194},[21,8965,8966],{},"  void swap\u003CWidget>(Widget& a, Widget& b)\n",[21,8968,8969],{"class":23,"line":200},[21,8970,2788],{},[21,8972,8973],{"class":23,"line":206},[21,8974,8975],{},"    a.swap(b);\n",[21,8977,8978],{"class":23,"line":212},[21,8979,103],{},[21,8981,8982],{"class":23,"line":218},[21,8983,115],{},[1447,8985],{},"但当Widget为模板类时，就无法特化std::swap",[11,8988,8990],{"className":13,"code":8989,"language":15,"meta":16,"style":16},"template\u003Ctypename T> class Widget;\nnamespace std\n{\n  template\u003Ctypename T>\n  void swap\u003CWidget\u003CT>>(Widget\u003CT>& a, Widget\u003CT>& b)\n  {\n    a.swap(b);\n  }\n\n  \u002F\u002F 上面为部分特化，而函数不支持部分特化\n  \u002F\u002F 相当于将T1特化为Widget(我猜的)\n  \u002F\u002F template\u003Ctypename T1, typename T>\n  \u002F\u002F void swap\u003CT1\u003CT>>(T1\u003CT>&, T1\u003CT>&)\n}\n",[18,8991,8992,8997,9001,9005,9009,9014,9018,9022,9026,9030,9035,9040,9045,9050],{"__ignoreMap":16},[21,8993,8994],{"class":23,"line":24},[21,8995,8996],{},"template\u003Ctypename T> class Widget;\n",[21,8998,8999],{"class":23,"line":30},[21,9000,8952],{},[21,9002,9003],{"class":23,"line":36},[21,9004,2296],{},[21,9006,9007],{"class":23,"line":42},[21,9008,6490],{},[21,9010,9011],{"class":23,"line":48},[21,9012,9013],{},"  void swap\u003CWidget\u003CT>>(Widget\u003CT>& a, Widget\u003CT>& b)\n",[21,9015,9016],{"class":23,"line":88},[21,9017,2788],{},[21,9019,9020],{"class":23,"line":94},[21,9021,8975],{},[21,9023,9024],{"class":23,"line":100},[21,9025,103],{},[21,9027,9028],{"class":23,"line":106},[21,9029,144],{"emptyLinePlaceholder":143},[21,9031,9032],{"class":23,"line":112},[21,9033,9034],{},"  \u002F\u002F 上面为部分特化，而函数不支持部分特化\n",[21,9036,9037],{"class":23,"line":176},[21,9038,9039],{},"  \u002F\u002F 相当于将T1特化为Widget(我猜的)\n",[21,9041,9042],{"class":23,"line":182},[21,9043,9044],{},"  \u002F\u002F template\u003Ctypename T1, typename T>\n",[21,9046,9047],{"class":23,"line":188},[21,9048,9049],{},"  \u002F\u002F void swap\u003CT1\u003CT>>(T1\u003CT>&, T1\u003CT>&)\n",[21,9051,9052],{"class":23,"line":194},[21,9053,115],{},[786,9055,9056,9057,9060],{},"调用swap时应声明",[18,9058,9059],{},"using std::swap","，然后调用不带任何命名空间修饰的swap。这样编译器会先择合适的swap",[786,9062,9063,9064,9066,9067,9105,9107,9108],{},"为自定义类型进行std template全特化是好的，但不要尝试在std中加入新的东西",[1447,9065],{},"模板类时，无法部分特化std::swap函数，但可以重载std::swap。不过重载属于添加新的模板，不推荐",[11,9068,9070],{"className":13,"code":9069,"language":15,"meta":16,"style":16},"namespace std\n{\n  template\u003Ctypename T>\n  void swap(Widget\u003CT>& a, Widget\u003CT>& b)\n  {\n    a.swap(b);\n  }\n}\n",[18,9071,9072,9076,9080,9084,9089,9093,9097,9101],{"__ignoreMap":16},[21,9073,9074],{"class":23,"line":24},[21,9075,8952],{},[21,9077,9078],{"class":23,"line":30},[21,9079,2296],{},[21,9081,9082],{"class":23,"line":36},[21,9083,6490],{},[21,9085,9086],{"class":23,"line":42},[21,9087,9088],{},"  void swap(Widget\u003CT>& a, Widget\u003CT>& b)\n",[21,9090,9091],{"class":23,"line":48},[21,9092,2788],{},[21,9094,9095],{"class":23,"line":88},[21,9096,8975],{},[21,9098,9099],{"class":23,"line":94},[21,9100,103],{},[21,9102,9103],{"class":23,"line":100},[21,9104,115],{},[1447,9106],{},"但可以重新声明个non-member swap来调用member swap",[11,9109,9111],{"className":13,"code":9110,"language":15,"meta":16,"style":16},"namespace WidgetStuff\n{\n  template\u003Ctypename T>\n  class Widget { ... };\n  ...\n  template\u003Ctypename T>\n  void swap(Widget\u003CT>& a, Widget\u003CT>& b)\n  {\n    a.swap(b);\n  }\n}\n",[18,9112,9113,9118,9122,9126,9131,9135,9139,9143,9147,9151,9155],{"__ignoreMap":16},[21,9114,9115],{"class":23,"line":24},[21,9116,9117],{},"namespace WidgetStuff\n",[21,9119,9120],{"class":23,"line":30},[21,9121,2296],{},[21,9123,9124],{"class":23,"line":36},[21,9125,6490],{},[21,9127,9128],{"class":23,"line":42},[21,9129,9130],{},"  class Widget { ... };\n",[21,9132,9133],{"class":23,"line":48},[21,9134,2311],{},[21,9136,9137],{"class":23,"line":88},[21,9138,6490],{},[21,9140,9141],{"class":23,"line":94},[21,9142,9088],{},[21,9144,9145],{"class":23,"line":100},[21,9146,2788],{},[21,9148,9149],{"class":23,"line":106},[21,9150,8975],{},[21,9152,9153],{"class":23,"line":112},[21,9154,103],{},[21,9156,9157],{"class":23,"line":176},[21,9158,115],{},[53,9160,9161],{"id":9161},"实现",[1297,9163,9165],{"id":9164},"条款26-尽可能延后变量定义的时间","条款26 尽可能延后变量定义的时间",[1297,9167,9169],{"id":9168},"条款27-少做类型转换","条款27 少做类型转换",[1297,9171,9173],{"id":9172},"条款28-避免返回handles指向对象内部成分","条款28 避免返回handles指向对象内部成分",[1183,9175,9176],{},[280,9177,9178],{},"handles(号码牌，用来取得某个对象)指引用、指针和迭代器",[1297,9180,9182],{"id":9181},"条款29-为异常安全努力","条款29 为异常安全努力",[1297,9184,9186],{"id":9185},"条款30-透彻了解inline","条款30 透彻了解inline",[1297,9188,9190],{"id":9189},"条款31-将文件的编译依存关系降到最低","条款31 将文件的编译依存关系降到最低",[53,9192,9193],{"id":9193},"继承于面向对象设计",[1297,9195,9197],{"id":9196},"条款32-public继承塑造出is-a关系","条款32 public继承塑造出is-a关系",[280,9199,9200,9203],{},[18,9201,9202],{},"class derived : public base","即derived is a base",[1297,9205,9207],{"id":9206},"条款33-避免遮掩继承来的名称","条款33 避免遮掩继承来的名称",[280,9209,9210],{},"derived class 内的名称会遮掩base class 中的名称。为避免，可在基类使用using声明",[11,9212,9214],{"className":13,"code":9213,"language":15,"meta":16,"style":16},"class Base\n{\npublic:\n  void mf3();\n};\nclass Derived\n{\npublic:\n  using Base::mf3;\n  \u002F\u002F 因为在作用域按名称查找，当前作用域中只要名称匹配就会停止，无匹配才会向外继续查找\n  void mf3(int);\n};\n",[18,9215,9216,9221,9225,9229,9234,9238,9243,9247,9251,9256,9261,9266],{"__ignoreMap":16},[21,9217,9218],{"class":23,"line":24},[21,9219,9220],{},"class Base\n",[21,9222,9223],{"class":23,"line":30},[21,9224,2296],{},[21,9226,9227],{"class":23,"line":36},[21,9228,1347],{},[21,9230,9231],{"class":23,"line":42},[21,9232,9233],{},"  void mf3();\n",[21,9235,9236],{"class":23,"line":48},[21,9237,51],{},[21,9239,9240],{"class":23,"line":88},[21,9241,9242],{},"class Derived\n",[21,9244,9245],{"class":23,"line":94},[21,9246,2296],{},[21,9248,9249],{"class":23,"line":100},[21,9250,1347],{},[21,9252,9253],{"class":23,"line":106},[21,9254,9255],{},"  using Base::mf3;\n",[21,9257,9258],{"class":23,"line":112},[21,9259,9260],{},"  \u002F\u002F 因为在作用域按名称查找，当前作用域中只要名称匹配就会停止，无匹配才会向外继续查找\n",[21,9262,9263],{"class":23,"line":176},[21,9264,9265],{},"  void mf3(int);\n",[21,9267,9268],{"class":23,"line":182},[21,9269,51],{},[1297,9271,9273],{"id":9272},"条款34-区分接口设计与实现继承","条款34 区分接口设计与实现继承",[1297,9275,9277],{"id":9276},"条款35-考虑virtual函数以外的其他选择","条款35 考虑virtual函数以外的其他选择",[783,9279,9280,9287],{},[786,9281,9282,9283,9286],{},"non-virtual interface(NVI)手法。是",[8057,9284,9285],{},"Template Method","设计模式的一种特殊形式，以public non-virtual\n成员函数包裹低访问性(private, protected)的virtual函数",[786,9288,9289,9290,9293],{},"将virtual函数替换为函数指针或std::function对象，这是",[8057,9291,9292],{},"Strategy","设计模式的一种形式",[1297,9295,9297],{"id":9296},"条款36-不重新定义继承而来的non-virtual函数","条款36 不重新定义继承而来的non-virtual函数",[1297,9299,9301],{"id":9300},"条款37-不重新定义继承而来的缺省参数值","条款37 不重新定义继承而来的缺省参数值",[11,9303,9305],{"className":13,"code":9304,"language":15,"meta":16,"style":16},"class Shape\n{\npublic:\n  enum ShapeColor { Red, Green, Blue };\n  virtual void draw(ShapeColor color = Red) const = 0;\n};\nclass Rectangle: public Shape\n{\npublic:\n  virtual void draw(ShapeColor color = Green) const;\n};\n\nShape* pr = new Rectangle;\npr.draw();\n",[18,9306,9307,9312,9316,9320,9325,9330,9334,9339,9343,9347,9352,9356,9360,9365],{"__ignoreMap":16},[21,9308,9309],{"class":23,"line":24},[21,9310,9311],{},"class Shape\n",[21,9313,9314],{"class":23,"line":30},[21,9315,2296],{},[21,9317,9318],{"class":23,"line":36},[21,9319,1347],{},[21,9321,9322],{"class":23,"line":42},[21,9323,9324],{},"  enum ShapeColor { Red, Green, Blue };\n",[21,9326,9327],{"class":23,"line":48},[21,9328,9329],{},"  virtual void draw(ShapeColor color = Red) const = 0;\n",[21,9331,9332],{"class":23,"line":88},[21,9333,51],{},[21,9335,9336],{"class":23,"line":94},[21,9337,9338],{},"class Rectangle: public Shape\n",[21,9340,9341],{"class":23,"line":100},[21,9342,2296],{},[21,9344,9345],{"class":23,"line":106},[21,9346,1347],{},[21,9348,9349],{"class":23,"line":112},[21,9350,9351],{},"  virtual void draw(ShapeColor color = Green) const;\n",[21,9353,9354],{"class":23,"line":176},[21,9355,51],{},[21,9357,9358],{"class":23,"line":182},[21,9359,144],{"emptyLinePlaceholder":143},[21,9361,9362],{"class":23,"line":188},[21,9363,9364],{},"Shape* pr = new Rectangle;\n",[21,9366,9367],{"class":23,"line":194},[21,9368,9369],{},"pr.draw();\n",[280,9371,9372,9373,9375],{},"虽然draw函数会动态绑定，即调用Rectangle的draw。但draw函数的默认实参只能静态绑定，\n即使用静态类型Shape的draw的默认实参",[1447,9374],{},"\n当Rectangle::draw()的默认实参定义与基类一样时，如果Shape类的默认实参改变，也就必须同时修改\nRectangle处的代码。可使用条款35中的NVI来解决，定义public函数调用virtual函数",[1297,9377,9379],{"id":9378},"条款38-复合的has-a与根据某物实现出","条款38 复合的has-a与\"根据某物实现出\"",[783,9381,9382,9416],{},[786,9383,9384,9385],{},"在应用域，复合意味着has-a。如：Person有一个Address",[11,9386,9388],{"className":13,"code":9387,"language":15,"meta":16,"style":16},"class Address;\nclass Person\n{\npublic:\n  Address m_address;\n}\n",[18,9389,9390,9395,9399,9403,9407,9412],{"__ignoreMap":16},[21,9391,9392],{"class":23,"line":24},[21,9393,9394],{},"class Address;\n",[21,9396,9397],{"class":23,"line":30},[21,9398,6477],{},[21,9400,9401],{"class":23,"line":36},[21,9402,2296],{},[21,9404,9405],{"class":23,"line":42},[21,9406,1347],{},[21,9408,9409],{"class":23,"line":48},[21,9410,9411],{},"  Address m_address;\n",[21,9413,9414],{"class":23,"line":88},[21,9415,115],{},[786,9417,9418,9419],{},"在实现域，复合意味着\"根据...实现出\"。如：根据list实现Set",[11,9420,9422],{"className":13,"code":9421,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nclass Set\n{\nprivate:\n  std::list\u003CT> rep;\n}\n",[18,9423,9424,9428,9433,9437,9441,9446],{"__ignoreMap":16},[21,9425,9426],{"class":23,"line":24},[21,9427,4705],{},[21,9429,9430],{"class":23,"line":30},[21,9431,9432],{},"class Set\n",[21,9434,9435],{"class":23,"line":36},[21,9436,2296],{},[21,9438,9439],{"class":23,"line":42},[21,9440,1316],{},[21,9442,9443],{"class":23,"line":48},[21,9444,9445],{},"  std::list\u003CT> rep;\n",[21,9447,9448],{"class":23,"line":88},[21,9449,115],{},[1297,9451,9453],{"id":9452},"条款39-审慎地使用private继承","条款39 审慎地使用private继承",[783,9455,9456,9463],{},[786,9457,9458,9459,9462],{},"private继承意味着is-implemented-in-terms-of。通常比复合的级别低(",[1292,9460,9461],{},"优先使用聚合，但不知道此处级别低有没有其他含义",")。但当子类需要访问基类protected\n成员或需要重新定义virtual函数时，可以使用private继承",[786,9464,9465,9466,9475],{},"和复合不同，private继承可以使empty base optimization(EBO，空白基类最优化)，可使对象尺寸最小化",[1183,9467,9468],{},[280,9469,9470,9471,9474],{},"空白类不是专指这样",[18,9472,9473],{},"class A {};","，类中什么都没有。可能含有typedefs，enums，static成员变量\n或non-virtual函数",[11,9476,9478],{"className":13,"code":9477,"language":15,"meta":16,"style":16},"class A {};\nclass B\n{\n  int i;\n  A a;\n};\n\u002F\u002F sizeof(B) > sizeof(int)\n\u002F\u002F 但是class B: private A { int i; }; 这样sizeof(B) == sizeof(int)成立\n",[18,9479,9480,9485,9490,9494,9499,9504,9508,9513],{"__ignoreMap":16},[21,9481,9482],{"class":23,"line":24},[21,9483,9484],{},"class A {};\n",[21,9486,9487],{"class":23,"line":30},[21,9488,9489],{},"class B\n",[21,9491,9492],{"class":23,"line":36},[21,9493,2296],{},[21,9495,9496],{"class":23,"line":42},[21,9497,9498],{},"  int i;\n",[21,9500,9501],{"class":23,"line":48},[21,9502,9503],{},"  A a;\n",[21,9505,9506],{"class":23,"line":88},[21,9507,51],{},[21,9509,9510],{"class":23,"line":94},[21,9511,9512],{},"\u002F\u002F sizeof(B) > sizeof(int)\n",[21,9514,9515],{"class":23,"line":100},[21,9516,9517],{},"\u002F\u002F 但是class B: private A { int i; }; 这样sizeof(B) == sizeof(int)成立\n",[1297,9519,9521],{"id":9520},"条款40-审慎使用多重继承","条款40 审慎使用多重继承",[53,9523,9524],{"id":9524},"模板与泛型编程",[1297,9526,9528],{"id":9527},"条款41-了解隐式接口和编译期多态","条款41 了解隐式接口和编译期多态",[11,9530,9532],{"className":13,"code":9531,"language":15,"meta":16,"style":16},"class Widget\n{\npublic:\n  Widget();\n  virtual ~Widget();\n  virtual std::size_t size() const;\n  virtual void normalize();\n  void swap(Widget&);\n  ...\n};\n\nvoid doProcessing(Widget& w)\n{\n  if(w.size() > 10 && w != otherWidget)\n  {\n    Widget temp(w);\n    temp.normalize();\n    temp.swap(w);\n  }\n}\n",[18,9533,9534,9538,9542,9546,9550,9555,9560,9565,9570,9574,9578,9582,9587,9591,9596,9600,9605,9610,9615,9619],{"__ignoreMap":16},[21,9535,9536],{"class":23,"line":24},[21,9537,5322],{},[21,9539,9540],{"class":23,"line":30},[21,9541,2296],{},[21,9543,9544],{"class":23,"line":36},[21,9545,1347],{},[21,9547,9548],{"class":23,"line":42},[21,9549,5932],{},[21,9551,9552],{"class":23,"line":48},[21,9553,9554],{},"  virtual ~Widget();\n",[21,9556,9557],{"class":23,"line":88},[21,9558,9559],{},"  virtual std::size_t size() const;\n",[21,9561,9562],{"class":23,"line":94},[21,9563,9564],{},"  virtual void normalize();\n",[21,9566,9567],{"class":23,"line":100},[21,9568,9569],{},"  void swap(Widget&);\n",[21,9571,9572],{"class":23,"line":106},[21,9573,2311],{},[21,9575,9576],{"class":23,"line":112},[21,9577,51],{},[21,9579,9580],{"class":23,"line":176},[21,9581,144],{"emptyLinePlaceholder":143},[21,9583,9584],{"class":23,"line":182},[21,9585,9586],{},"void doProcessing(Widget& w)\n",[21,9588,9589],{"class":23,"line":188},[21,9590,2296],{},[21,9592,9593],{"class":23,"line":194},[21,9594,9595],{},"  if(w.size() > 10 && w != otherWidget)\n",[21,9597,9598],{"class":23,"line":200},[21,9599,2788],{},[21,9601,9602],{"class":23,"line":206},[21,9603,9604],{},"    Widget temp(w);\n",[21,9606,9607],{"class":23,"line":212},[21,9608,9609],{},"    temp.normalize();\n",[21,9611,9612],{"class":23,"line":218},[21,9613,9614],{},"    temp.swap(w);\n",[21,9616,9617],{"class":23,"line":224},[21,9618,103],{},[21,9620,9621],{"class":23,"line":229},[21,9622,115],{},[280,9624,9625],{},"能在源码中找到的接口，称为显示接口，也就是它在源码中明确可见",[11,9627,9629],{"className":13,"code":9628,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nvoid doProcessing(T& w)\n{\n  if(w.size() > 10 && w != otherWidget)\n  {\n    Widget temp(w);\n    temp.normalize();\n    temp.swap(w);\n  }\n}\n",[18,9630,9631,9635,9640,9644,9648,9652,9656,9660,9664,9668],{"__ignoreMap":16},[21,9632,9633],{"class":23,"line":24},[21,9634,4705],{},[21,9636,9637],{"class":23,"line":30},[21,9638,9639],{},"void doProcessing(T& w)\n",[21,9641,9642],{"class":23,"line":36},[21,9643,2296],{},[21,9645,9646],{"class":23,"line":42},[21,9647,9595],{},[21,9649,9650],{"class":23,"line":48},[21,9651,2788],{},[21,9653,9654],{"class":23,"line":88},[21,9655,9604],{},[21,9657,9658],{"class":23,"line":94},[21,9659,9609],{},[21,9661,9662],{"class":23,"line":100},[21,9663,9614],{},[21,9665,9666],{"class":23,"line":106},[21,9667,103],{},[21,9669,9670],{"class":23,"line":112},[21,9671,115],{},[280,9673,9674,9675,9678],{},"w的类型T",[6109,9676,9677],{},"好像","必须支持size，normalize和swap成员函数以及其他，这些便是T必须支持的隐式接口",[280,9680,9681,9682,9685],{},"凡是涉及w的函数调用，例如",[18,9683,9684],{},"operator>","和operator!=，可能造成template具现化。以不同的template参数具现化\nfunction template会导致调用不同的函数，这就是编译期多态",[1297,9687,9689],{"id":9688},"条款42-typename的双重意义","条款42 typename的双重意义",[783,9691,9692,9695],{},[786,9693,9694],{},"声明template参数时，class与typename是一样的",[786,9696,9697],{},"请使用typename标识嵌套从属类型名称，但不得在继承时的基类列表和成员初始化列表内以它作为基类修饰符",[1297,9699,9701],{"id":9700},"条款43-处理模板化基类内的名称","条款43 处理模板化基类内的名称",[11,9703,9705],{"className":13,"code":9704,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nclass MsgSender\n{\npublic:\n  void sendClear();\n};\n\ntemplate\u003Ctypename T>\nclass LoggingMsgSender: public MsgSender\u003CT>\n{\npublic:\n  void sendClearMsg()\n  {\n    sendClear();  \u002F\u002F 无法通过编译，因为编译器无法知道MsgSender\u003CT>是否有sendClear函数\n    ...\n  }\n};\n",[18,9706,9707,9711,9716,9720,9724,9729,9733,9737,9741,9746,9750,9754,9759,9763,9768,9772,9776],{"__ignoreMap":16},[21,9708,9709],{"class":23,"line":24},[21,9710,4705],{},[21,9712,9713],{"class":23,"line":30},[21,9714,9715],{},"class MsgSender\n",[21,9717,9718],{"class":23,"line":36},[21,9719,2296],{},[21,9721,9722],{"class":23,"line":42},[21,9723,1347],{},[21,9725,9726],{"class":23,"line":48},[21,9727,9728],{},"  void sendClear();\n",[21,9730,9731],{"class":23,"line":88},[21,9732,51],{},[21,9734,9735],{"class":23,"line":94},[21,9736,144],{"emptyLinePlaceholder":143},[21,9738,9739],{"class":23,"line":100},[21,9740,4705],{},[21,9742,9743],{"class":23,"line":106},[21,9744,9745],{},"class LoggingMsgSender: public MsgSender\u003CT>\n",[21,9747,9748],{"class":23,"line":112},[21,9749,2296],{},[21,9751,9752],{"class":23,"line":176},[21,9753,1347],{},[21,9755,9756],{"class":23,"line":182},[21,9757,9758],{},"  void sendClearMsg()\n",[21,9760,9761],{"class":23,"line":188},[21,9762,2788],{},[21,9764,9765],{"class":23,"line":194},[21,9766,9767],{},"    sendClear();  \u002F\u002F 无法通过编译，因为编译器无法知道MsgSender\u003CT>是否有sendClear函数\n",[21,9769,9770],{"class":23,"line":200},[21,9771,7196],{},[21,9773,9774],{"class":23,"line":206},[21,9775,103],{},[21,9777,9778],{"class":23,"line":212},[21,9779,51],{},[783,9781,9782,9791,9797],{},[786,9783,5796,9784,9787,9788],{},[18,9785,9786],{},"this->","调用基类函数即",[18,9789,9790],{},"this->sendClear();",[786,9792,9793,9794],{},"使用using声明基类函数，",[18,9795,9796],{},"using MsgSender\u003CT>::sendClear;",[786,9798,9799,9800,9803],{},"明确指出函数位于基类，",[18,9801,9802],{},"MsgSender\u003CT>::sendClear();","，但如果被调用的为虚函数，就只会调用基类虚函数了",[1297,9805,9807],{"id":9806},"条款44-将与参数无关的代码抽离template","条款44 将与参数无关的代码抽离template",[1297,9809,9811],{"id":9810},"条款45-运用成员函数模板接受所有兼容类型","条款45 运用成员函数模板接受所有兼容类型",[1297,9813,9815],{"id":9814},"条款46-需要类型转换时请为模板定义非成员模板","条款46 需要类型转换时请为模板定义非成员模板",[11,9817,9819],{"className":13,"code":9818,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nclass Rational\n{\npublic:\n  Rational(const T& numberator = 0, const T& denominator = 1);\n};\ntemplate\u003Ctypename T>\nconst Rational\u003CT> operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n{\n...\n}\n\nRational\u003Cint> one_half(1, 2);\nRational\u003Cint> result = one_half * 2; \u002F\u002F无法通过编译\n",[18,9820,9821,9825,9829,9833,9837,9842,9846,9850,9855,9859,9863,9867,9871,9876],{"__ignoreMap":16},[21,9822,9823],{"class":23,"line":24},[21,9824,4705],{},[21,9826,9827],{"class":23,"line":30},[21,9828,8808],{},[21,9830,9831],{"class":23,"line":36},[21,9832,2296],{},[21,9834,9835],{"class":23,"line":42},[21,9836,1347],{},[21,9838,9839],{"class":23,"line":48},[21,9840,9841],{},"  Rational(const T& numberator = 0, const T& denominator = 1);\n",[21,9843,9844],{"class":23,"line":88},[21,9845,51],{},[21,9847,9848],{"class":23,"line":94},[21,9849,4705],{},[21,9851,9852],{"class":23,"line":100},[21,9853,9854],{},"const Rational\u003CT> operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n",[21,9856,9857],{"class":23,"line":106},[21,9858,2296],{},[21,9860,9861],{"class":23,"line":112},[21,9862,2256],{},[21,9864,9865],{"class":23,"line":176},[21,9866,115],{},[21,9868,9869],{"class":23,"line":182},[21,9870,144],{"emptyLinePlaceholder":143},[21,9872,9873],{"class":23,"line":188},[21,9874,9875],{},"Rational\u003Cint> one_half(1, 2);\n",[21,9877,9878],{"class":23,"line":194},[21,9879,9880],{},"Rational\u003Cint> result = one_half * 2; \u002F\u002F无法通过编译\n",[280,9882,9883,9886,9887,9890,9891],{},[18,9884,9885],{},"operator*","为函数模板，在使用",[18,9888,9889],{},"one_half * 2","时，会根据实参推到T,具现化一个",[18,9892,9885],{},[280,9894,9895,4507,9898,9901,9902,9905,9906],{},[18,9896,9897],{},"one_half",[18,9899,9900],{},"Rational\u003Cint>","类型，很容易推出T为int，但第二个实参\"2\"并不能推导出",[18,9903,9904],{},"const Rational\u003CT>&","\n中的T。函数模板并不会在通过实参推到T时，将实参进行隐式转换，即不能将\"2\"隐式构造为",[18,9907,9900],{},[280,9909,9910,9911,9913,9914,9916,9917,9919],{},"可将",[18,9912,9885],{},"声明为friend函数，在声明",[18,9915,9897],{},"时，",[18,9918,9900],{},"被具现化，friend也被自动声明，friend函数\n并非函数模板，因此在调用时可以隐式转换",[11,9921,9923],{"className":13,"code":9922,"language":15,"meta":16,"style":16},"template\u003Ctypename T>\nclass Rational\n{\npublic:\n  friend const Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs);\n};\ntemplate\u003Ctypename T>\nconst Rational\u003CT> operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n{\n...\n}\n",[18,9924,9925,9929,9933,9937,9941,9946,9950,9954,9958,9962,9966],{"__ignoreMap":16},[21,9926,9927],{"class":23,"line":24},[21,9928,4705],{},[21,9930,9931],{"class":23,"line":30},[21,9932,8808],{},[21,9934,9935],{"class":23,"line":36},[21,9936,2296],{},[21,9938,9939],{"class":23,"line":42},[21,9940,1347],{},[21,9942,9943],{"class":23,"line":48},[21,9944,9945],{},"  friend const Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs);\n",[21,9947,9948],{"class":23,"line":88},[21,9949,51],{},[21,9951,9952],{"class":23,"line":94},[21,9953,4705],{},[21,9955,9956],{"class":23,"line":100},[21,9957,9854],{},[21,9959,9960],{"class":23,"line":106},[21,9961,2296],{},[21,9963,9964],{"class":23,"line":112},[21,9965,2256],{},[21,9967,9968],{"class":23,"line":176},[21,9969,115],{},[280,9971,9972,9973,9975,9976,9978],{},"上述虽然可以通过编译，但由于类模板Rational中",[18,9974,9885],{},"是个普通函数的友元声明，而非模板函数，\n无法与下面的",[18,9977,9885],{},"模板函数的定义链接",[783,9980,9981,10029],{},[786,9982,9983,9984,10026,10028],{},"可直接在模板类中声明并定义友元",[11,9985,9987],{"className":13,"code":9986,"language":15,"meta":16,"style":16},"template \u003Ctypename T>\nclass Rational\n{\npublic:\n  friend const Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n  {\n  ...\n  }\n}\n",[18,9988,9989,9993,9997,10001,10005,10010,10014,10018,10022],{"__ignoreMap":16},[21,9990,9991],{"class":23,"line":24},[21,9992,1200],{},[21,9994,9995],{"class":23,"line":30},[21,9996,8808],{},[21,9998,9999],{"class":23,"line":36},[21,10000,2296],{},[21,10002,10003],{"class":23,"line":42},[21,10004,1347],{},[21,10006,10007],{"class":23,"line":48},[21,10008,10009],{},"  friend const Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n",[21,10011,10012],{"class":23,"line":88},[21,10013,2788],{},[21,10015,10016],{"class":23,"line":94},[21,10017,2311],{},[21,10019,10020],{"class":23,"line":100},[21,10021,103],{},[21,10023,10024],{"class":23,"line":106},[21,10025,115],{},[1447,10027],{},"由于类中定义默认为inline，可另外定义个函数进行相乘的逻辑，而用类内的函数调用",[786,10030,10031,10032],{},"类前声明函数，类内声明友元，类外定义函数",[11,10033,10035],{"className":13,"code":10034,"language":15,"meta":16,"style":16},"template \u003Ctypename T> clas Rational;\ntemplate \u003Ctypename T>\nconst Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs);\n\ntemplate \u003Ctypename T>\nclass Rational\n{\npublic:\n  friend const Rational operator*\u003CT>(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n}\n\ntemplate\u003Ctypename T>\nconst Rational\u003CT> operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n{\n...\n}\n",[18,10036,10037,10042,10046,10051,10055,10059,10063,10067,10071,10076,10080,10084,10088,10092,10096,10100],{"__ignoreMap":16},[21,10038,10039],{"class":23,"line":24},[21,10040,10041],{},"template \u003Ctypename T> clas Rational;\n",[21,10043,10044],{"class":23,"line":30},[21,10045,1200],{},[21,10047,10048],{"class":23,"line":36},[21,10049,10050],{},"const Rational operator*(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs);\n",[21,10052,10053],{"class":23,"line":42},[21,10054,144],{"emptyLinePlaceholder":143},[21,10056,10057],{"class":23,"line":48},[21,10058,1200],{},[21,10060,10061],{"class":23,"line":88},[21,10062,8808],{},[21,10064,10065],{"class":23,"line":94},[21,10066,2296],{},[21,10068,10069],{"class":23,"line":100},[21,10070,1347],{},[21,10072,10073],{"class":23,"line":106},[21,10074,10075],{},"  friend const Rational operator*\u003CT>(const Rational\u003CT>& lhs, const Rational\u003CT>& rhs)\n",[21,10077,10078],{"class":23,"line":112},[21,10079,115],{},[21,10081,10082],{"class":23,"line":176},[21,10083,144],{"emptyLinePlaceholder":143},[21,10085,10086],{"class":23,"line":182},[21,10087,4705],{},[21,10089,10090],{"class":23,"line":188},[21,10091,9854],{},[21,10093,10094],{"class":23,"line":194},[21,10095,2296],{},[21,10097,10098],{"class":23,"line":200},[21,10099,2256],{},[21,10101,10102],{"class":23,"line":206},[21,10103,115],{},[1297,10105,10107],{"id":10106},"条款47-使用traits-classes表现类型信息","条款47 使用traits classes表现类型信息",[783,10109,10110,10113],{},[786,10111,10112],{},"Traits classes使得类型相关信息在编译期可用，它们以template和template特化完成实现",[786,10114,10115,10116,10161,10163,10164],{},"通过重载，traits classes可以在编译期对类型进行if...else",[11,10117,10119],{"className":13,"code":10118,"language":15,"meta":16,"style":16},"template \u003Ctypename IterT, typename DistT>\nvoid Advance(IterT& iter, DistT d)\n{\n  if(typeid(typename std::iterator_traits\u003CIterT>::iterator_category))\n        == typeid(std::random_access_iterator_tag)\n  {\n  ...\n  }\n}\n",[18,10120,10121,10126,10131,10135,10140,10145,10149,10153,10157],{"__ignoreMap":16},[21,10122,10123],{"class":23,"line":24},[21,10124,10125],{},"template \u003Ctypename IterT, typename DistT>\n",[21,10127,10128],{"class":23,"line":30},[21,10129,10130],{},"void Advance(IterT& iter, DistT d)\n",[21,10132,10133],{"class":23,"line":36},[21,10134,2296],{},[21,10136,10137],{"class":23,"line":42},[21,10138,10139],{},"  if(typeid(typename std::iterator_traits\u003CIterT>::iterator_category))\n",[21,10141,10142],{"class":23,"line":48},[21,10143,10144],{},"        == typeid(std::random_access_iterator_tag)\n",[21,10146,10147],{"class":23,"line":88},[21,10148,2788],{},[21,10150,10151],{"class":23,"line":94},[21,10152,2311],{},[21,10154,10155],{"class":23,"line":100},[21,10156,103],{},[21,10158,10159],{"class":23,"line":106},[21,10160,115],{},[1447,10162],{},"在编译期就知道IterT的类型，但if语句在运行时才会核定，因此可通过函数重载实现在编译时的\nif判断",[11,10165,10167],{"className":13,"code":10166,"language":15,"meta":16,"style":16},"template \u003Ctypename IterT, typename DistT>\nvoid doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag)\n{\n  iter += d;\n}\ntemplate \u003Ctypename IterT, typename DistT>\nvoid doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)\n{\n  if(d >= 0) {while(d--) ++iter;}\n  else {while(d++) --iter;}\n}\n\ntemplate \u003Ctypename IterT, typename DistT>\nvoid Advance(IterT& iter, DistT d)\n{\n  doAdvance(iter, d, typename std::iterator_traits\u003CIterT>::iterator_category)\n}\n",[18,10168,10169,10173,10178,10182,10187,10191,10195,10200,10204,10209,10214,10218,10222,10226,10230,10234,10239],{"__ignoreMap":16},[21,10170,10171],{"class":23,"line":24},[21,10172,10125],{},[21,10174,10175],{"class":23,"line":30},[21,10176,10177],{},"void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag)\n",[21,10179,10180],{"class":23,"line":36},[21,10181,2296],{},[21,10183,10184],{"class":23,"line":42},[21,10185,10186],{},"  iter += d;\n",[21,10188,10189],{"class":23,"line":48},[21,10190,115],{},[21,10192,10193],{"class":23,"line":88},[21,10194,10125],{},[21,10196,10197],{"class":23,"line":94},[21,10198,10199],{},"void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)\n",[21,10201,10202],{"class":23,"line":100},[21,10203,2296],{},[21,10205,10206],{"class":23,"line":106},[21,10207,10208],{},"  if(d >= 0) {while(d--) ++iter;}\n",[21,10210,10211],{"class":23,"line":112},[21,10212,10213],{},"  else {while(d++) --iter;}\n",[21,10215,10216],{"class":23,"line":176},[21,10217,115],{},[21,10219,10220],{"class":23,"line":182},[21,10221,144],{"emptyLinePlaceholder":143},[21,10223,10224],{"class":23,"line":188},[21,10225,10125],{},[21,10227,10228],{"class":23,"line":194},[21,10229,10130],{},[21,10231,10232],{"class":23,"line":200},[21,10233,2296],{},[21,10235,10236],{"class":23,"line":206},[21,10237,10238],{},"  doAdvance(iter, d, typename std::iterator_traits\u003CIterT>::iterator_category)\n",[21,10240,10241],{"class":23,"line":212},[21,10242,115],{},[1297,10244,10246],{"id":10245},"条款48-template元编程","条款48 template元编程",[53,10248,10249],{"id":10249},"定制new和delete",[1297,10251,10253],{"id":10252},"条款49-new-handler的行为","条款49 new-handler的行为",[280,10255,10256,10257],{},"当operator new抛出异常前，会先调用客户指定的错误处理函数new-handler。为了指定该函数，必须调用",[18,10258,10259],{},"set_new_handler",[11,10261,10263],{"className":13,"code":10262,"language":15,"meta":16,"style":16},"namespace std\n{\n  typedef void (*new_handler)();\n  new_handler set_new_handler(new_handler p) throw();\n}\n",[18,10264,10265,10269,10273,10278,10283],{"__ignoreMap":16},[21,10266,10267],{"class":23,"line":24},[21,10268,8952],{},[21,10270,10271],{"class":23,"line":30},[21,10272,2296],{},[21,10274,10275],{"class":23,"line":36},[21,10276,10277],{},"  typedef void (*new_handler)();\n",[21,10279,10280],{"class":23,"line":42},[21,10281,10282],{},"  new_handler set_new_handler(new_handler p) throw();\n",[21,10284,10285],{"class":23,"line":48},[21,10286,115],{},[280,10288,10289,10291],{},[18,10290,10259],{},"返回值指向的是被调用前正在执行(但马上要被p替换的)那个new-handler函数",[11,10293,10295],{"className":13,"code":10294,"language":15,"meta":16,"style":16},"void OutOfMemeory()\n{\n  std::cerr \u003C\u003C \"...\" \u003C\u003C \"\\n\";\n  std::abort();\n}\n\nint main()\n{\n  std::set_new_handler(OutOfMemeory);\n  int* pBigDataArrary = new int[100000000L];\n}\n",[18,10296,10297,10302,10306,10311,10316,10320,10324,10328,10332,10337,10342],{"__ignoreMap":16},[21,10298,10299],{"class":23,"line":24},[21,10300,10301],{},"void OutOfMemeory()\n",[21,10303,10304],{"class":23,"line":30},[21,10305,2296],{},[21,10307,10308],{"class":23,"line":36},[21,10309,10310],{},"  std::cerr \u003C\u003C \"...\" \u003C\u003C \"\\n\";\n",[21,10312,10313],{"class":23,"line":42},[21,10314,10315],{},"  std::abort();\n",[21,10317,10318],{"class":23,"line":48},[21,10319,115],{},[21,10321,10322],{"class":23,"line":88},[21,10323,144],{"emptyLinePlaceholder":143},[21,10325,10326],{"class":23,"line":94},[21,10327,4098],{},[21,10329,10330],{"class":23,"line":100},[21,10331,2296],{},[21,10333,10334],{"class":23,"line":106},[21,10335,10336],{},"  std::set_new_handler(OutOfMemeory);\n",[21,10338,10339],{"class":23,"line":112},[21,10340,10341],{},"  int* pBigDataArrary = new int[100000000L];\n",[21,10343,10344],{"class":23,"line":176},[21,10345,115],{},[280,10347,10348],{},"有时候需要以不同的方式处理内存分配失败的情况，希望不同的class有不同的处理",[11,10350,10352],{"className":13,"code":10351,"language":15,"meta":16,"style":16},"class A\n{\npublic:\n  static void OutOfMemeory();\n};\nclass B\n{\npublic:\n  static void OutOfMemeory();\n};\n\nA* pa = new A; \u002F\u002F 分配失败时调用A::OutOfMemeory()\nB* pb = new B; \u002F\u002F 分配失败时调用B::OutOfMemeory()\n",[18,10353,10354,10359,10363,10367,10372,10376,10380,10384,10388,10392,10396,10400,10405],{"__ignoreMap":16},[21,10355,10356],{"class":23,"line":24},[21,10357,10358],{},"class A\n",[21,10360,10361],{"class":23,"line":30},[21,10362,2296],{},[21,10364,10365],{"class":23,"line":36},[21,10366,1347],{},[21,10368,10369],{"class":23,"line":42},[21,10370,10371],{},"  static void OutOfMemeory();\n",[21,10373,10374],{"class":23,"line":48},[21,10375,51],{},[21,10377,10378],{"class":23,"line":88},[21,10379,9489],{},[21,10381,10382],{"class":23,"line":94},[21,10383,2296],{},[21,10385,10386],{"class":23,"line":100},[21,10387,1347],{},[21,10389,10390],{"class":23,"line":106},[21,10391,10371],{},[21,10393,10394],{"class":23,"line":112},[21,10395,51],{},[21,10397,10398],{"class":23,"line":176},[21,10399,144],{"emptyLinePlaceholder":143},[21,10401,10402],{"class":23,"line":182},[21,10403,10404],{},"A* pa = new A; \u002F\u002F 分配失败时调用A::OutOfMemeory()\n",[21,10406,10407],{"class":23,"line":188},[21,10408,10409],{},"B* pb = new B; \u002F\u002F 分配失败时调用B::OutOfMemeory()\n",[280,10411,10412,10413,10415],{},"并不支持class的专属new-handler，但为每个class提供自己的",[18,10414,10259],{},"和operator new即可",[783,10417,10418,10423],{},[786,10419,10420,10422],{},[18,10421,10259],{},"指定class的专属new-handler",[786,10424,10425],{},"operator new确保在分配class对象内存时，以class专属new-handler替换global new-handler",[11,10427,10429],{"className":13,"code":10428,"language":15,"meta":16,"style":16},"class Widget\n{\npublic:\n  static std::new_handler set_new_handler(std::new_handler p) throw();\n  static void* operator new(std::size_t size) throw(std::bad_alloc);\nprivate:\n  static std::new_handler current_handler;\n};\n\nstd::new_handler Widget::current_handler = nullptr;\nstd::new_handler Widget::set_new_handler(std::new_handler p) throw()\n{\n  std::new_handler old_handler = current_handler;\n  current_handler = p;\n  return old_handler;\n}\n",[18,10430,10431,10435,10439,10443,10448,10453,10457,10462,10466,10470,10475,10480,10484,10489,10494,10499],{"__ignoreMap":16},[21,10432,10433],{"class":23,"line":24},[21,10434,5322],{},[21,10436,10437],{"class":23,"line":30},[21,10438,2296],{},[21,10440,10441],{"class":23,"line":36},[21,10442,1347],{},[21,10444,10445],{"class":23,"line":42},[21,10446,10447],{},"  static std::new_handler set_new_handler(std::new_handler p) throw();\n",[21,10449,10450],{"class":23,"line":48},[21,10451,10452],{},"  static void* operator new(std::size_t size) throw(std::bad_alloc);\n",[21,10454,10455],{"class":23,"line":88},[21,10456,1316],{},[21,10458,10459],{"class":23,"line":94},[21,10460,10461],{},"  static std::new_handler current_handler;\n",[21,10463,10464],{"class":23,"line":100},[21,10465,51],{},[21,10467,10468],{"class":23,"line":106},[21,10469,144],{"emptyLinePlaceholder":143},[21,10471,10472],{"class":23,"line":112},[21,10473,10474],{},"std::new_handler Widget::current_handler = nullptr;\n",[21,10476,10477],{"class":23,"line":176},[21,10478,10479],{},"std::new_handler Widget::set_new_handler(std::new_handler p) throw()\n",[21,10481,10482],{"class":23,"line":182},[21,10483,2296],{},[21,10485,10486],{"class":23,"line":188},[21,10487,10488],{},"  std::new_handler old_handler = current_handler;\n",[21,10490,10491],{"class":23,"line":194},[21,10492,10493],{},"  current_handler = p;\n",[21,10495,10496],{"class":23,"line":200},[21,10497,10498],{},"  return old_handler;\n",[21,10500,10501],{"class":23,"line":206},[21,10502,115],{},[280,10504,10505,10506,10509],{},"在operator new中，调用",[18,10507,10508],{},"std::set_new_handler","，当global operator new无法分配足够内存，抛出异常后需要\n将global new-handler恢复。而成功分配内存也需要恢复global new-handler，因此创建资源管理类",[11,10511,10513],{"className":13,"code":10512,"language":15,"meta":16,"style":16},"class NewHandlerHolder\n{\npublic:\n  explicit NewHandlerHolder(std::new_handler nh): handler(nh) {}\n  ~NewHandlerHolder()\n  {\n    std::set_new_handler(handler);\n  }\n  NewHandlerHolder(const NewHandlerHolder&) = delete;\n  NewHandlerHolder& operator=(const NewHandlerHolder&)\n\nprivate:\n  std::new_handler handler;\n};\n",[18,10514,10515,10520,10524,10528,10533,10538,10542,10547,10551,10556,10561,10565,10569,10574],{"__ignoreMap":16},[21,10516,10517],{"class":23,"line":24},[21,10518,10519],{},"class NewHandlerHolder\n",[21,10521,10522],{"class":23,"line":30},[21,10523,2296],{},[21,10525,10526],{"class":23,"line":36},[21,10527,1347],{},[21,10529,10530],{"class":23,"line":42},[21,10531,10532],{},"  explicit NewHandlerHolder(std::new_handler nh): handler(nh) {}\n",[21,10534,10535],{"class":23,"line":48},[21,10536,10537],{},"  ~NewHandlerHolder()\n",[21,10539,10540],{"class":23,"line":88},[21,10541,2788],{},[21,10543,10544],{"class":23,"line":94},[21,10545,10546],{},"    std::set_new_handler(handler);\n",[21,10548,10549],{"class":23,"line":100},[21,10550,103],{},[21,10552,10553],{"class":23,"line":106},[21,10554,10555],{},"  NewHandlerHolder(const NewHandlerHolder&) = delete;\n",[21,10557,10558],{"class":23,"line":112},[21,10559,10560],{},"  NewHandlerHolder& operator=(const NewHandlerHolder&)\n",[21,10562,10563],{"class":23,"line":176},[21,10564,144],{"emptyLinePlaceholder":143},[21,10566,10567],{"class":23,"line":182},[21,10568,1316],{},[21,10570,10571],{"class":23,"line":188},[21,10572,10573],{},"  std::new_handler handler;\n",[21,10575,10576],{"class":23,"line":194},[21,10577,51],{},[280,10579,10580],{},"此时，operator new实现",[11,10582,10584],{"className":13,"code":10583,"language":15,"meta":16,"style":16},"void* Widget::operator new(std::size_t size) throw(bad_alloc)\n{\n  NewHandlerHolder h(std::set_new_handler(current_handler));\n  return ::operator new(size);\n}\n",[18,10585,10586,10591,10595,10600,10605],{"__ignoreMap":16},[21,10587,10588],{"class":23,"line":24},[21,10589,10590],{},"void* Widget::operator new(std::size_t size) throw(bad_alloc)\n",[21,10592,10593],{"class":23,"line":30},[21,10594,2296],{},[21,10596,10597],{"class":23,"line":36},[21,10598,10599],{},"  NewHandlerHolder h(std::set_new_handler(current_handler));\n",[21,10601,10602],{"class":23,"line":42},[21,10603,10604],{},"  return ::operator new(size);\n",[21,10606,10607],{"class":23,"line":48},[21,10608,115],{},[280,10610,10611,10612,10614],{},"实现这一方案的代码并不因为class的不同而改变，类中的operator new，",[18,10613,10259],{},"函数可以进行复用\n而非在每个想要自定义new-handler的类中，声明这些static函数以及定义static变量",[280,10616,10617,10618,10621,10622,10624],{},"由于",[18,10619,10620],{},"current_handler","为静态变量，如果直接继承，多个子类中的",[18,10623,10620],{},"是同一个，因此将父类定义成模板\n每个子类继承的父类就不同",[11,10626,10628],{"className":13,"code":10627,"language":15,"meta":16,"style":16},"template \u003Ctypename T> \u002F\u002F T并没有被使用，只是便于生成不同的父类\nclass NewHandlerSupport\n{\npublic:\n  static std::new_handler set_new_handler(std::new_handler p) throw();\n  static void* operator new(std::size_t size) throw(std::bad_alloc);\nprivate:\n  static std::new_handler current_handler;\n};\n\nclass Widget: public NewHandlerSupport\u003CWidget>\n{\n...\n};\n",[18,10629,10630,10635,10640,10644,10648,10652,10656,10660,10664,10668,10672,10677,10681,10685],{"__ignoreMap":16},[21,10631,10632],{"class":23,"line":24},[21,10633,10634],{},"template \u003Ctypename T> \u002F\u002F T并没有被使用，只是便于生成不同的父类\n",[21,10636,10637],{"class":23,"line":30},[21,10638,10639],{},"class NewHandlerSupport\n",[21,10641,10642],{"class":23,"line":36},[21,10643,2296],{},[21,10645,10646],{"class":23,"line":42},[21,10647,1347],{},[21,10649,10650],{"class":23,"line":48},[21,10651,10447],{},[21,10653,10654],{"class":23,"line":88},[21,10655,10452],{},[21,10657,10658],{"class":23,"line":94},[21,10659,1316],{},[21,10661,10662],{"class":23,"line":100},[21,10663,10461],{},[21,10665,10666],{"class":23,"line":106},[21,10667,51],{},[21,10669,10670],{"class":23,"line":112},[21,10671,144],{"emptyLinePlaceholder":143},[21,10673,10674],{"class":23,"line":176},[21,10675,10676],{},"class Widget: public NewHandlerSupport\u003CWidget>\n",[21,10678,10679],{"class":23,"line":182},[21,10680,2296],{},[21,10682,10683],{"class":23,"line":188},[21,10684,2256],{},[21,10686,10687],{"class":23,"line":194},[21,10688,51],{},[280,10690,10691,10692,10695],{},"operator new无法分配足够内存时，应该抛出",[18,10693,10694],{},"bad_alloc","异常，但同时也可指定返回0即",[11,10697,10699],{"className":13,"code":10698,"language":15,"meta":16,"style":16},"Widget* p1 = new Widget;                \u002F\u002F 失败时抛出异常\nWidget* p2 = new(std::nothrow) Widget;  \u002F\u002F 失败时返回0\n",[18,10700,10701,10706],{"__ignoreMap":16},[21,10702,10703],{"class":23,"line":24},[21,10704,10705],{},"Widget* p1 = new Widget;                \u002F\u002F 失败时抛出异常\n",[21,10707,10708],{"class":23,"line":30},[21,10709,10710],{},"Widget* p2 = new(std::nothrow) Widget;  \u002F\u002F 失败时返回0\n",[1183,10712,10713],{},[280,10714,10715],{},"nothrow只是保证operator new，而new分配内存调用operator new后还会调用构造函数，因此new(std::nothrow) Widget还是会有异常",[1297,10717,10719],{"id":10718},"条款50-new和delete的合理替换时机","条款50 new和delete的合理替换时机",[1297,10721,10723],{"id":10722},"条款51-编写new和delete时需固守常规","条款51 编写new和delete时需固守常规",[783,10725,10726,10872],{},[786,10727,10728,10729,10828,10830,10831],{},"operator new应该含有一个无限循环，在循环中分配内存，如果无法满足内存需求，就该调用new-handler。同时\n也应该有处理0字节的申请。以及class专属版本中，如果子类(Derived)继承父类(Base)的operator new，此时的大小\n为sizeof(Derived)，而调用的时Base::operator new，函数可能有依照sizeof(Base)设计的其他功能",[11,10730,10732],{"className":13,"code":10731,"language":15,"meta":16,"style":16},"void* operator new(std::size_t size) throw(std::bad_alloc)\n{\n  if(size == 0) \u002F\u002F处理0字节申请\n    size = 1;\n  while(true)\n  {\n    分配size大小的内存\n    if(分配成功)\n      return 地址;\n\n    \u002F\u002F 获取当前new-handler，只能通过set_new_handler获得\n    std::new_handler global_handler = set_new_handler(nullptr);\n    set_new_handler(global_handler);\n\n    if(global_handler)\n      global_handler();\n    \u002F\u002F 只有当new-handler指针为null时，才抛出异常\n    else throw std::bad_alloc();\n  }\n}\n",[18,10733,10734,10739,10743,10748,10753,10758,10762,10767,10772,10777,10781,10786,10791,10796,10800,10805,10810,10815,10820,10824],{"__ignoreMap":16},[21,10735,10736],{"class":23,"line":24},[21,10737,10738],{},"void* operator new(std::size_t size) throw(std::bad_alloc)\n",[21,10740,10741],{"class":23,"line":30},[21,10742,2296],{},[21,10744,10745],{"class":23,"line":36},[21,10746,10747],{},"  if(size == 0) \u002F\u002F处理0字节申请\n",[21,10749,10750],{"class":23,"line":42},[21,10751,10752],{},"    size = 1;\n",[21,10754,10755],{"class":23,"line":48},[21,10756,10757],{},"  while(true)\n",[21,10759,10760],{"class":23,"line":88},[21,10761,2788],{},[21,10763,10764],{"class":23,"line":94},[21,10765,10766],{},"    分配size大小的内存\n",[21,10768,10769],{"class":23,"line":100},[21,10770,10771],{},"    if(分配成功)\n",[21,10773,10774],{"class":23,"line":106},[21,10775,10776],{},"      return 地址;\n",[21,10778,10779],{"class":23,"line":112},[21,10780,144],{"emptyLinePlaceholder":143},[21,10782,10783],{"class":23,"line":176},[21,10784,10785],{},"    \u002F\u002F 获取当前new-handler，只能通过set_new_handler获得\n",[21,10787,10788],{"class":23,"line":182},[21,10789,10790],{},"    std::new_handler global_handler = set_new_handler(nullptr);\n",[21,10792,10793],{"class":23,"line":188},[21,10794,10795],{},"    set_new_handler(global_handler);\n",[21,10797,10798],{"class":23,"line":194},[21,10799,144],{"emptyLinePlaceholder":143},[21,10801,10802],{"class":23,"line":200},[21,10803,10804],{},"    if(global_handler)\n",[21,10806,10807],{"class":23,"line":206},[21,10808,10809],{},"      global_handler();\n",[21,10811,10812],{"class":23,"line":212},[21,10813,10814],{},"    \u002F\u002F 只有当new-handler指针为null时，才抛出异常\n",[21,10816,10817],{"class":23,"line":218},[21,10818,10819],{},"    else throw std::bad_alloc();\n",[21,10821,10822],{"class":23,"line":224},[21,10823,103],{},[21,10825,10826],{"class":23,"line":229},[21,10827,115],{},[1447,10829],{},"Base class专属operator new",[11,10832,10834],{"className":13,"code":10833,"language":15,"meta":16,"style":16},"void* Base::operator new(std::size_t size) throw(std::bad_alloc)\n{\n  \u002F\u002F size为0或子类调用时，size与sizeof(Base)不等\n  if(size != sizeof(Base))\n    return ::operator new(size)\n\n  ...\n}\n",[18,10835,10836,10841,10845,10850,10855,10860,10864,10868],{"__ignoreMap":16},[21,10837,10838],{"class":23,"line":24},[21,10839,10840],{},"void* Base::operator new(std::size_t size) throw(std::bad_alloc)\n",[21,10842,10843],{"class":23,"line":30},[21,10844,2296],{},[21,10846,10847],{"class":23,"line":36},[21,10848,10849],{},"  \u002F\u002F size为0或子类调用时，size与sizeof(Base)不等\n",[21,10851,10852],{"class":23,"line":42},[21,10853,10854],{},"  if(size != sizeof(Base))\n",[21,10856,10857],{"class":23,"line":48},[21,10858,10859],{},"    return ::operator new(size)\n",[21,10861,10862],{"class":23,"line":88},[21,10863,144],{"emptyLinePlaceholder":143},[21,10865,10866],{"class":23,"line":94},[21,10867,2311],{},[21,10869,10870],{"class":23,"line":100},[21,10871,115],{},[786,10873,10874,10875],{},"operator delete在收到nullptr时，不做任何事。class专属的operator delete应该处理大小不同的问题",[11,10876,10878],{"className":13,"code":10877,"language":15,"meta":16,"style":16},"void Base::operator delete(void* raw_memory, std::size_t size)\n{\n  if(raw_memory)\n  {\n    if(size != sizeof(Base))\n    {\n      ::operator delete(raw_memory);\n      return;\n    }\n    return;\n  }\n}\n",[18,10879,10880,10885,10889,10894,10898,10903,10908,10913,10918,10922,10927,10931],{"__ignoreMap":16},[21,10881,10882],{"class":23,"line":24},[21,10883,10884],{},"void Base::operator delete(void* raw_memory, std::size_t size)\n",[21,10886,10887],{"class":23,"line":30},[21,10888,2296],{},[21,10890,10891],{"class":23,"line":36},[21,10892,10893],{},"  if(raw_memory)\n",[21,10895,10896],{"class":23,"line":42},[21,10897,2788],{},[21,10899,10900],{"class":23,"line":48},[21,10901,10902],{},"    if(size != sizeof(Base))\n",[21,10904,10905],{"class":23,"line":88},[21,10906,10907],{},"    {\n",[21,10909,10910],{"class":23,"line":94},[21,10911,10912],{},"      ::operator delete(raw_memory);\n",[21,10914,10915],{"class":23,"line":100},[21,10916,10917],{},"      return;\n",[21,10919,10920],{"class":23,"line":106},[21,10921,215],{},[21,10923,10924],{"class":23,"line":112},[21,10925,10926],{},"    return;\n",[21,10928,10929],{"class":23,"line":176},[21,10930,103],{},[21,10932,10933],{"class":23,"line":182},[21,10934,115],{},[1297,10936,10938],{"id":10937},"条款52-写了placement-new也应有placement-delete","条款52 写了placement new也应有placement delete",[783,10940,10941,10995],{},[786,10942,10943,10944,10946,10947,10952,10954,10955,10957,10958,10992,10994],{},"写了placement operator new也应该写出对于的placement operator delete",[1447,10945],{},"placement new指接受额外参数的operator new，placement delete指接受额外参数的operator delete",[1183,10948,10949],{},[280,10950,10951],{},"特别的一个placement new是，接受一个指针，指向对象该被构造的地址",[1447,10953],{},"operator new成功分配内存，但对象构造时出现异常后，会调用operator delete恢复原先的内存，\n而使用placement new后，如果没有对应的placement delete，就不会有任何的operator delete被调用",[1447,10956],{},"如果没有发生异常，在最终释放分配空间时",[11,10959,10961],{"className":13,"code":10960,"language":15,"meta":16,"style":16},"void* Widget::operator new(std::size_t size, std::ostream& log_stream) throw(std::bad_alloc);\nvoid Widget::operator delete(void* pmemory) throw();\nvoid Widget::operator delete(void* pmemory, std::ostream& log_stream) throw();\n\nWidget* pw = new(std::cerr) Widget;\ndelete pw;\n",[18,10962,10963,10968,10973,10978,10982,10987],{"__ignoreMap":16},[21,10964,10965],{"class":23,"line":24},[21,10966,10967],{},"void* Widget::operator new(std::size_t size, std::ostream& log_stream) throw(std::bad_alloc);\n",[21,10969,10970],{"class":23,"line":30},[21,10971,10972],{},"void Widget::operator delete(void* pmemory) throw();\n",[21,10974,10975],{"class":23,"line":36},[21,10976,10977],{},"void Widget::operator delete(void* pmemory, std::ostream& log_stream) throw();\n",[21,10979,10980],{"class":23,"line":42},[21,10981,144],{"emptyLinePlaceholder":143},[21,10983,10984],{"class":23,"line":48},[21,10985,10986],{},"Widget* pw = new(std::cerr) Widget;\n",[21,10988,10989],{"class":23,"line":88},[21,10990,10991],{},"delete pw;\n",[1447,10993],{},"delete pw调用的是正常的operator new而非placement delete。placement delete只有在\"伴随placement new调用而触发\n的构造函数\"出现异常时才会被调用。因此，除了placement delete外，还需提供一个正常的operator delete",[786,10996,10997,10998,11000,11001],{},"当你声明placement new和placement delete，请不要遮掩正常版本",[1447,10999],{},"成员函数的名称会遮掩外围作用域中相同的名称",[11,11002,11004],{"className":13,"code":11003,"language":15,"meta":16,"style":16},"class Base\n{\npublic:\n  static void* operator new(std::size_t size, std::ostream& log_stream) throw(std::bad_alloc);\n};\nBase* pb = new Base;            \u002F\u002F 错误，正常形式的operator new被遮掩\nBase* pb = new(std::cerr) Base;\n",[18,11005,11006,11010,11014,11018,11023,11027,11032],{"__ignoreMap":16},[21,11007,11008],{"class":23,"line":24},[21,11009,9220],{},[21,11011,11012],{"class":23,"line":30},[21,11013,2296],{},[21,11015,11016],{"class":23,"line":36},[21,11017,1347],{},[21,11019,11020],{"class":23,"line":42},[21,11021,11022],{},"  static void* operator new(std::size_t size, std::ostream& log_stream) throw(std::bad_alloc);\n",[21,11024,11025],{"class":23,"line":48},[21,11026,51],{},[21,11028,11029],{"class":23,"line":88},[21,11030,11031],{},"Base* pb = new Base;            \u002F\u002F 错误，正常形式的operator new被遮掩\n",[21,11033,11034],{"class":23,"line":94},[21,11035,11036],{},"Base* pb = new(std::cerr) Base;\n",[284,11038,286],{},{"title":16,"searchDepth":30,"depth":30,"links":11040},[11041,11047,11057,11064,11074,11082,11093,11103],{"id":7983,"depth":30,"text":7984,"children":11042},[11043,11044,11045,11046],{"id":7987,"depth":36,"text":7988},{"id":8008,"depth":36,"text":8009},{"id":8161,"depth":36,"text":8162},{"id":8268,"depth":36,"text":8269},{"id":8288,"depth":30,"text":8289,"children":11048},[11049,11050,11051,11052,11053,11054,11055,11056],{"id":8292,"depth":36,"text":8293},{"id":8296,"depth":36,"text":8297},{"id":8300,"depth":36,"text":8301},{"id":8317,"depth":36,"text":8318},{"id":8329,"depth":36,"text":8330},{"id":8333,"depth":36,"text":8334},{"id":8337,"depth":36,"text":8338},{"id":8542,"depth":36,"text":8543},{"id":8554,"depth":30,"text":8554,"children":11058},[11059,11060,11061,11062,11063],{"id":8557,"depth":36,"text":8558},{"id":8571,"depth":36,"text":8572},{"id":8663,"depth":36,"text":8664},{"id":8684,"depth":36,"text":8685},{"id":8688,"depth":36,"text":8689},{"id":8737,"depth":30,"text":8737,"children":11065},[11066,11067,11068,11069,11070,11071,11072,11073],{"id":8740,"depth":36,"text":8741},{"id":8744,"depth":36,"text":8745},{"id":8748,"depth":36,"text":8749},{"id":8776,"depth":36,"text":8777},{"id":8780,"depth":36,"text":8781},{"id":8790,"depth":36,"text":8791},{"id":8797,"depth":36,"text":8798},{"id":8890,"depth":36,"text":8891},{"id":9161,"depth":30,"text":9161,"children":11075},[11076,11077,11078,11079,11080,11081],{"id":9164,"depth":36,"text":9165},{"id":9168,"depth":36,"text":9169},{"id":9172,"depth":36,"text":9173},{"id":9181,"depth":36,"text":9182},{"id":9185,"depth":36,"text":9186},{"id":9189,"depth":36,"text":9190},{"id":9193,"depth":30,"text":9193,"children":11083},[11084,11085,11086,11087,11088,11089,11090,11091,11092],{"id":9196,"depth":36,"text":9197},{"id":9206,"depth":36,"text":9207},{"id":9272,"depth":36,"text":9273},{"id":9276,"depth":36,"text":9277},{"id":9296,"depth":36,"text":9297},{"id":9300,"depth":36,"text":9301},{"id":9378,"depth":36,"text":9379},{"id":9452,"depth":36,"text":9453},{"id":9520,"depth":36,"text":9521},{"id":9524,"depth":30,"text":9524,"children":11094},[11095,11096,11097,11098,11099,11100,11101,11102],{"id":9527,"depth":36,"text":9528},{"id":9688,"depth":36,"text":9689},{"id":9700,"depth":36,"text":9701},{"id":9806,"depth":36,"text":9807},{"id":9810,"depth":36,"text":9811},{"id":9814,"depth":36,"text":9815},{"id":10106,"depth":36,"text":10107},{"id":10245,"depth":36,"text":10246},{"id":10249,"depth":30,"text":10249,"children":11104},[11105,11106,11107,11108],{"id":10252,"depth":36,"text":10253},{"id":10718,"depth":36,"text":10719},{"id":10722,"depth":36,"text":10723},{"id":10937,"depth":36,"text":10938},"2023-05-18",{},"\u002Fblog\u002Feffective-cplusplus",{"title":7978,"description":294},"blog\u002Feffective-cplusplus",[1175],"UFl8iHZ1IL-i5EB-GiuKcNMwvm192v0NekQJsayKiJM",{"id":11117,"title":11118,"body":11119,"date":12143,"description":294,"extension":295,"meta":12144,"navigation":143,"path":12145,"seo":12146,"stem":12147,"tags":12148,"__hash__":12150},"blog\u002Fblog\u002Fgit-learning.md","Git学习记录",{"type":8,"value":11120,"toc":12122},[11121,11131,11134,11187,11191,11194,11208,11310,11313,11318,11411,11414,11420,11426,11429,11455,11460,11463,11529,11532,11544,11547,11608,11610,11615,11619,11690,11692,11705,11719,11741,11747,11750,11756,11758,11762,11785,11788,11795,11801,11819,11825,11832,11838,11841,11844,11850,11853,11856,11859,11873,11887,11889,11895,11908,11913,11917,11924,11929,11932,11935,11938,11979,11982,11992,11998,12005,12011,12018,12020,12031,12034,12072,12075,12097,12100,12120],[1183,11122,11123],{},[280,11124,11125,11126],{},"git官网书籍",[531,11127,11130],{"href":11128,"rel":11129},"https:\u002F\u002Fgit-scm.com\u002Fbook\u002Fzh\u002Fv2",[535],"《Pro Git》",[53,11132,11133],{"id":11133},"目录",[783,11135,11136,11162,11173,11184],{},[786,11137,11138,11139],{},"基础\n",[783,11140,11141,11144,11147,11150,11153,11156,11159],{},[786,11142,11143],{},"更新与提交：add, status, diff, commit, rm, mv",[786,11145,11146],{},"提交历史：log",[786,11148,11149],{},"撤销：restore",[786,11151,11152],{},"远程仓库: remote",[786,11154,11155],{},"标签：tag",[786,11157,11158],{},"别名",[786,11160,11161],{},".gitingore",[786,11163,11164,11165],{},"分支\n",[783,11166,11167,11170],{},[786,11168,11169],{},"远程分支",[786,11171,11172],{},"变基：rebase",[786,11174,11175,11176],{},"原理\n",[783,11177,11178,11181],{},[786,11179,11180],{},"对象",[786,11182,11183],{},"引用",[786,11185,11186],{},"...",[53,11188,11190],{"id":11189},"git基础","Git基础",[1297,11192,11193],{"id":11193},"更新与提交",[280,11195,11196,11197,3771,11202,11207],{},"工作目录下的文件只有两种状态：",[8057,11198,11199],{},[6109,11200,11201],{},"已跟踪",[8057,11203,11204],{},[6109,11205,11206],{},"未跟踪","，对于已跟踪的文件，只有暂存的文件才会被提交",[783,11209,11210,11216,11222,11239,11256,11298],{},[786,11211,11212,11215],{},[18,11213,11214],{},"git status","查看状态",[786,11217,11218,11221],{},[18,11219,11220],{},"git add \u003Cfile>","跟踪文件以及将文件暂存(staged)",[786,11223,11224,11227,11228],{},[18,11225,11226],{},"git diff","查看未暂存的修改",[783,11229,11230],{},[786,11231,11232,3058,11235,11238],{},[18,11233,11234],{},"--staged",[18,11236,11237],{},"--cached","查看暂存文件与上次提交的文件差异",[786,11240,11241,11244,11245,11247,11248],{},[18,11242,11243],{},"git commit","提交文件，提交前",[18,11246,11214],{},"下确认所需文件是否在暂存区",[783,11249,11250],{},[786,11251,11252,11255],{},[18,11253,11254],{},"-a","可跳过暂存阶段，直接将跟踪文件暂存提交",[786,11257,11258,11261,11262,11265,11266,11269,11270,11273,11274,11277,11278,11280,11281,11284,11285],{},[18,11259,11260],{},"git rm \u003Cfile>","删除已跟踪文件。相当于",[18,11263,11264],{},"rm \u003Cfile>","后，",[18,11267,11268],{},"git add","进暂存区，与创建文件后",[18,11271,11272],{},"git add\u003Cfile>","对应",[1292,11275,11276],{},"个人理解，可能有误","。",[1447,11279],{},"当",[18,11282,11283],{},"\u003Cfile>","修改过或已暂存后，可使用：",[783,11286,11287,11293],{},[786,11288,11289,11292],{},[18,11290,11291],{},"-f","安全特性，防止未添加到快照的数据误删",[786,11294,11295,11297],{},[18,11296,11237],{},"仅仅从暂存区删除，文件还在",[786,11299,11300,11303,11304],{},[18,11301,11302],{},"git mv \u003Cfile_a> \u003Cfile_b>","等价于：",[11,11305,11308],{"className":11306,"code":11307,"language":4625},[4623],"$ mv file_a file_b\n$ git rm file_a\n$ git add file_b\n",[18,11309,11307],{"__ignoreMap":16},[1297,11311,11312],{"id":11312},"查看提交历史",[280,11314,11315],{},[18,11316,11317],{},"git log",[783,11319,11320,11333,11339,11355,11361,11367,11377,11393,11402,11408],{},[786,11321,11322,3058,11325,11328,11329,11332],{},[18,11323,11324],{},"-p",[18,11326,11327],{},"--patch","，以",[6109,11330,11331],{},"补丁","的格式输出每次提交的所引入的差异",[786,11334,11335,11338],{},[18,11336,11337],{},"--stat","每次提交的简略统计信息",[786,11340,11341,11344,11345,3611,11348,3611,11351,11354],{},[18,11342,11343],{},"--pretty=oneline","一行显示一条提交，其他样式",[18,11346,11347],{},"--pretty=short",[18,11349,11350],{},"--pretty=full",[18,11352,11353],{},"--pretty=fuller","仅信息详细程度不同",[786,11356,11357,11360],{},[18,11358,11359],{},"--pretty=format","自定义输出格式",[786,11362,11363,11366],{},[18,11364,11365],{},"--graph","更形象的显示分支",[786,11368,11369,11372,11373,11376],{},[18,11370,11371],{},"-\u003Cn>","如",[18,11374,11375],{},"git log -2","显示两条记录",[786,11378,11379,1016,11382,11385,11386,4544,11389,11392],{},[18,11380,11381],{},"--since=\u003Ctime_a>",[18,11383,11384],{},"--until=\u003Ctime_b>"," ，显示从",[18,11387,11388],{},"time_a",[18,11390,11391],{},"time_b","的提交",[786,11394,11395,1016,11398,11401],{},[18,11396,11397],{},"--after=\u003Ctime_a>",[18,11399,11400],{},"--before=\u003Ctime_b>","与上条相同",[786,11403,11404,11407],{},[18,11405,11406],{},"-S \u003Cstring>","过滤器，只显示增加或删除该字符串的提交",[786,11409,11410],{},"其他。。。",[280,11412,11413],{},"比如：",[11,11415,11418],{"className":11416,"code":11417,"language":4625},[4623],"$ git log --pretty=\"%h - %s\" --author='Junio C Hamano' --since=\"2008-10-01\" \\\n   --before=\"2008-11-01\" --no-merges -- t\u002F\n",[18,11419,11417],{"__ignoreMap":16},[280,11421,11422,11425],{},[18,11423,11424],{},"-- t\u002F","指对t文件夹的提交",[1297,11427,11428],{"id":11428},"撤销操作",[783,11430,11431,11443,11449],{},[786,11432,11433,11436,11437],{},[18,11434,11435],{},"git commit --amend","重新提交，替代上一次的提交结果，如：",[11,11438,11441],{"className":11439,"code":11440,"language":4625},[4623],"$ git commit -m 'initial commit'\n$ git add forgotten_file\n$ git commit --amend\n",[18,11442,11440],{"__ignoreMap":16},[786,11444,11445,11448],{},[18,11446,11447],{},"git restore --staged \u003Cfile>","取消暂存",[786,11450,11451,11454],{},[18,11452,11453],{},"git restore \u003Cfile>","撤销修改",[280,11456,11457],{},[1292,11458,11459],{},"上两条与《Pro Git》不同，可能更新了",[1297,11461,11462],{"id":11462},"远程仓库",[783,11464,11465,11514,11523],{},[786,11466,11467,11470,11471],{},[18,11468,11469],{},"git remote","查看远程仓库\n",[783,11472,11473,11479,11485,11495,11501],{},[786,11474,11475,11478],{},[18,11476,11477],{},"-v","显示远程仓库以及url",[786,11480,11481,11484],{},[18,11482,11483],{},"add \u003Cshortname> \u003Curl>","添加新的远程仓库并指定简写",[786,11486,11487,11490,11491,11494],{},[18,11488,11489],{},"show \u003Cremote>","查看",[18,11492,11493],{},"remote","的信息",[786,11496,11497,11500],{},[18,11498,11499],{},"rename \u003Cremote> \u003Cnew_remote>","远程仓库重命名",[786,11502,11503,11506,11507,11510,11511,11513],{},[18,11504,11505],{},"remove \u003Cremote>","或者",[18,11508,11509],{},"rm \u003Cremote>","移除",[18,11512,11493],{},"仓库",[786,11515,11516,11519,11520,11522],{},[18,11517,11518],{},"git fetch \u003Cremote>","访问",[18,11521,11493],{},"仓库，拉取没有的数据",[786,11524,11525,11528],{},[18,11526,11527],{},"git push \u003Creomte> \u003Cbranch>","推送",[1297,11530,11531],{"id":11531},"标签",[280,11533,11534,11537,11538,3058,11541,11277],{},[18,11535,11536],{},"git tag","列出所有标签可带",[18,11539,11540],{},"-l",[18,11542,11543],{},"--list",[280,11545,11546],{},"而标签分为轻量与附注，附注标签包含打标签人的信息、日期等，另外可以附加标签信息",[783,11548,11549,11555,11561,11573,11579,11589,11602],{},[786,11550,11551,11554],{},[18,11552,11553],{},"git tag \u003Ctagname>","创建轻量标签",[786,11556,11557,11560],{},[18,11558,11559],{},"git tag -a \u003Ctagname> -m \u003Ctag_message>","创建附注标签",[786,11562,11563,4507,11566,11569,11570,11572],{},[18,11564,11565],{},"git tag -a \u003Ctagname> \u003Ccommit>",[18,11567,11568],{},"commit","打标签，",[18,11571,11568],{},"为提交的完整或部分校验和",[786,11574,11575,11578],{},[18,11576,11577],{},"git show \u003Ctagname>","查看标签信息与对应的提交信息",[786,11580,11581,11584,11585,11588],{},[18,11582,11583],{},"git push \u003Cremote> \u003Ctagname>","推送标签，或",[18,11586,11587],{},"git push \u003Cremote> --tags","推送所有标签",[786,11590,11591,11594,11595,3058,11598,11601],{},[18,11592,11593],{},"git tage -d \u003Ctagname>","删除标签，同时",[18,11596,11597],{},"git push \u003Cremote>:refs\u002Ftags\u002F\u003Ctagname>",[18,11599,11600],{},"git push \u003Cremote> --delete \u003Ctagname>","删除远程仓库的标签",[786,11603,11604,11607],{},[18,11605,11606],{},"git checkout \u003Ctagname>","检出标签",[1297,11609,11158],{"id":11158},[280,11611,11612],{},[1292,11613,11614],{},"敬请期待",[53,11616,11618],{"id":11617},"git分支","Git分支",[783,11620,11621,11655,11669],{},[786,11622,11623,11626,11627],{},[18,11624,11625],{},"git branch","列出分支\n",[783,11628,11629,11634,11640,11646],{},[786,11630,11631,11633],{},[18,11632,11477],{},"查看每个分支的最后一次提交",[786,11635,11636,11639],{},[18,11637,11638],{},"\u003Cbranch>","创建分支",[786,11641,11642,11645],{},[18,11643,11644],{},"-d \u003Cbranch>","删除分支",[786,11647,11648,2819,11651,11654],{},[18,11649,11650],{},"--merged",[18,11652,11653],{},"--no-merged","已经与当前分支合并和未合并的分支",[786,11656,11657,11660,11661],{},[18,11658,11659],{},"git checkout \u003Cbranch>","分支切换\n",[783,11662,11663],{},[786,11664,11665,11668],{},[18,11666,11667],{},"git checkout -b \u003Cbranch>","创建并切换分支",[786,11670,11671,11674,11675],{},[18,11672,11673],{},"git merge \u003Cbranch>","合并分支\n",[783,11676,11677,11683],{},[786,11678,11679,11680],{},"顺着分支能到达另一分支时，只会简单的指针前进（右移），合并操作并无分歧，叫做",[18,11681,11682],{},"fast-forward",[786,11684,11685,11686,11689],{},"两分支岔开（diverged），会与两分支的共同祖先三方合并，创建一个新的提交。而遇到",[8057,11687,11688],{},"冲突","，即两个分支对同一文件进行修改，会进行合并但没创建提交，需修改后自行提交",[1297,11691,11169],{"id":11169},[280,11693,11694,11695,11698,11699,3058,11702],{},"远程跟踪分支",[8057,11696,11697],{},"检出","本地分支会自动创建\"跟踪分支\"（所跟踪的分支称为上游分支），使用\n",[18,11700,11701],{},"git checkout -b \u003Cbranch> \u003Cremote>\u002F\u003Cbranch>",[18,11703,11704],{},"git checkout --track \u003Cremote>\u002F\u003Cbranch>",[280,11706,11707,11708,11711,11712,11715,11716],{},"而",[8057,11709,11710],{},"已有的","本地分支需要跟踪或者修改上游分支则可以使用：\n",[18,11713,11714],{},"git branch -u \u003Cremote>\u002F\u003Cbranch>","或着",[18,11717,11718],{},"--set-upstream-to",[280,11720,11721,11722,3058,11725,11728,11729,11732,11733,11736,11737,11740],{},"同时可以使用",[18,11723,11724],{},"@{u}",[18,11726,11727],{},"@{upstream}","代表上游分支，如",[18,11730,11731],{},"git merge @{u}","代替",[18,11734,11735],{},"git merge origin\u002Fmain","\n可使用",[18,11738,11739],{},"git branch -vv","来查看本地与上游分支的更多信息",[280,11742,11743,11744],{},"删除远程分支",[18,11745,11746],{},"git push \u003Cremote> --delete \u003Cbranch>",[1297,11748,11749],{"id":11749},"变基",[280,11751,11752,11755],{},[18,11753,11754],{},"git rebase \u003Cbranch>","\n待续。。。",[4268,11757],{},[53,11759,11761],{"id":11760},"git内部原理","Git内部原理",[280,11763,11764,11767,11770,11771,11774,11775,11774,11778,11781,11782,11133],{},[1292,11765,11766],{},"太无聊了",[18,11768,11769],{},".git","中重要文件：",[18,11772,11773],{},"HEAD","文件、",[18,11776,11777],{},"index",[18,11779,11780],{},"objects","目录、",[18,11783,11784],{},"refs",[1297,11786,11787],{"id":11787},"objects目录",[280,11789,11790,11791,11794],{},"保存所有数据，以Key-Value存储。通过",[18,11792,11793],{},"git hash-object","演示存储效果：",[11,11796,11799],{"className":11797,"code":11798,"language":4625},[4623],"$ echo \"hello\" | git hash-object -w --stdin\nce013625030ba8dba906f756967f9e9ca394464a\n",[18,11800,11798],{"__ignoreMap":16},[280,11802,11803,11806,11807,11810,11811,11814,11815,11818],{},[18,11804,11805],{},"-w","写入数据库，",[18,11808,11809],{},"--stdin","从标准输入读取，或直接",[18,11812,11813],{},"git hash-object -w \u003Cfile>","。通过",[18,11816,11817],{},"git cat-file","读取数据：",[11,11820,11823],{"className":11821,"code":11822,"language":4625},[4623],"$ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a\nhello\n",[18,11824,11822],{"__ignoreMap":16},[280,11826,11827,11828,11831],{},"只保存了文件内容没有文件名等信息，该类型对象称为数据对象（blob object）。通过",[18,11829,11830],{},"-t","读取数据类型",[11,11833,11836],{"className":11834,"code":11835,"language":4625},[4623],"$ git cat-file -t ce013625030ba8dba906f756967f9e9ca394464a\nblob\n",[18,11837,11835],{"__ignoreMap":16},[2208,11839,11840],{"id":11840},"树对象",[280,11842,11843],{},"一个树对象有一条或多条树对象记录（tree entry），每条记录指向：文件模式、类型、数据对象或者子树对象的指针、文件名信息。",[11,11845,11848],{"className":11846,"code":11847,"language":4625},[4623],"$ git cat-file -p main^{tree}\n100644 blob 45f16d261584a37f3c3f3f631c7c08d0958baa2a    init.lua\n100644 blob 7bf88613cead41206b364137ca310a829645f228    lazy-lock.json\n040000 tree aff0b5e3d38b57a379d538056f60fa6f7a386fd0    lua\n",[18,11849,11847],{"__ignoreMap":16},[2208,11851,11852],{"id":11852},"提交对象",[280,11854,11855],{},"包含树对象的SHA-1值、父提交对象、作者信息以及提交注释",[2208,11857,11858],{"id":11858},"对象存储",[280,11860,11861,11862,11865,11866,11869,11870],{},"假设数据为",[18,11863,11864],{},"content = \"hello\"","，令",[18,11867,11868],{},"header = \"blob #{content.length}\\0\"","，\n头部信息为对象类型 + 空格 + 数据的字节数 + 空字节。即",[18,11871,11872],{},"blob 5\\u0000",[280,11874,11707,11875,11878,11879,11882,11883,11886],{},[18,11876,11877],{},"store = heander + content","，Git会对",[18,11880,11881],{},"store","计算SHA-1校验和（作为Key）。\n由zlib压缩后即为对象的内容（Value）数据对象的",[18,11884,11885],{},"content","无限制但树对象与提交对象的内容固定",[1297,11888,11183],{"id":11183},[280,11890,11891,11894],{},[18,11892,11893],{},".git\u002Frefs","目录下的含有SHA-1值的文件",[783,11896,11897,11902],{},[786,11898,11899],{},[18,11900,11901],{},"echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git\u002Frefs\u002Fheads\u002Fmaster",[786,11903,11904,11907],{},[18,11905,11906],{},"git update-ref refs\u002Fheads\u002Fmaster 1a410efbd13591db07496601ebc7a059dd55cfe9","，git提供的方法更安全",[1183,11909,11910],{},[280,11911,11912],{},"Git分支的本质就是一个指向某一系列提交之首的指针或引用",[2208,11914,11916],{"id":11915},"head引用","HEAD引用",[280,11918,11919,11920,11923],{},"运行",[18,11921,11922],{},"git branch \u003Cbranch>","时，通过HEAD文件获取最新提交的SHA-1值",[1183,11925,11926],{},[280,11927,11928],{},"HEAD文件通常是一个符号引用，即指向其他引用的指针",[2208,11930,11931],{"id":11931},"标签引用",[280,11933,11934],{},"除三种主要对象类型外，还有标签对象。",[280,11936,11937],{},"而标签就是一个引用",[783,11939,11940,11949],{},[786,11941,11942,11943],{},"轻量标签：",[11,11944,11947],{"className":11945,"code":11946,"language":4625},[4623],"$ git update-ref refs\u002Ftags\u002Fv1.0 cac0cab538b970a37ea1e769cbbde608743bc96d\n",[18,11948,11946],{"__ignoreMap":16},[786,11950,11951,11952,11958,11960,11961,11963,11964,11967,11968,11974],{},"附注标签：",[11,11953,11956],{"className":11954,"code":11955,"language":4625},[4623],"$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'\n$ cat .git\u002Frefs\u002Ftags\u002Fv1.1\n9585191f37f7b0fb9444f35a9bf50de191beadc2\n",[18,11957,11955],{"__ignoreMap":16},[1447,11959],{},"先创建标签对象再通过",[8057,11962,11183],{},"指向标签对象，通过",[18,11965,11966],{},"git cat-file -p","得到：",[11,11969,11972],{"className":11970,"code":11971,"language":4625},[4623],"$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2\nobject 1a410efbd13591db07496601ebc7a059dd55cfe9\ntype commit\ntag v1.1\ntagger Scott Chacon \u003Cschacon@gmail.com> Sat May 23 16:48:58 2009 -0700\n\ntest tag\n",[18,11973,11971],{"__ignoreMap":16},[1183,11975,11976],{},[280,11977,11978],{},"标签对象并不一定需要指向提交对象，也可以为数据对象或树对象打标签",[2208,11980,11981],{"id":11981},"远程引用",[280,11983,11984,11985,11988,11989,11991],{},"保存在",[18,11986,11987],{},"refs\u002Fremotes","下。当添加了远程仓库并进行推送，Git会记录最近一次推送的每一个分支对应的值，并保存在",[18,11990,11987],{},"目录下：",[11,11993,11996],{"className":11994,"code":11995,"language":4625},[4623],"$ git remote add origin git@github.com:schacon\u002Fsimplegit-progit.git\n$ git push origin master\nCounting objects: 11, done.\nCompressing objects: 100% (5\u002F5), done.\nWriting objects: 100% (7\u002F7), 716 bytes, done.\nTotal 7 (delta 2), reused 4 (delta 1)\nTo git@github.com:schacon\u002Fsimplegit-progit.git\n   a11bef0..ca82a6d  master -> master\n",[18,11997,11995],{"__ignoreMap":16},[280,11999,12000,12001,12004],{},"而查看",[18,12002,12003],{},"refs\u002Fremotes\u002Forigin\u002Fmaster","文件",[11,12006,12009],{"className":12007,"code":12008,"language":4625},[4623],"$ cat .git\u002Frefs\u002Fremotes\u002Forigin\u002Fmaster\nca82a6dff817ec66f44342007202690a93763949\n",[18,12010,12008],{"__ignoreMap":16},[280,12012,12013,12014,12017],{},"即为",[18,12015,12016],{},"origin\u002Fmaster","分支的SHA-1值",[4268,12019],{},[1183,12021,12022],{},[280,12023,12024,12025,12030],{},"学习",[531,12026,12029],{"href":12027,"rel":12028},"https:\u002F\u002Flearngitbranching.js.org\u002F?locale=zh_CN",[535],"Learn Git Branching","记录",[1297,12032,12033],{"id":12033},"相对引用",[783,12035,12036,12060],{},[786,12037,12038,12040,12041,12044,12045,12047,12048],{},[18,12039,1085],{},"，例如",[18,12042,12043],{},"HEAD^","表示",[18,12046,11773],{},"的上一次提交\n",[783,12049,12050],{},[786,12051,12052,12055,12056,12059],{},[18,12053,12054],{},"^{num}","，一个提交可能有多个父节点，",[18,12057,12058],{},"num","表示选择第几个父节点",[786,12061,12062,12040,12065,12044,12068,12071],{},[18,12063,12064],{},"~{num}",[18,12066,12067],{},"main~3",[18,12069,12070],{},"main","的上三次提交",[1297,12073,12074],{"id":12074},"扩展",[783,12076,12077,12089],{},[786,12078,12079,5583,12082,12085,12086,12088],{},[18,12080,12081],{},"git branch -f \u003Cbranch> \u003Ccommit>",[18,12083,12084],{},"branch","分支指向",[18,12087,11568],{},"提交",[786,12090,12091,12094,12095],{},[18,12092,12093],{},"git switch -c \u003Cbranch>","等同于",[18,12096,11667],{},[2208,12098,12099],{"id":12099},"撤销变更",[783,12101,12102,12111],{},[786,12103,12104,12107,12108,12110],{},[18,12105,12106],{},"git reset \u003Ccommit>","将当前分支回退到",[18,12109,11568],{},"提交上",[786,12112,12113,12116,12117,12119],{},[18,12114,12115],{},"git revert \u003Ccommit>","反做",[18,12118,11568],{},"的操作后添加提交",[280,12121,4272],{},{"title":16,"searchDepth":30,"depth":30,"links":12123},[12124,12125,12133,12137],{"id":11133,"depth":30,"text":11133},{"id":11189,"depth":30,"text":11190,"children":12126},[12127,12128,12129,12130,12131,12132],{"id":11193,"depth":36,"text":11193},{"id":11312,"depth":36,"text":11312},{"id":11428,"depth":36,"text":11428},{"id":11462,"depth":36,"text":11462},{"id":11531,"depth":36,"text":11531},{"id":11158,"depth":36,"text":11158},{"id":11617,"depth":30,"text":11618,"children":12134},[12135,12136],{"id":11169,"depth":36,"text":11169},{"id":11749,"depth":36,"text":11749},{"id":11760,"depth":30,"text":11761,"children":12138},[12139,12140,12141,12142],{"id":11787,"depth":36,"text":11787},{"id":11183,"depth":36,"text":11183},{"id":12033,"depth":36,"text":12033},{"id":12074,"depth":36,"text":12074},"2023-02-03",{},"\u002Fblog\u002Fgit-learning",{"title":11118,"description":294},"blog\u002Fgit-learning",[12149],"Git","0gEKzIYfF4AqZEfCQca6YxoImzKMbBu9INPxYIxGNmc",{"id":12152,"title":12153,"body":12154,"date":14451,"description":294,"extension":295,"meta":14452,"navigation":143,"path":14453,"seo":14454,"stem":14455,"tags":14456,"__hash__":14458},"blog\u002Fblog\u002Fcomplement.md","补数与补码",{"type":8,"value":12155,"toc":14440},[12156,12192,12195,12213,12218,12224,12227,12235,12243,12246,12260,12263,12411,12578,12581,12584,12587,12590,12598,12601,12604,12612,12615,12622,12627,12630,12633,12638,12649,12652,12666,12669,12672,12828,14260,14263],[280,12157,12158,12159,12162,12163,12166,12167,12170,12171,12173,12176,12177,12180,12181,12183,12184,12187,12188,12191],{},"始于《C++ Primer》中对超出无符号数范围的数的转换，转为无符号数后值为",[18,12160,12161],{},"{num} mod {count}","，\n",[18,12164,12165],{},"{num}","即为原来的数，",[18,12168,12169],{},"{count}","为无符号数据类型可代表的数的总和，如：",[1447,12172],{},[18,12174,12175],{},"unsigned char","可表示0~255共256个数, ",[18,12178,12179],{},"unsigned char a = -1","，实际上",[18,12182,531],{},"的值为",[18,12185,12186],{},"255","，由",[18,12189,12190],{},"-1 mod 256","得出",[53,12193,12194],{"id":12194},"模运算",[280,12196,12197,12198,12200,12201,12204,12205,12208,12209,12212],{},"商余定理：",[1447,12199],{},"\n给出",[6109,12202,12203],{},"任何整数","A和一个",[6109,12206,12207],{},"正整数","B，存在",[6109,12210,12211],{},"唯一整数Q和R","，满足：",[280,12214,12215],{},[6109,12216,12217],{},"A = B * Q + R，其中0 \u003C= R \u003C B",[280,12219,12220,12221],{},"A除以B，Q是商，R是余数。以另一种形式书写就是",[6109,12222,12223],{},"A mod B = R",[280,12225,12226],{},"求A mod B时，R = A - B * Q，求出商Q即可。而由于有多种取整方法，R的值随着取整方法的不同而变化",[1183,12228,12229,12232],{},[280,12230,12231],{},"ceil()，floor()，int()，round()分别为向上取整，向下取整，向零取整以及四舍五入",[280,12233,12234],{},"此处只讨论floor()和int()。并且正数取整时，这两个方法得到的结果相同，因此只讨论负数，即A \u003C 0时",[783,12236,12237,12240],{},[786,12238,12239],{},"向下取整，|Q| > |A\u002FB|，即BQ \u003C A，一个负数减去一个更小的负数得到R > 0",[786,12241,12242],{},"向零取整，|Q| \u003C |A\u002FB|，即BQ > A，R \u003C 0",[1297,12244,12245],{"id":12245},"运算性质",[783,12247,12248,12251,12254,12257],{},[786,12249,12250],{},"(A + B) mod C = (A mod C + B mod C) mod C",[786,12252,12253],{},"(A - B) mod C = (A mod C - B mod C) mod C",[786,12255,12256],{},"(A * B) mod C = (A mod C * B mod C) mod C",[786,12258,12259],{},"A^B mod C     = ((A mod C)^B) mod C",[53,12261,12262],{"id":12262},"同余",[280,12264,12265,12266],{},"A与B模C同余，表示A mod C = B mod C，记作",[21,12267,12270,12329],{"className":12268},[12269],"katex",[21,12271,12274],{"className":12272},[12273],"katex-mathml",[12275,12276,12278],"math",{"xmlns":12277},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[12279,12280,12281,12324],"semantics",{},[12282,12283,12284,12288,12292,12295,12298,12301,12305,12317,12320,12322],"mrow",{},[12285,12286,12287],"mi",{},"A",[12289,12290,12291],"mo",{},"≡",[12285,12293,12294],{},"B",[12296,12297],"mspace",{},[12296,12299],{"width":12300},"0.4444em",[12289,12302,12304],{"stretchy":12303},"false","(",[12282,12306,12307,12311,12314],{},[12285,12308,12310],{"mathvariant":12309},"normal","m",[12285,12312,12313],{"mathvariant":12309},"o",[12285,12315,12316],{"mathvariant":12309},"d",[12296,12318],{"width":12319},"0.3333em",[12285,12321,7996],{},[12289,12323,4003],{"stretchy":12303},[12325,12326,12328],"annotation",{"encoding":12327},"application\u002Fx-tex","A\\equiv B\\pmod{C}",[21,12330,12334,12359,12377],{"className":12331,"ariaHidden":12333},[12332],"katex-html","true",[21,12335,12338,12343,12348,12352,12356],{"className":12336},[12337],"base",[21,12339],{"className":12340,"style":12342},[12341],"strut","height:0.6833em;",[21,12344,12287],{"className":12345},[12346,12347],"mord","mathnormal",[21,12349],{"className":12350,"style":12351},[12296],"margin-right:0.2778em;",[21,12353,12291],{"className":12354},[12355],"mrel",[21,12357],{"className":12358,"style":12351},[12296],[21,12360,12362,12365,12369,12373],{"className":12361},[12337],[21,12363],{"className":12364,"style":12342},[12341],[21,12366,12294],{"className":12367,"style":12368},[12346,12347],"margin-right:0.0502em;",[21,12370],{"className":12371},[12296,12372],"allowbreak",[21,12374],{"className":12375,"style":12376},[12296],"margin-right:0.4444em;",[21,12378,12380,12384,12388,12399,12403,12407],{"className":12379},[12337],[21,12381],{"className":12382,"style":12383},[12341],"height:1em;vertical-align:-0.25em;",[21,12385,12304],{"className":12386},[12387],"mopen",[21,12389,12391],{"className":12390},[12346],[21,12392,12394],{"className":12393},[12346],[21,12395,12398],{"className":12396},[12346,12397],"mathrm","mod",[21,12400],{"className":12401,"style":12402},[12296],"margin-right:0.3333em;",[21,12404,7996],{"className":12405,"style":12406},[12346,12347],"margin-right:0.0715em;",[21,12408,4003],{"className":12409},[12410],"mclose",[783,12412,12413,12575],{},[786,12414,12415,12416,12574],{},"C|(A-B)，\"|\"符号意味整除，C能整除(A-B)，即",[21,12417,12419,12451],{"className":12418},[12269],[21,12420,12422],{"className":12421},[12273],[12275,12423,12424],{"xmlns":12277},[12279,12425,12426,12448],{},[12282,12427,12428,12442,12445],{},[12429,12430,12431,12440],"mfrac",{},[12282,12432,12433,12435,12438],{},[12285,12434,12287],{},[12289,12436,12437],{},"−",[12285,12439,12294],{},[12285,12441,7996],{},[12289,12443,12444],{},"=",[12285,12446,12447],{},"K",[12325,12449,12450],{"encoding":12327},"\\frac{A-B}{C}=K",[21,12452,12454,12565],{"className":12453,"ariaHidden":12333},[12332],[21,12455,12457,12461,12556,12559,12562],{"className":12456},[12337],[21,12458],{"className":12459,"style":12460},[12341],"height:1.2173em;vertical-align:-0.345em;",[21,12462,12464,12468,12553],{"className":12463},[12346],[21,12465],{"className":12466},[12387,12467],"nulldelimiter",[21,12469,12471],{"className":12470},[12429],[21,12472,12476,12544],{"className":12473},[12474,12475],"vlist-t","vlist-t2",[21,12477,12480,12539],{"className":12478},[12479],"vlist-r",[21,12481,12485,12506,12517],{"className":12482,"style":12484},[12483],"vlist","height:0.8723em;",[21,12486,12488,12493],{"style":12487},"top:-2.655em;",[21,12489],{"className":12490,"style":12492},[12491],"pstrut","height:3em;",[21,12494,12500],{"className":12495},[12496,12497,12498,12499],"sizing","reset-size6","size3","mtight",[21,12501,12503],{"className":12502},[12346,12499],[21,12504,7996],{"className":12505,"style":12406},[12346,12347,12499],[21,12507,12509,12512],{"style":12508},"top:-3.23em;",[21,12510],{"className":12511,"style":12492},[12491],[21,12513],{"className":12514,"style":12516},[12515],"frac-line","border-bottom-width:0.04em;",[21,12518,12520,12523],{"style":12519},"top:-3.394em;",[21,12521],{"className":12522,"style":12492},[12491],[21,12524,12526],{"className":12525},[12496,12497,12498,12499],[21,12527,12529,12532,12536],{"className":12528},[12346,12499],[21,12530,12287],{"className":12531},[12346,12347,12499],[21,12533,12437],{"className":12534},[12535,12499],"mbin",[21,12537,12294],{"className":12538,"style":12368},[12346,12347,12499],[21,12540,12543],{"className":12541},[12542],"vlist-s","​",[21,12545,12547],{"className":12546},[12479],[21,12548,12551],{"className":12549,"style":12550},[12483],"height:0.345em;",[21,12552],{},[21,12554],{"className":12555},[12410,12467],[21,12557],{"className":12558,"style":12351},[12296],[21,12560,12444],{"className":12561},[12355],[21,12563],{"className":12564,"style":12351},[12296],[21,12566,12568,12571],{"className":12567},[12337],[21,12569],{"className":12570,"style":12342},[12341],[21,12572,12447],{"className":12573,"style":12406},[12346,12347],"，与下面相同",[786,12576,12577],{},"A = B + K * C，A与B之间相差K个C。(K为整数)",[280,12579,12580],{},"同时，一个数X。当A ≡ B(mod C)时，A与B相差K倍的C，有：",[280,12582,12583],{},"(X + A) ≡ (X + B) mod C",[53,12585,12586],{"id":12586},"补数",[280,12588,12589],{},"假设现在时间为10点整，要想让时针指向2点，有两种方式：",[1420,12591,12592,12595],{},[786,12593,12594],{},"顺时针拨动4个小时",[786,12596,12597],{},"逆时针拨动8个小时",[280,12599,12600],{},"钟表时间以12为模，在模的范围内，两个相加等于模的数互为补数(complement)",[280,12602,12603],{},"在模的范围内做减法，即逆时针转动8个小时(-8 mod 12 = 4)与做加法，即顺时针转动4个小时(4 mod 12 = 4)得到的结果相同",[1183,12605,12606,12609],{},[280,12607,12608],{},"-8与4同余，有(X + (-8)) mod 12 = (X + 4) mod 12",[280,12610,12611],{},"因此可通过同余，令减法变成加法",[280,12613,12614],{},"在0~99中，以100为模，(30 + (- 40)) mod 100 = (30 + 60) mod 100，\"60\"可以替代\"-40\"进行加法运算，但是如果60用来表示-40，\n它也有原本的值，造成二义性",[280,12616,12617,12618,12621],{},"为解决该问题，将模范围内的数划分，0",[1292,12619,12620],{},"49区间的表示原来的值，而与之对应的补数来表示其负数，例如99的补数为1即\n99表示-1。当然，牺牲了一半的值表示负数，在以100为模的范围内，50","99的这些正数就无法表示了。要想取到50~99，可在\n更大的模中划分",[1183,12623,12624],{},[280,12625,12626],{},"取模为C，一个半模以下的正整数A，补数为B。B用来表示-A，同时 B - (-A) = C - A + A = C，即B与(-A) mod C同余",[53,12628,12629],{"id":12629},"二进制",[280,12631,12632],{},"二进制中划分范围后，半模以下的数最高位为0，半模以上的数最高位为1。这就是补码最高位为符号位的原因",[1183,12634,12635],{},[280,12636,12637],{},"符号位的0, 1并不是为了表示正负，只是半模以上数的二进制最高位为1，该数为正数，用它表示其补数的相反数",[280,12639,12640,12641,12644,12645,12648],{},"假设有一个8位二进制数，它可表示2的8次方共256个数，即0",[1292,12642,12643],{},"255其模为256。因此0","127表示正数，\n而128",[1292,12646,12647],{},"255表示对应补数的相反数，如255的补数为1，即255表示-1。同时，128的补数为128即128用来表示-128，\n故8位二进制数的可表示范围为-128","127",[1297,12650,12651],{"id":12651},"补码",[280,12653,12654,12655,12658,12659,12662,12663],{},"-1的原码为",[18,12656,12657],{},"1000 0001","故其补码为",[18,12660,12661],{},"1111 1111","对应255，正好是其绝对值的补数。也可得出-128的补码为\n其补数128的二进制形式",[18,12664,12665],{},"1000 0000",[1297,12667,12668],{"id":12668},"取反加一",[280,12670,12671],{},"n位二进制数，以2^n为模，取半模以下的数A，补数B = 2^n - A，B表示-A，即B的二进制为-A的补码",[1183,12673,12674],{},[280,12675,12676,12677,12757,12758,12827],{},"A的最高位为0，令其",[21,12678,12680,12701],{"className":12679},[12269],[21,12681,12683],{"className":12682},[12273],[12275,12684,12685],{"xmlns":12277},[12279,12686,12687,12698],{},[12282,12688,12689],{},[12690,12691,12692,12695],"msub",{},[12285,12693,12694],{},"k",[12285,12696,12697],{},"i",[12325,12699,12700],{"encoding":12327},"k_i",[21,12702,12704],{"className":12703,"ariaHidden":12333},[12332],[21,12705,12707,12711],{"className":12706},[12337],[21,12708],{"className":12709,"style":12710},[12341],"height:0.8444em;vertical-align:-0.15em;",[21,12712,12714,12718],{"className":12713},[12346],[21,12715,12694],{"className":12716,"style":12717},[12346,12347],"margin-right:0.0315em;",[21,12719,12722],{"className":12720},[12721],"msupsub",[21,12723,12725,12748],{"className":12724},[12474,12475],[21,12726,12728,12745],{"className":12727},[12479],[21,12729,12732],{"className":12730,"style":12731},[12483],"height:0.3117em;",[21,12733,12735,12739],{"style":12734},"top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;",[21,12736],{"className":12737,"style":12738},[12491],"height:2.7em;",[21,12740,12742],{"className":12741},[12496,12497,12498,12499],[21,12743,12697],{"className":12744},[12346,12347,12499],[21,12746,12543],{"className":12747},[12542],[21,12749,12751],{"className":12750},[12479],[21,12752,12755],{"className":12753,"style":12754},[12483],"height:0.15em;",[21,12756],{},"代表其余n-1位，",[21,12759,12761,12778],{"className":12760},[12269],[21,12762,12764],{"className":12763},[12273],[12275,12765,12766],{"xmlns":12277},[12279,12767,12768,12776],{},[12282,12769,12770],{},[12690,12771,12772,12774],{},[12285,12773,12694],{},[12285,12775,12697],{},[12325,12777,12700],{"encoding":12327},[21,12779,12781],{"className":12780,"ariaHidden":12333},[12332],[21,12782,12784,12787],{"className":12783},[12337],[21,12785],{"className":12786,"style":12710},[12341],[21,12788,12790,12793],{"className":12789},[12346],[21,12791,12694],{"className":12792,"style":12717},[12346,12347],[21,12794,12796],{"className":12795},[12721],[21,12797,12799,12819],{"className":12798},[12474,12475],[21,12800,12802,12816],{"className":12801},[12479],[21,12803,12805],{"className":12804,"style":12731},[12483],[21,12806,12807,12810],{"style":12734},[21,12808],{"className":12809,"style":12738},[12491],[21,12811,12813],{"className":12812},[12496,12497,12498,12499],[21,12814,12697],{"className":12815},[12346,12347,12499],[21,12817,12543],{"className":12818},[12542],[21,12820,12822],{"className":12821},[12479],[21,12823,12825],{"className":12824,"style":12754},[12483],[21,12826],{},"的值为0或者1",[280,12829,12830,13366,13712],{},[21,12831,12833,12941],{"className":12832},[12269],[21,12834,12836],{"className":12835},[12273],[12275,12837,12838],{"xmlns":12277},[12279,12839,12840,12938],{},[12282,12841,12842,12844,12846,12854,12857,12865,12868,12875,12877,12883,12885,12887,12889,12891,12893,12906,12908,12920,12922,12924,12926],{},[12285,12843,12287],{},[12289,12845,12444],{},[12690,12847,12848,12850],{},[12285,12849,12694],{},[12851,12852,12853],"mn",{},"0",[12289,12855,12856],{},"∗",[12858,12859,12860,12863],"msup",{},[12851,12861,12862],{},"2",[12851,12864,12853],{},[12289,12866,12867],{},"+",[12690,12869,12870,12872],{},[12285,12871,12694],{},[12851,12873,12874],{},"1",[12289,12876,12856],{},[12858,12878,12879,12881],{},[12851,12880,12862],{},[12851,12882,12874],{},[12289,12884,12867],{},[12285,12886,4565],{"mathvariant":12309},[12285,12888,4565],{"mathvariant":12309},[12285,12890,4565],{"mathvariant":12309},[12289,12892,12867],{},[12690,12894,12895,12897],{},[12285,12896,12694],{},[12282,12898,12899,12902,12904],{},[12285,12900,12901],{},"n",[12289,12903,12437],{},[12851,12905,12874],{},[12289,12907,12856],{},[12858,12909,12910,12912],{},[12851,12911,12862],{},[12282,12913,12914,12916,12918],{},[12285,12915,12901],{},[12289,12917,12437],{},[12851,12919,12862],{},[12289,12921,12867],{},[12851,12923,12853],{},[12289,12925,12856],{},[12858,12927,12928,12930],{},[12851,12929,12862],{},[12282,12931,12932,12934,12936],{},[12285,12933,12901],{},[12289,12935,12437],{},[12851,12937,12874],{},[12325,12939,12940],{"encoding":12327},"A=k_0*2^0+k_1*2^1+...+k_{n-1}*2^{n-2}+0*2^{n-1}",[21,12942,12944,12962,13019,13066,13121,13165,13184,13250,13303,13322],{"className":12943,"ariaHidden":12333},[12332],[21,12945,12947,12950,12953,12956,12959],{"className":12946},[12337],[21,12948],{"className":12949,"style":12342},[12341],[21,12951,12287],{"className":12952},[12346,12347],[21,12954],{"className":12955,"style":12351},[12296],[21,12957,12444],{"className":12958},[12355],[21,12960],{"className":12961,"style":12351},[12296],[21,12963,12965,12968,13009,13013,13016],{"className":12964},[12337],[21,12966],{"className":12967,"style":12710},[12341],[21,12969,12971,12974],{"className":12970},[12346],[21,12972,12694],{"className":12973,"style":12717},[12346,12347],[21,12975,12977],{"className":12976},[12721],[21,12978,12980,13001],{"className":12979},[12474,12475],[21,12981,12983,12998],{"className":12982},[12479],[21,12984,12987],{"className":12985,"style":12986},[12483],"height:0.3011em;",[21,12988,12989,12992],{"style":12734},[21,12990],{"className":12991,"style":12738},[12491],[21,12993,12995],{"className":12994},[12496,12497,12498,12499],[21,12996,12853],{"className":12997},[12346,12499],[21,12999,12543],{"className":13000},[12542],[21,13002,13004],{"className":13003},[12479],[21,13005,13007],{"className":13006,"style":12754},[12483],[21,13008],{},[21,13010],{"className":13011,"style":13012},[12296],"margin-right:0.2222em;",[21,13014,12856],{"className":13015},[12535],[21,13017],{"className":13018,"style":13012},[12296],[21,13020,13022,13026,13057,13060,13063],{"className":13021},[12337],[21,13023],{"className":13024,"style":13025},[12341],"height:0.8974em;vertical-align:-0.0833em;",[21,13027,13029,13032],{"className":13028},[12346],[21,13030,12862],{"className":13031},[12346],[21,13033,13035],{"className":13034},[12721],[21,13036,13038],{"className":13037},[12474],[21,13039,13041],{"className":13040},[12479],[21,13042,13045],{"className":13043,"style":13044},[12483],"height:0.8141em;",[21,13046,13048,13051],{"style":13047},"top:-3.063em;margin-right:0.05em;",[21,13049],{"className":13050,"style":12738},[12491],[21,13052,13054],{"className":13053},[12496,12497,12498,12499],[21,13055,12853],{"className":13056},[12346,12499],[21,13058],{"className":13059,"style":13012},[12296],[21,13061,12867],{"className":13062},[12535],[21,13064],{"className":13065,"style":13012},[12296],[21,13067,13069,13072,13112,13115,13118],{"className":13068},[12337],[21,13070],{"className":13071,"style":12710},[12341],[21,13073,13075,13078],{"className":13074},[12346],[21,13076,12694],{"className":13077,"style":12717},[12346,12347],[21,13079,13081],{"className":13080},[12721],[21,13082,13084,13104],{"className":13083},[12474,12475],[21,13085,13087,13101],{"className":13086},[12479],[21,13088,13090],{"className":13089,"style":12986},[12483],[21,13091,13092,13095],{"style":12734},[21,13093],{"className":13094,"style":12738},[12491],[21,13096,13098],{"className":13097},[12496,12497,12498,12499],[21,13099,12874],{"className":13100},[12346,12499],[21,13102,12543],{"className":13103},[12542],[21,13105,13107],{"className":13106},[12479],[21,13108,13110],{"className":13109,"style":12754},[12483],[21,13111],{},[21,13113],{"className":13114,"style":13012},[12296],[21,13116,12856],{"className":13117},[12535],[21,13119],{"className":13120,"style":13012},[12296],[21,13122,13124,13127,13156,13159,13162],{"className":13123},[12337],[21,13125],{"className":13126,"style":13025},[12341],[21,13128,13130,13133],{"className":13129},[12346],[21,13131,12862],{"className":13132},[12346],[21,13134,13136],{"className":13135},[12721],[21,13137,13139],{"className":13138},[12474],[21,13140,13142],{"className":13141},[12479],[21,13143,13145],{"className":13144,"style":13044},[12483],[21,13146,13147,13150],{"style":13047},[21,13148],{"className":13149,"style":12738},[12491],[21,13151,13153],{"className":13152},[12496,12497,12498,12499],[21,13154,12874],{"className":13155},[12346,12499],[21,13157],{"className":13158,"style":13012},[12296],[21,13160,12867],{"className":13161},[12535],[21,13163],{"className":13164,"style":13012},[12296],[21,13166,13168,13172,13175,13178,13181],{"className":13167},[12337],[21,13169],{"className":13170,"style":13171},[12341],"height:0.6667em;vertical-align:-0.0833em;",[21,13173,11186],{"className":13174},[12346],[21,13176],{"className":13177,"style":13012},[12296],[21,13179,12867],{"className":13180},[12535],[21,13182],{"className":13183,"style":13012},[12296],[21,13185,13187,13191,13241,13244,13247],{"className":13186},[12337],[21,13188],{"className":13189,"style":13190},[12341],"height:0.9028em;vertical-align:-0.2083em;",[21,13192,13194,13197],{"className":13193},[12346],[21,13195,12694],{"className":13196,"style":12717},[12346,12347],[21,13198,13200],{"className":13199},[12721],[21,13201,13203,13232],{"className":13202},[12474,12475],[21,13204,13206,13229],{"className":13205},[12479],[21,13207,13209],{"className":13208,"style":12986},[12483],[21,13210,13211,13214],{"style":12734},[21,13212],{"className":13213,"style":12738},[12491],[21,13215,13217],{"className":13216},[12496,12497,12498,12499],[21,13218,13220,13223,13226],{"className":13219},[12346,12499],[21,13221,12901],{"className":13222},[12346,12347,12499],[21,13224,12437],{"className":13225},[12535,12499],[21,13227,12874],{"className":13228},[12346,12499],[21,13230,12543],{"className":13231},[12542],[21,13233,13235],{"className":13234},[12479],[21,13236,13239],{"className":13237,"style":13238},[12483],"height:0.2083em;",[21,13240],{},[21,13242],{"className":13243,"style":13012},[12296],[21,13245,12856],{"className":13246},[12535],[21,13248],{"className":13249,"style":13012},[12296],[21,13251,13253,13256,13294,13297,13300],{"className":13252},[12337],[21,13254],{"className":13255,"style":13025},[12341],[21,13257,13259,13262],{"className":13258},[12346],[21,13260,12862],{"className":13261},[12346],[21,13263,13265],{"className":13264},[12721],[21,13266,13268],{"className":13267},[12474],[21,13269,13271],{"className":13270},[12479],[21,13272,13274],{"className":13273,"style":13044},[12483],[21,13275,13276,13279],{"style":13047},[21,13277],{"className":13278,"style":12738},[12491],[21,13280,13282],{"className":13281},[12496,12497,12498,12499],[21,13283,13285,13288,13291],{"className":13284},[12346,12499],[21,13286,12901],{"className":13287},[12346,12347,12499],[21,13289,12437],{"className":13290},[12535,12499],[21,13292,12862],{"className":13293},[12346,12499],[21,13295],{"className":13296,"style":13012},[12296],[21,13298,12867],{"className":13299},[12535],[21,13301],{"className":13302,"style":13012},[12296],[21,13304,13306,13310,13313,13316,13319],{"className":13305},[12337],[21,13307],{"className":13308,"style":13309},[12341],"height:0.6444em;",[21,13311,12853],{"className":13312},[12346],[21,13314],{"className":13315,"style":13012},[12296],[21,13317,12856],{"className":13318},[12535],[21,13320],{"className":13321,"style":13012},[12296],[21,13323,13325,13328],{"className":13324},[12337],[21,13326],{"className":13327,"style":13044},[12341],[21,13329,13331,13334],{"className":13330},[12346],[21,13332,12862],{"className":13333},[12346],[21,13335,13337],{"className":13336},[12721],[21,13338,13340],{"className":13339},[12474],[21,13341,13343],{"className":13342},[12479],[21,13344,13346],{"className":13345,"style":13044},[12483],[21,13347,13348,13351],{"style":13047},[21,13349],{"className":13350,"style":12738},[12491],[21,13352,13354],{"className":13353},[12496,12497,12498,12499],[21,13355,13357,13360,13363],{"className":13356},[12346,12499],[21,13358,12901],{"className":13359},[12346,12347,12499],[21,13361,12437],{"className":13362},[12535,12499],[21,13364,12874],{"className":13365},[12346,12499],[21,13367,13369,13441],{"className":13368},[12269],[21,13370,13372],{"className":13371},[12273],[12275,13373,13374],{"xmlns":12277},[12279,13375,13376,13438],{},[12282,13377,13378,13384,13386,13388,13390,13392,13394,13400,13402,13404,13406,13412,13414,13416,13418,13420,13422,13424,13426],{},[12858,13379,13380,13382],{},[12851,13381,12862],{},[12285,13383,12901],{},[12289,13385,12437],{},[12851,13387,12874],{},[12289,13389,12444],{},[12851,13391,12874],{},[12289,13393,12856],{},[12858,13395,13396,13398],{},[12851,13397,12862],{},[12851,13399,12853],{},[12289,13401,12867],{},[12851,13403,12874],{},[12289,13405,12856],{},[12858,13407,13408,13410],{},[12851,13409,12862],{},[12851,13411,12874],{},[12289,13413,12867],{},[12285,13415,4565],{"mathvariant":12309},[12285,13417,4565],{"mathvariant":12309},[12285,13419,4565],{"mathvariant":12309},[12289,13421,12867],{},[12851,13423,12874],{},[12289,13425,12856],{},[12858,13427,13428,13430],{},[12851,13429,12862],{},[12282,13431,13432,13434,13436],{},[12285,13433,12901],{},[12289,13435,12437],{},[12851,13437,12862],{},[12325,13439,13440],{"encoding":12327},"2^n-1=1*2^0+1*2^1+...+1*2^{n-2}",[21,13442,13444,13490,13508,13526,13570,13588,13632,13650,13668],{"className":13443,"ariaHidden":12333},[12332],[21,13445,13447,13451,13481,13484,13487],{"className":13446},[12337],[21,13448],{"className":13449,"style":13450},[12341],"height:0.7477em;vertical-align:-0.0833em;",[21,13452,13454,13457],{"className":13453},[12346],[21,13455,12862],{"className":13456},[12346],[21,13458,13460],{"className":13459},[12721],[21,13461,13463],{"className":13462},[12474],[21,13464,13466],{"className":13465},[12479],[21,13467,13470],{"className":13468,"style":13469},[12483],"height:0.6644em;",[21,13471,13472,13475],{"style":13047},[21,13473],{"className":13474,"style":12738},[12491],[21,13476,13478],{"className":13477},[12496,12497,12498,12499],[21,13479,12901],{"className":13480},[12346,12347,12499],[21,13482],{"className":13483,"style":13012},[12296],[21,13485,12437],{"className":13486},[12535],[21,13488],{"className":13489,"style":13012},[12296],[21,13491,13493,13496,13499,13502,13505],{"className":13492},[12337],[21,13494],{"className":13495,"style":13309},[12341],[21,13497,12874],{"className":13498},[12346],[21,13500],{"className":13501,"style":12351},[12296],[21,13503,12444],{"className":13504},[12355],[21,13506],{"className":13507,"style":12351},[12296],[21,13509,13511,13514,13517,13520,13523],{"className":13510},[12337],[21,13512],{"className":13513,"style":13309},[12341],[21,13515,12874],{"className":13516},[12346],[21,13518],{"className":13519,"style":13012},[12296],[21,13521,12856],{"className":13522},[12535],[21,13524],{"className":13525,"style":13012},[12296],[21,13527,13529,13532,13561,13564,13567],{"className":13528},[12337],[21,13530],{"className":13531,"style":13025},[12341],[21,13533,13535,13538],{"className":13534},[12346],[21,13536,12862],{"className":13537},[12346],[21,13539,13541],{"className":13540},[12721],[21,13542,13544],{"className":13543},[12474],[21,13545,13547],{"className":13546},[12479],[21,13548,13550],{"className":13549,"style":13044},[12483],[21,13551,13552,13555],{"style":13047},[21,13553],{"className":13554,"style":12738},[12491],[21,13556,13558],{"className":13557},[12496,12497,12498,12499],[21,13559,12853],{"className":13560},[12346,12499],[21,13562],{"className":13563,"style":13012},[12296],[21,13565,12867],{"className":13566},[12535],[21,13568],{"className":13569,"style":13012},[12296],[21,13571,13573,13576,13579,13582,13585],{"className":13572},[12337],[21,13574],{"className":13575,"style":13309},[12341],[21,13577,12874],{"className":13578},[12346],[21,13580],{"className":13581,"style":13012},[12296],[21,13583,12856],{"className":13584},[12535],[21,13586],{"className":13587,"style":13012},[12296],[21,13589,13591,13594,13623,13626,13629],{"className":13590},[12337],[21,13592],{"className":13593,"style":13025},[12341],[21,13595,13597,13600],{"className":13596},[12346],[21,13598,12862],{"className":13599},[12346],[21,13601,13603],{"className":13602},[12721],[21,13604,13606],{"className":13605},[12474],[21,13607,13609],{"className":13608},[12479],[21,13610,13612],{"className":13611,"style":13044},[12483],[21,13613,13614,13617],{"style":13047},[21,13615],{"className":13616,"style":12738},[12491],[21,13618,13620],{"className":13619},[12496,12497,12498,12499],[21,13621,12874],{"className":13622},[12346,12499],[21,13624],{"className":13625,"style":13012},[12296],[21,13627,12867],{"className":13628},[12535],[21,13630],{"className":13631,"style":13012},[12296],[21,13633,13635,13638,13641,13644,13647],{"className":13634},[12337],[21,13636],{"className":13637,"style":13171},[12341],[21,13639,11186],{"className":13640},[12346],[21,13642],{"className":13643,"style":13012},[12296],[21,13645,12867],{"className":13646},[12535],[21,13648],{"className":13649,"style":13012},[12296],[21,13651,13653,13656,13659,13662,13665],{"className":13652},[12337],[21,13654],{"className":13655,"style":13309},[12341],[21,13657,12874],{"className":13658},[12346],[21,13660],{"className":13661,"style":13012},[12296],[21,13663,12856],{"className":13664},[12535],[21,13666],{"className":13667,"style":13012},[12296],[21,13669,13671,13674],{"className":13670},[12337],[21,13672],{"className":13673,"style":13044},[12341],[21,13675,13677,13680],{"className":13676},[12346],[21,13678,12862],{"className":13679},[12346],[21,13681,13683],{"className":13682},[12721],[21,13684,13686],{"className":13685},[12474],[21,13687,13689],{"className":13688},[12479],[21,13690,13692],{"className":13691,"style":13044},[12483],[21,13693,13694,13697],{"style":13047},[21,13695],{"className":13696,"style":12738},[12491],[21,13698,13700],{"className":13699},[12496,12497,12498,12499],[21,13701,13703,13706,13709],{"className":13702},[12346,12499],[21,13704,12901],{"className":13705},[12346,12347,12499],[21,13707,12437],{"className":13708},[12535,12499],[21,13710,12862],{"className":13711},[12346,12499],[21,13713,13715,13825],{"className":13714},[12269],[21,13716,13718],{"className":13717},[12273],[12275,13719,13720],{"xmlns":12277},[12279,13721,13722,13822],{},[12282,13723,13724,13726,13728,13730,13732,13734,13740,13742,13744,13750,13752,13754,13756,13758,13764,13766,13768,13774,13776,13778,13780,13782,13784,13786,13788,13790,13802,13804,13806,13818,13820],{},[12285,13725,12294],{},[12289,13727,12444],{},[12289,13729,12304],{"stretchy":12303},[12851,13731,12874],{},[12289,13733,12437],{},[12690,13735,13736,13738],{},[12285,13737,12694],{},[12851,13739,12853],{},[12289,13741,4003],{"stretchy":12303},[12289,13743,12856],{},[12858,13745,13746,13748],{},[12851,13747,12862],{},[12851,13749,12853],{},[12289,13751,12867],{},[12289,13753,12304],{"stretchy":12303},[12851,13755,12874],{},[12289,13757,12437],{},[12690,13759,13760,13762],{},[12285,13761,12694],{},[12851,13763,12874],{},[12289,13765,4003],{"stretchy":12303},[12289,13767,12856],{},[12858,13769,13770,13772],{},[12851,13771,12862],{},[12851,13773,12874],{},[12289,13775,12867],{},[12285,13777,4565],{"mathvariant":12309},[12285,13779,4565],{"mathvariant":12309},[12285,13781,4565],{"mathvariant":12309},[12289,13783,12867],{},[12289,13785,12304],{"stretchy":12303},[12851,13787,12874],{},[12289,13789,12437],{},[12690,13791,13792,13794],{},[12285,13793,12694],{},[12282,13795,13796,13798,13800],{},[12285,13797,12901],{},[12289,13799,12437],{},[12851,13801,12862],{},[12289,13803,4003],{"stretchy":12303},[12289,13805,12856],{},[12858,13807,13808,13810],{},[12851,13809,12862],{},[12282,13811,13812,13814,13816],{},[12285,13813,12901],{},[12289,13815,12437],{},[12851,13817,12862],{},[12289,13819,12867],{},[12851,13821,12874],{},[12325,13823,13824],{"encoding":12327},"B=(1-k_0)*2^0+(1-k_1)*2^1+...+(1-k_{n-2})*2^{n-2}+1",[21,13826,13828,13846,13867,13925,13969,13990,14048,14092,14110,14131,14198,14251],{"className":13827,"ariaHidden":12333},[12332],[21,13829,13831,13834,13837,13840,13843],{"className":13830},[12337],[21,13832],{"className":13833,"style":12342},[12341],[21,13835,12294],{"className":13836,"style":12368},[12346,12347],[21,13838],{"className":13839,"style":12351},[12296],[21,13841,12444],{"className":13842},[12355],[21,13844],{"className":13845,"style":12351},[12296],[21,13847,13849,13852,13855,13858,13861,13864],{"className":13848},[12337],[21,13850],{"className":13851,"style":12383},[12341],[21,13853,12304],{"className":13854},[12387],[21,13856,12874],{"className":13857},[12346],[21,13859],{"className":13860,"style":13012},[12296],[21,13862,12437],{"className":13863},[12535],[21,13865],{"className":13866,"style":13012},[12296],[21,13868,13870,13873,13913,13916,13919,13922],{"className":13869},[12337],[21,13871],{"className":13872,"style":12383},[12341],[21,13874,13876,13879],{"className":13875},[12346],[21,13877,12694],{"className":13878,"style":12717},[12346,12347],[21,13880,13882],{"className":13881},[12721],[21,13883,13885,13905],{"className":13884},[12474,12475],[21,13886,13888,13902],{"className":13887},[12479],[21,13889,13891],{"className":13890,"style":12986},[12483],[21,13892,13893,13896],{"style":12734},[21,13894],{"className":13895,"style":12738},[12491],[21,13897,13899],{"className":13898},[12496,12497,12498,12499],[21,13900,12853],{"className":13901},[12346,12499],[21,13903,12543],{"className":13904},[12542],[21,13906,13908],{"className":13907},[12479],[21,13909,13911],{"className":13910,"style":12754},[12483],[21,13912],{},[21,13914,4003],{"className":13915},[12410],[21,13917],{"className":13918,"style":13012},[12296],[21,13920,12856],{"className":13921},[12535],[21,13923],{"className":13924,"style":13012},[12296],[21,13926,13928,13931,13960,13963,13966],{"className":13927},[12337],[21,13929],{"className":13930,"style":13025},[12341],[21,13932,13934,13937],{"className":13933},[12346],[21,13935,12862],{"className":13936},[12346],[21,13938,13940],{"className":13939},[12721],[21,13941,13943],{"className":13942},[12474],[21,13944,13946],{"className":13945},[12479],[21,13947,13949],{"className":13948,"style":13044},[12483],[21,13950,13951,13954],{"style":13047},[21,13952],{"className":13953,"style":12738},[12491],[21,13955,13957],{"className":13956},[12496,12497,12498,12499],[21,13958,12853],{"className":13959},[12346,12499],[21,13961],{"className":13962,"style":13012},[12296],[21,13964,12867],{"className":13965},[12535],[21,13967],{"className":13968,"style":13012},[12296],[21,13970,13972,13975,13978,13981,13984,13987],{"className":13971},[12337],[21,13973],{"className":13974,"style":12383},[12341],[21,13976,12304],{"className":13977},[12387],[21,13979,12874],{"className":13980},[12346],[21,13982],{"className":13983,"style":13012},[12296],[21,13985,12437],{"className":13986},[12535],[21,13988],{"className":13989,"style":13012},[12296],[21,13991,13993,13996,14036,14039,14042,14045],{"className":13992},[12337],[21,13994],{"className":13995,"style":12383},[12341],[21,13997,13999,14002],{"className":13998},[12346],[21,14000,12694],{"className":14001,"style":12717},[12346,12347],[21,14003,14005],{"className":14004},[12721],[21,14006,14008,14028],{"className":14007},[12474,12475],[21,14009,14011,14025],{"className":14010},[12479],[21,14012,14014],{"className":14013,"style":12986},[12483],[21,14015,14016,14019],{"style":12734},[21,14017],{"className":14018,"style":12738},[12491],[21,14020,14022],{"className":14021},[12496,12497,12498,12499],[21,14023,12874],{"className":14024},[12346,12499],[21,14026,12543],{"className":14027},[12542],[21,14029,14031],{"className":14030},[12479],[21,14032,14034],{"className":14033,"style":12754},[12483],[21,14035],{},[21,14037,4003],{"className":14038},[12410],[21,14040],{"className":14041,"style":13012},[12296],[21,14043,12856],{"className":14044},[12535],[21,14046],{"className":14047,"style":13012},[12296],[21,14049,14051,14054,14083,14086,14089],{"className":14050},[12337],[21,14052],{"className":14053,"style":13025},[12341],[21,14055,14057,14060],{"className":14056},[12346],[21,14058,12862],{"className":14059},[12346],[21,14061,14063],{"className":14062},[12721],[21,14064,14066],{"className":14065},[12474],[21,14067,14069],{"className":14068},[12479],[21,14070,14072],{"className":14071,"style":13044},[12483],[21,14073,14074,14077],{"style":13047},[21,14075],{"className":14076,"style":12738},[12491],[21,14078,14080],{"className":14079},[12496,12497,12498,12499],[21,14081,12874],{"className":14082},[12346,12499],[21,14084],{"className":14085,"style":13012},[12296],[21,14087,12867],{"className":14088},[12535],[21,14090],{"className":14091,"style":13012},[12296],[21,14093,14095,14098,14101,14104,14107],{"className":14094},[12337],[21,14096],{"className":14097,"style":13171},[12341],[21,14099,11186],{"className":14100},[12346],[21,14102],{"className":14103,"style":13012},[12296],[21,14105,12867],{"className":14106},[12535],[21,14108],{"className":14109,"style":13012},[12296],[21,14111,14113,14116,14119,14122,14125,14128],{"className":14112},[12337],[21,14114],{"className":14115,"style":12383},[12341],[21,14117,12304],{"className":14118},[12387],[21,14120,12874],{"className":14121},[12346],[21,14123],{"className":14124,"style":13012},[12296],[21,14126,12437],{"className":14127},[12535],[21,14129],{"className":14130,"style":13012},[12296],[21,14132,14134,14137,14186,14189,14192,14195],{"className":14133},[12337],[21,14135],{"className":14136,"style":12383},[12341],[21,14138,14140,14143],{"className":14139},[12346],[21,14141,12694],{"className":14142,"style":12717},[12346,12347],[21,14144,14146],{"className":14145},[12721],[21,14147,14149,14178],{"className":14148},[12474,12475],[21,14150,14152,14175],{"className":14151},[12479],[21,14153,14155],{"className":14154,"style":12986},[12483],[21,14156,14157,14160],{"style":12734},[21,14158],{"className":14159,"style":12738},[12491],[21,14161,14163],{"className":14162},[12496,12497,12498,12499],[21,14164,14166,14169,14172],{"className":14165},[12346,12499],[21,14167,12901],{"className":14168},[12346,12347,12499],[21,14170,12437],{"className":14171},[12535,12499],[21,14173,12862],{"className":14174},[12346,12499],[21,14176,12543],{"className":14177},[12542],[21,14179,14181],{"className":14180},[12479],[21,14182,14184],{"className":14183,"style":13238},[12483],[21,14185],{},[21,14187,4003],{"className":14188},[12410],[21,14190],{"className":14191,"style":13012},[12296],[21,14193,12856],{"className":14194},[12535],[21,14196],{"className":14197,"style":13012},[12296],[21,14199,14201,14204,14242,14245,14248],{"className":14200},[12337],[21,14202],{"className":14203,"style":13025},[12341],[21,14205,14207,14210],{"className":14206},[12346],[21,14208,12862],{"className":14209},[12346],[21,14211,14213],{"className":14212},[12721],[21,14214,14216],{"className":14215},[12474],[21,14217,14219],{"className":14218},[12479],[21,14220,14222],{"className":14221,"style":13044},[12483],[21,14223,14224,14227],{"style":13047},[21,14225],{"className":14226,"style":12738},[12491],[21,14228,14230],{"className":14229},[12496,12497,12498,12499],[21,14231,14233,14236,14239],{"className":14232},[12346,12499],[21,14234,12901],{"className":14235},[12346,12347,12499],[21,14237,12437],{"className":14238},[12535,12499],[21,14240,12862],{"className":14241},[12346,12499],[21,14243],{"className":14244,"style":13012},[12296],[21,14246,12867],{"className":14247},[12535],[21,14249],{"className":14250,"style":13012},[12296],[21,14252,14254,14257],{"className":14253},[12337],[21,14255],{"className":14256,"style":13309},[12341],[21,14258,12874],{"className":14259},[12346],[280,14261,14262],{},"对照B与A的表达式，B为A取反后加1",[1183,14264,14265],{},[280,14266,14267,14369,14370,14439],{},[21,14268,14270,14296],{"className":14269},[12269],[21,14271,14273],{"className":14272},[12273],[12275,14274,14275],{"xmlns":12277},[12279,14276,14277,14293],{},[12282,14278,14279,14281,14283,14285,14291],{},[12289,14280,12304],{"stretchy":12303},[12851,14282,12874],{},[12289,14284,12437],{},[12690,14286,14287,14289],{},[12285,14288,12694],{},[12285,14290,12697],{},[12289,14292,4003],{"stretchy":12303},[12325,14294,14295],{"encoding":12327},"(1-k_i)",[21,14297,14299,14320],{"className":14298,"ariaHidden":12333},[12332],[21,14300,14302,14305,14308,14311,14314,14317],{"className":14301},[12337],[21,14303],{"className":14304,"style":12383},[12341],[21,14306,12304],{"className":14307},[12387],[21,14309,12874],{"className":14310},[12346],[21,14312],{"className":14313,"style":13012},[12296],[21,14315,12437],{"className":14316},[12535],[21,14318],{"className":14319,"style":13012},[12296],[21,14321,14323,14326,14366],{"className":14322},[12337],[21,14324],{"className":14325,"style":12383},[12341],[21,14327,14329,14332],{"className":14328},[12346],[21,14330,12694],{"className":14331,"style":12717},[12346,12347],[21,14333,14335],{"className":14334},[12721],[21,14336,14338,14358],{"className":14337},[12474,12475],[21,14339,14341,14355],{"className":14340},[12479],[21,14342,14344],{"className":14343,"style":12731},[12483],[21,14345,14346,14349],{"style":12734},[21,14347],{"className":14348,"style":12738},[12491],[21,14350,14352],{"className":14351},[12496,12497,12498,12499],[21,14353,12697],{"className":14354},[12346,12347,12499],[21,14356,12543],{"className":14357},[12542],[21,14359,14361],{"className":14360},[12479],[21,14362,14364],{"className":14363,"style":12754},[12483],[21,14365],{},[21,14367,4003],{"className":14368},[12410],"相当于对",[21,14371,14373,14390],{"className":14372},[12269],[21,14374,14376],{"className":14375},[12273],[12275,14377,14378],{"xmlns":12277},[12279,14379,14380,14388],{},[12282,14381,14382],{},[12690,14383,14384,14386],{},[12285,14385,12694],{},[12285,14387,12697],{},[12325,14389,12700],{"encoding":12327},[21,14391,14393],{"className":14392,"ariaHidden":12333},[12332],[21,14394,14396,14399],{"className":14395},[12337],[21,14397],{"className":14398,"style":12710},[12341],[21,14400,14402,14405],{"className":14401},[12346],[21,14403,12694],{"className":14404,"style":12717},[12346,12347],[21,14406,14408],{"className":14407},[12721],[21,14409,14411,14431],{"className":14410},[12474,12475],[21,14412,14414,14428],{"className":14413},[12479],[21,14415,14417],{"className":14416,"style":12731},[12483],[21,14418,14419,14422],{"style":12734},[21,14420],{"className":14421,"style":12738},[12491],[21,14423,14425],{"className":14424},[12496,12497,12498,12499],[21,14426,12697],{"className":14427},[12346,12347,12499],[21,14429,12543],{"className":14430},[12542],[21,14432,14434],{"className":14433},[12479],[21,14435,14437],{"className":14436,"style":12754},[12483],[21,14438],{},"取反",{"title":16,"searchDepth":30,"depth":30,"links":14441},[14442,14445,14446,14447],{"id":12194,"depth":30,"text":12194,"children":14443},[14444],{"id":12245,"depth":36,"text":12245},{"id":12262,"depth":30,"text":12262},{"id":12586,"depth":30,"text":12586},{"id":12629,"depth":30,"text":12629,"children":14448},[14449,14450],{"id":12651,"depth":36,"text":12651},{"id":12668,"depth":36,"text":12668},"2023-01-15",{},"\u002Fblog\u002Fcomplement",{"title":12153,"description":294},"blog\u002Fcomplement",[14457],"计算机基础","wJd52QgJzLZLToP6Ya9bsta4Cl_UbHvp9a6E-rxpRUY",1777128585436]