├── .gitignore ├── README.md ├── day01 ├── 01namesapce │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02struct │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03union │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04enum │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05overload │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05overload2 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 06defaultparameter │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day02 ├── 01new │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02reference │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03reference02 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 04cast │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day03 ├── 01oop │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02separate │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── clock.cpp │ ├── clock.h │ ├── main.cpp │ ├── student.cpp │ └── student.h ├── 03constructor │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04explicit │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 05copyconstructor │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day04 ├── 01initlist │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02initlist-const-ref │ ├── CMakeLists.txt │ └── main.cpp ├── 03this │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04returnthis │ ├── CMakeLists.txt │ └── main.cpp ├── 05question │ ├── CMakeLists.txt │ └── main.cpp ├── 06constfunc │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 07destructor │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 08deepcopy │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day05 ├── 01staticmember │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02singleton │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03singleton02 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04singletoncounter │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05memberptr │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 06complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 07complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 08complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 09complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 10complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day06 ├── 01complex │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02array │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03square │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04ptr │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05integer │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 06new │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 07human │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 08ppp │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day07 ├── 01order │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02hide │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03mulinherit │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04mulinherit02 │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 05diamond │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day08 ├── 01world │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02shape │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03override │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04poly │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05oppoly │ ├── CMakeLists.txt │ └── main.cpp ├── 06pdf │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 07vptr │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 08dc │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 09type │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 10vd │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 11absfactory │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day09 ├── 01reterr │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02jmperr │ ├── CMakeLists.txt │ └── main.cpp ├── 03experr │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04stdexp │ ├── CMakeLists.txt │ └── main.cpp ├── 05expdeclare │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 06continuethrow │ ├── CMakeLists.txt │ └── main.cpp ├── 07consexp │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 08finally │ ├── CMakeLists.txt │ └── main.cpp └── 10descexp │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── day10 ├── 01open │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 02state │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 03format │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 04manip │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 05get │ ├── CMakeLists.txt │ ├── README.md │ ├── get.txt │ └── main.cpp ├── 06xor │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── 07seek │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── 08sstring │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp └── docs └── pics ├── absfactory.png ├── access.png ├── continue_throw.png ├── deepcopy.png ├── diamond.png ├── diamondvirtual.png ├── exception.png ├── handleexp.png ├── hollywood.png ├── inherit01.png ├── inherit02.png ├── ioclass.png ├── iofmtfunc.png ├── iosbase.png ├── iostate.png ├── manipnoparam.png ├── manipsingleparam.png ├── memlayout.png ├── memptr.png ├── mulin.png ├── noexception.png ├── object.png ├── opoverload_array.png ├── opoverload_func.png ├── opoverload_ptr.png ├── separate.png ├── shadowcopy.png ├── static.png ├── stdexp.png ├── vdes1.png ├── vdes2.png ├── vinherit.png └── vtable.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | build/ 4 | cmake-build-debug/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp-toturial -------------------------------------------------------------------------------- /day01/01namesapce/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01namespace) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(namesapce main.cpp) 7 | -------------------------------------------------------------------------------- /day01/01namesapce/README.md: -------------------------------------------------------------------------------- 1 | # 名字空间 2 | 3 | ## 为什么需要名字空间 4 | 5 | * 划分逻辑单元 6 | * 避免名字冲突 7 | 8 | ## 什么是名字空间 9 | 10 | * 名字空间定义 11 | * 名字空间合并 12 | * 声明定义分开 13 | 14 | ## 怎样用名字空间 15 | 16 | * 作用域限定符 17 | * 名字空间指令 18 | * 名字空间声明 19 | 20 | ## 无名名字空间 21 | 22 | * 不属于任何有名名字空间的标识符,隶属于无名命名空间 23 | * 无名命名空间的成员,直接通过`::`访问 24 | 25 | ## 名字空间嵌套与名字空间别名 26 | 27 | * 内层标识符隐藏外层同名标识符 28 | * 嵌套的名字空间需要逐层分解 29 | * 可通过名字空间别名简化书写 30 | * `namespace ns_four = ns1::ns2::ns3::ns4;` -------------------------------------------------------------------------------- /day01/01namesapce/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | float g_balance = 1234; 4 | 5 | namespace abc { 6 | float g_balance = 0.0; 7 | 8 | void save(float money) { 9 | g_balance += money; 10 | } 11 | 12 | void withdraw(float money) { 13 | g_balance -= money; 14 | } 15 | 16 | void show() { 17 | std::cout << g_balance << std::endl; 18 | std::cout << ::g_balance << std::endl; 19 | } 20 | } 21 | 22 | namespace cbc { 23 | float g_balance = 0.0; 24 | 25 | void save(float money) { 26 | g_balance += money; 27 | } 28 | 29 | void withdraw(float money) { 30 | g_balance -= money; 31 | } 32 | 33 | void show() { 34 | std::cout << g_balance << std::endl; 35 | } 36 | } 37 | 38 | namespace abc { 39 | void foo() { 40 | std::cout << "abc::foo" << std::endl; 41 | } 42 | void bar(); 43 | } 44 | void abc::bar() { 45 | std::cout << "abc::bar" << std::endl; 46 | } 47 | 48 | 49 | namespace ns1 { 50 | int x = 100; 51 | namespace ns2 { 52 | int x = 200; 53 | namespace ns3 { 54 | int x = 300; 55 | } 56 | } 57 | } 58 | 59 | int main() { 60 | abc::show(); 61 | abc::save(10000); 62 | abc::withdraw(5000); 63 | abc::show(); 64 | 65 | cbc::save(8000); 66 | cbc::withdraw(2000); 67 | cbc::show(); 68 | 69 | abc::foo(); 70 | abc::bar(); 71 | 72 | /* 73 | using namespace abc; // 名字空间指令(全部可见) 74 | foo(); 75 | bar(); 76 | */ 77 | using abc::foo; // 名字空间声明(部分可见) 78 | foo(); 79 | abc::bar(); 80 | 81 | std::cout << ns1::x << std::endl; 82 | std::cout << ns1::ns2::x << std::endl; 83 | std::cout << ns1::ns2::ns3::x << std::endl; 84 | // 名字空间别名 85 | namespace ns_three = ns1::ns2::ns3; 86 | std::cout << ns_three::x << std::endl; 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /day01/02struct/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02struct) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02struct main.cpp) 7 | -------------------------------------------------------------------------------- /day01/02struct/README.md: -------------------------------------------------------------------------------- 1 | # 结构 2 | 3 | ## C++结构 4 | 5 | * 声明或定义结构型变量,可以省略`struct`关键字 6 | * 可以定义成员函数,在结构体的成员函数中可以直接访问该结构体的成员变量,无需通过`.`或`->` 7 | 8 | -------------------------------------------------------------------------------- /day01/02struct/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | struct Student{ 5 | // 成员变量 6 | char name[256]; 7 | int age; 8 | 9 | // 成员函数 10 | void who() { 11 | cout << "I am " << name << ", " << age << " years old." << endl; 12 | } 13 | }; 14 | 15 | 16 | 17 | int main() { 18 | Student s1 = {"zhangfei", 25}; 19 | // .: 成员访问运算符 20 | cout << s1.name << ' ' << s1.age << endl; 21 | 22 | Student* ps = &s1; 23 | // ->: 间接成员访问运算符 24 | cout << ps->name << ' ' << ps->age << endl; 25 | 26 | s1.who(); 27 | ps->who(); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /day01/03union/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03union) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03union main.cpp) 7 | -------------------------------------------------------------------------------- /day01/03union/README.md: -------------------------------------------------------------------------------- 1 | # 联合 2 | 3 | ## C++联合 4 | 5 | * 声明或定义联合型变量,可以省略`union`关键字 6 | * 支持匿名联合 7 | -------------------------------------------------------------------------------- /day01/03union/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | union { 5 | int i; 6 | char c[4]; 7 | }; 8 | i = 0x12345678; 9 | printf("%x %x %x %x\n", c[0],c[1],c[2],c[3]); 10 | i = 65536; 11 | printf("%x %x %x %x\n", c[0],c[1],c[2],c[3]); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /day01/04enum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04enum) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04enum main.cpp) 7 | -------------------------------------------------------------------------------- /day01/04enum/README.md: -------------------------------------------------------------------------------- 1 | # 枚举 2 | 3 | ## C++枚举 4 | 5 | * 声明或定义枚举型变量,可以省略`enum`关键字 6 | * 独立的类型,和整型之间不能隐式地相互转换 7 | -------------------------------------------------------------------------------- /day01/04enum/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main() { 5 | enum COLOR { 6 | RED, // 0 7 | GREEN, // 1 8 | BLUE // 2 9 | }; 10 | 11 | COLOR color = RED; 12 | cout << color << endl; 13 | color = BLUE; 14 | cout << color << endl; 15 | 16 | // 因为int比枚举的值域大 17 | int i = GREEN; 18 | cout << i << endl; 19 | int j = color; 20 | cout << j << endl; 21 | 22 | // 大值域赋值给小值域,有风险 23 | // color = 0; // error 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /day01/05overload/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05overload) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05overload main.cpp) 7 | -------------------------------------------------------------------------------- /day01/05overload/README.md: -------------------------------------------------------------------------------- 1 | # 重载 2 | 3 | ## 重载关系 4 | 5 | * 同一作用域中,函数名相同,参数列表不同的函数 6 | * 只有同一作用域中的同名函数才涉及重载问题,不同作用域中同名函数遵循标识符隐藏原则 7 | 8 | ## 重载解析 9 | 10 | * 完全匹配 > 常量转换 > 升级转换 > 标准转换 > 自定义转换 > 省略号匹配 11 | * 函数指针的类型决定其匹配的重载版本 12 | * 重载是通过C++换名实现的 13 | * 通过`extern "C"`可以要求C++编译器按照C方式处理函数接口,即不做换名,当然也就无法重载 14 | 15 | -------------------------------------------------------------------------------- /day01/05overload/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | void foo() { 6 | cout << "foo()" << endl; 7 | } 8 | 9 | void foo(int a) { 10 | cout << "foo(int)" << endl; 11 | } 12 | // 重载与返回类型无关 13 | // int foo(int a) {} // error 14 | 15 | void bar(int a, float b) { 16 | cout << "bar(int,float)" << endl; 17 | } 18 | // 参数表不同指类型不同,而不是参数名 19 | // void bar(int b, float a) {} // error 20 | 21 | // 参数个数不同 22 | void bar(int a) { 23 | cout << "bar(int)" << endl; 24 | } 25 | 26 | // 参数顺序不同 27 | void bar(float a, int b) { 28 | cout << "bar(float, int)" << endl; 29 | } 30 | 31 | 32 | namespace ns1 { 33 | void nsfoo(int a) { 34 | cout << "ns1::foo(int)" << endl; 35 | } 36 | } 37 | 38 | namespace ns2 { 39 | void nsfoo(float a) { 40 | cout << "ns2::foo(float)" << endl; 41 | } 42 | } 43 | 44 | int main() { 45 | foo(); 46 | foo(10); 47 | bar(10, 1.2f); 48 | bar(10); 49 | bar(1.2f, 10); 50 | 51 | using namespace ns1; 52 | using namespace ns2; 53 | // 构成重载关系 54 | nsfoo(10); 55 | nsfoo(1.2f); 56 | 57 | using ns1::nsfoo; 58 | // 不构成重载关系 59 | nsfoo(10); // ns1::foo 60 | nsfoo(1.2f); // ns1::foo 61 | 62 | using ns2::nsfoo; 63 | // 构成重载关系 64 | nsfoo(10); // ns1::foo 65 | nsfoo(1.2f); // ns2::foo 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /day01/05overload2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05overload2) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05overload2 main.cpp) 7 | -------------------------------------------------------------------------------- /day01/05overload2/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 000000000000122d T _Z3barc bar(char) 3 | 000000000000126c T _Z3bari bar(int) 4 | 00000000000012a9 T _Z3barx bar(loong long) 5 | 00000000000011a9 T _Z3fooPci foo(char*, int) 6 | 00000000000011ea T _Z3fooPKcc foo(const char*, char) 7 | 00000000000012e7 T _Z3humdz hum(double, ...) 8 | 0000000000001376 T _Z3humiPv hum(int, void*) 9 | ``` 10 | 可以发现,只要参数表(类型、顺序、个数)不同,C++换名之后一定不同 11 | -------------------------------------------------------------------------------- /day01/05overload2/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | void foo(char* p, int n) { cout << "foo(1)" << endl; } 5 | void foo(const char* p, char c) { cout << "foo(2)" << endl; } 6 | 7 | void bar(char c) { cout << "bar(1)" << endl; } 8 | void bar(int n) { cout << "bar(2)" << endl; } 9 | void bar(long long l) { cout << "bar(3)" << endl; } 10 | 11 | void hum(double f, ...) { cout << "hum(1)" << endl; } 12 | void hum(int n, void* p) { cout << "hum(2)" << endl; } 13 | 14 | int main() { 15 | // 编译器: 16 | // 安全原则 17 | // 工作量最小原则 18 | 19 | char* p; 20 | char c; 21 | // foo(1): 升级转换, char -> int 22 | // foo(2): 常量转换, char* -> const char* 23 | foo(p, c); // f(2) 24 | 25 | short s; 26 | // bar(1): 标准转换,short -> char 27 | // bar(2): 升级转换, short -> int,增加2个字节,工作量小 28 | // bar(3): 升级转换, short -> long long,要增加6个字节,工作量大 29 | bar(s); // bar(2) 30 | 31 | double f; 32 | void* pv; 33 | // hum(1): 省略号匹配是最差的选择 34 | // hum(2): 标准转换 35 | hum(f, pv); // hum(2) 36 | 37 | void(*pfunc)(char*,int) = foo; // 指定了只能调foo(char*,char) 38 | // pfunc((const char*)NULL, (char)0); // error 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /day01/06defaultparameter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06defaultparameter) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06defaultparameter main.cpp) 7 | -------------------------------------------------------------------------------- /day01/06defaultparameter/README.md: -------------------------------------------------------------------------------- 1 | # 缺省参数和哑元 2 | 3 | * 可以为函数的参数指定缺省值,调用该函数时,若未指定实参,则与该实参相对应的形参取缺省值 4 | * 函数参数的缺省值只能在函数声明中指定 5 | * 如果函数的某一个参数具有缺省值,那么该参数后面所有的参数必须具有缺省值 6 | * 不要因为使用缺省参数而导致重载歧义 7 | * 只指定类型而不指定名称的函数参数,称为哑元(dummy) 8 | * 保证函数的向下兼容 9 | * 形成函数的重载版本 10 | 11 | -------------------------------------------------------------------------------- /day01/06defaultparameter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | void foo(int a = 0, char b = 'X', float c = 4.56f, const char* d = "hello") { 6 | cout << a << ' ' << b << ' ' << c << ' ' << d << endl; 7 | } 8 | 9 | // 哑元 10 | void bar(int) { 11 | cout << "bar(int)" << endl; 12 | } 13 | 14 | int main() { 15 | foo(10, 'A', 1.23f, "world"); 16 | // 缺省参数是编译期确定 17 | foo(10, 'A', 1.23f); // foo(10, 'A', 1.23f, "hello"); 18 | foo(10, 'A'); // foo(10, 'A', 4,56f, "hello"); 19 | foo(10); // foo(10, 'X', 4.56f, "hello"); 20 | foo(); // foo(0, 'X', 4.56f, "hello"); 21 | 22 | bar(10); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /day02/01new/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01new) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01new main.cpp) 7 | -------------------------------------------------------------------------------- /day02/01new/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 内联 4 | 5 | * 内联就是用函数已被编译好的二进制代码,替换对该函数的调用指令 6 | * 内联在保证函数特性的同时,避免了函数调用开销 7 | * 注意内联与有参宏(宏函数)的区别 8 | 9 | * 内联会使可执行文件的体积和进程代码的内存变大,因此只有频繁调用的简单函数才适合内联 10 | * 若函数在类声明中直接定义,则自动被优化为内联,否则可在其声明处加上inline关键字 11 | * inline关键字仅表示期望该函数被优化为内联,但是否适合内联则完全由编译器决定 12 | * 稀少被调用的复杂函数和递归函数都不适合内联 13 | 14 | 15 | # 动态内存分配 16 | 17 | * 继续使用标准C库函数`malloc/calloc/realloc/free` 18 | * 使用new/delete操作符在堆中分配/释放内存 19 | ```c++ 20 | int* pi = new int; 21 | delete pi; 22 | ``` 23 | * 在分配内存时同时初始化 24 | ```c++ 25 | int* pi = new int(100); 26 | delete pi; 27 | ``` 28 | * 以数组方式new的也是以数组方式delete 29 | ```c++ 30 | int* pi = new int[4]{1,2}; 31 | delete[] pi; 32 | ``` 33 | * 通过new操作符分配N维数组,返回N-1维数组指针 34 | ```c++ 35 | int(*prow)[4] = new int[3][4]; 36 | delete[] prow; 37 | 38 | int(*ppage)[4][5] = new int[3][4][5]; 39 | delete[] ppage; 40 | ``` 41 | * 不能通过delete操作符释放已释放过的内存 42 | * delete野指针后果未定义,delete空指针安全 43 | * 内存分配失败,new操作符抛出`bad_alloc`异常 44 | * 定位分配 45 | * new(指针)类型(初值) 46 | * 在一个已分配的内存空间中创建对象 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /day02/01new/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | int main() { 6 | // int* p1 = (int*)malloc(sizeof(int)); 7 | int* p1 = new int; 8 | *p1 = 1234; 9 | cout << *p1 << endl; 10 | // free(p1); 11 | delete p1; 12 | 13 | p1 = new int(1000); 14 | cout << *p1 << endl; 15 | delete p1; 16 | 17 | p1 = NULL; // NULL不会产生double free 18 | delete p1; 19 | 20 | p1 = new int[4]{0,1,2,3}; 21 | cout << p1[0] << ' ' << p1[1] << ' ' << p1[2] << ' ' << p1[3] << endl; 22 | delete[] p1; 23 | 24 | int(*p2)[4] = new int[3][4]{1,2, 3, 4, 25 | 5, 6, 7, 8, 26 | 9, 10, 11, 12}; 27 | for (size_t i = 0; i < 3; i++) { 28 | for (size_t j = 0; j < 4; j++) { 29 | cout << p2[i][j] << ' '; 30 | } 31 | cout << endl; 32 | } 33 | 34 | delete[] p2; 35 | /* 36 | try { 37 | p1 = new int[0xFFFFFFFF]; 38 | } catch(exception& ex) { 39 | cout << "new failed: " << ex.what() << endl; 40 | return -1; 41 | } 42 | delete[] p1; 43 | */ 44 | 45 | short buf[4] = {0}; 46 | // 在buf中所指向的内存(栈)中分配内存,因此不需要delete 47 | p1 = new(buf)int(0x12345678); 48 | cout << showbase << hex << *p1 << endl; 49 | for (size_t i = 0; i< 4; ++i) { 50 | cout << (int)buf[i] << ' '; 51 | } 52 | cout << endl; 53 | cout << "buf: " < 2 | using namespace std; 3 | 4 | void swap1(int a, int b) { 5 | int c = a; 6 | a = b; 7 | b = c; 8 | } 9 | 10 | void swap2(int* a, int *b) { 11 | int c = *a; 12 | *a = *b; 13 | *b = c; 14 | } 15 | 16 | void swap3(int& a, int& b) { 17 | int c = a; 18 | a = b; 19 | b = c; 20 | } 21 | 22 | void swap4(const char* x, const char* y) { 23 | const char* z = x; 24 | x = y; 25 | y = z; 26 | } 27 | 28 | void swap5(const char** x, const char** y) { 29 | const char* z = *x; 30 | *x = *y; 31 | *y = z; 32 | } 33 | 34 | void swap6(const char*&x, const char*& y) { 35 | const char* z = x; 36 | x = y; 37 | y = z; 38 | } 39 | 40 | struct Student { 41 | char name[256]; 42 | int age; 43 | }; 44 | 45 | // 不用复制实参 46 | // 常引用防止对实参的意外修改 47 | void print(const Student& s) { 48 | // cout << s.name << ' ' << ++s.age << endl; 49 | cout << s.name << ' ' << s.age << endl; 50 | } 51 | // 接受常量型实参 52 | void show(const int& i) { 53 | cout << i << endl; 54 | } 55 | 56 | int main() { 57 | int a = 10; 58 | int& b = a; 59 | cout << b << endl; 60 | ++b; 61 | cout << a << endl; 62 | 63 | // int& c; // error 64 | int d = 20; 65 | b = d; 66 | cout << a << ' ' << b << endl; 67 | // int& c = NULL; // error 68 | const int& c = NULL; 69 | cout << c << endl; 70 | 71 | int n1 = 10, n2 = 20; 72 | swap1(n1, n2); 73 | cout << n1 << ' ' << n2 << endl; // 10 20 74 | swap2(&n1, &n2); 75 | cout << n1 << ' ' << n2 << endl; // 20 10 76 | swap3(n1, n2); 77 | cout << n1 << ' ' << n2 << endl; // 10 20 78 | 79 | const char* x = "hello"; 80 | const char* y = "world"; 81 | swap4(x, y); 82 | cout << x << ' ' << y << endl; // hello world 83 | swap5(&x, &y); 84 | cout << x << ' ' << y << endl; // world hello 85 | swap6(x, y); 86 | cout << x << ' ' << y << endl; // hello world 87 | 88 | Student s = Student{"zhangfei", 20}; 89 | print(s); 90 | show(a); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /day02/03reference02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03reference02) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03reference02 main.cpp) 7 | -------------------------------------------------------------------------------- /day02/03reference02/README.md: -------------------------------------------------------------------------------- 1 | # 引用 2 | * 引用型返回值,从函数中返回引用,一定要保证在函数返回后,改引用的目标依然有效 3 | * 返回左值 4 | * 可以返回全局、静态乃至成员变量的引用 5 | * 可以返回在堆中动态创建的对象的引用 6 | * 可以返回调用对象自身的引用 7 | * 可以返回引用型参数本身 8 | * 不能返回局部变量的引用 9 | * 常引用型返回值 10 | * 返回右值 11 | 12 | ```c++ 13 | // 左值:值可以改变 14 | int a; 15 | a = 10; 16 | 17 | // 右值:值不能改变 18 | // 10 = 20; // error 19 | // 10++; // error 20 | int a = 10, b = 20; 21 | // (a + b) = 30; // error,表达式的值是又值 22 | 23 | int g_x; 24 | int foo() { return g_x; } 25 | // foo() = 10; // error,函数返回值拷贝到匿名变量中,再返回匿名变量,而匿名变量是右值 26 | int& foo2() { return g_x; } 27 | foo2() = 10; // ok,直接引用g_x而不是匿名变量 28 | ``` 29 | 综上所述:返回引用后,所引用的目标要有效。 30 | 31 | * 在实现层面,引用就是指针,但在语言层面,引用不是实体类型(在内存中占空间),因此与指针存在明显的差别。 32 | * 指针可以不初始化,其目标可在初始化后随意变更(除非是指针常量),而引用必须初始化,且一旦初始化就无法变更其目标 33 | * 存在空指针,不存在空引用 34 | * 存在指向指针的指针,不存在引用的引用 35 | * 存在指针的引用,不存在引用的指针 36 | * 存在指针数组,不存在引用数组,但存在数组引用 -------------------------------------------------------------------------------- /day02/03reference02/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | int g_x; 6 | 7 | int& foo() { return g_x; } 8 | const int& foo2() { return g_x; } 9 | 10 | int& bar() { 11 | int a = 100; 12 | // 返回局部变量的引用 13 | return a; 14 | } 15 | 16 | int& hum() { 17 | int b = 200; 18 | return b; 19 | } 20 | 21 | struct Student { 22 | char name[256]; 23 | int age; 24 | 25 | int& howOld() { 26 | return age; 27 | } 28 | }; 29 | 30 | int& fun(int& r) { return r; } 31 | 32 | /* 33 | int& fun(int r) { // ERROR 34 | return r; 35 | } 36 | int fun(int r) { // OK 37 | return r; 38 | } 39 | int fun(int& r) { // OK 40 | return r; 41 | } 42 | */ 43 | int main() { 44 | foo() = 10; 45 | cout << g_x << endl; 46 | ++foo(); 47 | cout << g_x << endl; 48 | foo()++; 49 | cout << g_x << endl; 50 | foo() += 10; 51 | cout << g_x << endl; 52 | 53 | // int& a = bar(); 54 | // cout << a << endl; 55 | // hum(); 56 | // cout << a << endl; 57 | 58 | Student s = {"zhengfei", 20}; 59 | // age在s中,而s在main函数的栈中 60 | // 只要main函数不返回,就不用担心age被释放 61 | int age = s.howOld(); 62 | cout << age << endl; 63 | 64 | int y = 1000; 65 | int& r = fun(y); 66 | ++r; 67 | cout << y << endl; 68 | 69 | cout << foo2() << endl; 70 | // foo2()++; // ERROR 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /day02/04cast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04cast) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04cast main.cpp) 7 | -------------------------------------------------------------------------------- /day02/04cast/README.md: -------------------------------------------------------------------------------- 1 | # 显式类型转换 2 | 3 | * C风格的显式类型转换 4 | * (目标类型变量)源类型变量 5 | * C++风格的显式类型转换 6 | * 目标类型变量(源类型变量) 7 | * 静态类型转换 8 | * `static_cast<目标类型>(源类型)` 9 | * 目标类型必须可以隐式地转换为源类型。即目标类型到源类型可以隐式转换,那么源类型到目标类型就可以静态转换。 10 | * 隐式类型转换的逆转换 11 | * 自定义类型转换 12 | * 动态类型转换 13 | * `dynamic_cast<目标类型>(源类型变量)` 14 | * 多台父子类指针或引用之间的转换 15 | * 常类型转换 16 | * `const_cast<目标类型>(源类型变量)` 17 | * 去除指针或引用上的const属性 18 | * 重解释类型转换 19 | * `reinterpret_cast<目标类型>(源类型变量)` 20 | * 任意类型的指针或引用之间的转换 21 | * 任意类型的指针和整型之间的转换 22 | 23 | # 来自C++社区的建议 24 | 25 | * 用`const`取代常量宏 26 | 27 | ```c++ 28 | #define PI 3.14 29 | 30 | const double PI = 3.14; 31 | ``` 32 | 33 | * 用`enum`取代唯一标识宏 34 | 35 | ```c++ 36 | #define RED 0 37 | #define GREEN 1 38 | #define BLUE 2 39 | #define YELLOW 2 // 可能重复 40 | 41 | enum COLOR { RED, GREEN, BLUE, YELLOW }; 42 | ``` 43 | 44 | * 用`inline`取代参数宏 45 | * 用`namespace`取代条件编译解决名字冲突 46 | * 变量随用随声明,并立即初始化 47 | * 少用`malloc/free`,`new/delete`更好 48 | * 避免使用`void*`、指针算术、联合和强制类型转换 49 | * 用`string`和`STL`容器取代低级数组 50 | * 树立面向对象的编程思想,程序设计的过程是用类描述世界的过程,而非用函数处理数据的过程 51 | 52 | -------------------------------------------------------------------------------- /day02/04cast/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | int main() { 6 | int* pi; 7 | // int*能隐式地转为void* 8 | void* pv = pi; 9 | pi = static_cast(pv); 10 | 11 | // doule不能隐式地转为void* 12 | // double d = static_cast(pv); 13 | 14 | const int* p1; 15 | int* p2 = const_cast(p1); 16 | 17 | int a = 10; 18 | const int& r1 = a; 19 | int& r2 = const_cast(r1); 20 | r2 = 20; 21 | cout << a << endl; 22 | 23 | // 编译器常量优化:c有长属性,所用出现c的地方都用100替换 24 | // 如果想改,可以使用volatile关键字。告诉编译器可以通过变量名意外的方式修改。 25 | const int c = 100; 26 | int* pc = const_cast(&c); 27 | *pc = 200; 28 | cout << pc << ' ' << &c << endl; 29 | cout << *pc << endl; // 200 30 | cout << c << endl; // 100 31 | 32 | const volatile int d = 300; 33 | int* pd = const_cast(&d); 34 | *pd = 400; 35 | cout << pd << ' ' << &c << endl; 36 | cout << *pd << endl; // 400 37 | cout << d << endl; // 400 38 | 39 | struct Student { 40 | char name[256]; 41 | int age; 42 | }; 43 | Student s = {"zhangfei", 20}; 44 | char* ps = reinterpret_cast(&s); 45 | cout << ps << endl; // zhangfei 46 | int* pn = reinterpret_cast(ps + 256); 47 | cout << *pn << endl; // 20 48 | uint64_t n = reinterpret_cast(pn); 49 | cout << showbase << hex << n << ' ' << pn << endl; 50 | cout << dec << *reinterpret_cast(n) << endl; // 20 51 | 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /day03/01oop/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01oop) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01oop main.cpp) 7 | -------------------------------------------------------------------------------- /day03/01oop/README.md: -------------------------------------------------------------------------------- 1 | # 面向对象 2 | 3 | ## 什么是面向对象 4 | 5 | * 万物皆对象,这是人类面对世界最朴素最自然的感觉、想法和观点 6 | * 把大型软件看成是一个由对象组成的社会 7 | * 对象拥有足够的智能,能够理解来自其他对象的信息,并以适当的行为做出反应 8 | * 对象能够从高层对象继承属性和行为,并允许底层对象从自己继承属性和行为等 9 | * 编写程序的过程就是描述对象属性和行为的过程,凭借这种能力是问题域和解域获得最大成都的统一 10 | * 面向对象三大要件:封装、继承、多态 11 | 12 | ## 为什么要面向对象 13 | 14 | * 相比于分而治之的结构化程序设计,强调大处着眼的面向对象程序设计思想,更适合于开发大型软件 15 | * 得益于数据抽象、代码复用等面向对象的固有特征,软件开发的效率获得极大的提升,成本却大幅降低 16 | * 面向对象技术在数据库、网络通信、图形界面等领域的广泛应用,已催生出各种设计模式和应用框架 17 | * 面向对象技术的表现如此出众,以至于那些原本并不直接支持面向对象的语言(如C),也越来越多的通过各种方法模拟一些面向对象的软件架构 18 | 19 | ## 怎样面向对象 20 | 21 | * 至少成我一种面向对象的程序设计语言,如C++ 22 | * 深入理解封装、继承、多态等面向对象的重要概念 23 | * 精通一种元语言(如UML),在概念层次上描述设计 24 | * 学习设计模式,源自多年成功经验的积累和总结 25 | 26 | ## 类和对象 27 | 28 | * 拥有相同属性和行为的对象被成一组,即一个类 29 | * 类可用于表达那些不能直接与内置类型建立自然映射关系的逻辑对象 30 | * 类是一种用户自定义的复合数据类型,即包括表达属性的成员变量,也包括表达行为的成员函数 31 | * 类是现实世界的抽象,对象是类在虚拟世界的实力 32 | 33 | ## 类的定义与实例化 34 | 35 | * 类的一般形式 36 | 37 | ``` 38 | class/struct 类名: 继承方式 基类, ... { 39 | 访问控制限定符: 40 | 类名(形参表):成员变量(初值), ... { 41 | 函数体; 42 | } 43 | ~类名() { 44 | 函数体; 45 | } 46 | 返回类型 函数名(形参表) 常属性 异常说明{ 47 | 函数体; 48 | } 49 | 数据类型 变量名; 50 | }; 51 | ``` 52 | 53 | * 访问控制限定符 54 | * `public`:公有成员,谁都能访问 55 | * `protected`:保护成员,只有自己和子类可以访问 56 | * `private`:私有成员,只有自己能访问 57 | * 在C++中,类(struct)和结构(struct)已经没有本质的差别,唯一的不同在于: 58 | * 类的缺省访问控制属性为私有(private) 59 | * 结构的缺省访问控制属性为公有(public) 60 | * 访问控制限定符仅作用于类,而非作用于对象,因此同一个类的不同对象,可以互相访问非公有部分 61 | * 对不同成员的访问控制属性加以区分,体现了C++作为面向对象程序设计语言的封装特性 62 | 63 | ![Access Control](../../docs/pics/access.png) 64 | 65 | * 构造函数 66 | * 函数名和类名相同,且没有返回类型 67 | * 在创建对象时自动被调用,且仅被调用一次 68 | * 对象定义语句 69 | * new操作符 70 | * 为成员变量赋初值,分配资源,设置对象的初始状态 71 | * 对象创建过程 72 | * 为整个对象分配内存空间 73 | * 以构造实参调用构造函数 74 | * 构造基类部分 75 | * 构造成员变量 76 | * 执行构造代码 77 | 78 | * 类的声明与实现分开 79 | ``` 80 | class 类名 { 81 | 返回类型 函数名(形参表); 82 | }; 83 | 84 | 返回类型 类名::函数名(形参表) { 85 | 函数体; 86 | } 87 | ``` 88 | * 对象的创建于销毁 89 | 90 | * 在栈总创建单个对象 91 | 92 | ``` 93 | 类名 对象; 94 | 类型 对象(实参表); 95 | ``` 96 | 97 | * 在栈中创建数组对象 98 | 99 | ``` 100 | 类名 对象数组[元素个数]; 101 | 类型 对象数组[元素个数] = {类型(实参表), ... }; 102 | 类名 对象数组[] = {类型(实参表), ... }; 103 | ``` 104 | * 在堆中创建/销毁对象 105 | 106 | ``` 107 | 类名* 对象指针 = new 类名; 108 | 类名* 对象指针 = new 类名(); 109 | 类名* 对象指针 = new 类名(实参表); 110 | delete 对象指针; 111 | ``` 112 | 113 | * 在堆中创建/销毁数组 114 | 115 | ``` 116 | 类名* 对象数组指针 = new 类名[元素个数]; 117 | 类名* 对象数组指针 = new 类名[元素个数]{类名(实参表), ...}; 118 | delete[] 对象数组指针; 119 | ``` 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /day03/01oop/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Student { 5 | public: 6 | Student(const string &name = "anonymous", int age = 18); 7 | // 在类内实现的成员函数缺省就是内联的,如果是类外实现如需要内联,则需显式加上inline关键字 8 | inline void who(); 9 | /* 10 | Student(const string& name, int age) { 11 | m_name = name; 12 | m_age = age; 13 | } 14 | void who() { 15 | cout << "I am " << m_name << ", " << m_age << " years old" << endl; 16 | } 17 | */ 18 | void learn(const string& course) { 19 | cout << "I am learning " << course << endl; 20 | } 21 | void setName(const string& name) { 22 | if (name == "250") { 23 | cout << "invalid name" << endl; 24 | return; 25 | } 26 | m_name = name; 27 | } 28 | void setAge(int age) { 29 | if (age <= 0) { 30 | cout << "invalid age" << endl; 31 | return; 32 | } 33 | m_age = age; 34 | } 35 | void foo(Student& s) { 36 | // 可以访问同一个类的对象的非公有部分 37 | s.m_name = "250"; 38 | } 39 | private: 40 | string m_name; 41 | int m_age; 42 | }; 43 | 44 | // 类的声明与实现分开 45 | Student::Student(const string& name, int age) { 46 | m_name = name; 47 | m_age = age; 48 | } 49 | void Student::who() { 50 | cout << "I am " << m_name << ", " << m_age << " years old" << endl; 51 | } 52 | 53 | int main() { 54 | Student s1("zhangfei", 20); 55 | s1.setName("250"); 56 | s1.setAge(-10); 57 | s1.who(); 58 | // s1.setName("zhangfei"); 59 | // s1.setAge(30); 60 | // s1.who(); 61 | s1.learn("C++"); 62 | 63 | Student s2("zhaoyun", 20); 64 | s1.foo(s2); 65 | s2.who(); 66 | 67 | Student s3("guanyu", 40); 68 | s3.who(); 69 | 70 | // 编译器会认为在声明函数 71 | // Student s4(); 72 | Student s4; // 别加() 73 | s4.who(); 74 | 75 | Student sa[3]; 76 | for (Student& s : sa) { 77 | s.who(); 78 | } 79 | Student sc[3] = { 80 | Student("zhangfei", 18), 81 | Student("zhaoyun", 17), 82 | Student("guanyu", 30) 83 | }; 84 | for(auto& s: sc) { 85 | s.who(); 86 | } 87 | 88 | 89 | Student* ps = new Student; 90 | ps->who(); 91 | auto ps2 = new Student(); // 前面有new编译器会不会认为是声明函数 92 | ps2->who(); 93 | auto ps3 = new Student("hello", 10); 94 | ps3->who(); 95 | delete ps; 96 | delete ps2; 97 | delete ps3; 98 | 99 | Student* parr1 = new Student[3]; 100 | delete[] parr1; 101 | 102 | auto parr2 = new Student[3]{Student("hello"), 103 | Student("world"), 104 | Student("haha")}; 105 | delete[] parr2; 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /day03/02separate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02seperate) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02seperate main.cpp 7 | student.cpp 8 | student.h 9 | clock.cpp 10 | clock.h) 11 | -------------------------------------------------------------------------------- /day03/02separate/Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | RM=rm 3 | CFLAGS=-c -Wall -I. 4 | 5 | all:student 6 | 7 | student: student.o main.o 8 | $(CC) $^ -o $@ 9 | 10 | student.o: student.cpp 11 | $(CC) $(CFLAGS) $^ 12 | 13 | main.o: main.cpp 14 | $(CC) $(CFLAGS) $^ 15 | 16 | clean: 17 | $(RM) *.o student 18 | 19 | 20 | -------------------------------------------------------------------------------- /day03/02separate/README.md: -------------------------------------------------------------------------------- 1 | * 将类的声明、实现与使用放到不同的文件里 2 | 3 | ![Separate](../../docs/pics/separate.png) 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /day03/02separate/clock.cpp: -------------------------------------------------------------------------------- 1 | #include "clock.h" 2 | -------------------------------------------------------------------------------- /day03/02separate/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef INC_CLOCK_H 2 | #define INC_CLOCK_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | class Clock { 11 | public: 12 | Clock(time_t now) { 13 | tm* local = localtime(&now); 14 | m_hour = local->tm_hour; 15 | m_min = local->tm_min; 16 | m_sec = local->tm_sec; 17 | } 18 | 19 | void run() { 20 | while(true) { 21 | show(); 22 | tick(); 23 | } 24 | } 25 | private: 26 | void show() { 27 | cout << '\r' 28 | << setfill('0') << setw(2) << m_hour << ':' 29 | << setfill('0') << setw(2) << m_min << ':' 30 | << setfill('0') << setw(2) << m_sec << flush; 31 | } 32 | void tick() { 33 | sleep(1); 34 | if (++m_sec % 60 == 0) { 35 | m_sec = 0; 36 | if (++m_min % 60 == 0) { 37 | m_min = 0; 38 | if (++m_hour % 24 == 0) { 39 | m_hour = 0; 40 | } 41 | } 42 | } 43 | } 44 | 45 | int m_hour; 46 | int m_min; 47 | int m_sec; 48 | }; 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /day03/02separate/main.cpp: -------------------------------------------------------------------------------- 1 | #include "student.h" 2 | #include "clock.h" 3 | 4 | int main() { 5 | Student s; 6 | s.setName("Zhangfei"); 7 | s.setAge(30); 8 | s.who(); 9 | s.learn("Sun Zi Bing Fa"); 10 | 11 | Clock clock(time(NULL)); 12 | clock.run(); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /day03/02separate/student.cpp: -------------------------------------------------------------------------------- 1 | #include "student.h" 2 | 3 | Student::Student(const string& name, int age){ 4 | m_name = name; 5 | m_age = age; 6 | } 7 | 8 | void Student::who() { 9 | cout << "I am " << m_name << ", " << m_age << " years old" << endl; 10 | } 11 | 12 | void Student::learn(const string& course) { 13 | cout << "I am learning " << course << endl; 14 | } 15 | 16 | void Student::setName(const string& name) { 17 | if (name == "250") { 18 | cout << "invalid name" << endl; 19 | return; 20 | } 21 | m_name = name; 22 | } 23 | 24 | void Student::setAge(int age) { 25 | if (age <= 0) { 26 | cout << "invalid age" << endl; 27 | return; 28 | } 29 | m_age = age; 30 | } 31 | -------------------------------------------------------------------------------- /day03/02separate/student.h: -------------------------------------------------------------------------------- 1 | #ifndef INC_STUDENT_H 2 | #define INC_STUDENT_H 3 | 4 | #include 5 | using namespace std; 6 | 7 | class Student { 8 | public: 9 | Student(const string& name="hello", int age = 20); 10 | void who(); 11 | void learn(const string& course); 12 | void setName(const string& name); 13 | void setAge(int age); 14 | 15 | private: 16 | string m_name; 17 | int m_age; 18 | }; 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /day03/03constructor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03constructor) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03constructor main.cpp 7 | ) 8 | -------------------------------------------------------------------------------- /day03/03constructor/README.md: -------------------------------------------------------------------------------- 1 | # 构造函数与初始化表 2 | 3 | * 构造函数可以重载 4 | * 构造函数可以通过参数表的差别化形成重载 5 | * 重载的构造函数通过构造实参的类型选择匹配 6 | * 不同的构造函数版本表示不同的对象创建方式 7 | * 使用缺省参数可以减少构造函数重载版本数量 8 | * 某些构造函数具有特殊的意义 9 | * 缺省构造函数:按缺省方式构造 10 | * 类型转换构造函数:从不同类型的对象构造 11 | * 拷贝构造函数:从相同类型的对象构造 12 | 13 | * 缺省构造函数 14 | * 缺省构造函数亦称无参构造函数,但其未必真的没有任何参数,有一个参数构造函数的每个参数都提供一个缺省值,同样可以达到无参构造函数的效果 15 | * 如果一个类没有定义任何构造函数,那么编译器会为其提供一个缺省构造函数 16 | * 对于基本类型的成员变量,不做初始化 17 | * 对类类型的成员变量和基类子对象,调用其相应的缺省构造函数初始化 18 | * 对于已定义至少一个构造函数的类,无论其构造函数是否带有参数,编译器都不会为其提供缺省构造函数 19 | * 有时必须自己定义缺省构造函数,即使它什么也不做,尤其是在使用数组或容器的时候,某些基于早期C++标准的编译器不支持对象数组的初始化语法 20 | * 有时必须为一个类提供缺省构造函数,仅仅因为它可能作为另一个类的子对象而被缺省构造 21 | * 若子对象不宜缺省构造,则需要为父对象提供缺省构造函数,并在其中显式地以非缺省方式构造该子对象 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /day03/03constructor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Student { 5 | public: 6 | Student() { 7 | m_name = "default"; 8 | m_age = 18; 9 | } 10 | 11 | Student(const string& name) { 12 | m_name = name; 13 | m_age = 18; 14 | } 15 | 16 | Student(int age) { 17 | m_name = "default"; 18 | m_age = age; 19 | } 20 | 21 | Student(const string& name, int age) { 22 | m_name = name; 23 | m_age = age; 24 | } 25 | 26 | void show() { 27 | cout << m_name << ": " << m_age << endl; 28 | } 29 | 30 | private: 31 | string m_name; 32 | int m_age; 33 | }; 34 | 35 | class A { 36 | public: 37 | // A(){} 38 | A(int a) {} 39 | }; 40 | 41 | class B { 42 | public: 43 | B(): m_a(0) {} 44 | private: 45 | A m_a; 46 | }; 47 | 48 | int main() { 49 | Student s1; 50 | s1.show(); 51 | 52 | Student s2("zhangfei"); 53 | s2.show(); 54 | 55 | Student s3(30); 56 | s3.show(); 57 | 58 | Student s4("zhaoyun", 25); 59 | s4.show(); 60 | 61 | Student sarr[3]; // 必须要有缺省构造函数 62 | 63 | B b; 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /day03/04explicit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04explicit) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04explicit main.cpp) 7 | -------------------------------------------------------------------------------- /day03/04explicit/README.md: -------------------------------------------------------------------------------- 1 | # 类型转换构造函数 2 | 3 | * 在目标类型中,可接收单个源类型对象实参的构造函数,支持从源类型到目标类型的隐式类型转换 4 | 5 | ``` 6 | class 目标类型{ 7 | 目标类型(const 源类型& src) { ... } 8 | }; 9 | ``` 10 | 11 | * 通过explicit关键字,可强制这种构造函数实现的类型转换必须显示地进行 12 | 13 | ``` 14 | class 目标类型{ 15 | explicit 目标类型(const 源类型& src) { ... } 16 | }; 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /day03/04explicit/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Dog; 5 | 6 | class Cat { 7 | public: 8 | Cat(const string& name) { 9 | m_name = name; 10 | } 11 | explicit Cat(const Dog& dog); 12 | // Cat(const Dog& dog) { 13 | // m_name = dog.m_name; 14 | // } 15 | void talk() { 16 | cout << m_name << ": 喵喵" << endl; 17 | } 18 | private: 19 | string m_name; 20 | }; 21 | 22 | class Dog { 23 | public: 24 | Dog(const string& name) { 25 | m_name = name; 26 | } 27 | void talk() { 28 | cout << m_name << ": 汪汪" << endl; 29 | } 30 | private: 31 | string m_name; 32 | friend class Cat; // 友元 33 | }; 34 | 35 | 36 | Cat::Cat(const Dog& dog) { 37 | m_name = dog.m_name; 38 | } 39 | 40 | int main() { 41 | Dog dog("小白"); 42 | dog.talk(); 43 | 44 | // 编译器会去寻找有没有能接受Dog的构造函数 45 | // Cat cat = dog; 46 | Cat cat = static_cast(dog); 47 | cat.talk(); 48 | 49 | Cat cat2("大橘"); 50 | cat2.talk(); 51 | cat2 = static_cast(dog); 52 | cat2.talk(); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /day03/05copyconstructor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05copyconstructor) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05copyconstructor main.cpp) 7 | -------------------------------------------------------------------------------- /day03/05copyconstructor/README.md: -------------------------------------------------------------------------------- 1 | # 拷贝构造函数 2 | 3 | * 形如 4 | ``` 5 | class 类名{ 6 | 类名(const 类名& that) { ... } 7 | }; 8 | ``` 9 | 的构造函数称为拷贝构造函数,用于从一个已定义的对象构造其同类型的副本,即对象克隆。 10 | 11 | * 如果一个类没有定义拷贝构造函数,那么编译器会为其提供一个缺省拷贝构造函数 12 | * 对于基本类型的成员变量,按字节复制 13 | * 对类类型成员变量和基类子对象,调用相应类型的拷贝构造函数 14 | * 如果自己定义了拷贝构造函数,编译器将不再提供缺省的拷贝构造函数,这时所有与成员复制有关的操作都必须在自定义的拷贝构造函数中编写代码完成 15 | * 若缺省的拷贝构造函数不能满足要求,则需自己定义 16 | * 拷贝构造的时机 17 | * 用已定义对象作为同类型对象的构造实参 18 | * 以对象的形式向函数传递参数 19 | * 从函数中返回对象 20 | * 某些拷贝构造过程会因编译优化而被省略 21 | * 自定义构造函数和系统定义构造函数 22 | 23 | | 自定义构造函数 | 系统定义构造函数 | 24 | |-----------------|----------------------| 25 | | 无 | 缺省构造函数
缺省拷贝构造函数 | 26 | | 除拷贝构造函数外的任何构造函数 | 缺省拷贝构造函数 | 27 | | 拷贝构造函数 | 无 | 28 | 29 | 所有系统定义的构造函数,其访问控制属性均为public 30 | 31 | -------------------------------------------------------------------------------- /day03/05copyconstructor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Student { 5 | public: 6 | Student(const string& name, int age) { 7 | m_name = name; 8 | m_age = age; 9 | } 10 | 11 | Student(const Student& that) { 12 | m_name = that.m_name; 13 | m_age = that.m_age; 14 | } 15 | 16 | void show() { 17 | cout << m_name << ": " << m_age << endl; 18 | } 19 | private: 20 | string m_name; 21 | int m_age; 22 | }; 23 | 24 | 25 | int main() { 26 | Student s1("zhangfei", 30); 27 | s1.show(); 28 | 29 | Student s2(s1); 30 | s2.show(); 31 | 32 | Student s3 = s2; 33 | s3.show(); 34 | 35 | return 0; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /day04/01initlist/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01initlist) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01initlist main.cpp) 7 | -------------------------------------------------------------------------------- /day04/01initlist/README.md: -------------------------------------------------------------------------------- 1 | # 初始化表 2 | 3 | * 通过在类的构造函数中使用初始化表,指明该类的成员变量如何被初始化 4 | * 数组和结构型成员变量需要用花括号`{}`初始化 5 | * 类的类类型成员变量和基类子对象,必须在初始化表中显式初始化,否则将调用相应类型的缺省构造函数初始化 6 | * 类的常量型和引用型成员变量,必须在初始化表中显式初始化 7 | * 类的成员变量按其在类中的声明顺序依次被初始化,而与其在初始化表中的顺序无关 8 | 9 | -------------------------------------------------------------------------------- /day04/01initlist/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | class Date{ 4 | public: 5 | Date(int year, int month, int day) : 6 | m_year(year), 7 | m_month(month), 8 | m_day(day) 9 | {} 10 | 11 | void print() { 12 | cout << m_year << '-' << m_month << '-' << m_day << endl; 13 | } 14 | int year() { return m_year; } 15 | int month() { return m_month; } 16 | int day() { return m_day; } 17 | private: 18 | int m_year; 19 | int m_month; 20 | int m_day; 21 | }; 22 | 23 | class Student{ 24 | public: 25 | /* 26 | // 两步:先构造,后赋值 27 | Student(const string& name = "", int age = 0) { 28 | cout << "name: " << m_name << endl; // "" 29 | cout << "age: " << m_age << endl; // 未定义的值 30 | m_name = name; 31 | m_age = age; 32 | }*/ 33 | 34 | // 一步:构造同时就赋值,性能有所提升 35 | Student(const string& name = "", int age = 0, int year = 2000, int month = 1, int day=1) : 36 | m_name(name), 37 | m_age(age), 38 | m_arr{10,20,30}, 39 | m_birth{2011, 11, 11}, 40 | m_born(year, month, day) 41 | {} 42 | 43 | void who() { 44 | cout << m_name << ", " << m_age << ", " << endl; 45 | for(auto& i:m_arr) { 46 | cout << i << ' '; 47 | } 48 | cout << endl; 49 | cout << m_birth.year << '-' << m_birth.month << '-' << m_birth.day << endl; 50 | cout << m_born.year() << '-' << m_born.month() << '-' << m_born.day() << endl; 51 | } 52 | 53 | 54 | private: 55 | // 所有成员变量的定义都是在构造函数中做的,而不是在声明中做的 56 | // string m_name = "hello"; // ERROR 57 | // int m_age = 18; // ERROR 58 | // int m_arr[3] = { 10, 20, 30 }; // ERROR 59 | 60 | string m_name; 61 | int m_age; 62 | int m_arr[3]; 63 | struct { 64 | int year; 65 | int month; 66 | int day; 67 | } m_birth; 68 | Date m_born; 69 | }; 70 | 71 | 72 | 73 | int main() { 74 | Student s1("zhengfei", 35); 75 | s1.who(); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /day04/02initlist-const-ref/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02initlist_const_ref) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02initlist_const_ref main.cpp) 7 | -------------------------------------------------------------------------------- /day04/02initlist-const-ref/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class A { 7 | public: 8 | A() : m_r(* new int(100)), m_c(200) { 9 | // 引用和常量要在定义时初始化,如下两行不是初始化而是赋值 10 | // m_r = *new int(100); 11 | // m_c = 200; 12 | } 13 | 14 | void print() { 15 | cout << m_r << ", " << m_c << endl; 16 | } 17 | 18 | private: 19 | int& m_r; 20 | const int m_c; 21 | }; 22 | 23 | class Dummy{ 24 | public: 25 | // 根据声明顺序,先初始化m_len再初始化m_str 26 | // Dummy(const char* psz) : m_str(psz), m_len(m_str.length()) {} 27 | 28 | // 降低耦合,减少对成员变量的依赖 29 | Dummy(const char* psz) : m_str(psz), m_len(strlen(psz ? psz : "")) {} 30 | int len() { return m_len; } 31 | string str() { return m_str; } 32 | 33 | private: 34 | size_t m_len; 35 | string m_str; 36 | }; 37 | 38 | int main() { 39 | A a; 40 | a.print(); 41 | 42 | Dummy d("hello"); 43 | cout << d.len() << " " << d.str() << endl; 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /day04/03this/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03this) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03this main.cpp) 7 | -------------------------------------------------------------------------------- /day04/03this/README.md: -------------------------------------------------------------------------------- 1 | # This指针 2 | 3 | ## C++对象模型 4 | 5 | * 相同类型的不同对象各自拥有独立的成员变量实例 6 | * 相同类型的不同对象彼此共享同一份成员函数代码 7 | * 在代码区中,为相同类型的不同对象所共享的成员,如何区分所访问的成员变量隶属于哪个对象 8 | * 类的每个成员函数、构造函数和析构函数,都有一个隐藏的指针类型参数`this`,指向调用该成员函数、正在被构造或正在被析构的对象,这就是`this指针` 9 | * 在类的成员函数、构造函数和析构函数中,对所有成员的访问,都是同多`this指针`进行的 10 | 11 | ![object](../../docs/pics/object.png) 12 | 13 | ## This指针的应用 14 | 15 | * 多数情况下,程序并不需要显式地使用this指针 16 | * 有时为了方便,将一个类的某个成员变量与该类构造函数的相应参数取相同标识符,这是在构造函数内部,可通过this指针将二者加以区分 17 | * 返回基于this指针的自身引用,以支持串连调用 18 | * 将this指针作为函数的参数,已实现对象交互 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /day04/03this/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class User { 5 | public: 6 | User(const string& m_name = "", int m_age = 0) { 7 | cout << "构造函数: " << this << endl; 8 | // 函数的作用域小,会隐藏类相同的变量 9 | // m_name = m_name; 10 | // m_age = m_age; 11 | this->m_name = m_name; 12 | this->m_age = m_age; 13 | } 14 | void print() { 15 | // 编译器加了this,可以直接用 16 | cout << this->m_name << ", " << this->m_age << endl; 17 | } 18 | /* 19 | void print(User* this) { 20 | cout << this->m_name << ", " << this->m_age << endl; 21 | } 22 | */ 23 | 24 | private: 25 | string m_name; 26 | int m_age; 27 | }; 28 | 29 | 30 | int main() { 31 | User user1("zhangfei", 35); 32 | cout << "user1: " << &user1 << endl; 33 | user1.print(); // print(&user1); 34 | 35 | User user2("zhaoyun", 25); 36 | cout << "user2: " << &user2 << endl; 37 | user2.print(); // print(&user2); 38 | 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /day04/04returnthis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04returnthis) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04returnthis main.cpp) 7 | -------------------------------------------------------------------------------- /day04/04returnthis/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Counter { 5 | public: 6 | Counter(int data=0) : m_data(data) {} 7 | 8 | Counter& add() { 9 | ++m_data; 10 | return *this; 11 | } 12 | 13 | void print() { cout << m_data << endl; } 14 | void destroy() { delete this; } 15 | 16 | private: 17 | int m_data; 18 | }; 19 | 20 | int main() { 21 | Counter c; 22 | //c.add(); 23 | //c.add(); 24 | //c.add(); 25 | c.add().add().add(); 26 | c.print(); 27 | 28 | Counter* pc = new Counter; 29 | pc->destroy(); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /day04/05question/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05question) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05question main.cpp) 7 | -------------------------------------------------------------------------------- /day04/05question/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Student; 5 | 6 | class Teacher { 7 | public: 8 | /* 9 | void educate(Student* student) { 10 | student->ask("什么事this指针?", this); 11 | cout << "回答:" << m_answer << endl; 12 | } 13 | */ 14 | void educate(Student* student); 15 | 16 | void reply(const string& answer) { 17 | m_answer = answer; 18 | } 19 | private: 20 | string m_answer; 21 | }; 22 | 23 | class Student { 24 | public: 25 | void ask(const string& question, Teacher* teacher) { 26 | cout << "问题: " << question << endl; 27 | teacher->reply("不知道!"); 28 | } 29 | }; 30 | 31 | // 将educate放到Student的面 32 | void Teacher::educate(Student* student) { 33 | student->ask("什么事this指针?", this); 34 | cout << "回答:" << m_answer << endl; 35 | } 36 | 37 | int main() { 38 | Teacher t; 39 | Student s; 40 | t.educate(&s); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /day04/06constfunc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06constfunc) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06constfunc main.cpp) 7 | -------------------------------------------------------------------------------- /day04/06constfunc/README.md: -------------------------------------------------------------------------------- 1 | # 常函数与常对象 2 | 3 | * 在类成员函数的形参表后,函数体之前加上`const`关键字,该成员函数的this指针即具有长属性,这样的成员函数被称为常函数 4 | 5 | ``` 6 | class 类名{ 7 | 返回类型 函数名(形参表) const { 8 | 函数体; 9 | } 10 | }; 11 | ``` 12 | 13 | * 在常函数内部无法修改成员变量的值,除非该成员变量被`mutable`关键字修饰 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /day04/06constfunc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | class A{ 6 | public: 7 | A(int a = 0) : m_data(a), m_mtb(0) {} 8 | void print() const { 9 | cout << m_data << endl; 10 | m_mtb = 200; // const_cast(this)->m_mtb = 100; 11 | const_cast(this)->m_mtb = 300; 12 | cout << m_mtb << endl; 13 | } 14 | /* 15 | void print(const A* this) { 16 | cout << this->m_data << endl; 17 | } 18 | */ 19 | void foo() { cout << "非常函数" << endl; } 20 | void foo() const { cout << "常函数" << endl; } 21 | private: 22 | int m_data; 23 | mutable int m_mtb; 24 | }; 25 | 26 | int main() { 27 | A a(100); 28 | a.print(); 29 | a.foo(); 30 | 31 | const A& cr = a; 32 | cr.foo(); 33 | const A* cp = &a; 34 | cp->foo(); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /day04/07destructor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(07destructor) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07destructor main.cpp) 7 | -------------------------------------------------------------------------------- /day04/07destructor/README.md: -------------------------------------------------------------------------------- 1 | # 析构函数 2 | 3 | * 析构函数的函数名就是在类名签名加`~`,没有返回类型也没有参数,不能重载 4 | * 在销毁对象时自动被调用,且仅被调用一次 5 | * 对象离开作用域 6 | * delete操作符 7 | * 释放在对象的构造过程或声明周期内所获的资源 8 | * 其功能并不局限在释放资源上,它可以执行任何类的设计者希望在最后一次使用对象之后执行的动作 9 | * 通常情况下,若对象在其声明周期的最终时刻,并不持有任何动态分配的资源,可以不定义析构函数 10 | * 如果一个类没有定义析构函数,那么编译器会为其提供一个缺省析构函数 11 | * 对基本类型的成员变量,什么也不做 12 | * 对类类型的成员变量和基类子对象,调用相应类型的析构函数 13 | * 对象的销毁过程 14 | * 调用析构函数 15 | * 执行析构代码 16 | * 析构成员变量 17 | * 析构基类部分 18 | * 释放这个对象所占的内存空间 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /day04/07destructor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | A() { 7 | cout << "A构造" << endl; 8 | } 9 | ~A() { 10 | cout << "A析构" << endl; 11 | } 12 | }; 13 | 14 | class B { 15 | public: 16 | B():m_aa(new A) { 17 | cout << "B构造" << endl; 18 | } 19 | ~B() { 20 | cout << "B析构" << endl; 21 | 22 | // 指针是基本类型,它指向的内存要手动释放 23 | delete m_aa; 24 | } 25 | private: 26 | A m_a; // 类类型的变量会自动调用相应类型的析构函数 27 | A* m_aa; 28 | }; 29 | 30 | int main() { 31 | B b; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /day04/08deepcopy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08deepcopy) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08deepcopy main.cpp) 7 | -------------------------------------------------------------------------------- /day04/08deepcopy/README.md: -------------------------------------------------------------------------------- 1 | # 拷贝构造与拷贝赋值 2 | 3 | * 缺省方式的拷贝构造和拷贝赋值,对包括指针在内的基本类型成员变量按字节复制,导致浅拷贝问题 4 | 5 | ![shadowcopy](../../docs/pics/shadowcopy.png) 6 | 7 | * 为了获得完整意义上的对象副本,必须自己定义拷贝构造与拷贝赋值,针对指针类型成员变量做深拷贝 8 | 9 | ![deepcopy](../../docs/pics/deepcopy.png) 10 | 11 | * 相对于拷贝构造,拷贝赋值需要做更多的工作 12 | * 避免自复制 13 | * 分配新资源 14 | * 拷贝新资源 15 | * 释放旧资源 16 | * 返回自引用 17 | 18 | * 尽量复用拷贝构造函数和析构函数中的代码 19 | * 拷贝构造:分配新资源、拷贝新内容 20 | * 析构函数:释放旧资源 21 | 22 | * 无论是拷贝构造还是拷贝赋值,其缺省实现对类类型成员变量和基类子对象,都会调用相应类型的拷贝构造和拷贝赋值运算符函数,而不是简单地按字节复制,因此应尽量避免使用指针型成员变量 23 | * 尽量通过引用或指针向函数传递对象型参数,既可以降低参数传递的开销,也能减少拷贝构造的机会 24 | * 出于具体原因的考虑,确实无法实现完整意义上的拷贝构造和拷贝赋值,可将它们私有化,以防误用 25 | * 如果一个类提供了自定义的拷贝构造函数,就没有理由不提供实现相同逻辑的拷贝赋值运算符函数 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /day04/08deepcopy/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Integer { 5 | public: 6 | Integer(int val): m_val(new int(val)) {} 7 | 8 | Integer(const Integer& that) { 9 | m_val = new int(*that.m_val); 10 | } 11 | 12 | Integer& operator=(const Integer& that) { 13 | if (&that != this) { // 避免自赋值 14 | /* 15 | delete m_val; // 释放旧资源 16 | m_val = new int(*that.m_val); // 分配新资源,拷贝新内容 17 | */ 18 | Integer temp(that); 19 | swap(m_val, temp.m_val); 20 | } 21 | 22 | return *this; // 返回自引用 23 | } 24 | 25 | ~Integer() { 26 | delete m_val; 27 | } 28 | 29 | void print() const { 30 | cout << *m_val << endl; 31 | } 32 | 33 | private: 34 | int* m_val; 35 | }; 36 | 37 | 38 | int main() { 39 | Integer t1(100); 40 | t1.print(); 41 | 42 | Integer t2(t1); 43 | t2.print(); 44 | 45 | Integer t3(300); 46 | t2 = t3; // i2.operator=(t3) 47 | t2.print(); 48 | 49 | 50 | return 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /day05/01staticmember/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01staticmember) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01staticmember main.cpp) 7 | -------------------------------------------------------------------------------- /day05/01staticmember/README.md: -------------------------------------------------------------------------------- 1 | # 静态成员 2 | 3 | * 静态成员属于类而不属于对象 4 | * 静态成员变量不包含在对象实例中,进程级生命期 5 | * 静态成员函数没有`this`指针,也没有常属性 6 | * 静态成员依然受类作用域和访问控制限定符的约束 7 | * 静态成员变量的定义和初始化,只能在类的外部而不能在构造函数中进程 8 | * 静态成员变量为该类的所有对象实例所共享 9 | * 访问静态成员,既可以通过类也可以通过对象 10 | * 静态成员函数只能访问静态成员,而非静态成员函数既可以访问静态成员,也可以访问非静态成员 11 | * 事实上,类的静态成员变量和静态成员函数,更像是普通的全局变量和全局函数,只是多了一层类作用域和访问控制属性的限制,相当于具有成员访问性的全局变量和全局函数 12 | 13 | ![static](../../docs/pics/static.png) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day05/01staticmember/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | void bar() { 7 | cout << m_i << endl; 8 | cout << m_d << endl; 9 | } 10 | 11 | static void foo() { 12 | cout << "A::foo()" << endl; 13 | cout << m_i << endl; 14 | // cout << m_d << endl; // ERROR 15 | } 16 | 17 | static int m_i; 18 | 19 | private: 20 | double m_d; 21 | }; 22 | 23 | int A::m_i = 1234; 24 | 25 | 26 | class B { 27 | public: 28 | void foo() {} // 代码区 29 | static int m_data; // 数据区 30 | }; 31 | 32 | int main() { 33 | cout << A::m_i << endl; 34 | 35 | A a1; 36 | cout << sizeof(a1) << endl; 37 | cout << a1.m_i < 2 | using namespace std; 3 | 4 | 5 | class Singleton { 6 | public: 7 | static Singleton& getInstance() { 8 | return s_instance; 9 | } 10 | private: 11 | Singleton(){} 12 | Singleton(const Singleton& that){} 13 | 14 | // 静态变量不在类中,计算大小不计算它 15 | static Singleton s_instance; 16 | }; 17 | 18 | Singleton Singleton::s_instance; 19 | 20 | int main() { 21 | // Singleton s; // ERROR 22 | // Singleton* ps = new Singleton; // ERROR 23 | Singleton& s1 = Singleton::getInstance(); 24 | Singleton& s2 = Singleton::getInstance(); 25 | Singleton& s3 = Singleton::getInstance(); 26 | cout << &s1 << " " << &s2 << " " << &s3 << endl; 27 | 28 | // Singleton s4(s3); // ERROR 29 | 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /day05/03singleton02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03singleton02) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03singleton02 main.cpp) 7 | -------------------------------------------------------------------------------- /day05/03singleton02/README.md: -------------------------------------------------------------------------------- 1 | # 单例模式 2 | ## 懒汉型 3 | 用的时候才创建实例 -------------------------------------------------------------------------------- /day05/03singleton02/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | class Singleton { 6 | public: 7 | static Singleton& getInstance() { 8 | if (!s_instance) { 9 | s_instance = new Singleton; 10 | } 11 | return *s_instance; 12 | } 13 | private: 14 | Singleton(){} 15 | Singleton(const Singleton& that){} 16 | 17 | static Singleton* s_instance; 18 | }; 19 | 20 | Singleton* Singleton::s_instance = nullptr; 21 | 22 | int main() { 23 | Singleton& s1 = Singleton::getInstance(); 24 | Singleton& s2 = Singleton::getInstance(); 25 | Singleton& s3 = Singleton::getInstance(); 26 | cout << &s1 << " " << &s2 << " " << &s3 << endl; 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /day05/04singletoncounter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04singletoncounter) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04singletoncounter main.cpp) 7 | -------------------------------------------------------------------------------- /day05/04singletoncounter/README.md: -------------------------------------------------------------------------------- 1 | # 单例模式 2 | ## 懒汉型 3 | * 引用计数 4 | * 线程安全 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /day05/04singletoncounter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | 5 | class Singleton { 6 | public: 7 | static Singleton& getInstance() { 8 | if (!s_instance) { 9 | pthread_mutex_lock(&s_mutex); 10 | if (!s_instance) { 11 | s_instance = new Singleton; 12 | } 13 | pthread_mutex_unlock(&s_mutex); 14 | } 15 | ++s_counter; 16 | return *s_instance; 17 | } 18 | 19 | void releaseInstance() { 20 | if (s_counter && --s_counter == 0) { 21 | delete this; 22 | } 23 | } 24 | 25 | private: 26 | Singleton() { 27 | cout << "Singleton::Singleton()" << endl; 28 | } 29 | Singleton(const Singleton& that) {} 30 | ~Singleton() { 31 | cout << "Singleton::~Singleton()" << endl; 32 | s_instance = nullptr; 33 | } 34 | static size_t s_counter; 35 | static Singleton* s_instance; 36 | static pthread_mutex_t s_mutex; 37 | }; 38 | pthread_mutex_t Singleton::s_mutex = PTHREAD_MUTEX_INITIALIZER; 39 | size_t Singleton::s_counter = 0; 40 | Singleton* Singleton::s_instance = nullptr; 41 | 42 | int main() { 43 | Singleton& s1 = Singleton::getInstance(); 44 | Singleton& s2 = Singleton::getInstance(); 45 | Singleton& s3 = Singleton::getInstance(); 46 | cout << &s1 << " " << &s2 << " " << &s3 << endl; 47 | s1.releaseInstance(); 48 | s2.releaseInstance(); 49 | s3.releaseInstance(); 50 | 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /day05/05memberptr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05memberptr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05memberptr main.cpp) 7 | -------------------------------------------------------------------------------- /day05/05memberptr/README.md: -------------------------------------------------------------------------------- 1 | # 成员指针 2 | 3 | ## 成员变量指针 4 | 5 | * 类型 类名::*成员变量指针; 6 | * 成员变量指针 = &类名::成员变量; 7 | * 对象`.*`成员变量指针,对象指针`->*`成员变量指针 8 | * 其本质就是特定成员变量在对象实例中的相对地址,解引用时再根据调用对象的地址计算出该成员变量的绝对地址 9 | 10 | ![memptr](../../docs/pics/memptr.png) 11 | 12 | ## 成员函数指针 13 | 14 | * 返回类型(类名::*成员函数指针)(形参表); 15 | * 成员函数指针 = &类名::成员函数名; 16 | * (对象`.*`成员函数指针)(实参表),(对象指针`->*`成员函数指针)(实参表) 17 | * 虽然成员函数指针并不存储在对象中,但也要通过对象或者对象指针对成员函数指针解引用,其目的只有一个,即提供this指针 18 | 19 | ## 静态成员变量指针 20 | 21 | * 类型* 静态成员变量指针; 22 | * 静态成员变量指针= &类名::静态成员变量; 23 | * *静态成员变量指针 24 | 25 | ## 静态成员函数指针 26 | 27 | * 返回类型(*静态成员函数指针)(形参表); 28 | * 静态成员函数指针 = 类名::静态成员函数名; 29 | * 静态成员函数指针(实参表) 30 | 31 | 静态成员与对象无关,因此静态成员指针与普通指针并没有任何本质区别 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /day05/05memberptr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Student { 6 | public: 7 | Student(const string& name, int age) : m_name(name), m_age(age) {} 8 | void who() const { 9 | cout << m_name << ", " << m_age << endl; 10 | } 11 | string m_name; 12 | int m_age; 13 | }; 14 | 15 | 16 | int main() { 17 | string Student::* p1 = &Student::m_name; 18 | int Student::* p2 = &Student::m_age; 19 | 20 | void(Student::*who)() const = &Student::who; 21 | 22 | int x = 0; 23 | memcpy((void*)&x, &p1, 4); 24 | printf("%d\n", (int)x); 25 | memcpy((void*)&x, &p2, 4); 26 | printf("%d\n", (int)x); 27 | 28 | 29 | Student s1("zhangfei", 35); 30 | cout << s1.*p1 << ", " << s1.*p2 << endl; 31 | (s1.*who)(); 32 | 33 | Student* ps = new Student("zhaoyun", 25); 34 | cout << ps->*p1 << ", " << ps->*p2 << endl; 35 | (ps->*who)(); 36 | 37 | delete ps; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /day05/06complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06complex main.cpp) 7 | -------------------------------------------------------------------------------- /day05/06complex/README.md: -------------------------------------------------------------------------------- 1 | # 操作符重载 2 | 3 | ## 操作符标记与操作符函数 4 | 5 | * 操作符标记 6 | * 单目操作符:-、++、--、*、->等 7 | * 双目操作符:+、-、+=、-=、>>、<<、[ ]等 8 | * 三目操作符:? : 9 | 10 | * 操作符函数 11 | * 在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对一个全局或成员函数的调用,该全局或成语函数被称为操作符函数 12 | * 通过定义操作符函数,可以实现针对自定义类型的运算法则,并使之与内置类型一样参与各种表达式 13 | 14 | * 双目操作符表达式:`L#R` 15 | * 成员函数形式:`L.operator#(R)`,左操作数是调用对象,右操作数是参数对象 16 | * 全局函数形式:`::operator#(L,R)`,左操作数是第一参数,右操作数是第二参数 17 | 18 | * 单目操作符表达式:`#O/O#` 19 | * 成员函数形式:`O.operator#()` 20 | * 全局函数形式:`::operator#(o)` 21 | 22 | * 三目操作符表达式:`F#S#T` 23 | * 无法重载 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /day05/06complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Complex { 5 | public: 6 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 7 | 8 | void print() const { 9 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 10 | } 11 | 12 | Complex add(const Complex& c) { 13 | return Complex(m_r+c.m_r, m_i+c.m_i); 14 | } 15 | 16 | Complex sub(const Complex& c) { 17 | return Complex(m_r-c.m_r, m_i-c.m_i); 18 | } 19 | 20 | private: 21 | int m_r; 22 | int m_i; 23 | }; 24 | 25 | int main() { 26 | Complex c1(1, 2); 27 | c1.print(); 28 | 29 | Complex c2(3,4); 30 | Complex c3 = c1.add(c2); 31 | c3.print(); 32 | 33 | Complex c4 = c1.sub(c2); 34 | c4.print(); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /day05/07complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.27) 2 | project(07complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07complex main.cpp) 7 | -------------------------------------------------------------------------------- /day05/07complex/README.md: -------------------------------------------------------------------------------- 1 | # 双目操作符重载 2 | 3 | ## 运算类双目操作符:+、-、*、/等 4 | 5 | * 左右操作数均可为左值或右值 6 | * 表达式的值为右值 7 | * 成员函数形式 8 | ``` 9 | class LEFT { 10 | const RESULT operator#(const RIGHT& right) const { ... } 11 | }; 12 | ``` 13 | * 全局函数形式 14 | ``` 15 | const RESULT operator#(const LEFT& left, const RIGHT& right) { ... } 16 | ``` 17 | 18 | ## 友元 19 | 20 | * 可以通过`friend`关键字把 一个全局函数、另一个类的成员函数或者另一个类整体,声明为某个类的友元 21 | * 友元拥有访问授权类任何非公有成员的特权 22 | * 友元声明可以出现在授权类的公有、私有或者保护等任何区域,且不受范围控制限定符的约束 23 | * 友元不是成员,其作用域并不隶属于授权类,也不拥有授权类类型的this指针 24 | * 操作符函数常被声明为其参数类型的友元 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /day05/07complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Complex { 5 | public: 6 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 7 | 8 | void print() const { 9 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 10 | } 11 | 12 | // 从左到右的三个const: 13 | // 返回右值 14 | // 常右操作数 15 | // 常左操作数 16 | Complex operator+(const Complex& r) const { 17 | return Complex(m_r+r.m_r, m_i+r.m_i); 18 | } 19 | 20 | // 友元函数 21 | // 虽然写在类内,但仍然是全局函数 22 | friend const Complex operator-(const Complex& l, const Complex& r) { 23 | return Complex(l.m_r - r.m_r, l.m_i - r.m_i); 24 | } 25 | 26 | private: 27 | int m_r; 28 | int m_i; 29 | }; 30 | 31 | /* 32 | const Complex operator-(const Complex& l, const Complex& r) { 33 | return Complex(l.m_r - r.m_r, l.m_i - r.m_i); 34 | } 35 | */ 36 | 37 | 38 | int main() { 39 | Complex c1(1,2); 40 | Complex c2(3,4); 41 | Complex c3 = c1 + c2; 42 | c3.print(); 43 | 44 | Complex c4 = c1 - (c3 + c2); 45 | c4.print(); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /day05/08complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08complex main.cpp) 7 | -------------------------------------------------------------------------------- /day05/08complex/README.md: -------------------------------------------------------------------------------- 1 | ## 赋值类双目操作符:=、+=、-=、*=、/=等 2 | 3 | * 右操作数为左值或右值,但左操作数必须是左值 4 | * 表达式的值是左值,且为做操作数本身(而非副本) 5 | * 成员函数形式 6 | ``` 7 | class LEFT { 8 | LEFT& operator#(const RIGHT& right) { ... } 9 | }; 10 | ``` 11 | * 全局函数形式 12 | ``` 13 | LEFT& operator#(LEFT& left, const RIGHT& right) { ... } 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /day05/08complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Complex { 5 | public: 6 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 7 | 8 | void print() const { 9 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 10 | } 11 | 12 | Complex& operator+=(const Complex& r) { 13 | m_r += r.m_r; 14 | m_i += r.m_i; 15 | return *this; 16 | } 17 | 18 | friend Complex& operator-=(Complex& l, const Complex& r) { 19 | l.m_r -= r.m_r; 20 | l.m_i -= r.m_i; 21 | return l; 22 | } 23 | 24 | private: 25 | int m_r; 26 | int m_i; 27 | }; 28 | 29 | int main() { 30 | Complex c1(1, 2); 31 | Complex c2(3, 4); 32 | const Complex c3(5, 6); 33 | c1 += c2; 34 | c1.print(); // 4 + 6i 35 | 36 | (c1 += c2) += c3; 37 | c1.print(); // 12 + 16i 38 | 39 | ((c1 -= c3) -= c2) -= c2; 40 | c1.print(); // 1 + 2i 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /day05/09complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(09complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(09complex main.cpp) 7 | -------------------------------------------------------------------------------- /day05/09complex/README.md: -------------------------------------------------------------------------------- 1 | # 单目操作运算符重载 2 | 3 | ## 运算类单目操作符:-、~、!等 4 | 5 | * 操作数为左值或右值 6 | * 表达式的值为右值 7 | * 成员函数形式 8 | ``` 9 | class OPERAND { 10 | const RESULT operator#() const { ... } 11 | }; 12 | ``` 13 | * 全局函数形式 14 | ``` 15 | const RESULT operator#(const OPERAND& operand) { ... } 16 | ``` 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /day05/09complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Complex { 5 | public: 6 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 7 | 8 | void print() const { 9 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 10 | } 11 | 12 | const Complex operator-() { 13 | return Complex(-m_r, -m_i); 14 | } 15 | 16 | friend const Complex operator~(const Complex& o) { 17 | return Complex(o.m_i, o.m_r); 18 | } 19 | 20 | private: 21 | int m_r; 22 | int m_i; 23 | }; 24 | 25 | int main() { 26 | Complex c1(1, 2); 27 | c1.print(); 28 | Complex c2 = -c1; 29 | c2.print(); 30 | 31 | c2 = ~c1; 32 | c2.print(); // 2 + 1i 33 | 34 | return 0; 35 | } -------------------------------------------------------------------------------- /day05/10complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(10complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(10complex main.cpp) 7 | -------------------------------------------------------------------------------- /day05/10complex/README.md: -------------------------------------------------------------------------------- 1 | # 前自增减单目操作符:前++、前-- 2 | 3 | * 操作数为左值 4 | * 表达式的值为左值,且为操作数本身(而非副本) 5 | * 成员函数形式 6 | ``` 7 | class OPERAND { 8 | OPERAND& operator#() { ... } 9 | }; 10 | ``` 11 | * 全局函数形式 12 | ``` 13 | OPERAND& operator#(OPERAND& operand) { ... } 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /day05/10complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Complex { 5 | public: 6 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 7 | 8 | void print() const { 9 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 10 | } 11 | 12 | Complex& operator++() { 13 | ++m_r; 14 | ++m_i; 15 | return *this; 16 | } 17 | 18 | const Complex operator++(int) { 19 | Complex old = *this; 20 | ++*this; 21 | return old; 22 | } 23 | 24 | friend Complex& operator--(Complex& o) { 25 | --o.m_r; 26 | --o.m_i; 27 | return o; 28 | } 29 | 30 | friend const Complex operator--(Complex& o, int) { 31 | Complex old = o; 32 | --o; 33 | return old; 34 | } 35 | 36 | private: 37 | int m_r; 38 | int m_i; 39 | }; 40 | 41 | int main() { 42 | Complex c1(1, 2); 43 | Complex c2 = ++++c1; // c1.operator++().operator++(); 44 | c1.print(); // 3 + 4i 45 | c2.print(); // 3 + 4i 46 | 47 | c2 = ----c1; 48 | c2.print(); // 1 + 2i 49 | 50 | c2 = c1++; // c2 = c1.operator++(0) 51 | c1.print(); // 2 + 3i 52 | c2.print(); // 1 + 2i 53 | 54 | c2 = c1--; 55 | c1.print(); // 1 + 2i 56 | c2.print(); // 2 + 3i 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /day06/01complex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01complex) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01complex main.cpp) 7 | -------------------------------------------------------------------------------- /day06/01complex/README.md: -------------------------------------------------------------------------------- 1 | # 输出操作符:<< 2 | 3 | * 左操作数为左值形式的输出流(`ostream`)对象,右操作数为左值或右值 4 | * 表达式的值为左值,且为做操作数本身(而非副本) 5 | * 做操作数的类型为`ostream`,若以成员函数的形式重载该操作符,就应将其定义为`ostream`类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符 6 | ``` 7 | ostream& operator<<(ostream& os, const RIGHT& right) { ... } 8 | ``` 9 | 10 | # 输出操作符:>> 11 | 12 | * 左操作数为左值形式的输入流(`istream`)对象,右操作数位左值 13 | * 表达式的值为左值,且为做操作数本身(而非副本) 14 | * 左操作数的类型为`istream`,若以成员函数形式重载该操作符,就应将其定义为`istream`类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符 15 | ``` 16 | istream& operator>>(istream& is, RIGHT& right) { ... } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /day06/01complex/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Complex { 6 | public: 7 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 8 | 9 | void print() const { 10 | cout << m_r << (m_i > 0 ? "+" : "") << m_i << "i" << endl; 11 | } 12 | 13 | friend ostream& operator<<(ostream& os, const Complex& c) { 14 | return os << c.m_r << (c.m_i > 0 ? "+" : "") << c.m_i << "i"; 15 | } 16 | friend istream& operator>>(istream& is, Complex& c) { 17 | return is >> c.m_r >> c.m_i; 18 | } 19 | private: 20 | int m_r; 21 | int m_i; 22 | }; 23 | 24 | int main() { 25 | Complex c1(1, 2); 26 | // cout.operator<<(c) 27 | // ::operator<<(cout, c).operator<<(endl); 28 | cout << c1 << endl; 29 | 30 | ofstream ofs("complex.txt"); 31 | ofs << c1 << endl; 32 | 33 | Complex c2, c3; 34 | cin >> c2 >> c3;// ::operator>>(::operator>>(cin,c1), c2); 35 | cout << c2 << ", " << c3 << endl; 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /day06/02array/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02array) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02array main.cpp) 7 | -------------------------------------------------------------------------------- /day06/02array/README.md: -------------------------------------------------------------------------------- 1 | # 下标操作符:[ ] 2 | 3 | * 常用于在容器类型中以下标方式获取数据元素 4 | * 非常容器的元素为左值,常容器的元素为右值 5 | 6 | ![array](../../docs/pics/opoverload_array.png) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /day06/02array/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A100 { 5 | public: 6 | A100() : m_arr{ 0 } {} 7 | 8 | int& at(size_t i) { 9 | return m_arr[i]; 10 | } 11 | const int& at(size_t i) const { 12 | return const_cast(this)->at(i); 13 | } 14 | int& operator[](size_t i) { 15 | return m_arr[i]; 16 | } 17 | const int& operator[](size_t i) const { 18 | return const_cast(*this)[i]; 19 | } 20 | 21 | private: 22 | int m_arr[100]; 23 | }; 24 | 25 | int main() { 26 | A100 a; 27 | /* 28 | a.at(0) = 100; 29 | a.at(1) = 200; 30 | a.at(0)++; 31 | --a.at(1); 32 | cout << a.at(0) << " " << a.at(1) << endl; 33 | const A100* p = &a; 34 | cout << p->at(0) << " " << p->at(1) << endl; 35 | */ 36 | 37 | a[0] = 100; // a.operator[](0); 38 | a[1] = 200; 39 | a[0]++; 40 | --a[1]; 41 | cout << a[0] << " " << a[1] << endl; 42 | const A100* p = &a; 43 | cout << (*p)[0] << " " << (*p)[1] << endl; 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /day06/03square/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03square) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03square main.cpp) 7 | -------------------------------------------------------------------------------- /day06/03square/README.md: -------------------------------------------------------------------------------- 1 | # 函数操作符:() 2 | 3 | * 如果一个类重载了函数操作符,那么该类的对象就可以被当做函数来调用,其参数和返回值就是函数操作符函数的参数和返回值 4 | * 参数的个数、类型以及返回值的类型,没有限制 5 | * 唯一可以带有缺省参数的操作符函数 6 | 7 | ![opfunc](../../docs/pics/opoverload_func.png) 8 | 9 | 10 | -------------------------------------------------------------------------------- /day06/03square/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Square { 5 | public: 6 | double operator()(double r = 0) { 7 | return r * r; 8 | } 9 | }; 10 | 11 | int main() { 12 | Square square; 13 | cout << square(10) << endl; // s.operator()(10) 14 | cout << square() << endl; 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /day06/04ptr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04ptr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04ptr main.cpp) 7 | -------------------------------------------------------------------------------- /day06/04ptr/README.md: -------------------------------------------------------------------------------- 1 | # 解引用和间接成员访问操作符:*、-> 2 | 3 | * 如果一个重载了解引用和间接成员访问操作符,那么该类的对象就可以被当做指针来使用 4 | 5 | ![ptr](../../docs/pics/opoverload_ptr.png) 6 | 7 | 8 | # 智能指针(auto_ptr) 9 | 10 | ## 常规指针的缺点 11 | 12 | * 当一个常规指针离开它的作用域时,只有该指针变量本身所占据的内存空间(通常是4字节)会被释放,而它所指向的动态内存并未得到释放 13 | * 在某些特殊情况下,包含`free/delete/delete[]`的代码根本执行不到,形成内存泄漏 14 | 15 | ## 智能指针的优点 16 | 17 | * 智能指针是一个封装了常规指针的类类型对象,当它离开作用域时,其析构函数负责释放该常规指针所指向的动态内存 18 | * 以正确方式创建智能指针,其析构函数总会执行 19 | 20 | ## 智能指针与常规指针的一致性 21 | 22 | * 为了使智能指针也能像常规指针一样,通过`*`操作符解引用,通过`->`操作符访问其目标的成员,就需要对这两个操作进行重载 23 | 24 | ## 智能指针与常规指针的不一致性 25 | 26 | * 任何时候,针对同一个对象,只允许有一个智能指针持有其地址,否则该对象在多个智能指针中被析构多次(double free) 27 | * 智能指针的拷贝构造和拷贝赋值需要做特殊处理,对其所持有的对象地址,以指针间的转移代替复制 28 | * 智能指针的转移语义与常规指针的复制语义不一致 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /day06/04ptr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class A { 6 | public: 7 | A() { cout << "A::A()" << endl; } 8 | ~A() { cout << "A::~A()" << endl; } 9 | void foo() { cout << "A::foo() " << m_data << endl; } 10 | 11 | private: 12 | int m_data; 13 | }; 14 | 15 | class PA { 16 | public: 17 | PA(A* p = nullptr) : m_p(p) { } 18 | 19 | ~PA() { 20 | if (m_p) { 21 | delete m_p; 22 | } 23 | } 24 | 25 | PA(PA& that) : m_p(that.release()) { } 26 | 27 | PA& operator=(PA& that) { 28 | if (&that != this) { 29 | reset(that.release()); 30 | } 31 | return *this; 32 | } 33 | 34 | A* operator->() { 35 | return m_p; 36 | // return &**this; 37 | } 38 | 39 | A& operator*() { 40 | return *m_p; 41 | } 42 | 43 | private: 44 | A* release() { 45 | A* p = m_p; 46 | m_p = nullptr; 47 | return p; 48 | } 49 | void reset(A* p) { 50 | if (p != m_p) { 51 | delete m_p; 52 | m_p = p; 53 | } 54 | } 55 | 56 | A* m_p; 57 | }; 58 | 59 | int main() { 60 | /* 61 | A* pa = new A; 62 | pa->foo(); 63 | (*pa).foo(); 64 | delete pa; 65 | */ 66 | 67 | PA pa(new A); 68 | pa->foo(); // pa.operator->()->foo(); 69 | (*pa).foo(); 70 | 71 | PA pb; 72 | pb = pa; 73 | (*pb).foo(); 74 | // pa->foo(); // 此刻pa已经失效 ERROR 75 | 76 | auto_ptr ap(new A); 77 | ap->foo(); 78 | (*ap).foo(); 79 | 80 | auto_ptr bp = ap; 81 | bp->foo(); 82 | (*bp).foo(); 83 | // ap->foo(); // ERROR 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /day06/05integer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05integer) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05integer main.cpp) 7 | -------------------------------------------------------------------------------- /day06/05integer/README.md: -------------------------------------------------------------------------------- 1 | # 自定义类型转换 2 | 3 | * 通过构造函数实现自定义类型转换 4 | ``` 5 | class 目标类型 { 6 | [explicit] 目标类型(const 源类型& src) { ... } 7 | }; 8 | ``` 9 | * 通过类型转换操作符函数实现自定义类型转换 10 | ``` 11 | class 源类型 { 12 | [explicit] operator 目标类型() const { ... } 13 | }; 14 | ``` 15 | * 若源类型是基本类型,则只能通过构造函数实现自定义类型转换 16 | * 若目标类型是基本类型,则只能通过类型转换操作符函数实现自定义类型转换 17 | * 若源类型和目标类型都不是基本类型,则既可以通过构造函数也可以通过类型转换操作符函数实现自定义类型转换,但不要两者同时使用,引发歧义错误 18 | * 若源类型和目标类型都是基本类型,则无法实现自定义类型转换,基本类型间的类型转换规则完全由编译器内置 19 | 20 | -------------------------------------------------------------------------------- /day06/05integer/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | 6 | class Integer { 7 | public: 8 | explicit Integer(int data = 0) : m_data(data) { } 9 | 10 | explicit operator int() const { 11 | return m_data; 12 | } 13 | 14 | explicit operator int&() { 15 | return m_data; 16 | } 17 | 18 | private: 19 | int m_data; 20 | }; 21 | 22 | class Student { 23 | public: 24 | Student(const string& name="", int age=0) : m_name(name), m_age(age) {} 25 | 26 | explicit operator string() const { 27 | char str[128] = {0}; 28 | sprintf(str, "%s, %d", m_name.c_str(), m_age); 29 | return str; 30 | } 31 | 32 | private: 33 | string m_name; 34 | int m_age; 35 | }; 36 | 37 | int main() { 38 | Integer i1 = Integer(100); 39 | int i2 = int(i1); 40 | cout << i2 << endl; 41 | 42 | i1 = Integer(200); 43 | cout << int(i1) << endl; 44 | 45 | 46 | Student s("zhangfei", 35); 47 | cout << string(s) << endl; 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /day06/06new/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06new) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06new main.cpp) 7 | -------------------------------------------------------------------------------- /day06/06new/README.md: -------------------------------------------------------------------------------- 1 | # 对象创建操作符:new/new[ ] 2 | 3 | * 如果一个类重载了`new/new[]`操作符,那么当通过`new/new[]`创建该类对象/对象数组时,将首先调用该操作符函数分配内存,然后再调用该类的构造函数 4 | ``` 5 | class 类名 { 6 | static void* operator new(size_t size) { ... } 7 | static void* operator new[](size_t size) { ... } 8 | }; 9 | ``` 10 | * 包含自定义析构函数的类,通过`new[]`创建对象数组,所分配的内存会在低地址部分预留出`sizeof(size_t)`个字节,存放数组长度 11 | 12 | # 对象销毁操作符:delete/delete[ ] 13 | 14 | * 如果一个类重载了`delete/delete[]`操作符,那么当通过`delete/delete[]`销毁该类对象/对象数组时,将首先调用该类的析构函数,然后再调用该操作符函数释放内存 15 | ``` 16 | class 类名 { 17 | static void operator delete(void* p) { ... } 18 | static void operator delete[](void* p) { ... } 19 | }; 20 | ``` 21 | * 包含自定义析构函数的类,通过`delete[]`销毁对象数组,会根据低地址部分预留的数组长度,从高地址到低地址一次对每个数组元素调用析构函数 22 | 23 | # 操作符重载的限制 24 | 25 | * 不是富有的操作符都能重载,以下操作符不能重载 26 | * 作用限定操作符:`::` 27 | * 直接成员访问符:`.` 28 | * 直接成员指针解引用操作符:`.*` 29 | * 条件操作符:`?:` 30 | * 字节长度操作符:`sizeof` 31 | * 类型信息操作符:`typeid` 32 | * 无法重载所有操作数均为基本类型的操作符 33 | * 无法通过操作符重载改变操作符优先级 34 | * 无法通过操作符重载改变操作数的个数 35 | * 无法通过操作符重载发明新的操作符 36 | * 操作符重载着力于对一致性的追求 37 | * 操作符重载的价值在于提高代码的可读性,而不是沦为少"数算符"控们赖以生存的奇技淫巧 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /day06/06new/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class A { 6 | public: 7 | A(int data = 0, char c=0): m_data(data), m_ch(c) { 8 | cout << "A::A(): this=" << this << endl; 9 | } 10 | 11 | ~A() { 12 | cout << "A::~A(), this=" << this << endl; 13 | } 14 | 15 | static void* operator new(size_t size) { 16 | cout << "A::new(" << size << "): "; 17 | void* pv = malloc(size); 18 | cout << pv << endl; 19 | return pv; 20 | } 21 | 22 | static void operator delete(void* pv) { 23 | cout << "A::delete(" << pv << ")" << endl; 24 | free(pv); 25 | } 26 | 27 | static void* operator new[](size_t size) { 28 | cout << "A::new[](" << size <<"): "; 29 | void* pv = malloc(size); 30 | cout << pv << endl; 31 | return pv; 32 | } 33 | 34 | static void operator delete[](void* pv) { 35 | cout << "A::delete[](" << pv << ")" << endl; 36 | free(pv); 37 | } 38 | 39 | private: 40 | int m_data; 41 | char m_ch; 42 | }; 43 | 44 | 45 | int main() { 46 | A* pa1 = new A; 47 | /* 48 | A* pa1 = (A*)A::operator new(sizeof(A)); 49 | pa1->A(); 50 | */ 51 | 52 | cout << "pa1 = " << pa1 << endl; 53 | 54 | delete pa1; 55 | /* 56 | pa1->~A(); 57 | A::operator delete(pa1); 58 | */ 59 | cout << "-------------------------------------" << endl; 60 | A* pa2 = new A[3]; 61 | /* 62 | A* pa2 = ((size_t*)A::operator new[](sizeof(size_t) + sizeof(A) * 3) + 1); 63 | *((size_t*)pa2 - 1) = 3; 64 | for (size_t i = 0; i < *(size_t*)pa2 - 1; ++i) { 65 | (pa2+i)->A(); 66 | } 67 | */ 68 | cout << "len=" << *((size_t*)pa2 -1) << endl; 69 | cout << "pa2 = " << pa2 << endl; 70 | delete[] pa2; 71 | /* 72 | for (size_t i = *((size_t*)pa2 -1) - 1; ; --i ) { 73 | (pa2+i)->~A(); 74 | if (i == 0) { 75 | break; 76 | } 77 | } 78 | A::operator delete[]((size_t*)pa2-1); 79 | */ 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /day06/07human/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | project(07human) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07human main.cpp) 7 | -------------------------------------------------------------------------------- /day06/07human/README.md: -------------------------------------------------------------------------------- 1 | # 继承 2 | 3 | ## 继承的基本概念和语法 4 | 5 | * 共性与个性 6 | * 超集与子集 7 | * 基类与子类 8 | * 继承与派生 9 | * 继承的语法 10 | 11 | ``` 12 | class 子类: 继承方式1 基类1, 继承方式2 基类2, ... { 13 | ... 14 | }; 15 | ``` 16 | 17 | * 继承方式 18 | * 公有继承:public 19 | * 保护继承:protected 20 | * 私有继承:private 21 | 22 | ## 公有继承的基本特点 23 | 24 | * 子类对象任何时候都可以被当做其基类类型的对象 25 | ``` 26 | class Human {...}; 27 | class Student : public Human {...}; 28 | Student student(...); 29 | Human* phuman = &student; 30 | Human& rhuman = student; 31 | ``` 32 | * 编译器认为访问范围缩小是安全的 33 | 34 | ![inherit01](../../docs/pics/inherit01.png) 35 | 36 | * 基类类型的指针或者引用不能隐式转换为子类类型 37 | ``` 38 | class Human {...}; 39 | class Student : public Human {...}; 40 | Human human(...); 41 | Student* pstudent = static_cast(&human); 42 | Student& rstudent = static_cast(human); 43 | ``` 44 | * 编译器认为访问返回扩大是危险的 45 | 46 | ![inherit02](../../docs/pics/inherit02.png) 47 | 48 | * 编译器对类型安全的检测仅仅基于指针或引用本身 49 | ``` 50 | class Human {...}; 51 | class Student : public Human {...}; 52 | Student student(...); 53 | Human* phuman = &student; 54 | Human& rhuamn = student; 55 | Student* pstudent = static_cast(phuman); 56 | Student& rstudent = static_cast(rhuamn); 57 | ``` 58 | * 基类指针或引用的实际目标,究竟是不是子类对象,完全由程序员自己判断 59 | * 在子类中可以直接访问基类的所有公有和保护成员,就如同它们是在子类中声明一样 60 | * 基类的私有成员在子类中虽然存在却不可见,故无法直接访问 61 | * 尽管子类的公有和保护成员在子类中直接可见,但仍然可以在子类中重新定义这些名字,子类中的名字会隐藏所有基类中的同名定义 62 | * 如果需要再子类中或通过子类访问一个在基类中定义却为子类所隐藏的名字,可以借助作用域限定操作符`::`实现 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /day06/07human/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Human { 5 | public: 6 | Human(const string& name = "", int age = 0) : m_age(age), m_name(name) {} 7 | void who() const { 8 | cout << m_name << ", " << m_age << endl; 9 | } 10 | void eat(const string& food) const { 11 | cout << "eat " << food << endl; 12 | } 13 | void sleep(int hour) const { 14 | cout << "sleep " << hour << " hours" << endl; 15 | } 16 | 17 | protected: 18 | string m_name; 19 | int m_age; 20 | }; 21 | 22 | 23 | class Student: public Human { 24 | public: 25 | Student(const string& name = "", int age = 0, int no = 0) { 26 | m_name = name; 27 | m_age = age; 28 | m_no = no; 29 | } 30 | 31 | void learn(const string& course) const { 32 | cout << "learn " << course << endl; 33 | } 34 | 35 | // 继承不会改变作用域,不会和父类的who构成重载 36 | // 父子类同名标识符构成隐藏关系 37 | void who() const { 38 | cout << m_name << ", " << m_age << ", " << m_no << endl; 39 | } 40 | private: 41 | int m_no; 42 | }; 43 | 44 | class Teacher : public Human { 45 | public: 46 | Teacher(const string& name = "", int age = 0, double salary = 10000) 47 | : m_salary(salary) // 写在初始化表里的成员必须是自己的,不能是继承的 48 | , Human(name, age) 49 | {} 50 | void who() const { 51 | cout << m_name << ", " << m_age << ", " << m_salary << endl; 52 | } 53 | void teach(const string& course) const { 54 | cout << "teach " << course << endl; 55 | } 56 | private: 57 | double m_salary; 58 | }; 59 | 60 | 61 | int main() { 62 | Student s("zhangfei", 35, 1001); 63 | s.who(); 64 | s.Human::who(); // 显式调用父类的函数 65 | s.eat("pancake"); 66 | s.sleep(2); 67 | s.learn("C++"); 68 | cout << "--------------------------------" << endl; 69 | Human* phuman = &s; // 隐式转换 70 | phuman->who(); 71 | phuman->sleep(3); 72 | phuman->eat("banana"); 73 | cout << "--------------------------------" << endl; 74 | // 编译器只看类型 75 | Student* pstudent = static_cast(phuman); 76 | pstudent->who(); 77 | pstudent->learn("C"); 78 | pstudent->sleep(4); 79 | pstudent->eat("apple"); 80 | cout << "--------------------------------" << endl; 81 | Teacher t("zhaoyun", 25, 20000); 82 | t.who(); 83 | t.eat("steak"); 84 | t.sleep(5); 85 | t.teach("Unix"); 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /day06/08ppp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08ppp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08ppp main.cpp) 7 | -------------------------------------------------------------------------------- /day06/08ppp/README.md: -------------------------------------------------------------------------------- 1 | ## 继承方式与访问控制 2 | 3 | * 类成员的访问控制限定符与访问控制属性 4 | 5 | | 访问控制限定符 | 访问控制属性 | 基类 | 子类 | 外部 | 友元 | 6 | |-----------|--------|----|----|----|----| 7 | | public | 公有成员 | OK | OK | OK | OK | 8 | | protected | 保护成员 | OK | OK | NO | OK | 9 | | private | 私有成员 | OK | NO | NO | OK | 10 | 11 | * 基类中的公有、保护和私有成员,在其公有、保护和私有子类中的访问控制属性,会因继承方式而异 12 | 13 | | 基类中的 | 在公有子类(公有继承)中变成 | 在保护子类(保护继承)中变成 | 在私有子类(私有继承)中变成 | 14 | |------|----------------|----------------|----------------| 15 | | 公有成员 | 公有成员 | 保护成员 | 私有成员 | 16 | | 保护成员 | 保护成员 | 保护成员 | 私有成员 | 17 | | 私有成员 | 私有成员 | 私有成员 | 私有成员 | 18 | 19 | * 当通过子类访问其所继承的基类的成员时,需要考虑继承方式对访问控制属性的影响 20 | 21 | ## private,protected的作用是防止接口扩散 22 | ```c++ 23 | class DCT { 24 | public: 25 | void codec() { 26 | //... 27 | } 28 | }; 29 | 30 | class Jpeg : public DCT { 31 | public: 32 | void render() { 33 | //... 34 | render(); 35 | //... 36 | } 37 | }; 38 | 39 | Jpeg j; 40 | j.codec(); // 接口扩散了 41 | ``` 42 | ```c++ 43 | class DCT { 44 | public: 45 | void codec() { 46 | //... 47 | } 48 | }; 49 | 50 | class Jpeg : private DCT { 51 | public: 52 | void render() { 53 | //... 54 | render(); 55 | //... 56 | } 57 | }; 58 | 59 | Jpeg j; 60 | // 防止扩散 61 | j.codec(); // ERROR 62 | ``` 63 | 64 | ```c++ 65 | class DCT { 66 | public: 67 | void codec() { 68 | //... 69 | } 70 | }; 71 | 72 | class Jpeg : private DCT { 73 | public: 74 | void render() { 75 | //... 76 | render(); 77 | //... 78 | } 79 | }; 80 | 81 | class PDF : public Jpeg{}; 82 | 83 | PDF pdf; 84 | pdf.codec(); //有限的扩散 85 | ``` -------------------------------------------------------------------------------- /day06/08ppp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | int m_pub; 7 | protected: 8 | int m_pro; 9 | private: 10 | int m_pri; 11 | }; 12 | 13 | class B : public A {}; 14 | class C : protected A {}; 15 | class D : private A { 16 | public: 17 | void foo() { 18 | // D是A的直接子类,不用考虑继承方式。即除了私有的,都能访问 19 | m_pub = 10; 20 | m_pro = 10; 21 | // m_pri = 10; 22 | } 23 | }; 24 | 25 | class X : public B { 26 | public: 27 | void foo() { 28 | m_pub = 10; 29 | m_pro = 10; 30 | // m_pri = 10; // ERROR 31 | } 32 | }; 33 | 34 | class Y : public C { 35 | public: 36 | void foo() { 37 | m_pub = 10; 38 | m_pro = 10; 39 | // m_pri = 10; // ERROR 40 | } 41 | }; 42 | 43 | class Z : public D { 44 | public: 45 | void foo() { 46 | // m_pub = 10; // ERROR 47 | // m_pro = 10; // ERROR 48 | // m_pri = 10; // ERROR 49 | } 50 | }; 51 | 52 | 53 | int main() { 54 | B b; 55 | b.m_pub = 10; 56 | // b.m_pro = 10; // ERROR 57 | // b.m_pri = 10; // ERROR 58 | 59 | C c; 60 | // c.m_pub = 10; // ERROR 61 | // c.m_pro = 10; // ERROR 62 | // c.m_pri = 10; // ERROR 63 | 64 | D d; 65 | // d.m_pub = 10; // ERROR 66 | // d.m_pro = 10; // ERROR 67 | // d.m_pri = 10; // ERROR 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /day07/01order/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01order) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01order main.cpp) 7 | -------------------------------------------------------------------------------- /day07/01order/README.md: -------------------------------------------------------------------------------- 1 | ## 子类的构造与析构 2 | 3 | * 子类构造函数隐式调用基类构造函数 4 | * 如果子类的构造函数没有显式指明其基类部分的构造方式,那么编译器会选择其基类的缺省构造函数,构造该子类对象中的基类子对象 5 | * 子类构造函数显式调用基类构造函数 6 | * 子类的构造函数可以在`初始化表`中显式指明其基类部分的构造方式,即通过其基类的特定构造函数构造该子类对象中的基类子对象 7 | * 子类对象的构造过程 8 | * 构造基类子对象->构造成员变量->执行构造代码 9 | * 阻断继承 10 | * 子类的构造函数无论如何都会调用基类的构造函数,构造子类对象中的基类子对象 11 | * 如果把基类的构造函数定义为私有,那么该类的子类就永远无法实例化对象 12 | * 在C++中可以用这种方法阻断一个类被扩展 13 | * 子类析构函数隐式调用基类析构函数 14 | * 子类的析构函数在执行完其中的析构代码,并析构完所有的成员变量以后,会自动调用其基类的析构函数,析构该子类对象中的基类子对象 15 | * 基类析构函数不会调用子类析构函数 16 | * 通过基类指针析构子类对象,实际被析构的仅仅是子类对象中的基类子对象,子类的扩展部分将失去被析构的机会,极有可能形成内存泄漏 17 | * 子类对象的析构过程 18 | * 执行析构代码->析构成员变量->析构基类子对象 19 | 20 | ## 子类的拷贝构造与拷贝赋值 21 | 22 | * 子类没有定义拷贝构造函数 23 | * 编译器为子类提供的缺省拷贝构造函数,会自动调用其基类的拷贝构造函数,构造该子类对象中的基类子对象 24 | * 子类定义了拷贝构造函数,但没有显式指明其基类部分的构造方式 25 | * 编译器会选择其基类的缺省构造函数,构造该子类对象中的基类子对象 26 | * 子类定义了拷贝构造函数,同时显式指明了其基类部分以拷贝方式构造 27 | * 子类对象中的基类部分和扩展部分一起被复制 28 | * 子类没有定义拷贝赋值运算符函数 29 | * 编译器为子类提供的缺省拷贝赋值运算符函数,会自动调用其基类的拷贝赋值运算符函数,复制该子类对象中的基类子对象 30 | * 子类定义了拷贝赋值运算符函数,但没有显式调用其基类的拷贝赋值运算符函数 31 | * 子类对象中的基类子对象将得不到复制 32 | * 子类定义了拷贝赋值运算符函数,同时显式调用了其基类的拷贝赋值运算符函数 33 | * 子类对象中的基类部分和扩展部分一起被复制 34 | 35 | ## 子类的操作符重载 36 | 37 | * 在为子类提供操作符重载定义时,往往需要调用其基类针对该操作符所做的重载定义,完成部分工作 38 | * 通过将子类对象的指针或引用向上造型为其基类类型的指针或引用,可以迫使针对基类的操作符重载函数在针对子类的操作符重载函数中被调用 39 | 40 | ``` 41 | ostream& operator<<(ostream& os, const Manager& manager) { 42 | return os <<(Employee&)manager << "," << manager.m_title; 43 | } 44 | ``` 45 | 46 | 47 | -------------------------------------------------------------------------------- /day07/01order/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | A() { 7 | cout << "A::A(): " << this << endl; 8 | } 9 | ~A() { 10 | cout << "A::~A()" << endl; 11 | } 12 | private: 13 | int m_data; 14 | }; 15 | 16 | class C { 17 | public: 18 | C() { 19 | cout << "C::C(): " << this << endl; 20 | } 21 | ~C() { 22 | cout << "C::~C()" << endl; 23 | } 24 | private: 25 | int m_data; 26 | }; 27 | 28 | class B : public A { 29 | public: 30 | B() { 31 | cout << "B::B(): " << this << endl; 32 | } 33 | ~B() { 34 | cout << "B::~B()" << endl; 35 | } 36 | private: 37 | C m_c; 38 | int m_data; 39 | }; 40 | 41 | int main() { 42 | // 构造基类子对象->构造成员变量->执行构造代码 43 | // B b; 44 | // cout << sizeof(b) << endl; 45 | // cout << &b.m_data << endl; 46 | // 执行析构代码->析构成员变量->析构基类子对象 47 | 48 | A* p = new B; 49 | // 基类析构函数不会调用子类析构函数 50 | // 并未执行B、C的析构函数 51 | // delete p; 52 | 53 | delete static_cast(p); 54 | 55 | return 0; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /day07/02hide/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02hide) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02hide main.cpp) 7 | -------------------------------------------------------------------------------- /day07/02hide/README.md: -------------------------------------------------------------------------------- 1 | ## 名字隐藏与重载 2 | 3 | * 继承不会改变类成员的作用域,基类的成员永远都是基类的成员,并不会因继承而变成子类的成员 4 | * 因为作用域的不同,分别在子类和基类中定义的同名成员函数(包括静态成员函数)并不构成重载关系,相反是一种隐藏关系,除非通过using声明将基类的成员函数引入子类的作用域形成重载 5 | * 任何时候,无论在子类的内部还是外部,总是可以通过作用域限定操作符`::`,显式地调用那些在基类中定义却未子类所隐藏的成员函数 6 | 7 | ## 私有继承与保护继承 8 | 9 | * 私有继承亦称实现继承,旨在于子类中的将其基类的公有和保护成员私有化,既禁止从外部通过该子类访问这些成员,也禁止在该子类的子类中访问这些成员 10 | * 保护继承是一种特殊形式的实现继承,旨在于子类中将其基类的公有和保护成员进程有限的私有化,只禁止从外部通过该子类访问这些成员,但并不禁止在该子类的子类中访问这些成员 11 | * 私有子类和保护子类类型的指针或引用,不能隐式转换为其基类类型的指针或引用 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day07/02hide/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Base { 5 | public: 6 | void foo() { 7 | cout << "Base::foo()" << endl; 8 | } 9 | }; 10 | 11 | class Derive : public Base { 12 | public: 13 | using Base::foo; 14 | void foo(int x) { 15 | cout << "Derive::foo(int)" << endl; 16 | } 17 | }; 18 | 19 | int main() { 20 | Derive d; 21 | //d.Base::foo(); 22 | d.foo(); 23 | d.foo(1); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /day07/03mulinherit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03mulinherit) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03mulinherit main.cpp) 7 | -------------------------------------------------------------------------------- /day07/03mulinherit/README.md: -------------------------------------------------------------------------------- 1 | ## 多重继承、钻石继承与虚继承 2 | 3 | * 一个类可以同时从多个基类继承实现代码 4 | 5 | * 多重继承的内存布局和类型转换 6 | * 子类对象中的多个基类子对象,按照`继承表`的顺序依次被构造,并从`低地址到高地址`排列,析构的顺序则与构造`严格相反` 7 | 8 | ![mulin](../../docs/pics/mulin.png) 9 | * 将继承自多个基类的子类类型指针,隐式或静态转换为其他的基类类型,编译器会根据各个基类子对象在子类对象中的内不能布局,进行适当的偏移计算,以保证指针的类型与其所指向目标对象的类型一致 10 | * 反之,将该子类的任何一个基类类型的指针静态转换为子类类型,编译器同样会进行适当的偏移计算 11 | * 无论在哪个方向上,重解释类型转换都不进程任何偏移计算 12 | * 引用的情况与指针类似,因为引用的本质就是指针 13 | 14 | ![memlayout](../../docs/pics/memlayout.png) 15 | 16 | * 围绕多重继承,历来争议颇多 17 | * 现实世界中的实体本来就具有同时从多个来源共同继承的特性,因此多重继承有助于面向现实世界的问题域直接建立模型 18 | * 多重继承可能会在大型程序设计中引入令人难以察觉的BUG,并及大地增加对类层次体系进程扩展的难度 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /day07/03mulinherit/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Phone { 5 | public: 6 | Phone(const string& no) : m_no(no) { 7 | cout << "Phone::Phone(): " << this << endl; 8 | } 9 | ~Phone() { 10 | cout << "Phone::~Phone()" << endl; 11 | } 12 | void call(const string& no) const { 13 | cout << m_no << " call " << no << endl; 14 | } 15 | private: 16 | string m_no; 17 | }; 18 | 19 | class Player { 20 | public: 21 | Player(const string& media) : m_media(media) { 22 | cout << "Player::Player(): " << this << endl; 23 | } 24 | ~Player() { 25 | cout << "Player::~Player()" << endl; 26 | } 27 | void play(const string& clip) const { 28 | cout << m_media << " play " << clip << endl; 29 | } 30 | private: 31 | string m_media; 32 | }; 33 | 34 | class Computer { 35 | public: 36 | Computer(const string& os) : m_os(os) { 37 | cout << "Computer::Computer(): " << this << endl; 38 | } 39 | ~Computer() { 40 | cout << "Computer::~Computer()" << endl; 41 | } 42 | void run(const string& app) const { 43 | cout << m_os << " run " << app << endl; 44 | } 45 | private: 46 | string m_os; 47 | }; 48 | 49 | class SmartPhone : public Phone, public Player, public Computer { 50 | public: 51 | SmartPhone(const string& no, const string& media, const string& os) : Phone(no), Player(media), Computer(os) { 52 | cout << "SmartPhone::SmartPhone(): " << this << endl; 53 | } 54 | ~SmartPhone() { 55 | cout << "SmartPhone::~SmartPhone()" << endl; 56 | } 57 | void gps() const {} 58 | }; 59 | 60 | 61 | 62 | int main() { 63 | SmartPhone sp("13910110072", "MP4", "Android"); 64 | sp.call("62332018"); 65 | sp.play("high"); 66 | sp.run("qq"); 67 | cout << sizeof(sp) << endl; 68 | //编译器会做偏移,指向对应的子对象 69 | Phone* phone = &sp; 70 | Player* player = &sp; 71 | Computer* computer = &sp; 72 | cout << phone << ", " << player << ", " << computer << endl; 73 | 74 | // 编译器会做偏移,指向起始位置 75 | SmartPhone* psp = static_cast(computer); 76 | cout << psp << endl; 77 | 78 | // 只做类型转换,不做偏移计算 79 | psp = reinterpret_cast(computer); 80 | cout << psp << endl; 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /day07/04mulinherit02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04mulinherit02) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04mulinherit02 main.cpp) 7 | -------------------------------------------------------------------------------- /day07/04mulinherit02/README.md: -------------------------------------------------------------------------------- 1 | # 名字冲突问题 2 | 如果在类型的多个基类中,存在同名的标识符,而且子类又没有隐藏该名字,那么任何试图在子类中或通过子类对象访问该名字的操作,都将引发歧义,除非通过作用域限定符`::`显式指明所属基类 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /day07/04mulinherit02/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | void foo(int x) { 7 | cout << "A::foo()" << endl; 8 | } 9 | void fun() { 10 | cout << "A::fun()" << endl; 11 | } 12 | }; 13 | 14 | class B { 15 | public: 16 | void foo(double x) { 17 | cout << "B::foo()" << endl; 18 | } 19 | void fun() { 20 | cout << "B::fun()" << endl; 21 | } 22 | }; 23 | 24 | class C : public A, public B { 25 | public: 26 | C(bool flag) : m_flag(flag) {} 27 | // 引入当前作用域,构成重载 28 | using A::foo; 29 | using B::foo; 30 | // 如果无法构成重载,只能用作用域限定符 31 | // using A::fun; 32 | // using B::fun; 33 | void bar() { 34 | foo(10); 35 | foo(1.23); 36 | } 37 | 38 | // 提供一个隐藏的版本,根据业务逻辑来区分调用哪个 39 | void fun() { 40 | if (m_flag) { 41 | A::fun(); 42 | } else { 43 | B::fun(); 44 | } 45 | } 46 | 47 | private: 48 | bool m_flag; 49 | }; 50 | 51 | int main() { 52 | C c(true); 53 | /* 54 | // 作用域不同,不构成重载 55 | c.foo(100); // ERROR 56 | c.foo(1.23); // ERROR 57 | */ 58 | c.foo(10); 59 | c.foo(1.23); 60 | //c.A::fun(); 61 | //c.B::fun(); 62 | c.fun(); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /day07/05diamond/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05diamond) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05diamond main.cpp) 7 | -------------------------------------------------------------------------------- /day07/05diamond/README.md: -------------------------------------------------------------------------------- 1 | ## 钻石继承问题 2 | 3 | * 派生多个中间子类的公共基类子对象,在继承自多个中间子类的汇聚子类对象中,存在多个实例 4 | 5 | ![diamond](../../docs/pics/diamond.png) 6 | 7 | * 在汇聚子类中,或通过汇聚子类对象,访问公共基类成员,会因继承路径的不同而导致不一致 8 | 9 | * 通过虚继承,可以保证公共基类子对象在汇聚子类对象中,仅存一份实例,且为多个中间子类对象所共享 10 | 11 | ![diamondvirtual](../../docs/pics/diamondvirtual.png) 12 | 13 | ## 虚继承、虚基类、虚表和虚表指针 14 | 15 | * 在继承中使用virtual关键字 16 | * 位于继承链最末端的子类的构造函数负责构造虚基类子对象 17 | * 虚基类的所有子类(无论直接的还是间接的)都必须在其构造函数中显式指明该虚基类子对象的构造方式,否则编译器将选择以缺省方式构造该子对象 18 | * 虚基类的所有子类(无论直接的还是间接的)都必须在其拷贝构造函数中显式指明以拷贝构造该虚基类子对象,否则编译器将选择以缺省方式构造该子对象 19 | * 与构造函数和拷贝构造函数的情况不同,无论是否存在虚基类,拷贝赋值运算符函数的实现没有区别 20 | * 汇聚子类对象中的每个中间子类对象都持有一个虚表指针,该指针指向一个被称为虚表的指针数组的中部,该数组的高地址侧存放虚函数指针,低地址侧则存放所有虚基类地对象相对于每个中间子类对象起始地址的偏移量 21 | * 某些C++实现会将虚基类对象的绝对地址直接存放在中间子类子对象中,而另一些实现(比如微软)则提供了单独的虚基类表,但他们的原理都是一样 22 | * 包含虚继承的对象模型 23 | 24 | ![vinherit](../../docs/pics/vinherit.png) 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day07/05diamond/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | A(int n) : m_n(n) { 7 | cout << "A::A(int) : " << this << endl; 8 | } 9 | protected: 10 | int m_n; 11 | }; 12 | 13 | class X : virtual public A { 14 | public: 15 | X(int n) : A(n) { 16 | cout << "X::X(int) : " << this << endl; 17 | } 18 | int get() { 19 | cout << "X::get " << &m_n << endl; 20 | return m_n; 21 | } 22 | }; 23 | 24 | class Y : virtual public A { 25 | public: 26 | Y(int n) : A(n) { 27 | cout << "Y::Y(int) : " << this << endl; 28 | } 29 | 30 | void set(int n) { 31 | cout << "Y::set " << &m_n << endl; 32 | m_n = n; 33 | } 34 | }; 35 | 36 | class Z : public X, public Y { 37 | public: 38 | Z(int n): X(n), Y(n), A(n) { 39 | cout << "Z::Z(int) : " << this << endl; 40 | } 41 | Z(const Z& that): X(that), Y(that), A(that) { 42 | 43 | } 44 | }; 45 | 46 | 47 | int main() { 48 | Z z(100); 49 | z.set(200); 50 | cout << z.get() << endl; 51 | 52 | Z z2 = z; 53 | cout << z2.get() << endl; 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /day08/01world/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01world) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01world main.cpp) 7 | -------------------------------------------------------------------------------- /day08/01world/README.md: -------------------------------------------------------------------------------- 1 | # 非虚的世界 2 | 3 | * 对象的自洽性 4 | * 对同样的函数调用,每个对象都会做出恰当的响应 5 | * 通过指向子类对象的基类指针调用函数 6 | * 只能调用基类的成员函数,虽然指针指向子类对象 7 | * 一旦调用子类所特有的成员函数,将引发编译错误 8 | * 通过指向基类对象的子类指针调用函数 9 | * 可以调用子类的成员函数,尽管指针指向基类对象 10 | * 直接或间接地访问子类的成员变量,后果不可预知 11 | * 名字隐藏 12 | * 子类成员函数隐藏基类的同名成员函数 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day08/01world/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Cat { 5 | public: 6 | void talk() { 7 | cout << "miao!" << endl; 8 | } 9 | }; 10 | 11 | class Dog { 12 | public: 13 | void talk() { 14 | cout << "wang!" << endl; 15 | } 16 | }; 17 | 18 | class Human { 19 | public: 20 | void talk() { 21 | cout << "hey!" << endl; 22 | } 23 | }; 24 | 25 | class A { 26 | public: 27 | void foo() { 28 | cout << "A::foo()" << endl; 29 | } 30 | }; 31 | class B :public A { 32 | public: 33 | void bar() { 34 | cout << "B::bar()" << endl; 35 | cout << ++m_data << endl; 36 | } 37 | void foo() { 38 | cout << "B::foo()" << endl; 39 | } 40 | int m_data; 41 | }; 42 | 43 | int main() { 44 | Cat cat; 45 | Dog dog; 46 | Human human; 47 | cat.talk(); 48 | dog.talk(); 49 | human.talk(); 50 | 51 | B b; 52 | A* pa = &b; 53 | // 编译时,编译器只能看到类型,还没有对象 54 | // p->bar(); // ERROR 55 | 56 | A a; 57 | B* pb = static_cast(&a); 58 | pb->bar(); 59 | //pb = nullptr; 60 | //pb->bar(); // 运行时错误 61 | 62 | b.foo(); 63 | b.A::foo(); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /day08/02shape/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02shape) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02shape main.cpp) 7 | -------------------------------------------------------------------------------- /day08/02shape/README.md: -------------------------------------------------------------------------------- 1 | # 虚函数、覆盖和多态 2 | 3 | * 虚函数 4 | * 形如 5 | ``` 6 | class 类名 { 7 | virtual 返回类型 函数名(形参表) {...} 8 | }; 9 | ``` 10 | 的成员函数,称为虚函数或虚方法。 11 | * 覆盖 12 | * 如果子类的成员函数和基类的虚函数具有相同的函数原型,那么该成员函数也是虚函数,无论其是否带有`virtual`关键字,且对基类的虚函数构成覆盖 13 | * 多态 14 | * 如果子类提供了对基类虚函数的有效覆盖,那么通过一个指向子类对象的基类指针,或者引用子类对象的基类引用,调用该虚函数,实际被调用的将是子类中的覆盖版本,而非基类中的原始版本,这种现象称为多态。 15 | * 多态的重要意义在于,一般情况下,调用哪个类的成员函数是由调用者指针或引用本身的类型决定的,而当多态发生时,调用哪个类的成员函数则完全有调用者指针或引用的实际目标对象的类型决定的。 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /day08/02shape/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Shape { 5 | public: 6 | Shape(int x, int y) : m_x(x), m_y(y) {} 7 | virtual void draw() { 8 | cout << "draw shape(" << m_x << ',' << m_y <<')' << endl; 9 | } 10 | protected: 11 | int m_x; 12 | int m_y; 13 | }; 14 | 15 | class Rect : public Shape { 16 | public: 17 | Rect(int x, int y, int width, int height) : Shape(x, y), m_width(width), m_height(height) {} 18 | void draw() override { 19 | cout << "draw Rect(" << m_x <<',' << m_y <<',' << m_width << ',' << m_height << ')' << endl; 20 | } 21 | private: 22 | int m_width; 23 | int m_height; 24 | }; 25 | 26 | class Circle : public Shape { 27 | public: 28 | Circle(int x, int y, int radius) : Shape(x, y), m_radius(radius) {} 29 | void draw() override { 30 | cout << "draw Circle(" << m_x <<',' << m_y << ',' << m_radius << ')' << endl; 31 | } 32 | private: 33 | int m_radius; 34 | }; 35 | 36 | void render(Shape* shapes[]) { 37 | for (size_t i = 0; shapes[i]; ++i) { 38 | shapes[i]->draw(); 39 | } 40 | } 41 | 42 | int main() { 43 | Shape* shapes[1024]; 44 | shapes[0] = new Rect(1, 2, 3, 4); 45 | shapes[1] = new Circle( 5, 6, 7); 46 | shapes[2] = new Circle( 8, 9, 10); 47 | shapes[3] = new Rect( 11, 12, 13, 14); 48 | shapes[4] = new Rect( 15, 16, 17, 18); 49 | shapes[5] = nullptr; 50 | 51 | render(shapes); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /day08/03override/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03override) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03override main.cpp) 7 | -------------------------------------------------------------------------------- /day08/03override/README.md: -------------------------------------------------------------------------------- 1 | # 覆盖的条件 2 | 3 | * 该函数必须时成员函数,既不能是全局函数也不能是静态函数 4 | * 该函数必须在基类中用`virtual`关键字声明为虚函数 5 | * 覆盖版本与基类版本必须拥有完全相同的签名,即函数名、形参表和常属性严格一致 6 | * 如果基类版本返回基本类型数据,那么覆盖版本必须返回相同类型的数据 7 | * 如果基类版本返回类类型对象的指针或引用,那么覆盖版本可以返回其子类对象的指针或引用 8 | * 如果基类版本带有异常说明,那么覆盖版本不能说明比基类版本抛出更多的异常 9 | * 无论基类版本位于基类的公有、私有还是保护部分,覆盖版本都可以出现在子类包括公有、私有和保护在内的任何部分 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /day08/03override/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | class X {}; 4 | class T {}; 5 | class Y : public X {}; 6 | class Z : public Y, public T {}; 7 | 8 | class A { 9 | public: 10 | //virtual void foo() { 11 | virtual X* foo() { 12 | cout << "A::foo()" << endl; 13 | return nullptr; 14 | } 15 | }; 16 | 17 | class B : public A { 18 | public: 19 | B() {} 20 | private: 21 | // int foo() { // 返回类型如果是基本类型,则必须和基类虚函数原型一致 22 | // int foo() const { // 23 | // void foo() { 24 | // Y* foo() { 25 | Z* foo() { 26 | cout << "B::foo()" << endl; 27 | // return 0; 28 | return nullptr; 29 | } 30 | }; 31 | 32 | int main() { 33 | B b; 34 | A& rb = b; 35 | // 编译器在编译期只能看到类型,还没有对象 36 | // A中foo是公有的,执行的时发生多态,去B中执行 37 | rb.foo(); // B::foo 38 | 39 | A a1 = b; 40 | a1.foo(); // A::foo 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /day08/04poly/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04poly) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04poly main.cpp) 7 | -------------------------------------------------------------------------------- /day08/04poly/README.md: -------------------------------------------------------------------------------- 1 | # 多态的条件 2 | 3 | * 多态特性除了需要在基类中定义虚函数以外,还必须借助指针或者引用调用该虚函数,才能表现出来 4 | * 调用函数的指针也可能是基类中的`this`指针,同样能满足多态的条件,但是在构造和析构中除外 5 | * 全局函数形式的操作符函数无法实现多态,只有成员函数形式的操作符函数,才能借助于虚函数,表现出多态特性 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /day08/04poly/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | A() { 7 | // 基类部分先于子类部分构造,此时子类部分还没构造出来 8 | // this是指向基类对象的基类指针 9 | bar(); 10 | } 11 | 12 | ~A() { 13 | // 子类先析构,此时子类部分已经析构了,因此是指向基类对象的基类指针 14 | bar(); 15 | } 16 | 17 | void foo() { 18 | this->bar(); // 指向子类的基类指针调用子类的覆盖函数 19 | } 20 | 21 | virtual void bar() { 22 | cout << "A" << endl; 23 | } 24 | }; 25 | 26 | class B : public A { 27 | public: 28 | void bar() { 29 | cout << "B" << endl; 30 | } 31 | }; 32 | 33 | int main() { 34 | B b; 35 | b.foo(); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /day08/05oppoly/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05oppoly) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05oppoly main.cpp) 7 | -------------------------------------------------------------------------------- /day08/05oppoly/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Integer { 5 | public: 6 | Integer(int r) : m_r(r) {} 7 | virtual Integer& operator+=(const Integer& i) { 8 | m_r+=i.m_r; 9 | return *this; 10 | } 11 | 12 | int m_r; 13 | }; 14 | 15 | class Complex : public Integer { 16 | public: 17 | Complex(int r, int i) : Integer(r), m_i(i) {} 18 | // 参数表不同,无法形成有效覆盖 19 | // Complex& operator+=(const Complex& c) override { 20 | 21 | Complex& operator+=(const Integer& c) override { 22 | Integer::operator+=(c); 23 | m_i += dynamic_cast(c).m_i; 24 | return *this; 25 | } 26 | 27 | int m_i; 28 | }; 29 | 30 | int main() { 31 | Complex c1(1, 2), c2(3, 4); 32 | //c1 += c2; 33 | 34 | Integer& i1 = c1; 35 | Integer& i2 = c2; 36 | i1 += i2; 37 | cout << c1.m_r << '+' << c1.m_i <<'i' << endl; 38 | 39 | 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /day08/06pdf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06pdf) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06pdf main.cpp) 7 | -------------------------------------------------------------------------------- /day08/06pdf/README.md: -------------------------------------------------------------------------------- 1 | # 纯虚函数、抽象类和纯抽象类 2 | 3 | * 纯虚函数 4 | * 形如 5 | ``` 6 | class 类名 { 7 | virtual 返回类型 函数名(形参表) = 0; 8 | }; 9 | ``` 10 | 的虚函数,称为纯虚函数或抽象方法。 11 | 12 | * 抽象类 13 | * 至少拥有一个纯虚函数的类型称为抽象类 14 | * 抽象类不能实例化为对象 15 | * 抽象类的子类如果不对基类中的全部纯虚函数提供有效的覆盖,那么该子类也是抽象类 16 | 17 | * 纯抽象类 18 | * 全部由纯虚函数构成的抽象类称为纯抽象类或接口 19 | 20 | 21 | # 好莱坞模式 - 职责分离 22 | 23 | ![hollywood](../../docs/pics/hollywood.png) 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /day08/06pdf/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 模板方法模式(好莱坞模式) 5 | class PDFParser { 6 | public: 7 | void parse(const char* pdffile) { 8 | // 解析矩形 9 | // ... 10 | this->onRect(); 11 | 12 | // 解析圆 13 | // ... 14 | onCircle(); 15 | 16 | // 解析文字 17 | // ... 18 | onText(); 19 | 20 | // 解析图片 21 | // ... 22 | onImage(); 23 | } 24 | 25 | private: 26 | virtual void onRect() = 0; 27 | virtual void onCircle() = 0; 28 | virtual void onText() = 0; 29 | virtual void onImage() = 0; 30 | }; 31 | 32 | class PDFRender : public PDFParser { 33 | private: 34 | void onRect() override { 35 | cout << "draw Rect" << endl; 36 | } 37 | void onCircle() override { 38 | cout << "draw Circle" << endl; 39 | } 40 | void onText() override { 41 | cout << "draw Text" << endl; 42 | } 43 | void onImage() override { 44 | cout << "draw Image" << endl; 45 | } 46 | }; 47 | 48 | int main() { 49 | PDFRender render; 50 | // 发生多态 51 | render.parse("something.pdf"); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /day08/07vptr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(07vptr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07vptr main.cpp) 7 | -------------------------------------------------------------------------------- /day08/07vptr/README.md: -------------------------------------------------------------------------------- 1 | # 虚函数表与动态绑定 2 | 3 | ## 动态绑定 4 | 5 | * 当编译器看到通过指针或引用调用虚函数的语句时,并不急于生成有关函数调用的指令,相反它会用一段代码替代该语句,这段代码在运行时被执行,完成如下操作: 6 | * 确定调用指针或引用的目标对象的真实类型 7 | * 从调用指针或引用的目标对象中找到虚函数表,并从虚函数表中获取调用虚函数的入口地址 8 | * 根据入口地址,调用该函数 9 | 10 | * 动态绑定对性能的影响 11 | * 虚函数表本省会增加内存空间的开销 12 | * 与普通函数调用相比,虚函数调用要多出几个步骤,增加运行时间的开销 13 | * 动态绑定会妨碍编译器通过内联来优化代码 14 | * 只有在确实需要多态特性的场合才是用虚函数,否则尽量使用普通函数 15 | 16 | ![vtable](../../docs/pics/vtable.png) 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day08/07vptr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | virtual void foo() { 7 | cout << "A::foo()" << endl; 8 | } 9 | virtual void bar() { 10 | cout << "A::bar()" << endl; 11 | } 12 | }; 13 | 14 | class B : public A { 15 | public: 16 | void foo() override { 17 | cout << "B::foo()" << endl; 18 | } 19 | }; 20 | 21 | 22 | int main() { 23 | /* 24 | B b; 25 | A* pa = &b; 26 | pa->foo(); 27 | pa->bar(); 28 | */ 29 | 30 | A a; 31 | void(**vptr)() = *(void(***)())&a; 32 | cout << vptr << endl; 33 | cout << (void*)vptr[0] << ' ' << (void*)vptr[1] << endl; 34 | vptr[0](); 35 | vptr[1](); 36 | 37 | cout << "--------------------------------------------" << endl; 38 | B b; 39 | vptr = *(void(***)())&b; 40 | cout << vptr << endl; 41 | cout << (void*)vptr[0] << ' ' << (void*)vptr[1] << endl; 42 | vptr[0](); 43 | vptr[1](); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /day08/08dc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08dc) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08dc main.cpp) 7 | -------------------------------------------------------------------------------- /day08/08dc/README.md: -------------------------------------------------------------------------------- 1 | # 运行时类型信息(RTTI) 2 | 3 | ## 动态类型转化(dynamic_cast) 4 | 5 | * 用于将基类类型的指针或引用转换为其子类类型的指针或引用,前提是子类必须从基类多态继承,即基类至少包含一个虚函数 6 | * 动态类型转会会对所需转换的基类指针或引用做检查,如果其目标确实为期望得到的子类类型的对象,则转换成功,否则转换失败 7 | * 针对指针的动态类型转换,以返回空指针(NULL)表示失败,针对应用的动态类型转换,以抛出`bad_cast`异常表示失败 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /day08/08dc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class A { 5 | public: 6 | virtual void foo() {} 7 | }; 8 | 9 | class B : public A {}; 10 | class C : public B {}; 11 | class D {}; 12 | 13 | int main() { 14 | B b; 15 | A* pa = &b; 16 | A& ra = b; 17 | cout << "pa = " << pa << endl; 18 | cout << "-----dynamic_cast-----" << endl; 19 | B* pb = dynamic_cast(pa); 20 | cout << "pb = " << pb << endl; 21 | C* pc = dynamic_cast(pa); 22 | cout << "pc = " << pc << endl; 23 | try { 24 | C& rc = dynamic_cast(ra); 25 | } 26 | catch (exception& e) { 27 | cout << "dynamic cast: " << e.what() << endl; 28 | } 29 | D* pd = dynamic_cast(pa); 30 | cout << "pd = " << pd << endl; 31 | 32 | cout << "-----static_cast-----" << endl; 33 | pb = static_cast(pa); 34 | cout << "pb = " << pb << endl; 35 | pc = static_cast(pa); 36 | cout << "pc = " << pc << endl; 37 | //pd = static_cast(pa); 38 | //cout << "pd = " << pd << endl; 39 | 40 | cout << "-----reinterpret_cast-----" << endl; 41 | pb = reinterpret_cast(pa); 42 | cout << "pb = " << pb << endl; 43 | pc = reinterpret_cast(pa); 44 | cout << "pc = " << pc << endl; 45 | pd = reinterpret_cast(pa); 46 | cout << "pd = " << pd << endl; 47 | 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /day08/09type/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(09type) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(09type main.cpp) 7 | -------------------------------------------------------------------------------- /day08/09type/README.md: -------------------------------------------------------------------------------- 1 | ## typeid操作符 2 | 3 | * `#include ` 4 | * 返回`typeinfo`类型对象的异常 5 | * `typeinfo`类的成员函数`name()`,返回空字符结尾的类型名字符串首地址 6 | * `typeinfo`类支持`==`和`!=`操作符,可直接用于类型相同与否的判断 7 | * 当其作用于基类类型的指针或应用的目标时,若基类至少包含一个虚函数,即存在多态继承,`typeid`所返回类型信息将由该指针或应用的实际目标对象的类型决定,否则由该指针或应用本身的类型决定 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /day08/09type/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class Human {}; 8 | class Student : public Human {}; 9 | 10 | class Animal{ 11 | public: 12 | virtual void eat(){} 13 | }; 14 | class Cat : public Animal {}; 15 | class Dog : public Animal {}; 16 | 17 | void bar(Animal* pa) { 18 | //if (!strcmp(typeid(*pa).name(), "3Cat")) { 19 | if (typeid(*pa) == typeid(Cat)){ 20 | cout << "Cat!!!" << endl; 21 | } 22 | //else if (!strcmp(typeid(*pa).name(), "3Dog")) { 23 | else if (typeid(*pa) == typeid(Dog)) { 24 | cout << "Dog!!!" << endl; 25 | } 26 | else { 27 | cout <<"unknown" << endl; 28 | } 29 | } 30 | 31 | int main() { 32 | cout << typeid(char).name() << endl; // c 33 | cout << typeid(unsigned char).name() << endl; // h 34 | cout << typeid(int).name() << endl; // i 35 | cout << typeid(int*).name() << endl; // Pi 36 | cout << typeid(unsigned int).name() << endl; // j 37 | cout << typeid(float [10]).name() << endl; // A10_f 38 | cout << typeid(int [10]).name() << endl; // A10_i 39 | cout << typeid(int* [10]).name() << endl; // A10_Pi 40 | cout << typeid(int** [10]).name() << endl; // A10_PPi 41 | cout << typeid(int(*) [10]).name() << endl; // PA10_i 42 | cout << typeid(int(*)(float)).name() << endl; // PFifE 43 | cout << typeid(int(*[10])(float)).name() << endl; // A10_PFifE 44 | cout << typeid(const char*).name() << endl; // PKc 45 | cout << typeid(char const*).name() << endl; // PKc 46 | cout << typeid(char* const).name() << endl; // Pc 47 | cout << typeid(int&).name() << endl; // i 48 | cout << typeid(Student).name() << endl; // 7Student 49 | 50 | Human* ph = new Student(); 51 | cout << typeid(ph).name() << endl; // P5Human 52 | cout << typeid(*ph).name() << endl; // 5Human 53 | 54 | Animal* pc = new Cat(); 55 | cout << typeid(pc).name() << endl; // P6Animal 56 | // 虚函数表中还有类型信息,typeid会根据虚函数表中的类型信息判断 57 | cout << typeid(*pc).name() << endl; // 3Cat 58 | 59 | Cat c; 60 | Dog d; 61 | bar(&c); 62 | bar(&d); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /day08/10vd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(10vd) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(10vd main.cpp) 7 | -------------------------------------------------------------------------------- /day08/10vd/README.md: -------------------------------------------------------------------------------- 1 | # 虚析构 2 | 3 | * delete一个指向子类对象的基类指针 4 | * 实际被调用的仅仅是基类的析构函数 5 | * 基类的析构函数负责析构子类对象的基类子对象 6 | * 基类的析构函数不会调用子类的析构函数 7 | * 在子类中分配的资源将形成内存泄漏 8 | 9 | ![vdes1](../../docs/pics/vdes1.png) 10 | * 如果将基类的析构函数声明为虚函数,那么实际被调用的将是子类的析构函数 11 | * 子类的析构函数将首先析构子类对象的扩展部分,然后再通过基类的析构函数析构该对象基类部分,最终实现完美的资源释放 12 | 13 | ![vdes2](../../docs/pics/vdes2.png) 14 | 15 | * 空虚析构函数 16 | * 没有分配任何资源的类,无需定义析构函数 17 | * 没有定义析构函数的类,编译器会为其提供一个缺省析构函数,但缺省析构函数并不是虚函数 18 | * 为了保证delete一个指向子类对象的基类指针时,能够正确调用子类的析构函数,就必须把基类的析构函数定义为虚函数,即使它是一个空函数 19 | 20 | * 一个类中,除了构造函数和静态成员函数外,任何函数都可以被声明为虚函数 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /day08/10vd/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class X { 5 | public: 6 | X() { 7 | cout << "X::X()" << endl; 8 | } 9 | ~X() { 10 | cout << "X::~X()" << endl; 11 | } 12 | }; 13 | 14 | class A { 15 | public: 16 | A() { 17 | cout << "A::A()" << endl; 18 | } 19 | virtual ~A(){ 20 | cout << "A::~A()" << endl; 21 | } 22 | }; 23 | 24 | class B : public A { 25 | public: 26 | B() { 27 | cout << "B::B()" << endl; 28 | } 29 | ~B() { 30 | cout << "B::~B()" << endl; 31 | } 32 | private: 33 | X m_x; 34 | }; 35 | 36 | 37 | int main() { 38 | // 子类析构时会调用基类的析构 39 | // B* pa = new B; 40 | // 基类析构时不会调用子类的析构,除非用virtual修饰析构函数 41 | A* pa = new B; 42 | delete pa; 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /day08/11absfactory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(11absfactory) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(11absfactory main.cpp) 7 | -------------------------------------------------------------------------------- /day08/11absfactory/README.md: -------------------------------------------------------------------------------- 1 | # 抽象工程模式-接口与解耦 2 | 3 | ![absfactory](../../docs/pics/absfactory.png) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /day08/11absfactory/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Employee {}; 5 | class DaoEmployee { 6 | public: 7 | ~DaoEmployee(){} 8 | virtual void insert(const Employee& e) = 0; 9 | virtual Employee select(int id) = 0; 10 | }; 11 | 12 | class SQLServerEmployee : public DaoEmployee { 13 | public: 14 | void insert(const Employee& e) override { 15 | cout << "SQLServer insert employee ..." << endl; 16 | } 17 | Employee select(int id) override { 18 | cout << "SQLServer select employee ..." << endl; 19 | return Employee(); 20 | } 21 | }; 22 | 23 | class OracleEmployee : public DaoEmployee { 24 | public: 25 | void insert(const Employee& e) override { 26 | cout << "Oracle insert employee ..." << endl; 27 | } 28 | Employee select(int id) override { 29 | cout << "Oracle select employee ..." << endl; 30 | return Employee(); 31 | } 32 | }; 33 | 34 | class Dept{}; 35 | class DaoDept { 36 | public: 37 | virtual ~DaoDept(){} 38 | virtual void insert(const Dept& d) = 0; 39 | virtual Dept select(int id) = 0; 40 | }; 41 | 42 | class SQLServerDept : public DaoDept { 43 | public: 44 | void insert(const Dept& e) override { 45 | cout << "SQLServer insert dept ..." << endl; 46 | } 47 | Dept select(int id) override { 48 | cout << "SQLServer select dept ..." << endl; 49 | return Dept(); 50 | } 51 | }; 52 | 53 | class OracleDept : public DaoDept { 54 | public: 55 | void insert(const Dept& e) override { 56 | cout << "Oracle insert dept ..." << endl; 57 | } 58 | Dept select(int id) override { 59 | cout << "Oracle select dept ..." << endl; 60 | return Dept(); 61 | } 62 | }; 63 | 64 | class DaoFactory{ 65 | public: 66 | virtual ~DaoFactory() {} 67 | virtual DaoEmployee* createEmployee() = 0; 68 | virtual DaoDept* createDept() = 0; 69 | }; 70 | 71 | class SQLServerFactory : public DaoFactory { 72 | public: 73 | DaoEmployee* createEmployee() override { 74 | return new SQLServerEmployee(); 75 | } 76 | 77 | DaoDept* createDept() override { 78 | return new SQLServerDept(); 79 | } 80 | }; 81 | 82 | class OracleFactory : public DaoFactory { 83 | public: 84 | OracleEmployee* createEmployee() override { 85 | return new OracleEmployee(); 86 | } 87 | 88 | OracleDept* createDept() override { 89 | return new OracleDept(); 90 | } 91 | }; 92 | 93 | class Client { 94 | public: 95 | Client( DaoFactory* factory) : m_factory(factory) {} 96 | 97 | void business() const { 98 | Employee employee; 99 | DaoEmployee* daoEmployee = m_factory->createEmployee(); 100 | daoEmployee->insert(employee); 101 | daoEmployee->select(1001); 102 | delete daoEmployee; 103 | 104 | Dept dept; 105 | DaoDept* daoDept = m_factory->createDept(); 106 | daoDept->insert(dept); 107 | daoDept->select(1010); 108 | delete daoDept; 109 | } 110 | 111 | private: 112 | DaoFactory* m_factory; 113 | }; 114 | 115 | int main() { 116 | //OracleFactory factory; 117 | SQLServerFactory factory; 118 | Client client(&factory); 119 | client.business(); 120 | 121 | 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /day09/01reterr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01reterr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01reterr main.cpp) 7 | -------------------------------------------------------------------------------- /day09/01reterr/README.md: -------------------------------------------------------------------------------- 1 | # 错误与错误处理 2 | 3 | * 何为错误 4 | * 语法错误:程序员在编码阶段解决并通过编译 5 | * 逻辑错误:程序员借助于调试工具诊断并修改 6 | * 功能错误:程序员修改代码,测试员回归验证 7 | * 设计缺陷:设计员修改设计,程序员重新编码 8 | * 需求不符:分析员修改需求,设计员调整设计 9 | * 环境异常:可以协助用户调整程序的运行环境 10 | * 操作不当:客服指导用户按照正确的方法操作 11 | 12 | * 错误处理主要针对在实际运行环境中发生,却在设计、编码和测试阶段无法预料的,各种潜在的异常 13 | 14 | * 三种典型的错误处理机制 15 | * 通过返回值返回错误信息 16 | * 所有局部对象都能正确地被析构 17 | * 逐层判断,流程繁琐 18 | * 借助`setjmp/longjmp`远程跳转 19 | * 一步到位,流程简单 20 | * 某些局部对象可能因此丧失被析构的机会 21 | * 抛出-捕获异常对象 22 | * 形式上一步到位,流程简单 23 | * 实际上逐层析构局部对象,避免内存泄漏 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /day09/01reterr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class A { 7 | public: 8 | A() { 9 | cout << "A::A()" << endl; 10 | } 11 | ~A() { 12 | cout << "A::~A()" << endl; 13 | } 14 | }; 15 | 16 | int func3() { 17 | A a; 18 | FILE* fp = fopen("none.txt", "r"); 19 | if (! fp) { 20 | cout << "fopen failed" << endl; 21 | return -1; // return下一步到函数的右括号,它的指令就包括对局部变量的析构 22 | } 23 | // ... 24 | fclose(fp); 25 | return 0; 26 | } 27 | 28 | int func2() { 29 | A a; 30 | if (func3() == -1) { 31 | return -1; 32 | } 33 | // ... 34 | return 0; 35 | } 36 | 37 | int func1() { 38 | A a; 39 | if (func2() == -1) { 40 | return -1; 41 | } 42 | // ... 43 | return 0; 44 | } 45 | 46 | 47 | int main() { 48 | if (func1() == -1) { 49 | return -1; 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /day09/02jmperr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02jmperr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02jmperr main.cpp) 7 | -------------------------------------------------------------------------------- /day09/02jmperr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | jmp_buf g_env; 8 | 9 | class A { 10 | public: 11 | A() { 12 | cout << "A::A()" << endl; 13 | } 14 | ~A() { 15 | cout << "A::~A()" << endl; 16 | } 17 | }; 18 | 19 | void func3() { 20 | A a; 21 | FILE* fp = fopen("none.txt", "r"); 22 | if (!fp) { 23 | // goto不能跨函数,即goto和escape要在同一个函数 24 | //goto escape; 25 | longjmp(g_env, -1); 26 | } 27 | // ... 28 | fclose(fp); 29 | } 30 | 31 | void func2() { 32 | A a; 33 | func3(); 34 | } 35 | 36 | void func1() { 37 | A a; 38 | func2(); 39 | } 40 | 41 | 42 | int main() { 43 | // 第一次调用setjmp会返回0,把当前栈帧存到g_env中 44 | if (setjmp(g_env) == -1) { 45 | cout << "fopen failed" << endl; 46 | return -1; 47 | } 48 | 49 | func1(); 50 | 51 | return 0; 52 | 53 | escape: 54 | cout << "fopen failed" << endl; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /day09/03experr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03experr) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03experr main.cpp) 7 | -------------------------------------------------------------------------------- /day09/03experr/README.md: -------------------------------------------------------------------------------- 1 | # 异常处理语法 2 | 3 | ## 抛出异常 4 | * throw异常对象 5 | * 可以抛出基本类型的对象,如 6 | ``` 7 | throw -1; 8 | throw "内存分配失败"; 9 | ``` 10 | 11 | * 可以抛出类类型对象,如 12 | ``` 13 | MemoryException ex; 14 | throw ex; 15 | throw MemoryException(); 16 | ``` 17 | 18 | * 不要抛出局部对象的指针,如: 19 | ``` 20 | MemoryException ex; 21 | throw &ex; //错误 22 | ``` 23 | 24 | ## 捕获异常 25 | 26 | * 语法 27 | 28 | ``` 29 | try { 30 | 可能引发异常的语句; 31 | } 32 | catch(异常类型1& ex){ 33 | 针对异常类型1的异常处理; 34 | } 35 | catch(异常类型2& ex){ 36 | 针对异常类型2的异常处理; 37 | } 38 | ... 39 | catch(...){ 40 | 针对其他异常类型的异常处理; 41 | } 42 | ``` 43 | 44 | * 根据异常对象的类型自上而下顺序匹配,而非最优匹配,因此对子类类型异常的捕获不要放在对基类异常的捕获后面 45 | * 建议在catch子句中使用使用接收异常对象,避免因为拷贝构造带来性能损失,或引发新的异常 46 | * 异常对象 47 | * 为每一种异常定义相应的异常类型 48 | * 推荐以匿名临时对象的形式抛出异常 49 | * 异常对象必须允许被拷贝构造和析构(throw的时候要拷贝,catch完成后要释放) 50 | * 建议从标准库异常中派生自己的异常 51 | 52 | * 未发生异常时的流程 53 | 54 | ![noexception](../../docs/pics/noexception.png) 55 | 56 | * 异常发生时的流程 57 | 58 | ![exception](../../docs/pics/exception.png) 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /day09/03experr/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class A { 8 | public: 9 | A() { 10 | cout << "A::A()" << endl; 11 | } 12 | ~A() { 13 | cout << "A::~A()" << endl; 14 | } 15 | }; 16 | 17 | class FileError { 18 | public: 19 | FileError(const string& file, int line) : m_file(file), m_line(line) {} 20 | friend ostream& operator<<(ostream& os, const FileError& ex) { 21 | return os << ex.m_file << ':' << ex.m_line; 22 | } 23 | private: 24 | string m_file; 25 | int m_line; 26 | }; 27 | 28 | class MemoryError{}; 29 | class SocketError{}; 30 | class Error {}; 31 | class MathError: public Error {}; 32 | 33 | void func3() { 34 | A a; 35 | // ... 36 | throw MathError(); 37 | throw SocketError(); 38 | 39 | FILE* fp = fopen("none.txt", "r"); 40 | if (!fp) { 41 | // 异常会放到安全区中,系统保证了异常从抛出到捕获期间,安全区的数据是安全的(不会被修改) 42 | // 把throw的异常复制到安全区,catch从安全区中取 43 | // 异常执行流程: 44 | // 1.跳转到当func3()的右括号 45 | // 2.跳转到当func2()的右括号 46 | // 3.跳转到当func1()的右括号 47 | // 4.跳转到当try的右括号 48 | // 5.在catch中有没有处理异常值的代码 49 | //throw -1; 50 | throw FileError(__FILE__, __LINE__); 51 | } 52 | 53 | char* buf = (char*)malloc(0xFFFFFFFFFFFF); 54 | if (!buf) { 55 | //throw -2; 56 | throw MemoryError(); 57 | } 58 | 59 | // ... 60 | fclose(fp); 61 | } 62 | 63 | void func2() { 64 | A a; 65 | func3(); 66 | } 67 | 68 | void func1() { 69 | A a; 70 | func2(); 71 | } 72 | 73 | 74 | int main() { 75 | try { 76 | func1(); 77 | // func1()抛出异常时,不执行其后面的代码 78 | cout << "after exception" << endl; 79 | } 80 | // catch(int ex) { 81 | // if (ex == -1) { 82 | // cout << "fopen failed" << endl; 83 | // return -1; 84 | // } 85 | // else if (ex == -2) { 86 | // cout << "alloc memory failed" << endl; 87 | // return -1; 88 | // } 89 | // } 90 | catch (FileError& ex) { 91 | cout << "fopen failed" << endl; 92 | cout << ex << endl; 93 | return -1; 94 | } 95 | catch (MemoryError& ex) { 96 | cout << "alloc memory failed" << endl; 97 | return -1; 98 | } 99 | catch (MathError& ex) { 100 | cout << "math error" << endl; 101 | return -1; 102 | } 103 | catch (Error& ex) { 104 | cout << "error" << endl; 105 | return -1; 106 | } 107 | catch (...) { 108 | cout << "other error" << endl; 109 | return -1; 110 | } 111 | return 0; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /day09/04stdexp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04stdexp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04stdexp main.cpp) 7 | -------------------------------------------------------------------------------- /day09/04stdexp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class FileError : public exception { 5 | public: 6 | const char* what() const throw() override { 7 | return "file error"; 8 | } 9 | }; 10 | 11 | 12 | int main() { 13 | try { 14 | throw FileError(); 15 | 16 | char* p = new char[0xFFFFFFFFFF]; 17 | } 18 | catch (exception& ex) { 19 | cout << ex.what() << endl; 20 | return -1; 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /day09/05expdeclare/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05expdeclare) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | add_executable(05expdeclare main.cpp) 7 | -------------------------------------------------------------------------------- /day09/05expdeclare/README.md: -------------------------------------------------------------------------------- 1 | ## 异常说明 2 | 3 | * 异常说明是函数原型的一部分,旨在说明函数可能抛出的异常类型 4 | 5 | ``` 6 | 返回类型 函数名(形参表) throw(异常类型1, 异常类型2, ...) { 7 | 函数体; 8 | } 9 | ``` 10 | 11 | * 异常说明是一种承诺,承诺函数不会抛出异常说明以外的异常类型 12 | * 如果函数抛出了异常说明以外的异常类型,那么该异常将无法被捕获,并导致进程中止 13 | * `std::unexpected() -> std::terminate() -> abort()` 14 | * 隐式抛出异常的函数也可以列出它的异常说明 15 | * 异常说明可以没有也可以为空 16 | * 没有异常说明,表示可能抛出任何类型的异常:`void foo() { ... }` 17 | * 异常说明为空,表示不会抛出任何类型的异常:`void foo() throw() { ... }` 18 | * 异常说明是虚函数覆盖的条件之一 19 | * 如果基类中的虚函数带有异常说明,那么子类的覆盖版本不能说明比基类版本抛出更多的异常 20 | * 异常说明在函数的声明和定义中必须保持严格一致,否则将导致编译错误 21 | 22 | ## 异常处理模式 23 | 24 | * 抛出基本类型的异常,根据异常对象的值分别处理 25 | * 抛出类类型的异常,根据异常对象的类型分别处理 26 | * 利用类类型的异常,携带更多诊断信息,以便查错 27 | * 忽略异常,不做处理 28 | 29 | ![handle](../../docs/pics/handleexp.png) 30 | 31 | * 从catch块中继续抛出所捕获的异常,或其他异常 32 | * 任何未被捕获的异常,都会由`std::unexpected()`函数处理,缺省的处理方式是中止进程 33 | * 抛出标准库异常,或标准库异常的子类异常,将允许用户以与标准库一致的方式捕获该异常 34 | 35 | ![continue](../../docs/pics/continue_throw.png) 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /day09/05expdeclare/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class FileError {}; 5 | class MemoryError {}; 6 | 7 | void foo() throw(FileError, MemoryError) { 8 | throw -1; 9 | throw MemoryError(); 10 | throw FileError(); 11 | } 12 | 13 | int main() { 14 | try { 15 | foo(); 16 | } 17 | catch (FileError& e) { 18 | cout << "file error" << endl; 19 | } 20 | catch (MemoryError& e) { 21 | cout << "memory error" << endl; 22 | } 23 | catch (...) { 24 | cout << "other error" << endl; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /day09/06continuethrow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06continuethrow) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06continuethrow main.cpp) 7 | -------------------------------------------------------------------------------- /day09/06continuethrow/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | void hum() { 7 | // ... 8 | throw 1; 9 | } 10 | 11 | void bar() { 12 | char* buf = (char*)malloc(256); 13 | try { 14 | hum(); 15 | } 16 | //catch (int ex) { // 捕获的ex是从安全区复制的异常的一个副本 17 | catch (int& ex) { 18 | cout << ex << " hum failed" << endl; 19 | free(buf); 20 | ++ex; 21 | throw; // 把捕获的异常继续往上扔 22 | } 23 | } 24 | 25 | void foo() { 26 | FILE* fp = fopen("good.txt", "r"); 27 | try { 28 | bar(); 29 | } 30 | catch(int& ex) { 31 | cout << ex << " bar failed" << endl; 32 | fclose(fp); 33 | ++ex; 34 | throw; 35 | } 36 | } 37 | 38 | int main() { 39 | try { 40 | foo(); 41 | } 42 | catch (int& ex) { 43 | cout << ex << " foo failed" << endl; 44 | return -1; 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /day09/07consexp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(07consexp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07consexp main.cpp) 7 | -------------------------------------------------------------------------------- /day09/07consexp/README.md: -------------------------------------------------------------------------------- 1 | # 构造函数中的异常 2 | 3 | * 构造函数可以抛出异常,某些时候还必须抛出异常 4 | * 构造过程中可能遇到各种错误,比如内存分配失败 5 | * 构造函数没有返回值,无法通过返回值通知调用者 6 | * 构造函数抛出异常,对象将被不完整构造,而一个被不完整构造的对象,其析构函数永远不会被执行 7 | * 构造函数的回滚机制,可以保证所有对象形式的成员变量,在抛出异常的瞬间,都能得到正确的析构 8 | * 所有动态分配的资源,必须在抛出异常之前手动释放,否则将形成内存泄漏,除非使用了智能指针 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /day09/07consexp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | class A { 7 | public: 8 | A() { cout << "A::A()" << endl; } 9 | ~A() { cout << "A::~A()" << endl; } 10 | }; 11 | 12 | class C { 13 | public: 14 | C() { cout << "C::C()" << endl; } 15 | ~C() { cout << "C::~C()" << endl; } 16 | }; 17 | 18 | class B { 19 | public: 20 | B() : m_apa(new A) { 21 | m_a = new A; 22 | FILE* fp = fopen("none.txt", "r"); 23 | if (!fp) { 24 | delete m_a; 25 | throw -1; 26 | } 27 | fclose(fp); 28 | } 29 | ~B() { 30 | delete m_a; 31 | } 32 | 33 | private: 34 | A* m_a; 35 | auto_ptr m_apa; // 智能指针是类 36 | C m_c; // 类类型对象,会被正确析构 37 | }; 38 | 39 | 40 | 41 | int main() { 42 | try { 43 | B b; 44 | } 45 | catch (int& ex) { 46 | cout << "err: " << ex << endl; 47 | } 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /day09/08finally/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08finally) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08finally main.cpp) 7 | -------------------------------------------------------------------------------- /day09/08finally/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Finally 7 | { 8 | public: 9 | Finally(const char* filename) : m_fp(fopen(filename, "r")) {} 10 | ~Finally() { 11 | cout << "Finally::~Finally()" << endl; 12 | if (m_fp) { 13 | delete m_fp; 14 | } 15 | } 16 | 17 | private: 18 | FILE* m_fp; 19 | }; 20 | 21 | void fun() { 22 | throw -1; 23 | } 24 | 25 | int foo() { 26 | Finally f("a.out"); 27 | try{ 28 | fun(); 29 | } 30 | catch (int& ex) { 31 | cout << "err: " << ex << endl; 32 | return -1; 33 | } 34 | 35 | return 0; 36 | } 37 | 38 | int main() 39 | { 40 | foo(); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /day09/10descexp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(10descexp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(10descexp main.cpp) 7 | -------------------------------------------------------------------------------- /day09/10descexp/README.md: -------------------------------------------------------------------------------- 1 | # 析构函数中的异常 2 | 3 | * 不要从析构函数中主动抛出异常 4 | * 在两种情况下,析构函数会被调用 5 | * 正常销毁对象,离开作用域或显式delete 6 | * 在异常传递的堆栈辗转开解过程中,由异常处理系统销毁对象 7 | * 针对第二钟情况,异常正处于激活状态,而析构函数又抛出了一场,并试图将流程转移至析构函数以外,这时C++将通过`std::terminate()`函数,令进程中止 8 | * 对于可能引发异常的操作,尽量内部消化 9 | 10 | ``` 11 | try { ... } catch(...) { ... } 12 | ``` 13 | 14 | # 标准库异常 15 | 16 | ![stdexception](../../docs/pics/stdexp.png) 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /day09/10descexp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | void bar() 5 | { 6 | throw -3; 7 | } 8 | 9 | class A 10 | { 11 | public: 12 | void foo() 13 | { 14 | throw -1; 15 | } 16 | ~A() 17 | { 18 | // 内部消化 19 | try 20 | { 21 | bar(); 22 | } 23 | catch (int& ex) 24 | { 25 | cout << "bar err: " << ex << endl; 26 | } 27 | catch (...) 28 | { 29 | 30 | } 31 | // 不要从析构函数中主动抛出异常 32 | // throw -2; 33 | } 34 | }; 35 | 36 | int main() 37 | { 38 | A a; 39 | try 40 | { 41 | a.foo(); 42 | } 43 | catch (int& ex) 44 | { 45 | cout << "foo err: " << ex << endl; 46 | return -1; 47 | } 48 | cout << "ok" << endl; 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /day10/01open/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(01open) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(01open main.cpp) 7 | -------------------------------------------------------------------------------- /day10/01open/README.md: -------------------------------------------------------------------------------- 1 | # I/O流的基本概念 2 | 3 | * 流(stream) 4 | * 字节序形式的数据,犹如流水一般,从一个对象流向另一个对象 5 | * 输入流(Input Stream) 6 | * 数据字节从表示输入设备(如键盘、磁盘文件等)的对象流向内存对象,如:`cin>>student;` 7 | * 输出流(Output Stream) 8 | * 数据字节从内存对象流向表示输出设备(如显示器、打印机、磁盘文件等)的对象,如`cout << student;` 9 | 10 | * 流数据(Stream Data) 11 | * 各种形式的字节序列,二进制数据、文本字符、图片图像、音频视频等 12 | * 流缓冲(Stream Buffer) 13 | * 介于各种I/O设备和内存对象之间的内存缓冲区 14 | * 当从键盘输入时,数据首先进入键盘缓冲区,直到按下回车键,才将键盘缓冲中的数据灌注到输入流缓冲区,之后再通过流操作符`>>`进入内存对象 15 | * 当向显示器输出时,数据首先通过流操作符`<<`从内存对象进入输出流缓冲区,直到缓冲区满活遇到换行符,才将其中的数据灌注到显示器上显示出来。 16 | 17 | * 流对象 18 | * 表示各种输入输出设备的对象,如键盘、显示器、打印机、磁盘文件等,因其皆以流的方式接收或提供数据,故称为流对象 19 | * 向下访问各种物理设备接口,向上与应用程序交互,中间维护流缓冲区 20 | * 四个预定义的标准流对象 21 | * `cin`:标准输入设备——键盘 22 | * `cout`:标准输出设备——显示器 23 | * `cerr`:标准错误输出设备——显示器,不带缓冲 24 | * `clog`:标准错误输出设备——显示器 25 | 26 | * 流类(Stream Class) 27 | * 用于实例化流对象的类 28 | * `cin`和`cout`分别是`istream_withassgin`和`ostream_withassign`类的对象 29 | 30 | * 流类库(Stream Class Library) 31 | * C++以继承方式定义了一组流类,并将其作为标准C++库的一部分提供给用户 32 | * 基于流类库可以构建三种形式的流对象 33 | * 面向控制台的I/O流 34 | * 面向文件的I/O流 35 | * 面向内存的I/O流 36 | 37 | # IO流类库 38 | 39 | ![iosbase](../../docs/pics/iosbase.png) 40 | 41 | * 流缓冲类(streambuf) 42 | * 实现对流缓冲区的低级操作,如设置缓冲区、控制缓冲区指针、从缓冲区提取字符、向缓冲区存储字符等 43 | * 控制台流缓冲类(conbuf) 44 | * 从流缓冲类(streambuf)派生 45 | * 实现控制光标、设置颜色、定义活动窗口、清屏等功能,为面向控制台的输入输出提供缓冲区管理 46 | * 文件流缓冲类(filebuf) 47 | * 从流缓冲类(streambuf)派生 48 | * 实现文件的打开关闭、读取写入、随机访问等功能,为面向文件的输入输出提供缓冲区管理 49 | 50 | * 字符串流缓冲类(strstreambuf) 51 | * 从流缓冲类(streambuf)派生 52 | * 提供了在内存中进行提取和插入操作的缓冲区管理 53 | * IO流类(ios)及其子类 54 | * IO流类作为其所有子类的公共抽象虚基类,主要定义用于格式化输入输出和错误处理等成员函数 55 | * 在IO流泪和它的各级子类中,均含有一个指向流缓冲类(streambuf)特定子类对象的指针 56 | * 各种流对象的实际输入和输出功能,都是通过相应类型的流缓冲对象实现的 57 | 58 | ![ioclass](../../docs/pics/ioclass.png) 59 | 60 | * 以上只有蓝色和红色的9个类,针对具体目标执行具体操作 61 | * 其中蓝色的3个类已经预定义了cin/cout/cerr/clog流对象 62 | * 实际变成中主要使用红色的6个类实现针对文件和内存的IO 63 | * 处于某种原因,所有IO流类都不支持拷贝构造和拷贝赋值 64 | 65 | * `#include ` 66 | * `ios、istream、ostream、iostream` 67 | * `istream_withassign、ostream_withassign、iostream_withassgin` 68 | * `#include ` 69 | * `ifstream、ofstream、fstream` 70 | * `#include ` 71 | * `istrstream、ostrstream、strstream` 72 | * `#include ` 73 | * `istringstream、ostringstream、stringstream` 74 | 75 | # IO流的打开与关闭 76 | 77 | * 通过构造函数打开IO流 78 | * 打开输入流:`ifstream(const char* filename, openmode mode);` 79 | * 打开输出流:`ofstream(const char* filename, openmode mode);` 80 | * 打开输入输出流:`fstream(const char* filename, openmode mode);` 81 | * 通过成员函数打开IO流 82 | * `void open(const char* filename, openmode mode);` 83 | * 其中filename表示文件路径,mode表示打开模式 84 | 85 | * 打开模式 86 | * `ios::in` 87 | * 打开文件用于读取,不存在则失败,存在则不清空 88 | * 适用于`ifstream(缺省)/fstream` 89 | * `ios::out` 90 | * 打开文件用于写入,不存在则创建,存在则清空 91 | * 适用于`ofstream(缺省)/fstream` 92 | * `ios::app` 93 | * 打开文件用于追加,不能再则创建,存在不清空 94 | * 适用于`ofstream/fstream` 95 | * `ios::trunc` 96 | * 打开时清空原内容 97 | * 适用于`ofstream/fstream` 98 | * `ios::ate` 99 | * 打开时定位文件尾 100 | * 适用于`ifstream/oftream/fstream` 101 | * `ios::binary` 102 | * 以二进制模式读写 103 | * 适用于`ifstream/ofstream/fstream` 104 | * 打开模式可以组合使用,比如: 105 | * `ios::in|ios::out`,表示既读取又写入 106 | * 打开模式不能随意组合,比如 107 | * `ios::in|ios::trunc`,清空同时读取没有意义,但是`ios::in|ios::out|ios::trunc`是合理的,清空原内容,写入新内容,同时读取 108 | * 析构函数和close()成员函数都可以关闭I/O流 109 | -------------------------------------------------------------------------------- /day10/01open/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Complex { 6 | public: 7 | Complex(int r = 0, int i = 0) : m_r(r), m_i(i) {} 8 | 9 | friend istream& operator>>(istream& is, Complex& c) { 10 | return is >> c.m_r >> c.m_i; 11 | } 12 | 13 | friend ostream& operator<<(ostream& os, const Complex& c) { 14 | return os << c.m_r << ' ' << c.m_i; 15 | } 16 | private: 17 | int m_r, m_i; 18 | }; 19 | 20 | int main() { 21 | // 离开作用于,析构函数会调用close 22 | ofstream ofs("open.txt", ios::out); 23 | // 显式关闭 24 | ofs.close(); 25 | 26 | ofs.open("open.txt"); 27 | ofs << "hello,world " << 1234 << ' ' << 56.68 << endl; 28 | Complex c(123, 456); 29 | ofs << c << endl; 30 | ofs.close(); 31 | 32 | ofs.open("open.txt", ios::app); 33 | ofs << "good" << endl; 34 | ofs.close(); 35 | 36 | ifstream ifs("open.txt", ios::in); 37 | int i; 38 | double d; 39 | string s; 40 | Complex c2; 41 | ifs >> s >> i >> d >> c2; // 按空格分割 42 | cout << s << endl; 43 | cout << i << endl; 44 | cout << d << endl; 45 | cout << c2 << endl; 46 | ifs.close(); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /day10/02state/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(02state) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(02state main.cpp) 7 | -------------------------------------------------------------------------------- /day10/02state/README.md: -------------------------------------------------------------------------------- 1 | # IO流状态 2 | 3 | * IO流内部维护当前状态,其值尾以下常量的位或 4 | * `ios::goodbit`:0,一切正常 5 | * `ios::badbit`:1,发生致命错误 6 | * `ios::eofbit`:2,遇到文件尾 7 | * `ios::failbit`:4,实际读写字节数未达预期 8 | 9 | * IO流类支持到bool类型的隐式转换 10 | * 发生致命错误或遇到文件尾,返回false,否则返回true 11 | * 将IO流对象直接应用到布尔上下文中,即可实现转换 12 | * 处于致命错误或文件尾状态的流,在复位前无法工作 13 | 14 | # IO流的状态成员函数 15 | 16 | ![iostate](../../docs/pics/iostate.png) 17 | 18 | # 格式化IO 19 | 20 | * 流函数 21 | * IO流类定义了一组用于控制输入输出格式的公有成员函数,调用这些函数可以改变IO流对象内部的格式状态,进而影响后续输入输出的格式化方式 22 | * 流控制符(Stream Manipulator) 23 | * 标准库定义了一组特殊的全局函数,它们有的带有参数(在iomanip头文件中声明),有的不带参数(在iostream头文件中声明),可被直接嵌入到输入输出表达式中,影响后续输入输出格式,称为流控制符 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /day10/02state/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | int main() { 6 | ifstream ifs("none.txt"); 7 | if (!ifs) { 8 | cout << "open failed" << endl; 9 | cout << boolalpha << ifs.good() << endl; // false 10 | cout << ifs.fail() << endl; // true 11 | cout << ifs.bad() << endl; // false 12 | return -1; 13 | } 14 | 15 | cout << "open success" << endl; 16 | int i; 17 | ifs >> i; 18 | cout << i << endl; 19 | if (!(ifs >> i)) { 20 | cout << "read failed" << endl; 21 | if (ifs.eof()) { 22 | cout << "end of file" << endl; 23 | } 24 | if (ifs.bad()) { 25 | cout << "read error" << endl; 26 | } 27 | } 28 | ifs.clear(); 29 | ifs.seekg(ios::beg); 30 | i = 0; 31 | ifs >> i; 32 | cout << i << endl; 33 | 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /day10/03format/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(03format) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(03format main.cpp) 7 | -------------------------------------------------------------------------------- /day10/03format/README.md: -------------------------------------------------------------------------------- 1 | # 格式化IO 2 | 3 | * 流函数 4 | * IO流类定义了一组用于控制输入输出格式的公有成员函数,调用这些函数可以改变IO流对象内部的格式状态,进而影响后续输入输出的格式化方式 5 | * 流控制符(Stream Manipulator) 6 | * 标准库定义了一组特殊的全局函数,它们有的带有参数(在iomanip头文件中声明),有的不带参数(在iostream头文件中声明),可被直接嵌入到输入 7 | 输出表达式中,影响后续输入输出格式,称为流控制符 8 | 9 | # IO流格式化函数 10 | 11 | ![iofmtfunc](../../docs/pics/iofmtfunc.png) 12 | 13 | * 一般而言,对IO流格式的改变都是持久的,即只要不再设置新格式,当前格式将始终保持下去 14 | * 显式域宽是个例外,通过`ios::width(int)`所设置的显示域宽,只影响紧随其后的第一次输出,再往后的输出又恢复到默认状态 15 | 16 | # I/O流格式标志 17 | 18 | ![fmtflag](../../docs/pics/fmtflag.png) 19 | 20 | # I/O流格式化控制符 21 | 22 | ![fmtcontrol](../../docs/pics/fmtcontrol.png) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /day10/03format/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | int main() { 8 | cout << cout.precision() << endl; // 永久有效,精度是小数点前后的总位数 9 | cout << sqrt(200) << endl; // 14.1421 10 | cout << cout.precision(10) << endl; // 返回的是改之前的值 11 | cout << sqrt(200) << endl; // 14.14213562 12 | cout << sqrt(300) << endl; 13 | cout.setf(ios::right); 14 | cout.width(10); // 一次有效 15 | cout << 123 << endl; 16 | cout.width(10); 17 | cout << 456 << endl; 18 | cout.width(10); 19 | cout.fill('0'); // 持续有效,填充字符 20 | cout.setf(ios::left, ios::adjustfield); 21 | cout << 789 << endl; // 7890000000 22 | cout.width(10); 23 | cout << 9527 << endl; // 9527000000 24 | 25 | cout.setf(ios::internal, ios::adjustfield); // 符号位靠左,数字靠右 26 | cout.width(10); 27 | cout << -123 << endl; // -000000123 28 | 29 | cout.setf(ios::uppercase); 30 | cout.setf(ios::showbase); 31 | cout.setf(ios::hex, ios::basefield); 32 | cout << 255 << endl; // 0xFF 33 | cout.setf(ios::dec, ios::basefield); 34 | 35 | cout.setf(ios::scientific, ios::floatfield); 36 | cout << 1234.5678 << endl; // 1.2345678000E+03 37 | cout.setf(ios::fixed, ios::floatfield); 38 | cout << 1234.5678 << endl; // 1234.5678000000 39 | 40 | cout.setf(ios::showpos); 41 | cout << 100 << endl; // +100 42 | cout << 100.0 << endl; // +100.0000000000 43 | cout.setf(ios::showpoint); 44 | cout.unsetf(ios::fixed); 45 | cout << 100.0 << endl; // +100.0000000 46 | 47 | cout << true << ' ' << false << endl; 48 | cout.setf(ios::boolalpha); 49 | cout << true << ' ' << false << endl; 50 | cout << (1 > 0) << ' ' << (1 < 0) << endl; 51 | cout.setf(ios::unitbuf); 52 | cout << "hello, world"; 53 | sleep(5); 54 | cout << endl; 55 | 56 | ifstream ifs("ws.txt"); 57 | ifs.unsetf(ios::skipws); 58 | char c; 59 | while(ifs >> c) { 60 | cout << c; 61 | } 62 | cout << endl; 63 | 64 | cout << showpos << 123 << endl; 65 | cout << noshowpos << 456 << endl; 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /day10/04manip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(04manip) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(04manip main.cpp) 7 | -------------------------------------------------------------------------------- /day10/04manip/README.md: -------------------------------------------------------------------------------- 1 | # 自定义流控制符 2 | 3 | * 无参流控制符 4 | * `流类型& 流控制符(流类型&)` 5 | 6 | ![manipnoparam](../../docs/pics/manipnoparam.png) 7 | 8 | * 单参流控制符 9 | 10 | ``` 11 | #include 12 | 流类型& 流控制符(流类型&, 参数类型) 13 | [i/o/io]manip<参数类型> 流控制符(参数类型); 14 | ``` 15 | 16 | ![manipsingleparam](../../docs/pics/manipsingleparam.png) 17 | 18 | 19 | -------------------------------------------------------------------------------- /day10/04manip/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | ostream& datetime(ostream& os) { 9 | time_t t = time(nullptr); 10 | tm* local = localtime(&t); 11 | 12 | return os << '[' << 13 | setfill('0') << 14 | local->tm_year + 1900 << '-' << 15 | setw(2) << local->tm_mon+1 << '-' << 16 | setw(2) << local->tm_mday << ' ' << 17 | setw(2) << local->tm_hour << ':' << 18 | setw(2) << local->tm_min << ':' << 19 | setw(2) << local->tm_sec <<']'; 20 | } 21 | 22 | ostream& red(ostream& os) { 23 | return os <<"\033[;;31m"; 24 | } 25 | 26 | ostream& def(ostream& os) { 27 | return os << "\033[0m"; 28 | } 29 | 30 | int main() { 31 | cout << red << datetime << def << "open failed" << endl; 32 | sleep(1); 33 | cout << red << datetime << def << "alloc failed" << endl; 34 | sleep(1); 35 | cout << red << datetime << def << "create process failed" << endl; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /day10/05get/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(05get) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(05get main.cpp) 7 | -------------------------------------------------------------------------------- /day10/05get/README.md: -------------------------------------------------------------------------------- 1 | # 非格式化IO 2 | 3 | * 读取字符 4 | * `int istream::get();` 5 | * 成功返回读到的字符,失败或遇到文件尾返回`EOF` 6 | * `istream& istream::get(char& ch);` 7 | * 返回输入流本省,其在buffer上下文中的值,成功为rue,失败或遇到文件尾为false 8 | * `istream& istream::get(char* buff, streamsize num, char delin='\n');` 9 | * 读取字符到buffer中,直到读满num-1个字符,或遇到文件尾,或遇到定界符`delim`(缺省为换行符),追加结尾空字符,返回流本身 10 | * 如果因为遇到定界符返回,定界符并不被读取,读指针停在该定界符处 11 | * 读取行 12 | * `istream& istream::getline(char* buffer, streamsize num, char delim='\n');` 13 | * 读取字符到buffer中,直到读满num-1个字符,或遇到文件尾,或遇到定界符delim(缺省为换行符),追加结尾空字符,返回流本身 14 | * 如果因为遇到定界符返回,定界符被读取并丢弃,读指针停在该定界符的下一个位置 15 | * 放回字符 16 | * `istream& istream::putback(char ch);` 17 | * 将字符ch放回输入流,返回流本 18 | * 窥视字符 19 | * `int istream::peek();` 20 | * 返回但不读取读指针当前位置处的字符,失败或遇到文件尾返回`EOF` 21 | * 忽略字符 22 | * `istream& istream::ignore(streamsize num=1, int delim=EOF);` 23 | * 忽略输入流中的num(缺省为1)个字符,或遇到定界符delim(缺省到文件尾),返回流本省 24 | * 如果因为遇到定界符返回,定界符并不被忽略,读指针停在该定界符处 25 | * 获取读长度 26 | * `streamsize istream::gcount();` 27 | * 返回最后一次从输入流中读取的字节数 28 | * 写入字符 29 | * `ostream& ostream::put(char ch);` 30 | * 一次向输出流写入一个字符,返回流本身 31 | * 刷输出流 32 | * `ostream& ostream::flush();` 33 | * 将输出流缓冲区中的数据刷到设备上,返回流本身 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /day10/05get/get.txt: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopqrstuvwxyz 2 | hello,world 3 | hehe -------------------------------------------------------------------------------- /day10/05get/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | int main() 6 | { 7 | ifstream ifs("get.txt"); 8 | char c; 9 | while((c = ifs.get()) != EOF) 10 | { 11 | cout << c; 12 | } 13 | cout << "------------------------------------" << endl; 14 | ifs.clear(); 15 | ifs.seekg(ios::beg); 16 | 17 | while(ifs.get(c)) 18 | { 19 | cout << c; 20 | } 21 | cout << "------------------------------------" << endl; 22 | 23 | ifs.clear(); 24 | ifs.seekg(ios::beg); 25 | char sz[4]; 26 | while(ifs.get(sz, sizeof(sz), '\n')) 27 | { 28 | cout << sz; 29 | if (ifs.peek() == '\n') 30 | { 31 | ifs.ignore(); 32 | } 33 | } 34 | cout << "------------------------------------" << endl; 35 | ifs.clear(); 36 | ifs.seekg(ios::beg); 37 | char s2[256]; 38 | while(ifs.getline(s2, sizeof(s2), '\n')) 39 | { 40 | cout << s2 << endl; 41 | } 42 | 43 | cout << "------------------------------------" << endl; 44 | ifs.clear(); 45 | ifs.seekg(ios::beg); 46 | c = ifs.get(); 47 | cout << c << endl; 48 | ifs.putback(c); 49 | c = ifs.get(); 50 | cout << c << endl; 51 | c = ifs.get(); 52 | cout << c << endl; 53 | ifs.close(); 54 | 55 | fstream fs("put.txt", ios::out); 56 | for (char c = ' '; c <= '~'; ++c) 57 | { 58 | fs.put(c); 59 | } 60 | fs.clear(); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /day10/06xor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(06xor) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(06xor main.cpp) 7 | -------------------------------------------------------------------------------- /day10/06xor/README.md: -------------------------------------------------------------------------------- 1 | # 二进制I/O 2 | 3 | * 读取二进制数据 4 | * `istream& istream::read(char* buffer, streamsize num);` 5 | * 从输入流中读取num个字节到缓冲区buffer中 6 | * 返回流本身,其在buer上下文中的值,成功(读满)为true,失败(没读满)为false 7 | * 如果没读满num个字节,函数就返回了,比如遇到文件尾,最后一次读到缓冲区buffer中的字节数,可以通过`istream::gcount()`函数获得 8 | 9 | * 写入二进制数据 10 | * `ostream& ostream::write(const char* buffer, streamsize num);` 11 | * 将缓冲区buffer中num个字节写入到输出流中 12 | * 返回流本身,其在布尔上下文中的值,成功(写满)为true,失败(没写满)为false 13 | 14 | 15 | -------------------------------------------------------------------------------- /day10/06xor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int _xor(const char* src, const char* dst, unsigned char key) 7 | { 8 | ifstream ifs(src, ios::binary); 9 | if (!ifs) 10 | { 11 | cout << "open source file failed" << endl; 12 | return -1; 13 | } 14 | 15 | ofstream ofs(dst, ios::binary); 16 | if (!ofs) 17 | { 18 | cout << "open dest file failed" << endl; 19 | return -1; 20 | } 21 | 22 | char buf[1024]; 23 | // read没读满就会失败 24 | while (ifs.read(buf, sizeof(buf))) 25 | { 26 | for (int i = 0; i < sizeof(buf); ++i) 27 | { 28 | buf[i] ^= key; 29 | } 30 | ofs.write(buf, sizeof(buf)); 31 | } 32 | if (!ifs.eof()) 33 | { 34 | cout << "read source file failed" << endl; 35 | return -1; 36 | } 37 | 38 | for (int i = 0; i< ifs.gcount(); ++i) 39 | { 40 | buf[i] ^= key; 41 | } 42 | ofs.write(buf, ifs.gcount()); 43 | ofs.close(); 44 | ifs.close(); 45 | 46 | return 0; 47 | } 48 | 49 | int encrypt(const char* plain, const char* cipher) 50 | { 51 | srand(time(nullptr)); 52 | unsigned char key = rand() % 256; 53 | if (_xor(plain, cipher, key) == -1) 54 | { 55 | return -1; 56 | } 57 | cout << "key: " << int(key) << endl; 58 | return 0; 59 | } 60 | 61 | int decrypt(const char* cihper, const char* plain, unsigned char key) 62 | { 63 | return _xor(cihper, plain, key); 64 | } 65 | 66 | int main(int argc, char* argv[]) 67 | { 68 | if (argc < 3) 69 | { 70 | cout << "encrypt: " << argv[0] << " " << endl; 71 | cout << "decrypt: " << argv[0] << " " << endl; 72 | return -1; 73 | } 74 | 75 | if (argc < 4) 76 | { 77 | return encrypt(argv[1], argv[2]); 78 | } 79 | else 80 | { 81 | return decrypt(argv[1], argv[2], atoi(argv[3])); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /day10/07seek/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(07seek) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(07seek main.cpp) 7 | -------------------------------------------------------------------------------- /day10/07seek/README.md: -------------------------------------------------------------------------------- 1 | # 读写指针随机访问 2 | * 设置读写还真位置 3 | * `istream& istream::seekg(off_type offset, ios::seekdir origin);` 4 | * `ostream& ostream::seekp(off_type offset, ios::seekdir origin);` 5 | * origin表示偏移量offset的起点 6 | * `ios::beg`:从文件的第一个字节 7 | * `ios::cur`:从文件的当前位置 8 | * `ios::end`:从文件最后一个字节的下一个位置 9 | * offset为负/正表示向文件头/尾的方向偏移 10 | * 读写指针被移到文件头之前或文件尾之后,则失败 11 | 12 | * 获取读/写指针位置 13 | * `pos_type istream::tellg()`; 14 | * `pos_type ostream::tellp()`; 15 | * 返回读/写指针当前位置相对于文件头的字节偏移量 16 | * iostream的子类,如fstream 17 | * 同时拥有针对读/写指针位置的两套设置/获取函数 18 | * 理论上应该拥有两个相互肚子里的读/写指针 19 | * 多数编译器仍然使用一个指针记录文件的当前位置 20 | * 建议读取时用`seekg/tellg`,写入时用`seekp/tellp` 21 | 22 | 23 | -------------------------------------------------------------------------------- /day10/07seek/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | 6 | int main() 7 | { 8 | fstream fs("seek.txt", ios::in|ios::out); 9 | fs << "0123456789"; 10 | cout << fs.tellp() << endl; 11 | cout << fs.tellg() << endl; 12 | 13 | fs.seekp(-3, ios::cur); 14 | fs << "XYZ"; 15 | fs.seekg(3, ios::beg); 16 | string s; 17 | fs >> s; 18 | cout << s << endl; 19 | fs.clear(); 20 | fs.seekp(ios::beg); 21 | fs << "ABC"; 22 | 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /day10/08sstring/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(08sstring) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(08sstring main.cpp) 7 | -------------------------------------------------------------------------------- /day10/08sstring/README.md: -------------------------------------------------------------------------------- 1 | # 字符串流 -------------------------------------------------------------------------------- /day10/08sstring/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | int main () 6 | { 7 | string is("1234 56.78 hello,world"); 8 | //istringstream iss(is); 9 | istringstream iss; 10 | iss.str(is); 11 | int i; 12 | double d; 13 | string s; 14 | iss >> i >> d >> s; 15 | cout << i << endl; 16 | cout << d << endl; 17 | cout << s << endl; 18 | 19 | ostringstream oss; 20 | oss << 9527 << ' ' << 54.32 << ' ' << "hello,world"; 21 | string os = oss.str(); 22 | cout << os << endl; 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /docs/pics/absfactory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/absfactory.png -------------------------------------------------------------------------------- /docs/pics/access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/access.png -------------------------------------------------------------------------------- /docs/pics/continue_throw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/continue_throw.png -------------------------------------------------------------------------------- /docs/pics/deepcopy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/deepcopy.png -------------------------------------------------------------------------------- /docs/pics/diamond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/diamond.png -------------------------------------------------------------------------------- /docs/pics/diamondvirtual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/diamondvirtual.png -------------------------------------------------------------------------------- /docs/pics/exception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/exception.png -------------------------------------------------------------------------------- /docs/pics/handleexp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/handleexp.png -------------------------------------------------------------------------------- /docs/pics/hollywood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/hollywood.png -------------------------------------------------------------------------------- /docs/pics/inherit01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/inherit01.png -------------------------------------------------------------------------------- /docs/pics/inherit02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/inherit02.png -------------------------------------------------------------------------------- /docs/pics/ioclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/ioclass.png -------------------------------------------------------------------------------- /docs/pics/iofmtfunc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/iofmtfunc.png -------------------------------------------------------------------------------- /docs/pics/iosbase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/iosbase.png -------------------------------------------------------------------------------- /docs/pics/iostate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/iostate.png -------------------------------------------------------------------------------- /docs/pics/manipnoparam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/manipnoparam.png -------------------------------------------------------------------------------- /docs/pics/manipsingleparam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/manipsingleparam.png -------------------------------------------------------------------------------- /docs/pics/memlayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/memlayout.png -------------------------------------------------------------------------------- /docs/pics/memptr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/memptr.png -------------------------------------------------------------------------------- /docs/pics/mulin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/mulin.png -------------------------------------------------------------------------------- /docs/pics/noexception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/noexception.png -------------------------------------------------------------------------------- /docs/pics/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/object.png -------------------------------------------------------------------------------- /docs/pics/opoverload_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/opoverload_array.png -------------------------------------------------------------------------------- /docs/pics/opoverload_func.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/opoverload_func.png -------------------------------------------------------------------------------- /docs/pics/opoverload_ptr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/opoverload_ptr.png -------------------------------------------------------------------------------- /docs/pics/separate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/separate.png -------------------------------------------------------------------------------- /docs/pics/shadowcopy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/shadowcopy.png -------------------------------------------------------------------------------- /docs/pics/static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/static.png -------------------------------------------------------------------------------- /docs/pics/stdexp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/stdexp.png -------------------------------------------------------------------------------- /docs/pics/vdes1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/vdes1.png -------------------------------------------------------------------------------- /docs/pics/vdes2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/vdes2.png -------------------------------------------------------------------------------- /docs/pics/vinherit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/vinherit.png -------------------------------------------------------------------------------- /docs/pics/vtable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssuchichan/cpp-tutorial/4129cfd2d9c9a27f3f4df29f772d28b57cf5bce1/docs/pics/vtable.png --------------------------------------------------------------------------------