├── .gitignore ├── C++11_14 ├── C++11_14_jjhou.md ├── [vczh]C++11.pptx ├── assets │ ├── 1561365339994.png │ ├── 1561374764032.png │ ├── 1561378316251.png │ ├── 1561379849604.png │ ├── 1561379925287.png │ ├── 1561379992899.png │ ├── 1561382069723.png │ ├── 1561553789950.png │ ├── 1561553965104.png │ ├── 1561554468601.png │ ├── 1561554777156.png │ ├── 1561555267366.png │ ├── 1561555475197.png │ ├── 1561555693157.png │ ├── 1561556005409.png │ ├── 1561606171002.png │ ├── 1561606408112.png │ ├── 1561606683917.png │ ├── 1561615144711.png │ ├── 1561615997563.png │ ├── 1561616013503.png │ ├── 1561616128477.png │ ├── 1561616221927.png │ ├── 1561617356511.png │ ├── 1561617793229.png │ ├── 1561618425129.png │ ├── 1561619185785.png │ ├── 1561620986751.png │ ├── 1561622745241.png │ ├── 1561625490878.png │ ├── 1561626345740.png │ ├── 1561627676584.png │ ├── 1561628332035.png │ ├── 1561634722050.png │ ├── 1561635119640.png │ ├── 1561635286581.png │ ├── 1561635976912.png │ ├── 1561636731726.png │ ├── 1561637095441.png │ ├── 1561637526193.png │ ├── 1561637843402.png │ ├── 1561637895896.png │ ├── 1561638544250.png │ ├── 1561638870294.png │ ├── 1561639105366.png │ ├── 1561691318808.png │ ├── 1561691540157.png │ ├── 1561692080652.png │ ├── 1561692308172.png │ ├── 1561692484391.png │ ├── 1561706232308.png │ ├── 1561708905361.png │ ├── 1561713015542.png │ ├── 1561717430124.png │ ├── 1561723021811.png │ ├── 1561725331121.png │ ├── 1561728160229.png │ ├── 1561728415477.png │ ├── 1561787721037.png │ ├── 1561788622372.png │ ├── 1561789478633.png │ ├── 1561792248957.png │ ├── 1561792840864.png │ ├── 1561792943974.png │ ├── 1561793021661.png │ ├── 1561796395862.png │ ├── 1562592085099.png │ ├── 1562593580624.png │ ├── 1562593805165.png │ ├── 1562636675292.png │ ├── 1562636782385.png │ ├── 1562637273173.png │ ├── 1562637551804.png │ ├── 1562637991925.png │ ├── 1562638648850.png │ ├── 1562638720654.png │ ├── 1562639513705.png │ ├── 1562639709758.png │ ├── 1562640393636.png │ ├── 1562640412000.png │ ├── 1562640429861.png │ ├── 1562640443566.png │ ├── 1562900253744.png │ ├── 1562901115372.png │ ├── 1562901655107.png │ ├── 1562901757867.png │ ├── 1562902274876.png │ ├── 1562902864080.png │ ├── 1562927561122.png │ ├── 1562927748674.png │ ├── 1562927905752.png │ ├── 1562927991200.png │ ├── 1562928493241.png │ ├── 1562929786826.png │ ├── 1562929808570.png │ ├── 1562929956782.png │ ├── 1562933363516.png │ ├── 1562933773025.png │ ├── 1562933959634.png │ ├── 1562939123324.png │ ├── 1562940226064.png │ └── 1562940245228.png └── code │ └── test_variadic_template.cpp ├── C++11并发与多线程 ├── C++11内存模型.md ├── README.md ├── assets │ ├── 1565868441548.png │ ├── image-20200816233034724.png │ ├── image-20200816234252090.png │ └── image-20200816235229162.png ├── code │ ├── 2_2.cpp │ ├── 2_3_1.cpp │ ├── 2_3_2.cpp │ ├── 2_3_3.cpp │ └── 2_3_4.cpp └── notes.md ├── C++内存管理 ├── assets │ ├── image-20191024214950918.png │ ├── image-20191026165236830.png │ └── image-20191027112733778.png ├── 内存管理.md └── 内存管理.pptx ├── C++小知识 ├── copy-and-swap idiom.md └── print │ ├── assets │ ├── 1567580434140.png │ └── 1567581621695.png │ ├── print.cpp │ ├── print.md │ └── print.pptx ├── C++面向对象高级编程 ├── C++面向对象程序设计_part1.md ├── C++面向对象程序设计_part2.md ├── assets │ ├── 1557455342813.png │ ├── 1557455426279.png │ ├── 1557455877512.png │ ├── 1557456561799.png │ ├── 1557457075841.png │ ├── 1557457377946.png │ ├── 1557458654337.png │ ├── 1557459096287.png │ ├── 1557463294773.png │ ├── 1557463840516.png │ ├── 1557469726240.png │ ├── 1557470130720.png │ ├── 1557470685958.png │ ├── 1557471481557.png │ ├── 1557472309252.png │ ├── 1557473149205.png │ ├── 1557473358698.png │ ├── 1557473691604.png │ ├── 1557474432766.png │ ├── 1557477552802.png │ ├── 1557477761856.png │ ├── 1557478475362.png │ ├── 1557479620018.png │ ├── 1557480221092.png │ ├── 1557481099332.png │ ├── 1557481941108.png │ ├── 1557482417743.png │ ├── 1557487075666.png │ ├── 1557487606126.png │ ├── 1557487638928.png │ ├── 1557487724501.png │ ├── 1557492888491.png │ ├── 1557492926893.png │ ├── 1557493242994.png │ ├── 1557493258187.png │ ├── 1557493966655.png │ ├── 1557494889736.png │ ├── 1557495133404.png │ ├── 1557714511998.png │ ├── 1557715259428.png │ ├── 1557715824622.png │ ├── 1557716265949.png │ ├── 1557716414284.png │ ├── 1557716746695.png │ ├── 1557717032223.png │ ├── 1557717432386.png │ ├── 1558453874580.png │ ├── 1558457528106.png │ ├── 1558457888072.png │ ├── 1558459151904.png │ ├── 1558491481526.png │ ├── 1558716277153.png │ ├── 1558716850059.png │ ├── 1558718409179.png │ ├── 1558718827030.png │ ├── 1558752779674.png │ ├── 1558753009197.png │ ├── 1558760439215.png │ ├── 1558873574510.png │ ├── 1558873750010.png │ ├── 1558875339692.png │ ├── 1558887391028.png │ ├── 1558923111947.png │ ├── 1558925338958.png │ ├── 1558925377433.png │ ├── 1558925446700.png │ ├── 1558925624955.png │ ├── 1558939321858.png │ ├── 1558943366112.png │ ├── 1558945922875.png │ ├── 1558948644501.png │ ├── 1558949507638.png │ ├── 1560404584181.png │ ├── 1560404974745.png │ ├── 1560405283461.png │ ├── 1560405362848.png │ ├── 1560406299464.png │ ├── 1560406833869.png │ ├── 1560407154869.png │ ├── 1560410852393.png │ ├── 1560411003846.png │ ├── 1560411429029.png │ ├── 1560411583591.png │ ├── 1560411683785.png │ ├── 1560411846337.png │ ├── 1560412186484.png │ ├── 1560413833493.png │ ├── 1560413886633.png │ ├── 1560419363053.png │ ├── 1560419673831.png │ ├── 1560419917708.png │ ├── 1560422076723.png │ ├── 1560422092804.png │ ├── 1560422107865.png │ ├── 1560761309230.png │ ├── 1560761942136.png │ ├── 1560762376012.png │ ├── 1560773568977.png │ ├── 1560773903634.png │ ├── 1560774047068.png │ ├── 1560774481410.png │ ├── 1560774759364.png │ ├── 1560775017510.png │ └── 1560775583436.png ├── code │ ├── complex.h │ ├── complex_test.cpp │ ├── string.h │ ├── string_test.cpp │ └── test.cpp └── 补充内容.md ├── CPU缓存 ├── README.md └── codedive-CPUCachesHandouts.pdf ├── README.md └── 数据结构与算法 └── A └── 字符串 ├── README.md ├── assets ├── 1570527790304.png ├── 1570533478385.png ├── 1570542610277.png ├── 1570543288650.png ├── 1570590461519.png ├── 1570591941387.png ├── 1570592311427.png ├── 1570613990056.png ├── 1570614018748.png ├── 1570614568268.png ├── 1570620227448.png └── 1570622593407.png └── code ├── BM_bc+gs.cpp ├── Brute-force-version1.cpp ├── Brute-force-version2.cpp ├── KMP.cpp └── KMP_Pro.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | slides/ 2 | pdfs/ 3 | sample.cpp -------------------------------------------------------------------------------- /C++11_14/C++11_14_jjhou.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # C++11/14 笔记 6 | 7 | ​ 作者:方阳,笔记地址:https://github.com/FangYang970206/Cpp-Notes 8 | 9 | 这篇文章记录了学习侯捷老师C++11/14课程的笔记。C++11是C++2.0,引入了许多新的特性,将从语言层面和标准库层面(以header files形式呈现)来介绍这些新的特性。 10 | 11 | [TOC] 12 | 13 | ## 语言层面 14 | 怎样确定C++环境是否支持C++11呢? 15 | 使用如下语句: 16 | 17 | ```c++ 18 | cout << __cplusplus << endl; 19 | ``` 20 | 21 | 如果出现的头六位数是大于等于201103的,则支持C++11。 22 | 23 | ### 模板表达式中的空格 24 | 25 | ![1561365339994](assets/1561365339994.png) 26 | 27 | 在C++11之前,模板后面的尖括号需要空格,C++11之后就不需要了。 28 | 29 | ### nullptr和std::nullptr_t 30 | 31 | ![1561374764032](assets/1561374764032.png) 32 | 33 | 使用nullptr代替NULL和0来代表指针没有指向值。这可以避免把空指针当int而引发错误。上图中给出了函数调用的实例,使用nullptr不会出现这种问题,这是因为nullptr是std::nullptr_t类型,c++11以后,std::nullptr_t也是基础类型了,可以自己定义变量。 34 | 35 | ### 自动推导类型----auto 36 | 37 | ![1561378316251](assets/1561378316251.png) 38 | 39 | C++11之后,可以使用auto自动推导变量和对象的类型,而不需要自己手写出来,对于有非常复杂而长的类型来说,这是很方便的,另外auto还可以自动推导lambda表达式的类型,然后就可以把lambda函数当普通函数使用。 40 | 41 | 典型用法(更简单): 42 | 43 | ![1561379849604](assets/1561379849604.png) 44 | 45 | 标准库中的使用: 46 | 47 | ![1561379925287](assets/1561379925287.png) 48 | 49 | ### 一致性初始化----Uniform Initialization 50 | 51 | ![1561379992899](assets/1561379992899.png) 52 | 53 | C++11引入了一个通用的初始化方式——一致性初始化,使用大括号括起来进行初始化,编译器看到这种初始化会转换成一个initializer_list,然后要分两种情况: 54 | 55 | - 如果对象带有接受initializer_list的构造函数版本,那使用该构造函数进行初始化。(如上vector初始化) 56 | - 如果对象没有initializer_list的构造函数版本,那编译器会将initializer_list逐一分解,传给对应的构造函数。(如上complex初始化) 57 | 58 | 另外,如果函数的参数就是initializer_list,那么就必须传入initializer_list,而不能传入多个T参数。 59 | 60 | ### 初始化列表(initializer_list) 61 | 62 | ![1561382069723](assets/1561382069723.png) 63 | 64 | 大括号可以设定初值(默认值),另外,大括号初始化不允许窄化转换(书籍上这样说的,实际gcc只会给出警告,但这不是好习惯)。 65 | 66 | ![1561553789950](assets/1561553789950.png) 67 | 68 | C++11提供了一个std::initializer_list<>, 可以接受任意个数的相同类型,上面是一个实例。 69 | 70 | ![1561553965104](assets/1561553965104.png) 71 | 72 | 上图的左边是initializer_list在构造函数中的应用,右边是initializer_list的源代码,它的内部有一个array和一个长度,另外initializer_list的构造函数是私有的,但编译器当看到大括号的时候,就会调用这个构造函数,编译器有无上权力。initializer_list构造函数会传入array(C++11新提出的,对数组进行封装,可以使用算法库)的头部迭代器,以及它的长度。 73 | 74 | ![1561554468601](assets/1561554468601.png) 75 | 76 | 上面是array的源代码,里面就是一个基本的数组,然后封装了begin和end迭代器。 77 | 78 | ![1561554777156](assets/1561554777156.png) 79 | 80 | 左下角是新东西,其他的之前出现过了,第一句话是指initializer_list背后有一个array在支撑,第二句话是说initializer_list并没有包含那个array,只是有一个指针指向那个array的头部和一个size_t等于array的长度。如果拷贝initializer_list,只是浅拷贝,指向同一个array以及得到同一个长度。最后一句话是说那个临时array的生命周期与initializer_list是相同的。 81 | 82 | ![1561555267366](assets/1561555267366.png) 83 | 84 | 这是标准库中使用initializer_list的各个地方,非常之多,这里只列举vector里面的使用,有初始化,重载赋值运算符,插入以及分配。 85 | 86 | ![1561555475197](assets/1561555475197.png) 87 | 88 | 上面的是具体事例以及对应调用有initializr_list的方法。 89 | 90 | ### explicit 91 | 92 | ![1561555693157](assets/1561555693157.png) 93 | 94 | 这节在C++面向对象高级编程中有很多的补充,可以去看看,在构造函数前面加上explicit, 就是告诉编译器, 不要在将int类型隐式转成Complex, 只能通过显式地进行构造。 95 | 96 | ![1561556005409](assets/1561556005409.png) 97 | 98 | 之前一张图是只有一个实参,这里是多个实参的例子,当使用运行p3{77, 5, 42}的时候,直接调用的是带有initializer_list的构造函数(一致性初始化),而p5 = {77, 5, 42}, {77, 5, 42}是initialization_list类型,不能隐式将initialization_list转成各个int,更详细的可以参考https://stackoverflow.com/questions/30142484/explicit-constructor-and-initialization-with-stdinitializer-list/30142573 ,提问一模一样。我自己也进行了测试,发现P p5 = {77, 5, 42}并没有报错,它调用的是initialization_list的构造函数,可能使用的编译器不一样,导致结果也不一样,我使用的是Clion+MinGW。但一样的,如果没有initialization_list的构造函数,就会报错,原因上面说了。 99 | 100 | ### range-based for 101 | 102 | ![1561606171002](assets/1561606171002.png) 103 | 104 | 这一小节讲的是非常实用的for,C++11提供了range-based for,如上所述,decl是申明,coll是容器,意思是一个个拿出coll中的元素,下面有实例,可以搭配auto使用,非常方便,需要in-place的话,加上&即可。 105 | 106 | ![1561606408112](assets/1561606408112.png) 107 | 108 | 左边是range-based for,右边是编译器对它的解释。 109 | 110 | ![1561606683917](assets/1561606683917.png) 111 | 112 | 这是explicit的一个例子,禁止编译器隐式将String转化C,所以会报错。 113 | 114 | ### =default, =delete 115 | 116 | ![1561615144711](assets/1561615144711.png) 117 | 118 | =default要的是编译器给的default ctor,=delete是不要对应的ctor,例如,上述的`Zoo(const Zoo&)=delete`是说不要拷贝构造,`Zoo(const Zoo&&)=default`是说要编译器默认给我的那一个。 119 | 120 | ![1561615997563](assets/1561615997563.png) 121 | 122 | ![1561616013503](assets/1561616013503.png) 123 | 124 | ![1561616128477](assets/1561616128477.png) 125 | 126 | 以上三张图是C++标准库中使用=default和=delete的事例,标准库都用了,那自然是好的。(这里注意析构函数不能用=delete,可能会出大问题) 127 | 128 | ![1561616221927](assets/1561616221927.png) 129 | 130 | 构造函数可以有多个版本,上述定义了两个Foo的构造函数,一个是有实参的,另一个使用=default得到编译器默认给出的构造函数。对于拷贝构造而言,只能允许一个,所以当使用=default的时候,由于已经写出一个了,就无法进行重载了,而使用=delete的时候,由于写出来了,无法进行删除了。拷贝赋值情况类似。对于一般函数来说,没有default版本,所以对一般函数进行=default是不对的,但=delete可以有,但没必要,写出来不要还不如不写。上图中还给出了=default,=delete与=0的区别,区别在与=default只能用于big-five(构造函数,拷贝构造,赋值构造,析构,移动构造,移动赋值), =delete可以用于任何函数,但有时没有必要使用,如上面所说,而=0只能用于虚函数,代表纯虚函数。 131 | 132 | ![1561617356511](assets/1561617356511.png) 133 | 134 | 对于一个空的class,C++会在空的class内部插入一些代码(默认的构造函数,拷贝构造,拷贝赋值以及析构函数,都是public并且是inline的),这样才会使左下角的的代码运行正常,作用还不止这些,这些默认的函数还给编译器放置藏身幕后的一些代码,比如当涉及继承的时候,调用base classes的构造和析构就会对应放置在默认生成的构造和析构当中。 135 | 136 | ![1561617793229](assets/1561617793229.png) 137 | 138 | 如果一个类带有pointer member,则需要自己定义big-three,而没有pointer member的话,用编译器默认提供的就足够了。上面的complex就是直接使用编译器默认提供的拷贝赋值和析构。更详细的推荐看我写的面向对象程序设计_part1部分的笔记,有非常详细的讲述。 139 | 140 | ![1561618425129](assets/1561618425129.png) 141 | 142 | 上图是=default和=delete的使用事例,class NoCopy把拷贝构造和拷贝赋值都=delete,也就是没有这两个了,不允许外界去拷贝这个类的对象,这个在一些事例上是有用的。class NoDtor则不要析构函数了,对象创建无法删除,会报错。(一般不会这么使用)最后的PrivateCopy把拷贝构造和拷贝赋值放入了private里面,这限制了访问这两个函数的使用者,一般用户代码无法调用,但友元以及成员可以进行拷贝。 143 | 144 | ![1561619185785](assets/1561619185785.png) 145 | 146 | 这是一个Boost库的例子,与上述的PrivateCopy一样,它的作用是让其他类继承这个类,这样其他类也拥有noncopyable同样的性质。 147 | 148 | ### Alias Template 与 Template Template parameter 149 | 150 | ![1561620986751](assets/1561620986751.png) 151 | 152 | C++11引入了Alias Template,用法如上所示,先些template , 然后使用using命令设定别名模板,这样些可以自己设定类型以及容器的分配器。而使用define和typedef确不能达到效果。但别名模板有一个限制,不能进行偏特化(可以参考面向对象的笔记了解什么是偏特化) 153 | 154 | ![1561622745241](assets/1561622745241.png) 155 | 156 | Alias template难道只是少打几个字吗? 不是的,上图进行说明,函数test_moveable测试不同容器的move操作(右值引用)和拷贝操作的时间比较,想使用容器和元素的类型,这是天方夜谈的,container和T是不能再函数内部使用,报出了三个错误。然后再进行改进,改成右边形式的,利用函数模板的实参推导可以推出Container和T的类型,不然依然是天方夜谭,编译器不认识Container是个模板,无法使用尖括号. 157 | 158 | ![1561625490878](assets/1561625490878.png) 159 | 160 | 这一页在Container前面加上了typename,告诉编译器Container就是一个typename,然而编译器还是报错,认为期望嵌套名称说明符在Container前面,还是无法识别模板。 161 | 162 | ![1561626345740](assets/1561626345740.png) 163 | 164 | 上图就是解决方案,传入的实参只有一个,根据模板函数的自动推导,得到它的迭代器(前面要加typename),然后通过一个迭代器萃取机引出对象的Value_type, 然后根据typedef得到值类型,这样就不会报错了。然后看右上角黄色的话语,如果没有iterator和traits,该怎么解决这一问题呢?上面就是思考路径,在模板接受模板,能不能从中取出模板的参数? 165 | 166 | 这就需要template template parameter了。 167 | 168 | ![1561627676584](assets/1561627676584.png) 169 | 170 | 模板模板参数是模板嵌套模板,如上面所示,XCI接受两个参数,第一个是T,第二个是模板Container,然后就可以直接使用`Container c;` 因为Container是一个模板,但再调用`XCIs c1;`的时候,出现报错,原因是vector有两个模板参数,第二个模板参数(分配器)是默认的,但编译器不知道,这个时候就需要用到Alias Template了。 171 | 172 | ![1561628332035](assets/1561628332035.png) 173 | 174 | 使用Alias Template,就可以将Vec变为一个模板参数的模板,然后就可以初始化对象了。可以看到Alias Template不仅是少打几个字,还有减少模板参数个数以适配模板模板参数,非常有用处。 175 | 176 | ### Type Alias 177 | 178 | ![1561634722050](assets/1561634722050.png) 179 | 180 | Type Alias是另一个typedef的写法,不过更加清晰,通过using关键字去实现,左上角的是定义了一个函数指针,用typedef不太明显,用using很清晰,另外还可以用于类中成员,右下角所示。左下角是Alias Template的例子,我们日常使用的string实则是basic_string, 左下角给出了using和typedef的写法,右上角说明两者是没有任何差别的。 181 | 182 | ### using 183 | 184 | ![1561635119640](assets/1561635119640.png) 185 | 186 | 给出了using的使用场景。 187 | 188 | ### noexcept 189 | 190 | ![1561635286581](assets/1561635286581.png) 191 | 192 | noexcept是放在函数右括号后,宣称这个函数不会抛出异常(这里还给出异常的回传机制,调用foo如果抛出异常,foo会接着往上层抛出异常,如果最上层没有处理,则会调用terminate函数,terminate函数内部调用abort,使程序退出),noexcept可以接受条件,如上所示,没有加条件,默认是不会抛出异常,swap函数不发生异常的条件是noexcept(x.swap(y))不会发生异常。 193 | 194 | ![1561635976912](assets/1561635976912.png) 195 | 196 | 在使用vector和deque的移动构造和移动赋值的时候,如果移动构造和移动赋值没有加上noexcept,则容器增长的时候不会调用move constructor,效率就会偏低(逐一拷贝),所以后面需要加上noexcept,安心使用。 197 | 198 | ### override 199 | 200 | ![1561636731726](assets/1561636731726.png) 201 | 202 | override用于虚函数,上面的virtual void vfunc(int)实际上不是重写父类的虚函数,而是定义一个新的虚函数,我们的本意是重写虚函数,但这样写编译器不会报错,确实没问题,那如果像下面加上override的话,则会报错,因为已经告诉了编译器,我确实要重写,但写错了,没有重写,于是就报错了。 203 | 204 | ### final 205 | 206 | ![1561637095441](assets/1561637095441.png) 207 | 208 | final关键字用于两个地方,第一个用在类,用于说明该类是继承体系下最后的一个类,不要其他类继承我,当继承时就会报错。第二个用在虚函数,表示这个虚函数不能再被override了,再override就会报错。 209 | 210 | ### decltype 211 | 212 | ![1561637526193](assets/1561637526193.png) 213 | 214 | 使用decltype关键字,可以让编译器找到一个表达式它的类型,这个很像typeof的功能,然而已存在的typeof的实现并无完整和一致,所以C++11介绍了一个新的关键字,上面给出了一个事例(coll可能离elem很远)。 215 | 216 | decltype的应用有三部分,用作返回值的类型,元编程以及lambda函数的类型 217 | 218 | ![1561637895896](assets/1561637895896.png) 219 | 220 | 上图就是用作返回值的类型,图中第一个代码块编译无法通过,因为return表达式所用的对象没有在定义域内。C++11则允许另外一种写法,第二个代码块,返回类型用auto暂定,但在后面写出,用`-> decltype(x+y)`, 这里要说明,模板是一种半成品,类型没有定义,`decltype(x+y)`也能是正确的也可能是错误的,取决于调用者本身的使用。`-> decltype(x+y)`与lambda的返回类似。 221 | 222 | ![1561638544250](assets/1561638544250.png) 223 | 224 | 上图是decltype在标准库中的使用,到处可见。 225 | 226 | ![1561638870294](assets/1561638870294.png) 227 | 228 | 用于元编程推导实参的类型,由于加了`::iterator`, 传入的实参必须是容器,传入复数会报错,这就是模板的半成品特性。 229 | 230 | ![1561639105366](assets/1561639105366.png) 231 | 232 | 对于lambda函数,很少有人能够写出它的类型,而有时就需要知道它的类型,如上定义所示,这时候就可以使用decltype来自动推导lambda函数的类型。 233 | 234 | ### lambdas 235 | 236 | ![1561691318808](assets/1561691318808.png) 237 | 238 | C++11介绍了lambdas(可以说是匿名函数或仿函数),允许定义在声明和表达式中,作为一种内联函数。如上所示,最简单的lambda通过一个[]{statements}表示,可以直接加()运行,或者使用auto l = []{statements},l则代表lambda函数,可以在后面进行调用。 239 | 240 | ![1561691540157](assets/1561691540157.png) 241 | 242 | 上面是lambdas函数的结构类型,中括号[]内部是可以抓取外面的非静态对象进行函数内部的使用,有以值[=]进行抓取和以引用[&]进行抓取,如果只抓取部分对象,可以进行指定,如上面的x,y,x就是按值进行抓取,y就是按引用进行抓取。小括号()里面则是可以接函数参数,跟普通函数一样。`mutable`可选,指的是以值进行抓取的对象是否可变,可变就需要加上,否则会报错。`throwSepc`是指这个函数可以不可以抛出异常。`->retType`指的是lambda函数的返回类型。大括号内部则是函数的主体。 243 | 244 | ![1561692080652](assets/1561692080652.png) 245 | 246 | 上面是一个事例,这页幻灯片说的是lambda函数映射类似一个仿函数和mutable的作用,之所以说类似,这是因为如果lambda以值传递,则要修改值对象,需要加上mutable,否则会报错,而仿函数没有限制。 247 | 248 | ![1561692308172](assets/1561692308172.png) 249 | 250 | 这页幻灯片是上页的比较,要修改以值传递的对象,需要加mutable,如果是按引用传递的对象,则可以不加,如果修改以值传递的对象而不加mutable,则会报错read-only. 此外,以引用传递的对象,不仅会受lambda函数内部的影响,还会受到外部的影响。另外,在lambda函数中,可以申明变量和返回数值。 251 | 252 | ![1561692484391](assets/1561692484391.png) 253 | 254 | 上图是编译器给lambda函数生成得代码,可以看到就是一个仿函数(重载了小括号操作符)的类,用lambda形式写非常简洁,并且要高效一些(inline)。 255 | 256 | ![1561706232308](assets/1561706232308.png) 257 | 258 | 这张图的最上面是说每一个lambda函数都是独特的,要申明lambda对象的类型,可以使用template或者auto进行自动推导。如果需要知道类型,可以使用decltype,比如,让lambda函数作为关联容器或者无序容器的排序函数或者哈希函数。上面代码给出了事例(decltype的第三种用法中的事例),定义了一个lambda函数用cmp表示,用来比较Person对象的大小,传入到Set容器中去,但根据右边的set容器的定义,我们传入的不仅是cmp(构造函数),还要传入模板的cmp类型(Set内部需要声明cmp类型),所以必须使用decltype来推导出类型。(如果没有向构造函数传入cmp,调用的是默认的构造函数,也就是`set() : t(Compare())`, 这里会报错, 因为Compare()指的是调用默认的lambda构造函数,而lambda函数没有默认构造函数和赋值函数) 259 | 260 | ![1561708905361](assets/1561708905361.png) 261 | 262 | 函数对象是很强大的,封装代码和数据来自定义标准库的行为,但需要写出函数对象需要写出整个class,这是不方便的,而且是非本地的,用起来也麻烦,需要去看怎样使用,另外编译出错的信息也不友好,而且它们不是inline的,效率会低一些(算法效率还是最重要的)。而lambda函数的提出解决了这个问题,简短有效清晰,上面的事例很好的说明了这个问题,用lambda要简短许多,功能一样,很直观。 263 | 264 | ### Variadic Template (重磅原子弹) 265 | 266 | #### print函数的例子 267 | 268 | ![1561713015542](assets/1561713015542.png) 269 | 270 | Variadic Template是指数量不定,类型不定的模板,这是C++11原子弹级别的炸弹,如上所示的print函数,可以看到接受了不同类型的参数,调用的函数就是拥有Variadic Template的函数,`print(7.5, "hello", bitset<16>(377), 42)`运行的时候,首先会7.5作为firstArg,剩余部分就是一包,然后在函数内部,继续递归调用print函数,然后把"hello"作为firstArg, 其余的作为一包,一直递归直到一包中没有数据,调用边界条件的print(空函数)结束。 271 | 272 | 函数的`...`表示一个包,可以看到,用在三个地方, 273 | 274 | - 第一个地方是模板参数`typename...` ,这代表模板参数包。 275 | 276 | - 第二个就是函数参数类型包(`Type&...`), 指代函数参数类型包。 277 | 278 | - 第三个就是函数参数包`args...`,指的是函数参数包。 279 | 280 | 另外,还可以使用`sizeof...(args)`得到包的长度。右边的是另外一种类型的print,可以和左边的print共同存在,我测试了一下: 281 | 282 | ```c++ 283 | #include 284 | #include 285 | 286 | using namespace std; 287 | 288 | void print() {}; 289 | 290 | template 291 | void print(const T& firstArg, const Types&... args) 292 | { 293 | cout << firstArg << endl; 294 | print(args...); 295 | } 296 | 297 | template 298 | void print(const Types&... args) 299 | { 300 | cout << "common print" << endl; 301 | } 302 | 303 | int main() { 304 | print(7.5, "hello", bitset<16>(377), 42); 305 | return 0; 306 | } 307 | ``` 308 | 309 | 输出的结果如下: 310 | 311 | ```bash 312 | 7.5 313 | hello 314 | 0000000101111001 315 | 42 316 | ``` 317 | 318 | 可以看到调用的还是左边的print,至于为什么,后面再说! 319 | 320 | #### 哈希表的例子 321 | 322 | ![1561717430124](assets/1561717430124.png) 323 | 324 | 上面这个是用variadic template实现哈希表的过程,CustomerHash重载了小括号操作符,内部调用了hash_val,有三个参数,调用的是前面有圆圈1的hash_val,因为其他的hash_val第一参数不符合,然后这个hash_val函数里面设定种子(seed),调用带有圆圈2的hash_val函数,取出第一个值,调用hash_combine重新设定seed,然后再递归调用圆圈2的hash_val, 再重新得到新种子,直到`arg...`只有一个参数的时候, 调用圆圈3的hash_val函数,hash_val函数调用hash_combine函数,得到最后的seed,即为哈希值。 325 | 326 | #### tuple 327 | 328 | ![1561723021811](assets/1561723021811.png) 329 | 330 | C++11还引入了一种新的容器,名为tuple,可以容纳不同类型的数据,左边是它的简单实现,关注继承那三行代码,可以看到tuple的模板参数是一个`Head`和一个包`...Tail`,继承的却是`private tuple<...Tail>`,而`tuple<...Tail>`还是tuple,所以又会拆分成`tuple`,不断递归,形成一种递归继承,终止条件就是空的tuple类,在左上角定义的,如果定义`tuple`,它的具体形式如右上角所示,是不断继承的结构,这就是能容纳不同类型的原因,中上角也是类似的抽象关系。tuple初始化先初始化Head,然后初始化继承的inherited,继承的inherited也会类似初始化,直到到达空的tuple,还给出tuple的两个函数head()和tail(),head()直接返回的是当前类本身的数据(不是从父类继承过来的),而调用tail()返回this指针(指向当前的那一块内存),经过向上转型得到inherited的地址(指向当前继承的那一块)。 331 | 332 | 以上是开头讲的variadic template,现在进入正式讲解variadic template的环节。 333 | 334 | ![1561725331121](assets/1561725331121.png) 335 | 336 | 先回顾了template,一般的模板有函数模板,类模板以及成员模板,强大的还是可变化的模板参数,变化表现在参数个数也表现在参数类型,利用参数个数逐一递减的特性,实现函数的递归调用,同时个数上的递减也会导致参数类型也逐一递减,从而实现递归继承(tuple)以及递归复合。最下面的是函数使用variadic template一种常见的写法。 337 | 338 | ![1561728160229](assets/1561728160229.png) 339 | 340 | 这页幻灯片前面已经讲述了,不过这里给出了之前幻灯片中的一个疑问,`print(7.5, "hello", bitset<16>(377), 42)`为什么调用左边的函数,而不是右边的,这是因为模板有特化的概念,相对于圆圈3实现的printX(泛化),圆圈1实现的printX更加特化,所以会调用左边的函数。 341 | 342 | #### printf例子 343 | 344 | ![1561728415477](assets/1561728415477.png) 345 | 346 | 上面这是使用variadic template实现C语言的printf,很简洁的写法。前面的`"%d %s %p %f\n"`是第一个参数s,后面的参数构造与print类似,一次取一个对象,参数s用以printf里面的循环条件,当`*s`非空时, 347 | 348 | - 如果`*s`等于`'%'`且下一个字符不等于`%`,则打印取出的对象,同时递归调用printf函数,要对字符指针进行自加移位。 349 | 350 | - 如果上述条件不成立,则打印`*s++` 351 | 352 | 最后的终止条件是`args...`为空,打印完了,调用边界条件的printf,对剩余的`*s`进行打印,还要进行`%`判断,因为已经打印完了,还有符合条件的`%`,则需要抛出异常。 353 | 354 | ![1561787721037](assets/1561787721037.png) 355 | 356 | 给定一包数据,找出它们的最大值,也可以通过variadic template实现,不过当数据的类型都相同的时候,无需动用大杀器,使用initializer_list足矣。上面是max使用initializer_list的实现,由于使用initializer_list,所以需要讲数据用大括号包起来,编译器会自动生成initializer_list,然后调用max_element函数得到最大值的地址,然后加`*`得到最大值,而max_element是一个模板函数,调用的是`__max_element`函数,`__max_element`函数内部使用了`__iter_less_iter`类得到一个比大小的临时对象,然后使用临时对象重载操作符的方法对每一个元素进行比较,最后返回最大值的地址。 357 | 358 | ![1561788622372](assets/1561788622372.png) 359 | 360 | 上面是variadic template实现的方法, 采用的是递归策略,很好懂的。还可以进行改进,讲上图中的int换成模板参数T的话,那么maximum方法就可以接受所有的类型的参数,混合在一起比较(比如double和int混合)。 361 | 362 | #### tuple输出操作符 363 | 364 | ![1561789478633](assets/1561789478633.png) 365 | 366 | tuple重载的输出流操作符,也使用variadic template,运行右边的那行代码,将得到下面黑色的输出,make_tuple函数是根据参数(可以任意个,内部估计也是使用了variadic template),初始化得到一个tuple,可以看到输出流操作符得第二个参数就是可变模板参数的tuple,内部调用PRINT_TUPLE类中的静态print函数,PRINT_TUPLE有三个模板参数,第一个当前索引IDX,第二个是tuple内含有MAX个对象,第三个就是模板参数包。通过`get(t)`可以得到tuple的第IDX元素,然后进行输出,依次递归调用print函数,如果IDX是最后一个元素了满足`IDX+1==MAX`, 输出`""`,然后调用终止的`PRINT_TUPLE::print`函数(空的)完成打印。 367 | 368 | #### tuple补充 369 | 370 | ![1561792248957](assets/1561792248957.png) 371 | 372 | 上图之前讲过了,这里有一句话很有意思,递归调用处理的是参数,使用function template,递归继承处理的是类型,使用的是class template。 373 | 374 | ![1561792840864](assets/1561792840864.png) 375 | 376 | 不过上述的代码编译时不通过的,因为`HEAD::type`这个原因(比如int::type是没有的)。 377 | 378 | ![1561792943974](assets/1561792943974.png) 379 | 380 | 然后修改成这样,使用decltype进行类型推导,得到返回类型。不过需要把数据移到上面取,太离谱了。 381 | 382 | ![1561793021661](assets/1561793021661.png) 383 | 384 | 最终发现直接返回Head就可以了,侯捷老师考虑太复杂了哈哈哈。 385 | 386 | ![1561796395862](assets/1561796395862.png) 387 | 388 | 之前的tuple是通过递归继承来实现的,上图展示了如何通过递归复合来实现tuple,原理与之前的类似,数据多了Composited类型的m_tail, 依次不断递归,直到最后复合到空的tuple。 389 | 390 | variadic template到此结束,真的很强大! 391 | 392 | ## 标准库层面 393 | 394 | ### 右值引用 395 | 396 | ![1562592085099](assets/1562592085099.png) 397 | 398 | 右值引用是为了解决不必要的拷贝以及使能完美转发而引入的新的引用类型。当右边的赋值类型是一个右值,左边的对象可以从右边的对象中偷取资源而不是重新分配拷贝,这个偷取的过程叫做移动语义。上述给出了事例,a+b和临时对象就是右值,右值只能出现在右边,左边则可以都出现,这里的complex类和string类是由C++作者写的,引入了不同的修改和赋值,没有遵守右值的定义,所以它们的事例没有报错。方便记忆,可以这里理解右值和左值,可以取地址,有名字的是左值,而不能取地址,没有名字的是右值。还有一种解释,右值由将亡值和纯右值组成,将亡值如a+b赋给a后就死掉,临时对象也是一样,纯右值指的是2,'a',true等等。 399 | 400 | ![1562593580624](assets/1562593580624.png) 401 | 402 | 右值出现,对其进行资源的搬移是合理的,所以引出了两点,第一点是要有语法告诉编译器这是右值,第二点是被调用段需要写出一个专门处理右值的搬移赋值(move assignment)函数。 403 | 404 | ![1562593805165](assets/1562593805165.png) 405 | 406 | 上述的是测试程序,在vector尾端插入Mystring的临时对象,调用的vector需要实现带有右值插入的版本,也就是箭头指向的版本——insert(..., &&x),insert函数则会调用MyString的拷贝构造函数,为了不进行拷贝,也需要写出一个右值引用类型的拷贝构造。noexcept是为了让编译器知道构造和析构不会抛出异常,当vector增长的时候,move构造函数才会调用起来。上图中还显示了关于copy和move的区别,可以看到copy中的数据是有两份的,其中一份是拷贝过来的,而move操作的数据是只有一份的,原来可能指向临时对象,现在指向搬移后的对象,原来的对象会设置为空指针。上图中有一个std:move函数很有帮助,它会将左值对象转成右值对象,代码中可以经常用到。 407 | 408 | ![1562636675292](assets/1562636675292.png) 409 | 410 | 上面的GCC2.9和GCC4.9版的insert函数,可以看到GCC4.9版引入move aware的insert函数。 411 | 412 | ![1562636782385](assets/1562636782385.png) 413 | 414 | 除了拷贝构造以外,还有拷贝赋值也需要写一个move版本的。 415 | 416 | ### perfect forwarding 417 | 418 | ![1562637551804](assets/1562637551804.png) 419 | 420 | 在看perfect forwarding之前,先看看unperfect forwarding,关注一下forward(2)的调用,2是纯右值,调用的是forward(int&& i)函数,但在forward(int&& i)函数里面使用i,i就会变为左值,这是我们不想看到的,左值意味着可能会有不必要的拷贝,所以有perfect forwarding. 421 | 422 | ![1562637273173](assets/1562637273173.png) 423 | 424 | perfect forwarding可以允许你写一个函数模板,有任意个参数,透明地转发给另一个函数,其中参数的本质(可修改性,const,左值,右值)都会在转发过程中保留下来,使用的是std::forward模板函数。 425 | 426 | ![1562637991925](assets/1562637991925.png) 427 | 428 | 可以看到forward内部使用了static_cast对传入的对象进行转型。move函数也一样。 429 | 430 | ### move aware class 431 | 432 | ![1562638720654](assets/1562638720654.png) 433 | 434 | ![1562638648850](assets/1562638648850.png) 435 | 436 | 上面两页是带有move aware的Mystring实现,灰色部分就是move版本的拷贝构造与拷贝赋值。可以看到直接就是浅拷贝,对指针和长度直接赋值,然后将原来对象的内部指针设置为空指针,内部长度为0。而不带有move的拷贝构造和拷贝赋值都调用了_init_data函数,内部调用的是memcpy函数进行拷贝。还有一点需要注意,由于有了move版本,析构函数需要进行少量修改,当指针为空时,不进行delete操作,此时没有指向对象了。 437 | 438 | ### move aware测试 439 | 440 | ![1562639513705](assets/1562639513705.png) 441 | 442 | 与之前的幻灯片相比,多了一个NoMove的参数,这是为了比较copy和move的性能差异。 443 | 444 | ![1562639709758](assets/1562639709758.png) 445 | 446 | 可以看到在vector容器中,使用copy和move版本的insert函数,差异很大,copy操作花费了更多的时间。而对于直接std::move和传统的拷贝构造,更是差异巨大。这里虽说只有三百万个元素,但由于vector有动态增长,所以构造函数调用次数会多于三百万次。 447 | 448 | ![1562640393636](assets/1562640393636.png) 449 | 450 | ![1562640412000](assets/1562640412000.png) 451 | 452 | ![1562640429861](assets/1562640429861.png) 453 | 454 | ![1562640443566](assets/1562640443566.png) 455 | 456 | 而对于其他容器而言,构造函数阶段差别不大,但move版本还是快一些,当然,std::move与传统的拷贝赋值还是差异巨大,毕竟一个是拷贝所有的值,一个只是拷贝指针。 457 | 458 | ### vector的拷贝构造与移动构造 459 | 460 | ![](assets/1562900253744.png)可以看到拷贝构造实际上是先分配要拷贝的对象的长度的内存,然后调用copy ctors一一复制.(注意看参数的箭头) 461 | 462 | ![1562901115372](assets/1562901115372.png) 463 | 464 | 而移动构造调用的是_M_swap_data函数,内部是指针的交换,c2现在成了c,c没有意义了,不能再使用。 465 | 466 | ### array 467 | 468 | ![1562901655107](assets/1562901655107.png) 469 | 470 | TR1版本的array,内部是一个数组,封装了一些接口,可以适配算法库。![1562901757867](assets/1562901757867.png) 471 | 472 | GCC4.9版本的array,接口一样,用到了面向对象的东西,代码更复杂。 473 | 474 | ### Unordered容器 475 | 476 | ![1562902274876](assets/1562902274876.png) 477 | 478 | 无序容器内部是通过哈希表实现的,哈希表的结构如上所示,key实际上是一个指针vector,vector的长度叫做buckets(分箱), 每个vector中的指针指向一个链表,不同环境的哈希表实现方式不一样,有的是双向链表,有的是单项链表。哈希表是利用键值进行取值,比如键值为6,则访问vector第7个指针所指向的链表,如果链表有多个元素,则按序查找。哈希表都有一个特性,当元素的个数大于buckets时,需要rehashing,将hash表的buckets进行增大,一般是两倍大左右的质数,然后重新分配。 479 | 480 | ![1562902864080](assets/1562902864080.png) 481 | 482 | C++11引入了4种无序容器,分别是unordered_set, unordered_multiset, unordered_map以及unordered_multimap。具体使用将在体系结构那一个大课上写。 483 | 484 | ### 哈希函数 485 | 486 | ![1562927561122](assets/1562927561122.png) 487 | 488 | 上面没说如何根据数据得到键值,这就要用到哈希函数了,以上GCC4.9版本计算每一个类型的哈希值事例,其中hash()是生成了一个临时对象,然后进行仿函数的调用。对于整型和字符型,返回的是本身,符合常规。而其他类型不相同,后面介绍。 489 | 490 | ![1562927748674](assets/1562927748674.png) 491 | 492 | GCC2.9要清楚许多,代码将每一个类型进行了特化,形成对应了哈希函数,上面的都是整型的。 493 | 494 | ![1562927905752](assets/1562927905752.png) 495 | 496 | 上面的是关于字符数组的哈希函数,再GCC2.9版本中,没有提供string的哈希函数,只有字符数组的。 497 | 498 | ![1562927991200](assets/1562927991200.png) 499 | 500 | 上面的是字符数组的使用事例,结合之前提到的哈希表的结构,可以很清楚的知道具体流程。 501 | 502 | ![1562928493241](assets/1562928493241.png) 503 | 504 | GCC2.9许多类型没有支持,GCC4.9则基本都支持了,思想还是对每种类型进行特化,上面的就是GCC4.9版本的哈希函数,后面几页都是接着这一页的。类__hash_base定义了两种类型,一种是返回结果(哈希函数的返回结果都是size_t), 另 一种是参数类型,用以给哈希函数进行继承。上图左边给出了指针的特化版本,实现是通过reinterpret_cast对指针进行了转型,这种转型的运行期定义的,C++还有另外三种转型——static_cast, dynamic_cast以及const_cast,一般转型使用的是static_cast, dynamic_cast用于继承转型,const_cast用于去除对象的只读性,更多细节请查看《more effective C++》的条目二。 505 | 506 | 对于整型,使用了宏定义简化相同代码,因为整型都一样,直接返回即可。 507 | 508 | ![1562929786826](assets/1562929786826.png) 509 | 510 | ![1562929808570](assets/1562929808570.png) 511 | 512 | ![1562929956782](assets/1562929956782.png) 513 | 514 | 对于上面的浮点数以及字符串来说,调用的是Hash_impl类的hash函数,该函数调用的是_Hash_bytes函数,然后_Hash_bytes函数只有声明,没有定义,侯捷老师认为是该函数是二进制码函数(编译好的二进制),所以无法在源代码中找到。 515 | 516 | ### 再探万用的hash函数 517 | 518 | 对于数据而言,最小组成单元无非都是整形,浮点型以及字符串,所以对于任意对象,是可以进行hash的,这一节就是要设计万用的hash函数。 519 | 520 | ![1562933363516](assets/1562933363516.png) 521 | 522 | 对于某一个类的哈希函数,可以有以上三种写法,第一种是自定义类,该类是重载()的仿函数,第二种是写成正儿八经的函数,不过在定义容器时,写的类型较复杂。第三种使用namespace,将类包在std中,相当于特化此类,这样定义的时候无需写哈希函数类型。 523 | 524 | ![1562933773025](assets/1562933773025.png) 525 | 526 | 为了支持不同个数的变量,使用了之前章节的variadic template,可以传入任意个数的参数的hash_val函数,具体流程已在variadic template一节讲述,可以往回看。 527 | 528 | ![1562933959634](assets/1562933959634.png) 529 | 530 | 上页幻灯片中计算seed表达式中的0x9e3779b9是一个特殊的值,黄金比例,为了让哈希函数生成的键足够乱而引入的。 531 | 532 | ### tuple实例 533 | 534 | ![1562939123324](assets/1562939123324.png) 535 | 536 | 这里主要示范了一下tuple的一些用法,不细说,看代码就能看懂,对于代码中为什么内存大小不是28的问题,侯捷老师不知道,我也没有查到,在window10-64位电脑的mingw64测试,结果是56,两倍,在vc2017中测试是64。不同编译器有区别,具体原因不太清楚。 537 | 538 | 右下角的是元编程的范例程序,通常编程是操作变量,元编程是操作类型。 539 | 540 | ### 旧版tuple 541 | 542 | ![1562940226064](assets/1562940226064.png) 543 | 544 | ![1562940245228](assets/1562940245228.png) 545 | 546 | 上面两张幻灯片是最早tuple的实现方式,由于没有variadic template,所以boost实现tuple使用多个类,最多可以容纳15个参数,最早的思想是来自与modern C++ design那本书,作者使用宏定义构建了类似variadic template的方式。 547 | 548 | 好了,关于侯捷老师的C++11/14课程的笔记到这里就结束了,后面会补充一些C++11其他的内容,这些内容是侯捷老师没有提到的。 549 | 550 | -------------------------------------------------------------------------------- /C++11_14/[vczh]C++11.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/[vczh]C++11.pptx -------------------------------------------------------------------------------- /C++11_14/assets/1561365339994.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561365339994.png -------------------------------------------------------------------------------- /C++11_14/assets/1561374764032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561374764032.png -------------------------------------------------------------------------------- /C++11_14/assets/1561378316251.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561378316251.png -------------------------------------------------------------------------------- /C++11_14/assets/1561379849604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561379849604.png -------------------------------------------------------------------------------- /C++11_14/assets/1561379925287.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561379925287.png -------------------------------------------------------------------------------- /C++11_14/assets/1561379992899.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561379992899.png -------------------------------------------------------------------------------- /C++11_14/assets/1561382069723.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561382069723.png -------------------------------------------------------------------------------- /C++11_14/assets/1561553789950.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561553789950.png -------------------------------------------------------------------------------- /C++11_14/assets/1561553965104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561553965104.png -------------------------------------------------------------------------------- /C++11_14/assets/1561554468601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561554468601.png -------------------------------------------------------------------------------- /C++11_14/assets/1561554777156.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561554777156.png -------------------------------------------------------------------------------- /C++11_14/assets/1561555267366.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561555267366.png -------------------------------------------------------------------------------- /C++11_14/assets/1561555475197.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561555475197.png -------------------------------------------------------------------------------- /C++11_14/assets/1561555693157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561555693157.png -------------------------------------------------------------------------------- /C++11_14/assets/1561556005409.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561556005409.png -------------------------------------------------------------------------------- /C++11_14/assets/1561606171002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561606171002.png -------------------------------------------------------------------------------- /C++11_14/assets/1561606408112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561606408112.png -------------------------------------------------------------------------------- /C++11_14/assets/1561606683917.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561606683917.png -------------------------------------------------------------------------------- /C++11_14/assets/1561615144711.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561615144711.png -------------------------------------------------------------------------------- /C++11_14/assets/1561615997563.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561615997563.png -------------------------------------------------------------------------------- /C++11_14/assets/1561616013503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561616013503.png -------------------------------------------------------------------------------- /C++11_14/assets/1561616128477.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561616128477.png -------------------------------------------------------------------------------- /C++11_14/assets/1561616221927.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561616221927.png -------------------------------------------------------------------------------- /C++11_14/assets/1561617356511.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561617356511.png -------------------------------------------------------------------------------- /C++11_14/assets/1561617793229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561617793229.png -------------------------------------------------------------------------------- /C++11_14/assets/1561618425129.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561618425129.png -------------------------------------------------------------------------------- /C++11_14/assets/1561619185785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561619185785.png -------------------------------------------------------------------------------- /C++11_14/assets/1561620986751.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561620986751.png -------------------------------------------------------------------------------- /C++11_14/assets/1561622745241.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561622745241.png -------------------------------------------------------------------------------- /C++11_14/assets/1561625490878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561625490878.png -------------------------------------------------------------------------------- /C++11_14/assets/1561626345740.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561626345740.png -------------------------------------------------------------------------------- /C++11_14/assets/1561627676584.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561627676584.png -------------------------------------------------------------------------------- /C++11_14/assets/1561628332035.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561628332035.png -------------------------------------------------------------------------------- /C++11_14/assets/1561634722050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561634722050.png -------------------------------------------------------------------------------- /C++11_14/assets/1561635119640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561635119640.png -------------------------------------------------------------------------------- /C++11_14/assets/1561635286581.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561635286581.png -------------------------------------------------------------------------------- /C++11_14/assets/1561635976912.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561635976912.png -------------------------------------------------------------------------------- /C++11_14/assets/1561636731726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561636731726.png -------------------------------------------------------------------------------- /C++11_14/assets/1561637095441.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561637095441.png -------------------------------------------------------------------------------- /C++11_14/assets/1561637526193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561637526193.png -------------------------------------------------------------------------------- /C++11_14/assets/1561637843402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561637843402.png -------------------------------------------------------------------------------- /C++11_14/assets/1561637895896.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561637895896.png -------------------------------------------------------------------------------- /C++11_14/assets/1561638544250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561638544250.png -------------------------------------------------------------------------------- /C++11_14/assets/1561638870294.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561638870294.png -------------------------------------------------------------------------------- /C++11_14/assets/1561639105366.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561639105366.png -------------------------------------------------------------------------------- /C++11_14/assets/1561691318808.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561691318808.png -------------------------------------------------------------------------------- /C++11_14/assets/1561691540157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561691540157.png -------------------------------------------------------------------------------- /C++11_14/assets/1561692080652.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561692080652.png -------------------------------------------------------------------------------- /C++11_14/assets/1561692308172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561692308172.png -------------------------------------------------------------------------------- /C++11_14/assets/1561692484391.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561692484391.png -------------------------------------------------------------------------------- /C++11_14/assets/1561706232308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561706232308.png -------------------------------------------------------------------------------- /C++11_14/assets/1561708905361.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561708905361.png -------------------------------------------------------------------------------- /C++11_14/assets/1561713015542.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561713015542.png -------------------------------------------------------------------------------- /C++11_14/assets/1561717430124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561717430124.png -------------------------------------------------------------------------------- /C++11_14/assets/1561723021811.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561723021811.png -------------------------------------------------------------------------------- /C++11_14/assets/1561725331121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561725331121.png -------------------------------------------------------------------------------- /C++11_14/assets/1561728160229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561728160229.png -------------------------------------------------------------------------------- /C++11_14/assets/1561728415477.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561728415477.png -------------------------------------------------------------------------------- /C++11_14/assets/1561787721037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561787721037.png -------------------------------------------------------------------------------- /C++11_14/assets/1561788622372.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561788622372.png -------------------------------------------------------------------------------- /C++11_14/assets/1561789478633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561789478633.png -------------------------------------------------------------------------------- /C++11_14/assets/1561792248957.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561792248957.png -------------------------------------------------------------------------------- /C++11_14/assets/1561792840864.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561792840864.png -------------------------------------------------------------------------------- /C++11_14/assets/1561792943974.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561792943974.png -------------------------------------------------------------------------------- /C++11_14/assets/1561793021661.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561793021661.png -------------------------------------------------------------------------------- /C++11_14/assets/1561796395862.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1561796395862.png -------------------------------------------------------------------------------- /C++11_14/assets/1562592085099.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562592085099.png -------------------------------------------------------------------------------- /C++11_14/assets/1562593580624.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562593580624.png -------------------------------------------------------------------------------- /C++11_14/assets/1562593805165.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562593805165.png -------------------------------------------------------------------------------- /C++11_14/assets/1562636675292.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562636675292.png -------------------------------------------------------------------------------- /C++11_14/assets/1562636782385.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562636782385.png -------------------------------------------------------------------------------- /C++11_14/assets/1562637273173.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562637273173.png -------------------------------------------------------------------------------- /C++11_14/assets/1562637551804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562637551804.png -------------------------------------------------------------------------------- /C++11_14/assets/1562637991925.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562637991925.png -------------------------------------------------------------------------------- /C++11_14/assets/1562638648850.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562638648850.png -------------------------------------------------------------------------------- /C++11_14/assets/1562638720654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562638720654.png -------------------------------------------------------------------------------- /C++11_14/assets/1562639513705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562639513705.png -------------------------------------------------------------------------------- /C++11_14/assets/1562639709758.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562639709758.png -------------------------------------------------------------------------------- /C++11_14/assets/1562640393636.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562640393636.png -------------------------------------------------------------------------------- /C++11_14/assets/1562640412000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562640412000.png -------------------------------------------------------------------------------- /C++11_14/assets/1562640429861.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562640429861.png -------------------------------------------------------------------------------- /C++11_14/assets/1562640443566.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562640443566.png -------------------------------------------------------------------------------- /C++11_14/assets/1562900253744.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562900253744.png -------------------------------------------------------------------------------- /C++11_14/assets/1562901115372.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562901115372.png -------------------------------------------------------------------------------- /C++11_14/assets/1562901655107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562901655107.png -------------------------------------------------------------------------------- /C++11_14/assets/1562901757867.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562901757867.png -------------------------------------------------------------------------------- /C++11_14/assets/1562902274876.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562902274876.png -------------------------------------------------------------------------------- /C++11_14/assets/1562902864080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562902864080.png -------------------------------------------------------------------------------- /C++11_14/assets/1562927561122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562927561122.png -------------------------------------------------------------------------------- /C++11_14/assets/1562927748674.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562927748674.png -------------------------------------------------------------------------------- /C++11_14/assets/1562927905752.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562927905752.png -------------------------------------------------------------------------------- /C++11_14/assets/1562927991200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562927991200.png -------------------------------------------------------------------------------- /C++11_14/assets/1562928493241.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562928493241.png -------------------------------------------------------------------------------- /C++11_14/assets/1562929786826.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562929786826.png -------------------------------------------------------------------------------- /C++11_14/assets/1562929808570.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562929808570.png -------------------------------------------------------------------------------- /C++11_14/assets/1562929956782.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562929956782.png -------------------------------------------------------------------------------- /C++11_14/assets/1562933363516.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562933363516.png -------------------------------------------------------------------------------- /C++11_14/assets/1562933773025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562933773025.png -------------------------------------------------------------------------------- /C++11_14/assets/1562933959634.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562933959634.png -------------------------------------------------------------------------------- /C++11_14/assets/1562939123324.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562939123324.png -------------------------------------------------------------------------------- /C++11_14/assets/1562940226064.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562940226064.png -------------------------------------------------------------------------------- /C++11_14/assets/1562940245228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11_14/assets/1562940245228.png -------------------------------------------------------------------------------- /C++11_14/code/test_variadic_template.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | 8 | void print() {}; 9 | 10 | template 11 | void print(const T &firstArg, const Types &... args) { 12 | cout << firstArg << endl; 13 | print(args...); 14 | } 15 | 16 | template 17 | void print(const Types &... args) 18 | { 19 | cout << "common print" << endl; 20 | } 21 | 22 | template 23 | void printf(const char *s, T val, Args... args) { 24 | while (*s) { 25 | if (*s == '%' && *(++s) != '%') { 26 | cout << val; 27 | printf(++s, args...); 28 | return; 29 | } 30 | cout << *(s++); 31 | } 32 | throw logic_error("extra arguments provided to printf"); 33 | } 34 | 35 | void printf(const char *s) { 36 | while (*s) { 37 | if (*s == '%' && *(++s) != '%') 38 | throw runtime_error("invalid format string: missing arguments"); 39 | cout << *(s++); 40 | } 41 | } 42 | 43 | template class tuple1; 44 | template <> class tuple1<> {}; 45 | 46 | template 47 | class tuple1 :private tuple1 48 | { 49 | typedef tuple1 inherited; 50 | public: 51 | tuple1() {} 52 | tuple1(Head v, Tails... tails) 53 | : m_head(v), inherited(tails...) {} 54 | 55 | Head head() {return m_head;} 56 | inherited& tail() {return *this;} 57 | protected: 58 | Head m_head; 59 | }; 60 | 61 | template class tuple2; 62 | template <> class tuple2<> {}; 63 | 64 | template 65 | class tuple2 66 | { 67 | typedef tuple2 composited; 68 | protected: 69 | Head m_head; 70 | composited m_tail; 71 | public: 72 | tuple2() {} 73 | tuple2(Head v, Tails... vtails) 74 | : m_tail(vtails...), m_head(v) {} 75 | 76 | Head head() {return m_head;} 77 | composited& tail() {return m_tail;} 78 | }; 79 | 80 | 81 | 82 | int main() { 83 | print(7.5, "hello", bitset<16>(377), 42); 84 | int* p = new int(); 85 | printf("%d %s %p %f", 15, "This is ace", p, 3.1415); 86 | tuple1 t1(41, 6.3, "nico"); 87 | cout << sizeof(t1) << endl; 88 | cout << t1.head() << endl; 89 | cout << t1.tail().head() << endl; 90 | cout << t1.tail().tail().head() << endl; 91 | tuple2 t2(41, 6.3, "nico"); 92 | cout << sizeof(t2) << endl; 93 | cout << t2.head() << endl; 94 | cout << t2.tail().head() << endl; 95 | cout << t2.tail().tail().head() << endl; 96 | return 0; 97 | } -------------------------------------------------------------------------------- /C++11并发与多线程/C++11内存模型.md: -------------------------------------------------------------------------------- 1 | # C++11内存模型 2 | 3 | 最近看了极客时间——《现代C++实战三十讲》中的内存模型与Atomic一节,感觉对C++的内存模型理解还不是很清楚,看了后面的参考文献以及看了一些好的博客,算是基本了解了,根据参考文献整合一下。更多细节可以看看参考文献。 4 | 5 | ## 内存模型解决的问题 6 | 7 | ### 并发的不确定性 8 | 9 | 多个线程操作共享的变量,由于操作不是原子性的,很有可能会导致结果未定义。 10 | 11 | ```c++ 12 | int64_t i = 0; // global variable 13 | Thread-1: Thread-2: 14 | i = 100; std::cout << i; 15 | ``` 16 | 17 | 对于上面的程序,线程1对一个int64类型的变量进行写操作,需要两个CPU指令,所以线程2可能会读到只执行1个CPU指令的中间状态,导致线程2输出未定义的变量。 18 | 19 | ### 乱序执行 20 | 21 | ```c++ 22 | Thread-1: Thread-2: 23 | x = 1; if (y == 2) { 24 | y = 2; x = 3; 25 | y = 4; 26 | } 27 | ``` 28 | 29 | 对于上面的程序,x和y最后的结果可能会是1和4,这是因为编译器会根据上下文调整代码的执行顺序,使其最有利于处理器的架构,运行得更快。线程1中有可能先执行y的赋值,然后再执行x的赋值,执行到y的赋值,切换到线程2运行结束,再切换至线程1,就会导致1和4的结果。 30 | 31 | 当然最著名的乱序执行还是属于单例模式的double-check了。 32 | 33 | ### 缓存一致性 34 | 35 | 指令乱序执行一节中的示例输出1和4其实还可能跟缓存一致性有关,现代处理器是多核的,每个核都有自己的缓存,对于y可能会先于x写入到内存当中,然后线程2执行结束,写入到内存,最后线程1的x再从缓存写入到内存。 36 | 37 | 更直观的是下面这个示例,线程1对x进行写操作,但可能还没来得及写入内存,线程2从内存中读入x打印,这也是缓存不一致所引起的。 38 | 39 | ```c++ 40 | int x = 0; // global variable 41 | 42 | Thread-1: Thread-2: 43 | x = 100; // A std::cout << x; // B 44 | ``` 45 | 46 | ## C++11的内存模型 47 | 48 | 从上面的示例看出,多线程不约束会出很多问题,这里的解决方案是std::atomic。 49 | 50 | C++11的内存模型共有6种,分四类。其中一致性的减弱会伴随着性能的增强。 51 | 52 | image-20200816233034724 53 | 54 | ### Sequential Consistency 55 | 56 | atomic默认的模型是顺序一致性的,这种模型对程序的执行结果有两个要求: 57 | 58 | - 每个处理器的执行顺序和代码中的顺序一样。 59 | - 所有处理器都只能看到一个单一的操作执行顺序。 60 | 61 | 这意味着将程序看做是一个简单的序列。如果对于一个原子变量的操作都是顺序一致的,那么多线程程序的行为就像是这些操作都以一种特定顺序被单线程程序执行。以单线程顺序执行的缺点就是效率低。 62 | 63 | ### Acquire-Release 64 | 65 | 原子操作有三类: 66 | 67 | - 读:在读取的过程中,读取位置的内容不会发生任何变动。 68 | 69 | - 写:在写入的过程中,其他执行线程不会看到部分写入的结果。 70 | 71 | - 读‐修改‐写:读取内存、修改数值、然后写回内存,整个操作的过程中间不会有其他写入操作插入,其他执行线程不会看到部分写入的结果。 72 | 73 | 还是上面的例子,这次把y改成atomic, Store(写操作)使用memory_order_release,条件判断的Load(读操作)使用memory_order_acquire。 74 | 75 | ``` 76 | Thread-1: Thread-2: 77 | x = 1; if (y.load(memory_order_acquire) == 2) { 78 | y.store(2, memory_order_release); x = 3; 79 | y.store(4, memory_order_relaxed); //先不管 80 | } 81 | ``` 82 | 83 | 通过这样做,就可以得到我们想要的结果了。用下图示意一下,每一边的代码都不允许重排越过黄色区域,且如果 y 上的释放早于 y 上的获取的话,释放前对内存的修改都在另一个线程的获取操作后可见: 84 | 85 | ![image-20200816235229162](assets/image-20200816235229162.png) 86 | 87 | 下面是获得和释放操作具体的作用: 88 | 89 | - memory_order_acquire:**获得操作**,在读取某原子对象时,当前线程的任何**后面的读写操作**都不允许重排到这个操作的**前面**去,并且其他线程在对同一个原子对象释放**之前的所有内存写入**都在**当前线程可见**。 90 | - memory_order_release:**释放操作**,在写入某原子对象时,当前线程的任何**前面的读写操作**都不允许重排到这个操作的**后面**去,并且当前线程的所有内存**写入**都在对同一个原子对象进行获取的**其他线程可见**。 91 | 92 | 还有一种读‐修改‐写操作,使用memory_order_acq_rel,含义如下: 93 | 94 | - memory_order_acq_rel:**获得释放操作**,一个**读‐修改‐写操作同时具有获得语义和释放语义**,即它**前后的任何读写操作都不允许重排**,并且其他线程在对同一个原子对象释放之前的所有内存写入都在当前线程可见,当前线程的所有内存写入都在对同一个原子对象进行获取的其他线程可见. 95 | 96 | ### Relaxed 97 | 98 | 在这种模型下,load()和store()都要带上memory_order_relaxed参数。Relaxed ordering 仅仅保证load()和store()是原子操作,除此之外,不提供任何跨线程的同步,乱序执行依然有。上面Acquire-Release的示例进入条件后,由于不再需要同步了,对循环内部进行重排序不会影响结果,性能还高。 99 | 100 | ### Release-Consume 101 | 102 | - Acquire-Release能保证不同线程之间的Synchronizes-With关系,这同时也约束到同一个线程中前后语句的执行顺序。 103 | - 而Release-Consume只约束有明确的carry-a-dependency关系的语句的执行顺序,同一个线程中的其他语句的执行先后顺序并不受这个内存模型的影响。 104 | 105 | 该模型目前不鼓励使用,有兴趣可以看下面的参考链接。 106 | 107 | 108 | 109 | ## 参考链接 110 | 111 | 【1】[C++11中的内存模型上篇 - 内存模型基础](https://www.codedump.info/post/20191214-cxx11-memory-model-1/#sequential-consistency-顺序一致性) 112 | 113 | 【2】[C++11中的内存模型下篇 - C++11支持的几种内存模型](https://www.codedump.info/post/20191214-cxx11-memory-model-2/#release-consume) 114 | 115 | 【3】[理解 C++ 的 Memory Order](http://senlinzhan.github.io/2017/12/04/cpp-memory-order/) 116 | 117 | 【4】[如何理解 C++11 的六种 memory order](https://www.zhihu.com/question/24301047) 118 | 119 | 【5】《现代C++实战三十讲》中的内存模型与Atomic -------------------------------------------------------------------------------- /C++11并发与多线程/README.md: -------------------------------------------------------------------------------- 1 | # C++并发与多线程 2 | 3 | 推荐直接看[C++并发实战第二版](https://chenxiaowei.gitbook.io/c-concurrency-in-action-second-edition-2019/1.0-chinese) -------------------------------------------------------------------------------- /C++11并发与多线程/assets/1565868441548.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/assets/1565868441548.png -------------------------------------------------------------------------------- /C++11并发与多线程/assets/image-20200816233034724.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/assets/image-20200816233034724.png -------------------------------------------------------------------------------- /C++11并发与多线程/assets/image-20200816234252090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/assets/image-20200816234252090.png -------------------------------------------------------------------------------- /C++11并发与多线程/assets/image-20200816235229162.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/assets/image-20200816235229162.png -------------------------------------------------------------------------------- /C++11并发与多线程/code/2_2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | void my_print() 7 | { 8 | cout << "线程开始启动" << endl; 9 | //... 10 | cout << "线程结束运行" << endl; 11 | } 12 | 13 | int main() 14 | { 15 | thread myobj(my_print); 16 | myobj.detach(); // 或者myobj.detach() 17 | if (myobj.joinable()) 18 | cout << "joinable() == true" << endl; 19 | else 20 | cout << "joinable() == false" << endl; 21 | cout << "i love china" << endl; 22 | cin.ignore(); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /C++11并发与多线程/code/2_3_1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/code/2_3_1.cpp -------------------------------------------------------------------------------- /C++11并发与多线程/code/2_3_2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class TA 7 | { 8 | private: 9 | int m_data; 10 | public: 11 | TA(int m) : m_data(m) { cout << "TA构造函数" << endl; } 12 | TA(TA& t) : m_data(t.m_data) { cout << "TA拷贝构造" << endl; } 13 | ~TA() { cout << "TA析构函数" << endl; } 14 | void operator()() 15 | { 16 | cout << "线程1开始启动" << endl; 17 | cout << "m_data: " << m_data << endl; 18 | cout << "线程1结束运行" << endl; 19 | } 20 | 21 | void my_print() 22 | { 23 | cout << "线程2开始启动" << endl; 24 | cout << "m_data: " << m_data << endl; 25 | cout << "线程2结束运行" << endl; 26 | } 27 | }; 28 | 29 | int main() 30 | { 31 | TA ta(6); 32 | cout << "ta 构造完成" << endl; 33 | thread myobj1(ta); 34 | myobj1.join(); 35 | thread myobj2(&TA::my_print, ta); 36 | myobj2.join(); 37 | cout << "i love china" << endl; 38 | return 0; 39 | } -------------------------------------------------------------------------------- /C++11并发与多线程/code/2_3_3.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/code/2_3_3.cpp -------------------------------------------------------------------------------- /C++11并发与多线程/code/2_3_4.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++11并发与多线程/code/2_3_4.cpp -------------------------------------------------------------------------------- /C++11并发与多线程/notes.md: -------------------------------------------------------------------------------- 1 | # C++并发与多线程笔记 2 | 3 | [TOC] 4 | 5 | ## 前言 6 | 7 | 以往计算机是单核处理器的,某一时刻只能执行一个任务,由操作系统调度,每秒钟进行多次所谓的“任务切换”,这是一种并发的假象,不是真正的并发,这种切换叫上下文切换,是有时间开销的,比如操作系统要保存切换时的各种状态、执行的进度等信息,都需要时间,一会儿切换回来的时候需要复原这些信息。 8 | 9 | 而当代计算机绝大多数都是多核处理器,能够真正的并行执行多个任务(硬件并发)。并发的目的是为了能够同时干多个事,提高性能,它指的是两个或者更多的任务(独立的活动)同时进行,一个程序同时执行多个任务;如下图就是双核CPU并发执行任务的过程。 10 | 11 | ![1565868441548](assets/1565868441548.png) 12 | 13 | ## 1. 进程、线程与并发 14 | 15 | **进程**:一个可执行程序运行起来,就创建了一个进程。Windows下双击一个可执行程序,Linux下,./文件名。可以通过在window任务管理器中查看进程的情况,而在Linux中则是通过`ps aux`命令。 16 | 17 | **线程**:用来执行代码的,理解成一条代码的执行道路。每个进程就是执行起来的可执行程序,都有一个主线程,这个主线程是唯一的,一个进程中只能有一个主线程。当你执行可执行程序,产生了一个进程后,这个主线程就随着这个进程默默的启动起来了。程序运行起来的时候,实际上是进程中的主线程执行这个main函数的代码;主线程和进程不可分割。除了主线程,可以通过代码创建其他线程。每创建一个新线程,就可以在同一时刻,可以多做一件事。 18 | 19 | **并发**:多进程并发和多线程并发。 20 | 21 | - 多进程并发:进程之间通信。同一个电脑,管道,文件,消息队列,共享内存等;不同电脑,socket通信技术。 22 | 23 | - 多线程并发:一个进程中的所有线程共享地址空间(共享内存),全局变量,指针,引用都可以在线程之间传递:使用多线程开销小于多进程。 24 | 25 | 和进程相比,线程有如下优点:线程启动速度更快,更轻量级;系统开销更小,执行速度跟块,比如共享内存这种通信方式比任何其他的通信方式更快。 26 | 27 | ## 2. 线程启动、结束,创建线程方法、join和detach 28 | 29 | ### 2.1 线程运行的开始和结束 30 | 31 | 主线程从main()开始执行,自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,就代表这这个线程结束。 32 | 33 | 整个进程时候执行完毕的标志是:主线程是否执行完毕,如果进程执行完毕了,就代表着整个进程执行完毕了; 34 | 35 | 一般情况下,如果其他子线程没有执行完毕,但是主线程执行完毕了,这些子线程也会被操作系统强行终止。如果要保持子线程的运行状态的话,那么就要让主线程一直运行。也有例外情况,使用detach(),主线程结束子线程继续运行。 36 | 37 | ### 2.2 join和detach 38 | 39 | 通过一个例子说明: 40 | 41 | ```c++ 42 | #include 43 | #include 44 | 45 | using namespace std; 46 | 47 | void my_print() 48 | { 49 | cout << "线程开始启动" << endl; 50 | //... 51 | cout << "线程结束运行" << endl; 52 | } 53 | 54 | int main() 55 | { 56 | thread myobj(my_print); 57 | myobj.join(); // 或者myobj.detach() 58 | cout << "i love china" << endl; 59 | cin.ignore(); 60 | return 0; 61 | } 62 | ``` 63 | 64 | 首先创建线程需要包括头文件thread,直接通过thread类创建一个线程对象,如thread myobj(my_print),线程的入口是my_print,创建同时开始运行。可以看到下面接着调用myobj的join方法,注释当中还显示着可以调用detach方法。 65 | 66 | **join**:汇合,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程继续执行。 67 | 68 | **detach**:分离,主线程和子线程分离,各自执行各自的。引入原因:创建很多子线程,让主线程逐个等待子线程结束,不太好,所以引入detach。一旦detach之后,与这个主线程关联的thread对象就会失去与这个主线程的关联。此时子线程就会在后台运行,当子线程执行完毕后,有运行时库负责清理相关的资源(守护线程)。detach使线程失去控制。一旦使用detach后,不能再用join了。 69 | 70 | 运行join的输出如下: 71 | 72 | ```bash 73 | 线程开始启动 74 | 线程结束运行 75 | i love china 76 | ``` 77 | 78 | 运行detach可能的输出如下(运行时可能会有不同): 79 | 80 | ```bash 81 | 线程开始启动 82 | i love china 83 | 线程结束运行 84 | ``` 85 | 86 | 可以看到join确实是等子线程运行完毕,主线程才开始运行。而detach则是主线程和子线程分开运行。 87 | 88 | **joinable**:判断当前进程是否可以进行join,可以返回true,否则返回false。 89 | 90 | ### 2.3 用类和lambda表达式创建线程 91 | 92 | 用类创建线程有两种方式:一种是重载括号 operator()(); 第二种则是调用成员函数 93 | 94 | 重载括号:thread myobj(对象名,参数); 95 | 调用成员函数: thread myobj(&类型:成员函数名,对象名,参数); 96 | 97 | 例子与上述例子类似: 98 | 99 | ```c++ 100 | #include 101 | #include 102 | 103 | using namespace std; 104 | 105 | class TA 106 | { 107 | public: 108 | void operator()() 109 | { 110 | cout << "线程1开始启动" << endl; 111 | //... 112 | cout << "线程1结束运行" << endl; 113 | } 114 | 115 | void my_print() 116 | { 117 | cout << "线程2开始启动" << endl; 118 | //... 119 | cout << "线程2结束运行" << endl; 120 | } 121 | }; 122 | 123 | int main() 124 | { 125 | TA ta; 126 | thread myobj1(ta); 127 | myobj1.join(); 128 | thread myobj2(&TA::my_print, ta); 129 | myobj2.join(); 130 | cout << "i love china" << endl; 131 | cin.ignore(); 132 | return 0; 133 | } 134 | ``` 135 | 136 | join运行结果如下: 137 | 138 | ```bash 139 | 线程1开始启动 140 | 线程1结束运行 141 | 线程2开始启动 142 | 线程2结束运行 143 | i love china 144 | ``` 145 | 146 | 上述中的ta是一个局部对象,有一点需要注意,在使用detach的时候,主线程先结束,ta就会被系统回收,那怎么能运行ta中的函数?实际上ta对象是被复制到线程中去的,所以主线程回收ta不会影响子线程。下面代码进行了测试。 147 | 148 | ```c++ 149 | #include 150 | #include 151 | 152 | using namespace std; 153 | 154 | class TA 155 | { 156 | private: 157 | int m_data; 158 | public: 159 | TA(int m) : m_data(m) { cout << "TA构造函数" << endl; } 160 | TA(TA& t) : m_data(t.m_data) { cout << "TA拷贝构造" << endl; } 161 | ~TA() { cout << "TA析构函数" << endl; } 162 | void operator()() 163 | { 164 | cout << "线程1开始启动" << endl; 165 | cout << "m_data: " << m_data << endl; 166 | cout << "线程1结束运行" << endl; 167 | } 168 | 169 | void my_print() 170 | { 171 | cout << "线程2开始启动" << endl; 172 | cout << "m_data: " << m_data << endl; 173 | cout << "线程2结束运行" << endl; 174 | } 175 | }; 176 | 177 | int main() 178 | { 179 | TA ta(6); 180 | cout << "ta 构造完成" << endl; 181 | thread myobj1(ta); 182 | myobj1.join(); 183 | thread myobj2(&TA::my_print, ta); 184 | myobj2.join(); 185 | cout << "i love china" << endl; 186 | return 0; 187 | } 188 | ``` 189 | 190 | 运行结果如下,可以看到线程先调用了拷贝构造,然后在线程结束时析构拷贝的TA。 191 | 192 | ```bash 193 | TA构造函数 //主函数TA构造 194 | ta 构造完成 195 | TA拷贝构造 //线程1开始 196 | 线程1开始启动 197 | m_data: 6 198 | 线程1结束运行 199 | TA析构函数 //线程1结束,释放拷贝的TA 200 | TA拷贝构造 //线程2开始 201 | 线程2开始启动 202 | m_data: 6 203 | 线程2结束运行 204 | TA析构函数 //线程1结束,释放拷贝的TA 205 | i love china 206 | TA析构函数 //主函数TA析构 207 | ``` 208 | 209 | 然后再看lambda表达式创建线程。 210 | 211 | ```c++ 212 | #include 213 | #include 214 | 215 | using namespace std; 216 | 217 | int main() 218 | { 219 | auto ta = [](){ cout << "线程开始启动" << endl; 220 | cout << "线程结束运行" << endl; }; 221 | thread myobj(ta); 222 | myobj.join(); 223 | cout << "i love china" << endl; 224 | cin.ignore(); 225 | return 0; 226 | } 227 | ``` 228 | 229 | ## 3. 线程传参详解,detach()大坑,成员函数做线程函数 230 | 231 | ### 3.1 传递临时对象作为线程参数 232 | 233 | ```c++ 234 | #include 235 | #include 236 | 237 | using namespace std; 238 | 239 | void my_print(const int& i, char* p) 240 | { 241 | cout << "线程开始启动" << endl; 242 | cout << "i: " << i << " i地址:" << &i << endl; 243 | cout << "p: " << p << " p地址:" << (int*)(p) << endl; 244 | cout << "线程结束运行" << endl; 245 | } 246 | 247 | int main() 248 | { 249 | int m = 6; 250 | char my_buf[] = "this is a test"; 251 | cout << "m的地址: " << &m << endl; 252 | cout << "my_buf的地址:" << &my_buf << endl; 253 | thread myobj(my_print, m, my_buf); 254 | myobj.join(); 255 | cout << "i love china" << endl; 256 | cin.ignore(); 257 | return 0; 258 | } 259 | ``` 260 | 261 | 上述代码的运行结果如下: 262 | 263 | ```bash 264 | m的地址: 0115FE4C 265 | my_buf的地址:0115FE34 266 | 线程开始启动 267 | i: 6 i地址:01220584 268 | p: this is a test p地址:0115FE34 269 | 线程结束运行 270 | i love china 271 | ``` 272 | 273 | 可见通过引用传递地址发生了改变,而指针则没有改变,上述是join模式,如果是detach模式,由于是两个线程分开进行,可见当my_buf被主线程回收后,那么子线程的my_buf就是一个不确定的数,这是很危险的。 274 | 275 | -------------------------------------------------------------------------------- /C++内存管理/assets/image-20191024214950918.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++内存管理/assets/image-20191024214950918.png -------------------------------------------------------------------------------- /C++内存管理/assets/image-20191026165236830.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++内存管理/assets/image-20191026165236830.png -------------------------------------------------------------------------------- /C++内存管理/assets/image-20191027112733778.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++内存管理/assets/image-20191027112733778.png -------------------------------------------------------------------------------- /C++内存管理/内存管理.md: -------------------------------------------------------------------------------- 1 | ## C++内存管理笔记 2 | 3 | ## 前言 4 | 5 | 內存是计算机中的“脑”吗?CPU才是脑,CPU才是计算机的三魂六魄。但若沒有內存,一切只存在于虚无缥缈间,等同 6 | 于不存在。C++有一个很大的优势是用户可以动态分配内存,并加以控制,以达到最优性能。对于一个C++程序员,对内存有深厚的了解将使你的C++功力更上一层楼。而这篇文章则是分享学习侯捷老师的内存管理课的笔记,在此分享,希望各位学习内存管理有所帮助。 7 | 8 | 测试环境:Clion2018.2 GNU C++14 9 | 10 | ## C++ Primitives 11 | 12 | 这一节介绍C++与内存相关的所有组件,包括malloc/free, new/delete, operator new/operator delete, placement new/placement delete,将探讨它们的意义,运作方式和重载方式,并以此开发一个极小的内存池。 13 | 14 | 对于一个应用程序而言,使用内存的途径有下面几种方式: 15 | 16 | image-20191024214950918 17 | 18 | 看图形知道越往底层调用越费劲,并且高层内部调用低层,这里不会讲述O.S.API,这是操作系统级的API了,太过底层,所讲述的最低只到malloc和free这一层级,对于学过C语言的朋友来说,malloc和free是C语言动态分配内存的两个函数,malloc主管内存分配,free主管内存释放。new和delete是C++语言中动态分配的语法,类属表达式,分配一个对象的内存,相同的还有new[ ]和delete[ ],这也是表达式,不过分配的是对象的数组。::operator new和::operator delete就是C++标准库的函数了,可以自己重载定制。allocator是C++标准库的分配器类,用于容器的参数,有默认分配器。下面的表格是它们的区别: 19 | 20 | | 分配 | 释放 | 类属 | 可否重载 | 21 | | ------------------------ | -------------------------- | --------- | ------------------------ | 22 | | malloc() | free() | C函数 | 否 | 23 | | new | delete | 表达式 | 否 | 24 | | ::operator new | ::operator delete | C++函数 | 可以重载 | 25 | | allocator::allocate() | allocator::deallocate() | C++标准库 | 可自由设计并搭配任何容器 | 26 | 27 | 现在来小试牛刀,使用上述的各种动态分配操作: 28 | 29 | ```c++ 30 | #include 31 | #include 32 | #include //std::allocator 33 | #ifdef __GNUC__ 34 | #include //欲使用 std::allocator 以外的 allocator, 就得自行 #include 35 | #endif 36 | 37 | using namespace std; 38 | 39 | namespace jj01 40 | { 41 | void test_primitives() 42 | { 43 | cout << "\ntest_primitives().......... \n"; 44 | 45 | void* p1 = malloc(512); //512 bytes 46 | free(p1); 47 | 48 | char* p1_c = (char*)malloc(512); //char*指向512字节 49 | free(p1_c); 50 | 51 | complex* p2 = new complex; //one object 52 | delete p2; 53 | 54 | complex* p2_array = new complex[10]; //10 objects 55 | delete [] p2_array; 56 | 57 | void* p3 = ::operator new(512); //512 bytes 58 | ::operator delete(p3); 59 | 60 | //以下两函数都是 non-static,定要通过 object 调用。以下分配 3 个 ints. 61 | int* p4 = allocator().allocate(3); 62 | allocator().deallocate(p4,3); 63 | 64 | #ifdef __GNUC__ //GNU C++ 65 | //以下两函数都是 non-static,定要通过 object 调用。以下分配 9 个 ints. 66 | int* p5 = __gnu_cxx::__pool_alloc().allocate(9); 67 | __gnu_cxx::__pool_alloc().deallocate(p5,9); 68 | #endif 69 | } 70 | } //namespace 71 | 72 | int main(int argc, char** argv) 73 | { 74 | jj01::test_primitives(); 75 | return 0; 76 | } 77 | ``` 78 | 79 | 现在来看看new和delete的底层逻辑,对于下面这一行代码: 80 | 81 | ```c++ 82 | complex* pc = new complex(1, 2); 83 | ``` 84 | 85 | 编译器会将它演绎成下面的形式: 86 | 87 | ```c++ 88 | complex* pc; 89 | try{ 90 | void* mem = operator new( sizeof(complex) ); //分配内存 91 | pc = static_cast*>(mem); // 指针转型 92 | pc->complex::complex(1, 2); //调用构造函数,注意,只有编译器才可以这样调用构造函数。不过老版本VC6支持。 93 | } 94 | catch( std::bad_alloc ) { 95 | //若分配内存失败,就不执行指针转型以及构造操作 96 | } 97 | ``` 98 | 99 | 对于new表达式来说,编译器会编译成上面三步:分配内存,指针转型,以及构造对象。对于上面的operator new函数,其VC98源代码如下: 100 | 101 | ```c++ 102 | void* operator new(size_t size, const std::nothrow_t&) _THROW0() //第二参数是为了不抛出异常 103 | { 104 | void* p; 105 | while ((p == malloc(size)) == NULL) 106 | { 107 | //buy more memory or return null pointer 108 | _TRY_BEGIN // typedef 109 | if (_callnewh(size) == 0) break; 110 | _CATCH(std::bad_alloc) return NULL; 111 | _CATCH_END // typedef 112 | } 113 | return p; 114 | } 115 | ``` 116 | 117 | 可以看到operator new的内部调用的是malloc分配内存,当分配成功,直接返回指向分配好内存的指针,如果失败,malloc函数返回空指针,进入循环体,这里有两种方式处理,一种是调用_callnewh函数,这个是调用new handler函数(后面会讲,可以自己设定),当内存分配失败,此时说明内存不足,可以在new handler函数中释放掉某些不需要的内存,然后再进入循环判断条件调用malloc,第二种是直接返回空指针。 118 | 119 | 然后再来看看delete表达式,承接上面的new代码,当我们写出: 120 | 121 | ```c++ 122 | delete pc; 123 | ``` 124 | 125 | 编译器会将它转为下面的程序: 126 | 127 | ```c++ 128 | pc->~complex(); //先析构 与上面通过指针调用构造函数不同,可以这样调用析构函数 129 | operator delete(pc); //然后释放内存 130 | ``` 131 | 132 | 对于一个complex对象而言,complex内部没有指针,也就是它的内部没有指向其他内存,析构函数什么都不会做,operator delete(pc) 则是释放pc所指向的内存,也就是动态分配的complex对象。 133 | 134 | 对于operator delete函数,它的VC98源码如下: 135 | 136 | ```c++ 137 | void _cdecl operator delete(void* p) _THROW0() 138 | { 139 | free(p); 140 | } 141 | ``` 142 | 143 | 可以看到operator delete内部调用就是free函数,直接释放内存。 144 | 145 | 通过上面的讲述,知道了malloc/free,new/delete以及operator new/operator delete的关系,在这里小结一下。new表达式内部会调用operator new,而operator new内部则会调用malloc,通过malloc分配内存,然后对指针转型,最后调用构造函数。而delete表达式则是先调用对象的析构函数,确保先将对象(如string)内部的动态分配内存在析构中释放,然后再通过operator delete函数,里面调用free函数释放对象的内存。 146 | 147 | 还剩下array new/array delete和placement new,现在来讲array new和array delete。 148 | 149 | 分下面两种情况: 150 | 151 | ```c++ 152 | //情况a 153 | complex* pca1 = new complex[3]; //动态分配3个complex的空间,调用三次构造函数 154 | delete [] pca1; // 调用三次析构函数和释放内存 155 | 156 | //情况b 157 | string* pca2 = new string[3]; //动态分配3个string(指针)的空间,调用三次构造函数 158 | //对三个string进行赋值... 159 | *pca2 = "fang yang"; 160 | *(pca2 + 1) = "love"; 161 | *(pca2 + 2) = "chun xiao"; 162 | delete [] pca2; //调用三次析构函数和释放内存 163 | ``` 164 | 165 | 分两种情况是因为它们的内存情况不同(有简化),如下所示: 166 | 167 | ![image-20191026165236830](assets/image-20191026165236830.png) 168 | 169 | 对于complex对象而言,析构函数可有可无,因为只需要让free(pca1)就可以达到释放内存的目的,而对于string对象来说,析构函数很重要,因为string内部的指针指向字符串动态分配的内存,需要在析构函数将这部分内存释放,然后再调用free(pca2)释放string内部指针的内存。 170 | 171 | > 对于free(pca1)为什么能够正确释放内存,有一个重要原因,那就是cookie,它的作用是记录内存块的起始位置和终止位置,所以就知道要释放多大的内存块。另外,cookie占用四个字节,上下两个占用8个字节,内存管理其中很重要的一个原因就是减小cookie占用量。可以想想,动态分配一个4个字节的对象,对象内存布局中cookie占用8个字节,并且由于内存对齐问题,分配内存需要是16字节的倍数,这样还需要填充4个字节,对象内存占用率才25%,如果是这样的一百万个对象,那太糟糕! 172 | 173 | 知道了上面的原理,现在来说说为什么array new和array delete需要搭配使用,不搭配使用就是报错呢? 174 | 175 | 其实上面已经给出了答案,那就是构造函数和析构函数的次数问题,看看下面的代码: 176 | 177 | ```c++ 178 | string* pca2 = new string[3]; 179 | ``` 180 | 181 | 编译器转换为: 182 | 183 | ```c++ 184 | string* pca2[3]; 185 | try{ 186 | void* mem = operator new[]( sizeof(string) * 3 ); //分配内存 187 | for (int i = 0; i < 3; i++){ //三次转型和构造函数 188 | pca2[i] = static_cast((mem + i)); // 指针转型 189 | pca2[i]->string::string(); 190 | } 191 | } 192 | catch( std::bad_alloc ) { 193 | //若分配内存失败,就不执行指针转型以及构造操作 194 | } 195 | ``` 196 | 197 | 同理,对于array delete: 198 | 199 | ```c++ 200 | delete [] pca2; 201 | ``` 202 | 203 | 编译器转换为: 204 | 205 | ```c++ 206 | for (int i = 3 - 1; i <= 0; --i){ //在GNU C++中,是从上往下构造,从下往上析构 207 | pca2[i]->~string(); 208 | } 209 | operator delete[](pca2); 210 | ``` 211 | 212 | 如果将`delete [] pca2`换成`delete pca2`,那么析构函数就只调用了一次(调用一次发生在string没有析构函数,具体原因见下一段话),剩下的两个没有调用,那么当operator delete释放掉三个指针的内存后,其中两个指针当初所指向的内存就悬空了,没有指针指向,发生内存泄漏。 213 | 214 | 另外,上面的图,complex对象没有3(内置类型也不会有),而string对象有3,这是因为complex没有重要的析构函数(需要释放内存),编译器不会记录3这个长度,而string则需要记录,`delete [] pca2`并没有带有3,这是因为当编译器看到`[]`,此时pca2没有指向string数组的首地址,而是回跳到3的地址(所以上面一段话中delete pca2其实会报错,多了一个3,内存布局变了),所以就知道析构多少次。 215 | 216 | 下面的是一个事例,帮助理解: 217 | 218 | ```c++ 219 | namespace jj02 220 | { 221 | class A 222 | { 223 | public: 224 | int id; 225 | A() : id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; } 226 | A(int i) : id(i) { cout << "ctor. this=" << this << " id=" << id << endl; } 227 | ~A() { cout << "dtor. this=" << this << " id=" << id << endl; } 228 | }; 229 | 230 | void test_call_ctor_directly() 231 | { 232 | int size = 3; 233 | A* buf = new A[size]; //动态分配调用default ctor 3 次. [0]先于[1]先于[2]) 234 | //A必须有 default ctor, 否则 [Error] no matching function for call to 'jj02::A::A()' 235 | A* tmp = buf; 236 | 237 | cout << "buf=" << buf << " tmp=" << tmp << endl; 238 | 239 | for(int i = 0; i < size; ++i) 240 | new (tmp++) A(i); //placement new, 3次 ctor 241 | 242 | cout << "buf=" << buf << " tmp=" << tmp << endl; 243 | 244 | delete [] buf; //dtor three times (次序逆反, [2]先于[1]先于[0]) 245 | } 246 | } //namespace 247 | 248 | int main(int argc, char** argv) 249 | { 250 | jj02::test_call_ctor_directly(); 251 | return 0; 252 | } 253 | ``` 254 | 255 | 程序输出如下: 256 | 257 | ```c++ 258 | default ctor. this=0xf91258 id=0 259 | default ctor. this=0xf9125c id=0 260 | default ctor. this=0xf91260 id=0 261 | buf=0xf91258 tmp=0xf91258 262 | ctor. this=0xf91258 id=0 263 | ctor. this=0xf9125c id=1 264 | ctor. this=0xf91260 id=2 265 | buf=0xf91258 tmp=0xf91264 266 | dtor. this=0xf91260 id=2 267 | dtor. this=0xf9125c id=1 268 | dtor. this=0xf91258 id=0 269 | ``` 270 | 271 | 上面的程序出现了placement new, 现在就进入placement new和placement delete。 272 | 273 | 如果operator new接受的参数除了一定会有的size_t,还有其他参数,这就是placement new。众多placement new版本特别有用的一个是接受一个指针让对象构建在该指针所分配的已分配内存当中,就像上面程序中的`new (tmp++) A(i)`, tmp是指向已分配内存的指针,A(i)则是在已分配内存当中执行构造函数,这也是最早的placement new,该版本也已纳入C++标准库。 274 | 275 | > 《Effective C++》第52条款指出写了placement new,也要写placement delete,placement delete用来处理placement new中分配内存成功,但构造失败的情况,此时如果不释放内存,就会发生内存泄漏,但侯捷老师在G2.9测试确实会调用,但新版本的G4.9却不会调用。 276 | 277 | 现在来看看上述的placement new到底做了什么,有以下程序: 278 | 279 | ```c++ 280 | char* buf = new char[sizeof(complex) * 3]; //分配三个 281 | complex* pc = new(buf) complex(1, 2); 282 | //... 283 | delete [] buf; 284 | ``` 285 | 286 | 对于`complex* pc = new(buf) complex(1, 2)`这句话,编译器会转换为下面代码: 287 | 288 | ```c++ 289 | complex* pc; 290 | void* mem = operator new( sizeof(complex), buf ); //直接返回buf指针 291 | pc = static_cast*> (mem); //指针转型 292 | pc->complex::complex(1, 2); //调用构造函数 293 | ``` 294 | 295 | 对于上面的operator new,就和之前的operator new不太一样了,它现在多了第二参数,它的代码如下: 296 | 297 | ```c++ 298 | void* operator new(size_t, void* loc) 299 | { 300 | return loc; 301 | } 302 | ``` 303 | 304 | 可以看到直接返回buf指针,啥都没做,所以placement new做的事有两件,一件转型,一件调用构造函数。 305 | 306 | 现在来梳理一下前面的,谈谈作用,如下图所示: 307 | 308 | image-20191027112733778 309 | 310 | 在应用程序中的内存分配表达式是不可变的,而且无法重载,这部分是不可以变的,编译器会将表达式解析为中间部分,调用operator new函数,placement new函数以及operator delete函数,这时候就有两条路了,一条是让编译器调用全局函数,另一条就是让编译器调用重载对象的动态分配成员函数,而内存管理就是要让编译器走重载的这条路,构造适合当前对象的内存池,减小内存消耗以及多次内存分配所带来的时间开销。 311 | 312 | 上面说的是重载对象的动态分配成员函数,那么是否可以在全局直接重载全局函数,这是可以的,但不推荐这么做,影响无比深远,所有的动态分配内存都将改变。 313 | 314 | 下面通过一个例子来看看如何重载,代码如下: 315 | 316 | ```c++ 317 | #include 318 | #include 319 | #include 320 | 321 | using namespace std; 322 | 323 | namespace jj06 324 | { 325 | 326 | class Foo 327 | { 328 | public: 329 | int _id; 330 | long _data; 331 | 332 | public: 333 | static void* operator new(size_t size); 334 | static void operator delete(void* deadObject, size_t size); 335 | static void* operator new[](size_t size); 336 | static void operator delete[](void* deadObject, size_t size); 337 | static void* operator new(size_t size, void* loc); //placement new 338 | 339 | Foo() : _id(0) { cout << "default ctor. this=" << this << " id=" << _id << endl; } 340 | Foo(int i) : _id(i) { cout << "ctor. this=" << this << " id=" << _id << endl; } 341 | ~Foo() { cout << "dtor. this=" << this << " id=" << _id << endl; } 342 | }; 343 | 344 | void* Foo::operator new(size_t size) 345 | { 346 | Foo* p = (Foo*)malloc(size); 347 | cout << "Foo::operator new(), size=" << size << "\t return: " << p << endl; 348 | return p; 349 | } 350 | 351 | void Foo::operator delete(void* pdead, size_t size) 352 | { 353 | cout << "Foo::operator delete(), pdead= " << pdead << " size= " << size << endl; 354 | free(pdead); 355 | } 356 | 357 | void* Foo::operator new[](size_t size) 358 | { 359 | Foo* p = (Foo*)malloc(size); 360 | cout << "Foo::operator new[](), size=" << size << "\t return: " << p << endl; 361 | return p; 362 | } 363 | 364 | void Foo::operator delete[](void* pdead, size_t size) 365 | { 366 | cout << "Foo::operator delete[](), pdead= " << pdead << " size= " << size << endl; 367 | free(pdead); 368 | } 369 | 370 | void* Foo::operator new(size_t size, void* loc) 371 | { 372 | cout << "Foo::placement new(), p= " << loc << " size= " << size << endl; 373 | return loc; 374 | } 375 | 376 | void test_overload_operator_new_and_array_new() 377 | { 378 | cout << "\ntest_overload_operator_new_and_array_new().......... \n"; 379 | 380 | cout << "sizeof(Foo)= " << sizeof(Foo) << endl; 381 | 382 | { 383 | Foo *p = new Foo(7); //Foo::operator new 384 | delete p; //Foo::operator delete 385 | 386 | Foo *pArray = new Foo[3]; //Foo::operator new[], 无法给elements 以 initializer 387 | delete[] pArray; //Foo::operator delete[] 388 | } 389 | 390 | { 391 | cout << "\ntest_overload_placement_new().......... \n"; 392 | Foo* p1 = new Foo; //Foo::operator new 393 | new(p1) Foo(7); //Foo replacement new 394 | delete p1; //Foo::operator delete 395 | } 396 | 397 | { 398 | cout << "\ntesting global expression ::new and ::new[] \n"; 399 | // 这会绕过 overloaded new(), delete(), new[](), delete[]() 400 | 401 | Foo* p = ::new Foo(7); 402 | ::delete p; 403 | 404 | Foo* pArray = ::new Foo[3]; 405 | ::delete [] pArray; 406 | } 407 | } 408 | } //namespace 409 | 410 | int main(int argc, char** argv) 411 | { 412 | jj06::test_overload_operator_new_and_array_new(); 413 | return 0; 414 | } 415 | ``` 416 | 417 | 运行结果如下: 418 | 419 | ```c++ 420 | test_overload_operator_new_and_array_new().......... 421 | sizeof(Foo)= 8 422 | Foo::operator new(), size=8 return: 0x1b1250 423 | ctor. this=0x1b1250 id=7 424 | dtor. this=0x1b1250 id=7 425 | Foo::operator delete(), pdead= 0x1b1250 size= 8 426 | Foo::operator new[](), size=32 return: 0x1b6fa0 427 | default ctor. this=0x1b6fa8 id=0 428 | default ctor. this=0x1b6fb0 id=0 429 | default ctor. this=0x1b6fb8 id=0 430 | dtor. this=0x1b6fb8 id=0 431 | dtor. this=0x1b6fb0 id=0 432 | dtor. this=0x1b6fa8 id=0 433 | Foo::operator delete[](), pdead= 0x1b6fa0 size= 32 434 | 435 | test_overload_placement_new().......... 436 | Foo::operator new(), size=8 return: 0x1b1250 437 | default ctor. this=0x1b1250 id=0 438 | Foo::replacement new(), p= 0x1b1250 size= 8 439 | ctor. this=0x1b1250 id=7 440 | dtor. this=0x1b1250 id=7 441 | Foo::operator delete(), pdead= 0x1b1250 size= 8 442 | 443 | testing global expression ::new and ::new[] 444 | ctor. this=0x1b1250 id=7 445 | dtor. this=0x1b1250 id=7 446 | default ctor. this=0x1b6fa8 id=0 447 | default ctor. this=0x1b6fb0 id=0 448 | default ctor. this=0x1b6fb8 id=0 449 | dtor. this=0x1b6fb8 id=0 450 | dtor. this=0x1b6fb0 id=0 451 | dtor. this=0x1b6fa8 id=0 452 | ``` 453 | 454 | placement new可以有很多个版本,前提是每个版本的声明都必须有独特的参数列,且其中第一参数必须是size_t, 其余参数以new所指定的placement arguments为初值,出现于new(.....)小括号内的便是placement arguments。就像上面的new(p1), p1为重载placement new的第二参数,需要注意的是,上面重载的是标准库placement new,并不是新的placement new。 455 | 456 | 下面的是一个placement new和placement delete的事例: 457 | 458 | ```c++ 459 | #include 460 | 461 | using namespace std; 462 | 463 | namespace jj07 464 | { 465 | class Foo 466 | { 467 | public: 468 | Foo() { cout << "Foo::Foo()" << endl; } 469 | Foo(int) { 470 | cout << "Foo::Foo(int)" << endl; 471 | } 472 | 473 | //(1) 这个就是一般的 operator new() 的重载 474 | void* operator new(size_t size) { 475 | cout << "operator new(size_t size), size= " << size << endl; 476 | return malloc(size); 477 | } 478 | 479 | //(2) 这个就是标准库已经提供的 placement new() 480 | void* operator new(size_t size, void* start) { 481 | cout << "operator new(size_t size, void* start), size= " << size << " start= " << start << endl; 482 | return start; 483 | } 484 | 485 | //(3) 这个才是崭新的 placement new 486 | void* operator new(size_t size, long extra) { 487 | cout << "operator new(size_t size, long extra) " << size << ' ' << extra << endl; 488 | return malloc(size+extra); 489 | } 490 | 491 | //(4) 这又是一个 placement new 492 | void* operator new(size_t size, long extra, char init) { 493 | cout << "operator new(size_t size, long extra, char init) " << size << ' ' << extra << ' ' << init << endl; 494 | return malloc(size+extra); 495 | } 496 | 497 | //以下是搭配上述 placement new 的各个placement delete. 498 | //当 ctor 发出异常,这儿对应的 placement delete 就会被唤起. 499 | //应该是要负责释放其搭档兄弟 (placement new) 分配所得的 memory. 500 | //但其实新版本并没有调用placement delete 501 | //(1) 这个就是一般的 operator delete() 的重载 502 | void operator delete(void*,size_t) 503 | { cout << "operator delete(void*,size_t) " << endl; } 504 | 505 | //(2) 这是对应上述的 (2) 506 | void operator delete(void*,void*) 507 | { cout << "operator delete(void*,void*) " << endl; } 508 | 509 | //(3) 这是对应上述的 (3) 510 | void operator delete(void*,long) 511 | { cout << "operator delete(void*,long) " << endl; } 512 | 513 | //(4) 这是对应上述的 (4) 514 | //如果没有一一对应, 也不会有任何编译报错 515 | void operator delete(void*,long,char) 516 | { cout << "operator delete(void*,long,char) " << endl; } 517 | 518 | private: 519 | int m_i; 520 | }; 521 | 522 | void test_overload_placement_new() 523 | { 524 | cout << "\ntest_overload_placement_new().......... \n"; 525 | 526 | Foo start; //Foo::Foo 527 | 528 | Foo* p1 = new Foo; //op-new(size_t) 529 | Foo* p2 = new (&start) Foo; //op-new(size_t,void*), 在栈内存构造对象 530 | Foo* p3 = new (100) Foo; //op-new(size_t,long) 531 | Foo* p4 = new (100,'a') Foo; //op-new(size_t,long,char) 532 | 533 | Foo* p5 = new (100) Foo(1); //op-new(size_t,long) op-del(void*,long) 534 | Foo* p6 = new (100,'a') Foo(1); 535 | Foo* p7 = new (&start) Foo(1); 536 | Foo* p8 = new Foo(1); 537 | } 538 | } 539 | 540 | int main(int argc, char** argv) 541 | { 542 | jj07::test_overload_placement_new(); 543 | return 0; 544 | } 545 | ``` 546 | 547 | 运行结果如下: 548 | 549 | ```bash 550 | test_overload_placement_new().......... 551 | Foo::Foo() 552 | operator new(size_t size), size= 4 553 | Foo::Foo() 554 | operator new(size_t size, void* start), size= 4 start= 0x61fd8c 555 | Foo::Foo() 556 | operator new(size_t size, long extra) 4 100 557 | Foo::Foo() 558 | operator new(size_t size, long extra, char init) 4 100 a 559 | Foo::Foo() 560 | operator new(size_t size, long extra) 4 100 561 | Foo::Foo(int) 562 | operator new(size_t size, long extra, char init) 4 100 a 563 | Foo::Foo(int) 564 | operator new(size_t size, void* start), size= 4 start= 0x61fd8c 565 | Foo::Foo(int) 566 | operator new(size_t size), size= 4 567 | Foo::Foo(int) 568 | ``` 569 | 570 | 下面讲述per-class allocator,也就是重载类的new和delete,构造内存池。 571 | 572 | ```c++ 573 | #include 574 | 575 | using namespace std; 576 | 577 | namespace jj04 578 | { 579 | //ref. C++Primer 3/e, p.765 580 | //per-class allocator 581 | class Screen { 582 | public: 583 | Screen(int x) : i(x) { }; 584 | int get() { return i; } 585 | void* operator new(size_t); 586 | void operator delete(void*, size_t); 587 | private: 588 | Screen* next; 589 | static Screen* freeStore; 590 | static const int screenChunk; 591 | private: 592 | int i; 593 | }; 594 | Screen* Screen::freeStore = 0; 595 | const int Screen::screenChunk = 24; 596 | 597 | void* Screen::operator new(size_t size) 598 | { 599 | Screen *p; 600 | if (!freeStore) { 601 | //linked list 是空的,所以攫取一大块 memory 602 | //以下呼叫的是 global operator new 603 | size_t chunk = screenChunk * size; 604 | freeStore = p = 605 | reinterpret_cast(new char[chunk]); 606 | //将分配得来的一大块 memory 当做 linked list 般小块小块串接起来 607 | for (; p != &freeStore[screenChunk-1]; ++p) 608 | p->next = p+1; 609 | p->next = 0; 610 | } 611 | p = freeStore; 612 | freeStore = freeStore->next; 613 | return p; 614 | } 615 | 616 | void Screen::operator delete(void *p, size_t) 617 | { 618 | //将 deleted object 收回插入 free list 前端 619 | (static_cast(p))->next = freeStore; 620 | freeStore = static_cast(p); 621 | } 622 | 623 | //------------- 624 | void test_per_class_allocator_1() 625 | { 626 | cout << "\ntest_per_class_allocator_1().......... \n"; 627 | 628 | cout << sizeof(Screen) << endl; //8 629 | 630 | size_t const N = 100; 631 | Screen* p[N]; 632 | 633 | for (int i=0; i< N; ++i) 634 | p[i] = new Screen(i); 635 | 636 | //输出前 10 个 pointers, 用以比较其间隔 637 | for (int i=0; i< 10; ++i) 638 | cout << p[i] << endl; 639 | 640 | for (int i=0; i< N; ++i) 641 | delete p[i]; 642 | } 643 | } //namespace 644 | 645 | int main(int argc, char** argv) 646 | { 647 | jj04::test_per_class_allocator_1(); 648 | return 0; 649 | } 650 | ``` 651 | 652 | 上面的类重载了new和delete,所以当使用new表达式动态分配Screen对象的时候,会调用重载的成员函数,如果freestore指针为空,说明没有多余内存给Screen使用,需要再分配一大块内存(24个Screen的大小),然后利用Screen类的next指针将内存串起来。串起来后,下一次new,freestore指针不为空,freestore所指向的位置就是对象构建的位置。依次这样,就可以达到一次分配,24次构造。 653 | 654 | 运行结果如下,可以看到对象的大小为16,而每个下面的内存间隔也是16,对象是连续的。 655 | 656 | ```bash 657 | test_per_class_allocator_1().......... 658 | 16 659 | 0xef1590 660 | 0xef15a0 661 | 0xef15b0 662 | 0xef15c0 663 | 0xef15d0 664 | 0xef15e0 665 | 0xef15f0 666 | 0xef1600 667 | 0xef1610 668 | 0xef1620 669 | ``` 670 | 671 | 下面的是不重载new和delete。 672 | 673 | ```c++ 674 | #include 675 | 676 | using namespace std; 677 | 678 | namespace jj04 679 | { 680 | class Screen { 681 | public: 682 | Screen(int x) : i(x) { }; 683 | int get() { return i; } 684 | private: 685 | Screen* next; 686 | private: 687 | int i; 688 | }; 689 | 690 | void test_per_class_allocator_1() 691 | { 692 | cout << "\ntest_per_class_allocator_1().......... \n"; 693 | 694 | cout << sizeof(Screen) << endl; //8 695 | 696 | size_t const N = 100; 697 | Screen* p[N]; 698 | 699 | for (int i=0; i< N; ++i) 700 | p[i] = new Screen(i); 701 | 702 | //输出前 10 个 pointers, 用以比较其间隔 703 | for (int i=0; i< 10; ++i) 704 | cout << p[i] << endl; 705 | 706 | for (int i=0; i< N; ++i) 707 | delete p[i]; 708 | } 709 | } //namespace 710 | 711 | int main(int argc, char** argv) 712 | { 713 | jj04::test_per_class_allocator_1(); 714 | return 0; 715 | } 716 | ``` 717 | 718 | 运行结果如下,没有重载new和delete,调用的就是全局的new和delete,可以看到有些对象的内存是不连续的,例如第一个和第二个,后面几个的内存是连续的,但可以看到,间隔不是16,而是32,这是因为cookie导致的。 719 | 720 | ```bash 721 | test_per_class_allocator_1().......... 722 | 16 723 | 0xe61250 724 | 0xe66fa0 725 | 0xe61590 726 | 0xe615b0 727 | 0xe615d0 728 | 0xe615f0 729 | 0xe61610 730 | 0xe61630 731 | 0xe61650 732 | 0xe61670 733 | ``` 734 | 735 | 736 | 737 | 738 | 739 | -------------------------------------------------------------------------------- /C++内存管理/内存管理.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++内存管理/内存管理.pptx -------------------------------------------------------------------------------- /C++小知识/copy-and-swap idiom.md: -------------------------------------------------------------------------------- 1 | This answer is from https://stackoverflow.com/a/3279550/10133369 2 | 3 | ## Overview 4 | 5 | ### Why do we need the copy-and-swap idiom? 6 | 7 | Any class that manages a resource (a *wrapper*, like a smart pointer) needs to implement [The Big Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). While the goals and implementation of the copy-constructor and destructor are straightforward, the copy-assignment operator is arguably the most nuanced and difficult. How should it be done? What pitfalls need to be avoided? 8 | 9 | The *copy-and-swap idiom* is the solution, and elegantly assists the assignment operator in achieving two things: avoiding [code duplication](http://en.wikipedia.org/wiki/Don't_repeat_yourself), and providing a [strong exception guarantee](http://en.wikipedia.org/wiki/Exception_guarantees). 10 | 11 | ### How does it work? 12 | 13 | [Conceptually](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom/3279616#3279616), it works by using the copy-constructor's functionality to create a local copy of the data, then takes the copied data with a `swap` function, swapping the old data with the new data. The temporary copy then destructs, taking the old data with it. We are left with a copy of the new data. 14 | 15 | In order to use the copy-and-swap idiom, we need three things: a working copy-constructor, a working destructor (both are the basis of any wrapper, so should be complete anyway), and a `swap` function. 16 | 17 | A swap function is a *non-throwing* function that swaps two objects of a class, member for member. We might be tempted to use `std::swap` instead of providing our own, but this would be impossible; `std::swap` uses the copy-constructor and copy-assignment operator within its implementation, and we'd ultimately be trying to define the assignment operator in terms of itself! 18 | 19 | (Not only that, but unqualified calls to `swap` will use our custom swap operator, skipping over the unnecessary construction and destruction of our class that `std::swap` would entail.) 20 | 21 | ------ 22 | 23 | ## An in-depth explanation 24 | 25 | ### The goal 26 | 27 | Let's consider a concrete case. We want to manage, in an otherwise useless class, a dynamic array. We start with a working constructor, copy-constructor, and destructor: 28 | 29 | ```cpp 30 | #include // std::copy 31 | #include // std::size_t 32 | 33 | class dumb_array 34 | { 35 | public: 36 | // (default) constructor 37 | dumb_array(std::size_t size = 0) 38 | : mSize(size), 39 | mArray(mSize ? new int[mSize]() : nullptr) 40 | { 41 | } 42 | 43 | // copy-constructor 44 | dumb_array(const dumb_array& other) 45 | : mSize(other.mSize), 46 | mArray(mSize ? new int[mSize] : nullptr), 47 | { 48 | // note that this is non-throwing, because of the data 49 | // types being used; more attention to detail with regards 50 | // to exceptions must be given in a more general case, however 51 | std::copy(other.mArray, other.mArray + mSize, mArray); 52 | } 53 | 54 | // destructor 55 | ~dumb_array() 56 | { 57 | delete [] mArray; 58 | } 59 | 60 | private: 61 | std::size_t mSize; 62 | int* mArray; 63 | }; 64 | ``` 65 | 66 | This class almost manages the array successfully, but it needs `operator=` to work correctly. 67 | 68 | ### A failed solution 69 | 70 | Here's how a naive implementation might look: 71 | 72 | ```cpp 73 | // the hard part 74 | dumb_array& operator=(const dumb_array& other) 75 | { 76 | if (this != &other) // (1) 77 | { 78 | // get rid of the old data... 79 | delete [] mArray; // (2) 80 | mArray = nullptr; // (2) *(see footnote for rationale) 81 | 82 | // ...and put in the new 83 | mSize = other.mSize; // (3) 84 | mArray = mSize ? new int[mSize] : nullptr; // (3) 85 | std::copy(other.mArray, other.mArray + mSize, mArray); // (3) 86 | } 87 | 88 | return *this; 89 | } 90 | ``` 91 | 92 | And we say we're finished; this now manages an array, without leaks. However, it suffers from three problems, marked sequentially in the code as `(n)`. 93 | 94 | 1. The first is the self-assignment test. This check serves two purposes: it's an easy way to prevent us from running needless code on self-assignment, and it protects us from subtle bugs (such as deleting the array only to try and copy it). But in all other cases it merely serves to slow the program down, and act as noise in the code; self-assignment rarely occurs, so most of the time this check is a waste. It would be better if the operator could work properly without it. 95 | 96 | 2. The second is that it only provides a basic exception guarantee. If `new int[mSize]` fails, `*this` will have been modified. (Namely, the size is wrong and the data is gone!) For a strong exception guarantee, it would need to be something akin to: 97 | 98 | ```cpp 99 | dumb_array& operator=(const dumb_array& other) 100 | { 101 | if (this != &other) // (1) 102 | { 103 | // get the new data ready before we replace the old 104 | std::size_t newSize = other.mSize; 105 | int* newArray = newSize ? new int[newSize]() : nullptr; // (3) 106 | std::copy(other.mArray, other.mArray + newSize, newArray); // (3) 107 | 108 | // replace the old data (all are non-throwing) 109 | delete [] mArray; 110 | mSize = newSize; 111 | mArray = newArray; 112 | } 113 | 114 | return *this; 115 | } 116 | ``` 117 | 118 | 3. The code has expanded! Which leads us to the third problem: code duplication. Our assignment operator effectively duplicates all the code we've already written elsewhere, and that's a terrible thing. 119 | 120 | In our case, the core of it is only two lines (the allocation and the copy), but with more complex resources this code bloat can be quite a hassle. We should strive to never repeat ourselves. 121 | 122 | (One might wonder: if this much code is needed to manage one resource correctly, what if my class manages more than one? While this may seem to be a valid concern, and indeed it requires non-trivial `try`/`catch` clauses, this is a non-issue. That's because a class should manage [*one resource only*](http://en.wikipedia.org/wiki/Single_responsibility_principle)!) 123 | 124 | ### A successful solution 125 | 126 | As mentioned, the copy-and-swap idiom will fix all these issues. But right now, we have all the requirements except one: a `swap` function. While The Rule of Three successfully entails the existence of our copy-constructor, assignment operator, and destructor, it should really be called "The Big Three and A Half": any time your class manages a resource it also makes sense to provide a `swap` function. 127 | 128 | We need to add swap functionality to our class, and we do that as follows†: 129 | 130 | ```cpp 131 | class dumb_array 132 | { 133 | public: 134 | // ... 135 | 136 | friend void swap(dumb_array& first, dumb_array& second) // nothrow 137 | { 138 | // enable ADL (not necessary in our case, but good practice) 139 | using std::swap; 140 | 141 | // by swapping the members of two objects, 142 | // the two objects are effectively swapped 143 | swap(first.mSize, second.mSize); 144 | swap(first.mArray, second.mArray); 145 | } 146 | 147 | // ... 148 | }; 149 | ``` 150 | 151 | ([Here](https://stackoverflow.com/questions/5695548/public-friend-swap-member-function) is the explanation why `public friend swap`.) Now not only can we swap our `dumb_array`'s, but swaps in general can be more efficient; it merely swaps pointers and sizes, rather than allocating and copying entire arrays. Aside from this bonus in functionality and efficiency, we are now ready to implement the copy-and-swap idiom. 152 | 153 | Without further ado, our assignment operator is: 154 | 155 | ```cpp 156 | dumb_array& operator=(dumb_array other) // (1) 157 | { 158 | swap(*this, other); // (2) 159 | 160 | return *this; 161 | } 162 | ``` 163 | 164 | And that's it! With one fell swoop, all three problems are elegantly tackled at once. 165 | 166 | ### Why does it work? 167 | 168 | We first notice an important choice: the parameter argument is taken *by-value*. While one could just as easily do the following (and indeed, many naive implementations of the idiom do): 169 | 170 | ```cpp 171 | dumb_array& operator=(const dumb_array& other) 172 | { 173 | dumb_array temp(other); 174 | swap(*this, temp); 175 | 176 | return *this; 177 | } 178 | ``` 179 | 180 | We lose an [important optimization opportunity](https://web.archive.org/web/20140113221447/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/). Not only that, but this choice is critical in C++11, which is discussed later. (On a general note, a remarkably useful guideline is as follows: if you're going to make a copy of something in a function, let the compiler do it in the parameter list.‡) 181 | 182 | Either way, this method of obtaining our resource is the key to eliminating code duplication: we get to use the code from the copy-constructor to make the copy, and never need to repeat any bit of it. Now that the copy is made, we are ready to swap. 183 | 184 | Observe that upon entering the function that all the new data is already allocated, copied, and ready to be used. This is what gives us a strong exception guarantee for free: we won't even enter the function if construction of the copy fails, and it's therefore not possible to alter the state of `*this`. (What we did manually before for a strong exception guarantee, the compiler is doing for us now; how kind.) 185 | 186 | At this point we are home-free, because `swap` is non-throwing. We swap our current data with the copied data, safely altering our state, and the old data gets put into the temporary. The old data is then released when the function returns. (Where upon the parameter's scope ends and its destructor is called.) 187 | 188 | Because the idiom repeats no code, we cannot introduce bugs within the operator. Note that this means we are rid of the need for a self-assignment check, allowing a single uniform implementation of `operator=`. (Additionally, we no longer have a performance penalty on non-self-assignments.) 189 | 190 | And that is the copy-and-swap idiom. 191 | 192 | ## What about C++11? 193 | 194 | The next version of C++, C++11, makes one very important change to how we manage resources: the Rule of Three is now **The Rule of Four** (and a half). Why? Because not only do we need to be able to copy-construct our resource, [we need to move-construct it as well](https://stackoverflow.com/questions/3106110/can-someone-please-explain-move-semantics-to-me). 195 | 196 | Luckily for us, this is easy: 197 | 198 | ```cpp 199 | class dumb_array 200 | { 201 | public: 202 | // ... 203 | 204 | // move constructor 205 | dumb_array(dumb_array&& other) 206 | : dumb_array() // initialize via default constructor, C++11 only 207 | { 208 | swap(*this, other); 209 | } 210 | 211 | // ... 212 | }; 213 | ``` 214 | 215 | What's going on here? Recall the goal of move-construction: to take the resources from another instance of the class, leaving it in a state guaranteed to be assignable and destructible. 216 | 217 | So what we've done is simple: initialize via the default constructor (a C++11 feature), then swap with `other`; we know a default constructed instance of our class can safely be assigned and destructed, so we know `other` will be able to do the same, after swapping. 218 | 219 | (Note that some compilers do not support constructor delegation; in this case, we have to manually default construct the class. This is an unfortunate but luckily trivial task.) 220 | 221 | ### Why does that work? 222 | 223 | That is the only change we need to make to our class, so why does it work? Remember the ever-important decision we made to make the parameter a value and not a reference: 224 | 225 | ```cpp 226 | dumb_array& operator=(dumb_array other); // (1) 227 | ``` 228 | 229 | Now, if `other` is being initialized with an rvalue, *it will be move-constructed*. Perfect. In the same way C++03 let us re-use our copy-constructor functionality by taking the argument by-value, C++11 will *automatically* pick the move-constructor when appropriate as well. (And, of course, as mentioned in previously linked article, the copying/moving of the value may simply be elided altogether.) 230 | 231 | And so concludes the copy-and-swap idiom. 232 | 233 | ------ 234 | 235 | ### Footnotes 236 | 237 | *Why do we set `mArray` to null? Because if any further code in the operator throws, the destructor of `dumb_array` might be called; and if that happens without setting it to null, we attempt to delete memory that's already been deleted! We avoid this by setting it to null, as deleting null is a no-operation. 238 | 239 | †There are other claims that we should specialize `std::swap` for our type, provide an in-class `swap` along-side a free-function `swap`, etc. But this is all unnecessary: any proper use of `swap` will be through an unqualified call, and our function will be found through [ADL](http://en.wikipedia.org/wiki/Argument-dependent_name_lookup). One function will do. 240 | 241 | ‡The reason is simple: once you have the resource to yourself, you may swap and/or move it (C++11) anywhere it needs to be. And by making the copy in the parameter list, you maximize optimization. -------------------------------------------------------------------------------- /C++小知识/print/assets/1567580434140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++小知识/print/assets/1567580434140.png -------------------------------------------------------------------------------- /C++小知识/print/assets/1567581621695.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++小知识/print/assets/1567581621695.png -------------------------------------------------------------------------------- /C++小知识/print/print.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | // version1 10 | void print1() {}; 11 | 12 | template 13 | void print1(const T& firstArg, const Types&... args) 14 | { 15 | cout << firstArg << endl; 16 | print1(args...); 17 | } 18 | 19 | // version2 20 | template < typename T , typename ... Types> 21 | void print2 (const T& firstArg , const Types&... args) 22 | { 23 | cout << firstArg << endl; 24 | if constexpr ( sizeof ...(args) > 0) print2 (args...) ; 25 | } 26 | 27 | // version3 28 | template < typename T , typename ... Types> 29 | void print3 (const T& firstArg , const Types&... args) 30 | { 31 | cout << firstArg << endl ; 32 | initializer_list { 33 | ( [&args] {cout << args << endl;}(), firstArg)...}; 34 | } 35 | 36 | // version4 37 | template 38 | void print4 (const Types&... args) 39 | { 40 | initializer_list { ([&args] {cout << args << endl;}(), 0)...}; 41 | } 42 | 43 | template < typename T , typename ... Types> 44 | void print3_test1 (const T& firstArg , const Types&... args) 45 | { 46 | cout << firstArg << endl ; 47 | auto i_l = initializer_list { 48 | ( [&args] {cout << args << endl;}(), firstArg)...}; 49 | for (auto i : i_l) 50 | cout << i << endl; 51 | } 52 | 53 | template < typename T , typename ... Types> 54 | void print3_test2 (const T& firstArg , const Types&... args) 55 | { 56 | cout << firstArg << endl ; 57 | auto i_l = initializer_list { 58 | ( [&args] {cout << args << endl;}(), [&args] {cout << args << endl;}(), firstArg)...}; 59 | for (auto i : i_l) 60 | cout << i << endl; 61 | } 62 | 63 | template 64 | ostream& operator << (ostream& os, const vector& vec){ 65 | os << "{ "; 66 | for (auto& v : vec) 67 | os << v << ' '; 68 | os << "}"; 69 | return os; 70 | } 71 | 72 | template class mytuple1; 73 | template <> class mytuple1<> {}; 74 | 75 | template 76 | class mytuple1 : private mytuple1 77 | { 78 | using inherited = mytuple1; 79 | public: 80 | mytuple1() {} 81 | mytuple1(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {} 82 | Head head() {return m_head;} 83 | inherited& tail() {return *this;} 84 | protected: 85 | Head m_head; 86 | }; 87 | 88 | template class mytuple2; 89 | template <> class mytuple2<> {}; 90 | 91 | template 92 | class mytuple2 93 | { 94 | using composited = mytuple2; 95 | public: 96 | mytuple2() {} 97 | mytuple2(Head v, Tail... vtail) : m_head(v), m_tail(vtail...) {} 98 | Head head() {return m_head;} 99 | composited& tail() {return m_tail;} 100 | protected: 101 | Head m_head; 102 | composited m_tail; 103 | }; 104 | 105 | template 106 | struct PRINT_TUPLE { 107 | static void print (ostream& os, const tuple& t){ 108 | os << get(t) << (IDX + 1 == MAX ? "": ","); 109 | PRINT_TUPLE::print(os, t); 110 | } 111 | }; 112 | 113 | template 114 | struct PRINT_TUPLE { 115 | static void print (ostream& os, const tuple& t){ 116 | 117 | } 118 | }; 119 | 120 | template 121 | ostream& operator << (ostream& os, const tuple& t) { 122 | os << "["; 123 | PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t); 124 | return os << "]"; 125 | } 126 | 127 | int main() 128 | { 129 | print1(7.5, "hello", bitset<16>(377), 42); 130 | print2(7.5, "hello", bitset<16>(377), 42); 131 | print3(7.5, "hello", bitset<16>(377), 42); 132 | print4(7.5, "hello", bitset<16>(377), 42); 133 | print1(vector {1, 2, 3, 4}); 134 | print2(vector {1, 2, 3, 4}); 135 | print3(vector {1, 2, 3, 4}); 136 | print4(vector {1, 2, 3, 4}); 137 | mytuple1 t1(41, 6.3, "nico"); 138 | print1(t1.head(), t1.tail().head(), t1.tail().tail().head()); 139 | mytuple2 t2(41, 6.3, "nico"); 140 | print1(t2.head(), t2.tail().head(), t2.tail().tail().head()); 141 | cout << make_tuple(41, 6.3, "nico"); 142 | return 0; 143 | } -------------------------------------------------------------------------------- /C++小知识/print/print.md: -------------------------------------------------------------------------------- 1 | # 使用现代C++实现多种print 2 | 3 | [TOC] 4 | 5 | 学习C++的朋友会遇到这样的问题,有char,int,double等对象,我们想把它们打印出来看看,初学者会通过cout或者传统C语言的printf函数来打印这些对象。 6 | 7 | 例如: 8 | 9 | ```c++ 10 | int i = 1; 11 | char c = 'f'; 12 | double d = 3.14; 13 | 14 | //cout 15 | cout << i << endl; 16 | cout << c << endl; 17 | cout << d << endl; 18 | 19 | //printf 20 | printf("%d\n%c\n%f", i, c, d); 21 | ``` 22 | 23 | 传统C中的printf 函数,虽然也能达成不定个数的形参的调用,但其并非类别安全,写输出格式也不方便,而且支持的是基础类型。使用cout缺点在于代码量会比较多,不好看。所以,能不能很简单地打印出每一个元素呢? 24 | 25 | ## Print Version1 26 | 27 | 幸运的是,有更好的解决方案,那就是使用C++11引入的variadic template,先来看看第一个版本的print,并介绍variadic template,代码如下: 28 | 29 | ``` 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | // version1 36 | void print1() {}; 37 | 38 | template 39 | void print1(const T& firstArg, const Types&... args) 40 | { 41 | cout << firstArg << endl; 42 | print1(args...); 43 | } 44 | 45 | int main() 46 | { 47 | print1(7.5, "hello", bitset<16>(377), 42); 48 | return 0; 49 | } 50 | ``` 51 | 52 | 运行结果: 53 | 54 | ```c++ 55 | 7.5 56 | hello 57 | 0000000101111001 58 | 42 59 | ``` 60 | 61 | Variadic Template: 是指数量不定,类型不定的模板,如上所示的print函数,可以看到接受了不同类型的参数,调用的函数就是拥有Variadic Template的函数,`print(7.5, "hello", bitset<16>(377), 42)`运行的时候,首先会7.5作为firstArg,剩余部分就是一包,然后在函数内部,继续递归调用print函数,然后把"hello"作为firstArg, 其余的作为一包,一直递归直到一包中没有数据,调用边界条件的print(空函数)结束。 62 | 63 | 函数的`...`表示一个包,可以看到,用在三个地方, 64 | 65 | - 第一个地方是模板参数`typename...` ,这代表模板参数包。 66 | 67 | - 第二个就是函数参数类型包(`Type&...`), 指代函数参数类型包。 68 | 69 | - 第三个就是函数参数包`args...`,指的是函数参数包。 70 | 71 | 另外,还可以使用`sizeof...(args)`得到包的长度。 72 | 73 | 总的来说,上面是一种递归解决方案,依次展开参数包,然后进行操作。 74 | 75 | ## Print Version2 76 | 77 | 你可能觉得上述边界条件的print函数有些多余,比version1更简洁的就是下面的version2: 78 | 79 | ```c++ 80 | template < typename T , typename ... Types> 81 | void print2 (const T& firstArg , const Types&... args) 82 | { 83 | cout << firstArg << endl; 84 | if constexpr ( sizeof ...(args) > 0) print2 (args...) ; 85 | } 86 | ``` 87 | 88 | 上述函数能够实现version1一样的功能,通过判断args的长度来选择是否结束递归,constexpr可以确保在编译期间去创建空的print边界条件以及print函数。 89 | 90 | ## Print Version3 91 | 92 | 除了递归,还有一种通过逗号表达式和初始化列表的方式来解开参数包。 93 | 94 | ```c++ 95 | template < typename T , typename ... Types> 96 | void print3 (const T& firstArg , const Types&... args) 97 | { 98 | cout << firstArg << endl ; 99 | initializer_list { ( [&args] {cout << args << endl;}(), firstArg )...}; 100 | } 101 | ``` 102 | 103 | 其中的`[&args] {cout << args << endl;}()`是构建了一个lambda表达式,并直接运行,没有`]`和`{`之间省略了`()`,所谓逗号表达式展开是指`initializer_list`会将`( [&args] {cout << args << endl;}(), firstArg )...`展开为`([&args1] {cout << args1 << endl;}(), firstArg)`,.... ,`([&argsN] {cout << argsN << endl;}(), firstArg)`,内部的lambda表达式只会生成临时对象,所以最后的`initializer_list`变为了N个firstArg,也就是类型为T,所以`initializer_list`后面才会接上``。当然也可以将initializer_list打印出来看看: 104 | 105 | ```c++ 106 | template < typename T , typename ... Types> 107 | void prin3_test (const T& firstArg , const Types&... args) 108 | { 109 | cout << firstArg << endl ; 110 | auto i_l = initializer_list { ( [&args] {cout << args << endl;}(), firstArg )...}; 111 | for (auto i : i_l) 112 | cout << i << endl; 113 | } 114 | ``` 115 | 116 | 另外, 逗号表达式可以接上多个,不限于一个: 117 | 118 | ```c++ 119 | initializer_list { 120 | ( [&args] {cout << args << endl;}(), [&args] {cout << args << endl;}(), firstArg)...}; 121 | ``` 122 | 123 | ## Print Version4 124 | 125 | 知道上面的initializer_list的解包方式, 还可以只使用一行实现print: 126 | 127 | ```c++ 128 | template 129 | void print4 (const Types&... args) 130 | { 131 | initializer_list { ([&args] {cout << args << endl;}(), 0)...}; 132 | } 133 | ``` 134 | 135 | 关键在于直接传递参数包 , 第一个参数不需要分开 , 如此就可以达到一行实现print的功能. 136 | 137 | ## 容器的Print 138 | 139 | 上述的print只能针对那些基础类型以及重构了`<<`操作符的自定义对象, 对于STL中的容器, 则需要自己重载操作符, 下面给出vector的重载操作符函数(当然容器内部的对象也需要重载`<<`): 140 | 141 | ```c++ 142 | template 143 | ostream& operator << (ostream& os, const vector& vec){ 144 | os << "{ "; 145 | for (auto& v : vec) 146 | os << v << ' '; 147 | os << "}"; 148 | return os; 149 | } 150 | ``` 151 | 152 | 重载后, 也可以使用上述的print函数了, 除了tuple容器以外, 其他容器的重载操作符与上述类似, 有些许差异. 153 | 154 | ## tuple容器的print 155 | 156 | tuple是C++11提出来的, 内部实现使用的是variadic template, 所以需要特别处理. 下面给出tuple一种基于递归继承的简洁实现: 157 | 158 | ```c++ 159 | template class mytuple1; //前向申明 160 | template <> class mytuple1<> {}; //递归终止类 161 | 162 | template 163 | class mytuple : private mytuple1 //递归继承 164 | { 165 | using inherited = mytuple1; 166 | public: 167 | mytuple1() {} 168 | mytuple1(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {} 169 | Head head() {return m_head;} 170 | inherited& tail() {return *this;} 171 | protected: 172 | Head m_head; 173 | }; 174 | ``` 175 | 176 | ```c++ 177 | mytuple1 t(41, 6.3, "nico"); 178 | print1(t1.head(), t1.tail().head(), t1.tail().tail().head()); 179 | ``` 180 | 181 | 上述继承关系可以表示为如下结构: 182 | 183 | ![1567580434140](assets/1567580434140.png) 184 | 185 | tuple还有一种递归组合的实现方式, 也列出来, 有兴趣的朋友也可以看看: 186 | 187 | ```c++ 188 | template class mytuple2; 189 | template <> class mytuple2<> {}; 190 | 191 | template 192 | class mytuple2 193 | { 194 | using composited = mytuple2; 195 | public: 196 | mytuple2() {} 197 | mytuple2(Head v, Tail... vtail) : m_head(v), m_tail(vtail...) {} 198 | Head head() {return m_head;} 199 | composited& tail() {return m_tail;} 200 | protected: 201 | Head m_head; 202 | composited m_tail; 203 | }; 204 | ``` 205 | 206 | 结构图就不是继承了,而是组合了,与上面类似: 207 | 208 | ![1567581621695](assets/1567581621695.png) 209 | 210 | 现在来重载tuple容器的操作符, 代码如下: 211 | 212 | ```c++ 213 | template 214 | struct PRINT_TUPLE { 215 | static void print (ostream& os, const tuple& t){ 216 | os << get(t) << (IDX + 1 == MAX ? "": ","); 217 | PRINT_TUPLE::print(os, t); 218 | } 219 | }; 220 | 221 | template 222 | struct PRINT_TUPLE { 223 | static void print (ostream& os, const tuple& t){ 224 | 225 | } 226 | }; 227 | 228 | template 229 | ostream& operator << (ostream& os, const tuple& t) { 230 | os << "["; 231 | PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t); 232 | return os << "]"; 233 | } 234 | ``` 235 | 236 | 一个技巧是通过`sizeof...`求出参数包的长度, 然后从建立一个索引, 依次调用get函数打印元素, 直到索引等于包的长度调用递归结束函数, 其中PRINT_TUPLE类中的是否打印逗号也是一样的道理. 237 | 238 | ## 结语 239 | 240 | 最后附上所有代码, 以供试玩, 建议在C++17环境运行, if constexpr是C++17引入的新功能. 241 | 242 | ```c++ 243 | #include 244 | #include 245 | #include 246 | #include 247 | #include 248 | 249 | using namespace std; 250 | 251 | // version1 252 | void print1() {}; 253 | 254 | template 255 | void print1(const T& firstArg, const Types&... args) 256 | { 257 | cout << firstArg << endl; 258 | print1(args...); 259 | } 260 | 261 | // version2 262 | template < typename T , typename ... Types> 263 | void print2 (const T& firstArg , const Types&... args) 264 | { 265 | cout << firstArg << endl; 266 | if constexpr ( sizeof ...(args) > 0) print2 (args...) ; 267 | } 268 | 269 | // version3 270 | template < typename T , typename ... Types> 271 | void print3 (const T& firstArg , const Types&... args) 272 | { 273 | cout << firstArg << endl ; 274 | initializer_list { 275 | ( [&args] {cout << args << endl;}(), firstArg)...}; 276 | } 277 | 278 | // version4 279 | template 280 | void print4 (const Types&... args) 281 | { 282 | initializer_list { ([&args] {cout << args << endl;}(), 0)...}; 283 | } 284 | 285 | template < typename T , typename ... Types> 286 | void print3_test1 (const T& firstArg , const Types&... args) 287 | { 288 | cout << firstArg << endl ; 289 | auto i_l = initializer_list { 290 | ( [&args] {cout << args << endl;}(), firstArg)...}; 291 | for (auto i : i_l) 292 | cout << i << endl; 293 | } 294 | 295 | template < typename T , typename ... Types> 296 | void print3_test2 (const T& firstArg , const Types&... args) 297 | { 298 | cout << firstArg << endl ; 299 | auto i_l = initializer_list { 300 | ( [&args] {cout << args << endl;}(), [&args] {cout << args << endl;}(), firstArg)...}; 301 | for (auto i : i_l) 302 | cout << i << endl; 303 | } 304 | 305 | template 306 | ostream& operator << (ostream& os, const vector& vec){ 307 | os << "{ "; 308 | for (auto& v : vec) 309 | os << v << ' '; 310 | os << "}"; 311 | return os; 312 | } 313 | 314 | template class mytuple1; 315 | template <> class mytuple1<> {}; 316 | 317 | template 318 | class mytuple1 : private mytuple1 319 | { 320 | using inherited = mytuple1; 321 | public: 322 | mytuple1() {} 323 | mytuple1(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {} 324 | Head head() {return m_head;} 325 | inherited& tail() {return *this;} 326 | protected: 327 | Head m_head; 328 | }; 329 | 330 | template class mytuple2; 331 | template <> class mytuple2<> {}; 332 | 333 | template 334 | class mytuple2 335 | { 336 | using composited = mytuple2; 337 | public: 338 | mytuple2() {} 339 | mytuple2(Head v, Tail... vtail) : m_head(v), m_tail(vtail...) {} 340 | Head head() {return m_head;} 341 | composited& tail() {return m_tail;} 342 | protected: 343 | Head m_head; 344 | composited m_tail; 345 | }; 346 | 347 | template 348 | struct PRINT_TUPLE { 349 | static void print (ostream& os, const tuple& t){ 350 | os << get(t) << (IDX + 1 == MAX ? "": ","); 351 | PRINT_TUPLE::print(os, t); 352 | } 353 | }; 354 | 355 | template 356 | struct PRINT_TUPLE { 357 | static void print (ostream& os, const tuple& t){ 358 | 359 | } 360 | }; 361 | 362 | template 363 | ostream& operator << (ostream& os, const tuple& t) { 364 | os << "["; 365 | PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t); 366 | return os << "]"; 367 | } 368 | 369 | int main() 370 | { 371 | print1(7.5, "hello", bitset<16>(377), 42); 372 | print2(7.5, "hello", bitset<16>(377), 42); 373 | print3(7.5, "hello", bitset<16>(377), 42); 374 | print4(7.5, "hello", bitset<16>(377), 42); 375 | print1(vector {1, 2, 3, 4}); 376 | print2(vector {1, 2, 3, 4}); 377 | print3(vector {1, 2, 3, 4}); 378 | print4(vector {1, 2, 3, 4}); 379 | mytuple1 t1(41, 6.3, "nico"); 380 | print1(t1.head(), t1.tail().head(), t1.tail().tail().head()); 381 | mytuple2 t2(41, 6.3, "nico"); 382 | print1(t2.head(), t2.tail().head(), t2.tail().tail().head()); 383 | cout << make_tuple(41, 6.3, "nico"); 384 | return 0; 385 | } 386 | ``` 387 | 388 | Print("到此结束!"):smile: 389 | 390 | -------------------------------------------------------------------------------- /C++小知识/print/print.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++小知识/print/print.pptx -------------------------------------------------------------------------------- /C++面向对象高级编程/C++面向对象程序设计_part1.md: -------------------------------------------------------------------------------- 1 | # C++面向对象程序设计_Part1 2 | 3 | ​ 作者:方阳,笔记地址:https://github.com/FangYang970206/Cpp-Notes 4 | 5 | C++笔记主要参考侯捷老师的课程,这是一份是C++面向对象程序设计(Object Oriented Programming)的part1部分,这一部分讲述的是以良好的习惯构造C++类,基于对象(object based)讲述了两个c++类的经典实例——complex类和string类。看这份笔记需要有c++和c语言的基础,有一些很基础的不会解释。 6 | 7 | [TOC] 8 | 9 | ## C++历史 10 | 11 | 谈到c++,课程首先过了一遍历史,c++是建立在c语言之上,最早期叫c++ with class,后来在1983年正式命名为c++,在1998年,c++98标志c++1.0诞生,c++03是c++的一次科技报告,加了一些新东西,c++11加入了更多新的东西,标志着c++2.0的诞生,然后后面接着出现c++14,c++17,到现在的c++20。 12 | 13 | ## C++的组成 14 | 15 | ![1557455342813](assets/1557455342813.png) 16 | 17 | ## C++ 与 C 的数据和函数区别 18 | 19 | ![1557455426279](assets/1557455426279.png) 20 | 21 | 在c语言中,数据和函数是分开的,构造出的都是一个变量,函数通过变量进行操作,而在c++中,生成的是对象,数据和函数都包在对象中,数据和函数都是对象的成员,这是说得通,一个对象所具有的属性和数据应该放在一块,而不是分开,并且C++类通常都是通过暴露接口隐藏数据的形式,让使用者可以调用,更加安全与便捷。 22 | 23 | 下图为part1两个类的数据和函数分布,可以看看: 24 | 25 | ![1557455877512](assets/1557455877512.png) 26 | 27 | ## 基于对象与面向对象的区别 28 | 29 | 基于对象(Object Based):面对的是单一class的設計 30 | 31 | 面向对象(Object Oriented):面对的是多重classes 的设计,classes 和classes 之间的关系。 32 | 33 | 显然,要写好面向对象的程序,先基于对象写出单个class是比不可少的。 34 | 35 | ## C++类的两个经典分类 36 | 37 | 一个是没有指针的类,比如将要写的complex类,只有实部和虚部,另一个就是带有指针的类,比如将要写的另一个类string,数据内部只有一个指针,采用动态分配内存,该指针就指向动态分配的内存。 38 | 39 | ## 头文件防卫式声明 40 | 41 | ![1557456561799](assets/1557456561799.png) 42 | 43 | 从这开始介绍complex类,首先是防卫式声明,与c语言一样,防止头文件重复包含,上面是经典写法,还有一个`# pragma once`的写法,两者的区别可以参考这篇[博客]()。 44 | 45 | ## 头文件的布局 46 | 47 | ![1557457075841](assets/1557457075841.png) 48 | 49 | 首先是防卫式声明,然后是前置声明(声明要构建的类,这个例子中还有友元函数),类声明中主要写出这个类的成员数据以及成员函数,类定义部分则是将类声明中的成员函数进行实现。 50 | 51 | ## 类的声明 52 | 53 | ![1557457377946](assets/1557457377946.png) 54 | 55 | 这里的complex类是侯捷老师从c++标准库中截取的一段代码,足够说明问题,complex类主体分为public和private两部分,public放置的是类的初始化,以及复数实虚部访问和运算操作等等。private中主要防止类的数据,目的就是要隐藏数据,只暴露public中的接口,private中有double类型的实虚部,以及一个友元函数,这个友元函数实现的是复数的相加,将用于public中的+=操作符重载中,在public中,有四个函数,第一个是构造函数,目的是初始化复数,实虚部默认值为0,当传入实虚部时,后面的列表初始化会对private中的数据进行初始化,非常推荐使用列表初始化数据。第二个是重载复数的+=操作符,应该系统内部没有定义复数运算操作符,所以需要自己重载定义。第三个和第四个是分别访问复数的实部和虚部,可以看到在第一个大括号前面有一个const,**这个原因将在后面讲述**(加粗提醒自己),只要不改变成员数据的函数,都需要加上const,这是规范写法。 56 | 57 | ## 类模板简介 58 | 59 | ![1557458654337](assets/1557458654337.png) 60 | 61 | 由于我们不光是想创建double类型的复数,还想创建int类型的复数,愚蠢的想法是在实现一遍int类的complex,这时候类模板派出用场了,模板是一个很大的话题,侯捷老师有一个专门课程讲模板,笔记也会更新到那里。模板可以只写一份模板代码,需要生成不同类型的class,编译器会自动生成,具体做法是在类定义最上方加入template ,然后讲所有的double都换成T即可,在初始化的时候,在类的后面使用尖括号,尖括号中放入你想要生成的类型即可。 62 | 63 | ## 内联(inline)函数 64 | 65 | ![1557459096287](assets/1557459096287.png) 66 | 67 | 内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样。是一种空间换取时间的做法,当函数的行数只有几行的时候,应该将函数设置为内联,提高程序整体的运行效率。更加详细的说明可以参考这篇[文章](). (补充:在类的内部实现的函数编译器会自动变为inline,好像现在新的编译器可以自动对函数进行inline,无需加inline,即使加了编译器也未必真的会把函数变为inline,看编译器的判断) 68 | 69 | ## 访问级别 70 | 71 | ![1557463294773](assets/1557463294773.png) 72 | 73 | 这里上面说过,private内部的函数和成员变量是不能被对象调用的,可以通过public提供的接口对数据进行访问。 74 | 75 | ## 函数重载 76 | 77 | ![1557463840516](assets/1557463840516.png) 78 | 79 | c++中允许“函数名”相同,但函数参数需要不同(参数后面修饰函数的const也算是参数的一部分),这样可以满足不同类型参数的应用。上述中就有不同的real,不必担心它们名字相同而反正调用混乱,相同函数名和不同参数,编译器编译后的实际名称会不一样,实际调用名并不一样,所以在开始的函数名打了引号。另外,写相同函数名还是要注意一下,比如上面有两个构造函数,当使用complex c1初始化对象时,编译器不知道调用哪一个构造函数,因为两个构造函数都可以不用参数,这就发生冲突了,第二个构造函数是不需要的。 80 | 81 | ## 构造函数的位置 82 | 83 | ![1557469726240](assets/1557469726240.png) 84 | 85 | 一般情况下,构造函数都放在public里面,不然外界无法初始化对象,不过也有例外的,有一种单例设计模式,就将构造函数放入在private里面,通过public静态(static)函数进行生成对象,这个类只能创建一份对象,所以叫单例设计模式 86 | 87 | ![1557470130720](assets/1557470130720.png) 88 | 89 | ## 参数传递 90 | 91 | ![1557470685958](assets/1557470685958.png) 92 | 93 | 参数传递分为两种:pass-by-value和pass-by-reference 94 | 95 | **一条非常考验你是否受过良好c++训练就是看你是不是用pass-by-reference**。传值会分配局部变量,然后将传入的值拷贝到变量中,这既要花费时间又要花费内存,传引用就是传指针,4个字节,要快好多,如果担心传入的值被改变,在引用前加const,如果函数试图改变,就会报错。 96 | 97 | ## 返回值传递 98 | 99 | ![1557471481557](assets/1557471481557.png) 100 | 101 | 与参数传递一样,返回值传引用速度也会很快,但有一点是不能传引用的,如果你想返回的是函数内的局部变量,传引用后,函数所分配的内存清空,引用所指的局部变量也清空了,空指针出现了,这就很危险了。(引用本质上就是指针,主要用在参数传递和返回值传递) 102 | 103 | ## 友元 104 | 105 | ![1557472309252](assets/1557472309252.png) 106 | 107 | 友元函数是类的朋友,被设定为友元的函数可以访问朋友的私有成员,这个函数(do assignment plus)用来做复数加法的具体实现。第一个参数是复数的指针,这个会在this一节中进行说明。 108 | 109 | 另外还有一种情况很有意思,如下图所示,复数c2可以访问c1的数据,这个也是可以的,这可能让人感到奇怪,侯捷老师说了原因:相同类的各個对象互為友元。所以可以c2可以访问c1的数据。 110 | 111 | ![1557473149205](assets/1557473149205.png) 112 | 113 | ## 操作符重载(一),this, cout 114 | 115 | ![1557473358698](assets/1557473358698.png) 116 | 117 | 上面介绍的`__doapl`函数将在操作符重载中进行调用,可以看到第一个参数是this,对于成员函数来说,都有一个隐藏参数,那就是this,**this是一个指针,指向调用这个函数的对象**,而**操作符重载一定是作用在左边的对象**,所以+=的操作符作用在c2上,所以this指向的是c2这个对象,然后在`__doapl`函数中修改this指向c2的值。 118 | 119 | 另外,还记得上面说过`<<`运算符重载嘛,它作用的不是复数,而是ostream,这是处于使用者习惯的考量,作用复数的话将形成`complex<)。 182 | 183 | ![1557487724501](assets/1557487724501.png) 184 | 185 | ## 重探new与delete 186 | 187 | ![1557492888491](assets/1557492888491.png) 188 | 189 | ![1557492926893](assets/1557492926893.png) 190 | 191 | 可以到使用new命令动态分配内存,主要有以下三步,首先分配要构建对象的内存,返回的是一个空指针,然后对空指针进行转型,转成要生成对象类型初始化给指针,然后指针调用构造函数初始化对象。 192 | 193 | ![1557493242994](assets/1557493242994.png) 194 | 195 | ![1557493258187](assets/1557493258187.png) 196 | 197 | 可以看到delete操作可以分为两步,首先调用析构函数释放对象,然后通过操作符delete(内部调用free函数)释放分配的内存。 198 | 199 | ## 探究动态分配过程的内存块 200 | 201 | ![1557493966655](assets/1557493966655.png) 202 | 203 | 上图中就是vc创建complex类以及string类的内存块图,左边两个是complex类,长的那个是调试(debug)模式下的内存块分布,短的那个是执行(release)模式下的内存块分布,复数有两个double,所以内存占用8个字节,vc调试模式下,调试的信息部分内存占用是上面灰色块的32个字节以及下面灰色块的4个字节,红色的代表内存块的头和尾(叫cookie),占用八个字节,合在一起是52个字节,vc会以16个字节对齐,所以会填充12字节,对应的是pad的部分,另外,为了凸显这是分配出去的内存,所以在头尾部分,用最后一位为1代表该内存分配出去了,为0就是收回来了。执行模式下没有调试信息。string类类似分析。 204 | 205 | ## 动态分配array需要注意的问题 206 | 207 | ![1557494889736](assets/1557494889736.png) 208 | 209 | 上面是动态分配内存,生成complex类的数组以及string类的数组的内存块图,与上面类似,不过这里多了一个长度的字节,都为3,标记对象的个数。 210 | 211 | ![1557495133404](assets/1557495133404.png) 212 | 213 | 上面说明的是,如果分配的是动态对象数组,就一定要在delete后面加上[]符号,不然就无法完全释放动态分配的内存。**array new一定要搭配array delete**。 214 | 215 | part1到此结束。 -------------------------------------------------------------------------------- /C++面向对象高级编程/C++面向对象程序设计_part2.md: -------------------------------------------------------------------------------- 1 | # C++面向对象程序设计_Part2 2 | 3 | ​ 作者:方阳,笔记地址:https://github.com/FangYang970206/Cpp-Notes 4 | 5 | part1讲述了基于对象,part2则是在基于对象的基础上,建立类与类之间的联系,即面向对象编程以及面向对象设计。 6 | 7 | 主要讲述以下三点: 8 | 9 | - Inheritance (继承) 10 | - Composition(复合) 11 | - Delegation (委托) 12 | 13 | 另外,我把补充内容中的对象模型放入到Part2,我觉得放入这里更加合适。 14 | 15 | [TOC] 16 | 17 | ## Composition(复合) 18 | 19 | ![1558453874580](assets/1558453874580.png) 20 | 21 | Composition(复合)就是has a, 上面的事例就是队列(queue)类中有一个双端队列(deque)类,队列中的成员函数通过双端队列的操作函数完成,这是类与类之间的第一个关系。(黑色菱形代表内部拥有) 22 | 23 | deque中可能拥有很多方法,但queue中只通过deque提供了非常少的方法,这是一个设计模式Adapter,将一个拥有许多功能的类改造一下,得到另一个类。 24 | 25 | ## 内存视角下的composition(复合) 26 | 27 | ![1558459151904](assets/1558459151904.png) 28 | 29 | 可以看到有两个复合关系,最后queue的内存是40. 30 | 31 | ## composition(复合)关系下的构造与析构 32 | 33 | ![1558457528106](assets/1558457528106.png) 34 | 35 | 由于Container类是拥有Component类,所以在构造方面,先调用Component类的默认构造函数,然后再调用Container的构造函数,由内而外的构造,里面做好了,再做外面。析构则相反,先对Container进行析构,然后再对Component进行析构,过程是由外而内,将外面的去掉,才能看到里面去掉里面,符合常识。 36 | 37 | ## Delegation (委託) —— Composition by reference 38 | 39 | ![1558457888072](assets/1558457888072.png) 40 | 41 | 如果一个类(string)中拥有一个指针(StringRep*),该指针指向的是另一个类(StringRep),这种关系是Delegation(委托),更好的说法就是Composition by reference(学术界不说by pointer),两种类的生命周期不一样,与复合两种类会同时初始化不同,委托当需要用的时候再进行初始化。上图中的实例是一种非常有名的设计,叫handle/body,指针指向的类负责具体实现,可以看到有一个//pimpl,意思是pointer to implement,而拥有那个指针的类只提供外界接口,就是基于委托这种方式,Handle(string)是提供给外界的接口,body(StringRep)就是实现部分,为什么这么有名,这是因为String类设计好了就不需修改了,只需要修改实现的那一个类,具有很好的弹性,另外,还有可以进行共享机制(减小内存),下图的a,b,c共享一个StringRep,这种方式叫做reference counting,当需要修改其中一个时,需要把内容copy出来一份进行修改,另外两个依然指向原StringRep。(白色菱形代表指针指向) 42 | 43 | ## Inheritance (继承) 44 | 45 | ![1558491481526](assets/1558491481526.png) 46 | 47 | 继承的语法就是在类名后加上:public(还可以是protected,private)你想要继承的类,如果想继承多个类,用逗号隔开就可以了。什么时候用继承,确定一个关键点,子类is a 父类(例如,狗is a动物)。上述的List_nodes是继承了List_node_base所有的数据,另外还有自己的数据。 48 | 49 | ## Inheritance (继承)关系下的构造与析构 50 | 51 | ![1558716277153](assets/1558716277153.png) 52 | 53 | 继承的类(derived object)的一部分是基类(base part),对于要被继承的基类,它的析构函数必须是virtual,不然会出现问题,这个问题将在后面说。继承的构造函数会首先调用基类的构造函数,然后调用自己的构造函数(由内而外)。析构则相反,先析构自己,然后再调用基类的析构函数。 54 | 55 | ## Inheritance (继承)with virtual functions(虚函数) 56 | 57 | ![1558716850059](assets/1558716850059.png) 58 | 59 | 子类继承了父类的两样东西,一种是父类的数据,一种是父类函数的调用权。对于一个类而言,它的子类都可以访问所以的public方法,而子类要不要重新定义父类的函数呢?这时候就需要虚函数了,当public里面的函数不是虚函数时,则希望子类不重新定义该函数。当函数是虚函数时(在返回类型前加入关键字virtual),则希望子类重新定义它,并且父类已经有了默认定义。当函数是纯虚函数时(在结束符;前面加上=0),则希望子类一定要重新定义它,父类没有默认定义(但可以有默认定义)。该事例是定义了一个基类shape,然后矩形Rectangle和椭圆Ellipse对shape进行继承,基类的objectID是无需继承的,可以提前定好,在父类调用即可,而error函数,父类有默认的错误信息,如果子类有更精细的错误信息,父类允许子类可以重新定义的,打印出子类调用时的错误,而draw函数则必须重新定义,父类没有定义(draw shape没有意义),子类不同,所画出的形状自然不同。 60 | 61 | ## Inheritance (继承)with virtual ——经典实例 62 | 63 | ![1558718409179](assets/1558718409179.png) 64 | 65 | 对于在powerpoint打开ppt文件而言,有以下几步,先点打开命令的菜单栏,然后出现文件界面,选择我们要打开的文件名,然后程序会检查文件名是否符合规范,符合规范则在磁盘上搜索文件,搜索到了打开文件即可。而遇到注意的是,所以打开文件的过程都是这样,只有最后打开文件可能会不同(可能会打开不同格式的文件),于是有团队就将除文件打开函数以外的函数进行打包,子类直接继承,只要子类重新定义父类打开文件的函数即可。如下图所示: 66 | 67 | ![1558718827030](assets/1558718827030.png) 68 | 69 | 团队开发了CDocument类,定义Serialize函数需要重新定义,在OnFileOpen函数中的省略号即为打包好的过程。用CDocument类的人只需重新定义Serialize函数即可,则在main函数中,先创建一个CMyDoc实例myDoc,调用myDoc.OnFileOpen函数,子类没有定义这个函数,实则调用的是父类的函数,即CDocument::OnFileOpen(&myDoc), 进入父类函数中,运行打包好的过程,当运行到Serialize函数时,发现子类重新定义了它,则调用子类重新定义的Serialize函数,最后再返回到CDocument::OnFileOpen,继续下面的过程。再也不用写一般的步骤了,完美!这是一种非常有名的设计模式Template method(不是说C++ template),提供了一种应用框架,它将重复一样的操作写好,不确定的步骤留给实际应用设计者重新实现。十年前最有名的产品MFC就是这样一种应用框架。 70 | 71 | 深层次的理解,谁调用函数,this就是谁,当调用Serialize函数是,编译器是通过this->Serialize()调用,于是就调用到了this重新定义的Serialize函数。 72 | 73 | ![1558752779674](assets/1558752779674.png) 74 | 75 | 上图就是CDocument和CMyDoc的实例,用cout来模拟步骤,呼应上面两张图片。 76 | 77 | ## Inheritance + Composition关系下的构造与析构 78 | 79 | ![1558753009197](assets/1558753009197.png) 80 | 81 | 当同时存在继承和复合,类是如何进行构造和析构呢?这一节要讨论的问题: 82 | 83 | 1. 子类有父类的成分还有包含着另一个类; 84 | 2. 子类继承父类,父类拥有另外一个类。 85 | 86 | 情况2就很明显了,构造依然是自内而外,析构是由外而内。 87 | 88 | 对于情况1,这是侯捷老师留的作业,自己写代码判断,我写了一个: 89 | 90 | ```c++ 91 | #include 92 | 93 | using namespace std; 94 | 95 | namespace fy1{ 96 | class Base; 97 | class Derived; 98 | class Component; 99 | 100 | class Base{ 101 | public: 102 | Base() {cout << "Base Ctor" << endl;} 103 | virtual ~Base() {cout << "Base Dtor" << endl;} 104 | }; 105 | 106 | class Component{ 107 | public: 108 | Component() {cout << "Component Ctor" << endl;}; 109 | ~Component() {cout << "Component Dtor" << endl;}; 110 | }; 111 | 112 | class Derived : Base{ 113 | public: 114 | Derived() {cout << "Derived Ctor" << endl;} 115 | ~Derived() {cout << "Derived Dtor" << endl;} 116 | protected: 117 | Component c; 118 | }; 119 | 120 | void fy1_test(){ 121 | Derived d; 122 | } 123 | } 124 | 125 | int main() { 126 | cout << "Ctor and Dtor test:" << endl; 127 | fy1::fy1_test(); 128 | return 0; 129 | } 130 | ``` 131 | 132 | 运行结果为: 133 | 134 | ```bash 135 | Ctor and Dtor test: 136 | Base Ctor 137 | Component Ctor 138 | Derived Ctor 139 | Derived Dtor 140 | Component Dtor 141 | Base Dtor 142 | ``` 143 | 144 | 可以看到先初始化父类(Base),然后再初始化Component类,再初始化自己,析构与构造相反。 145 | 146 | 下图也给出了结论。 147 | 148 | ![1558760439215](assets/1558760439215.png) 149 | 150 | 至此,面向对象的概念说完了,下面进入实例环节。 151 | 152 | ## Delegation (委托) + Inheritance (继承) (一) 153 | 154 | ![1558873574510](assets/1558873574510.png) 155 | 156 | 上述代码解决的是下图所示的问题,对同一份文件使用四个不同窗口去查看,或者右下角所示的,同一个数据,三种不同的viewer查看。数据只有一份,表现多种形式,数据变化,表现形式也会发生变化,要表现这样的特性,这就对表现文件的class和存储数据的class之间关系要有要求,上图就是下图的一种解法,23种设计模式之一。Subject类是存储数据的类,不过类中有delegation,使用了一个vector类用来存放Observer类的指针,这样Observer类以及它的所有子类都可以导入这个vector中,Observer类相当于表现形式类的父类,可以有多种表现形式,这些都是子类。update则是子类需要重新定义的方法,不同表现形式可以有不同的更新方法。对于Subject类来说,当我们想创建新的窗口(新的observer类)去查看它的时候,需要对将新的Observer类进行注册,函数attach就是实现这样的功能,可以将新的observer子类的指针加到vector中,注销的函数没有写出来,另外,当数据发生变化时,使用set_val函数,需要使用一个函数去更新所有的observer子类,这就是notify函数干的事,遍历vector每一个observer指针,调用指针指向的update方法,对表现形式进行更新。Delegation + Inheritance真的感觉好强大呀。 157 | 158 | ![1558873750010](assets/1558873750010.png) 159 | 160 | 下图是一个更详细的Observer解法构建 161 | 162 | ![1558875339692](assets/1558875339692.png) 163 | 164 | ## Delegation (委托) + Inheritance (继承) (二) 165 | 166 | ![1558887391028](assets/1558887391028.png) 167 | 168 | 第二个实例,构建一个文件系统。可以把Primitive类当作文件类,而Composite类当作目录类,与日常使用的文件系统一样,目录里面可以包含目录,也可以包含文件,所以目录里面存放的不止是目录本身还可以是文件,但是需要放入到同一个容器中,想法是使用指针,但文件和目录是不太一样的,所以解决方案是将文件和目录共同继承Component类,然后Composite类中的容器存放的是Component的指针,这样就可以既存放文件又可以存放目录了,这是一种经典的解决方案,也是23种设计模式之一,Composite。另外,Component类中还有一个虚函数add,这是给目录进行继承的,因为目录可以新建目录和文件,这里不能设置为纯虚函数,因为文件不能继承这个函数,文件是不能在进行添加的。 169 | 170 | ## Prototype 171 | 172 | ![1558923111947](assets/1558923111947.png) 173 | 174 | 这又是23种设计模式之一的Prototype,Prototype要解决的是要设计一个类,这个类是为未来的子类所设计的,他可以访问和操作未来的子类,这就很有意思了,都不知道未来的子类是啥,要去访问这个子类,这是怎么去做呢?原来在子类内部会申明一个关于子类的静态变量,就是上图中的LSAT : LandSatImage,另外这个子类会定义一个私有的构造方法(前面有一个负号,可以通过定义静态变量调用私有的构造方法),构造方法里面会调用父类的addPrototype函数,将静态变量的指针传到父类,父类就把传入的指针(通过父类的指针形式)加入到自己的容器当中,这样父类就知道子类的类型,就可以操作子类了,上述操作是这样的,父类有一个findAndClone函数,根据输入参数i选择父类容器中的子类进行clone,返回子类的指针,而clone父类定义的是一个纯虚函数,子类必须重新定义,上图中子类重新定义的是返回新建一个子类,返回它的指针,不过这个新建是调用的另外一个构造函数(有一个#号,代表protect),使用private里面的构造函数是不行的,它是为父类打通桥梁的,为了与private里面的构造函数区分开,形参有一个int类型,这个int类型不会进行使用。 175 | 176 | 下面的图片是相关代码,解释上面的文字已经说的很清楚了。 177 | 178 | 父类Image 179 | 180 | ![1558925338958](assets/1558925338958.png) 181 | 182 | 两个子类代码: 183 | 184 | ![1558925377433](assets/1558925377433.png) 185 | 186 | 主函数: 187 | 188 | ![1558925446700](assets/1558925446700.png) 189 | 190 | Prototype例子来自于《Design Patterns Explained Simply》这本书,经典致敬! 191 | 192 | ![1558925624955](assets/1558925624955.png) 193 | 194 | 面向对象的例子讲完了,下面介绍更加深层次的内容,理解面向对象更底层的东西。 195 | 196 | ## 对象模型 197 | 198 | ![1558939321858](assets/1558939321858.png) 199 | 200 | 这张图所蕴含的信息量很大,现在一步步来看,首先最右边有三个类,A,B,C,A是爷爷,B是父亲,C是孙子,向上继承的关系,首先我们从内存的角度来看类的布局,对于有虚函数(不管有多少个虚函数)的类来说,它在内存中不仅有自己定义的数据,还会多出一个指针,这个指针学名叫做虚指针(vptr),虚指针会指向一个虚表(vtbl),虚表里面定义的是各个虚函数所在的地址。A类内存中第一个就是虚指针,指向虚表,虚标里面有两个指针指向A的两个虚函数,下面两个是A类的数据,B类的前三个是继承了A类的数据以及虚指针,不同的是B类重新定义了vfunc1函数,这将更新虚表,会将原来指向A::vfunc1函数的指针改为指向B::vfunc1函数的指针,如图就是将黄色的0x401ED0变为了浅蓝的0x401F80,C类继承B类的数据和虚指针,另外还有自己的数据,同时又重载了vfunc1,所以对应虚表中的vfunc1指针也要发生变化。在调用的时候,根据类中虚指针定义的虚表所指向的函数进行调用虚函数,这就是继承的根本原理,虚函数则是面向对象最强大的武器。非虚函数就是普通的函数,不过前面加上了类作用域。 201 | 202 | 静态绑定与动态绑定:当new一个C类时,得到一个指针p(上图所示),当通过p调用vfunc1的时候,实际上是最下面中间语句进行调用的(其中n为编译器给虚函数在虚表中的索引),这是函数的调用方式与c很不一样,在c的时代,当编译器看到函数调用,编译器会直接调用call XXX(XXX代表地址),地址是静态的,不会发生变化,这种方式叫做静态绑定,而C++通过类指针找到虚指针,根据虚指针找到虚表,从虚表中取出虚函数地址进行调用,这是一种动态的调用,不同类的指针所调用的虚函数是不一样的,这种方式叫做动态绑定,也叫虚机制。 203 | 204 | 下图就是一个实例,展示了这种动态绑定的强大威力。 205 | 206 | ![1558943366112](assets/1558943366112.png) 207 | 208 | 我要设计一个powerpoint,要画出各种不同的形状,我们可以用一个List,承载A类的指针(放指针是因为容器只能放内存大小一致的东西,不同的形状内存不一致),这样它的所有子类都可以放入List中了,因为都是A类,放入List后,循环遍历直接调用各自形状的draw函数就可以完成要求,这里走的依然是动态绑定,这是很好的,为了更加突出动态绑定的好处,这里要写出c语言中是如何做的,c语言只有静态绑定,上述过程需要条件判断当前的指针是指向哪一个类,各种形状要使用多重判断,另外,当需要添加新的形状类时,又要修改条件判断代码,这是很不好的,不符合直观理解,应该像C++的虚函数一样,指针指向什么形状,就应该调用那种类型的draw。由此可见,C++动态绑定很棒,很强大。C++支持动态绑定和静态绑定,符合下面三个条件,C++采用动态绑定,条件如下: 209 | 210 | - 必须使用指针调用函数 211 | - 该指针必须是向上转型 (List中定义的类型是A的指针,但可以存储C的指针,通过继承向上转型) 212 | - 调用的函数必须是虚函数 213 | 214 | 这里还有一个概念——多态,List申明的时候,是通过pointer-to-A进行申明的,但实际是List可以存储各种不同的形状,都属于A,确是不同形态,所以叫多态。 215 | 216 | 所以,所谓的多态,虚函数,动态绑定,虚指针以及虚表,所有的故事都是同一件事情,真的了然于胸啊!:smile: 217 | 218 | ![1558945922875](assets/1558945922875.png) 219 | 220 | 可以再来看看之前讲的Template method,这就是动态绑定的一个应用,子类CMyDoc调用父类的OnFileOpen函数,调用的时候会将子类的地址传入到父类的函数中,也就是this pointer(成员函数都是一个默认参数,代表的就是this pointer,谁调用成员函数,谁的地址就是this pointer),在OnFileOpen中,所有的调用前面都会加上一个this pointer,而对于Serialize函数来说,它是符合上述我们说的三个条件的,首先调用者是this,是指针,然后指向的是子类,向上转型,调用的Serialize函数是虚函数,所以会使用动态绑定,调用CMyDoc的Serialize函数。这是很好的! 221 | 222 | ![1558948644501](assets/1558948644501.png) 223 | 224 | 现在从汇编代码的角度来看函数调用,初始化B类,讲B类强制转型为A类,得到a,调用a.vfunc1()函数,这里是静态绑定,因为是通过类调用函数,不是指针调用,汇编代码也说明了这个问题,使用的是call xxx形式编译函数。 225 | 226 | ![1558949507638](assets/1558949507638.png) 227 | 228 | 而新创建了一个指针B,给的类型是指针A的类型,通过然后调用vfunc1函数,符合三个条件,是动态绑定,汇编代码的形式也不一样了,汇编表示看不懂,不过call一行连同上面几行最后的表示在c语言中的描述确实是动态绑定的描述。另外,将b的地址赋给pa,重新调用vfunc1,一样是动态绑定,与new B是一样的。 229 | 230 | ```c++ 231 | #include 232 | 233 | using namespace std; 234 | 235 | 236 | namespace fy2{ 237 | class A{ 238 | public: 239 | virtual void vfunc1() {cout << "A::vfunc1()" << endl;} 240 | private: 241 | int data1; 242 | }; 243 | 244 | class B : public A{ 245 | public: 246 | virtual void vfunc1() {cout << "B::vfunc1()" << endl;} 247 | private: 248 | int data2; 249 | }; 250 | 251 | void fy2_test(){ 252 | B b; 253 | A a = (A) b; 254 | a.vfunc1(); 255 | 256 | A* pa = new B; 257 | pa->vfunc1(); 258 | 259 | pa = &b; 260 | pa->vfunc1(); 261 | } 262 | } 263 | 264 | int main() { 265 | cout << "object model test:" << endl; 266 | fy2::fy2_test(); 267 | return 0; 268 | } 269 | ``` 270 | 271 | 输出结果: 272 | 273 | ```bash 274 | object model test: 275 | A::vfunc1() 276 | B::vfunc1() 277 | B::vfunc1() 278 | ``` 279 | 280 | 面向对象的笔记到此结束,深深感受到了C++面向对象程序的power,fighting! -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557455342813.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557455342813.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557455426279.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557455426279.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557455877512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557455877512.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557456561799.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557456561799.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557457075841.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557457075841.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557457377946.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557457377946.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557458654337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557458654337.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557459096287.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557459096287.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557463294773.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557463294773.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557463840516.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557463840516.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557469726240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557469726240.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557470130720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557470130720.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557470685958.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557470685958.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557471481557.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557471481557.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557472309252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557472309252.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557473149205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557473149205.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557473358698.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557473358698.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557473691604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557473691604.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557474432766.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557474432766.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557477552802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557477552802.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557477761856.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557477761856.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557478475362.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557478475362.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557479620018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557479620018.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557480221092.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557480221092.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557481099332.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557481099332.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557481941108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557481941108.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557482417743.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557482417743.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557487075666.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557487075666.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557487606126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557487606126.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557487638928.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557487638928.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557487724501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557487724501.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557492888491.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557492888491.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557492926893.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557492926893.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557493242994.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557493242994.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557493258187.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557493258187.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557493966655.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557493966655.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557494889736.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557494889736.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557495133404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557495133404.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557714511998.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557714511998.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557715259428.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557715259428.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557715824622.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557715824622.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557716265949.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557716265949.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557716414284.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557716414284.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557716746695.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557716746695.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557717032223.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557717032223.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1557717432386.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1557717432386.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558453874580.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558453874580.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558457528106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558457528106.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558457888072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558457888072.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558459151904.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558459151904.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558491481526.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558491481526.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558716277153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558716277153.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558716850059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558716850059.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558718409179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558718409179.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558718827030.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558718827030.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558752779674.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558752779674.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558753009197.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558753009197.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558760439215.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558760439215.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558873574510.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558873574510.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558873750010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558873750010.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558875339692.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558875339692.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558887391028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558887391028.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558923111947.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558923111947.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558925338958.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558925338958.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558925377433.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558925377433.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558925446700.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558925446700.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558925624955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558925624955.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558939321858.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558939321858.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558943366112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558943366112.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558945922875.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558945922875.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558948644501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558948644501.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1558949507638.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1558949507638.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560404584181.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560404584181.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560404974745.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560404974745.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560405283461.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560405283461.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560405362848.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560405362848.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560406299464.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560406299464.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560406833869.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560406833869.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560407154869.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560407154869.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560410852393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560410852393.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560411003846.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560411003846.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560411429029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560411429029.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560411583591.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560411583591.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560411683785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560411683785.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560411846337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560411846337.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560412186484.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560412186484.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560413833493.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560413833493.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560413886633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560413886633.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560419363053.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560419363053.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560419673831.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560419673831.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560419917708.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560419917708.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560422076723.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560422076723.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560422092804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560422092804.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560422107865.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560422107865.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560761309230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560761309230.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560761942136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560761942136.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560762376012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560762376012.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560773568977.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560773568977.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560773903634.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560773903634.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560774047068.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560774047068.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560774481410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560774481410.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560774759364.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560774759364.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560775017510.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560775017510.png -------------------------------------------------------------------------------- /C++面向对象高级编程/assets/1560775583436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/C++面向对象高级编程/assets/1560775583436.png -------------------------------------------------------------------------------- /C++面向对象高级编程/code/complex.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYCOMPLEX__ 2 | #define __MYCOMPLEX__ 3 | 4 | class complex; 5 | complex& 6 | __doapl (complex* ths, const complex& r); 7 | complex& 8 | __doami (complex* ths, const complex& r); 9 | complex& 10 | __doaml (complex* ths, const complex& r); 11 | 12 | 13 | class complex 14 | { 15 | public: 16 | complex (double r = 0, double i = 0): re (r), im (i) { } 17 | complex& operator += (const complex&); 18 | complex& operator -= (const complex&); 19 | complex& operator *= (const complex&); 20 | complex& operator /= (const complex&); 21 | double real () const { return re; } 22 | double imag () const { return im; } 23 | private: 24 | double re, im; 25 | 26 | friend complex& __doapl (complex *, const complex&); 27 | friend complex& __doami (complex *, const complex&); 28 | friend complex& __doaml (complex *, const complex&); 29 | }; 30 | 31 | 32 | inline complex& 33 | __doapl (complex* ths, const complex& r) 34 | { 35 | ths->re += r.re; 36 | ths->im += r.im; 37 | return *ths; 38 | } 39 | 40 | inline complex& 41 | complex::operator += (const complex& r) 42 | { 43 | return __doapl (this, r); 44 | } 45 | 46 | inline complex& 47 | __doami (complex* ths, const complex& r) 48 | { 49 | ths->re -= r.re; 50 | ths->im -= r.im; 51 | return *ths; 52 | } 53 | 54 | inline complex& 55 | complex::operator -= (const complex& r) 56 | { 57 | return __doami (this, r); 58 | } 59 | 60 | inline complex& 61 | __doaml (complex* ths, const complex& r) 62 | { 63 | double f = ths->re * r.re - ths->im * r.im; 64 | ths->im = ths->re * r.im + ths->im * r.re; 65 | ths->re = f; 66 | return *ths; 67 | } 68 | 69 | inline complex& 70 | complex::operator *= (const complex& r) 71 | { 72 | return __doaml (this, r); 73 | } 74 | 75 | inline double 76 | imag (const complex& x) 77 | { 78 | return x.imag (); 79 | } 80 | 81 | inline double 82 | real (const complex& x) 83 | { 84 | return x.real (); 85 | } 86 | 87 | inline complex 88 | operator + (const complex& x, const complex& y) 89 | { 90 | return complex (real (x) + real (y), imag (x) + imag (y)); 91 | } 92 | 93 | inline complex 94 | operator + (const complex& x, double y) 95 | { 96 | return complex (real (x) + y, imag (x)); 97 | } 98 | 99 | inline complex 100 | operator + (double x, const complex& y) 101 | { 102 | return complex (x + real (y), imag (y)); 103 | } 104 | 105 | inline complex 106 | operator - (const complex& x, const complex& y) 107 | { 108 | return complex (real (x) - real (y), imag (x) - imag (y)); 109 | } 110 | 111 | inline complex 112 | operator - (const complex& x, double y) 113 | { 114 | return complex (real (x) - y, imag (x)); 115 | } 116 | 117 | inline complex 118 | operator - (double x, const complex& y) 119 | { 120 | return complex (x - real (y), - imag (y)); 121 | } 122 | 123 | inline complex 124 | operator * (const complex& x, const complex& y) 125 | { 126 | return complex (real (x) * real (y) - imag (x) * imag (y), 127 | real (x) * imag (y) + imag (x) * real (y)); 128 | } 129 | 130 | inline complex 131 | operator * (const complex& x, double y) 132 | { 133 | return complex (real (x) * y, imag (x) * y); 134 | } 135 | 136 | inline complex 137 | operator * (double x, const complex& y) 138 | { 139 | return complex (x * real (y), x * imag (y)); 140 | } 141 | 142 | complex 143 | operator / (const complex& x, double y) 144 | { 145 | return complex (real (x) / y, imag (x) / y); 146 | } 147 | 148 | inline complex 149 | operator + (const complex& x) 150 | { 151 | return x; 152 | } 153 | 154 | inline complex 155 | operator - (const complex& x) 156 | { 157 | return complex (-real (x), -imag (x)); 158 | } 159 | 160 | inline bool 161 | operator == (const complex& x, const complex& y) 162 | { 163 | return real (x) == real (y) && imag (x) == imag (y); 164 | } 165 | 166 | inline bool 167 | operator == (const complex& x, double y) 168 | { 169 | return real (x) == y && imag (x) == 0; 170 | } 171 | 172 | inline bool 173 | operator == (double x, const complex& y) 174 | { 175 | return x == real (y) && imag (y) == 0; 176 | } 177 | 178 | inline bool 179 | operator != (const complex& x, const complex& y) 180 | { 181 | return real (x) != real (y) || imag (x) != imag (y); 182 | } 183 | 184 | inline bool 185 | operator != (const complex& x, double y) 186 | { 187 | return real (x) != y || imag (x) != 0; 188 | } 189 | 190 | inline bool 191 | operator != (double x, const complex& y) 192 | { 193 | return x != real (y) || imag (y) != 0; 194 | } 195 | 196 | #include 197 | 198 | inline complex 199 | polar (double r, double t) 200 | { 201 | return complex (r * cos (t), r * sin (t)); 202 | } 203 | 204 | inline complex 205 | conj (const complex& x) 206 | { 207 | return complex (real (x), -imag (x)); 208 | } 209 | 210 | inline double 211 | norm (const complex& x) 212 | { 213 | return real (x) * real (x) + imag (x) * imag (x); 214 | } 215 | 216 | #endif //__MYCOMPLEX__ 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /C++面向对象高级编程/code/complex_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "complex.h" 3 | 4 | using namespace std; 5 | 6 | ostream& 7 | operator << (ostream& os, const complex& x) 8 | { 9 | return os << '(' << real (x) << ',' << imag (x) << ')'; 10 | } 11 | 12 | int main() 13 | { 14 | complex c1(2, 1); 15 | complex c2(4, 0); 16 | 17 | cout << c1 << endl; 18 | cout << c2 << endl; 19 | 20 | cout << c1+c2 << endl; 21 | cout << c1-c2 << endl; 22 | cout << c1*c2 << endl; 23 | cout << c1 / 2 << endl; 24 | 25 | cout << conj(c1) << endl; 26 | cout << norm(c1) << endl; 27 | cout << polar(10,4) << endl; 28 | 29 | cout << (c1 += c2) << endl; 30 | 31 | cout << (c1 == c2) << endl; 32 | cout << (c1 != c2) << endl; 33 | cout << +c2 << endl; 34 | cout << -c2 << endl; 35 | 36 | cout << (c2 - 2) << endl; 37 | cout << (5 + c2) << endl; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /C++面向对象高级编程/code/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYSTRING__ 2 | #define __MYSTRING__ 3 | 4 | class String 5 | { 6 | public: 7 | String(const char* cstr=0); 8 | String(const String& str); 9 | String& operator=(const String& str); 10 | ~String(); 11 | char* get_c_str() const { return m_data; } 12 | private: 13 | char* m_data; 14 | }; 15 | 16 | #include 17 | 18 | inline 19 | String::String(const char* cstr) 20 | { 21 | if (cstr) { 22 | m_data = new char[strlen(cstr)+1]; 23 | strcpy(m_data, cstr); 24 | } 25 | else { 26 | m_data = new char[1]; 27 | *m_data = '\0'; 28 | } 29 | } 30 | 31 | inline 32 | String::~String() 33 | { 34 | delete[] m_data; 35 | } 36 | 37 | inline 38 | String& String::operator=(const String& str) 39 | { 40 | if (this == &str) 41 | return *this; 42 | 43 | delete[] m_data; 44 | m_data = new char[ strlen(str.m_data) + 1 ]; 45 | strcpy(m_data, str.m_data); 46 | return *this; 47 | } 48 | 49 | inline 50 | String::String(const String& str) 51 | { 52 | m_data = new char[ strlen(str.m_data) + 1 ]; 53 | strcpy(m_data, str.m_data); 54 | } 55 | 56 | #include 57 | using namespace std; 58 | 59 | ostream& operator<<(ostream& os, const String& str) 60 | { 61 | os << str.get_c_str(); 62 | return os; 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /C++面向对象高级编程/code/string_test.cpp: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | String s1("hello"); 9 | String s2("world"); 10 | 11 | String s3(s2); 12 | cout << s3 << endl; 13 | 14 | s3 = s1; 15 | cout << s3 << endl; 16 | cout << s2 << endl; 17 | cout << s1 << endl; 18 | } 19 | -------------------------------------------------------------------------------- /C++面向对象高级编程/code/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | namespace fy1{ 6 | class Base; 7 | class Derived; 8 | class Component; 9 | 10 | class Base{ 11 | public: 12 | Base() {cout << "Base Ctor" << endl;} 13 | virtual ~Base() {cout << "Base Dtor" << endl;} 14 | }; 15 | 16 | class Component{ 17 | public: 18 | Component() {cout << "Component Ctor" << endl;}; 19 | ~Component() {cout << "Component Dtor" << endl;}; 20 | }; 21 | 22 | class Derived : Base{ 23 | public: 24 | Derived() {cout << "Derived Ctor" << endl;} 25 | ~Derived() {cout << "Derived Dtor" << endl;} 26 | protected: 27 | Component c; 28 | }; 29 | 30 | void fy1_test(){ 31 | Derived d; 32 | } 33 | } 34 | 35 | namespace fy2{ 36 | class A{ 37 | public: 38 | virtual void vfunc1() {cout << "A::vfunc1()" << endl;} 39 | private: 40 | int data1; 41 | }; 42 | 43 | class B : public A{ 44 | public: 45 | virtual void vfunc1() {cout << "B::vfunc1()" << endl;} 46 | private: 47 | int data2; 48 | }; 49 | 50 | void fy2_test(){ 51 | B b; 52 | A a = (A) b; 53 | a.vfunc1(); 54 | 55 | A* pa = new B; 56 | pa->vfunc1(); 57 | 58 | pa = &b; 59 | pa->vfunc1(); 60 | } 61 | } 62 | 63 | 64 | int main() { 65 | cout << "Ctor and Dtor test:" << endl; 66 | fy1::fy1_test(); 67 | cout << endl; 68 | cout << "object model test:" << endl; 69 | fy2::fy2_test(); 70 | return 0; 71 | } -------------------------------------------------------------------------------- /C++面向对象高级编程/补充内容.md: -------------------------------------------------------------------------------- 1 | # 面向对象高级编程补充内容 2 | 3 | ​ 作者:方阳,笔记地址:https://github.com/FangYang970206/Cpp-Notes 4 | 5 | 课程除了将面向对象以外,还补充了一些其他内容,在这里进行介绍, 课程中有一部分涉及C++11, 我没有写出来,放到C++11部分专门去讨论, 另外, 下面有一些部分来自于[hujiese]([https://github.com/hujiese/C-Enhance-for-OOP/blob/master/%E4%B8%8B%E7%AF%87/C%2B%2B%E9%AB%98%E7%BA%A7%E5%9F%B9%E8%AE%AD%E7%AC%94%E8%AE%B0%E4%B8%80.md](https://github.com/hujiese/C-Enhance-for-OOP/blob/master/下篇/C%2B%2B高级培训笔记一.md))的笔记, 我进行了一些补充, 减小工作量. 6 | 7 | [TOC] 8 | 9 | ## static与this 10 | 11 | ![1557714511998](assets/1557714511998.png) 12 | 13 | 没有static类型的变量或者函数时,complex类的成员函数是只有一份代码,通过this指针(谁调用谁的地址就是this)来调用不同的对象,this指针是一个默认会传入的参数,比如在调用real这个函数时,直接返回的是re,但编译器会在前面加上this->,用来指明是调用对象的re。而加上static,static所声明的变量和函数与this是没有关系的,无法通过this调用static类型的变量和函数,那有什么用呢,比如,在银行存储不同用户的存款时,c1,c2和c3代表的是用户自身的数据,但有的数据跟用户的数据没有关系,比如利率,这时候就可以将利率声明为static,所有对象都可以访问(静态作用域为全局)。如下图: 14 | 15 | ![1557715259428](assets/1557715259428.png) 16 | 17 | 静态的数据在类中声明后,要在外面进行定义,静态set_rate函数操作静态m_rate(静态变量一般通过静态函数进行修改),调用静态set_rate函数,有两种方法,如上所示,在通过object调用时,不会传入this指针,这点要注意。 18 | 19 | ## 构造函数放入private(单例模式) 20 | 21 | ![1557715824622](assets/1557715824622.png) 22 | 23 | 这个在part1提过,侯捷老师更加仔细地进行了讲解,这是一种有名的设计模式(单例模式),只能创建一个对象,由于构造函数放入private中,所以不能通过对象名直接创建,具体做法时在private里面创建一个static类型的对象,作用全局,通过public中的静态函数进行访问。然后就可以调用其他非静态的成员函数了。以下是上例更好的写法: 24 | 25 | ![1557716265949](assets/1557716265949.png) 26 | 27 | 当没有调用getInstance,静态对象不会创建,只有调用getInstance时,才会有静态对象,这样写法更好。 28 | 29 | ## cout 30 | 31 | ![1557716414284](assets/1557716414284.png) 32 | 33 | cout为什么能够访问那么多不同类型的变量,这里给出了原因,图像右上角可以直到cout是ostream类的一种,与代码中用ostream吻合。然后看看ostream的定义,可以看到ostream里面重构了好多`<<`操作符,各种各样的类型,这也就是为什么cout可以打印各种不同类型的数据的原因。 34 | 35 | ## 类模板 36 | 37 | ![1557716746695](assets/1557716746695.png) 38 | 39 | 模板类是由于考虑到可能会存放不同类型的数据和函数,所以不先定义类型,例如,当上面出现complex, 编译器会将模板类的代码中的T替换为double,创造新的一份代码,遇到complex时,会将模板类的代码中的T替换为int,在创建一份代码。可以看到模板类的代码是一份多用,很方便。 40 | 41 | ## 函数模板 42 | 43 | ![1557717032223](assets/1557717032223.png) 44 | 45 | 函数模板与类模板差不多,不同的是编译器会对function进行实参推导,不需要指明类型,只要比较大小的对象重构了`<`操作符,这样设计是很合理的,比大小的函数都是那么写,而设计stone的人,它的责任就要重载`<`操作符,把责任分开是很好的。 46 | 47 | ## 成员模板 48 | 49 | ![1560412186484](assets/1560412186484.png) 50 | 51 | 成员模板在泛型编程里用得较多,为了有更好的可扩展性,以上图为例,T1往往是U1的基类,T2往往是U2的基类,可以看下面这个例子: 52 | 53 | ![1560413833493](assets/1560413833493.png) 54 | 55 | 通过这种方法,只要传入的U1和U2的父类或者祖类是T1和T2,那么通过这样的方式可以实现继承和多态的巧妙利用,但反之就不行了。这样的方式在STL中用得很多: 56 | 57 | ![1560413886633](assets/1560413886633.png) 58 | 59 | ## namespace 60 | 61 | ![1557717432386](assets/1557717432386.png) 62 | 63 | 上面的是使用命名空间的三种写法。namespace可以防止冲突,比如在开发一个项目时,不同的人可能会命名相同的函数以及相同的参数,这时候需要不同的人给各自的代码设立不同命名空间,这样即使函数重名也不必担心了。 64 | 65 | 写测试案例可以使用namespace,防止命名冲突,很方便。 66 | 67 | ![1560404974745](assets/1560404974745.png) 68 | 69 | ## 转换函数 70 | 71 | ![1560404584181](assets/1560404584181.png) 72 | 73 | 转换函数是将对象类型转为另一个对象类型,如上面的Fraction类,它的数据为分子和分母,当Fraction类与double进行操作时,会自动调用double类型的转换函数(转换函数是operator开头,后面接要转换的类型然后加括号)。以下面的事例为例,编译器在分析double d = 4 + f过程中判断4为整数,然后继续判断f,观察到f提供了double()函数,然后会对f进行double()操作,计算得到0.6,再与4相加,最后得到double类型的4.6。(转换函数一般不会改变数据,通常要加const) 74 | 75 | ## explicit与隐式转换 76 | 77 | ![1560405283461](assets/1560405283461.png) 78 | 79 | 上图中定义了一个类,叫Fraction,类里面重载了“+”运算符,在f+4操作过程中,“4”被编译器隐式转换(通过构造函数)为Fraction对象,然后通过Fraction重载的“+”运算符参与运算。 80 | 81 | ![1560405362848](assets/1560405362848.png) 82 | 83 | 如上图所示,在Fraction中增加了double()函数,将Fraction的两个成员变量进行除法运算,然后强制转化为double类型并返回结果,在f+4重载过程中编译器将报错,可以做出如下分析: 84 | 85 | 1、首先4被隐式转化(构造函数)为Fraction对象,然后通过重载的“+”运算符与“f”进行运算; 86 | 87 | 2、首先f被转化(转换函数)为double,然后两个double进行相加; 88 | 89 | 两种路径都可行,编译不知道执行哪一种,产生ambiguous error. 下面是解决方案. 90 | 91 | ![1560406299464](assets/1560406299464.png) 92 | 93 | 通过在构造函数前面加上explicit, 就是告诉编译器, 不要在将int类型转成Fraction, 只能通过显式地进行构造.那么再执行那条语句是, 就是执行两个double相加, 但是由于double转Fraction的转换函数没有实现, 所以报错. 94 | 95 | ![1560406833869](assets/1560406833869.png) 96 | 97 | 上面是STL的例子, 对于vector的operator[], 应该返回的是bool, 但是确实返回的是另一种类型, 那么这种类型应该有转换为bool的转换函数, 果不其然. 98 | 99 | ## pointer-like classes ---- 智能指针和迭代器 100 | 101 | c++类构建出来, 所产生出来的对象可能像一个指针, 或者一个函数, 这一节是讲述像指针. 102 | 103 | ![1560407154869](assets/1560407154869.png) 104 | 105 | 上述例子是智能指针, 智能指针是对原始的指针进行了包装, 赋予更多的功能, 其中主要是对内存进行自动管理 防止内存泄漏. 由上图可以看出, 智能指针在语法上有三个很关键的地方,第一个是保存的外部指针,对应于上图的T* px,这个指针将代替传入指针进行相关传入指针的操作;第二个是重载“*”运算符,解引用,返回一个指针所指向的对象;第三个是重载“->”运算符,返回一个指针,对应于上图就是px。这里的重载"->"运算符很有意思, 返回的是px, sp->method就变成了pxmethod, 实则不是, 还是px->method, 原因是操作符"->"比较不一样, 它会一直以"->"指下去, 直到指到对象的方法或数据. 106 | 107 | 迭代器也是一种智能指针,这里也存在上面提到的智能指针的三个要素,分别对应于下图的红色字体和黄色标注部分: 108 | 109 | ![1560410852393](assets/1560410852393.png) 110 | 111 | 下面将仔细分析迭代器重载的“*”和“->”重载符: 112 | 113 | ![1560411003846](assets/1560411003846.png) 114 | 115 | 创建一个list迭代器对象,list::iterator ite;这里的list用于保存Foo对象,也就是list模板定义里的class T,operator*()返回的是一个(*node).data对象,node是__link_type类型,然而__link_type又是__list_node*类型,这里的T是Foo,所以node是__list_node*类型,所以(*node).data得到的是Foo类型的一个对象,而&(operator*())最终得到的是该Foo对象的一个地址,即返回Foo* 类型的一个指针。 116 | 117 | ## function-like classes ---- 仿函数 118 | 119 | ![1560411429029](assets/1560411429029.png) 120 | 121 | 从上图可以看到,每个仿函数都是某个类重载“()”运算符,然后变成了“仿函数”,实质还是一个类,但看起来具有函数的属性。每个仿函数其实在背后都集成了一个奇怪的类,如下图所示,这个类不用程序员手动显式声明。 122 | 123 | ![1560411583591](assets/1560411583591.png) 124 | 125 | 标准库中的仿函数也同样继承了一个奇怪的类: 126 | 127 | ![1560411683785](assets/1560411683785.png) 128 | 129 | 这个类的内容如下图所示,只是声明了一些东西,里面没有实际的变量或者函数,具体的内容将在STL中讨论。 130 | 131 | ![1560411846337](assets/1560411846337.png) 132 | 133 | ## 模板特化 134 | 135 | ![1560419363053](assets/1560419363053.png) 136 | 137 | 模板特化指的是模板中指定特定的数据类型, 这样在调用指定类型的模板时, 用的是特化的版本(如上面的hash, hash, hash), 而不是泛化的版本(原始版本hash). 138 | 139 | ## 模板偏特化 140 | 141 | 模板特化也有程度之分,可以部分类型指定,称之为偏特化, 偏特化有两种, 一种是个数的偏, 一种是范围的偏. 142 | 143 | ![1560419673831](assets/1560419673831.png) 144 | 145 | ![1560419917708](assets/1560419917708.png) 146 | 147 | 第一个图是个数上的偏, 绑定部分类型, 第二个图是范围上的偏, 原始版本C是可以指向任何类型, 下面的C特指指针类型, obj1是class C构造的, obj2是 class C构造的. 148 | 149 | ## reference 150 | 151 | reference可以看做是某个被引用变量的别名。 152 | 153 | ![1560422076723](assets/1560422076723.png) 154 | 155 | ![1560422092804](assets/1560422092804.png) 156 | 157 | ![1560422107865](assets/1560422107865.png) 158 | 159 | ## new 和 delete 160 | 161 | 在面向对象程序设计_part1谈到了一些new和delete的原理,重复的内容不再讲述,补充内容讲述了重载new和delete。这里有篇很不错的博客,也可以进行参考: 162 | 163 | ![1560761309230](assets/1560761309230.png) 164 | 165 | 上面是重载new和delete的范例,和运算符重载类似,先是operator,然后后面介绍new,new和delete有两种格式,基于单个的new,以及基于array的new,所以上面有两种写法。有中括号的是基于array的。重载new的函数参数是根据要分配的对象的size推导的。重载delete的函数参数则是被释放对象的地址。上图中的重载是直接重载全局的new和delete,影响很大,出现内存泄漏很危险。 166 | 167 | ![1560761942136](assets/1560761942136.png) 168 | 169 | 上图中则是重载单个成员的new和delete操作符,更加适用。相应地,左边执行new和delete运算,将会映射到右边的步骤。 170 | 171 | ![1560762376012](assets/1560762376012.png) 172 | 173 | 相应地,也有基于array的重载对象的。上面的分配N个Foo对象,可以看到sizeof(Foo)*N + 4,这里的4存放的是N这个数,这样到时候就知道要调用多少次构造函数和析构函数了。 174 | 175 | ![1560773568977](assets/1560773568977.png) 176 | 177 | 下面是一个更具体地事例,对Foo类重载了new和delete操作符,Foo有三个数据,12个字节,上图中主要要注意重构类的new和delete要怎么写,另外,在new前面加上::, 代表的是强制使用全局的new和delete操作符。 178 | 179 | ![1560773903634](assets/1560773903634.png) 180 | 181 | 调用左边的语句,中间是输出,上面的是无虚函数,下面是有虚函数,对于基于array的new和delete,会多出四个字节,用来保存长度信息,构造是从下到上,析构是从下到上。 182 | 183 | ![1560774047068](assets/1560774047068.png) 184 | 185 | 对于全局的,过程类似。 186 | 187 | ![1560774481410](assets/1560774481410.png) 188 | 189 | 允许重载placement new和delete。下面是事例: 190 | 191 | ![1560774759364](assets/1560774759364.png) 192 | 193 | 定义了四个operator new函数。前两个标准库已提供。 194 | 195 | ![1560775017510](assets/1560775017510.png) 196 | 197 | 对应上页幻灯片的operator delete操作。 198 | 199 | ![1560775583436](assets/1560775583436.png) 200 | 201 | 标准库中的例子,多分配内存用来进行reference count。 202 | 203 | 面向对象部分到此结束。 -------------------------------------------------------------------------------- /CPU缓存/README.md: -------------------------------------------------------------------------------- 1 | 看了耗子叔的[与程序员相关的CPU缓存知识](https://coolshell.cn/articles/20793.html),收获挺大的,缓存对于程序的性能还是影响挺大的,看了文后Scott Meyers的链接文档,也很棒,在这里放一放。 2 | 3 | -------------------------------------------------------------------------------- /CPU缓存/codedive-CPUCachesHandouts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/CPU缓存/codedive-CPUCachesHandouts.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cpp-Notes 2 | 目前记录了侯捷老师的面向对象高级编程以及C++11/14两门课程笔记, 可以在[releases](https://github.com/FangYang970206/Cpp-Notes/releases)中下载pdf进行查看。 3 | -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/README.md: -------------------------------------------------------------------------------- 1 | # 串 2 | 3 | [TOC] 4 | 5 | 串通常是由字母表组成,也就是ASCII字符集组成的有限字符串序列。这里讨论有关串的算法。将以C++语言实现, 在C++中,串指的是string这种数据结构。 6 | 7 | ## 基本术语 8 | 9 | 介绍一下串的基本术语。 10 | 11 | - length() : 对应串的长度,对应string的size或者length成员函数。 12 | - charAt() : 取串中第i个字符,对应string的[]操作符。 13 | - substr(i, k) : 从串中第i个字符开始,依次由k个字符组成的字符串,与string的substr函数接口一致。 14 | - prefix(k) : 取前缀k个字符,string中使用substr函数即可达成目标。 15 | - suffix(k) : 取后缀k个字符,string中使用substr函数即可达成目标。 16 | - concat(T) : 将串T加到串S上去,对应string里面的+=运算符。 17 | - equal(T) : 两个字符串是否相等,对应string的==运算符。 18 | - indexOf(p): 在S中寻找串P,返回第一次出现串p的位置,对应string的find函数。 19 | 20 | ![1570527790304](assets/1570527790304.png) 21 | 22 | ## 模式匹配 23 | 24 | 串匹配问题是解决许多应用(文本编辑器,数据库检索,C++模板匹配,模式识别等等)的重要技术。 25 | 26 | 这个问题有两个输入,第一个是文本(Text),第二个是模式(Pattern),目的是要在文本中寻找模式。通常而言文本要远大于模式。 27 | 28 | T : now is the time for all good **people** to come (长度为**n**) 29 | 30 | P :**people** (长度为**m**) 31 | 32 | 串匹配问题可分为四种类型: 33 | 34 | - detection : P是否出现? 35 | - location : P首次出现在哪里? 36 | - counting : P出现了多少次? 37 | - enumeration : 各出现在哪里? 38 | 39 | 显然,解决location是最重要的,如果监测到了,就表明出现了(detection),出现多少次,只要将未比较的字符串根据同样的方法求得下一次首次出现的位置,直到整个文本结束,出现在哪里只要记录位置做标记即可。 40 | 41 | 下面开始介绍串匹配算法。 42 | 43 | ### 暴力匹配 44 | 45 | 思想是自左而右,以字符为单位,依次移动模式串,直到某个位置发生匹配。 46 | 47 | ![1570533478385](assets/1570533478385.png) 48 | 49 | 这个算法最好的情况是第一次就比对成功,最好情况的上边界则是每次比对时,第一个字符都不匹配,这样就移动一格,最好情况的复杂度就等于$\Omega(n)$, n为文本的长度。最坏的情况是每次比较模式最后一个字符的时候才发现不匹配,这样就会导致最坏情况,时间复杂度为$\mathcal{O}(n \cdot m)$. 50 | 51 | C++实现版本1: 52 | 53 | ```c++ 54 | int match(string P, string T) { 55 | size_t n = T.size(), i = 0; 56 | size_t m = P.size(), j = 0; 57 | while (i < n - m + 1 && j < m) //自左向右逐次比较 58 | if ( T[i] == P[j]) { i++; j++;} // 若匹配,则转到下一对字符 59 | else { i -= j - 1; j = 0;} // 否则,T回退,P复位 60 | return i - j; 61 | } 62 | ``` 63 | 64 | C++实现版本2: 65 | 66 | ```c++ 67 | int match(string P, string T) { 68 | size_t n = T.size(), i; 69 | size_t m = P.size(), j; 70 | for ( i = 0; i < n - m + 1; i++) { //T[i]与P[0]对齐 71 | for ( j = 0; j < m; j++) //逐次匹配 72 | if ( T[i+j] != P[j]) break; //失配则转到下一位置 73 | if ( m <= j) break; //匹配成功,退出,返回i 74 | } 75 | return i; 76 | } 77 | ``` 78 | 79 | 两个实现版本的返回值都是位置信息,当i等于n - m + 1的时候说明未找到模式,否则就是找到了。 80 | 81 | ### KMP :模式记忆 82 | 83 | 暴力匹配算法存在着冗余的问题,当最坏情况时,最后一个字符匹配失败,模式串和文本串的指针都要发生回退。 84 | 85 | KMP算法的原理是利用Pattern构建一个查询表,根据查询表进行来指导移动位数,并且文本的索引不需要回退。理解这种算法我推荐阮一峰老师的[KMP博客](http://www.ruanyifeng.com/blog/2013/05/Knuth–Morris–Pratt_algorithm.html)(真心推荐看看),讲得非常清晰,非常直观。 86 | 87 | 假设你看过阮老师的博客知道原理了,现在来看next表的构建代码: 88 | 89 | ```c++ 90 | vector buildNext(string P) { //构造模式串P的next表 91 | size_t m = P.size(), j = 0; //“主”串指针 92 | vector N(m, 0); //next表 93 | int t = N[0] = -1; //模式串指针(通配符*) 94 | while ( j < m - 1 ) //j是不会减小的,j会在循环内变为m-1,此时退出 95 | if ( 0 > t || P[j] == P[t] ) { //当出现通配符也就是0>t, 当前j自加1,next表对应j为0。 96 | //当不是通配符时,比较是否相等,相等则next表对应j自加1 97 | j++; t++; 98 | N[j] = t; 99 | } 100 | else 101 | t = N[t]; //失配,根据前面得到的next,来看应该从那里开始比较,比如下面的匹配等于4的时候,e不等于c,查表知e所在的位置为0,也就是没有相同的前后缀,所以从0开始继续匹配,如果大于0,说明有共同前后缀,此时应该不从0开始,因为有共同前后缀,可以避开节省时间。 102 | return N; 103 | } 104 | ``` 105 | 106 | 这里需要注意的一点是,阮一峰老师的博客中当前next表是代表当前j的公共最大前后缀的长度,而这个实现中当前next表是代表j-1的公共最大前后缀的长度。 107 | 108 | 关于t = N[t]可以见下图,当X不匹配Y的时候,此时我们根据next表,由当前next表的值知,P[0, t)和P[j - t, j)是相同的,此时应该移动j-t,也就是从第t位开始比较,也就是N(t)的长度。有一种特殊情况需要考虑,当N(t)等于0时,此时从0开始比较,如果第0位也不等于当前j,根据性质,t此时就等于-1了,此时就进入0>t的条件,自增j,自增t,当前j没有共同前后缀。这里开始设N[0]等于-1以及t等于-1,有两层作用,第一层是为了首轮比较时,需要隔开一位比较。第二层作用是为了防止后面与第一位不相等时,可以根据-1这个条件进入if条件,防止卡死。很是巧妙。 109 | 110 | ![1570590461519](assets/1570590461519.png) 111 | 112 | 下面有一个事例: 113 | 114 | ![1570542610277](assets/1570542610277.png) 115 | 116 | 117 | 118 | ![1570543288650](assets/1570543288650.png) 119 | 120 | 有了next表的构造方法,接下来就是根据next表进行匹配了。匹配代码如下: 121 | 122 | ```c++ 123 | int match(string P, string T) { 124 | vector next = buildNext(P); 125 | size_t n = T.size(), i = 0; 126 | size_t m = P.size(), j = 0; 127 | while (j < m && i < n) 128 | if (0 > j || T[i] == P[j]) { i++; j++;} 129 | else j = next[j]; 130 | return i - j; 131 | } 132 | ``` 133 | 134 | 理解了next表的构造原理,其实就理解了匹配过程,next构造过程就是模式串的自我匹配。当失配时,如果next表的值大于0,说明有公共的前后缀,那么就不需要从0开始比较,直接从公共前后缀的后一个字符与当前文本的第j个字符开始比较。 135 | 136 | ### KMP再改进 137 | 138 | 考虑下面这个情况,明知T[4]不等于P[4]且P[1] = P[2] = P[3] = P[4],还要比对剩余的P[1], P[2], P[3], 这是没有必要的,这是需要改进next表。 139 | 140 | ![1570591941387](assets/1570591941387.png) 141 | 142 | 改进只需要把next中的`N[j] = t`换成`N[j] = ( P[++j] != P[++t] ? t : N[t] )`即可。如下所示: 143 | 144 | ![1570592311427](assets/1570592311427.png) 145 | 146 | 因为相同,所以可以直接跳过他们,更快。 147 | 148 | KMP算法的时间复杂度是$O(m + n)$, 空间复杂度是$O(m+n)$. 匹配过程令k = 2i- j,k每次循环至少加1,判断为真则i加1,判断为假,j至少减1,所以k <= 2n - 1; 同理next过程也是如此。 149 | 150 | KMP小结: 151 | 152 | - 以判断公共前后缀来进行模式串的移动,有公共前后缀,移动到前缀的下一位即可,没有公共前后缀则移动到头部。 153 | - 通过通配符来有效构造next表,表的第一位为-1,当第一位对齐不相等的时候,这时通配符匹配,使文本串(也包括模式串的自我匹配)可以移动起来,不至于卡死。 154 | - 当发生重复串的时候,跳过他们,不进行比较。 155 | 156 | ### BM算法 157 | 158 | 对于BM算法的介绍,我同样推荐看阮一峰老师的[BM博客](http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html)(真心推荐看看),讲的十分清楚。同样假设你看过博客知道原理了,就知道BM算法有两个next表,一个是坏字符(bad character)bc表,另一个是好后缀(good suffix)gs表,现在来看看如何构造这两个表。 159 | 160 | #### bc表 161 | 162 | 对于坏字符表,构造起来很简单,它是记录模式串中每种字符最后出现的位置,代码如下: 163 | 164 | ```c++ 165 | vector buildBC(string P){ 166 | vector bc(256, -1); 167 | for(size_t m = P.size(), j = 0; j < m; j++) 168 | bc[ P[j] ] = j; 169 | return bc; 170 | } 171 | ``` 172 | 173 | 坏字符移动规则: **后移位数 = 坏字符的位置- 搜索词中的上一次出现位置** 174 | 175 | 基于BM-DC的算法最好情况就是$O(n/m)$, 最坏情况是$O(m*n)$。 176 | 177 | 最好情况: 178 | 179 | ![1570613990056](assets/1570613990056.png) 180 | 181 | 最坏情况: 182 | 183 | ![1570614018748](assets/1570614018748.png) 184 | 185 | #### gs表 186 | 187 | 相比于bc表,gs表就很不好构造了。首先来看看一个概念,最大匹配后缀长度表,通过它来构建ss(suffix size)表,然后通过ss表来构造gs表。 188 | 189 | 最大匹配后缀长度的意思是在P[0,j)的所有缀中,与P的某一后缀匹配最长者。例如下面的P[0, 3) = ICE, 与末尾的ICE最长匹配,则P[0, 3)的末尾就为最长匹配长度3,RICE同理。(ss表的值就等于最大匹配长度) 190 | 191 | ![1570614568268](assets/1570614568268.png) 192 | 193 | ss表末尾的值就是整个模式串的长度,简单的想法是遍历每一个字符向后递减,与后缀开始一一比较(暴力搜索),这样做的复杂度为$O(m^2)$, 很好的做法是下面的代码(从后往前遍历),时间复杂度只有$O(m)$。 194 | 195 | ```c++ 196 | vector buildSS ( string P ) { //构造最大匹配后缀长度表:O(m) 197 | int m = P.size(); 198 | vector ss(m, 0); //Suffix Size表 199 | ss[m - 1] = m; //对最后一个字符而言,与之匹配的最长后缀就是整个P串 200 | // 以下,从倒数第二个字符起自右向左扫描P,依次计算出ss[]其余各项 201 | for ( int lo = m - 1, hi = m - 1, j = lo - 1; j >= 0; j -- ) 202 | if ( ( lo < j ) && ( ss[m - hi + j - 1] <= j - lo ) ) //情况一:该情况处于最大匹配后缀后的字符,例如,RICE中的R,I,C. 203 | ss[j] = ss[m - hi + j - 1]; //直接利用此前已计算出的ss[] 204 | else { //情况二: 遇到匹配项,依次递减进行匹配 205 | hi = j; lo = min ( lo, hi ); 206 | while ( ( 0 <= lo ) && ( P[lo] == P[m - hi + lo - 1] ) ) 207 | lo--; //逐个对比处于(lo, hi]前端的字符 208 | ss[j] = hi - lo; // 高位减去递减后的低位,得到最长匹配长度 209 | } 210 | return ss; 211 | } 212 | ``` 213 | 214 | 知道ss表后,gs表可有ss表推导出,有两种情况: 215 | 216 | ![1570620227448](assets/1570620227448.png) 217 | 218 | 对应的代码如下: 219 | 220 | ```c++ 221 | vector buildGS ( string P ) { //构造好后缀位移量表:O(m) 222 | vector ss = buildSS ( P ); //Suffix Size表 223 | size_t m = P.size(); 224 | vector gs(m, m); //Good Suffix shift table 225 | for ( size_t i = 0, j = m - 1; j < UINT_MAX; j -- ) //逆向逐一扫描各字符P[j] 226 | if ( j + 1 == ss[j] ) //若P[0, j] = P[m - j - 1, m),则 227 | while ( i < m - j - 1 ) //对于P[m - j - 1]左侧的每个字符P[i]而言 228 | gs[i++] = m - j - 1; //m - j - 1都是gs[i]的一种选择 229 | for ( size_t j = 0; j < m - 1; j ++ ) //正向扫描P[]各字符,gs[j]不断递减,直至最小 230 | gs[m - ss[j] - 1] = m - j - 1; //m - j - 1必是其gs[m - ss[j] - 1]值的一种选择 231 | return gs; 232 | } 233 | ``` 234 | 235 | #### BM_BC+GS 236 | 237 | 知道了bc表和gs表,接下来就是匹配过程了,与阮老师的博客上说的一致,取两个表的最大值。代码如下: 238 | 239 | ```c++ 240 | int match ( string P, string T ) { //Boyer-Morre算法(完全版,兼顾Bad Character与Good Suffix) 241 | vector bc = buildBC ( P ), gs = buildGS ( P ); //构造BC表和GS表 242 | size_t i = 0; //模式串相对于文本串的起始位置(初始时与文本串左对齐) 243 | while ( T.size() >= i + P.size() ) { //不断右移(距离可能不止一个字符)模式串 244 | int j = P.size() - 1; //从模式串最末尾的字符开始 245 | while ( P[j] == T[i + j] ) //自右向左比对 246 | if ( 0 > --j ) break; 247 | if ( 0 > j ) //若极大匹配后缀 == 整个模式串(说明已经完全匹配) 248 | break; //返回匹配位置 249 | else //否则,适当地移动模式串 250 | i += max ( gs[j], j - bc[ T[i + j] ] ); //位移量根据BC表和GS表选择大者 251 | } 252 | return i; 253 | } 254 | ``` 255 | 256 | 基于BM_BC+GS算法最好情况是$O(n/m)$,最坏情况由于有了gs表,变为了$O(m+n)$. 257 | 258 | ### 综合性能 259 | 260 | 各种模式匹配算法的时间复杂度如下所示: 261 | 262 | ![1570622593407](assets/1570622593407.png) -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570527790304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570527790304.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570533478385.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570533478385.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570542610277.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570542610277.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570543288650.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570543288650.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570590461519.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570590461519.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570591941387.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570591941387.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570592311427.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570592311427.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570613990056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570613990056.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570614018748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570614018748.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570614568268.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570614568268.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570620227448.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570620227448.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/assets/1570622593407.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FangYang970206/Cpp-Notes/3b776fdf359aff3557b930689b9fa8aa2a4a8be9/数据结构与算法/A/字符串/assets/1570622593407.png -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/code/BM_bc+gs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | vector buildBC(string P){ 7 | vector bc(256, -1); 8 | for(size_t m = P.size(), j = 0; j < m; j++) 9 | bc[ P[j] ] = j; 10 | return bc; 11 | } 12 | 13 | vector buildSS ( string P ) { //构造最大匹配后缀长度表:O(m) 14 | int m = P.size(); 15 | vector ss(m, 0); //Suffix Size表 16 | ss[m - 1] = m; //对最后一个字符而言,与之匹配的最长后缀就是整个P串 17 | // 以下,从倒数第二个字符起自右向左扫描P,依次计算出ss[]其余各项 18 | for ( int lo = m - 1, hi = m - 1, j = lo - 1; j >= 0; j -- ) 19 | if ( ( lo < j ) && ( ss[m - hi + j - 1] <= j - lo ) ) //情况一:该情况处于最大匹配后缀后的字符,例如,RICE中的R,I,C. 20 | ss[j] = ss[m - hi + j - 1]; //直接利用此前已计算出的ss[] 21 | else { //情况二: 遇到匹配项,依次递减进行匹配 22 | hi = j; lo = min ( lo, hi ); 23 | while ( ( 0 <= lo ) && ( P[lo] == P[m - hi + lo - 1] ) ) 24 | lo--; //逐个对比处于(lo, hi]前端的字符 25 | ss[j] = hi - lo; // 高位减去递减后的低位,得到最长匹配长度 26 | } 27 | return ss; 28 | } 29 | 30 | vector buildGS ( string P ) { //构造好后缀位移量表:O(m) 31 | vector ss = buildSS ( P ); //Suffix Size表 32 | size_t m = P.size(); 33 | vector gs(m, m); //Good Suffix shift table 34 | for ( size_t i = 0, j = m - 1; j < UINT_MAX; j -- ) //逆向逐一扫描各字符P[j] 35 | if ( j + 1 == ss[j] ) //若P[0, j] = P[m - j - 1, m),则 36 | while ( i < m - j - 1 ) //对于P[m - j - 1]左侧的每个字符P[i]而言 37 | gs[i++] = m - j - 1; //m - j - 1都是gs[i]的一种选择 38 | for ( size_t j = 0; j < m - 1; j ++ ) //正向扫描P[]各字符,gs[j]不断递减,直至最小 39 | gs[m - ss[j] - 1] = m - j - 1; //m - j - 1必是其gs[m - ss[j] - 1]值的一种选择 40 | return gs; 41 | } 42 | 43 | int match ( string P, string T ) { //Boyer-Morre算法(完全版,兼顾Bad Character与Good Suffix) 44 | vector bc = buildBC ( P ), gs = buildGS ( P ); //构造BC表和GS表 45 | size_t i = 0; //模式串相对于文本串的起始位置(初始时与文本串左对齐) 46 | while ( T.size() >= i + P.size() ) { //不断右移(距离可能不止一个字符)模式串 47 | int j = P.size() - 1; //从模式串最末尾的字符开始 48 | while ( P[j] == T[i + j] ) //自右向左比对 49 | if ( 0 > --j ) break; 50 | if ( 0 > j ) //若极大匹配后缀 == 整个模式串(说明已经完全匹配) 51 | break; //返回匹配位置 52 | else //否则,适当地移动模式串 53 | i += max ( gs[j], j - bc[ T[i + j] ] ); //位移量根据BC表和GS表选择大者 54 | } 55 | return i; 56 | } -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/code/Brute-force-version1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | 6 | int match(string P, string T) { 7 | size_t n = T.size(), i = 0; 8 | size_t m = P.size(), j = 0; 9 | while (i < n && j < m) 10 | if ( T[i] == P[j]) { i++; j++;} 11 | else { i -= j - 1; j = 0;} 12 | return i - j; 13 | } -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/code/Brute-force-version2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | 6 | int match(string P, string T) { 7 | size_t n = T.size(), i; 8 | size_t m = P.size(), j; 9 | for ( i = 0; i < n - m + 1; i++) { 10 | for ( j = 0; j < m; j++) 11 | if ( T[i+j] != P[j]) break; 12 | if ( m <= j) break; 13 | } 14 | return i; 15 | } -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/code/KMP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | vector buildNext(string P) { //构造模式串P的next表 7 | size_t m = P.size(), j = 0; //“主”串指针 8 | vector N(m, 0); //next表 9 | int t = N[0] = -1; //模式串指针(通配符*) 10 | while ( j < m - 1 ) //j是不会减小的,j会在循环内变为m-1,此时退出 11 | if ( 0 > t || P[j] == P[t] ) { //当出现通配符也就是0>t, 当前j自加1,next表对应j为0。 12 | //当不是通配符时,比较是否相等,相等则next表对应j自加1 13 | j++; t++; 14 | N[j] = t; 15 | } 16 | else 17 | t = N[t]; //失配,根据前面得到的next,来看应该从哪里开始比较,比如下面的匹配等于4的时候, 18 | // e不等于c,查表知e所在的位置为0,也就是没有相同的前后缀,所以从0开始继续匹配, 19 | // 如果大于0,说明有共同前后缀,此时应该不从0开始,因为有共同前后缀,可以避开节省时间。 20 | return N; 21 | } 22 | 23 | int match(string P, string T) { 24 | vector next = buildNext(P); 25 | size_t n = T.size(), i = 0; 26 | size_t m = P.size(), j = 0; 27 | while (j < m && i < n) 28 | if (0 > j || T[i] == P[j]) { i++; j++;} 29 | else j = next[j]; 30 | return i - j; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /数据结构与算法/A/字符串/code/KMP_Pro.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | vector buildNext(string P) { //构造模式串P的next表 7 | size_t m = P.size(), j = 0; //“主”串指针 8 | vector N(m, 0); //next表 9 | int t = N[0] = -1; //模式串指针(通配符*) 10 | while ( j < m - 1 ) //j是不会减小的,j会在循环内变为m-1,此时退出 11 | if ( 0 > t || P[j] == P[t] ) { //当出现通配符也就是0>t, 当前j自加1,next表对应j为0。 12 | //当不是通配符时,比较是否相等,相等则next表对应j自加1 13 | N[j] = ( P[++j] != P[++t] ? t : N[t] ) 14 | } 15 | else 16 | t = N[t]; //失配,根据前面得到的next,来看应该从哪里开始比较,比如下面的匹配等于4的时候, 17 | // e不等于c,查表知e所在的位置为0,也就是没有相同的前后缀,所以从0开始继续匹配, 18 | // 如果大于0,说明有共同前后缀,此时应该不从0开始,因为有共同前后缀,可以避开节省时间。 19 | return N; 20 | } 21 | 22 | int match(string P, string T) { 23 | vector next = buildNext(P); 24 | size_t n = T.size(), i = 0; 25 | size_t m = P.size(), j = 0; 26 | while (j < m && i < n) 27 | if (0 > j || T[i] == P[j]) { i++; j++;} 28 | else j = next[j]; 29 | return i - j; 30 | } --------------------------------------------------------------------------------