diff --git "a/001C++\345\237\272\347\241\200\345\205\245\351\227\250.md" "b/001C++\345\237\272\347\241\200\345\205\245\351\227\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..5b48b68acf7c47b7764697bcf453295a4aeea8c9 --- /dev/null +++ "b/001C++\345\237\272\347\241\200\345\205\245\351\227\250.md" @@ -0,0 +1,2569 @@ +# C++基础入门 + +## 1 C++初识 + +### 1.1 第一个C++程序 + +编写一个C++程序总共分为4个步骤 + +* 创建项目 +* 创建文件 +* 编写代码 +* 运行程序 + +#### 1.1.1 创建项目 + +​Visual Studio是我们用来编写C++程序的主要工具,我们先将它打开 + +#### 1.1.2 创建文件 + +右键源文件,选择添加->新建项 + +给C++文件起个名称,然后点击添加即可。 + +#### 1.1.3 编写代码 + +```c++ +#include +using namespace std; +int main() { + cout << "Hello world" << endl; + system("pause"); + + return 0; +} +``` + +#### 1.1.4 运行程序 + +### 1.2 注释 + +**作用**:在代码中加一些说明和解释,方便自己或其他程序员程序员阅读代码 + +**两种格式** + +1. **单行注释**:`// 描述信息` + - 通常放在一行代码的上方,或者一条语句的末尾,==对该行代码说明== +2. **多行注释**: `/* 描述信息 */` + - 通常放在一段代码的上方,==对该段代码做整体说明== + +> 提示:编译器在编译代码时,会忽略注释的内容 + +### 1.3 变量 + +**作用**:给一段指定的内存空间起名,方便操作这段内存 + +**语法**:`数据类型 变量名 = 初始值;` + +**示例:** + +```C++ +#include +using namespace std; + +int main() { + + //变量的定义 + //语法:数据类型 变量名 = 初始值 + + int a = 10; + + cout << "a = " << a << endl; + + system("pause"); + + return 0; +} +``` + +> 注意:C++在创建变量时,必须给变量一个初始值,否则会报错 + + +### 1.4 常量 + +**作用**:用于记录程序中不可更改的数据 + +C++定义常量两种方式 + +1. **\#define** 宏常量: `#define 常量名 常量值` + * ==通常在文件上方定义==,表示一个常量 + + +2. **const**修饰的变量 `const 数据类型 常量名 = 常量值` + * ==通常在变量定义前加关键字const==,修饰该变量为常量,不可修改 + + +**示例:** + +```C++ +//1、宏常量 +#define day 7 + +int main() { + + cout << "一周里总共有 " << day << " 天" << endl; + //day = 8; //报错,宏常量不可以修改 + + //2、const修饰变量 + const int month = 12; + cout << "一年里总共有 " << month << " 个月份" << endl; + //month = 24; //报错,常量是不可以修改的 + + + system("pause"); + + return 0; +} +``` + +### 1.5 关键字 + +**作用:**关键字是C++中预先保留的单词(标识符) + +* **在定义变量或者常量时候,不要用关键字** C++关键字如下: + +| asm | do | if | return | typedef | +| ---------- | ------------ | ---------------- | ----------- | -------- | +| auto | double | inline | short | typeid | +| bool | dynamic_cast | int | signed | typename | +| break | else | long | sizeof | union | +| case | enum | mutable | static | unsigned | +| catch | explicit | namespace | static_cast | using | +| char | export | new | struct | virtual | +| class | extern | operator | switch | void | +| const | false | private | template | volatile | +| const_cast | float | protected | this | wchar_t | +| continue | for | public | throw | while | +| default | friend | register | true | | +| delete | goto | reinterpret_cast | try | | + +`提示:在给变量或者常量起名称时候,不要用C++得关键字,否则会产生歧义。` + + +### 1.6 标识符命名规则 + +**作用**:C++规定给标识符(变量、常量)命名时,有一套自己的规则 + +* 标识符不能是关键字 +* 标识符只能由字母、数字、下划线组成 +* 第一个字符必须为字母或下划线 +* 标识符中字母区分大小写 + +> 建议:给标识符命名时,争取做到见名知意的效果,方便自己和他人的阅读 + + +## 2 数据类型 + +C++规定在创建一个变量或者常量时,必须要指定出相应的数据类型,否则无法给变量分配内存 + +### 2.1 整型 + +**作用**:整型变量表示的是==整数类型==的数据 + +C++中能够表示整型的类型有以下几种方式,**区别在于所占内存空间不同**: + +| **数据类型** | **占用空间** | 取值范围 | +| ------------------- | ----------------------------------------------- | ---------------- | +| short(短整型) | 2字节 | (-2^15 ~ 2^15-1) | +| int(整型) | 4字节 | (-2^31 ~ 2^31-1) | +| long(长整形) | Windows为4字节,Linux为4字节(32位),8字节(64位) | (-2^31 ~ 2^31-1) | +| long long(长长整形) | 8字节 | (-2^63 ~ 2^63-1) | +### 2.2 sizeof关键字 + +**作用:**利用sizeof关键字可以==统计数据类型所占内存大小== + +**语法:** `sizeof( 数据类型 / 变量)` + +**示例:** + +```C++ +int main() { + + cout << "short 类型所占内存空间为: " << sizeof(short) << endl; + + cout << "int 类型所占内存空间为: " << sizeof(int) << endl; + + cout << "long 类型所占内存空间为: " << sizeof(long) << endl; + + cout << "long long 类型所占内存空间为: " << sizeof(long long) << endl; + + system("pause"); + + return 0; +} +``` + +> **整型结论**:==short < int <= long <= long long== + + +### 2.3 实型(浮点型) + +**作用**:用于==表示小数== + +浮点型变量分为两种: + +1. 单精度float +2. 双精度double + +两者的**区别**在于表示的有效数字范围不同。 + +| **数据类型** | **占用空间** | **有效数字范围** | +| ------------ | ------------ | ---------------- | +| float | 4字节 | 7位有效数字 | +| double | 8字节 | 15~16位有效数字 | + +**示例:** + +```C++ +int main() { + + float f1 = 3.14f; + double d1 = 3.14; + + cout << f1 << endl; + cout << d1<< endl; + + cout << "float sizeof = " << sizeof(f1) << endl; + cout << "double sizeof = " << sizeof(d1) << endl; + + //科学计数法 + float f2 = 3e2; // 3 * 10 ^ 2 + cout << "f2 = " << f2 << endl; + + float f3 = 3e-2; // 3 * 0.1 ^ 2 + cout << "f3 = " << f3 << endl; + + system("pause"); + + return 0; +} +``` + + +### 2.4 字符型 + +**作用:**字符型变量用于显示单个字符 + +**语法:**`char ch = 'a';` > 注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号 + +> 注意2:单引号内只能有一个字符,不可以是字符串 - C和C++中字符型变量只占用==1个字节==。 +- 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元 示例: + +```C++ +int main() { + + char ch = 'a'; + cout << ch << endl; + cout << sizeof(char) << endl; + + //ch = "abcde"; //错误,不可以用双引号 + //ch = 'abcde'; //错误,单引号内只能引用一个字符 + + cout << (int)ch << endl; //查看字符a对应的ASCII码 + ch = 97; //可以直接用ASCII给字符型变量赋值 + cout << ch << endl; + + system("pause"); + + return 0; +} +``` + +ASCII码表格: + +| **ASCII**值 | **控制字符** | **ASCII**值 | **字符** | **ASCII**值 | **字符** | **ASCII**值 | **字符** | +| ----------- | ------------ | ----------- | -------- | ----------- | -------- | ----------- | -------- | +| 0 | NUT | 32 | (space) | 64 | @ | 96 | 、 | +| 1 | SOH | 33 | ! | 65 | A | 97 | a | +| 2 | STX | 34 | " | 66 | B | 98 | b | +| 3 | ETX | 35 | # | 67 | C | 99 | c | +| 4 | EOT | 36 | $ | 68 | D | 100 | d | +| 5 | ENQ | 37 | % | 69 | E | 101 | e | +| 6 | ACK | 38 | & | 70 | F | 102 | f | +| 7 | BEL | 39 | , | 71 | G | 103 | g | +| 8 | BS | 40 | ( | 72 | H | 104 | h | +| 9 | HT | 41 | ) | 73 | I | 105 | i | +| 10 | LF | 42 | * | 74 | J | 106 | j | +| 11 | VT | 43 | + | 75 | K | 107 | k | +| 12 | FF | 44 | , | 76 | L | 108 | l | +| 13 | CR | 45 | - | 77 | M | 109 | m | +| 14 | SO | 46 | . | 78 | N | 110 | n | +| 15 | SI | 47 | / | 79 | O | 111 | o | +| 16 | DLE | 48 | 0 | 80 | P | 112 | p | +| 17 | DCI | 49 | 1 | 81 | Q | 113 | q | +| 18 | DC2 | 50 | 2 | 82 | R | 114 | r | +| 19 | DC3 | 51 | 3 | 83 | S | 115 | s | +| 20 | DC4 | 52 | 4 | 84 | T | 116 | t | +| 21 | NAK | 53 | 5 | 85 | U | 117 | u | +| 22 | SYN | 54 | 6 | 86 | V | 118 | v | +| 23 | TB | 55 | 7 | 87 | W | 119 | w | +| 24 | CAN | 56 | 8 | 88 | X | 120 | x | +| 25 | EM | 57 | 9 | 89 | Y | 121 | y | +| 26 | SUB | 58 | : | 90 | Z | 122 | z | +| 27 | ESC | 59 | ; | 91 | [ | 123 | { | +| 28 | FS | 60 | < | 92 | / | 124 | \| | +| 29 | GS | 61 | = | 93 | ] | 125 | } | +| 30 | RS | 62 | > | 94 | ^ | 126 | ` | +| 31 | US | 63 | ? | 95 | _ | 127 | DEL | + +ASCII 码大致由以下**两部分组**成: + +* ASCII 非打印控制字符: ASCII 表上的数字 **0-31** 分配给了控制字符,用于控制像打印机等一些外围设备。 +* ASCII 打印字符:数字 **32-126** 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。 +### 2.5 转义字符 + +**作用:**用于表示一些==不能显示出来的ASCII字符== + +现阶段我们常用的转义字符有:` \n \\ \t` + +| **转义字符** | **含义** | **ASCII**码值(十进制) | +| ------------ | --------------------------------------- | ----------------------- | +| \a | 警报 | 007 | +| \b | 退格(BS) ,将当前位置移到前一列 | 008 | +| \f | 换页(FF),将当前位置移到下页开头 | 012 | +| **\n** | **换行(LF) ,将当前位置移到下一行开头** | **010** | +| \r | 回车(CR) ,将当前位置移到本行开头 | 013 | +| **\t** | **水平制表(HT) (跳到下一个TAB位置)** | **009** | +| \v | 垂直制表(VT) | 011 | +| **\\\\** | **代表一个反斜线字符"\"** | **092** | +| \' | 代表一个单引号(撇号)字符 | 039 | +| \" | 代表一个双引号字符 | 034 | +| \? | 代表一个问号 | 063 | +| \0 | 数字0 | 000 | +| \ddd | 8进制转义字符,d范围0~7 | 3位8进制 | +| \xhh | 16进制转义字符,h范围0~9,a~f,A~F | 3位16进制 | + +示例: + +```C++ +int main() { + + + cout << "\\" << endl; + cout << "\tHello" << endl; + cout << "\n" << endl; + + system("pause"); + + return 0; +} +``` +### 2.6 字符串型 + +**作用**:用于表示一串字符 + +**两种风格** + +1. **C风格字符串**: `char 变量名[] = "字符串值"` + + 示例: + + ```C++ + int main() { + + char str1[] = "hello world"; + cout << str1 << endl; + + system("pause"); + + return 0; + } + ``` + +> 注意:C风格的字符串要用双引号括起来 + +1. **C++风格字符串**: `string 变量名 = "字符串值"` + + 示例: + + ```C++ + int main() { + + string str = "hello world"; + cout << str << endl; + + system("pause"); + + return 0; + } + ``` + + ​ + +> 注意:C++风格字符串,需要加入头文件==#include\== +### 2.7 布尔类型 bool + +**作用:**布尔数据类型代表真或假的值 + +bool类型只有两个值: + +* true --- 真(本质是1) +* false --- 假(本质是0) + +**bool类型占==1个字节==大小** + +示例: + +```C++ +int main() { + + bool flag = true; + cout << flag << endl; // 1 + + flag = false; + cout << flag << endl; // 0 + + cout << "size of bool = " << sizeof(bool) << endl; //1 + + system("pause"); + + return 0; +} +``` + + +### 2.8 数据的输入 + +**作用:用于从键盘获取数据** + +**关键字:**cin + +**语法:** `cin >> 变量 ` + +示例: + +```C++ +int main(){ + + //整型输入 + int a = 0; + cout << "请输入整型变量:" << endl; + cin >> a; + cout << a << endl; + + //浮点型输入 + double d = 0; + cout << "请输入浮点型变量:" << endl; + cin >> d; + cout << d << endl; + + //字符型输入 + char ch = 0; + cout << "请输入字符型变量:" << endl; + cin >> ch; + cout << ch << endl; + + //字符串型输入 + string str; + cout << "请输入字符串型变量:" << endl; + cin >> str; + cout << str << endl; + + //布尔类型输入 + bool flag = true; + cout << "请输入布尔型变量:" << endl; + cin >> flag; + cout << flag << endl; + system("pause"); + return EXIT_SUCCESS; +} +``` +## 3 运算符 + +**作用:**用于执行代码的运算 + +本章我们主要讲解以下几类运算符: + +| **运算符类型** | **作用** | +| -------------- | -------------------------------------- | +| 算术运算符 | 用于处理四则运算 | +| 赋值运算符 | 用于将表达式的值赋给变量 | +| 比较运算符 | 用于表达式的比较,并返回一个真值或假值 | +| 逻辑运算符 | 用于根据表达式的值返回真值或假值 | + +### 3.1 算术运算符 + +**作用**:用于处理四则运算 + +算术运算符包括以下符号: + +| **运算符** | **术语** | **示例** | **结果** | +| ---------- | ---------- | ----------- | --------- | +| + | 正号 | +3 | 3 | +| - | 负号 | -3 | -3 | +| + | 加 | 10 + 5 | 15 | +| - | 减 | 10 - 5 | 5 | +| * | 乘 | 10 * 5 | 50 | +| / | 除 | 10 / 5 | 2 | +| % | 取模(取余) | 10 % 3 | 1 | +| ++ | 前置递增 | a=2; b=++a; | a=3; b=3; | +| ++ | 后置递增 | a=2; b=a++; | a=3; b=2; | +| -- | 前置递减 | a=2; b=--a; | a=1; b=1; | +| -- | 后置递减 | a=2; b=a--; | a=1; b=2; | + +**示例1:** + +```C++ +//加减乘除 +int main() { + + int a1 = 10; + int b1 = 3; + + cout << a1 + b1 << endl; + cout << a1 - b1 << endl; + cout << a1 * b1 << endl; + cout << a1 / b1 << endl; //两个整数相除结果依然是整数 + + int a2 = 10; + int b2 = 20; + cout << a2 / b2 << endl; + + int a3 = 10; + int b3 = 0; + //cout << a3 / b3 << endl; //报错,除数不可以为0 + + + //两个小数可以相除 + double d1 = 0.5; + double d2 = 0.25; + cout << d1 / d2 << endl; + + system("pause"); + + return 0; +} +``` + +> 总结:在除法运算中,除数不能为0 + +**示例2:** + +```C++ +//取模 +int main() { + + int a1 = 10; + int b1 = 3; + + cout << 10 % 3 << endl; + + int a2 = 10; + int b2 = 20; + + cout << a2 % b2 << endl; + + int a3 = 10; + int b3 = 0; + + //cout << a3 % b3 << endl; //取模运算时,除数也不能为0 + + //两个小数不可以取模 + double d1 = 3.14; + double d2 = 1.1; + + //cout << d1 % d2 << endl; + + system("pause"); + + return 0; +} + +``` + +> 总结:只有整型变量可以进行取模运算 **示例3:** + +```C++ +//递增 +int main() { + + //后置递增 + int a = 10; + a++; //等价于a = a + 1 + cout << a << endl; // 11 + + //前置递增 + int b = 10; + ++b; + cout << b << endl; // 11 + + //区别 + //前置递增先对变量进行++,再计算表达式 + int a2 = 10; + int b2 = ++a2 * 10; + cout << b2 << endl; + + //后置递增先计算表达式,后对变量进行++ + int a3 = 10; + int b3 = a3++ * 10; + cout << b3 << endl; + + system("pause"); + + return 0; +} + +``` > 总结:前置递增先对变量进行++,再计算表达式,后置递增相反 +### 3.2 赋值运算符 + +**作用:**用于将表达式的值赋给变量 + +赋值运算符包括以下几个符号: + +| **运算符** | **术语** | **示例** | **结果** | +| ---------- | -------- | ---------- | --------- | +| = | 赋值 | a=2; b=3; | a=2; b=3; | +| += | 加等于 | a=0; a+=2; | a=2; | +| -= | 减等于 | a=5; a-=3; | a=2; | +| *= | 乘等于 | a=2; a*=2; | a=4; | +| /= | 除等于 | a=4; a/=2; | a=2; | +| %= | 模等于 | a=3; a%2; | a=1; | **示例:** + +```C++ +int main() { + + //赋值运算符 + + // = + int a = 10; + a = 100; + cout << "a = " << a << endl; + + // += + a = 10; + a += 2; // a = a + 2; + cout << "a = " << a << endl; + + // -= + a = 10; + a -= 2; // a = a - 2 + cout << "a = " << a << endl; + + // *= + a = 10; + a *= 2; // a = a * 2 + cout << "a = " << a << endl; + + // /= + a = 10; + a /= 2; // a = a / 2; + cout << "a = " << a << endl; + + // %= + a = 10; + a %= 2; // a = a % 2; + cout << "a = " << a << endl; + + system("pause"); + + return 0; +} +``` +### 3.3 比较运算符 + +**作用:**用于表达式的比较,并返回一个真值或假值 + +比较运算符有以下符号: + +| **运算符** | **术语** | **示例** | **结果** | +| ---------- | -------- | -------- | -------- | +| == | 相等于 | 4 == 3 | 0 | +| != | 不等于 | 4 != 3 | 1 | +| < | 小于 | 4 < 3 | 0 | +| \> | 大于 | 4 > 3 | 1 | +| <= | 小于等于 | 4 <= 3 | 0 | +| \>= | 大于等于 | 4 >= 1 | 1 | + +示例: + +```C++ +int main() { + + int a = 10; + int b = 20; + + cout << (a == b) << endl; // 0 + + cout << (a != b) << endl; // 1 + + cout << (a > b) << endl; // 0 + + cout << (a < b) << endl; // 1 + + cout << (a >= b) << endl; // 0 + + cout << (a <= b) << endl; // 1 + + system("pause"); + + return 0; +} +``` > 注意:C和C++ 语言的比较运算中, ==“真”用数字“1”来表示, “假”用数字“0”来表示。== +### 3.4 逻辑运算符 + +**作用:**用于根据表达式的值返回真值或假值 + +逻辑运算符有以下符号: + +| **运算符** | **术语** | **示例** | **结果** | +| ---------- | -------- | -------- | -------------------------------------------------------- | +| ! | 非 | !a | 如果a为假,则!a为真; 如果a为真,则!a为假。 | +| && | 与 | a && b | 如果a和b都为真,则结果为真,否则为假。 | +| \|\| | 或 | a \|\| b | 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。 | + +**示例1:**逻辑非 + +```C++ +//逻辑运算符 --- 非 +int main() { + + int a = 10; + + cout << !a << endl; // 0 + + cout << !!a << endl; // 1 + + system("pause"); + + return 0; +} +``` + +> 总结: 真变假,假变真 + +**示例2:**逻辑与 + +```C++ +//逻辑运算符 --- 与 +int main() { + + int a = 10; + int b = 10; + + cout << (a && b) << endl;// 1 + + a = 10; + b = 0; + + cout << (a && b) << endl;// 0 + + a = 0; + b = 0; + + cout << (a && b) << endl;// 0 + + system("pause"); + + return 0; +} + +``` + +> 总结:逻辑==与==运算符总结: ==同真为真,其余为假== **示例3:**逻辑或 + +```c++ +//逻辑运算符 --- 或 +int main() { + + int a = 10; + int b = 10; + + cout << (a || b) << endl;// 1 + + a = 10; + b = 0; + + cout << (a || b) << endl;// 1 + + a = 0; + b = 0; + + cout << (a || b) << endl;// 0 + + system("pause"); + + return 0; +} +``` + +> 逻辑==或==运算符总结: ==同假为假,其余为真== +## 4 程序流程结构 + +C/C++支持最基本的三种程序运行结构:==顺序结构、选择结构、循环结构== + +* 顺序结构:程序按顺序执行,不发生跳转 +* 选择结构:依据条件是否满足,有选择的执行相应功能 +* 循环结构:依据条件是否满足,循环多次执行某段代码 ### 4.1 选择结构 + +#### 4.1.1 if语句 + +**作用:**执行满足条件的语句 + +if语句的三种形式 + +* 单行格式if语句 + +* 多行格式if语句 + +* 多条件的if语句 + + ​ + +1. 单行格式if语句:`if(条件){ 条件满足执行的语句 }` + + ![img](assets/clip_image002.png) + + 示例: + + ```C++ + int main() { + + //选择结构-单行if语句 + //输入一个分数,如果分数大于600分,视为考上一本大学,并在屏幕上打印 + + int score = 0; + cout << "请输入一个分数:" << endl; + cin >> score; + + cout << "您输入的分数为: " << score << endl; + + //if语句 + //注意事项,在if判断语句后面,不要加分号 + if (score > 600) + { + cout << "我考上了一本大学!!!" << endl; + } + + system("pause"); + + return 0; + } + ``` + + ​ + + +> 注意:if条件表达式后不要加分号 2. 多行格式if语句:`if(条件){ 条件满足执行的语句 }else{ 条件不满足执行的语句 };` + +![img](assets/clip_image002-1541662519170.png) + +​ + +示例: + +```C++ +int main() { + + int score = 0; + + cout << "请输入考试分数:" << endl; + + cin >> score; + + if (score > 600) + { + cout << "我考上了一本大学" << endl; + } + else + { + cout << "我未考上一本大学" << endl; + } + + system("pause"); + + return 0; +} +``` + + +3. 多条件的if语句:`if(条件1){ 条件1满足执行的语句 }else if(条件2){条件2满足执行的语句}... else{ 都不满足执行的语句}` + +![img](assets/clip_image002-1541662566808.png) + +​ + +​ + +​ + +示例: + +```C++ + int main() { + + int score = 0; + + cout << "请输入考试分数:" << endl; + + cin >> score; + + if (score > 600) + { + cout << "我考上了一本大学" << endl; + } + else if (score > 500) + { + cout << "我考上了二本大学" << endl; + } + else if (score > 400) + { + cout << "我考上了三本大学" << endl; + } + else + { + cout << "我未考上本科" << endl; + } + + system("pause"); + + return 0; +} +``` + +​ **嵌套if语句**:在if语句中,可以嵌套使用if语句,达到更精确的条件判断 案例需求: + +* 提示用户输入一个高考考试分数,根据分数做如下判断 +* 分数如果大于600分视为考上一本,大于500分考上二本,大于400考上三本,其余视为未考上本科; +* 在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大。 **示例:** + +```c++ +int main() { + + int score = 0; + + cout << "请输入考试分数:" << endl; + + cin >> score; + + if (score > 600) + { + cout << "我考上了一本大学" << endl; + if (score > 700) + { + cout << "我考上了北大" << endl; + } + else if (score > 650) + { + cout << "我考上了清华" << endl; + } + else + { + cout << "我考上了人大" << endl; + } + + } + else if (score > 500) + { + cout << "我考上了二本大学" << endl; + } + else if (score > 400) + { + cout << "我考上了三本大学" << endl; + } + else + { + cout << "我未考上本科" << endl; + } + + system("pause"); + + return 0; +} +``` **练习案例:** 三只小猪称体重 + +有三只小猪ABC,请分别输入三只小猪的体重,并且判断哪只小猪最重?![三只小猪](assets/三只小猪.jpg) +#### 4.1.2 三目运算符 + +**作用:** 通过三目运算符实现简单的判断 + +**语法:**`表达式1 ? 表达式2 :表达式3` + +**解释:** + +如果表达式1的值为真,执行表达式2,并返回表达式2的结果; + +如果表达式1的值为假,执行表达式3,并返回表达式3的结果。 + +**示例:** + +```C++ +int main() { + + int a = 10; + int b = 20; + int c = 0; + + c = a > b ? a : b; + cout << "c = " << c << endl; + + //C++中三目运算符返回的是变量,可以继续赋值 + + (a > b ? a : b) = 100; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + cout << "c = " << c << endl; + + system("pause"); + + return 0; +} +``` + +> 总结:和if语句比较,三目运算符优点是短小整洁,缺点是如果用嵌套,结构不清晰 +#### 4.1.3 switch语句 + +**作用:**执行多条件分支语句 + +**语法:** + +```C++ +switch(表达式) + +{ + + case 结果1:执行语句;break; + + case 结果2:执行语句;break; + + ... + + default:执行语句;break; + +} + +``` **示例:** + +```C++ +int main() { + + //请给电影评分 + //10 ~ 9 经典 + // 8 ~ 7 非常好 + // 6 ~ 5 一般 + // 5分以下 烂片 + + int score = 0; + cout << "请给电影打分" << endl; + cin >> score; + + switch (score) + { + case 10: + case 9: + cout << "经典" << endl; + break; + case 8: + cout << "非常好" << endl; + break; + case 7: + case 6: + cout << "一般" << endl; + break; + default: + cout << "烂片" << endl; + break; + } + + system("pause"); + + return 0; +} +``` > 注意1:switch语句中表达式类型只能是整型或者字符型 + +> 注意2:case里如果没有break,那么程序会一直向下执行 + +> 总结:与if语句比,对于多条件判断时,switch的结构清晰,执行效率高,缺点是switch不可以判断区间 + + +### 4.2 循环结构 + +#### 4.2.1 while循环语句 + +**作用:**满足循环条件,执行循环语句 + +**语法:**` while(循环条件){ 循环语句 }` + +**解释:**==只要循环条件的结果为真,就执行循环语句== + +![img](assets/clip_image002-1541668640382.png) **示例:** + +```C++ +int main() { + + int num = 0; + while (num < 10) + { + cout << "num = " << num << endl; + num++; + } + + system("pause"); + + return 0; +} +``` > 注意:在执行循环语句时候,程序必须提供跳出循环的出口,否则出现死循环 +**while循环练习案例:**==猜数字== + +**案例描述:**系统随机生成一个1到100之间的数字,玩家进行猜测,如果猜错,提示玩家数字过大或过小,如果猜对恭喜玩家胜利,并且退出游戏。 ![猜数字](assets/猜数字.jpg) +#### 4.2.2 do...while循环语句 + +**作用:** 满足循环条件,执行循环语句 + +**语法:** `do{ 循环语句 } while(循环条件);` + +**注意:**与while的区别在于==do...while会先执行一次循环语句==,再判断循环条件 + +![img](assets/clip_image002-1541671163478.png) **示例:** + +```C++ +int main() { + + int num = 0; + + do + { + cout << num << endl; + num++; + + } while (num < 10); + + + system("pause"); + + return 0; +} +``` > 总结:与while循环区别在于,do...while先执行一次循环语句,再判断循环条件 +**练习案例:水仙花数** + +**案例描述:**水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身 + +例如:1^3 + 5^3+ 3^3 = 153 + +请利用do...while语句,求出所有3位数中的水仙花数 #### 4.2.3 for循环语句 + +**作用:** 满足循环条件,执行循环语句 + +**语法:**` for(起始表达式;条件表达式;末尾循环体) { 循环语句; }` **示例:** + +```C++ +int main() { + + for (int i = 0; i < 10; i++) + { + cout << i << endl; + } + + system("pause"); + + return 0; +} +``` **详解:** + +![1541673704101](assets/1541673704101.png) > 注意:for循环中的表达式,要用分号进行分隔 + +> 总结:while , do...while, for都是开发中常用的循环语句,for循环结构比较清晰,比较常用 + + +**练习案例:敲桌子** + +案例描述:从1开始数到数字100, 如果数字个位含有7,或者数字十位含有7,或者该数字是7的倍数,我们打印敲桌子,其余数字直接打印输出。 + +![timg](assets/timg.gif) +#### 4.2.4 嵌套循环 + +**作用:** 在循环体中再嵌套一层循环,解决一些实际问题 + +例如我们想在屏幕中打印如下图片,就需要利用嵌套循环 + +![1541676003486](assets/1541676003486.png) + + +**示例:** + +```C++ +int main() { + + //外层循环执行1次,内层循环执行1轮 + for (int i = 0; i < 10; i++) + { + for (int j = 0; j < 10; j++) + { + cout << "*" << " "; + } + cout << endl; + } + + system("pause"); + + return 0; +} +``` +**练习案例:**乘法口诀表 + +案例描述:利用嵌套循环,实现九九乘法表 + +![0006018857256120_b](assets/0006018857256120_b.jpg) + +### 4.3 跳转语句 + +#### 4.3.1 break语句 + +**作用:** 用于跳出==选择结构==或者==循环结构== + +break使用的时机: + +* 出现在switch条件语句中,作用是终止case并跳出switch +* 出现在循环语句中,作用是跳出当前的循环语句 +* 出现在嵌套循环中,跳出最近的内层循环语句 **示例1:** + +```C++ +int main() { + //1、在switch 语句中使用break + cout << "请选择您挑战副本的难度:" << endl; + cout << "1、普通" << endl; + cout << "2、中等" << endl; + cout << "3、困难" << endl; + + int num = 0; + + cin >> num; + + switch (num) + { + case 1: + cout << "您选择的是普通难度" << endl; + break; + case 2: + cout << "您选择的是中等难度" << endl; + break; + case 3: + cout << "您选择的是困难难度" << endl; + break; + } + + system("pause"); + + return 0; +} +``` **示例2:** + +```C++ +int main() { + //2、在循环语句中用break + for (int i = 0; i < 10; i++) + { + if (i == 5) + { + break; //跳出循环语句 + } + cout << i << endl; + } + + system("pause"); + + return 0; +} +``` **示例3:** + +```C++ +int main() { + //在嵌套循环语句中使用break,退出内层循环 + for (int i = 0; i < 10; i++) + { + for (int j = 0; j < 10; j++) + { + if (j == 5) + { + break; + } + cout << "*" << " "; + } + cout << endl; + } + + system("pause"); + + return 0; +} +``` + + +#### 4.3.2 continue语句 + +**作用:**在==循环语句==中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环 + +**示例:** + +```C++ +int main() { + + for (int i = 0; i < 100; i++) + { + if (i % 2 == 0) + { + continue; + } + cout << i << endl; + } + + system("pause"); + + return 0; +} +``` > 注意:continue并没有使整个循环终止,而break会跳出循环 + + +#### 4.3.3 goto语句 + +**作用:**可以无条件跳转语句 **语法:** `goto 标记;` + +**解释:**如果标记的名称存在,执行到goto语句时,会跳转到标记的位置 **示例:** + +```C++ +int main() { + + cout << "1" << endl; + + goto FLAG; + + cout << "2" << endl; + cout << "3" << endl; + cout << "4" << endl; + + FLAG: + + cout << "5" << endl; + + system("pause"); + + return 0; +} +``` > 注意:在程序中不建议使用goto语句,以免造成程序流程混乱 +## 5 数组 + +### 5.1 概述 + +所谓数组,就是一个集合,里面存放了相同类型的数据元素 **特点1:**数组中的每个==数据元素都是相同的数据类型== + +**特点2:**数组是由==连续的内存==位置组成的 +![1541748375356](assets/1541748375356.png) +### 5.2 一维数组 + +#### 5.2.1 一维数组定义方式 + +一维数组定义的三种方式: + +1. ` 数据类型 数组名[ 数组长度 ]; ` +2. `数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};` +3. `数据类型 数组名[ ] = { 值1,值2 ...};` 示例 + +```C++ +int main() { + + //定义方式1 + //数据类型 数组名[元素个数]; + int score[10]; + + //利用下标赋值 + score[0] = 100; + score[1] = 99; + score[2] = 85; + + //利用下标输出 + cout << score[0] << endl; + cout << score[1] << endl; + cout << score[2] << endl; + + + //第二种定义方式 + //数据类型 数组名[元素个数] = {值1,值2 ,值3 ...}; + //如果{}内不足10个数据,剩余数据用0补全 + int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 }; + + //逐个输出 + //cout << score2[0] << endl; + //cout << score2[1] << endl; + + //一个一个输出太麻烦,因此可以利用循环进行输出 + for (int i = 0; i < 10; i++) + { + cout << score2[i] << endl; + } + + //定义方式3 + //数据类型 数组名[] = {值1,值2 ,值3 ...}; + int score3[] = { 100,90,80,70,60,50,40,30,20,10 }; + + for (int i = 0; i < 10; i++) + { + cout << score3[i] << endl; + } + + system("pause"); + + return 0; +} +``` > 总结1:数组名的命名规范与变量名命名规范一致,不要和变量重名 + +> 总结2:数组中下标是从0开始索引 +#### 5.2.2 一维数组数组名 + +一维数组名称的**用途**: + +1. 可以统计整个数组在内存中的长度 +2. 可以获取数组在内存中的首地址 + +**示例:** + +```C++ +int main() { + + //数组名用途 + //1、可以获取整个数组占用内存空间大小 + int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; + + cout << "整个数组所占内存空间为: " << sizeof(arr) << endl; + cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl; + cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl; + + //2、可以通过数组名获取到数组首地址 + cout << "数组首地址为: " << (int)arr << endl; + cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl; + cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl; + + //arr = 100; 错误,数组名是常量,因此不可以赋值 + + + system("pause"); + + return 0; +} +``` > 注意:数组名是常量,不可以赋值 + +> 总结1:直接打印数组名,可以查看数组所占内存的首地址 + +>总结2:对数组名进行sizeof,可以获取整个数组占内存空间的大小 + + +**练习案例1**:五只小猪称体重 + +**案例描述:** + +在一个数组中记录了五只小猪的体重,如:int arr[5] = {300,350,200,400,250}; + +找出并打印最重的小猪体重。 +**练习案例2:**数组元素逆置 + +**案例描述:**请声明一个5个元素的数组,并且将元素逆置. + +(如原数组元素为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1); +#### 5.2.3 冒泡排序 + +**作用:** 最常用的排序算法,对数组内元素进行排序 + +1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 +2. 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。 +3. 重复以上的步骤,每次比较次数-1,直到不需要比较 + +![1541905327273](assets/1541905327273.png) + +**示例:** 将数组 { 4,2,8,0,5,7,1,3,9 } 进行升序排序 + +```C++ +int main() { + + int arr[9] = { 4,2,8,0,5,7,1,3,9 }; + + for (int i = 0; i < 9 - 1; i++) + { + for (int j = 0; j < 9 - 1 - i; j++) + { + if (arr[j] > arr[j + 1]) + { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } + + for (int i = 0; i < 9; i++) + { + cout << arr[i] << endl; + } + + system("pause"); + + return 0; +} +``` +### 5.3 二维数组 + +二维数组就是在一维数组上,多加一个维度。 + +![1541905559138](assets/1541905559138.png) + +#### 5.3.1 二维数组定义方式 + +二维数组定义的四种方式: + +1. ` 数据类型 数组名[ 行数 ][ 列数 ]; ` +2. `数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };` +3. `数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};` +4. ` 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};` > 建议:以上4种定义方式,利用==第二种更加直观,提高代码的可读性== + +示例: + +```C++ +int main() { + + //方式1 + //数组类型 数组名 [行数][列数] + int arr[2][3]; + arr[0][0] = 1; + arr[0][1] = 2; + arr[0][2] = 3; + arr[1][0] = 4; + arr[1][1] = 5; + arr[1][2] = 6; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 3; j++) + { + cout << arr[i][j] << " "; + } + cout << endl; + } + + //方式2 + //数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 } }; + int arr2[2][3] = + { + {1,2,3}, + {4,5,6} + }; + + //方式3 + //数据类型 数组名[行数][列数] = { 数据1,数据2 ,数据3,数据4 }; + int arr3[2][3] = { 1,2,3,4,5,6 }; + + //方式4 + //数据类型 数组名[][列数] = { 数据1,数据2 ,数据3,数据4 }; + int arr4[][3] = { 1,2,3,4,5,6 }; + + system("pause"); + + return 0; +} +``` > 总结:在定义二维数组时,如果初始化了数据,可以省略行数 +#### 5.3.2 二维数组数组名 * 查看二维数组所占内存空间 +* 获取二维数组首地址 + +**示例:** + +```C++ +int main() { + + //二维数组数组名 + int arr[2][3] = + { + {1,2,3}, + {4,5,6} + }; + + cout << "二维数组大小: " << sizeof(arr) << endl; + cout << "二维数组一行大小: " << sizeof(arr[0]) << endl; + cout << "二维数组元素大小: " << sizeof(arr[0][0]) << endl; + + cout << "二维数组行数: " << sizeof(arr) / sizeof(arr[0]) << endl; + cout << "二维数组列数: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl; + + //地址 + cout << "二维数组首地址:" << arr << endl; + cout << "二维数组第一行地址:" << arr[0] << endl; + cout << "二维数组第二行地址:" << arr[1] << endl; + + cout << "二维数组第一个元素地址:" << &arr[0][0] << endl; + cout << "二维数组第二个元素地址:" << &arr[0][1] << endl; + + system("pause"); + + return 0; +} +``` > 总结1:二维数组名就是这个数组的首地址 + +> 总结2:对二维数组名进行sizeof时,可以获取整个二维数组占用的内存空间大小 +#### **5.3.3 二维数组应用案例** + +**考试成绩统计:** + +案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,**请分别输出三名同学的总成绩** + +| | 语文 | 数学 | 英语 | +| ---- | ---- | ---- | ---- | +| 张三 | 100 | 100 | 100 | +| 李四 | 90 | 50 | 100 | +| 王五 | 60 | 70 | 80 | + +**参考答案:** + +```C++ +int main() { + + int scores[3][3] = + { + {100,100,100}, + {90,50,100}, + {60,70,80}, + }; + + string names[3] = { "张三","李四","王五" }; + + for (int i = 0; i < 3; i++) + { + int sum = 0; + for (int j = 0; j < 3; j++) + { + sum += scores[i][j]; + } + cout << names[i] << "同学总成绩为: " << sum << endl; + } + + system("pause"); + + return 0; +} +``` +## 6 函数 + +### 6.1 概述 + +**作用:**将一段经常使用的代码封装起来,减少重复代码 + +一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。 + +### 6.2 函数的定义 + +函数的定义一般主要有5个步骤: + +1、返回值类型 + +2、函数名 + +3、参数表列 + +4、函数体语句 + +5、return 表达式 + +**语法:** + +```C++ +返回值类型 函数名 (参数列表) +{ + + 函数体语句 + + return表达式 + +} +``` * 返回值类型 :一个函数可以返回一个值。在函数定义中 +* 函数名:给函数起个名称 +* 参数列表:使用该函数时,传入的数据 +* 函数体语句:花括号内的代码,函数内需要执行的语句 +* return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据 + +**示例:**定义一个加法函数,实现两个数相加 + +```C++ +//函数定义 +int add(int num1, int num2) +{ + int sum = num1 + num2; + return sum; +} +``` + + +### 6.3 函数的调用 + +**功能:**使用定义好的函数 + +**语法:**` 函数名(参数)` + +**示例:** + +```C++ +//函数定义 +int add(int num1, int num2) //定义中的num1,num2称为形式参数,简称形参 +{ + int sum = num1 + num2; + return sum; +} + +int main() { + + int a = 10; + int b = 10; + //调用add函数 + int sum = add(a, b);//调用时的a,b称为实际参数,简称实参 + cout << "sum = " << sum << endl; + + a = 100; + b = 100; + + sum = add(a, b); + cout << "sum = " << sum << endl; + + system("pause"); + + return 0; +} +``` + +> 总结:函数定义里小括号内称为形参,函数调用时传入的参数称为实参 +### 6.4 值传递 + +* 所谓值传递,就是函数调用时实参将数值传入给形参 +* 值传递时,==如果形参发生,并不会影响实参== **示例:** + +```C++ +void swap(int num1, int num2) +{ + cout << "交换前:" << endl; + cout << "num1 = " << num1 << endl; + cout << "num2 = " << num2 << endl; + + int temp = num1; + num1 = num2; + num2 = temp; + + cout << "交换后:" << endl; + cout << "num1 = " << num1 << endl; + cout << "num2 = " << num2 << endl; + + //return ; 当函数声明时候,不需要返回值,可以不写return +} + +int main() { + + int a = 10; + int b = 20; + + swap(a, b); + + cout << "mian中的 a = " << a << endl; + cout << "mian中的 b = " << b << endl; + + system("pause"); + + return 0; +} +``` > 总结: 值传递时,形参是修饰不了实参的 +### **6.5 函数的常见样式** + +常见的函数样式有4种 + +1. 无参无返 +2. 有参无返 +3. 无参有返 +4. 有参有返 + +**示例:** + +```C++ +//函数常见样式 +//1、 无参无返 +void test01() +{ + //void a = 10; //无类型不可以创建变量,原因无法分配内存 + cout << "this is test01" << endl; + //test01(); 函数调用 +} + +//2、 有参无返 +void test02(int a) +{ + cout << "this is test02" << endl; + cout << "a = " << a << endl; +} + +//3、无参有返 +int test03() +{ + cout << "this is test03 " << endl; + return 10; +} + +//4、有参有返 +int test04(int a, int b) +{ + cout << "this is test04 " << endl; + int sum = a + b; + return sum; +} +``` + + +### 6.6 函数的声明 + +**作用:** 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。 * 函数的**声明可以多次**,但是函数的**定义只能有一次** **示例:** + +```C++ +//声明可以多次,定义只能一次 +//声明 +int max(int a, int b); +int max(int a, int b); +//定义 +int max(int a, int b) +{ + return a > b ? a : b; +} + +int main() { + + int a = 100; + int b = 200; + + cout << max(a, b) << endl; + + system("pause"); + + return 0; +} +``` + + +### 6.7 函数的分文件编写 + +**作用:**让代码结构更加清晰 + +函数分文件编写一般有4个步骤 + +1. 创建后缀名为.h的头文件 +2. 创建后缀名为.cpp的源文件 +3. 在头文件中写函数的声明 +4. 在源文件中写函数的定义 + +**示例:** + +```C++ +//swap.h文件 +#include +using namespace std; + +//实现两个数字交换的函数声明 +void swap(int a, int b); + +``` + +```C++ +//swap.cpp文件 +#include "swap.h" + +void swap(int a, int b) +{ + int temp = a; + a = b; + b = temp; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; +} +``` + +```C++ +//main函数文件 +#include "swap.h" +int main() { + + int a = 100; + int b = 200; + swap(a, b); + + system("pause"); + + return 0; +} + +``` +## 7 指针 + +### 7.1 指针的基本概念 + +**指针的作用:** 可以通过指针间接访问内存 * 内存编号是从0开始记录的,一般用十六进制数字表示 +* 可以利用指针变量保存地址 + + ​ + +### 7.2 指针变量的定义和使用 + +指针变量定义语法: `数据类型 * 变量名;` + +**示例:** + +```C++ +int main() { + + //1、指针的定义 + int a = 10; //定义整型变量a + + //指针定义语法: 数据类型 * 变量名 ; + int * p; + + //指针变量赋值 + p = &a; //指针指向变量a的地址 + cout << &a << endl; //打印数据a的地址 + cout << p << endl; //打印指针变量p + + //2、指针的使用 + //通过*操作指针变量指向的内存 + cout << "*p = " << *p << endl; + + system("pause"); + + return 0; +} +``` 指针变量和普通变量的区别 + +* 普通变量存放的是数据,指针变量存放的是地址 +* 指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用 > 总结1: 我们可以通过 & 符号 获取变量的地址 + +> 总结2:利用指针可以记录地址 + +> 总结3:对指针变量解引用,可以操作指针指向的内存 + + +### 7.3 指针所占内存空间 提问:指针也是种数据类型,那么这种数据类型占用多少内存空间? **示例:** + +```C++ +int main() { + + int a = 10; + + int * p; + p = &a; //指针指向数据a的地址 + + cout << *p << endl; //* 解引用 + cout << sizeof(p) << endl; + cout << sizeof(char *) << endl; + cout << sizeof(float *) << endl; + cout << sizeof(double *) << endl; + + system("pause"); + + return 0; +} +``` > 总结:所有指针类型在32位操作系统下是4个字节 + + +### 7.4 空指针和野指针 + +**空指针**:指针变量指向内存中编号为0的空间 + +**用途:**初始化指针变量 + +**注意:**空指针指向的内存是不可以访问的 **示例1:空指针** + +```C++ +int main() { + + //指针变量p指向内存地址编号为0的空间 + int * p = NULL; + + //访问空指针报错 + //内存编号0 ~255为系统占用内存,不允许用户访问 + cout << *p << endl; + + system("pause"); + + return 0; +} +``` + + +**野指针**:指针变量指向非法的内存空间 + +**示例2:野指针** + +```C++ +int main() { + + //指针变量p指向内存地址编号为0x1100的空间 + int * p = (int *)0x1100; + + //访问野指针报错 + cout << *p << endl; + + system("pause"); + + return 0; +} +``` + +> 总结:空指针和野指针都不是我们申请的空间,因此不要访问。 +### 7.5 const修饰指针 + +const修饰指针有三种情况 + +1. const修饰指针 --- 常量指针 +2. const修饰常量 --- 指针常量 +3. const即修饰指针,又修饰常量 +**示例:** + + +```c++ +int main() { + + int a = 10; + int b = 10; + + //const修饰的是指针,指针指向可以改,指针指向的值不可以更改 + const int * p1 = &a; + p1 = &b; //正确 + //*p1 = 100; 报错 + + + //const修饰的是常量,指针指向不可以改,指针指向的值可以更改 + int * const p2 = &a; + //p2 = &b; //错误 + *p2 = 100; //正确 + + //const既修饰指针又修饰常量 + const int * const p3 = &a; + //p3 = &b; //错误 + //*p3 = 100; //错误 + + system("pause"); + + return 0; +} +``` > 技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量 +### 7.6 指针和数组 + +**作用:**利用指针访问数组中元素 + +**示例:** + +```C++ +int main() { + + int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; + + int * p = arr; //指向数组的指针 + + cout << "第一个元素: " << arr[0] << endl; + cout << "指针访问第一个元素: " << *p << endl; + + for (int i = 0; i < 10; i++) + { + //利用指针遍历数组 + cout << *p << endl; + p++; + } + + system("pause"); + + return 0; +} +``` + + +### 7.7 指针和函数 + +**作用:**利用指针作函数参数,可以修改实参的值 **示例:** + +```C++ +//值传递 +void swap1(int a ,int b) +{ + int temp = a; + a = b; + b = temp; +} +//地址传递 +void swap2(int * p1, int *p2) +{ + int temp = *p1; + *p1 = *p2; + *p2 = temp; +} + +int main() { + + int a = 10; + int b = 20; + swap1(a, b); // 值传递不会改变实参 + + swap2(&a, &b); //地址传递会改变实参 + + cout << "a = " << a << endl; + + cout << "b = " << b << endl; + + system("pause"); + + return 0; +} +``` > 总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递 +### 7.8 指针、数组、函数 + +**案例描述:**封装一个函数,利用冒泡排序,实现对整型数组的升序排序 + +例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 }; **示例:** + +```c++ +//冒泡排序函数 +void bubbleSort(int * arr, int len) //int * arr 也可以写为int arr[] +{ + for (int i = 0; i < len - 1; i++) + { + for (int j = 0; j < len - 1 - i; j++) + { + if (arr[j] > arr[j + 1]) + { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + +//打印数组函数 +void printArray(int arr[], int len) +{ + for (int i = 0; i < len; i++) + { + cout << arr[i] << endl; + } +} + +int main() { + + int arr[10] = { 4,3,6,9,1,2,10,8,7,5 }; + int len = sizeof(arr) / sizeof(int); + + bubbleSort(arr, len); + + printArray(arr, len); + + system("pause"); + + return 0; +} +``` > 总结:当数组名传入到函数作为参数时,被退化为指向首元素的指针 +## 8 结构体 + +### 8.1 结构体基本概念 + +结构体属于用户==自定义的数据类型==,允许用户存储不同的数据类型 ### 8.2 结构体定义和使用 + +**语法:**`struct 结构体名 { 结构体成员列表 };` + +通过结构体创建变量的方式有三种: + +* struct 结构体名 变量名 +* struct 结构体名 变量名 = { 成员1值 , 成员2值...} +* 定义结构体时顺便创建变量 + +**示例:** + +```C++ +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}stu3; //结构体变量创建方式3 + + +int main() { + + //结构体变量创建方式1 + struct student stu1; //struct 关键字可以省略 + + stu1.name = "张三"; + stu1.age = 18; + stu1.score = 100; + + cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << " 分数:" << stu1.score << endl; + + //结构体变量创建方式2 + struct student stu2 = { "李四",19,60 }; + + cout << "姓名:" << stu2.name << " 年龄:" << stu2.age << " 分数:" << stu2.score << endl; + + + stu3.name = "王五"; + stu3.age = 18; + stu3.score = 80; + + + cout << "姓名:" << stu3.name << " 年龄:" << stu3.age << " 分数:" << stu3.score << endl; + + system("pause"); + + return 0; +} +``` > 总结1:定义结构体时的关键字是struct,不可省略 + +> 总结2:创建结构体变量时,关键字struct可以省略 + +> 总结3:结构体变量利用操作符 ''.'' 访问成员 +### 8.3 结构体数组 + +**作用:**将自定义的结构体放入到数组中方便维护 + +**语法:**` struct 结构体名 数组名[元素个数] = { {} , {} , ... {} }` + +**示例:** + +```C++ +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +} + +int main() { + + //结构体数组 + struct student arr[3]= + { + {"张三",18,80 }, + {"李四",19,60 }, + {"王五",20,70 } + }; + + for (int i = 0; i < 3; i++) + { + cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl; + } + + system("pause"); + + return 0; +} +``` + + +### 8.4 结构体指针 + +**作用:**通过指针访问结构体中的成员 * 利用操作符 `-> `可以通过结构体指针访问结构体属性 **示例:** + +```C++ +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + + +int main() { + + struct student stu = { "张三",18,100, }; + + struct student * p = &stu; + + p->score = 80; //指针通过 -> 操作符可以访问成员 + + cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl; + + system("pause"); + + return 0; +} +``` > 总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员 +### 8.5 结构体嵌套结构体 + +**作用:** 结构体中的成员可以是另一个结构体 + +**例如:**每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体 + +**示例:** + +```C++ +//学生结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + +//教师结构体定义 +struct teacher +{ + //成员列表 + int id; //职工编号 + string name; //教师姓名 + int age; //教师年龄 + struct student stu; //子结构体 学生 +}; + + +int main() { + + struct teacher t1; + t1.id = 10000; + t1.name = "老王"; + t1.age = 40; + + t1.stu.name = "张三"; + t1.stu.age = 18; + t1.stu.score = 100; + + cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl; + + cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl; + + system("pause"); + + return 0; +} +``` **总结:**在结构体中可以定义另一个结构体作为成员,用来解决实际问题 +### 8.6 结构体做函数参数 + +**作用:**将结构体作为参数向函数中传递 + +传递方式有两种: + +* 值传递 +* 地址传递 + +**示例:** + +```C++ +//学生结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + +//值传递 +void printStudent(student stu ) +{ + stu.age = 28; + cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; +} + +//地址传递 +void printStudent2(student *stu) +{ + stu->age = 28; + cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age << " 分数:" << stu->score << endl; +} + +int main() { + + student stu = { "张三",18,100}; + //值传递 + printStudent(stu); + cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; + + cout << endl; + + //地址传递 + printStudent2(&stu); + cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; + + system("pause"); + + return 0; +} +``` + +> 总结:如果不想修改主函数中的数据,用值传递,反之用地址传递 ### 8.7 结构体中 const使用场景 + +**作用:**用const来防止误操作 + +**示例:** + +```C++ +//学生结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + +//const使用场景 +void printStudent(const student *stu) //加const防止函数体中的误操作 +{ + //stu->age = 100; //操作失败,因为加了const修饰 + cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl; + +} + +int main() { + + student stu = { "张三",18,100 }; + + printStudent(&stu); + + system("pause"); + + return 0; +} +``` +### 8.8 结构体案例 + +#### 8.8.1 案例1 + +**案例描述:** + +学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下 + +设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员 + +学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值 + +最终打印出老师数据以及老师所带的学生数据。 **示例:** + +```C++ +struct Student +{ + string name; + int score; +}; +struct Teacher +{ + string name; + Student sArray[5]; +}; + +void allocateSpace(Teacher tArray[] , int len) +{ + string tName = "教师"; + string sName = "学生"; + string nameSeed = "ABCDE"; + for (int i = 0; i < len; i++) + { + tArray[i].name = tName + nameSeed[i]; + + for (int j = 0; j < 5; j++) + { + tArray[i].sArray[j].name = sName + nameSeed[j]; + tArray[i].sArray[j].score = rand() % 61 + 40; + } + } +} + +void printTeachers(Teacher tArray[], int len) +{ + for (int i = 0; i < len; i++) + { + cout << tArray[i].name << endl; + for (int j = 0; j < 5; j++) + { + cout << "\t姓名:" << tArray[i].sArray[j].name << " 分数:" << tArray[i].sArray[j].score << endl; + } + } +} + +int main() { + + srand((unsigned int)time(NULL)); //随机数种子 头文件 #include + + Teacher tArray[3]; //老师数组 + + int len = sizeof(tArray) / sizeof(Teacher); + + allocateSpace(tArray, len); //创建数据 + + printTeachers(tArray, len); //打印数据 + + system("pause"); + + return 0; +} +``` +#### 8.8.2 案例2 + +**案例描述:** + +设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。 + +通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。 五名英雄信息如下: + +```C++ + {"刘备",23,"男"}, + {"关羽",22,"男"}, + {"张飞",20,"男"}, + {"赵云",21,"男"}, + {"貂蝉",19,"女"}, +``` +**示例:** + +```C++ +//英雄结构体 +struct hero +{ + string name; + int age; + string sex; +}; +//冒泡排序 +void bubbleSort(hero arr[] , int len) +{ + for (int i = 0; i < len - 1; i++) + { + for (int j = 0; j < len - 1 - i; j++) + { + if (arr[j].age > arr[j + 1].age) + { + hero temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} +//打印数组 +void printHeros(hero arr[], int len) +{ + for (int i = 0; i < len; i++) + { + cout << "姓名: " << arr[i].name << " 性别: " << arr[i].sex << " 年龄: " << arr[i].age << endl; + } +} + +int main() { + + struct hero arr[5] = + { + {"刘备",23,"男"}, + {"关羽",22,"男"}, + {"张飞",20,"男"}, + {"赵云",21,"男"}, + {"貂蝉",19,"女"}, + }; + + int len = sizeof(arr) / sizeof(hero); //获取数组元素个数 + + bubbleSort(arr, len); //排序 + + printHeros(arr, len); //打印 + + system("pause"); + + return 0; +} +``` # diff --git "a/C++ 20\350\257\255\350\250\200\347\211\271\346\200\247.md" "b/C++ 20\350\257\255\350\250\200\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..582d57ba8ff1feda23bafa7f3f8fa34dc5fd808f --- /dev/null +++ "b/C++ 20\350\257\255\350\250\200\347\211\271\346\200\247.md" @@ -0,0 +1,618 @@ +> 本文为译文,原文链接:https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP20.md + +### Coroutines(协程) + +协程是一种特殊的函数,它的执行可以被暂停或恢复。要定义协程,关键字`co_return `,` co_await `,或`co_yield `必须出现在函数体中。c++ 20的协程是无栈的;除非编译器进行了优化,否则它们的状态是在堆上分配的。 + +协程的一个例子是**generator**函数,它在每次调用时生成一个值: + +```cpp +generator range(int start, int end) { + while (start < end) { + co_yield start; + start++; + } + + // Implicit co_return at the end of this function: + // co_return; +} + +for (int n : range(0, 10)) { + std::cout << n << std::endl; +} +``` + +上面的` range `生成器函数生成的值从` start `开始直到` end `(互斥),每个迭代步骤生成存储在` start `中的当前值。生成器在每次调用` range `时都保持它的状态(在本例中,调用是针对for循环中的每次迭代)。` co_yield `接受给定的表达式,生成(即返回)它的值,并在那一点暂停协程。在恢复时,在` co_yield `之后继续执行。 + +协程的另一个例子是*task*,它是一个在等待任务时执行的异步计算: + +```cpp +task echo(socket s) { + for (;;) { + auto data = co_await s.async_read(); + co_await async_write(s, data); + } + + // Implicit co_return at the end of this function: + // co_return; +} +``` + +在本例中,引入了` co_await `关键字。这个关键字接受一个表达式,如果您正在等待的东西(在本例中是读或写)没有准备好,则挂起执行,否则继续执行。(注意,在内部,` co_yield `使用` co_await `。) + +使用任务惰性地评估一个值: + +```cpp +task calculate_meaning_of_life() { + co_return 42; +} + +auto meaning_of_life = calculate_meaning_of_life(); +// ... +co_await meaning_of_life; // == 42 +``` + +注意:虽然这些示例说明了如何在基本级别上使用协程,但在编译代码时还有更多内容。这些例子并不意味着完全覆盖c++ 20的协程。由于标准库还没有提供` generator `和` task `类,所以我使用cppcoro库来编译这些示例。 + +### Concepts(概念) + +*概念*被命名为约束类型的编译时谓词。它们的形式如下: + +```cpp +template < template-parameter-list > +concept concept-name = constraint-expression; +``` + +其中` constraint-expression `计算为constexpr布尔值。*约束*应该对语义需求进行建模,例如类型是数字类型还是可哈希类型。如果给定的类型不满足它所绑定的概念(例如:“约束表达式”返回“false”)。因为约束是在编译时计算的,所以它们可以提供更有意义的错误消息和运行时安全性。 + +```cpp +// `T` is not limited by any constraints. +template +concept always_satisfied = true; +// Limit `T` to integrals. +template +concept integral = std::is_integral_v; +// Limit `T` to both the `integral` constraint and signedness. +template +concept signed_integral = integral && std::is_signed_v; +// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint. +template +concept unsigned_integral = integral && !signed_integral; +``` + +有各种各样的语法形式来加强概念: + +```cpp +// Forms for function parameters: +// `T` is a constrained type template parameter. +template +void f(T v); + +// `T` is a constrained type template parameter. +template + requires my_concept +void f(T v); + +// `T` is a constrained type template parameter. +template +void f(T v) requires my_concept; + +// `v` is a constrained deduced parameter. +void f(my_concept auto v); + +// `v` is a constrained non-type template parameter. +template +void g(); + +// Forms for auto-deduced variables: +// `foo` is a constrained auto-deduced value. +my_concept auto foo = ...; + +// Forms for lambdas: +// `T` is a constrained type template parameter. +auto f = [] (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] requires my_concept (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] (T v) requires my_concept { + // ... +}; +// `v` is a constrained deduced parameter. +auto f = [](my_concept auto v) { + // ... +}; +// `v` is a constrained non-type template parameter. +auto g = [] () { + // ... +}; +``` + +` requires `关键字可以用来启动一个require子句或一个require表达式: + +```cpp +template + requires my_concept // `requires` clause. +void f(T); + +template +concept callable = requires (T f) { f(); }; // `requires` expression. + +template + requires requires (T x) { x + x; } // `requires` clause and expression on same line. +T add(T a, T b) { + return a + b; +} +``` + +注意,requires表达式中的参数列表是可选的。require表达式中的每个需求都是下列要求之一: + +- **Simple requirements** - 断言给定表达式是否有效。 + +```cpp +template +concept callable = requires (T f) { f(); }; +``` + +- **Type requirements** - 关键字` typename `后跟一个类型名表示,断言给定的类型名是有效的。 + +```cpp +struct foo { + int foo; +}; + +struct bar { + using value = int; + value data; +}; + +struct baz { + using value = int; + value data; +}; + +// Using SFINAE, enable if `T` is a `baz`. +template >> +struct S {}; + +template +using Ref = T&; + +template +concept C = requires { + // Requirements on type `T`: + typename T::value; // A) has an inner member named `value` + typename S; // B) must have a valid class template specialization for `S` + typename Ref; // C) must be a valid alias template substitution +}; + +template +void g(T a); + +g(foo{}); // ERROR: Fails requirement A. +g(bar{}); // ERROR: Fails requirement B. +g(baz{}); // PASS. +``` + +- **Compound requirements** - 用大括号括起来的表达式,后面跟着返回类型或类型约束。 + +```cpp +template +concept C = requires(T x) { + {*x} -> typename T::inner; // the type of the expression `*x` is convertible to `T::inner` + {x + 1} -> std::same_as; // the expression `x + 1` satisfies `std::same_as` + {x * 1} -> T; // the type of the expression `x * 1` is convertible to `T` +}; +``` + +- **Nested requirements** - 由` requires `关键字表示,指定额外的约束(例如本地参数参数)。 + +```cpp +template +concept C = requires(T x) { + requires std::same_as; +}; +``` + +### Designated initializers(指定初始化式) + +c风格指定初始化式语法。任何未显式列出在指定初始化列表中的成员字段都是默认初始化的。 + +```cpp +struct A { + int x; + int y; + int z = 123; +}; + +A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2 +``` + +### Template syntax for lambdas(lambda的模板语法) + +在lambda表达式中使用熟悉的模板语法。 + +```cpp +auto f = [](std::vector v) { + // ... +}; +``` + +### Range-based for loop with initializer(带初始化器的基于范围的for循环) + +该特性简化了常见的代码模式,有助于保持范围紧凑,并为常见的生存期问题提供了优雅的解决方案。 + +```cpp +for (std::vector v{1, 2, 3}; auto& e : v) { + std::cout << e; +} +// prints "123" +``` + +### likely and unlikely attributes(可能和不可能的属性) + +向优化器提供提示,说明已标记语句执行的概率很高。 + +```cpp +switch (n) { +case 1: + // ... + break; + +[[likely]] case 2: // n == 2 is considered to be arbitrarily more + // ... // likely than any other value of n + break; +} +``` + +如果一个可能/不太可能的属性出现在If语句的右括号之后,则表明分支可能/不太可能执行其子语句(体)。 + +```cpp +int random = get_random_number_between_x_and_y(0, 3); +if (random > 0) [[likely]] { + // body of if statement + // ... +} +``` + +它也可以应用于迭代语句的子语句(体)。 + +```cpp +while (unlikely_truthy_condition) [[unlikely]] { + // body of while statement + // ... +} +``` + +### Deprecate implicit capture of this(不建议隐式捕获) + +在lamdba捕获中使用`[=]`隐式捕获` this `现在已弃用;更喜欢使用` [=,this] `或` [=,*this] `显式捕获。 + +```cpp +struct int_value { + int n = 0; + auto getter_fn() { + // BAD: + // return [=]() { return n; }; + + // GOOD: + return [=, *this]() { return n; }; + } +}; +``` + +### Class types in non-type template parameters(非类型模板形参中的类型) + +类现在可以在非类型模板参数中使用。作为模板参数传入的对象的类型为` const T `,其中` T `是对象的类型,并且具有静态存储时间。 + +```cpp +struct foo { + foo() = default; + constexpr foo(int) {} +}; + +template +auto get_foo() { + return f; +} + +get_foo(); // uses implicit constructor +get_foo(); +``` + +### constexpr virtual functions(constexpr虚函数) + +虚函数现在可以是` constexpr `并在编译时计算。` constexpr `虚函数可以覆盖非` constexpr `虚函数,反之亦然。 + +```cpp +struct X1 { + virtual int f() const = 0; +}; + +struct X2: public X1 { + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 { + virtual int f() const { return 3; } +}; + +struct X4: public X3 { + constexpr virtual int f() const { return 4; } +}; + +constexpr X4 x4; +x4.f(); // == 4 +``` + +### explicit(bool)(是否显式) + +在编译时有条件地选择构造函数是否显式。` explicit(true) `与指定` explicit `相同。 + +```cpp +struct foo { + // Specify non-integral types (strings, floats, etc.) require explicit construction. + template + explicit(!std::is_integral_v) foo(T) {} +}; + +foo a = 123; // OK +foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true) +foo c {"123"}; // OK +``` + +### Immediate functions + +类似于` constexpr `函数,但是带有` consteval `说明符的函数必须产生一个常量。这些被称为“直接函数”。 + +```cpp +consteval int sqr(int n) { + return n * n; +} + +constexpr int r = sqr(100); // OK +int x = 100; +int r2 = sqr(x); // ERROR: the value of `x` is not usable in a constant expression + // OK if `sqr` were a `constexpr` function +``` + +### using enum + +将枚举成员引入作用域以提高可读性。之前: + +```cpp +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel channel) { + switch (channel) { + case rgba_color_channel::red: return "red"; + case rgba_color_channel::green: return "green"; + case rgba_color_channel::blue: return "blue"; + case rgba_color_channel::alpha: return "alpha"; + } +} +``` + +之后: + +```cpp +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel my_channel) { + switch (my_channel) { + using enum rgba_color_channel; + case red: return "red"; + case green: return "green"; + case blue: return "blue"; + case alpha: return "alpha"; + } +} +``` + +### Lambda capture of parameter pack(参数包的Lambda捕获) + +捕获参数按值包: + +```cpp +template +auto f(Args&&... args){ + // BY VALUE: + return [...args = std::forward(args)] { + // ... + }; +} +``` + +通过引用捕获参数包: + +```cpp +template +auto f(Args&&... args){ + // BY REFERENCE: + return [&...args = std::forward(args)] { + // ... + }; +} +``` + +### char8_t + +提供表示UTF-8字符串的标准类型。 + +```cpp +char8_t utf8_str[] = u8"\u0123"; +``` + +## c++ 20库功能 + +### Concepts library + +标准库还提供了用于构建更复杂概念的概念。其中包括: + +**核心语言概念:** + +- `same_as` - 指定两种相同的类型。 +- `derived_from` - 指定一个类型派生自另一个类型。 +- `convertible_to` - 指定一个类型可隐式转换为另一个类型。 +- `common_with` - 指定两个类型共享一个公共类型。 +- `integral` - 指定类型为整型。 +- `default_constructible` - 指定可以默认构造类型的对象。 + +**Comparison concepts:** + +- `boolean` - 指定可在布尔上下文中使用的类型。 +- `equality_comparable` - 指定` operator== `是一个等价关系。 + +**Object concepts:** + +- `movable` - 指定可移动和交换某一类型的对象。 +- `copyable` - 指定可复制、移动和交换某一类型的对象。 +- `semiregular` - 指定某个类型的对象可以被复制、移动、交换和默认构造。 +- `regular` - 指定类型为*regular*,即既为` semiregular `又为` equality_comparable `。 + +**Callable concepts:** + +- `invocable` - 指定可使用给定参数类型集调用可调用类型。 +- `predicate` - 指定可调用类型是布尔谓词。 + +### Synchronized buffered outputstream + +缓冲包装输出流的输出操作,以确保同步(即输出没有交错)。 + +```cpp +std::osyncstream{std::cout} << "The value of x is:" << x << std::endl; +``` + +### std::span + +span是容器的一个视图(即非所有者视图),它提供了对连续元素组的边界检查访问。由于视图不拥有它们自己的元素,它们的构造和复制成本很低——考虑视图的一种简单方法是它们持有对其数据的引用。跨度可以是动态大小的,也可以是固定大小的。 + +```cpp +void f(std::span ints) { + std::for_each(ints.begin(), ints.end(), [](auto i) { + // ... + }); +} + +std::vector v = {1, 2, 3}; +f(v); +std::array a = {1, 2, 3}; +f(a); +// etc. +``` + +示例:与维护指针和长度字段不同,span将这两个字段打包在一个容器中。 + +```cpp +constexpr size_t LENGTH_ELEMENTS = 3; +int* arr = new int[LENGTH_ELEMENTS]; // arr = {0, 0, 0} + +// Fixed-sized span which provides a view of `arr`. +std::span span = arr; +span[1] = 1; // arr = {0, 1, 0} + +// Dynamic-sized span which provides a view of `arr`. +std::span d_span = arr; +span[0] = 1; // arr = {1, 1, 0} +constexpr size_t LENGTH_ELEMENTS = 3; +int* arr = new int[LENGTH_ELEMENTS]; + +std::span span = arr; // OK +std::span span2 = arr; // ERROR +std::span span3 = arr; // ERROR +``` + +### Bit operations + +c++ 20提供了一个新的` `头,它提供了一些位操作,包括popcount。 + +```cpp +std::popcount(0u); // 0 +std::popcount(1u); // 1 +std::popcount(0b1111`0000u); // 4 +``` + +### Math constants + +在` ` header中定义的数学常量,包括PI、欧拉数等。 + +```cpp +std::numbers::pi; // 3.14159... +std::numbers::e; // 2.71828... +``` + +### std::is_constant_evaluated + +谓词函数,当它在编译时上下文中被调用时为真 + +```cpp +constexpr bool is_compile_time() { + return std::is_constant_evaluated(); +} + +constexpr bool a = is_compile_time(); // true +bool b = is_compile_time(); // false +``` + +### std::make_shared supports arrays + +```cpp +auto p = std::make_shared(5); // pointer to `int[5]` +// OR +auto p = std::make_shared(); // pointer to `int[5]` +``` + +### starts_with and ends_with on strings + +字符串(和字符串视图)现在有` starts_with `和` ends_with `成员函数来检查一个字符串是否以给定的字符串开始或结束。 + +```cpp +std::string str = "foobar"; +str.starts_with("foo"); // true +str.ends_with("baz"); // false +``` + +### Check if associative container has element + +像集合和映射这样的关联容器有一个“contains”成员函数,它可以用来代替“查找和检查迭代器的结束”习惯用法。 + +```cpp +std::map map {{1, `a`}, {2, `b`}}; +map.contains(2); // true +map.contains(123); // false + +std::set set {1, 2, 3}; +set.contains(2); // true +``` + +### std::bit_cast + +将对象从一种类型重新解释为另一种类型的更安全的方法。 + +```cpp +float f = 123.0; +int i = std::bit_cast(f); +``` + +### std::midpoint + +安全地计算两个整数的中点(不溢出)。 + +```cpp +std::midpoint(1, 3); // == 2 +``` + +### std::to_array + +将给定的数组/"array-like"对象转换为` std::array `。 + +```cpp +std::to_array("foo"); // returns `std::array` +std::to_array({1, 2, 3}); // returns `std::array` + +int a[] = {1, 2, 3}; +std::to_array(a); // returns `std::array` +``` diff --git "a/C++ \345\205\245\351\227\250\346\225\231\347\250\213\357\274\21041\350\257\276\346\227\266\357\274\211 - \351\230\277\351\207\214\344\272\221\345\244\247\345\255\246.md" "b/C++ \345\205\245\351\227\250\346\225\231\347\250\213\357\274\21041\350\257\276\346\227\266\357\274\211 - \351\230\277\351\207\214\344\272\221\345\244\247\345\255\246.md" new file mode 100644 index 0000000000000000000000000000000000000000..5eaff91a9f86a450272da3cbead59d82c0c662c1 --- /dev/null +++ "b/C++ \345\205\245\351\227\250\346\225\231\347\250\213\357\274\21041\350\257\276\346\227\266\357\274\211 - \351\230\277\351\207\214\344\272\221\345\244\247\345\255\246.md" @@ -0,0 +1,5300 @@ +# C++ 教程 + +![img](https://edu.aliyun.com/files/course/2017/09-24/1539291c8853274278.png) + +C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。C++ 可运行于多种平台上,如 Windows、MAC 操作系统以及 UNIX 的各种版本。 + +本教程通过通俗易懂的语言来讲解 C++ 编程语言。 + +**现在开始学习 C++ 编程!** + + + +## 谁适合阅读本教程? + +本教程是专门为初学者打造的,帮助他们理解与 C++ 编程语言相关的基础到高级的概念。 + +## 阅读本教程前,您需要了解的知识: + +在您开始练习本教程中所给出的各种实例之前,您需要对计算机程序和计算机程序设计语言有基本的了解。 + +## 编译/执行 C++ 程序 + +## 实例 + +```cpp +#include using namespace std;int main(){ + cout << "Hello, world!" << endl; return 0; +} +``` + +运行结果: + +```cpp +Hello, world! +``` + +你可以用 "\n" 代替以上代码里的 "endl"。 + +# C++ 简介 + +C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。 + +C++ 被认为是一种**中级**语言,它综合了高级语言和低级语言的特点。 + +C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的C,后来在 1983 年更名为 C++。 + +C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。 + +**注意:**使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。 + +## 面向对象程序设计 + +C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性: + +- 封装 +- 抽象 +- 继承 +- 多态 + +## 标准库 + +标准的 C++ 由三个重要部分组成: + +- 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。 +- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。 +- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。 + +## ANSI 标准 + +ANSI 标准是为了确保 C++ 的便携性 —— 您所编写的代码在 Mac、UNIX、Windows、Alpha 计算机上都能通过编译。 + +由于 ANSI 标准已稳定使用了很长的时间,所有主要的 C++ 编译器的制造商都支持 ANSI 标准。 + +## 学习 C++ + +学习 C++,关键是要理解概念,而不应过于深究语言的技术细节。 + +学习程序设计语言的目的是为了成为一个更好的程序员,也就是说,是为了能更有效率地设计和实现新系统,以及维护旧系统。 + +C++ 支持多种编程风格。您可以使用 Fortran、C、Smalltalk 等任意一种语言的编程风格来编写代码。每种风格都能有效地保证运行时间效率和空间效率。 + +## C++ 的使用 + +基本上每个应用程序领域的程序员都有使用 C++。 + +C++ 通常用于编写设备驱动程序和其他要求实时性的直接操作硬件的软件。 + +C++ 广泛用于教学和研究。 + +任何一个使用苹果电脑或 Windows PC 机的用户都在间接地使用 C++,因为这些系统的主要用户接口是使用 C++ 编写的。 + +------ + +## 标准化 + +| 发布时间 | 文档 | 通称 | 备注 | | +| :------- | :-------------------- | :----- | :------------------ | :--- | +| 2015 | ISO/IEC TS 19570:2015 | - | 用于并行计算的扩展 | | +| 2015 | ISO/IEC TS 18822:2015 | - | 文件系统 | | +| 2014 | ISO/IEC 14882:2014 | C++14 | 第四个C++标准 | | +| 2011 | ISO/IEC TR 24733:2011 | - | 十进制浮点数扩展 | | +| 2011 | ISO/IEC 14882:2011 | C++11 | 第三个C++标准 | | +| 2010 | ISO/IEC TR 29124:2010 | - | 数学函数扩展 | | +| 2007 | ISO/IEC TR 19768:2007 | C++TR1 | C++技术报告:库扩展 | | +| 2006 | ISO/IEC TR 18015:2006 | - | C++性能技术报告 | | +| 2003 | ISO/IEC 14882:2003 | C++03 | 第二个C++标准 | | +| 1998 | ISO/IEC 14882:1998 | C++98 | 第一个C++标准 | | + +# C++ 环境设置 + +## 本地环境设置 + +如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 + +## 文本编辑器 + +这将用于输入您的程序。文本编辑器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。 + +文本编辑器的名称和版本在不同的操作系统上可能会有所不同。例如,Notepad 通常用于 Windows 操作系统上,vim/vi 可用于 Windows 和 Linux/UNIX 操作系统上。 + +通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 + +在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 + +## C++ 编译器 + +写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。 + +C++ 编译器用于把源代码编译成最终的可执行程序。 + +大多数的 C++ 编译器并不在乎源文件的扩展名,但是如果您未指定扩展名,则默认使用 .cpp。 + +最常用的免费可用的编译器是 GNU 的 C/C++ 编译器,如果您使用的是 HP 或 Solaris,则可以使用各自操作系统上的编译器。 + +以下部分将指导您如何在不同的操作系统上安装 GNU 的 C/C++ 编译器。这里同时提到 C/C++,主要是因为 GNU 的 gcc 编译器适合于 C 和 C++ 编程语言。 + +## 安装 GNU 的 C/C++ 编译器 + +### UNIX/Linux 上的安装 + +如果您使用的是 **Linux 或 UNIX**,请在命令行使用下面的命令来检查您的系统上是否安装了 GCC: + +```cpp +$ g++ -v +``` + +如果您的计算机上已经安装了 GNU 编译器,则会显示如下消息: + +```cpp +Using built-in specs.Target: i386-redhat-linuxConfigured with: ../configure --prefix=/usr .......Thread model: posix +gcc version 4.1.2 20080704 (Red Hat 4.1.2-46) +``` + +如果未安装 GCC,那么请按照 http://gcc.gnu.org/install/ 上的详细说明安装 GCC。 + +### Mac OS X 上的安装 + +如果您使用的是 Mac OS X,最快捷的获取 GCC 的方法是从苹果的网站上下载 Xcode 开发环境,并按照安装说明进行安装。一旦安装上 Xcode,您就能使用 GNU 编译器。 + +Xcode 目前可从 developer.apple.com/technologies/tools/ 上下载。 + +### Windows 上的安装 + +为了在 Windows 上安装 GCC,您需要安装 MinGW。为了安装 MinGW,请访问 MinGW 的主页 www.mingw.org,进入 MinGW 下载页面,下载最新版本的 MinGW 安装程序,命名格式为 MinGW-.exe。 + +当安装 MinGW 时,您至少要安装 gcc-core、gcc-g++、binutils 和 MinGW runtime,但是一般情况下都会安装更多其他的项。 + +添加您安装的 MinGW 的 bin 子目录到您的 **PATH** 环境变量中,这样您就可以在命令行中通过简单的名称来指定这些工具。 + +当完成安装时,您可以从 Windows 命令行上运行 gcc、g++、ar、ranlib、dlltool 和其他一些 GNU 工具。 + +------ + +## 使用 Visual Studio (Graphical Interface) 编译 + +1、下载及安装 Visual Studio Community 2015。 + +2、打开 Visual Studio Community + +3、点击 File -> New -> Project + +![img](https://edu.aliyun.com/files/course/2017/09-24/154425967c4e731874.png) + +4、左侧列表选择 Templates -> Visual C++ -> Win32 Console Application,并设置项目名为 MyFirstProgram。 + +![img](https://edu.aliyun.com/files/course/2017/09-24/154430e60ee9018384.png) + + + +5、点击 OK。 + +6、在以下窗口中点击 Next + +![img](https://edu.aliyun.com/files/course/2017/09-24/15444190dd2e989801.png) + +7、在弹出的窗口中选择 Empty project 选项后,点击 Finish 按钮: + +8、右击文件夹 Source File 并点击 Add --> New Item... : + +![img](https://edu.aliyun.com/files/course/2017/09-24/15445135154c170829.png) + +9、选择 C++ File 然后设置文件名为 main.cpp,然后点击 Add: + +![img](https://edu.aliyun.com/files/course/2017/09-24/154459b03a55098499.png) + + + +10、拷贝以下代码到 main.cpp 中: + +```cpp +#include int main(){ + std::cout << "Hello World!\n"; + return 0;} +``` + +界面如下所示: + +![img](https://edu.aliyun.com/files/course/2017/09-24/15450518149e040143.png) + +11、点击菜单上的 Debug -> Start Without Debugging (或按下 ctrl + F5) : + +![img](https://edu.aliyun.com/files/course/2017/09-24/154511746480630632.png) + +12、完成以上操作后,你可以看到以下输出: + +![img](https://edu.aliyun.com/files/course/2017/09-24/154519fb938f394280.png) + +------ + +## g++ 应用说明 + +程序 g++ 是将 gcc 默认语言设为 C++ 的一个特殊的版本,链接时它自动使用 C++ 标准库而不用 C 标准库。通过遵循源码的命名规范并指定对应库的名字,用 gcc 来编译链接 C++ 程序是可行的,如下例所示: + +```cpp +$ gcc main.cpp -lstdc++ -o main +``` + +下面是一个保存在文件 helloworld.cpp 中一个简单的 C++ 程序的代码: + +```cpp +#include using namespace std;int main(){ + cout << "Hello, world!" << endl; + return 0;} +``` + +最简单的编译方式: + +```cpp +$ g++ helloworld.cpp +``` + +由于命令行中未指定可执行程序的文件名,编译器采用默认的 a.out。程序可以这样来运行: + +```cpp +$ ./a.outHello, world! +``` + +通常我们使用 **-o** 选项指定可执行程序的文件名,以下实例生成一个 helloworld 的可执行文件: + +```cpp +$ g++ helloworld.cpp -o helloworld +``` + +执行 helloworld: + +```cpp +$ ./helloworldHello, world! +``` + +如果是多个 C++ 代码文件,如 runoob1.cpp、runoob2.cpp,编译命令如下: + +```cpp +$ g++ runoob1.cpp cpp、runoob2.cpp -o runoob +``` + +生成一个 runoob 可执行文件。 + +g++ 有些系统默认是使用 C++98,我们可以指定使用 C++11 来编译 main.cpp 文件: + +```cpp +g++ -g -Wall -std=c++11 main.cpp +``` + +### g++ 常用命令选项 + +| 选项 | 解释 | +| :----------- | :----------------------------------------------------------- | +| -ansi | 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。 | +| -c | 只编译并生成目标文件。 | +| -DMACRO | 以字符串"1"定义 MACRO 宏。 | +| -DMACRO=DEFN | 以字符串"DEFN"定义 MACRO 宏。 | +| -E | 只运行 C 预编译器。 | +| -g | 生成调试信息。GNU 调试器可利用该信息。 | +| -IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY。 | +| -LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY。 | +| -lLIBRARY | 连接时搜索指定的函数库LIBRARY。 | +| -m486 | 针对 486 进行代码优化。 | +| -o | FILE 生成指定的输出文件。用在生成可执行文件时。 | +| -O0 | 不进行优化处理。 | +| -O | 或 -O1 优化生成代码。 | +| -O2 | 进一步优化。 | +| -O3 | 比 -O2 更进一步优化,包括 inline 函数。 | +| -shared | 生成共享目标文件。通常用在建立共享库时。 | +| -static | 禁止使用共享连接。 | +| -UMACRO | 取消对 MACRO 宏的定义。 | +| -w | 不生成任何警告信息。 | +| -Wall | 生成所有警告信息。 | + +# C++ 基本语法 + +C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 + +- **对象 -** 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 +- **类 -** 类可以定义为描述对象行为/状态的模板/蓝图。 +- **方法 -** 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 +- **即时变量 -** 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 + +## C++ 程序结构 + +让我们看一段简单的代码,可以输出单词 *Hello World*。 + +## 实例 + +```cpp +#include using namespace std; // main() 是程序开始执行的地方 + int main(){ + cout << "Hello World"; // 输出 Hello World return 0; + } +``` + +接下来我们讲解一下上面这段程序: + +- C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。上面这段程序中,包含了头文件 ****。 +- 行 **using namespace std;** 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。 +- 下一行 **// main() 是程序开始执行的地方** 是一个单行注释。单行注释以 // 开头,在行末结束。 +- 下一行 **int main()** 是主函数,程序从这里开始执行。 +- 下一行 **cout << "Hello World";** 会在屏幕上显示消息 "Hello World"。 +- 下一行 **return 0;** 终止 main( )函数,并向调用进程返回值 0。 + +## 编译 & 执行 C++ 程序 + +接下来让我们看看如何把源代码保存在一个文件中,以及如何编译并运行它。下面是简单的步骤: + +- 打开一个文本编辑器,添加上述代码。 +- 保存文件为 hello.cpp。 +- 打开命令提示符,进入到保存文件所在的目录。 +- 键入 'g++ hello.cpp ',输入回车,编译代码。如果代码中没有错误,命令提示符会跳到下一行,并生成 a.out 可执行文件。 +- 现在,键入 ' a.out' 来运行程序。 +- 您可以看到屏幕上显示 ' Hello World '。 + +```cpp +$ g++ hello.cpp$ ./a.outHello World +``` + +请确保您的路径中已包含 g++ 编译器,并确保在包含源文件 hello.cpp 的目录中运行它。 + +您也可以使用 makefile 来编译 C/C++ 程序。 + +## C++ 中的分号 & 块 + +在 C++ 中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。 + +例如,下面是三个不同的语句: + +```cpp +x = y;y = y+1;add(x, y); +``` + +块是一组使用大括号括起来的按逻辑连接的语句。例如: + +```cpp +{ cout << "Hello World"; // 输出 Hello World return 0;} +``` + +C++ 不以行末作为结束符的标识,因此,您可以在一行上放置多个语句。例如: + +```cpp +x = y;y = y+1;add(x, y); +``` + +等同于 + +```cpp +x = y; y = y+1; add(x, y); +``` + +## C++ 标识符 + +C++ 标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。 + +C++ 标识符内不允许出现标点字符,比如 @、$ 和 %。C++ 是区分大小写的编程语言。因此,在 C++ 中,**Manpower** 和 **manpower** 是两个不同的标识符。 + +下面列出几个有效的标识符: + +```cpp +mohd zara abc move_name a_123myname50 _temp j a23b9 retVal +``` + +## C++ 关键字 + +下表列出了 C++ 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。 + +| asm | else | new | this | +| ------------ | --------- | ---------------- | -------- | +| auto | enum | operator | throw | +| bool | explicit | private | true | +| break | export | protected | try | +| case | extern | public | typedef | +| catch | false | register | typeid | +| char | float | reinterpret_cast | typename | +| class | for | return | union | +| const | friend | short | unsigned | +| const_cast | goto | signed | using | +| continue | if | sizeof | virtual | +| default | inline | static | void | +| delete | int | static_cast | volatile | +| do | long | struct | wchar_t | +| double | mutable | switch | while | +| dynamic_cast | namespace | template | | + + + +## 三字符组 + +三字符组就是用于表示另一个字符的三个字符序列,又称为三字符序列。三字符序列总是以两个问号开头。 + +三字符序列不太常见,但 C++ 标准允许把某些字符指定为三字符序列。以前为了表示键盘上没有的字符,这是必不可少的一种方法。 + +三字符序列可以出现在任何地方,包括字符串、字符序列、注释和预处理指令。 + +下面列出了最常用的三字符序列: + +| 三字符组 | 替换 | +| :------- | :--- | +| ??= | # | +| ??/ | \ | +| ??' | ^ | +| ??( | [ | +| ??) | ] | +| ??! | \| | +| ??< | { | +| ??> | } | +| ??- | ~ | + +如果希望在源程序中有两个连续的问号,且不希望被预处理器替换,这种情况出现在字符常量、字符串字面值或者是程序注释中,可选办法是用字符串的自动连接:"...?""?..."或者转义序列:"...?\?..."。 + +从Microsoft Visual C++ 2010版开始,该编译器默认不再自动替换三字符组。如果需要使用三字符组替换(如为了兼容古老的软件代码),需要设置编译器命令行选项/Zc:trigraphs + +g++仍默认支持三字符组,但会给出编译警告。 + +## C++ 中的空格 + +只包含空格的行,被称为空白行,可能带有注释,C++ 编译器会完全忽略它。 + +在 C++ 中,空格用于描述空白符、制表符、换行符和注释。空格分隔语句的各个部分,让编译器能识别语句中的某个元素(比如 int)在哪里结束,下一个元素在哪里开始。因此,在下面的语句中: + +```cpp +int age; +``` + +在这里,int 和 age 之间必须至少有一个空格字符(通常是一个空白符),这样编译器才能够区分它们。另一方面,在下面的语句中: + +```cpp +fruit = apples + oranges; // 获取水果的总数 +``` + +fruit 和 =,或者 = 和 apples 之间的空格字符不是必需的,但是为了增强可读性,您可以根据需要适当增加一些空格。 + +# C++ 注释 + +程序的注释是解释性语句,您可以在 C++ 代码中包含注释,这将提高源代码的可读性。所有的编程语言都允许某种形式的注释。 + +C++ 支持单行注释和多行注释。注释中的所有字符会被 C++ 编译器忽略。 + +C++ 注释以 /* 开始,以 */ 终止。例如: + +```cpp +/* 这是注释 *//* C++ 注释也可以 + * 跨行 + */ +``` + +注释也能以 // 开始,直到行末为止。例如: + +```cpp +#include using namespace std;main(){ + cout << "Hello World"; // 输出 Hello World + + return 0;} +``` + +当上面的代码被编译时,编译器会忽略 **// 输出 Hello World**,最后会产生以下结果: + +```cpp +Hello World +``` + +在 /* 和 */ 注释内部,// 字符没有特殊的含义。在 // 注释内,/* 和 */ 字符也没有特殊的含义。因此,您可以在一种注释内嵌套另一种注释。例如: + +``` + +/* 用于输出 Hello World 的注释 + +cout << "Hello World"; // 输出 Hello World + +*/ +``` + +# C++ 数据类型 + +使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。 + +您可能需要存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等)的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么。 + +## 基本的内置类型 + +C++ 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的 C++ 数据类型: + +| 类型 | 关键字 | +| :------- | :------ | +| 布尔型 | bool | +| 字符型 | char | +| 整型 | int | +| 浮点型 | float | +| 双浮点型 | double | +| 无类型 | void | +| 宽字符型 | wchar_t | + +一些基本类型可以使用一个或多个类型修饰符进行修饰: + +- signed +- unsigned +- short +- long + +下表显示了各种变量类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值。 + +| 类型 | 位 | 范围 | +| :----------------- | :------------ | :------------------------------------------------------ | +| char | 1 个字节 | -128 到 127 或者 0 到 255 | +| unsigned char | 1 个字节 | 0 到 255 | +| signed char | 1 个字节 | -128 到 127 | +| int | 4 个字节 | -2147483648 到 2147483647 | +| unsigned int | 4 个字节 | 0 到 4294967295 | +| signed int | 4 个字节 | -2147483648 到 2147483647 | +| short int | 2 个字节 | -32768 到 32767 | +| unsigned short int | 2 个字节 | 0 到 65,535 | +| signed short int | 2 个字节 | -32768 到 32767 | +| long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | +| signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | +| unsigned long int | 8 个字节 | 0 to 18,446,744,073,709,551,615 | +| float | 4 个字节 | +/- 3.4e +/- 38 (~7 个数字) | +| double | 8 个字节 | +/- 1.7e +/- 308 (~15 个数字) | +| long double | 8 个字节 | +/- 1.7e +/- 308 (~15 个数字) | +| wchar_t | 2 或 4 个字节 | 1 个宽字符 | + +从上表可得知,变量的大小会根据编译器和所使用的电脑而有所不同。 + +下面实例会输出您电脑上各种数据类型的大小。 + +```cpp +#include using namespace std;int main(){ + cout << "Size of char : " << sizeof(char) << endl; + cout << "Size of int : " << sizeof(int) << endl; + cout << "Size of short int : " << sizeof(short int) << endl; + cout << "Size of long int : " << sizeof(long int) << endl; + cout << "Size of float : " << sizeof(float) << endl; + cout << "Size of double : " << sizeof(double) << endl; + cout << "Size of wchar_t : " << sizeof(wchar_t) << endl; return 0; +} +``` + +本实例使用了 **endl**,这将在每一行后插入一个换行符,<< 运算符用于向屏幕传多个值。我们也使用 **sizeof()** 函数来获取各种数据类型的大小。 + +当上面的代码被编译和执行时,它会产生以下的结果,结果会根据所使用的计算机而有所不同: + +```cpp +Size of char : 1Size of int : 4Size of short int : 2Size of long int : 8Size of float : 4Size of double : 8Size of wchar_t : 4 +``` + +## typedef 声明 + +您可以使用 **typedef** 为一个已有的类型取一个新的名字。下面是使用 typedef 定义一个新类型的语法: + +```cpp +typedef type newname; +``` + +例如,下面的语句会告诉编译器,feet 是 int 的另一个名称: + +```cpp +typedef int feet; +``` + +现在,下面的声明是完全合法的,它创建了一个整型变量 distance: + +```cpp +feet distance; +``` + +## 枚举类型 + +枚举类型(enumeration)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。 + +如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。 + +创建枚举,需要使用关键字 **enum**。枚举类型的一般形式为: + +```cpp +enum enum-name { list of names } var-list; +``` + +在这里,enum-name 是枚举类型的名称。名称列表 { list of names } 是用逗号分隔的。 + +例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 "blue"。 + +```cpp +enum color { red, green, blue } c;c = blue; +``` + +默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,**green** 的值为 5。 + +```cpp +enum color { red, green=5, blue }; +``` + +在这里,**blue** 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1。 + +# C++ 变量类型 + +变量其实只不过是程序可操作的存储区的名称。C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。 + +变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。大写字母和小写字母是不同的,因为 C++ 是大小写敏感的。 + +基于前一章讲解的基本类型,有以下几种基本的变量类型,将在下一章中进行讲解: + +| 类型 | 描述 | +| :------ | :------------------------------------------------- | +| bool | 存储值 true 或 false。 | +| char | 通常是一个八位字节(一个字节)。这是一个整数类型。 | +| int | 对机器而言,整数的最自然的大小。 | +| float | 单精度浮点值。 | +| double | 双精度浮点值。 | +| void | 表示类型的缺失。 | +| wchar_t | 宽字符类型。 | + +C++ 也允许定义各种其他类型的变量,比如**枚举、指针、数组、引用、数据结构、类**等等,这将会在后续的章节中进行讲解。 + +下面我们将讲解如何定义、声明和使用各种类型的变量。 + +## C++ 中的变量定义 + +变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表,如下所示: + +```cpp +type variable_list; +``` + +在这里,**type** 必须是一个有效的 C++ 数据类型,可以是 char、wchar_t、int、float、double、bool 或任何用户自定义的对象,**variable_list** 可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。下面列出几个有效的声明: + +```cpp +int i, j, k;char c, ch;float f, salary;double d; +``` + +行 **int i, j, k;** 声明并定义了变量 i、j 和 k,这指示编译器创建类型为 int 的名为 i、j、k 的变量。 + +变量可以在声明的时候被初始化(指定一个初始值)。初始化器由一个等号,后跟一个常量表达式组成,如下所示: + +```cpp +type variable_name = value; +``` + +下面列举几个实例: + +```cpp +extern int d = 3, f = 5; + // d 和 f 的声明 int d = 3, f = 5; + // 定义并初始化 d 和 fbyte z = 22; + // 定义并初始化 zchar x = 'x'; + // 变量 x 的值为 'x' +``` + +不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。 + +## C++ 中的变量声明 + +变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。 + +当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 **extern** 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。 + +## 实例 + +尝试下面的实例,其中,变量在头部就已经被声明,但它们是在主函数内被定义和初始化的: + +```cpp +#include using namespace std;// 变量声明extern int a, b;extern int c;extern float f; + int main (){ + // 变量定义 + int a, b; + int c; + float f; + + // 实际初始化 + a = 10; + b = 20; + c = a + b; + + cout << c << endl ; + + f = 70.0/3.0; + cout << f << endl ; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +3023.3333 +``` + +同样的,在函数声明时,提供一个函数名,而函数的实际定义则可以在任何地方进行。例如: + +```cpp +// 函数声明int func();int main(){ + // 函数调用 + int i = func();}// 函数定义int func(){ + return 0;} +``` + +## C++ 中的左值(Lvalues)和右值(Rvalues) + +C++ 中有两种类型的表达式: + +- **左值(lvalue):**指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。 +- **右值(rvalue):**术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。 + +变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句: + +```cpp +int g = 20; +``` + +但是下面这个就不是一个有效的语句,会生成编译时错误: + +```cpp +10 = 20; +``` + +# C++ 变量作用域 + +作用域是程序的一个区域,一般来说有三个地方可以声明变量: + +- 在函数或一个代码块内部声明的变量,称为局部变量。 +- 在函数参数的定义中声明的变量,称为形式参数。 +- 在所有函数外部声明的变量,称为全局变量。 + +我们将在后续的章节中学习什么是函数和参数。本章我们先来讲解声明是局部变量和全局变量。 + +## 局部变量 + +在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。下面的实例使用了局部变量: + +```cpp +#include using namespace std; + int main (){ + // 局部变量声明 + int a, b; + int c; + + // 实际初始化 + a = 10; + b = 20; + c = a + b; + + cout << c; + + return 0;} +``` + +## 全局变量 + +在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。 + +全局变量可以被任何函数访问。也就是说,全局变量一旦声明,在整个程序中都是可用的。下面的实例使用了全局变量和局部变量: + +```cpp +#include using namespace std; + // 全局变量声明int g; + int main (){ + // 局部变量声明 + int a, b; + + // 实际初始化 + a = 10; + b = 20; + g = a + b; + + cout << g; + + return 0;} +``` + +在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。下面是一个实例: + +```cpp +#include using namespace std; + // 全局变量声明int g = 20; + int main (){ + // 局部变量声明 + int g = 10; + + cout << g; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +10 +``` + +## 初始化局部变量和全局变量 + +当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值: + +| 数据类型 | 初始化默认值 | +| :------- | :----------- | +| int | 0 | +| char | '\0' | +| float | 0 | +| double | 0 | +| pointer | NULL | + +正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果。 + +# C++ 常量 + +常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做**字面量**。 + +常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。 + +常量就像是常规的变量,只不过常量的值在定义后不能进行修改。 + +## 整数常量 + +整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。 + +整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。 + +下面列举几个整数常量的实例: + +```cpp +212 // 合法的215u // 合法的0xFeeL // 合法的078 // 非法的:8 不是八进制的数字032UU // 非法的:不能重复后缀 +``` + +以下是各种类型的整数常量的实例: + +```cpp +85 // 十进制0213 // 八进制 0x4b // 十六进制 30 // 整数 30u // 无符号整数 30l // 长整数 30ul // 无符号长整数 +``` + +## 浮点常量 + +浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。 + +当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。 + +下面列举几个浮点常量的实例: + +```cpp +3.14159 // 合法的 314159E-5L // 合法的 510E // 非法的:不完整的指数210f // 非法的:没有小数或指数.e55 // 非法的:缺少整数或分数 +``` + +## 布尔常量 + +布尔常量共有两个,它们都是标准的 C++ 关键字: + +- **true** 值代表真。 +- **false** 值代表假。 + +我们不应把 true 的值看成 1,把 false 的值看成 0。 + +## 字符常量 + +字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 **wchar_t** 类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 **char** 类型的简单变量中。 + +字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。 + +在 C++ 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码: + +| 转义序列 | 含义 | +| :--------- | :------------------------- | +| \\ | \ 字符 | +| \' | ' 字符 | +| \" | " 字符 | +| \? | ? 字符 | +| \a | 警报铃声 | +| \b | 退格键 | +| \f | 换页符 | +| \n | 换行符 | +| \r | 回车 | +| \t | 水平制表符 | +| \v | 垂直制表符 | +| \ooo | 一到三位的八进制数 | +| \xhh . . . | 一个或多个数字的十六进制数 | + +下面的实例显示了一些转义序列字符: + +```cpp +#include using namespace std;int main(){ cout << "Hello\tWorld\n\n"; return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Hello World +``` + +## 字符串常量 + +字符串字面值或常量是括在双引号 "" 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。 + +您可以使用空格做分隔符,把一个很长的字符串常量进行分行。 + +下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。 + +```cpp +"quot;hello, dear""hello, \dear""hello, " "d" "ear" +``` + +## 定义常量 + +在 C++ 中,有两种简单的定义常量的方式: + +- 使用 **#define** 预处理器。 +- 使用 **const** 关键字。 + +## #define 预处理器 + +下面是使用 #define 预处理器定义常量的形式: + +```cpp +#define identifier value +``` + +具体请看下面的实例: + +```cpp +#include using namespace std;#define LENGTH 10 #define WIDTH 5#define NEWLINE '\n'int main(){ + + int area; + + area = LENGTH * WIDTH; + cout << area; + cout << NEWLINE; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +50 +``` + +## const 关键字 + +您可以使用 **const** 前缀声明指定类型的常量,如下所示: + +```cpp +const type variable = value; +``` + +具体请看下面的实例: + +```cpp +#include using namespace std;int main(){ + const int LENGTH = 10; + const int WIDTH = 5; + const char NEWLINE = '\n'; + int area; + + area = LENGTH * WIDTH; + cout << area; + cout << NEWLINE; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +50 +``` + +请注意,把常量定义为大写字母形式,是一个很好的编程实践。 + +# C++ 修饰符类型 + +C++ 允许在 **char、int 和 double** 数据类型前放置修饰符。修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。 + +下面列出了数据类型修饰符: + +- signed +- unsigned +- long +- short + +修饰符 **signed、unsigned、long 和 short** 可应用于整型,**signed** 和 **unsigned** 可应用于字符型,**long** 可应用于双精度型。 + +修饰符 **signed** 和 **unsigned** 也可以作为 **long** 或 **short** 修饰符的前缀。例如:**unsigned long int**。 + +C++ 允许使用速记符号来声明**无符号短整数**或**无符号长整数**。您可以不写 int,只写单词 **unsigned、short** 或 **unsigned、long**,int 是隐含的。例如,下面的两个语句都声明了无符号整型变量。 + +```cpp +unsigned x; +unsigned int y; +``` + +为了理解 C++ 解释有符号整数和无符号整数修饰符之间的差别,我们来运行一下下面这个短程序: + +```cpp +#include +using namespace std; + +/* + * 这个程序演示了有符号整数和无符号整数之间的差别 +*/ +int main() +{ + short int i; // 有符号短整数 + short unsigned int j; // 无符号短整数 + + j = 50000; + + i = j; + cout << i << " " << j; + + return 0; +} +``` + +当上面的程序运行时,会输出下列结果: + +```cpp +-15536 50000 +``` + +上述结果中,无符号短整数 50,000 的位模式被解释为有符号短整数 -15,536。 + +## C++ 中的类型限定符 + +类型限定符提供了变量的额外信息。 + +| 限定符 | 含义 | +| :------- | :----------------------------------------------------------- | +| const | **const** 类型的对象在程序执行期间不能被修改改变。 | +| volatile | 修饰符 **volatile** 告诉编译器,变量的值可能以程序未明确指定的方式被改变。 | +| restrict | 由 **restrict** 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 | + +# C++ 存储类 + +存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类: + +- auto +- register +- static +- extern +- mutable +- thread_local (C++11) + +从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。 + +## auto 存储类 + +自 C++ 11 以来,**auto** 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。 + +C++98标准中auto关键字用于自动变量的声明,但由于使用极少且多余,在C++11中已删除这一用法。 + +根据初始化表达式自动推断被声明的变量的类型,如: + +auto f=3.14; //doubleauto s("hello"); //const char*auto z = new auto(9); // int*auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型 + +## register 存储类 + +**register** 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。 + +{ register int miles;} + +寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 'register' 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。 + +## static 存储类 + +**static** 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。 + +static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。 + +在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。 + +## 实例 + +```cpp +#include + // 函数声明 void func(void); +static int count = 10; /* 全局变量 */ + int main(){ + while(count--) + { + func(); } + return 0;}// 函数定义void func( void ){ + static int i = 5; // 局部静态变量 + i++; std::cout << "变量 i 为 " << i ; std::cout << " , 变量 count 为 " << count << std::endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +变量 i 为 6 , 变量 count 为 9变量 i 为 7 , 变量 count 为 8变量 i 为 8 , 变量 count 为 7变量 i 为 9 , 变量 count 为 6变量 i 为 10 , 变量 count 为 5变量 i 为 11 , 变量 count 为 4变量 i 为 12 , 变量 count 为 3变量 i 为 13 , 变量 count 为 2变量 i 为 14 , 变量 count 为 1变量 i 为 15 , 变量 count 为 0 +``` + +## extern 存储类 + +**extern** 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。 + +当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 *extern* 来得到已定义的变量或函数的引用。可以这么理解,*extern* 是用来在另一个文件中声明一个全局变量或函数。 + +extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示: + +第一个文件:main.cpp + +## 实例 + +```cpp +#include + int count ;extern void write_extern(); +int main(){ + count = 5; write_extern();} +``` + +第二个文件:support.cpp + +## 实例 + +```cpp +#include + extern int count; +void write_extern(void){ + std::cout << "Count is " << count << std::endl;} +``` + +在这里,第二个文件中的 *extern* 关键字用于声明已经在第一个文件 main.cpp 中定义的 count。现在 ,编译这两个文件,如下所示: + +```cpp +$ g++ main.cpp support.cpp -o write +``` + +这会产生 **write** 可执行程序,尝试执行 **write**,它会产生下列结果: + +```cpp +$ ./writeCount is 5 +``` + +## mutable 存储类 + +**mutable** 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。 + +## thread_local 存储类 + +使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。 + +thread_local 说明符可以与 static 或 extern 合并。 + +可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。 + +以下演示了可以被声明为 thread_local 的变量: + +```cpp +thread_local int x; // 命名空间下的全局变量class X{ + static thread_local std::string s; // 类的static成员变量};static thread_local std::string X::s; // X::s 是需要定义的 + void foo(){ + thread_local std::vector v; // 本地变量} +``` + +# C++ 运算符 + +运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符,并提供了以下类型的运算符: + +- 算术运算符 +- 关系运算符 +- 逻辑运算符 +- 位运算符 +- 赋值运算符 +- 杂项运算符 + +本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。 + +## 算术运算符 + +下表显示了 C++ 支持的算术运算符。 + +假设变量 A 的值为 10,变量 B 的值为 20,则: + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------------------- | :--------------- | +| + | 把两个操作数相加 | A + B 将得到 30 | +| - | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10 | +| * | 把两个操作数相乘 | A * B 将得到 200 | +| / | 分子除以分母 | B / A 将得到 2 | +| % | 取模运算符,整除后的余数 | B % A 将得到 0 | +| ++ | [自增运算符](https://edu.aliyun.com/cplusplus/cpp-increment-decrement-operators.html),整数值增加 1 | A++ 将得到 11 | +| -- | [自减运算符](https://edu.aliyun.com/cplusplus/cpp-increment-decrement-operators.html),整数值减少 1 | A-- 将得到 9 | + +### 实例 + +请看下面的实例,了解 C++ 中可用的算术运算符。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + int a = 21; int b = 10; int c ; + c = a + b; cout << "Line 1 - c 的值是 " << c << endl ; c = a - b; cout << "Line 2 - c 的值是 " << c << endl ; c = a * b; cout << "Line 3 - c 的值是 " << c << endl ; c = a / b; cout << "Line 4 - c 的值是 " << c << endl ; c = a % b; cout << "Line 5 - c 的值是 " << c << endl ; + int d = 10; // 测试自增、自减 + c = d++; cout << "Line 6 - c 的值是 " << c << endl ; + d = 10; // 重新赋值 + c = d--; cout << "Line 7 - c 的值是 " << c << endl ; return 0;} +``` + +当上面的代码被编译和执行时,它会产生以下结果: + +```cpp +Line 1 - c 的值是 31Line 2 - c 的值是 11Line 3 - c 的值是 210Line 4 - c 的值是 2Line 5 - c 的值是 1Line 6 - c 的值是 10Line 7 - c 的值是 10 +``` + +## 关系运算符 + +下表显示了 C++ 支持的关系运算符。 + +假设变量 A 的值为 10,变量 B 的值为 20,则: + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------------------- | :---------------- | +| == | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 不为真。 | +| != | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。 | +| > | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A > B) 不为真。 | +| < | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。 | +| >= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A >= B) 不为真。 | +| <= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。 | + +### 实例 + +请看下面的实例,了解 C++ 中可用的关系运算符。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + int a = 21; int b = 10; int c ; + if( a == b ) + { + cout << "Line 1 - a 等于 b" << endl ; } + else + { + cout << "Line 1 - a 不等于 b" << endl ; } + if ( a < b ) + { + cout << "Line 2 - a 小于 b" << endl ; } + else + { + cout << "Line 2 - a 不小于 b" << endl ; } + if ( a > b ) + { + cout << "Line 3 - a 大于 b" << endl ; } + else + { + cout << "Line 3 - a 不大于 b" << endl ; } + /* 改变 a 和 b 的值 */ + a = 5; b = 20; if ( a <= b ) + { + cout << "Line 4 - a 小于或等于 b" << endl ; } + if ( b >= a ) + { + cout << "Line 5 - b 大于或等于 a" << endl ; } + return 0;} +``` + +当上面的代码被编译和执行时,它会产生以下结果: + +```cpp +Line 1 - a 不等于 bLine 2 - a 不小于 bLine 3 - a 大于 bLine 4 - a 小于或等于 bLine 5 - b 大于或等于 a +``` + +## 逻辑运算符 + +下表显示了 C++ 支持的关系逻辑运算符。 + +假设变量 A 的值为 1,变量 B 的值为 0,则: + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------------------- | :---------------- | +| && | 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 | (A && B) 为假。 | +| \|\| | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 | (A \|\| B) 为真。 | +| ! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 | !(A && B) 为真。 | + +### 实例 + +请看下面的实例,了解 C++ 中可用的逻辑运算符。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + int a = 5; int b = 20; int c ; + if ( a && b ) + { + cout << "Line 1 - 条件为真"<< endl ; } + if ( a || b ) + { + cout << "Line 2 - 条件为真"<< endl ; } + /* 改变 a 和 b 的值 */ + a = 0; b = 10; if ( a && b ) + { + cout << "Line 3 - 条件为真"<< endl ; } + else + { + cout << "Line 4 - 条件不为真"<< endl ; } + if ( !(a && b) ) + { + cout << "Line 5 - 条件为真"<< endl ; } + return 0;} +``` + +当上面的代码被编译和执行时,它会产生以下结果: + +```cpp +Line 1 - 条件为真Line 2 - 条件为真Line 4 - 条件不为真Line 5 - 条件为真 +``` + +## 位运算符 + +位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示: + +| p | q | p & q | p \| q | p ^ q | +| :--- | :--- | :---- | :----- | :---- | +| 0 | 0 | 0 | 0 | 0 | +| 0 | 1 | 0 | 1 | 1 | +| 1 | 1 | 1 | 1 | 0 | +| 1 | 0 | 0 | 1 | 1 | + +假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示: + +A = 0011 1100 + +B = 0000 1101 + +\----------------- + +A&B = 0000 1100 + +A|B = 0011 1101 + +A^B = 0011 0001 + +~A = 1100 0011 + +下表显示了 C++ 支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则: + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| & | 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 | (A & B) 将得到 12,即为 0000 1100 | +| \| | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 | (A \| B) 将得到 61,即为 0011 1101 | +| ^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 | (A ^ B) 将得到 49,即为 0011 0001 | +| ~ | 二进制补码运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0。 | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 | +| << | 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 | A << 2 将得到 240,即为 1111 0000 | +| >> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 | A >> 2 将得到 15,即为 0000 1111 | + +### 实例 + +请看下面的实例,了解 C++ 中可用的位运算符。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + unsigned int a = 60; // 60 = 0011 1100 + unsigned int b = 13; // 13 = 0000 1101 + int c = 0; + + c = a & b; // 12 = 0000 1100 + cout << "Line 1 - c 的值是 " << c << endl ; + c = a | b; // 61 = 0011 1101 + cout << "Line 2 - c 的值是 " << c << endl ; + c = a ^ b; // 49 = 0011 0001 + cout << "Line 3 - c 的值是 " << c << endl ; + c = ~a; // -61 = 1100 0011 + cout << "Line 4 - c 的值是 " << c << endl ; + c = a << 2; // 240 = 1111 0000 + cout << "Line 5 - c 的值是 " << c << endl ; + c = a >> 2; // 15 = 0000 1111 + cout << "Line 6 - c 的值是 " << c << endl ; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生以下结果: + +``` + +Line 1 - c 的值是 12Line 2 - c 的值是 61Line 3 - c 的值是 49Line 4 - c 的值是 -61Line 5 - c 的值是 240Line 6 - c 的值是 15 +``` + +## 赋值运算符 + +下表列出了 C++ 支持的赋值运算符: + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------------------- | :------------------------------ | +| = | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C | +| += | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A | +| -= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A | +| *= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A | +| /= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A | +| %= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A | +| <<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2 | +| >>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2 | +| &= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2 | +| ^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2 | +| \|= | 按位或且赋值运算符 | C \|= 2 等同于 C = C \| 2 | + +### 实例 + +请看下面的实例,了解 C++ 中可用的赋值运算符。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + int a = 21; int c ; + c = a; cout << "Line 1 - = 运算符实例,c 的值 = : " <>= 2; cout << "Line 8 - >>= 运算符实例,c 的值 = : " <>= 运算符实例,c 的值 = 11Line 9 - &= 运算符实例,c 的值 = 2Line 10 - ^= 运算符实例,c 的值 = 0Line 11 - |= 运算符实例,c 的值 = 2 +``` + +## 杂项运算符 + +下表列出了 C++ 支持的其他一些重要的运算符。 + +| 运算符 | 描述 | +| :------------------- | :----------------------------------------------------------- | +| sizeof | [sizeof 运算符](https://edu.aliyun.com/cplusplus/cpp-sizeof-operator.html)返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。 | +| Condition ? X : Y | [条件运算符](https://edu.aliyun.com/cplusplus/cpp-conditional-operator.html)。如果 Condition 为真 ? 则值为 X : 否则值为 Y。 | +| , | [逗号运算符](https://edu.aliyun.com/cplusplus/cpp-comma-operator.html)会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。 | +| .(点)和 ->(箭头) | [成员运算符](https://edu.aliyun.com/cplusplus/cpp-member-operators.html)用于引用类、结构和共用体的成员。 | +| Cast | [强制转换运算符](https://edu.aliyun.com/cplusplus/cpp-casting-operators.html)把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。 | +| & | [指针运算符 &](https://edu.aliyun.com/cplusplus/cpp-pointer-operators.html) 返回变量的地址。例如 &a; 将给出变量的实际地址。 | +| * | [指针运算符 *](https://edu.aliyun.com/cplusplus/cpp-pointer-operators.html) 指向一个变量。例如,*var; 将指向变量 var。 | + +## C++ 中的运算符优先级 + +运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。 + +例如 x = 7 + 3 * 2,在这里,x 被赋值为 13,而不是 20,因为运算符 * 具有比 + 更高的优先级,所以首先计算乘法 3*2,然后再加上 7。 + +下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。 + +| 类别 | 运算符 | 结合性 | +| :--------- | :-------------------------------- | :------- | +| 后缀 | () [] -> . ++ - - | 从左到右 | +| 一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 | +| 乘除 | * / % | 从左到右 | +| 加减 | + - | 从左到右 | +| 移位 | << >> | 从左到右 | +| 关系 | < <= > >= | 从左到右 | +| 相等 | == != | 从左到右 | +| 位与 AND | & | 从左到右 | +| 位异或 XOR | ^ | 从左到右 | +| 位或 OR | \| | 从左到右 | +| 逻辑与 AND | && | 从左到右 | +| 逻辑或 OR | \|\| | 从左到右 | +| 条件 | ?: | 从右到左 | +| 赋值 | = += -= *= /= %=>>= <<= &= ^= \|= | 从右到左 | +| 逗号 | , | 从左到右 | + +### 实例 + +请看下面的实例,了解 C++ 中运算符的优先级。 + +复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。 + +对比有括号和没有括号时的区别,这将产生不同的结果。因为 ()、 /、 * 和 + 有不同的优先级,高优先级的操作符将优先计算。 + +## 实例 + +```cpp +#include using namespace std; +int main(){ + int a = 20; int b = 10; int c = 15; int d = 5; int e; + e = (a + b) * c / d; // ( 30 * 15 ) / 5 + cout << "(a + b) * c / d 的值是 " << e << endl ; + e = ((a + b) * c) / d; // (30 * 15 ) / 5 + cout << "((a + b) * c) / d 的值是 " << e << endl ; + e = (a + b) * (c / d); // (30) * (15/5) + cout << "(a + b) * (c / d) 的值是 " << e << endl ; + e = a + (b * c) / d; // 20 + (150/5) + cout << "a + (b * c) / d 的值是 " << e << endl ; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生以下结果: + +```cpp +(a + b) * c / d 的值是 90((a + b) * c) / d 的值是 90(a + b) * (c / d) 的值是 90a + (b * c) / d 的值是 50 +``` + +# C++ 循环 + +有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 + +编程语言提供了允许更为复杂的执行路径的多种控制结构。 + +循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的一般形式: + +![img](https://edu.aliyun.com/files/course/2017/09-24/16103394c44b631388.png) + +## 循环类型 + +C++ 编程语言提供了以下几种循环类型。点击链接查看每个类型的细节。 + +| 循环类型 | 描述 | +| :-------------- | :----------------------------------------------------------- | +| while 循环 | 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 | +| for 循环 | 多次执行一个语句序列,简化管理循环变量的代码。 | +| do...while 循环 | 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。 | +| 嵌套循环 | 您可以在 while、for 或 do..while 循环内使用一个或多个循环。 | + + + +## 循环控制语句 + +循环控制语句更改执行的正常序列。当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁。 + +C++ 提供了下列的控制语句。点击链接查看每个语句的细节。 + +| 控制语句 | 描述 | +| :------------ | :----------------------------------------------------------- | +| break 语句 | 终止 **loop** 或 **switch** 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。 | +| continue 语句 | 引起循环跳过主体的剩余部分,立即重新开始测试条件。 | +| goto 语句 | 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。 | + + + +## 无限循环 + +如果条件永远不为假,则循环将变成无限循环。**for** 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。 + +```cpp +#include using namespace std; + int main (){ + + for( ; ; ) + { + printf("This loop will run forever.\n"); + } + + return 0;} +``` + +当条件表达式不存在时,它被假设为真。您也可以设置一个初始值和增量表达式,但是一般情况下,C++ 程序员偏向于使用 for(;;) 结构来表示一个无限循环。 + +**注意:**您可以按 Ctrl + C 键终止一个无限循环。 + +# C++ 判断 + +判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。 + +下面是大多数编程语言中典型的判断结构的一般形式: + +![img](https://edu.aliyun.com/files/course/2017/09-24/161155bc605b784844.png) + +## 判断语句 + +C++ 编程语言提供了以下类型的判断语句。点击链接查看每个语句的细节。 + +| 语句 | 描述 | +| :--------------- | :----------------------------------------------------------- | +| if 语句 | 一个 **if 语句** 由一个布尔表达式后跟一个或多个语句组成。 | +| if...else 语句 | 一个 **if 语句** 后可跟一个可选的 **else 语句**,else 语句在布尔表达式为假时执行。 | +| 嵌套 if 语句 | 您可以在一个 **if** 或 **else if** 语句内使用另一个 **if** 或 **else if** 语句。 | +| switch 语句 | 一个 **switch** 语句允许测试一个变量等于多个值时的情况。 | +| 嵌套 switch 语句 | 您可以在一个 **switch** 语句内使用另一个 **switch** 语句。 | + + + +## ? : 运算符 + +我们已经在前面的章节中讲解了 [**条件运算符 ? :**](https://edu.aliyun.com/cplusplus/cpp-conditional-operator.html),可以用来替代 **if...else** 语句。它的一般形式如下: + +```cpp +Exp1 ? Exp2 : Exp3; +``` + +其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置。 + +? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值。 + +# C++ 函数 + +函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数,即主函数 **main()** ,所有简单的程序都可以定义其他额外的函数。 + +您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。 + +函数**声明**告诉编译器函数的名称、返回类型和参数。函数**定义**提供了函数的实际主体。 + +C++ 标准库提供了大量的程序可以调用的内置函数。例如,函数 **strcat()** 用来连接两个字符串,函数 **memcpy()** 用来复制内存到另一个位置。 + +函数还有很多叫法,比如方法、子例程或程序,等等。 + +## 定义函数 + +C++ 中的函数定义的一般形式如下: + +```cpp +return_type function_name( parameter list ){ body of the function} +``` + +在 C++ 中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分: + +- **返回类型:**一个函数可以返回一个值。**return_type** 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 **void**。 +- **函数名称:**这是函数的实际名称。函数名和参数列表一起构成了函数签名。 +- **参数:**参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。 +- **函数主体:**函数主体包含一组定义函数执行任务的语句。 + +## 实例 + +以下是 **max()** 函数的源代码。该函数有两个参数 num1 和 num2,会返回这两个数中较大的那个数: + +```cpp +// 函数返回两个数中较大的那个数 int max(int num1, int num2) { // 局部变量声明 int result; if (num1 > num2) result = num1; else result = num2; return result; } +``` + +## 函数声明 + +函数**声明**会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。 + +函数声明包括以下几个部分: + +```cpp +return_type function_name( parameter list ); +``` + +针对上面定义的函数 max(),以下是函数声明: + +```cpp +int max(int num1, int num2); +``` + +在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明: + +```cpp +int max(int, int); +``` + +当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。 + +## 调用函数 + +创建 C++ 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。 + +当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。 + +调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。例如: + +```cpp +#include using namespace std; + // 函数声明int max(int num1, int num2); + int main (){ + // 局部变量声明 + int a = 100; + int b = 200; + int ret; + + // 调用函数来获取最大值 + ret = max(a, b); + + cout << "Max value is : " << ret << endl; + + return 0;} + // 函数返回两个数中较大的那个数int max(int num1, int num2) { + // 局部变量声明 + int result; + + if (num1 > num2) + result = num1; + else + result = num2; + + return result; } +``` + +把 max() 函数和 main() 函数放一块,编译源代码。当运行最后的可执行文件时,会产生下列结果: + +```cpp +Max value is : 200 +``` + +## 函数参数 + +如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的**形式参数**。 + +形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。 + +当调用函数时,有两种向函数传递参数的方式: + +| 调用类型 | 描述 | +| :----------------------------------------------------------- | :----------------------------------------------------------- | +| [传值调用](https://edu.aliyun.com/cplusplus/cpp-function-call-by-value.html) | 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 | +| [指针调用](https://edu.aliyun.com/cplusplus/cpp-function-call-by-pointer.html) | 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 | +| [引用调用](https://edu.aliyun.com/cplusplus/cpp-function-call-by-reference.html) | 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 | + +默认情况下,C++ 使用**传值调用**来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的参数。之前提到的实例,调用 max() 函数时,使用了相同的方法。 + +## 参数的默认值 + +当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。 + +这是通过在函数定义中使用赋值运算符来为参数赋值的。调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值。请看下面的实例: + +```cpp +#include using namespace std; + int sum(int a, int b=20){ + int result; + + result = a + b; + + return (result);}int main (){ + // 局部变量声明 + int a = 100; + int b = 200; + int result; + + // 调用函数来添加值 + result = sum(a, b); + cout << "Total value is :" << result << endl; + + // 再次调用函数 + result = sum(a); + cout << "Total value is :" << result << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Total value is :300Total value is :120 +``` + +------ + +## Lambda 函数与表达式 + +C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。 + +Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。 + +Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下: + +```cpp +[capture](parameters)->return-type{body} +``` + +例如: + +``` + +[](int x, int y){ return x < y ; } +``` + +如果没有参数可以表示为: + +```cpp +[capture](parameters){body} +``` + +例如: + +```cpp +[]{ ++global_x; } +``` + +在一个更为复杂的例子中,返回类型可以被明确的指定如下: + +```cpp +[](int x, int y) -> int { int z = x + y; return z + x; } +``` + +本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。 + +如果 lambda 函数没有传回值(例如 void),其回返类型可被完全忽略。 + +在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定: + +```cpp +[] // 沒有定义任何变量。使用未定义变量会引发错误。[x, &y] // x以传值方式传入(默认),y以引用方式传入。[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。 +``` + +另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入: + +```cpp +[this]() { this->someFunc(); }(); +``` + +# C++ 数字 + +通常,当我们需要用到数字时,我们会使用原始的数据类型,如 int、short、long、float 和 double 等等。这些用于数字的数据类型,其可能的值和数值范围,我们已经在 C++ 数据类型一章中讨论过。 + +## C++ 定义数字 + +我们已经在之前章节的各种实例中定义过数字。下面是一个 C++ 中定义各种类型数字的综合实例: + +```cpp +#include using namespace std; + int main (){ + // 数字定义 + short s; + int i; + long l; + float f; + double d; + + // 数字赋值 + s = 10; + i = 1000; + l = 1000000; + f = 230.47; + d = 30949.374; + + // 数字输出 + cout << "short s :" << s << endl; + cout << "int i :" << i << endl; + cout << "long l :" << l << endl; + cout << "float f :" << f << endl; + cout << "double d :" << d << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +short s :10int i :1000long l :1000000float f :230.47double d :30949.4 +``` + +## C++ 数学运算 + +在 C++ 中,除了可以创建各种函数,还包含了各种有用的函数供您使用。这些函数写在标准 C 和 C++ 库中,叫做**内置**函数。您可以在程序中引用这些函数。 + +C++ 内置了丰富的数学函数,可对各种数字进行运算。下表列出了 C++ 中一些有用的内置的数学函数。 + +为了利用这些函数,您需要引用数学头文件 ****。 + +| 序号 | 函数 & 描述 | +| :--- | :----------------------------------------------------------- | +| 1 | **double cos(double);** 该函数返回弧度角(double 型)的余弦。 | +| 2 | **double sin(double);** 该函数返回弧度角(double 型)的正弦。 | +| 3 | **double tan(double);** 该函数返回弧度角(double 型)的正切。 | +| 4 | **double log(double);** 该函数返回参数的自然对数。 | +| 5 | **double pow(double, double);** 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 | +| 6 | **double hypot(double, double);** 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 | +| 7 | **double sqrt(double);** 该函数返回参数的平方根。 | +| 8 | **int abs(int);** 该函数返回整数的绝对值。 | +| 9 | **double fabs(double);** 该函数返回任意一个十进制数的绝对值。 | +| 10 | **double floor(double);** 该函数返回一个小于或等于传入参数的最大整数。 | + +下面是一个关于数学运算的简单实例: + +```cpp +#include #include using namespace std; + int main (){ + // 数字定义 + short s = 10; + int i = -1000; + long l = 100000; + float f = 230.47; + double d = 200.374; + + // 数学运算 + cout << "sin(d) :" << sin(d) << endl; + cout << "abs(i) :" << abs(i) << endl; + cout << "floor(d) :" << floor(d) << endl; + cout << "sqrt(f) :" << sqrt(f) << endl; + cout << "pow( d, 2) :" << pow(d, 2) << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +sign(d) :-0.634939abs(i) :1000floor(d) :200sqrt(f) :15.1812pow( d, 2 ) :40149.7 +``` + +## C++ 随机数 + +在许多情况下,需要生成随机数。关于随机数生成器,有两个相关的函数。一个是 **rand()**,该函数只返回一个伪随机数。生成随机数之前必须先调用 **srand()** 函数。 + +下面是一个关于生成随机数的简单实例。实例中使用了 **time()** 函数来获取系统时间的秒数,通过调用 rand() 函数来生成随机数: + +```cpp +#include #include #include using namespace std; + int main (){ + int i,j; + + // 设置种子 + srand( (unsigned)time( NULL ) ); + + /* 生成 10 个随机数 */ + for( i = 0; i < 10; i++ ) + { + // 生成实际的随机数 + j= rand(); + cout <<"随机数: " << j << endl; + } + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +随机数: 1748144778随机数: 630873888随机数: 2134540646随机数: 219404170随机数: 902129458随机数: 920445370随机数: 1319072661随机数: 257938873随机数: 1256201101随机数: 580322989 +``` + +# C++ 数组 + +C++ 支持**数组**数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。 + +数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。 + +所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。 + +## 声明数组 + +在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示: + +```cpp +type arrayName [ arraySize ]; +``` + +这叫做一维数组。**arraySize** 必须是一个大于零的整数常量,**type** 可以是任意有效的 C++ 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 **balance**,声明语句如下: + +```cpp +double balance[10]; +``` + +现在 *balance* 是一个可用的数组,可以容纳 10 个类型为 double 的数字。 + +## 初始化数组 + +在 C++ 中,您可以逐个初始化数组,也可以使用一个初始化语句,如下所示: + +```cpp +double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0}; +``` + +大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。 + +如果您省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果: + +```cpp +double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0}; +``` + +您将创建一个数组,它与前一个实例中所创建的数组是完全相同的。下面是一个为数组中某个元素赋值的实例: + +```cpp +balance[4] = 50.0; +``` + +上述的语句把数组中第五个元素的值赋为 50.0。所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1。以下是上面所讨论的数组的的图形表示: + +![数组表示](https://edu.aliyun.com/ueditor/php/upload/image/20170504/1493862028514037.jpg) + +## 访问数组元素 + +数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如: + +```cpp +double salary = balance[9]; +``` + +上面的语句将把数组中第 10 个元素的值赋给 salary 变量。下面的实例使用了上述的三个概念,即,声明数组、数组赋值、访问数组: + +```cpp +#include using namespace std; + #include using std::setw; + int main (){ + int n[ 10 ]; // n 是一个包含 10 个整数的数组 + + // 初始化数组元素 + for ( int i = 0; i < 10; i++ ) + { + n[ i ] = i + 100; // 设置元素 i 为 i + 100 + } + cout << "Element" << setw( 13 ) << "Value" << endl; + + // 输出数组中每个元素的值 + for ( int j = 0; j < 10; j++ ) + { + cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl; + } + + return 0;} +``` + +上面的程序使用了 **setw()** 函数来格式化输出。当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Element Value + 0 100 + 1 101 + 2 102 + 3 103 + 4 104 + 5 105 + 6 106 + 7 107 + 8 108 + 9 109 +``` + +## C++ 中数组详解 + +在 C++ 中,数组是非常重要的,我们需要了解更多有关数组的细节。下面列出了 C++ 程序员必须清楚的一些与数组相关的重要概念: + +| 概念 | 描述 | +| :------------- | :----------------------------------------------------------- | +| 多维数组 | C++ 支持多维数组。多维数组最简单的形式是二维数组。 | +| 指向数组的指针 | 您可以通过指定不带索引的数组名称来生成一个指向数组中第一个元素的指针。 | +| 传递数组给函数 | 您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 | +| 从函数返回数组 | C++ 允许从函数返回数组。 | + +# C++ 字符串 + +C++ 提供了以下两种类型的字符串表示形式: + +- C 风格字符串 +- C++ 引入的 string 类类型 + +## C 风格字符串 + +C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 **null** 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。 + +下面的声明和初始化创建了一个 "Hello" 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。 + +```cpp +char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; +``` + +依据数组初始化规则,您可以把上面的语句写成以下语句: + +```cpp +char greeting[] = "Hello"; +``` + +以下是 C/C++ 中定义的字符串的内存表示: + +![img](https://edu.aliyun.com/files/course/2017/09-24/1618593bac57374932.jpg) + +其实,您不需要把 *null* 字符放在字符串常量的末尾。C++ 编译器会在初始化数组时,自动把 '\0' 放在字符串的末尾。让我们尝试输出上面的字符串: + +## 实例 + +```cpp +#include + using namespace std; +int main (){ + char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; + cout << "Greeting message: "; cout << greeting << endl; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Greeting message: Hello +``` + +C++ 中有大量的函数用来操作以 null 结尾的字符串:supports a wide range of functions that manipulate null-terminated strings: + +| 序号 | 函数 & 目的 | +| :--- | :----------------------------------------------------------- | +| 1 | **strcpy(s1, s2);** 复制字符串 s2 到字符串 s1。 | +| 2 | **strcat(s1, s2);** 连接字符串 s2 到字符串 s1 的末尾。 | +| 3 | **strlen(s1);** 返回字符串 s1 的长度。 | +| 4 | **strcmp(s1, s2);** 如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回大于 0。 | +| 5 | **strchr(s1, ch);** 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 | +| 6 | **strstr(s1, s2);** 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 | + +下面的实例使用了上述的一些函数: + +## 实例 + +```cpp +#include #include + using namespace std; +int main (){ + char str1[11] = "Hello"; char str2[11] = "World"; char str3[11]; int len ; + // 复制 str1 到 str3 + strcpy( str3, str1); cout << "strcpy( str3, str1) : " << str3 << endl; + // 连接 str1 和 str2 + strcat( str1, str2); cout << "strcat( str1, str2): " << str1 << endl; + // 连接后,str1 的总长度 + len = strlen(str1); cout << "strlen(str1) : " << len << endl; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +strcpy( str3, str1) : Hellostrcat( str1, str2): HelloWorldstrlen(str1) : 10 +``` + +## C++ 中的 String 类 + +C++ 标准库提供了 **string** 类类型,支持上述所有的操作,另外还增加了其他更多的功能。我们将学习 C++ 标准库中的这个类,现在让我们先来看看下面这个实例: + +现在您可能还无法透彻地理解这个实例,因为到目前为止我们还没有讨论类和对象。所以现在您可以只是粗略地看下这个实例,等理解了面向对象的概念之后再回头来理解这个实例。 + +## 实例 + +```cpp +#include #include + using namespace std; +int main (){ + string str1 = "Hello"; string str2 = "World"; string str3; int len ; + // 复制 str1 到 str3 + str3 = str1; cout << "str3 : " << str3 << endl; + // 连接 str1 和 str2 + str3 = str1 + str2; cout << "str1 + str2 : " << str3 << endl; + // 连接后,str3 的总长度 + len = str3.size(); cout << "str3.size() : " << len << endl; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +str3 : Hellostr1 + str2 : HelloWorldstr3.size() : 10 +``` + +# C++ 指针 + +学习 C++ 的指针既简单又有趣。通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C++ 程序员,学习指针是很有必要的。 + +正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址: + +```cpp +#include using namespace std;int main (){ + int var1; + char var2[10]; + + cout << "var1 变量的地址: "; + cout << &var1 << endl; + + cout << "var2 变量的地址: "; + cout << &var2 << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +var1 变量的地址: 0xbfebd5c0var2 变量的地址: 0xbfebd5b6 +``` + +通过上面的实例,我们了解了什么是内存地址以及如何访问它。接下来让我们看看什么是指针。 + +## 什么是指针? + +**指针**是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为: + +```cpp +type *var-name; +``` + +在这里,**type** 是指针的基类型,它必须是一个有效的 C++ 数据类型,**var-name** 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明: + +```cpp +int *ip; /* 一个整型的指针 */double *dp; /* 一个 double 型的指针 */float *fp; /* 一个浮点型的指针 */char *ch; /* 一个字符型的指针 */ +``` + +所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。 + +## C++ 中使用指针 + +使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作: + +```cpp +#include using namespace std;int main (){ + int var = 20; // 实际变量的声明 + int *ip; // 指针变量的声明 + + ip = &var; // 在指针变量中存储 var 的地址 + + cout << "Value of var variable: "; + cout << var << endl; + + // 输出在指针变量中存储的地址 + cout << "Address stored in ip variable: "; + cout << ip << endl; + + // 访问指针中地址的值 + cout << "Value of *ip variable: "; + cout << *ip << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Value of var variable: 20Address stored in ip variable: 0xbfc601acValue of *ip variable: 20 +``` + +## C++ 指针详解 + +在 C++ 中,有很多指针相关的概念,这些概念都很简单,但是都很重要。下面列出了 C++ 程序员必须清楚的一些与指针相关的重要概念: + +| 概念 | 描述 | +| :----------------- | :----------------------------------------------------------- | +| C++ Null 指针 | C++ 支持空指针。NULL 指针是一个定义在标准库中的值为零的常量。 | +| C++ 指针的算术运算 | 可以对指针进行四种算术运算:++、--、+、- | +| C++ 指针 vs 数组 | 指针和数组之间有着密切的关系。 | +| C++ 指针数组 | 可以定义用来存储指针的数组。 | +| C++ 指向指针的指针 | C++ 允许指向指针的指针。 | +| C++ 传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。 | +| C++ 从函数返回指针 | C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。 | + +# C++ 引用 + +引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。 + +## C++ 引用 vs 指针 + +引用很容易与指针混淆,它们之间有三个主要的不同: + +- 不存在空引用。引用必须连接到一块合法的内存。 +- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。 +- 引用必须在创建时被初始化。指针可以在任何时间被初始化。 + +## C++ 中创建引用 + +试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或引用来访问变量的内容。例如: + +```cpp +int i = 17; +``` + +我们可以为 i 声明引用变量,如下所示: + +```cpp +int& r = i; +``` + +在这些声明中,& 读作**引用**。因此,第一个声明可以读作 "r 是一个初始化为 i 的整型引用",第二个声明可以读作 "s 是一个初始化为 d 的 double 型引用"。下面的实例使用了 int 和 double 引用: + +```cpp +#include + using namespace std; + int main (){ + // 声明简单的变量 + int i; + double d; + + // 声明引用变量 + int& r = i; + double& s = d; + + i = 5; + cout << "Value of i : " << i << endl; + cout << "Value of i reference : " << r << endl; + + d = 11.7; + cout << "Value of d : " << d << endl; + cout << "Value of d reference : " << s << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Value of i : 5Value of i reference : 5Value of d : 11.7Value of d reference : 11.7 +``` + +引用通常用于函数参数列表和函数返回值。下面列出了 C++ 程序员必须清楚的两个与 C++ 引用相关的重要概念: + +| 概念 | 描述 | +| :--------------- | :------------------------------------------------------- | +| 把引用作为参数 | C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。 | +| 把引用作为返回值 | 可以从 C++ 函数中返回引用,就像返回其他数据类型一样。 | + +# C++ 日期 & 时间 + +C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 头文件。 + +有四个与时间相关的类型:**clock_t、time_t、size_t** 和 **tm**。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。 + +结构类型 **tm** 把日期和时间以 C 结构的形式保存,tm 结构的定义如下: + +```cpp +struct tm { + int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61 + int tm_min; // 分,范围从 0 到 59 + int tm_hour; // 小时,范围从 0 到 23 + int tm_mday; // 一月中的第几天,范围从 1 到 31 + int tm_mon; // 月,范围从 0 到 11 + int tm_year; // 自 1900 年起的年数 + int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起 + int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起 + int tm_isdst; // 夏令时} +``` + +下面是 C/C++ 中关于日期和时间的重要函数。所有这些函数都是 C/C++ 标准库的组成部分,您可以在 C++ 标准库中查看一下各个函数的细节。 + +| 序号 | 函数 & 描述 | +| :--- | :----------------------------------------------------------- | +| 1 | **time_t time(time_t \*time);** 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 .1。 | +| 2 | **char \*ctime(const time_t \*time);** 该返回一个表示当地时间的字符串指针,字符串形式 *day month year hours:minutes:seconds year\n\0*。 | +| 3 | **struct tm \*localtime(const time_t \*time);** 该函数返回一个指向表示本地时间的 **tm** 结构的指针。 | +| 4 | **clock_t clock(void);** 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。 | +| 5 | **char \* asctime ( const struct tm \* time );** 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。 | +| 6 | **struct tm \*gmtime(const time_t \*time);** 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 | +| 7 | **time_t mktime(struct tm \*time);** 该函数返回日历时间,相当于 time 所指向结构中存储的时间。 | +| 8 | **double difftime ( time_t time2, time_t time1 );** 该函数返回 time1 和 time2 之间相差的秒数。 | +| 9 | **size_t strftime();** 该函数可用于格式化日期和时间为指定的格式。 | + +## 当前日期和时间 + +下面的实例获取当前系统的日期和时间,包括本地时间和协调世界时(UTC)。 + +```cpp +#include #include using namespace std;int main( ){ + // 基于当前系统的当前日期/时间 + time_t now = time(0); + + // 把 now 转换为字符串形式 + char* dt = ctime(&now); + + cout << "本地日期和时间:" << dt << endl; + + // 把 now 转换为 tm 结构 + tm *gmtm = gmtime(&now); + dt = asctime(gmtm); + cout << "UTC 日期和时间:"<< dt << endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +本地日期和时间:Sat Jan 8 20:07:41 2011UTC 日期和时间:Sun Jan 9 03:07:41 2011 +``` + +## 使用结构 tm 格式化时间 + +**tm** 结构在 C/C++ 中处理日期和时间相关的操作时,显得尤为重要。tm 结构以 C 结构的形式保存日期和时间。大多数与时间相关的函数都使用了 tm 结构。下面的实例使用了 tm 结构和各种与日期和时间相关的函数。 + +在练习使用结构之前,需要对 C 结构有基本的了解,并懂得如何使用箭头 -> 运算符来访问结构成员。 + +```cpp +#include #include using namespace std;int main( ){ + // 基于当前系统的当前日期/时间 + time_t now = time(0); + + cout << "Number of sec since January 1,1970:" << now << endl; + + tm *ltm = localtime(&now); + + // 输出 tm 结构的各个组成部分 + cout << "Year: "<< 1900 + ltm->tm_year << endl; + cout << "Month: "<< 1 + ltm->tm_mon<< endl; + cout << "Day: "<< ltm->tm_mday << endl; + cout << "Time: "<< 1 + ltm->tm_hour << ":"; + cout << 1 + ltm->tm_min << ":"; + cout << 1 + ltm->tm_sec << endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Number of sec since January 1, 1970:1294548238Year: 2011Month: 1Day: 8Time: 22: 44:59 +``` + +# C++ 基本的输入输出 + +C++ 标准库提供了一组丰富的输入/输出功能,我们将在后续的章节进行介绍。本章将讨论 C++ 编程中最基本和最常见的 I/O 操作。 + +C++ 的 I/O 发生在流中,流是字节序列。如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做**输入操作**。如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做**输出操作**。 + +## I/O 库头文件 + +下列的头文件在 C++ 编程中很重要。 + +| 头文件 | 函数和描述 | +| :--------- | :----------------------------------------------------------- | +| | 该文件定义了 **cin、cout、cerr** 和 **clog** 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 | +| | 该文件通过所谓的参数化的流操纵器(比如 **setw** 和 **setprecision**),来声明对执行标准化 I/O 有用的服务。 | +| | 该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 | + +## 标准输出流(cout) + +预定义的对象 **cout** 是 **ostream** 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。**cout** 是与流插入运算符 << 结合使用的,如下所示: + +```cpp +#include using namespace std; int main( ){ char str[] = "Hello C++"; cout << "Value of str is : " << str << endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Value of str is : Hello C++ +``` + +C++ 编译器根据要输出变量的数据类型,选择合适的流插入运算符来显示值。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。 + +流插入运算符 << 在一个语句中可以多次使用,如上面实例中所示,**endl** 用于在行末添加一个换行符。 + +## 标准输入流(cin) + +预定义的对象 **cin** 是 **istream** 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。**cin** 是与流提取运算符 >> 结合使用的,如下所示: + +```cpp +#include using namespace std; int main( ){ + char name[50]; cout << "请输入您的名称: "; + cin >> name; cout << "您的名称是: " << name << endl; } +``` + +当上面的代码被编译和执行时,它会提示用户输入名称。当用户输入一个值,并按回车键,就会看到下列结果: + +```cpp +请输入您的名称: cplusplus您的名称是: cplusplus +``` + +C++ 编译器根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中。 + +流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句: + +```cpp +cin >> name >> age; +``` + +这相当于下面两个语句: + +```cpp +cin >> name;cin >> age; +``` + +## 标准错误流(cerr) + +预定义的对象 **cerr** 是 **ostream** 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 **cerr** 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。 + +**cerr** 也是与流插入运算符 << 结合使用的,如下所示: + +```cpp +#include + using namespace std; + int main( ){ + char str[] = "Unable to read...."; + + cerr << "Error message : " << str << endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Error message : Unable to read.... +``` + +## 标准日志流(clog) + +预定义的对象 **clog** 是 **ostream** 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 **clog** 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。 + +**clog** 也是与流插入运算符 << 结合使用的,如下所示: + +```cpp +#include + using namespace std; + int main( ){ + char str[] = "Unable to read...."; + + clog << "Error message : " << str << endl;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Error message : Unable to read.... +``` + +通过这些小实例,我们无法区分 cout、cerr 和 clog 的差异,但在编写和执行大型程序时,它们之间的差异就变得非常明显。所以良好的编程实践告诉我们,使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。 + +# C++ 数据结构 + +C/C++ 数组允许定义可存储相同类型数据项的变量,但是**结构**是 C++ 中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。 + +结构用于表示一条记录,假设您想要跟踪图书馆中书本的动态,您可能需要跟踪每本书的下列属性: + +- Title :标题 +- Author :作者 +- Subject :类目 +- Book ID :书的 ID + +## 定义结构 + +为了定义结构,您必须使用 **struct** 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下: + + + +```cpp +struct type_name {member_type1 member_name1;member_type2 member_name2;member_type3 member_name3;..} object_names; +``` + +**type_name** 是结构体类型的名称,**member_type1 member_name1** 是标准的变量定义,比如 **int i;** 或者 **float f;** 或者其他有效的变量定义。在结构定义的末尾,最后一个分号之前,您可以指定一个或多个结构变量,这是可选的。下面是声明一个结构体类型 **Books**,变量为 **book**: + +```cpp +struct Books{ char title[50]; char author[50]; char subject[100]; int book_id;} book; +``` + +## 访问结构成员 + +为了访问结构的成员,我们使用**成员访问运算符(.)**。成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号。 + +下面的实例演示了结构的用法: + +## 实例 + +```cpp +#include #include + using namespace std; +// 声明一个结构体类型 Books struct Books{ + char title[50]; char author[50]; char subject[100]; int book_id;}; +int main( ){ + Books Book1; // 定义结构体类型 Books 的变量 Book1 + Books Book2; // 定义结构体类型 Books 的变量 Book2 + + // Book1 详述 + strcpy( Book1.title, "C++ 教程"); strcpy( Book1.author, "Runoob"); + strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; + // Book2 详述 + strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; + // 输出 Book1 信息 + cout << "第一本书标题 : " << Book1.title <#include + using namespace std;void printBook( struct Books book ); +// 声明一个结构体类型 Books struct Books{ + char title[50]; char author[50]; char subject[100]; int book_id;}; +int main( ){ + Books Book1; // 定义结构体类型 Books 的变量 Book1 + Books Book2; // 定义结构体类型 Books 的变量 Book2 + + // Book1 详述 + strcpy( Book1.title, "C++ 教程"); strcpy( Book1.author, "Runoob"); + strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; + // Book2 详述 + strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); + strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; + // 输出 Book1 信息 + printBook( Book1 ); + // 输出 Book2 信息 + printBook( Book2 ); + return 0;}void printBook( struct Books book ){ + cout << "书标题 : " << book.title < 运算符,如下所示: + +```cpp +struct_pointer->title; +``` + +让我们使用结构指针来重写上面的实例,这将有助于您理解结构指针的概念: + +## 实例 + +```cpp +#include #include + using namespace std;void printBook( struct Books *book ); +struct Books{ + char title[50]; char author[50]; char subject[100]; int book_id;}; +int main( ){ + Books Book1; // 定义结构体类型 Books 的变量 Book1 + Books Book2; // 定义结构体类型 Books 的变量 Book2 + + // Book1 详述 + strcpy( Book1.title, "C++ 教程"); strcpy( Book1.author, "Runoob"); + strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; + // Book2 详述 + strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); + strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; + // 通过传 Book1 的地址来输出 Book1 信息 + printBook( &Book1 ); + // 通过传 Book2 的地址来输出 Book2 信息 + printBook( &Book2 ); + return 0;}// 该函数以结构指针作为参数void printBook( struct Books *book ){ + cout << "书标题 : " << book->title <author <subject <book_id <using namespace std;class Box{ + public: + double length; // 长度 + double breadth; // 宽度 + double height; // 高度};int main( ){ + Box Box1; // 声明 Box1,类型为 Box + Box Box2; // 声明 Box2,类型为 Box + double volume = 0.0; // 用于存储体积 + + // box 1 详述 + Box1.height = 5.0; + Box1.length = 6.0; + Box1.breadth = 7.0; + + // box 2 详述 + Box2.height = 10.0; + Box2.length = 12.0; + Box2.breadth = 13.0; + + // box 1 的体积 + volume = Box1.height * Box1.length * Box1.breadth; + cout << "Box1 的体积:" << volume < + using namespace std;// 基类class Shape { + public: + void setWidth(int w) + { + width = w; + } + void setHeight(int h) + { + height = h; + } + protected: + int width; + int height;};// 派生类class Rectangle: public Shape{ + public: + int getArea() + { + return (width * height); + }};int main(void){ + Rectangle Rect; + + Rect.setWidth(5); + Rect.setHeight(7); + + // 输出对象的面积 + cout << "Total area: " << Rect.getArea() << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +``` + Total area: 35 +``` + +## 访问控制和继承 + +派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。 + +我们可以根据访问权限总结出不同的访问类型,如下所示: + +| 访问 | public | protected | private | +| :------- | :----- | :-------- | :------ | +| 同一个类 | yes | yes | yes | +| 派生类 | yes | yes | no | +| 外部的类 | yes | no | no | + +一个派生类继承了所有的基类方法,但下列情况除外: + +- 基类的构造函数、析构函数和拷贝构造函数。 +- 基类的重载运算符。 +- 基类的友元函数。 + +## 继承类型 + +当一个类派生自基类,该基类可以被继承为 **public、protected** 或 **private** 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。 + +我们几乎不使用 **protected** 或 **private** 继承,通常使用 **public** 继承。当使用不同类型的继承时,遵循以下几个规则: + +- **公有继承(public):**当一个类派生自**公有**基类时,基类的**公有**成员也是派生类的**公有**成员,基类的**保护**成员也是派生类的**保护**成员,基类的**私有**成员不能直接被派生类访问,但是可以通过调用基类的**公有**和**保护**成员来访问。 +- **保护继承(protected):** 当一个类派生自**保护**基类时,基类的**公有**和**保护**成员将成为派生类的**保护**成员。 +- **私有继承(private):**当一个类派生自**私有**基类时,基类的**公有**和**保护**成员将成为派生类的**私有**成员。 + +## 多继承 + +多继承即一个子类可以有多个父类,它继承了多个父类的特性。 + +C++ 类可以从多个类继承成员,语法如下: + +```cpp +class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…{<派生类类体>}; +``` + +其中,访问修饰符继承方式是 **public、protected** 或 **private** 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例: + +```cpp +#include + using namespace std;// 基类 Shapeclass Shape { + public: + void setWidth(int w) + { + width = w; + } + void setHeight(int h) + { + height = h; + } + protected: + int width; + int height;};// 基类 PaintCostclass PaintCost { + public: + int getCost(int area) + { + return area * 70; + }};// 派生类class Rectangle: public Shape, public PaintCost{ + public: + int getArea() + { + return (width * height); + }};int main(void){ + Rectangle Rect; + int area; + + Rect.setWidth(5); + Rect.setHeight(7); + + area = Rect.getArea(); + + // 输出对象的面积 + cout << "Total area: " << Rect.getArea() << endl; + + // 输出总花费 + cout << "Total paint cost: $" << Rect.getCost(area) << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Total area: 35Total paint cost: $2450 +``` + +# C++ 重载运算符和重载函数 + +C++ 允许在同一作用域中的某个**函数**和**运算符**指定多个定义,分别称为**函数重载**和**运算符重载**。 + +重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。 + +当您调用一个**重载函数**或**重载运算符**时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为**重载决策**。 + +## C++ 中的函数重载 + +在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。 + +下面的实例中,同名函数 **print()** 被用于输出不同的数据类型: + +```cpp +#include using namespace std; + class printData +{ + public: + void print(int i) { + cout << "Printing int: " << i << endl; + } + + void print(double f) { + cout << "Printing float: " << f << endl; + } + + void print(char* c) { + cout << "Printing character: " << c << endl; + }};int main(void){ + printData pd; + + // Call print to print integer + pd.print(5); + // Call print to print float + pd.print(500.263); + // Call print to print character + pd.print("Hello C++"); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Printing int: 5Printing float: 500.263Printing character: Hello C++ +``` + +## C++ 中的运算符重载 + +您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。 + +重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。 + +```cpp +Box operator+(const Box&); +``` + +声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示: + +```cpp +Box operator+(const Box&, const Box&); +``` + +下面的实例使用成员函数演示了运算符重载的概念。在这里,对象作为参数进行传递,对象的属性使用 **this** 运算符进行访问,如下所示: + +```cpp +#include using namespace std;class Box{ + public: + + double getVolume(void) + { + return length * breadth * height; + } + void setLength( double len ) + { + length = len; + } + + void setBreadth( double bre ) + { + breadth = bre; + } + + void setHeight( double hei ) + { + height = hei; + } + // 重载 + 运算符,用于把两个 Box 对象相加 + Box operator+(const Box& b) + { + Box box; + box.length = this->length + b.length; + box.breadth = this->breadth + b.breadth; + box.height = this->height + b.height; + return box; + } + private: + double length; // 长度 + double breadth; // 宽度 + double height; // 高度};// 程序的主函数int main( ){ + Box Box1; // 声明 Box1,类型为 Box + Box Box2; // 声明 Box2,类型为 Box + Box Box3; // 声明 Box3,类型为 Box + double volume = 0.0; // 把体积存储在该变量中 + + // Box1 详述 + Box1.setLength(6.0); + Box1.setBreadth(7.0); + Box1.setHeight(5.0); + + // Box2 详述 + Box2.setLength(12.0); + Box2.setBreadth(13.0); + Box2.setHeight(10.0); + + // Box1 的体积 + volume = Box1.getVolume(); + cout << "Volume of Box1 : " << volume < | <= | >= | ++ | -- | +| << | >> | == | != | && | \|\| | +| += | -= | /= | %= | ^= | &= | +| \|= | *= | <<= | >>= | [] | () | +| -> | ->* | new | new [] | delete | delete [] | + +下面是不可重载的运算符列表: + +| :: | .* | . | ?: | +| ---- | ---- | ---- | ---- | +| | | | | + +## 运算符重载实例 + +下面提供了各种运算符重载的实例,帮助您更好地理解重载的概念。 + +| 序号 | 运算符和实例 | +| :--- | :----------------------- | +| 1 | 一元运算符重载 | +| 2 | 二元运算符重载 | +| 3 | 关系运算符重载 | +| 4 | 输入/输出运算符重载 | +| 5 | ++ 和 -- 运算符重载 | +| 6 | 赋值运算符重载 | +| 7 | 函数调用运算符 () 重载 | +| 8 | 下标运算符 [] 重载 | +| 9 | 类成员访问运算符 -> 重载 | + +# C++ 多态 + +**多态**按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 + +C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。 + +下面的实例中,基类 Shape 被派生为两个类,如下所示: + +```cpp +#include using namespace std; + class Shape { + protected: + int width, height; + public: + Shape( int a=0, int b=0) + { + width = a; + height = b; + } + int area() + { + cout << "Parent class area :" <area(); + + // 存储三角形的地址 + shape = &tri; + // 调用三角形的求面积函数 area + shape->area(); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Parent class areaParent class area +``` + +导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的**静态多态**,或**静态链接** - 函数调用在程序执行前就准备好了。有时候这也被称为**早绑定**,因为 area() 函数在程序编译期间就已经设置好了。 + +但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 **virtual**,如下所示: + +```cpp +class Shape { + protected: + int width, height; + public: + Shape( int a=0, int b=0) + { + width = a; + height = b; + } + virtual int area() + { + cout << "Parent class area :" <using namespace std;int main( ){ cout << "Hello C++" <using namespace std;class Adder{ + public: + // 构造函数 + Adder(int i = 0) + { + total = i; + } + // 对外的接口 + void addNum(int number) + { + total += number; + } + // 对外的接口 + int getTotal() + { + return total; + }; + private: + // 对外隐藏的数据 + int total;};int main( ){ + Adder a; + + a.addNum(10); + a.addNum(20); + a.addNum(30); + + cout << "Total " << a.getTotal() <using namespace std;class Adder{ + public: + // 构造函数 + Adder(int i = 0) + { + total = i; + } + // 对外的接口 + void addNum(int number) + { + total += number; + } + // 对外的接口 + int getTotal() + { + return total; + }; + private: + // 对外隐藏的数据 + int total;};int main( ){ + Adder a; + + a.addNum(10); + a.addNum(20); + a.addNum(30); + + cout << "Total " << a.getTotal() < + using namespace std; + // 基类class Shape {public: + // 提供接口框架的纯虚函数 + virtual int getArea() = 0; + void setWidth(int w) + { + width = w; + } + void setHeight(int h) + { + height = h; + }protected: + int width; + int height;}; + // 派生类class Rectangle: public Shape{public: + int getArea() + { + return (width * height); + }};class Triangle: public Shape{public: + int getArea() + { + return (width * height)/2; + }}; + int main(void){ + Rectangle Rect; + Triangle Tri; + + Rect.setWidth(5); + Rect.setHeight(7); + // 输出对象的面积 + cout << "Total Rectangle area: " << Rect.getArea() << endl; + + Tri.setWidth(5); + Tri.setHeight(7); + // 输出对象的面积 + cout << "Total Triangle area: " << Tri.getArea() << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Total Rectangle area: 35Total Triangle area: 17 +``` + +从上面的实例中,我们可以看到一个抽象类是如何定义一个接口 getArea(),两个派生类是如何通过不同的计算面积的算法来实现这个相同的函数。 + +## 设计策略 + +面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。 + +外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。这些纯虚函数在相应的派生类中被实现。 + +这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。 + +# C++ 文件和流 + +到目前为止,我们已经使用了 **iostream** 标准库,它提供了 **cin** 和 **cout** 方法分别用于从标准输入读取流和向标准输出写入流。 + +本教程介绍如何从文件读取流和向文件写入流。这就需要用到 C++ 中另一个标准库 **fstream**,它定义了三个新的数据类型: + +| 数据类型 | 描述 | +| :------- | :----------------------------------------------------------- | +| ofstream | 该数据类型表示输出文件流,用于创建文件并向文件写入信息。 | +| ifstream | 该数据类型表示输入文件流,用于从文件读取信息。 | +| fstream | 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。 | + +要在 C++ 中进行文件处理,必须在 C++ 源代码文件中包含头文件 。 + +## 打开文件 + +在从文件读取信息或者向文件写入信息之前,必须先打开文件。**ofstream** 和 **fstream** 对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用 **ifstream** 对象。 + +下面是 open() 函数的标准语法,open() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。 + +```cpp +void open(const char *filename, ios::openmode mode); +``` + +在这里,**open()** 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。 + +| 模式标志 | 描述 | +| :--------- | :----------------------------------------------------------- | +| ios::app | 追加模式。所有写入都追加到文件末尾。 | +| ios::ate | 文件打开后定位到文件末尾。 | +| ios::in | 打开文件用于读取。 | +| ios::out | 打开文件用于写入。 | +| ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 | + +您可以把以上两种或两种以上的模式结合使用。例如,如果您想要以写入模式打开文件,并希望截断文件,以防文件已存在,那么您可以使用下面的语法: + +```cpp +ofstream outfile;outfile.open("file.dat", ios::out | ios::trunc ); +``` + +类似地,您如果想要打开一个文件用于读写,可以使用下面的语法: + +```cpp +fstream afile;afile.open("file.dat", ios::out | ios::in ); +``` + +## 关闭文件 + +当 C++ 程序终止时,它会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件。但程序员应该养成一个好习惯,在程序终止前关闭所有打开的文件。 + +下面是 close() 函数的标准语法,close() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。 + +```cpp +void close(); +``` + +## 写入文件 + +在 C++ 编程中,我们使用流插入运算符( << )向文件写入信息,就像使用该运算符输出信息到屏幕上一样。唯一不同的是,在这里您使用的是 **ofstream** 或 **fstream** 对象,而不是 **cout** 对象。 + +## 读取文件 + +在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里您使用的是 **ifstream** 或 **fstream** 对象,而不是 **cin** 对象。 + +## 读取 & 写入实例 + +下面的 C++ 程序以读写模式打开一个文件。在向文件 afile.dat 写入用户输入的信息之后,程序从文件读取信息,并将其输出到屏幕上: + +```cpp +#include #include using namespace std; + int main (){ + + char data[100]; + + // 以写模式打开文件 + ofstream outfile; + outfile.open("afile.dat"); + + cout << "Writing to the file" << endl; + cout << "Enter your name: "; + cin.getline(data, 100); + + // 向文件写入用户输入的数据 + outfile << data << endl; + + cout << "Enter your age: "; + cin >> data; + cin.ignore(); + + // 再次向文件写入用户输入的数据 + outfile << data << endl; + + // 关闭打开的文件 + outfile.close(); + + // 以读模式打开文件 + ifstream infile; + infile.open("afile.dat"); + + cout << "Reading from the file" << endl; + infile >> data; + + // 在屏幕上写入数据 + cout << data << endl; + + // 再次从文件读取数据,并显示它 + infile >> data; + cout << data << endl; + + // 关闭打开的文件 + infile.close(); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列输入和输出: + +```cpp +$./a.outWriting to the fileEnter your name: ZaraEnter your age: 9Reading from the fileZara9 +``` + +上面的实例中使用了 cin 对象的附加函数,比如 getline()函数从外部读取一行,ignore() 函数会忽略掉之前读语句留下的多余字符。 + +## 文件位置指针 + +**istream** 和 **ostream** 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 **seekg**("seek get")和关于 ostream 的 **seekp**("seek put")。 + +seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 **ios::beg**(默认的,从流的开头开始定位),也可以是 **ios::cur**(从流的当前位置开始定位),也可以是 **ios::end**(从流的末尾开始定位)。 + +文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。下面是关于定位 "get" 文件位置指针的实例: + +```cpp +// 定位到 fileObject 的第 n 个字节(假设是 ios::beg)fileObject.seekg( n );// 把文件的读指针从 fileObject 当前位置向后移 n 个字节fileObject.seekg( n, ios::cur );// 把文件的读指针从 fileObject 末尾往回移 n 个字节fileObject.seekg( n, ios::end );// 定位到 fileObject 的末尾fileObject.seekg( 0, ios::end ); +``` + +# C++ 异常处理 + +异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。 + +异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:**try、catch、throw**。 + +- **throw:** 当问题出现时,程序会抛出一个异常。这是通过使用 **throw** 关键字来完成的。 +- **catch:** 在您想要处理问题的地方,通过异常处理程序捕获异常。**catch** 关键字用于捕获异常。 +- **try:** **try** 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。 + +如果有一个块抛出一个异常,捕获异常的方法会使用 **try** 和 **catch** 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示: + +```cpp +try{ + // 保护代码}catch( ExceptionName e1 ){ + // catch 块}catch( ExceptionName e2 ){ + // catch 块}catch( ExceptionName eN ){ + // catch 块} +``` + +如果 **try** 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 **catch** 语句,用于捕获不同类型的异常。 + +## 抛出异常 + +您可以使用 **throw** 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。 + +以下是尝试除以零时抛出异常的实例: + +```cpp +double division(int a, int b){ + if( b == 0 ) + { + throw "Division by zero condition!"; + } + return (a/b);} +``` + +## 捕获异常 + +**catch** 块跟在 **try** 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。 + +```cpp +try{ + // 保护代码}catch( ExceptionName e ){ + // 处理 ExceptionName 异常的代码} +``` + +上面的代码会捕获一个类型为 **ExceptionName** 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 ...,如下所示: + +```cpp +try{ + // 保护代码}catch(...){ + // 能处理任何异常的代码} +``` + +下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。 + +```cpp +#include using namespace std;double division(int a, int b){ + if( b == 0 ) + { + throw "Division by zero condition!"; + } + return (a/b);}int main (){ + int x = 50; + int y = 0; + double z = 0; + + try { + z = division(x, y); + cout << z << endl; + }catch (const char* msg) { + cerr << msg << endl; + } + + return 0;} +``` + +由于我们抛出了一个类型为 **const char\*** 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Division by zero condition! +``` + +## C++ 标准的异常 + +C++ 提供了一系列标准的异常,定义在 **** 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示: + +![img](https://edu.aliyun.com/files/course/2017/09-24/1641382aba76354408.jpg) + +下表是对上面层次结构中出现的每个异常的说明: + +| 异常 | 描述 | +| :--------------------- | :----------------------------------------------------------- | +| **std::exception** | 该异常是所有标准 C++ 异常的父类。 | +| std::bad_alloc | 该异常可以通过 **new** 抛出。 | +| std::bad_cast | 该异常可以通过 **dynamic_cast** 抛出。 | +| std::bad_exception | 这在处理 C++ 程序中无法预期的异常时非常有用。 | +| std::bad_typeid | 该异常可以通过 **typeid** 抛出。 | +| **std::logic_error** | 理论上可以通过读取代码来检测到的异常。 | +| std::domain_error | 当使用了一个无效的数学域时,会抛出该异常。 | +| std::invalid_argument | 当使用了无效的参数时,会抛出该异常。 | +| std::length_error | 当创建了太长的 std::string 时,会抛出该异常。 | +| std::out_of_range | 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator[]()。 | +| **std::runtime_error** | 理论上不可以通过读取代码来检测到的异常。 | +| std::overflow_error | 当发生数学上溢时,会抛出该异常。 | +| std::range_error | 当尝试存储超出范围的值时,会抛出该异常。 | +| std::underflow_error | 当发生数学下溢时,会抛出该异常。 | + +## 定义新的异常 + +您可以通过继承和重载 **exception** 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常: + +```cpp +#include #include using namespace std;struct MyException : public exception{ + const char * what () const throw () + { + return "C++ Exception"; + }}; + int main(){ + try + { + throw MyException(); + } + catch(MyException& e) + { + std::cout << "MyException caught" << std::endl; + std::cout << e.what() << std::endl; + } + catch(std::exception& e) + { + //其他的错误 + }} +``` + +这将产生以下结果: + +```cpp +MyException caught +C++ Exception +``` + +在这里,**what()** 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。 + +# C++ 动态内存 + +了解动态内存在 C++ 中是如何工作的是成为一名合格的 C++ 程序员必不可少的。C++ 程序中的内存分为两个部分: + +- **栈:**在函数内部声明的所有变量都将占用栈内存。 +- **堆:**这是程序中未使用的内存,在程序运行时可用于动态分配内存。 + +很多时候,您无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。 + +在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 **new** 运算符。 + +如果您不需要动态分配内存,可以使用 **delete** 运算符,删除之前由 new 运算符分配的内存。 + +## new 和 delete 运算符 + +下面是使用 new 运算符来为任意的数据类型动态分配内存的通用语法: + +```cpp +new data-type; +``` + +在这里,**data-type** 可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型。让我们先来看下内置的数据类型。例如,我们可以定义一个指向 double 类型的指针,然后请求内存,该内存在执行时被分配。我们可以按照下面的语句使用 **new** 运算符来完成这点: + +```cpp +double* pvalue = NULL; // 初始化为 null 的指针pvalue = new double; // 为变量请求内存 +``` + +如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作: + +```cpp +double* pvalue = NULL;if( !(pvalue = new double )){ + cout << "Error: out of memory." <using namespace std;int main (){ + double* pvalue = NULL; // 初始化为 null 的指针 + pvalue = new double; // 为变量请求内存 + + *pvalue = 29494.99; // 在分配的地址存储值 + cout << "Value of pvalue : " << *pvalue << endl; + + delete pvalue; // 释放内存 + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Value of pvalue : 29495 +``` + +## 数组的动态内存分配 + +假设我们要为一个字符数组(一个有 20 个字符的字符串)分配内存,我们可以使用上面实例中的语法来为数组动态地分配内存,如下所示: + +```cpp +char* pvalue = NULL; // 初始化为 null 的指针pvalue = new char[20]; // 为变量请求内存 +``` + +要删除我们刚才创建的数组,语句如下: + +```cpp +delete [] pvalue; // 删除 pvalue 所指向的数组 +``` + +下面是 new 操作符的通用语法,可以为多维数组分配内存,如下所示: + +```cpp +int ROW = 2;int COL = 3;double **pvalue = new double* [ROW]; // 为行分配内存// 为列分配内存for(int i = 0; i < COL; i++) { + pvalue[i] = new double[COL];} +``` + +释放多维数组内存: + +```cpp +for(int i = 0; i < COL; i++) { + delete[] pvalue[i];}delete [] pvalue; +``` + +## 对象的动态内存分配 + +对象与简单的数据类型没有什么不同。例如,请看下面的代码,我们将使用一个对象数组来理清这一概念: + +```cpp +#include using namespace std;class Box{ + public: + Box() { + cout << "调用构造函数!" <using namespace std;// 第一个命名空间namespace first_space{ + void func(){ + cout << "Inside first_space" << endl; + }}// 第二个命名空间namespace second_space{ + void func(){ + cout << "Inside second_space" << endl; + }}int main (){ + + // 调用第一个命名空间中的函数 + first_space::func(); + + // 调用第二个命名空间中的函数 + second_space::func(); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Inside first_spaceInside second_space +``` + +## using 指令 + +您可以使用 **using namespace** 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。 + +```cpp +#include using namespace std;// 第一个命名空间namespace first_space{ + void func(){ + cout << "Inside first_space" << endl; + }}// 第二个命名空间namespace second_space{ + void func(){ + cout << "Inside second_space" << endl; + }}using namespace first_space;int main (){ + + // 调用第一个命名空间中的函数 + func(); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Inside first_space +``` + +using 指令也可以用来指定命名空间中的特定项目。例如,如果您只打算使用 std 命名空间中的 cout 部分,您可以使用如下的语句: + +```cpp +using std::cout; +``` + +随后的代码中,在使用 cout 时就可以不用加上命名空间名称作为前缀,但是 **std** 命名空间中的其他项目仍然需要加上命名空间名称作为前缀,如下所示: + +```cpp +#include using std::cout;int main (){ + cout << "std::endl is used with std!" << std::endl; + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +std::endl is used with std! +``` + +**using** 指令引入的名称遵循正常的范围规则。名称从使用 **using** 指令开始是可见的,直到该范围结束。此时,在范围以外定义的同名实体是隐藏的。 + +## 不连续的命名空间 + +命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。 + +所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素: + +```cpp +namespace namespace_name { + // 代码声明} +``` + +## 嵌套的命名空间 + +命名空间可以嵌套,您可以在一个命名空间中定义另一个命名空间,如下所示: + +```cpp +namespace namespace_name1 { + // 代码声明 + namespace namespace_name2 { + // 代码声明 + }} +``` + +您可以通过使用 :: 运算符来访问嵌套的命名空间中的成员: + +```cpp +// 访问 namespace_name2 中的成员using namespace namespace_name1::namespace_name2;// 访问 namespace:name1 中的成员using namespace namespace_name1; +``` + +在上面的语句中,如果使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示: + +```cpp +#include using namespace std;// 第一个命名空间namespace first_space{ + void func(){ + cout << "Inside first_space" << endl; + } + // 第二个命名空间 + namespace second_space{ + void func(){ + cout << "Inside second_space" << endl; + } + }}using namespace first_space::second_space;int main (){ + + // 调用第二个命名空间中的函数 + func(); + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Inside second_space +``` + +# C++ 模板 + +模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。 + +模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。 + +每个容器都有一个单一的定义,比如 **向量**,我们可以定义许多不同类型的向量,比如 **vector ** 或 **vector **。 + +您可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。 + +## 函数模板 + +模板函数定义的一般形式如下所示: + +```cpp +template ret-type func-name(parameter list){ // 函数的主体} +``` + +在这里,type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。 + +下面是函数模板的实例,返回两个数种的最大值: + +```cpp +#include #include using namespace std;template inline T const& Max (T const& a, T const& b) { + return a < b ? b:a; } int main (){ + + int i = 39; + int j = 20; + cout << "Max(i, j): " << Max(i, j) << endl; + + double f1 = 13.5; + double f2 = 20.7; + cout << "Max(f1, f2): " << Max(f1, f2) << endl; + + string s1 = "Hello"; + string s2 = "World"; + cout << "Max(s1, s2): " << Max(s1, s2) << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Max(i, j): 39Max(f1, f2): 20.7Max(s1, s2): World +``` + +## 类模板 + +正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示: + +```cpp +template class class-name {...} +``` + +在这里,**type** 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。 + +下面的实例定义了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操作: + +```cpp +#include #include #include #include #include using namespace std;template class Stack { + private: + vector elems; // 元素 + + public: + void push(T const&); // 入栈 + void pop(); // 出栈 + T top() const; // 返回栈顶元素 + bool empty() const{ // 如果为空则返回真。 + return elems.empty(); + } }; template void Stack::push (T const& elem) { + // 追加传入元素的副本 + elems.push_back(elem); } template void Stack::pop () { + if (elems.empty()) { + throw out_of_range("Stack<>::pop(): empty stack"); + } + // 删除最后一个元素 + elems.pop_back(); } template T Stack::top () const { + if (elems.empty()) { + throw out_of_range("Stack<>::top(): empty stack"); + } + // 返回最后一个元素的副本 + return elems.back(); } int main() { + try { + Stack intStack; // int 类型的栈 + Stack stringStack; // string 类型的栈 + + // 操作 int 类型的栈 + intStack.push(7); + cout << intStack.top() <::pop(): empty stack +``` + +# C++ 预处理器 + +预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。 + +所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。 + +我们已经看到,之前所有的实例中都有 **#include** 指令。这个宏用于把头文件包含到源文件中。 + +C++ 还支持很多预处理指令,比如 #include、#define、#if、#else、#line 等,让我们一起看看这些重要指令。 + +## #define 预处理 + +\#define 预处理指令用于创建符号常量。该符号常量通常称为**宏**,指令的一般形式是: + +```cpp +#define macro-name replacement-text +``` + +当这一行代码出现在一个文件中时,在该文件中后续出现的所有宏都将会在程序编译之前被替换为 replacement-text。例如: + +```cpp +#include using namespace std;#define PI 3.14159int main (){ cout << "Value of PI :" << PI << endl; return 0;} +``` + +现在,让我们测试这段代码,看看预处理的结果。假设源代码文件已经存在,接下来使用 -E 选项进行编译,并把结果重定向到 test.p。现在,如果您查看 test.p 文件,将会看到它已经包含大量的信息,而且在文件底部的值被改为如下: + +```cpp +$gcc -E test.cpp > test.p...int main (){ cout << "Value of PI :" << 3.14159 << endl; return 0;} +``` + +## 函数宏 + +您可以使用 #define 来定义一个带有参数的宏,如下所示: + +```cpp +#include using namespace std;#define MIN(a,b) (ausing namespace std;#define MKSTR( x ) #xint main (){ cout << MKSTR(HELLO C++) << endl; return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +HELLO C++ +``` + +让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行: + +```cpp +cout << MKSTR(HELLO C++) << endl; +``` + +转换成了: + +```cpp +cout << "HELLO C++" << endl; +``` + +\## 运算符用于连接两个令牌。下面是一个实例: + +```cpp +#define CONCAT( x, y ) x ## y +``` + +当 CONCAT 出现在程序中时,它的参数会被连接起来,并用来取代宏。例如,程序中 CONCAT(HELLO, C++) 会被替换为 "HELLO C++",如下面实例所示。 + +```cpp +#include using namespace std;#define concat(a, b) a ## bint main(){ int xy = 100; cout << concat(x, y); return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +100 +``` + +让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行: + +```cpp +cout << concat(x, y); +``` + +转换成了: + +```cpp +cout << xy; +``` + +## C++ 中的预定义宏 + +C++ 提供了下表所示的一些预定义宏: + +| 宏 | 描述 | +| :------- | :----------------------------------------------------------- | +| __LINE__ | 这会在程序编译时包含当前行号。 | +| __FILE__ | 这会在程序编译时包含当前文件名。 | +| __DATE__ | 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。 | +| __TIME__ | 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 | + +让我们看看上述这些宏的实例: + +```cpp +#include using namespace std;int main (){ + cout << "Value of __LINE__ : " << __LINE__ << endl; + cout << "Value of __FILE__ : " << __FILE__ << endl; + cout << "Value of __DATE__ : " << __DATE__ << endl; + cout << "Value of __TIME__ : " << __TIME__ << endl; + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Value of __LINE__ : 6Value of __FILE__ : test.cppValue of __DATE__ : Feb 28 2011Value of __TIME__ : 18:52:48 +``` + +# C++ 信号处理 + +信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。 + +有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 中。 + +| 信号 | 描述 | +| :------ | :------------------------------------------- | +| SIGABRT | 程序的异常终止,如调用 **abort**。 | +| SIGFPE | 错误的算术运算,比如除以零或导致溢出的操作。 | +| SIGILL | 检测非法指令。 | +| SIGINT | 接收到交互注意信号。 | +| SIGSEGV | 非法访问内存。 | +| SIGTERM | 发送到程序的终止请求。 | + +## signal() 函数 + +C++ 信号处理库提供了 **signal** 函数,用来捕获突发事件。以下是 signal() 函数的语法: + +```cpp +void (*signal (int sig, void (*func)(int)))(int); +``` + +这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。 + +让我们编写一个简单的 C++ 程序,使用 signal() 函数捕获 SIGINT 信号。不管您想在程序中捕获什么信号,您都必须使用 **signal** 函数来注册信号,并将其与信号处理程序相关联。看看下面的实例: + +```cpp +#include #include using namespace std;void signalHandler( int signum ){ + cout << "Interrupt signal (" << signum << ") received.\n"; + + // 清理并关闭 + // 终止程序 + + exit(signum); }int main (){ + // 注册信号 SIGINT 和信号处理程序 + signal(SIGINT, signalHandler); + + while(1){ + cout << "Going to sleep...." << endl; + sleep(1); + } + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +Going to sleep....Going to sleep....Going to sleep.... +``` + +现在,按 Ctrl+C 来中断程序,您会看到程序捕获信号,程序打印如下内容并退出: + +```cpp +Going to sleep....Going to sleep....Going to sleep....Interrupt signal (2) received. +``` + +## raise() 函数 + +您可以使用函数 **raise()** 生成信号,该函数带有一个整数信号编号作为参数,语法如下: + +```cpp +int raise (signal sig); +``` + +在这里,**sig** 是要发送的信号的编号,这些信号包括:SIGINT、SIGABRT、SIGFPE、SIGILL、SIGSEGV、SIGTERM、SIGHUP。以下是我们使用 raise() 函数内部生成信号的实例: + +```cpp +#include #include using namespace std;void signalHandler( int signum ){ + cout << "Interrupt signal (" << signum << ") received.\n"; + + // 清理并关闭 + // 终止程序 + + exit(signum); }int main (){ + int i = 0; + // 注册信号 SIGINT 和信号处理程序 + signal(SIGINT, signalHandler); + + while(++i){ + cout << "Going to sleep...." << endl; + if( i == 3 ){ + raise( SIGINT); + } + sleep(1); + } + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果,并会自动退出: + +```cpp +Going to sleep....Going to sleep....Going to sleep....Interrupt signal (2) received. +``` + +# C++ 多线程 + +多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:**基于进程和基于线程**。 + +- 基于进程的多任务处理是程序的并发执行。 +- 基于线程的多任务处理是同一程序的片段的并发执行。 + +多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。 + +本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。 + +## 创建线程 + +下面的程序,我们可以用它来创建一个 POSIX 线程: + +```cpp +#include pthread_create (thread, attr, start_routine, arg) +``` + +在这里,**pthread_create** 创建一个新的线程,并让它可执行。下面是关于参数的说明: + +| 参数 | 描述 | +| :------------ | :----------------------------------------------------------- | +| thread | 指向线程标识符指针。 | +| attr | 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。 | +| start_routine | 线程运行函数起始地址,一旦线程被创建就会执行。 | +| arg | 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。 | + +创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。 + +## 终止线程 + +使用下面的程序,我们可以用它来终止一个 POSIX 线程: + +```cpp +#include pthread_exit (status) +``` + +在这里,**pthread_exit** 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。 + +如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。 + +## 实例 + +以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,每个线程输出"Hello Runoob!": + +```cpp +#include // 必须的头文件是#include using namespace std;#define NUM_THREADS 5// 线程的运行函数void* say_hello(void* args){ + cout << "Hello Runoob!" << endl;}int main(){ + // 定义线程的 id 变量,多个变量使用数组 + pthread_t tids[NUM_THREADS]; + for(int i = 0; i < NUM_THREADS; ++i) + { + //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 + int ret = pthread_create(&tids[i], NULL, say_hello, NULL); + if (ret != 0) + { + cout << "pthread_create error: error_code=" << ret << endl; + } + } + //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来; + pthread_exit(NULL);} +``` + +使用 -lpthread 库编译下面的程序: + +```cpp +$ g++ test.cpp -lpthread -o test.o +``` + +现在,执行程序,将产生下列结果: + +```cpp +$ ./test.oHello Runoob!Hello Runoob!Hello Runoob!Hello Runoob!Hello Runoob! +``` + +以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,并接收传入的参数。每个线程打印一个 "Hello Runoob!" 消息,并输出接收的参数,然后调用 pthread_exit() 终止线程。 + +```cpp +//文件名:test.cpp#include #include #include using namespace std;#define NUM_THREADS 5void *PrintHello(void *threadid){ + // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取 + int tid = *((int*)threadid); + cout << "Hello Runoob! 线程 ID, " << tid << endl; + pthread_exit(NULL);}int main (){ + pthread_t threads[NUM_THREADS]; + int indexes[NUM_THREADS];// 用数组来保存i的值 + int rc; + int i; + for( i=0; i < NUM_THREADS; i++ ){ + cout << "main() : 创建线程, " << i << endl; + indexes[i] = i; //先保存i的值 + // 传入的时候必须强制转换为void* 类型,即无类型指针 + rc = pthread_create(&threads[i], NULL, + PrintHello, (void *)&(indexes[i])); + if (rc){ + cout << "Error:无法创建线程," << rc << endl; + exit(-1); + } + } + pthread_exit(NULL);} +``` + +现在编译并执行程序,将产生下列结果: + +```cpp +$ g++ test.cpp -lpthread -o test.o$ ./test.omain() : 创建线程, 0main() : 创建线程, 1main() : 创建线程, 2main() : 创建线程, 3main() : 创建线程, 4Hello Runoob! 线程 ID, 4Hello Runoob! 线程 ID, 3Hello Runoob! 线程 ID, 2Hello Runoob! 线程 ID, 1Hello Runoob! 线程 ID, 0 +``` + +## 向线程传递参数 + +这个实例演示了如何通过结构传递多个参数。您可以在线程回调中传递任意的数据类型,因为它指向 void,如下面的实例所示: + +```cpp +#include #include #include using namespace std;#define NUM_THREADS 5struct thread_data{ int thread_id; char *message;};void *PrintHello(void *threadarg){ struct thread_data *my_data; my_data = (struct thread_data *) threadarg; cout << "Thread ID : " << my_data->thread_id ; cout << " Message : " << my_data->message << endl; pthread_exit(NULL);}int main (){ pthread_t threads[NUM_THREADS]; struct thread_data td[NUM_THREADS]; int rc; int i; for( i=0; i < NUM_THREADS; i++ ){ cout <<"main() : creating thread, " << i << endl; td[i].thread_id = i; td[i].message = "This is message"; rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]); if (rc){ cout << "Error:unable to create thread," << rc << endl; exit(-1); } } pthread_exit(NULL);} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +$ g++ test.cpp -lpthread -o test.o +$ ./test.o +main() : 创建线程, 0main() : 创建线程, 1main() : 创建线程, 2main() : 创建线程, 3main() : 创建线程, 4Hello Runoob! 线程 ID, 4Hello Runoob! 线程 ID, 3Hello Runoob! 线程 ID, 2Hello Runoob! 线程 ID, 1Hello Runoob! 线程 ID, 0 +``` + +## 连接和分离线程 + +我们可以使用以下两个函数来连接或分离线程: + +```cpp +pthread_join (threadid, status) pthread_detach (threadid) +``` + +pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。 + +这个实例演示了如何使用 pthread_join() 函数来等待线程的完成。 + +```cpp +#include #include #include #include using namespace std;#define NUM_THREADS 5void *wait(void *t){ + int i; + long tid; + + tid = (long)t; + + sleep(1); + cout << "Sleeping in thread " << endl; + cout << "Thread with id : " << tid << " ...exiting " << endl; + pthread_exit(NULL);}int main (){ + int rc; + int i; + pthread_t threads[NUM_THREADS]; + pthread_attr_t attr; + void *status; + + // 初始化并设置线程为可连接的(joinable) + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + for( i=0; i < NUM_THREADS; i++ ){ + cout << "main() : creating thread, " << i << endl; + rc = pthread_create(&threads[i], NULL, wait, (void *)&i ); + if (rc){ + cout << "Error:unable to create thread," << rc << endl; + exit(-1); + } + } + + // 删除属性,并等待其他线程 + pthread_attr_destroy(&attr); + for( i=0; i < NUM_THREADS; i++ ){ + rc = pthread_join(threads[i], &status); + if (rc){ + cout << "Error:unable to join," << rc << endl; + exit(-1); + } + cout << "Main: completed thread id :" << i ; + cout << " exiting with status :" << status << endl; + } + + cout << "Main: program exiting." << endl; + pthread_exit(NULL);} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +main() : creating thread, 0main() : creating thread, 1main() : creating thread, 2main() : creating thread, 3main() : creating thread, 4Sleeping in thread +Thread with id : 4 ...exiting +Sleeping in thread +Thread with id : 3 ...exiting +Sleeping in thread +Thread with id : 2 ...exiting +Sleeping in thread +Thread with id : 1 ...exiting +Sleeping in thread +Thread with id : 0 ...exiting +Main: completed thread id :0 exiting with status :0Main: completed thread id :1 exiting with status :0Main: completed thread id :2 exiting with status :0Main: completed thread id :3 exiting with status :0Main: completed thread id :4 exiting with status :0Main: program exiting. +``` + +# C++ Web 编程 + +## 什么是 CGI? + +- 公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。 +- CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下: +- 公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准。 +- 目前的版本是 CGI/1.1,CGI/1.2 版本正在推进中。 + +## Web 浏览 + +为了更好地了解 CGI 的概念,让我们点击一个超链接,浏览一个特定的网页或 URL,看看会发生什么。 + +- 您的浏览器联系上 HTTP Web 服务器,并请求 URL,即文件名。 +- Web 服务器将解析 URL,并查找文件名。如果找到请求的文件,Web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。 +- Web 浏览器从 Web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。 + +然而,以这种方式搭建起来的 HTTP 服务器,不管何时请求目录中的某个文件,HTTP 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。 + +公共网关接口(CGI),是使得应用程序(称为 CGI 程序或 CGI 脚本)能够与 Web 服务器以及客户端进行交互的标准协议。这些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等进行编写。 + +## CGI 架构图 + +下图演示了 CGI 的架构: + +![img](https://edu.aliyun.com/files/course/2017/09-24/1655506773c2493212.gif) + +## Web 服务器配置 + +在您进行 CGI 编程之前,请确保您的 Web 服务器支持 CGI,并已配置成可以处理 CGI 程序。所有由 HTTP 服务器执行的 CGI 程序,都必须在预配置的目录中。该目录称为 CGI 目录,按照惯例命名为 /var/www/cgi-bin。虽然 CGI 文件是 C++ 可执行文件,但是按照惯例它的扩展名是 **.cgi**。 + +默认情况下,Apache Web 服务器会配置在 /var/www/cgi-bin 中运行 CGI 程序。如果您想指定其他目录来运行 CGI 脚本,您可以在 httpd.conf 文件中修改以下部分: + +```cpp + AllowOverride None Options ExecCGI Order allow,deny Allow from all Options All +``` + +在这里,我们假设已经配置好 Web 服务器并能成功运行,你可以运行任意的 CGI 程序,比如 Perl 或 Shell 等。 + +## 第一个 CGI 程序 + +请看下面的 C++ 程序: + +```cpp +#include using namespace std; + int main (){ + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "Hello World - 第一个 CGI 程序\n"; + cout << "\n"; + cout << "\n"; + cout << "

Hello World! 这是我的第一个 CGI 程序

\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +编译上面的代码,把可执行文件命名为 cplusplus.cgi,并把这个文件保存在 /var/www/cgi-bin 目录中。在运行 CGI 程序之前,请使用 **chmod 755 cplusplus.cgi** UNIX 命令来修改文件模式,确保文件可执行。访问可执行文件,您会看到下面的输出: + +## Hello World! 这是我的第一个 CGI 程序 + +上面的 C++ 程序是一个简单的程序,把它的输出写在 STDOUT 文件上,即显示在屏幕上。在这里,值得注意一点,第一行输出 **Content-type:text/html\r\n\r\n**。这一行发送回浏览器,并指定要显示在浏览器窗口上的内容类型。您必须理解 CGI 的基本概念,这样才能进一步使用 Python 编写更多复杂的 CGI 程序。C++ CGI 程序可以与任何其他外部的系统(如 RDBMS)进行交互。 + +## HTTP 头信息 + +行 **Content-type:text/html\r\n\r\n** 是 HTTP 头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP 头信息的形式如下: + +```cpp +HTTP 字段名称: 字段内容 例如Content-type: text/html\r\n\r\n +``` + +还有一些其他的重要的 HTTP 头信息,这些在您的 CGI 编程中都会经常被用到。 + +| 头信息 | 描述 | +| :------------------ | :----------------------------------------------------------- | +| Content-type: | MIME 字符串,定义返回的文件格式。例如 Content-type:text/html。 | +| Expires: Date | 信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 Jan 1998 12:00:00 GMT。 | +| Location: URL | 这个 URL 是指应该返回的 URL,而不是请求的 URL。你可以使用它来重定向一个请求到任意的文件。 | +| Last-modified: Date | 资源的最后修改日期。 | +| Content-length: N | 要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。 | +| Set-Cookie: String | 通过 *string* 设置 cookie。 | + +## CGI 环境变量 + +所有的 CGI 程序都可以访问下列的环境变量。这些变量在编写 CGI 程序时扮演了非常重要的角色。 + +| 变量名 | 描述 | +| :-------------- | :----------------------------------------------------------- | +| CONTENT_TYPE | 内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。 | +| CONTENT_LENGTH | 查询的信息长度。只对 POST 请求可用。 | +| HTTP_COOKIE | 以键 & 值对的形式返回设置的 cookies。 | +| HTTP_USER_AGENT | 用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。 | +| PATH_INFO | CGI 脚本的路径。 | +| QUERY_STRING | 通过 GET 方法发送请求时的 URL 编码信息,包含 URL 中问号后面的参数。 | +| REMOTE_ADDR | 发出请求的远程主机的 IP 地址。这在日志记录和认证时是非常有用的。 | +| REMOTE_HOST | 发出请求的主机的完全限定名称。如果此信息不可用,则可以用 REMOTE_ADDR 来获取 IP 地址。 | +| REQUEST_METHOD | 用于发出请求的方法。最常见的方法是 GET 和 POST。 | +| SCRIPT_FILENAME | CGI 脚本的完整路径。 | +| SCRIPT_NAME | CGI 脚本的名称。 | +| SERVER_NAME | 服务器的主机名或 IP 地址。 | +| SERVER_SOFTWARE | 服务器上运行的软件的名称和版本。 | + +下面的 CGI 程序列出了所有的 CGI 变量。 + +```cpp +#include #include using namespace std;const string ENV[ 24 ] = { + "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", + "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", &nbsnbsp; + "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", + "HTTP_HOST", "HTTP_USER_AGENT", "PATH", + "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", + "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", + "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN", + "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL", + "SERVER_SIGNATURE","SERVER_SOFTWARE" }; int main (){ + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "CGI 环境变量\n"; + cout << "\n"; + cout << "\n"; + cout << ""; + + for ( int i = 0; i < 24; i++ ) + { + cout << "\n"; + } + cout << "
" << ENV[ i ] << ""; + // 尝试检索环境变量的值 + char *value = getenv( ENV[ i ].c_str() ); + if ( value != 0 ){ + cout << value; + }else{ + cout << "环境变量不存在。"; + } + cout << "
<\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +## C++ CGI 库 + +在真实的实例中,您需要通过 CGI 程序执行许多操作。这里有一个专为 C++ 程序而编写的 CGI 库,我们可以从 [ftp://ftp.gnu.org/gnu/cgicc/](ftp://ftp.gnu.org/gnu/cgicc/) 上下载这个 CGI 库,并按照下面的步骤安装库: + +```cpp +$tar xzf cgicc-X.X.X.tar.gz $cd cgicc-X.X.X/ $./configure --prefix=/usr $make$make install +``` + +您可以点击 C++ CGI Lib Documentation,查看相关的库文档。 + +## GET 和 POST 方法 + +您可能有遇到过这样的情况,当您需要从浏览器传递一些信息到 Web 服务器,最后再传到 CGI 程序。通常浏览器会使用两种方法把这个信息传到 Web 服务器,分别是 GET 和 POST 方法。 + +## 使用 GET 方法传递信息 + +GET 方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过 ? 字符分隔开,如下所示: + +```cpp +http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2 +``` + +GET 方法是默认的从浏览器向 Web 服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串。当您向服务器传密码或其他一些敏感信息时,不要使用 GET 方法。GET 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符。 + +当使用 GET 方法时,是使用 QUERY_STRING http 头来传递信息,在 CGI 程序中可使用 QUERY_STRING 环境变量来访问。 + +您可以通过在 URL 后跟上简单连接的键值对,也可以通过使用 HTML
标签的 GET 方法来传信息。 + +## 简单的 URL 实例:Get 方法 + +下面是一个简单的 URL,使用 GET 方法传递两个值给 hello_get.py 程序。 + +/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI + +下面的实例生成 **cpp_get.cgi** CGI 程序,用于处理 Web 浏览器给出的输入。通过使用 C++ CGI 库,可以很容易地访问传递的信息: + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc formData; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "使用 GET 和 POST 方法\n"; + cout << "\n"; + cout << "\n"; + + form_iterator fi = formData.getElement("first_name"); + if( !fi->isEmpty() && fi != (*formData).end()) { + cout << "名:" << **fi << endl; + }else{ + cout << "No text entered for first name" << endl; + } + cout << "
\n"; + fi = formData.getElement("last_name"); + if( !fi->isEmpty() &&fi != (*formData).end()) { + cout << "姓:" << **fi << endl; + }else{ + cout << "No text entered for last name" << endl; + } + cout << "
\n"; + + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +现在,编译上面的程序,如下所示: + +```cpp +$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc +``` + +生成 cpp_get.cgi,并把它放在 CGI 目录中,并尝试使用下面的链接进行访问: + +/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI + +这会产生以下结果: + +```cpp +名:ZARA 姓:ALI +``` + +## 简单的表单实例:GET 方法 + +下面是一个简单的实例,使用 HTML 表单和提交按钮传递两个值。我们将使用相同的 CGI 脚本 cpp_get.cgi 来处理输入。 + +```cpp +名:
姓: +``` + +下面是上述表单的实际输出,请输入名和姓,然后点击提交按钮查看结果。 + +## 使用 POST 方法传递信息 + +一个更可靠的向 CGI 程序传递信息的方法是 POST 方法。这种方法打包信息的方式与 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后进行传递,而是把它以单独的消息形式进行传递。该消息是以标准输入的形式传给 CGI 脚本的。 + +我们同样使用 cpp_get.cgi 程序来处理 POST 方法。让我们以同样的例子,通过使用 HTML 表单和提交按钮来传递两个值,只不过这次我们使用的不是 GET 方法,而是 POST 方法,如下所示: + +```cpp +
名:
姓:
+``` + +## 向 CGI 程序传递复选框数据 + +当需要选择多个选项时,我们使用复选框。 + +下面的 HTML 代码实例是一个带有两个复选框的表单: + +```html +
数学 物理
+``` + +下面的 C++ 程序会生成 cpp_checkbox.cgi 脚本,用于处理 Web 浏览器通过复选框给出的输入。 + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc formData; + bool maths_flag, physics_flag; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "向 CGI 程序传递复选框数据\n"; + cout << "\n"; + cout << "\n"; + + maths_flag = formData.queryCheckbox("maths"); + if( maths_flag ) { + cout << "Maths Flag: ON " << endl; + }else{ + cout << "Maths Flag: OFF " << endl; + } + cout << "
\n"; + + physics_flag = formData.queryCheckbox("physics"); + if( physics_flag ) { + cout << "Physics Flag: ON " << endl; + }else{ + cout << "Physics Flag: OFF " << endl; + } + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +## 向 CGI 程序传递单选按钮数据 + +当只需要选择一个选项时,我们使用单选按钮。 + +下面的 HTML 代码实例是一个带有两个单选按钮的表单: + +```cpp +
数学 + 物理
+``` + +下面的 C++ 程序会生成 cpp_radiobutton.cgi 脚本,用于处理 Web 浏览器通过单选按钮给出的输入。 + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc formData; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "向 CGI 程序传递单选按钮数据\n"; + cout << "\n"; + cout << "\n"; + + form_iterator fi = formData.getElement("subject"); + if( !fi->isEmpty() && fi != (*formData).end()) { + cout << "Radio box selected: " << **fi << endl; + } + + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +## 向 CGI 程序传递文本区域数据 + +当需要向 CGI 程序传递多行文本时,我们使用 TEXTAREA 元素。 + +下面的 HTML 代码实例是一个带有 TEXTAREA 框的表单: + +``` +
+``` + +下面的 C++ 程序会生成 cpp_textarea.cgi 脚本,用于处理 Web 浏览器通过文本区域给出的输入。 + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc formData; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "向 CGI 程序传递文本区域数据\n"; + cout << "\n"; + cout << "\n"; + + form_iterator fi = formData.getElement("textcontent"); + if( !fi->isEmpty() && fi != (*formData).end()) { + cout << "Text Content: " << **fi << endl; + }else{ + cout << "No text entered" << endl; + } + + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +## 向 CGI 程序传递下拉框数据 + +当有多个选项可用,但只能选择一个或两个选项时,我们使用下拉框。 + +下面的 HTML 代码实例是一个带有下拉框的表单: + +```cpp +
+``` + +下面的 C++ 程序会生成 cpp_dropdown.cgi 脚本,用于处理 Web 浏览器通过下拉框给出的输入。 + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc formData; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "向 CGI 程序传递下拉框数据\n"; + cout << "\n"; + cout << "\n"; + + form_iterator fi = formData.getElement("dropdown"); + if( !fi->isEmpty() && fi != (*formData).end()) { + cout << "Value Selected: " << **fi << endl; + } + + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +## 在 CGI 中使用 Cookies + +HTTP 协议是一种无状态的协议。但对于一个商业网站,它需要在不同页面间保持会话信息。例如,一个用户在完成多个页面的步骤之后结束注册。但是,如何在所有网页中保持用户的会话信息。 + +在许多情况下,使用 cookies 是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法。 + +### 它是如何工作的 + +服务器以 cookie 的形式向访客的浏览器发送一些数据。如果浏览器接受了 cookie,则 cookie 会以纯文本记录的形式存储在访客的硬盘上。现在,当访客访问网站上的另一个页面时,会检索 cookie。一旦找到 cookie,服务器就知道存储了什么。 + +cookie 是一种纯文本的数据记录,带有 5 个可变长度的字段: + +- **Expires :** cookie 的过期日期。如果此字段留空,cookie 会在访客退出浏览器时过期。 +- **Domain :** 网站的域名。 +- **Path :** 设置 cookie 的目录或网页的路径。如果您想从任意的目录或网页检索 cookie,此字段可以留空。 +- **Secure :** 如果此字段包含单词 "secure",那么 cookie 只能通过安全服务器进行检索。如果此字段留空,则不存在该限制。 +- **Name=Value :** cookie 以键值对的形式被设置和获取。 + +### 设置 Cookies + +向浏览器发送 cookies 是非常简单的。这些 cookies 会在 Content-type 字段之前,与 HTTP 头一起被发送。假设您想设置 UserID 和 Password 为 cookies,设置 cookies 的步骤如下所示: + +```cpp +#include using namespace std;int main (){ + + cout << "Set-Cookie:UserID=XYZ;\r\n"; + cout << "Set-Cookie:Password=XYZ123;\r\n"; + cout << "Set-Cookie:Domain=www.w3cschool.cc;\r\n"; + cout << "Set-Cookie:Path=/perl;\n"; + cout << "Content-type:text/html\r\n\r\n"; + + cout << "\n"; + cout << "\n"; + cout << "CGI 中的 Cookies\n"; + cout << "\n"; + cout << "\n"; + + cout << "设置 cookies" << endl; + + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +从这个实例中,我们了解了如何设置 cookies。我们使用 **Set-Cookie** HTTP 头来设置 cookies。 + +在这里,有一些设置 cookies 的属性是可选的,比如 Expires、Domain 和 Path。值得注意的是,cookies 是在发送行 **"Content-type:text/html\r\n\r\n** 之前被设置的。 + +编译上面的程序,生成 setcookies.cgi,并尝试使用下面的链接设置 cookies。它会在您的计算机上设置四个 cookies: + +/cgi-bin/setcookies.cgi + +### 获取 Cookies + +检索所有设置的 cookies 是非常简单的。cookies 被存储在 CGI 环境变量 HTTP_COOKIE 中,且它们的形式如下: + +```cpp +key1=value1;key2=value2;key3=value3.... +``` + +下面的实例演示了如何获取 cookies。 + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc cgi; + const_cookie_iterator cci; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "CGI 中的 Cookies\n"; + cout << "\n"; + cout << "\n"; + cout << ""; + + // 获取环境变量 + const CgiEnvironment& env = cgi.getEnvironment(); + + for( cci = env.getCookieList().begin(); + cci != env.getCookieList().end(); + ++cci ) + { + cout << "\n"; + } + cout << "
" << cci->getName() << ""; + cout << cci->getValue(); + cout << "
<\n"; + + cout << "
\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +现在,编译上面的程序,生成 getcookies.cgi,并尝试使用下面的链接获取您的计算机上所有可用的 cookies: + +/cgi-bin/getcookies.cgi + +这会产生一个列表,显示了上一节中设置的四个 cookies 以及您的计算机上所有其他的 cookies: + +```cpp +UserID XYZ Password XYZ123 Domain www.w3cschool.cc Path /perl +``` + +## 文件上传实例 + +为了上传一个文件,HTML 表单必须把 enctype 属性设置为 **multipart/form-data**。带有文件类型的 input 标签会创建一个 "Browse" 按钮。 + +```cpp + +
+

文件:

+

+
+``` + +这段代码的结果是下面的表单: + +文件: + + + +**注意:**上面的实例已经故意禁用了保存上传的文件在我们的服务器上。您可以在自己的服务器上尝试上面的代码。 + +下面是用于处理文件上传的脚本 **cpp_uploadfile.cpp**: + +```cpp +#include #include #include #include #include #include #include #include #include using namespace std;using namespace cgicc;int main (){ + Cgicc cgi; + + cout << "Content-type:text/html\r\n\r\n"; + cout << "\n"; + cout << "\n"; + cout << "CGI 中的文件上传\n"; + cout << "\n"; + cout << "\n"; + + // 获取要被上传的文件列表 + const_file_iterator file = cgi.getFile("userfile"); + if(file != cgi.getFiles().end()) { + // 在 cout 中发送数据类型 + cout << HTTPContentHeader(file->getDataType()); + // 在 cout 中写入内容 + file->writeToStream(cout); + } + cout << "<文件上传成功>\n"; + cout << "\n"; + cout << "\n"; + + return 0;} +``` + +上面的实例是在 **cout** 流中写入内容,但您可以打开文件流,并把上传的文件内容保存在目标位置的某个文件中。 + +# C++ STL 教程 + +在前面的章节中,我们已经学习了 C++ 模板的概念。C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。 + +C++ 标准模板库的核心包括以下三个组件: + +| 组件 | 描述 | +| :------------------ | :----------------------------------------------------------- | +| 容器(Containers) | 容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。 | +| 算法(Algorithms) | 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。 | +| 迭代器(iterators) | 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。 | + +这三个组件都带有丰富的预定义函数,帮助我们通过简单的方式处理复杂的任务。 + +下面的程序演示了向量容器(一个 C++ 标准的模板),它与数组十分相似,唯一不同的是,向量在需要扩展大小的时候,会自动处理它自己的存储需求: + +```cpp +#include #include using namespace std; + int main(){ + // 创建一个向量存储 int + vector vec; + int i; + + // 显示 vec 的原始大小 + cout << "vector size = " << vec.size() << endl; + + // 推入 5 个值到向量中 + for(i = 0; i < 5; i++){ + vec.push_back(i); + } + + // 显示 vec 扩展后的大小 + cout << "extended vector size = " << vec.size() << endl; + + // 访问向量中的 5 个值 + for(i = 0; i < 5; i++){ + cout << "value of vec [" << i << "] = " << vec[i] << endl; + } + + // 使用迭代器 iterator 访问值 + vector::iterator v = vec.begin(); + while( v != vec.end()) { + cout << "value of v = " << *v << endl; + v++; + } + + return 0;} +``` + +当上面的代码被编译和执行时,它会产生下列结果: + +```cpp +vector size = 0extended vector size = 5value of vec [0] = 0value of vec [1] = 1value of vec [2] = 2value of vec [3] = 3value of vec [4] = 4value of v = 0value of v = 1value of v = 2value of v = 3value of v = 4 +``` + +关于上面实例中所使用的各种函数,有几点要注意: + +- push_back( ) 成员函数在向量的末尾插入值,如果有必要会扩展向量的大小。 +- size( ) 函数显示向量的大小。 +- begin( ) 函数返回一个指向向量开头的迭代器。 +- end( ) 函数返回一个指向向量末尾的迭代器。 + +# C++ 标准库 + +C++ 标准库可以分为两部分: + +- **标准函数库:** 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。 +- **面向对象类库:** 这个库是类及其相关函数的集合。 + +C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。 + +## 标准函数库 + +标准函数库分为以下几类: + +- 输入/输出 I/O +- 字符串和字符处理 +- 数学 +- 时间、日期和本地化 +- 动态分配 +- 其他 +- 宽字符函数 + +## 面向对象类库 + +标准的 C++ 面向对象类库定义了大量支持一些常见操作的类,比如输入/输出 I/O、字符串处理、数值处理。面向对象类库包含以下内容: + +- 标准的 C++ I/O 类 +- String 类 +- 数值类 +- STL 容器类 +- STL 算法 +- STL 函数对象 +- STL 迭代器 +- STL 分配器 +- 本地化库 +- 异常处理类 +- 杂项支持库 + diff --git "a/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\270\200\357\274\211.md" "b/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\270\200\357\274\211.md" new file mode 100644 index 0000000000000000000000000000000000000000..06cce5698a5393a7d34ad7c50be452f1b0c26907 --- /dev/null +++ "b/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\270\200\357\274\211.md" @@ -0,0 +1,334 @@ +# C++11常用新特性(一) + +最近工作中,遇到一些问题,使用C11实现起来会更加方便,而线上的生产环境还不支持C11,于是决定新年开工后,在组内把C++11推广开来,整理以下文档,方便自己查阅,也方便同事快速上手。(对于异步编程十分实用的Future/Promise以及智能指针等,将不做整理介绍,组内使用的框架已经支持并广泛使用了,用的是自己公司参考boost实现的版本) # nullptr + +nullptr 出现的目的是为了替代 NULL。 + +在某种意义上来说,传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0。 + +C++ 不允许直接将 void *隐式转换到其他类型,但如果 NULL 被定义为 ((void*)0),那么当编译char *ch = NULL;时,NULL 只好被定义为 0。 + +而这依然会产生问题,将导致了 C++ 中重载特性会发生混乱,考虑: + +```c +void foo(char *); +void foo(int); +``` + +对于这两个函数来说,如果 NULL 又被定义为了 0 那么 foo(NULL); 这个语句将会去调用 foo(int),从而导致代码违反直观。 + +为了解决这个问题,C++11 引入了 nullptr 关键字,专门用来区分空指针、0。 + +nullptr 的类型为 nullptr_t,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。 + +当需要使用 NULL 时候,养成直接使用 nullptr的习惯。 + +# 类型推导 + +C++11 引入了 auto 和 decltype 这两个关键字实现了类型推导,让编译器来操心变量的类型。 + +## auto + +auto 在很早以前就已经进入了 C++,但是他始终作为一个存储类型的指示符存在,与 register 并存。在传统 C++ 中,如果一个变量没有声明为 register 变量,将自动被视为一个 auto 变量。而随着 register 被弃用,对 auto 的语义变更也就非常自然了。 + +使用 auto 进行类型推导的一个最为常见而且显著的例子就是迭代器。在以前我们需要这样来书写一个迭代器: + +```c +for(vector::const_iterator itr = vec.cbegin(); itr != vec.cend(); ++itr) +``` + +而有了 auto 之后可以: + +```c +// 由于 cbegin() 将返回 vector::const_iterator +// 所以 itr 也应该是 vector::const_iterator 类型 +for(auto itr = vec.cbegin(); itr != vec.cend(); ++itr); +``` + +一些其他的常见用法: + +```c +auto i = 5; // i 被推导为 int +auto arr = new auto(10) // arr 被推导为 int * +``` + +注意:auto 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板): + +```c +int add(auto x, auto y); +``` + +此外,auto 还不能用于推导数组类型: + +```c +#include + +int main() { + auto i = 5; + + int arr[10] = {0}; + auto auto_arr = arr; + auto auto_arr2[10] = arr; + + return 0; +} +``` + +## decltype + +decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 sizeof 很相似: + +```c +decltype(表达式) +``` + +在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。 + +有时候,我们可能需要计算某个表达式的类型,例如: + +```c +auto x = 1; +auto y = 2; +decltype(x+y) z; +``` + +## 拖尾返回类型、auto 与 decltype 配合 + +你可能会思考,auto 能不能用于推导函数的返回类型。考虑这样一个例子加法函数的例子,在传统 C++ 中我们必须这么写: + +```c +template +R add(T x, U y) { + return x+y +} +``` + +这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 add() 这个函数会做什么样的操作,获得一个什么样的返回类型。 + +在 C++11 中这个问题得到解决。虽然你可能马上回反应出来使用 decltype 推导 x+y 的类型,写出这样的代码: + +```c +decltype(x+y) add(T x, U y); +``` + +但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,x 和 y 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做拖尾返回类型(trailing return type),利用 auto 关键字将返回类型后置: ```c +template +auto add(T x, U y) -> decltype(x+y) { + return x+y; +} +``` 从 C++14 开始是可以直接让普通函数具备返回值推导,因此下面的写法变得合法: ```c +template +auto add(T x, U y) { + return x+y; +} +``` # 区间迭代 ## 基于范围的 for 循环 C++11 引入了基于范围的迭代写法,我们拥有了能够写出像 Python 一样简洁的循环语句。 +最常用的 std::vector 遍历将从原来的样子: ```c +std::vector arr(5, 100); +for(std::vector::iterator i = arr.begin(); i != arr.end(); ++i) { + std::cout << *i << std::endl; +} +``` 变得非常的简单: ```c +// & 启用了引用 +for(auto &i : arr) { + std::cout << i << std::endl; +} +``` # 初始化列表 C++11 提供了统一的语法来初始化任意的对象,例如: ```c +struct A { + int a; + float b; +}; +struct B { + B(int _a, float _b): a(_a), b(_b) {} + private: + int a; + float b; +}; + +A a {1, 1.1}; // 统一的初始化语法 +B b {2, 2.2}; +``` C++11 还把初始化列表的概念绑定到了类型上,并将其称之为`std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如: ```c +#include + +class Magic { +public: + Magic(std::initializer_list list) {} +}; + +Magic magic = {1,2,3,4,5}; +std::vector v = {1, 2, 3, 4}; +``` # 模板增强 ## 外部模板 传统 C++ 中,模板只有在使用时才会被编译器实例化。只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板实例化。 C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显式的告诉编译器何时进行模板的实例化: ```c +template class std::vector; // 强行实例化 +extern template class std::vector; // 不在该编译文件中实例化模板 +``` ## 尖括号 “>” 在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码: ```c +std::vector> wow; +``` 这在传统C编译器下是不能够被编译的,而 C11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。 ## 类型别名模板 在传统 C++中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如: ```c +template< typename T, typename U, int value> +class SuckType { +public: + T a; + U b; + SuckType():a(value),b(value){} +}; +template< typename U> +typedef SuckType, U, 1> NewType; // 不合法 +``` C++11 使用 using 引入了下面这种形式的写法,并且同时支持对传统 typedef 相同的功效: ```c +template +using NewType = SuckType; // 合法 +``` ## 默认模板参数 我们可能定义了一个加法函数: ```c +template +auto add(T x, U y) -> decltype(x+y) { + return x+y +} +``` 但在使用时发现,要使用 add,就必须每次都指定其模板参数的类型。 在 C++11 中提供了一种便利,可以指定模板的默认参数: ```c +template +auto add(T x, U y) -> decltype(x+y) { + return x+y; +} +``` # 构造函数 ## 委托构造 C++11 引入了委托构造的概念,这使得构造函数可以在同一个类中一个构造函数调用另一个构造函数,从而达到简化代码的目的: ```c +class Base { +public: + int value1; + int value2; + Base() { + value1 = 1; + } + Base(int value) : Base() { // 委托 Base() 构造函数 + value2 = 2; + } +}; +``` ## 继承构造 在继承体系中,如果派生类想要使用基类的构造函数,需要在构造函数中显式声明。 假若基类拥有为数众多的不同版本的构造函数,这样,在派生类中得写很多对应的“透传”构造函数。如下: ```c +struct A +{ + A(int i) {} + A(double d,int i){} + A(float f,int i,const char* c){} + //...等等系列的构造函数版本 +}; +struct B:A +{ + B(int i):A(i){} + B(double d,int i):A(d,i){} + B(folat f,int i,const char* c):A(f,i,e){} + //......等等好多个和基类构造函数对应的构造函数 +}; +``` C++11的继承构造: ```c +struct A +{ + A(int i) {} + A(double d,int i){} + A(float f,int i,const char* c){} + //...等等系列的构造函数版本 +}; +struct B:A +{ + using A::A; + //关于基类各构造函数的继承一句话搞定 + //...... +}; +``` 如果一个继承构造函数不被相关的代码使用,编译器不会为之产生真正的函数代码,这样比透传基类各种构造函数更加节省目标代码空间。 # + +# 新增容器 ## std::array std::array 保存在栈内存中,相比堆内存中的 std::vector,我们能够灵活的访问这里面的元素,从而获得更高的性能。 std::array 会在编译时创建一个固定大小的数组,std::array 不能够被隐式的转换成指针,使用 std::array只需指定其类型和大小即可: ```c +std::array arr= {1,2,3,4}; + +int len = 4; +std::array arr = {1,2,3,4}; // 非法, 数组大小参数必须是常量表达式 +``` 当我们开始用上了 std::array 时,难免会遇到要将其兼容 C 风格的接口,这里有三种做法: ```c +void foo(int *p, int len) { + return; +} + +std::array arr = {1,2,3,4}; + +// C 风格接口传参 +// foo(arr, arr.size()); // 非法, 无法隐式转换 +foo(&arr[0], arr.size()); +foo(arr.data(), arr.size()); + +// 使用 `std::sort` +std::sort(arr.begin(), arr.end()); +``` ## std::forward_list std::forward_list 是一个列表容器,使用方法和 std::list 基本类似。 和 std::list 的双向链表的实现不同,std::forward_list 使用单向链表进行实现,提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特点),也是标准库容器中唯一一个不提供 size() 方法的容器。当不需要双向迭代时,具有比 std::list 更高的空间利用率。 ## 无序容器 C++11 引入了两组无序容器: +`std::unordered_map/std::unordered_multimap`和 `std::unordered_set/std::unordered_multiset`。 无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 O(constant)。 ## 元组 std::tuple 元组的使用有三个核心的函数: `std::make_tuple`: 构造元组 +`std::get`: 获得元组某个位置的值 +`std::tie`: 元组拆包 ```c +#include +#include + +auto get_student(int id) +{ + // 返回类型被推断为 std::tuple + if (id == 0) + return std::make_tuple(3.8, 'A', "张三"); + if (id == 1) + return std::make_tuple(2.9, 'C', "李四"); + if (id == 2) + return std::make_tuple(1.7, 'D', "王五"); + return std::make_tuple(0.0, 'D', "null"); + // 如果只写 0 会出现推断错误, 编译失败 +} + +int main() +{ + auto student = get_student(0); + std::cout << "ID: 0, " + << "GPA: " << std::get<0>(student) << ", " + << "成绩: " << std::get<1>(student) << ", " + << "姓名: " << std::get<2>(student) << '\n'; + + double gpa; + char grade; + std::string name; + + // 元组进行拆包 + std::tie(gpa, grade, name) = get_student(1); + std::cout << "ID: 1, " + << "GPA: " << gpa << ", " + << "成绩: " << grade << ", " + << "姓名: " << name << '\n'; + +} +``` 合并两个元组,可以通过 std::tuple_cat 来实现。 ```c +auto new_tuple = std::tuple_cat(get_student(1), std::move(t)); +``` # 正则表达式 正则表达式描述了一种字符串匹配的模式。一般使用正则表达式主要是实现下面三个需求: 1. 检查一个串是否包含某种形式的子串; +2. 将匹配的子串替换; + +1. 从某个串中取出符合条件的子串。 C++11 提供的正则表达式库操作 std::string 对象,对模式 std::regex (本质是 std::basic_regex)进行初始化,通过 std::regex_match 进行匹配,从而产生 std::smatch (本质是 std::match_results 对象)。 我们通过一个简单的例子来简单介绍这个库的使用。考虑下面的正则表达式: [a-z]+.txt: 在这个正则表达式中, [a-z] 表示匹配一个小写字母, + 可以使前面的表达式匹配多次,因此 [a-z]+ 能够匹配一个及以上小写字母组成的字符串。在正则表达式中一个 . 表示匹配任意字符,而 . 转义后则表示匹配字符 . ,最后的 txt 表示严格匹配 txt 这三个字母。因此这个正则表达式的所要匹配的内容就是文件名为纯小写字母的文本文件。 +std::regex_match 用于匹配字符串和正则表达式,有很多不同的重载形式。最简单的一个形式就是传入std::string 以及一个 std::regex 进行匹配,当匹配成功时,会返回 true,否则返回 false。例如: ```c +#include +#include +#include + +int main() { + std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"}; + // 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.` + std::regex txt_regex("[a-z]+\\.txt"); + for (const auto &fname: fnames) + std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl; +} +``` 另一种常用的形式就是依次传入 std::string/std::smatch/std::regex 三个参数,其中 std::smatch 的本质其实是 std::match_results,在标准库中, std::smatch 被定义为了 std::match_results,也就是一个子串迭代器类型的 match_results。使用 std::smatch 可以方便的对匹配的结果进行获取,例如: ```c +std::regex base_regex("([a-z]+)\\.txt"); +std::smatch base_match; +for(const auto &fname: fnames) { + if (std::regex_match(fname, base_match, base_regex)) { + // sub_match 的第一个元素匹配整个字符串 + // sub_match 的第二个元素匹配了第一个括号表达式 + if (base_match.size() == 2) { + std::string base = base_match[1].str(); + std::cout << "sub-match[0]: " << base_match[0].str() << std::endl; + std::cout << fname << " sub-match[1]: " << base << std::endl; + } + } +} +``` 以上两个代码段的输出结果为: ```bash +foo.txt: 1 +bar.txt: 1 +test: 0 +a0.txt: 0 +AAA.txt: 0 +sub-match[0]: foo.txt +foo.txt sub-match[1]: foo +sub-match[0]: bar.txt +bar.txt sub-match[1]: bar +``` # 语言级线程支持 std::thread
+std::mutex/std::unique_lock
+std::future/std::packaged_task
+std::condition_variable
diff --git "a/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\272\214\357\274\211.md" "b/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\272\214\357\274\211.md" new file mode 100644 index 0000000000000000000000000000000000000000..4fbc34d86baed68ecf22fca323cd0c68f4318064 --- /dev/null +++ "b/C++11\345\270\270\347\224\250\346\226\260\347\211\271\346\200\247\357\274\210\344\272\214\357\274\211.md" @@ -0,0 +1,268 @@ +# C++11常用新特性(二) + +# Lambda 表达式 Lambda 表达式,实际上就是提供了一个类似匿名函数的特性,而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下去使用的。 ## Lambda 表达式的基本语法如下: ```c +[ caputrue ] ( params ) opt -> ret { body; }; +``` 1. capture是捕获列表; +2. params是参数表;(选填) + +1. opt是函数选项;可以填`mutable`,`exception`,`attribute`(选填) - mutable说明lambda表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获的对象的non-const方法。 +- exception说明lambda表达式是否抛出异常以及何种异常。 + +- attribute用来声明属性。 1. ret是返回值类型(拖尾返回类型)。(选填) +2. body是函数体。 捕获列表:lambda表达式的捕获列表精细控制了lambda表达式能够访问的外部变量,以及如何访问这些变量。 1. []不捕获任何变量。 +2. [&]捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。 + +1. [=]捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。注意值捕获的前提是变量可以拷贝,**且被捕获的量在 lambda 表达式被创建时拷贝,而非调用时才拷贝**。如果希望lambda表达式在调用时能即时访问外部变量,我们应当用引用方式捕获。 + +```c +int a = 0; +auto f = [=] { return a; }; + +a+=1; + +cout << f() << endl; //输出0 + +int a = 0; +auto f = [&a] { return a; }; + +a+=1; + +cout << f() < add_x(int x) +{ + return [&](int a) { return x + a; }; +} +``` 上面函数返回了一个lambda表达式,参数x仅是一个临时变量,函数add_x调用后就被销毁了,但是返回的lambda表达式却引用了该变量,当调用这个表达式时,引用的是一个垃圾值,会产生没有意义的结果。上面这种情况,使用默认传值方式可以避免悬挂引用问题。 但是采用默认值捕获所有变量仍然有风险,看下面的例子: ```c +class Filter +{ +public: + Filter(int divisorVal): + divisor{divisorVal} + {} + + std::function getFilter() + { + return [=](int value) {return value % divisor == 0; }; + } + +private: + int divisor; +}; +``` 这个类中有一个成员方法,可以返回一个lambda表达式,这个表达式使用了类的数据成员divisor。而且采用默认值方式捕捉所有变量。你可能认为这个lambda表达式也捕捉了divisor的一份副本,但是实际上并没有。因为数据成员divisor对lambda表达式并不可见,你可以用下面的代码验证: ```c +// 类的方法,下面无法编译,因为divisor并不在lambda捕捉的范围 +std::function getFilter() +{ + return [divisor](int value) {return value % divisor == 0; }; +} +``` 原代码中,lambda表达式实际上捕捉的是this指针的副本,所以原来的代码等价于: ```c +std::function getFilter() { return [this](int value) {return value % this->divisor == 0; };} +``` 尽管还是以值方式捕获,但是捕获的是指针,其实相当于以引用的方式捕获了当前类对象,**所以lambda表达式的闭包与一个类对象绑定在一起了,这很危险,因为你仍然有可能在类对象析构后使用这个lambda表达式**,那么类似“悬挂引用”的问题也会产生。所以,**采用默认值捕捉所有变量仍然是不安全的**,主要是由于指针变量的复制,实际上还是按引用传值。 lambda表达式可以赋值给对应类型的函数指针。但是使用函数指针并不是那么方便。所以STL定义在< functional >头文件提供了一个多态的函数对象封装std::function,其类似于函数指针。它可以绑定任何类函数对象,只要参数与返回类型相同。如下面的返回一个bool且接收两个int的函数包装器: ```c +std::function wrapper = [](int x, int y) { return x < y; }; +``` lambda表达式一个更重要的应用是其可以用于函数的参数,通过这种方式可以实现回调函数。 最常用的是在STL算法中,比如你要统计一个数组中满足特定条件的元素数量,通过lambda表达式给出条件,传递给count_if函数: ```c +int value = 3; +vector v {1, 3, 5, 2, 6, 10}; +int count = std::count_if(v.beigin(), v.end(), [value](int x) { return x > value; }); +``` 再比如你想生成斐波那契数列,然后保存在数组中,此时你可以使用generate函数,并辅助lambda表达式: ```cpp +vector v(10); +int a = 0; +int b = 1; +std::generate(v.begin(), v.end(), + [&a, &b] { int value = b; b = b + a; a = value; return value; }); +// 此时v {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} +``` 当需要遍历容器并对每个元素进行操作时: ```c +std::vector v = { 1, 2, 3, 4, 5, 6 }; +int even_count = 0; +for_each(v.begin(), v.end(), [&even_count](int val){ + if(!(val & 1)){ + ++ even_count; + } +}); +std::cout << "The number of even is " << even_count << std::endl; +``` 大部分STL算法,可以非常灵活地搭配lambda表达式来实现想要的效果。 # C++ std::function std::function是一个函数对象的包装器,std::function的实例可以存储,复制和调用任何可调用的目标,包括: 1. 函数。 +2. lamada表达式。 + +1. 绑定表达式或其他函数对象。 +2. 指向成员函数和指向数据成员的指针。 当`std::function`对象没有初始化任何实际的可调用元素,调用`std::function`对象将抛出`std::bad_function_call`异常。 ## std::function简介 类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。 **通常std::function是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值**。std::function使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。 C++标准库详细说明了这个的基本使用http://www.cplusplus.com/reference/functional/function/. 这里我们大概总结一下。 ### Member types + +| 成员类型 | 说明 | +| -------------------- | ------------------------------------------------------ | +| result_type | 返回类型 | +| argument_type | 如果函数对象只有一个参数,那么这个代表参数类型。 | +| first_argument_type | 如果函数对象有两个个参数,那么这个代表第一个参数类型。 | +| second_argument_type | 如果函数对象有两个个参数,那么这个代表第二个参数类型。 | ### Member functions + +| 成员函数声明 | 说明 | +| ------------- | ------------------------------------------------- | +| constructor | 构造函数:constructs a new std::function instance | +| destructor | 析构函数: destroys a std::function instance | +| operator= | 给定义的function对象赋值 | +| operator bool | 检查定义的function对象是否包含一个有效的对象 | +| operator() | 调用一个对象 | ## std::function使用 封装普通函数例子: ```c +#include #include #include #include #include #include #include #include #include using namespace std;typedef std::function Functional;int TestFunc(int a) { return a; }int main(){ Functional obj = TestFunc; int res = obj(1); std::cout << res << std::endl; while(1); return 0;} +``` 封装lambda表达式 : ```c +#include #include #include #include #include #include #include #include #include using namespace std;typedef std::function Functional;auto lambda = [](int a)->int{return a;};int main(){ Functional obj = lambda; res = obj(2); std::cout << res << std::endl; while(1); return 0;} +``` 封装仿函数: ```c +#include #include #include #include #include #include #include #include #include using namespace std;typedef std::function Functional;class Functor{public: int operator()(int a) { return a; }};int main(){ Functor func; Functional obj = func; res = obj(3); std::cout << res << std::endl; while(1); return 0;} +``` 封装类的成员函数和static成员函数 : ```c +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +typedef std::function Functional; + +class CTest +{ +public: + int Func(int a) + { + return a; + } + static int SFunc(int a) + { + return a; + } +}; + +int main() +{ + CTest t; + obj = std::bind(&CTest::Func, &t, std::placeholders::_1); + res = obj(3); + cout << "member function : " << res << endl; + + obj = CTest::SFunc; + res = obj(4); + cout << "static member function : " << res << endl; + + while(1); + return 0; +} +``` 关于可调用实体转换为std::function对象需要遵守以下两条原则: 1. 转换后的std::function对象的参数能转换为可调用实体的参数; +2. 可调用实体的返回值能转换为std::function对象的返回值。 **std::function对象最大的用处就是在实现函数回调**,使用者需要注意,**它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。** **为什么要用std::function?** 好用并实用的东西才会加入标准的。因为好用,实用,我们才在项目中使用它。std::function实现了一套类型消除机制,可以统一处理不同的函数对象类型。以前我们使用函数指针来完成这些;现在我们可以使用更安全的std::function来完成这些任务。 参考文档: + + + +[C++ std::function技术浅谈](https://blog.csdn.net/xiangbaohui/article/details/106741654) + +# + +# 右值引用和move语义 先看一个简单的例子直观感受下: ```c +string a(x); // line 1 +string b(x + y); // line 2 +string c(some_function_returning_a_string()); // line 3 +``` 如果使用以下拷贝构造函数: ```c +string(const string& that) +{ + size_t size = strlen(that.data) + 1; + data = new char[size]; + memcpy(data, that.data, size); +} +``` 以上3行中,只有第一行(line 1)的x深度拷贝是有必要的,因为我们可能会在后边用到x,x是一个左值(lvalues)。 第二行和第三行的参数则是右值,因为表达式产生的string对象是匿名对象,之后没有办法再使用了。 C++ 11引入了一种新的机制叫做“右值引用”,以便我们通过重载直接使用右值参数。我们所要做的就是写一个以右值引用为参数的构造函数: ```c +string(string&& that) // string&& is an rvalue reference to a string +{ +data = that.data; +that.data = 0; +} +``` 我们没有深度拷贝堆内存中的数据,而是仅仅复制了指针,并把源对象的指针置空。事实上,我们“偷取”了属于源对象的内存数据。由于源对象是一个右值,不会再被使用,因此客户并不会觉察到源对象被改变了。在这里,我们并没有真正的复制,所以我们把这个构造函数叫做“转移构造函数”(move constructor),他的工作就是把资源从一个对象转移到另一个对象,而不是复制他们。 有了右值引用,再来看看赋值操作符: ```c +string& operator=(string that) +{ +std::swap(data, that.data); +return *this; +} +``` 注意到我们是直接对参数that传值,所以that会像其他任何对象一样被初始化,那么确切的说,that是怎样被初始化的呢?对于C++ 98,答案是复制构造函数,**但是对于C++ 11,编译器会依据参数是左值还是右值在复制构造函数和转移构造函数间进行选择。** 如果是a=b,这样就会调用复制构造函数来初始化that(因为b是左值),赋值操作符会与新创建的对象交换数据,深度拷贝。这就是copy and swap 惯用法的定义:构造一个副本,与副本交换数据,并让副本在作用域内自动销毁。这里也一样。 如果是a = x + y,这样就会调用转移构造函数来初始化that(因为x+y是右值),所以这里没有深度拷贝,只有高效的数据转移。相对于参数,that依然是一个独立的对象,但是他的构造函数是无用的(trivial),因此堆中的数据没有必要复制,而仅仅是转移。没有必要复制他,因为x+y是右值,再次,从右值指向的对象中转移是没有问题的。 总结一下:复制构造函数执行的是深度拷贝,因为源对象本身必须不能被改变。而转移构造函数却可以复制指针,把源对象的指针置空,这种形式下,这是安全的,因为用户不可能再使用这个对象了。 下面我们进一步讨论右值引用和move语义。 C98标准库中提供了一种唯一拥有性的智能指针`std::auto_ptr`,**该类型在C11中已被废弃**,因为其“复制”行为是危险的。 ```c +auto_ptr a(new Triangle);auto_ptr b(a); +``` 注意b是怎样使用a进行初始化的,它不复制triangle,而是把triangle的所有权从a传递给了b,也可以说成“a 被转移进了b”或者“triangle被从a转移到了b”。 auto_ptr 的复制构造函数可能看起来像这样(简化): ```c +auto_ptr(auto_ptr& source) // note the missing const +{ +p = source.p; +source.p = 0; // now the source no longer owns the object +} +``` auto_ptr 的危险之处在于看上去应该是复制,但实际上确是转移。调用被转移过的auto_ptr 的成员函数将会导致不可预知的后果。所以你必须非常谨慎的使用auto_ptr ,如果他被转移过。 ```c +auto_ptr make_triangle() +{ + return auto_ptr(new Triangle); +} + +auto_ptr c(make_triangle()); // move temporary into c +double area = make_triangle()->area(); // perfectly safe + +auto_ptr a(new Triangle); // create triangle +auto_ptr b(a); // move a into b +double area = a->area(); // undefined behavior +``` 显然,在持有auto_ptr 对象的a表达式和持有调用函数返回的auto_ptr值类型的make_triangle()表达式之间一定有一些潜在的区别,每调用一次后者就会创建一个新的auto_ptr对象。这里a 其实就是一个左值(lvalue)的例子,而make_triangle()就是右值(rvalue)的例子。 转移像a这样的左值是非常危险的,因为我们可能调用a的成员函数,这会导致不可预知的行为。另一方面,转移像make_triangle()这样的右值却是非常安全的,因为复制构造函数之后,我们不能再使用这个临时对象了,因为这个转移后的临时对象会在下一行之前销毁掉。 我们现在知道转移左值是十分危险的,但是转移右值却是很安全的。如果C++能从语言级别支持区分左值和右值参数,我就可以完全杜绝对左值转移,或者把转移左值在调用的时候暴露出来,以使我们不会不经意的转移左值。 C++ 11对这个问题的答案是右值引用。右值引用是针对右值的新的引用类型,语法是X&&。以前的老的引用类型X& 现在被称作左值引用。 使用右值引用X&&作为参数的最有用的函数之一就是转移构造函数X::X(X&& source),它的主要作用是把源对象的本地资源转移给当前对象。 C++ 11中,std::auto_ptr< T >已经被std::unique_ptr< T >所取代,后者就是利用的右值引用。 其转移构造函数: ```c +unique_ptr(unique_ptr&& source) // note the rvalue reference +{ + ptr = source.ptr; + source.ptr = nullptr; +} +``` 这个转移构造函数跟auto_ptr中复制构造函数做的事情一样,但是它却只能接受右值作为参数。 ```c +unique_ptr a(new Triangle); +unique_ptr b(a); // error +unique_ptr c(make_triangle()); // okay +``` 第二行不能编译通过,因为a是左值,但是参数unique_ptr&& source只能接受右值,这正是我们所需要的,杜绝危险的隐式转移。第三行编译没有问题,因为make_triangle()是右值,转移构造函数会将临时对象的所有权转移给对象c,这正是我们需要的。 ## 转移左值 有时候,我们可能想转移左值,也就是说,有时候我们想让编译器把左值当作右值对待,以便能使用转移构造函数,即便这有点不安全。出于这个目的,C++ 11在标准库的头文件< utility >中提供了一个模板函数std::move。实际上,std::move仅仅是简单地将左值转换为右值,它本身并没有转移任何东西。它仅仅是让对象可以转移。 以下是如何正确的转移左值: ```c +unique_ptr a(new Triangle); +unique_ptr b(a); // still an error +unique_ptr c(std::move(a)); // okay +``` 请注意,第三行之后,a不再拥有Triangle对象。不过这没有关系,因为通过明确的写出std::move(a),我们很清楚我们的意图:亲爱的转移构造函数,你可以对a做任何想要做的事情来初始化c;我不再需要a了,对于a,您请自便。 当然,如果你在使用了mova(a)之后,还继续使用a,那无疑是搬起石头砸自己的脚,还是会导致严重的运行错误。 总之,std::move(some_lvalue)将左值转换为右值(可以理解为一种类型转换),使接下来的转移成为可能。 一个例子: ```c +class Foo +{ + unique_ptr member; + +public: + Foo(unique_ptr&& parameter) + : member(parameter) // error + {} + +}; +``` 上面的parameter,其类型是一个右值引用,只能说明parameter是指向右值的引用,而parameter本身是个左值。(Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.) 因此以上对parameter的转移是不允许的,需要使用`std::move`来显示转换成右值。 diff --git "a/C++14\346\226\260\347\211\271\346\200\247\347\232\204\346\211\200\346\234\211\347\237\245\350\257\206\347\202\271\345\205\250\345\234\250\350\277\231\345\204\277\345\225\246\357\274\201.md" "b/C++14\346\226\260\347\211\271\346\200\247\347\232\204\346\211\200\346\234\211\347\237\245\350\257\206\347\202\271\345\205\250\345\234\250\350\277\231\345\204\277\345\225\246\357\274\201.md" new file mode 100644 index 0000000000000000000000000000000000000000..3a104c6b9c206c33a9c3f325a34b051e107f95e0 --- /dev/null +++ "b/C++14\346\226\260\347\211\271\346\200\247\347\232\204\346\211\200\346\234\211\347\237\245\350\257\206\347\202\271\345\205\250\345\234\250\350\277\231\345\204\277\345\225\246\357\274\201.md" @@ -0,0 +1,357 @@ +> 本文转自:https://mp.weixin.qq.com/s?__biz=MzkyODE5NjU2Mw==&mid=2247484757&idx=1&sn=523a55fcf636113e678f1de297ec4691&chksm=c21d37e9f56abeffca414c0d43d55b36396956735e3cff1c13e1a229a3d21336371a54da84ce&scene=21#wechat_redirect + +**「函数返回值类型推导」** + +C++14对函数返回类型推导规则做了优化,先看一段代码: + +```cpp +#include + +using namespace std; + +auto func(int i) { + return i; +} + +int main() { + cout << func(4) << endl; + return 0; +} +``` + +使用C++11编译: + +```cpp +~/test$ g++ test.cc -std=c++11 +test.cc:5:16: error: ‘func’ function uses ‘auto’ type specifier without trailing return type +auto func(int i) { + ^ +test.cc:5:16: note: deduced return type only available with -std=c++14 or -std=gnu++14 +``` + +上面的代码使用C++11是不能通过编译的,通过编译器输出的信息也可以看见这个特性需要到C++14才被支持。 + +返回值类型推导也可以用在模板中: + +```cpp +#include +using namespace std; + +template auto func(T t) { return t; } + +int main() { + cout << func(4) << endl; + cout << func(3.4) << endl; + return 0; +} +``` + +**注意**: + +**函数内如果有多个return语句,它们必须返回相同的类型,否则编译失败** + +```cpp +auto func(bool flag) { + if (flag) return 1; + else return 2.3; // error +} +// inconsistent deduction for auto return type: ‘int’ and then ‘double’ +``` + +**如果return语句返回初始化列表,返回值类型推导也会失败** + +```cpp +auto func() { + return {1, 2, 3}; // error returning initializer list +} +``` + +**如果函数是虚函数,不能使用返回值类型推导** + +```cpp +struct A { +// error: virtual function cannot have deduced return type +virtual auto func() { return 1; } +} +``` + +**返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义** + +```cpp +auto f(); // declared, not yet defined +auto f() { return 42; } // defined, return type is int + +int main() { +cout << f() << endl; +} +``` + +**返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。** + +```cpp +auto sum(int i) { + if (i == 1) + return i; // return int + else + return sum(i - 1) + i; // ok +} +``` + +**lambda参数auto** + +在C++11中,lambda表达式参数需要使用具体的类型声明: + +```cpp +auto f = [] (int a) { return a; } +``` + +在C++14中,对此进行优化,lambda表达式参数可以直接是auto: + +```cpp +auto f = [] (auto a) { return a; }; +cout << f(1) << endl; +cout << f(2.3f) << endl; +``` + +**变量模板** + +C++14支持变量模板: + +```cpp +template +constexpr T pi = T(3.1415926535897932385L); + +int main() { + cout << pi << endl; // 3 + cout << pi << endl; // 3.14159 + return 0; +} +``` + +**别名模板** + +C++14也支持别名模板: + +```cpp +template +struct A { + T t; + U u; +}; + +template +using B = A; + +int main() { + B b; + b.t = 10; + b.u = 20; + cout << b.t << endl; + cout << b.u << endl; + return 0; +} +``` + +**constexpr的限制** + +C++14相较于C++11对constexpr减少了一些限制: + +**C++11中constexpr函数可以使用递归,在C++14中可以使用局部变量和循环** + +```cpp +constexpr int factorial(int n) { // C++14 和 C++11均可 + return n <= 1 ? 1 : (n * factorial(n - 1)); +} +``` + +在C++14中可以这样做: + +```cpp +constexpr int factorial(int n) { // C++11中不可,C++14中可以 + int ret = 0; + for (int i = 0; i < n; ++i) { + ret += i; + } + return ret; +} +``` + +**C++11中constexpr函数必须必须把所有东西都放在一个单独的return语句中,而constexpr则无此限制** + +```cpp +constexpr int func(bool flag) { // C++14 和 C++11均可 + return 0; +} +``` + +在C++14中可以这样: + +```cpp +constexpr int func(bool flag) { // C++11中不可,C++14中可以 + if (flag) return 1; + else return 0; +} +``` + +**[[deprecated]]标记** + +C++14中增加了deprecated标记,修饰类、变、函数等,当程序中使用到了被其修饰的代码时,编译时被产生警告,用户提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用。 + +```cpp +struct [[deprecated]] A { }; + +int main() { + A a; + return 0; +} +``` + +当编译时,会出现如下警告: + +```cpp +~/test$ g++ test.cc -std=c++14 +test.cc: In function ‘int main()’: +test.cc:11:7: warning: ‘A’ is deprecated [-Wdeprecated-declarations] + A a; + ^ +test.cc:6:23: note: declared here + struct [[deprecated]] A { +``` + +**二进制字面量与整形字面量分隔符** + +C++14引入了二进制字面量,也引入了分隔符,防止看起来眼花哈~ + +```cpp +int a = 0b0001'0011'1010; +double b = 3.14'1234'1234'1234; +``` + +**std::make_unique** + +我们都知道C++11中有std::make_shared,却没有std::make_unique,在C++14已经改善。 + +```cpp +struct A {}; +std::unique_ptr ptr = std::make_unique(); +``` + +**std::shared_timed_mutex与std::shared_lock** + +C++14通过std::shared_timed_mutex和std::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不可以同时和读操作一起进行。 + +实现方式如下: + +```cpp +struct ThreadSafe { + mutable std::shared_timed_mutex mutex_; + int value_; + + ThreadSafe() { + value_ = 0; + } + + int get() const { + std::shared_lock loc(mutex_); + return value_; + } + + void increase() { + std::unique_lock lock(mutex_); + value_ += 1; + } +}; +``` + +为什么是timed的锁呢,因为可以带超时时间,具体可以自行查询相关资料哈,网上有很多。 + +**std::integer_sequence** + +```cpp +template +void print_sequence(std::integer_sequence int_seq) +{ + std::cout << "The sequence of size " << int_seq.size() << ": "; + ((std::cout << ints << ' '), ...); + std::cout << '\n'; +} + +int main() { + print_sequence(std::integer_sequence{}); + return 0; +} + +输出:7 9 2 5 1 9 1 6 +``` + +std::integer_sequence和std::tuple的配合使用: + +```cpp +template +auto map_filter_tuple(F f, T& t) { + return std::make_tuple(f(std::get(t))...); +} + +template +auto map_filter_tuple(std::index_sequence, F f, T& t) { + return std::make_tuple(f(std::get(t))...); +} + +template +auto map_filter_tuple(F&& f, T& t) { + return map_filter_tuple(S{}, std::forward(f), t); +} +``` + +**std::exchange** + +直接看代码吧: + +```cpp +int main() { + std::vector v; + std::exchange(v, {1,2,3,4}); + cout << v.size() << endl; + for (int a : v) { + cout << a << " "; + } + return 0; +} +``` + +看样子貌似和std::swap作用相同,那它俩有什么区别呢? + +可以看下exchange的实现: + +```cpp +template +constexpr T exchange(T& obj, U&& new_value) { + T old_value = std::move(obj); + obj = std::forward(new_value); + return old_value; +} +``` + +可以看见new_value的值给了obj,而没有对new_value赋值,这里相信您已经知道了它和swap的区别了吧! + +**std::quoted** + +C++14引入std::quoted用于给字符串添加双引号,直接看代码: + +```cpp +int main() { + string str = "hello world"; + cout << str << endl; + cout << std::quoted(str) << endl; + return 0; +} +``` + +编译&输出: + +```cpp +~/test$ g++ test.cc -std=c++14 +~/test$ ./a.out +hello world +"hello world" +``` diff --git "a/C++\345\255\246\344\271\240\347\254\224\350\256\260.md" "b/C++\345\255\246\344\271\240\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..89fee24384fbf0de62399df4c09b69dceaf39210 --- /dev/null +++ "b/C++\345\255\246\344\271\240\347\254\224\350\256\260.md" @@ -0,0 +1,1278 @@ +# C++学习笔记 + +## 一、基础知识 + +## [📎C++基础入门.md](https://github.com/0voice/cpp_new_features/blob/main/C%2B%2B%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8.md) + +### 1、goto 语句(不建议使用) + +**作用:** 可以无条件的跳转语句 + +**语法:** `goto 标记;` + +**解释:** 如果标记的名称存在,执行到goto语句时,会跳转到标记的位置 + +```cpp +int main() { + cout << 1 << endl; + cout << 2 << endl; + goto FLAG; + cout << 3 << endl; + cout << 4 << endl; + FLAG: + cout << 5 << endl; + return 0; +} +``` + +### 2、一维数组 + +一维数组名称的**用途** + +1. 可以统计整个数组在内存中的长度 +2. 可以获取数组在内存中的首地址 **示例:** + +```cpp +int main() { + + //数组名用途 + //1、可以获取整个数组占用内存空间大小 + int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; + + cout << "整个数组所占内存空间为: " << sizeof(arr) << endl; + cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl; + cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl; + + //2、可以通过数组名获取到数组首地址 + cout << "数组首地址为: " << (int)arr << endl; + cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl; + cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl; + + //arr = 100; 错误,数组名是常量,因此不可以赋值 + + + system("pause"); + + return 0; +} +``` + +注意:数组名是常量,不可以赋值 + +总结1:直接打印数组名,可以查看数组所占内存的首地址 + +总结2:对数组名进行sizeof,可以获取整个数组占内存空间的大小 ### 3、二维数组 二维数组定义的四种方式: + +1. `数据类型 数组名[ 行数 ][ 列数 ];` +2. `数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };` +3. `数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};` +4. `数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};` + +- 查看二维数组所占内存空间 +- 获取二维数组首地址 + +```cpp +int main() { + + //二维数组数组名 + int arr[2][3] = + { + {1,2,3}, + {4,5,6} + }; + + cout << "二维数组大小: " << sizeof(arr) << endl; + cout << "二维数组一行大小: " << sizeof(arr[0]) << endl; + cout << "二维数组元素大小: " << sizeof(arr[0][0]) << endl; + + cout << "二维数组行数: " << sizeof(arr) / sizeof(arr[0]) << endl; + cout << "二维数组列数: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl; + + //地址 + cout << "二维数组首地址:" << arr << endl; + cout << "二维数组第一行地址:" << arr[0] << endl; + cout << "二维数组第二行地址:" << arr[1] << endl; + + cout << "二维数组第一个元素地址:" << &arr[0][0] << endl; + cout << "二维数组第二个元素地址:" << &arr[0][1] << endl; + + system("pause"); + + return 0; +} +``` + +总结1:二维数组名就是这个数组的首地址 + +总结2:对二维数组名进行sizeof时,可以获取整个二维数组占用的内存空间大小 ### 4、函数中的值传递 + +- 所谓值传递,就是函数调用时实参将数值传入给形参 +- 值传递时,如果形参发生,并不会影响实参(注意:和Java中不同) + +```cpp +void swap(int num1, int num2) +{ + cout << "交换前:" << endl; + cout << "num1 = " << num1 << endl; + cout << "num2 = " << num2 << endl; + + int temp = num1; + num1 = num2; + num2 = temp; + + cout << "交换后:" << endl; + cout << "num1 = " << num1 << endl; + cout << "num2 = " << num2 << endl; + + //return ; 当函数声明时候,不需要返回值,可以不写return +} + +int main() { + + int a = 10; + int b = 20; + + swap(a, b); + + cout << "mian中的 a = " << a << endl; + cout << "mian中的 b = " << b << endl; + + system("pause"); + + return 0; +} +``` + +总结: 值传递时,形参是修饰不了实参的 ### 5、函数的声明 **作用:** 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。 + +- 函数的**声明可以多次**,但是函数的**定义只能有一次** **(在C中,如果不自定义函数在main方法之后定义,如果main方法调用了此函数,则main方法找不到此函数,因此需要提前声明)** + +和Java中不同 ### 6、函数的分文件编写 **作用:** 让代码结构更加清晰 + +函数分文件编写一般有4个步骤 + +1. 创建后缀名为.h的头文件 +2. 创建后缀名为.cpp的源文件 + +1. 在头文件中写函数的声明 +2. 在源文件中写函数的定义 **示例:** + +```cpp +//swap.h文件 +#include +using namespace std; + +//实现两个数字交换的函数声明 +void swap(int a, int b); +//swap.cpp文件 +#include "swap.h" + +void swap(int a, int b) +{ + int temp = a; + a = b; + b = temp; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; +} +//main函数文件 +#include "swap.h" +int main() { + + int a = 100; + int b = 200; + swap(a, b); + + system("pause"); + + return 0; +} +``` ### 7、指针 #### 7.1 指针的基本概念 + +**指针的作用:** 可以通过指针间接访问内存 + +- 内存编号是从0开始记录的,一般用十六进制数字表示 +- 可以利用指针变量保存地址 #### 7.2 指针变量的定义和使用 + +指针变量定义语法: `数据类型 * 变量名;` + +**示例:** + +```cpp +int main() { + + //1、指针的定义 + int a = 10; //定义整型变量a + + //指针定义语法: 数据类型 * 变量名 ; + int * p; + + //指针变量赋值 + p = &a; //指针指向变量a的地址 + cout << &a << endl; //打印数据a的地址 + cout << p << endl; //打印指针变量p + + //2、指针的使用 + //通过*操作指针变量指向的内存 + cout << "*p = " << *p << endl; + + system("pause"); + + return 0; +} +``` + +指针变量和普通变量的区别 + +- 普通变量存放的是数据,指针变量存放的是地址 +- 指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用 + +总结1: 我们可以通过 & 符号 获取变量的地址 + +总结2:利用指针可以记录地址 + +总结3:对指针变量解引用,可以操作指针指向的内存 #### 7.3 指针所占内存空间 + +提问:指针也是种数据类型,那么这种数据类型占用多少内存空间? + +**示例:** + +```cpp +int main() { + + int a = 10; + + int * p; + p = &a; //指针指向数据a的地址 + + cout << *p << endl; //* 解引用 + cout << sizeof(p) << endl; + cout << sizeof(char *) << endl; + cout << sizeof(float *) << endl; + cout << sizeof(double *) << endl; + + system("pause"); + + return 0; +} +``` + +总结:所有指针类型在32位操作系统下是4个字节,64位操作系统为8个字节 + +#### 7.4 空指针和野指针 + +**空指针**:指针变量指向内存中编号为0的空间 + +**用途:** 初始化指针变量 + +**注意:** 空指针指向的内存是不可以访问的 + + +**示例1:空指针** + +```cpp +int main() { + + //指针变量p指向内存地址编号为0的空间 + int * p = NULL; + + //访问空指针报错 + //内存编号0 ~255为系统占用内存,不允许用户访问 + cout << *p << endl; + + system("pause"); + + return 0; +} +``` **野指针**:指针变量指向非法的内存空间 + +**示例2:野指针** + +```cpp +int main() { + + //指针变量p指向内存地址编号为0x1100的空间 + int * p = (int *)0x1100; + + //访问野指针报错 + cout << *p << endl; + + system("pause"); + + return 0; +} +``` + +总结:空指针和野指针都不是我们申请的空间,因此不要访问。 + + +#### 7.5 const修饰指针 + + +const修饰指针有三种情况 + +1. const修饰指针 --- 常量指针 +2. const修饰常量 --- 指针常量 + +1. const既修饰指针,又修饰常量 + + +**示例:** + +```cpp +int main() { + + int a = 10; + int b = 10; + + //const修饰的是指针,指针指向可以改,指针指向的值不可以更改 + const int * p1 = &a; + p1 = &b; //正确 + //*p1 = 100; 报错 + + + //const修饰的是常量,指针指向不可以改,指针指向的值可以更改 + int * const p2 = &a; + //p2 = &b; //错误 + *p2 = 100; //正确 + + //const既修饰指针又修饰常量 + const int * const p3 = &a; + //p3 = &b; //错误 + //*p3 = 100; //错误 + + system("pause"); + + return 0; +} +``` + +技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量 #### 7.6 指针和数组 **作用:** 利用指针访问数组中元素 + +**示例:** + +```cpp +int main() { + + int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; + + int * p = arr; //指向数组的指针 + + cout << "第一个元素: " << arr[0] << endl; + cout << "指针访问第一个元素: " << *p << endl; + + for (int i = 0; i < 10; i++) + { + //利用指针遍历数组 + cout << *p << endl; + p++; + } + + system("pause"); + + return 0; +} +``` #### 7.7 指针和函数 + +**作用:** 利用指针作函数参数,可以修改实参的值(和前边形参相反) **示例:** + +```cpp +//值传递 +void swap1(int a ,int b) +{ + int temp = a; + a = b; + b = temp; +} +//地址传递 +void swap2(int * p1, int *p2) +{ + int temp = *p1; + *p1 = *p2; + *p2 = temp; +} + +int main() { + + int a = 10; + int b = 20; + swap1(a, b); // 值传递不会改变实参 + + swap2(&a, &b); //地址传递会改变实参 + + cout << "a = " << a << endl; + + cout << "b = " << b << endl; + + system("pause"); + + return 0; +} +``` + +总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递 #### 7.8 指针、数组、函数 **案例描述:** 封装一个函数,利用冒泡排序,实现对整型数组的升序排序 + +例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 }; **示例:** + +```cpp +//冒泡排序函数 +void bubbleSort(int * arr, int len) //int * arr 也可以写为int arr[] +{ + for (int i = 0; i < len - 1; i++) + { + for (int j = 0; j < len - 1 - i; j++) + { + if (arr[j] > arr[j + 1]) + { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + +//打印数组函数 +void printArray(int arr[], int len) +{ + for (int i = 0; i < len; i++) + { + cout << arr[i] << endl; + } +} + +int main() { + + int arr[10] = { 4,3,6,9,1,2,10,8,7,5 }; + int len = sizeof(arr) / sizeof(int); + + bubbleSort(arr, len); + + printArray(arr, len); + + system("pause"); + + return 0; +} +``` + +### 8、结构体 #### 8.1 结构体基本概念 + +结构体属于用户 自定义的数据类型,允许用户存储不同的数据类型 + +(跟Java中的对象比较像,但是只有属性,没有方法。下面其他的结构体使用,都可以类比Java中对象的使用) #### 8.2 结构体定义和使用 + +**语法:** `struct 结构体名 { 结构体成员列表 };` + +通过结构体创建变量的方式有三种: + +- struct 结构体名 变量名 +- struct 结构体名 变量名 = { 成员1值 , 成员2值...} + +- 定义结构体时顺便创建变量 +- + +**示例:** + +```cpp +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}stu3; //结构体变量创建方式3 + + +int main() { + + //结构体变量创建方式1 + struct student stu1; //struct 关键字可以省略 + + stu1.name = "张三"; + stu1.age = 18; + stu1.score = 100; + + cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << " 分数:" << stu1.score << endl; + + //结构体变量创建方式2 + struct student stu2 = { "李四",19,60 }; + + cout << "姓名:" << stu2.name << " 年龄:" << stu2.age << " 分数:" << stu2.score << endl; + + + stu3.name = "王五"; + stu3.age = 18; + stu3.score = 80; + + + cout << "姓名:" << stu3.name << " 年龄:" << stu3.age << " 分数:" << stu3.score << endl; + + system("pause"); + + return 0; +} +``` + +总结1:定义结构体时的关键字是struct,不可省略 + +总结2:创建结构体变量时,关键字struct可以省略 + +总结3:结构体变量利用操作符 ''.'' 访问成员 #### 8.3 结构体数组 + +**作用:** 将自定义的结构体放入到数组中方便维护 + +**语法:** `struct 结构体名 数组名[元素个数] = { {} , {} , ... {} }` **示例:** + +```cpp +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +} + +int main() { + + //结构体数组 + struct student arr[3]= + { + {"张三",18,80 }, + {"李四",19,60 }, + {"王五",20,70 } + }; + + for (int i = 0; i < 3; i++) + { + cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl; + } + + system("pause"); + + return 0; +} +``` #### 8.4 结构体指针 + +**作用:** 通过指针访问结构体中的成员 + +- 利用操作符 `->`可以通过结构体指针访问结构体属性 **示例:** + +```cpp +//结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + + +int main() { + + struct student stu = { "张三",18,100, }; + + struct student * p = &stu; + + p->score = 80; //指针通过 -> 操作符可以访问成员 + + cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl; + + system("pause"); + + return 0; +} +``` + +总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员 #### 8.5 结构体嵌套结构体 + +**作用:** 结构体中的成员可以是另一个结构体 + +**例如:** 每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体 + +**示例:** + +```cpp +//学生结构体定义struct student{ //成员列表 string name; //姓名 int age; //年龄 int score; //分数};//教师结构体定义struct teacher{ //成员列表 int id; //职工编号 string name; //教师姓名 int age; //教师年龄 struct student stu; //子结构体 学生};int main() { struct teacher t1; t1.id = 10000; t1.name = "老王"; t1.age = 40; t1.stu.name = "张三"; t1.stu.age = 18; t1.stu.score = 100; cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl; cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl; system("pause"); return 0;} +``` + +**总结:** 在结构体中可以定义另一个结构体作为成员,用来解决实际问题 + +#### 8.6 结构体做函数参数 + +**作用:** 将结构体作为参数向函数中传递 + +传递方式有两种: + +- 值传递 +- 地址传递 + +**示例:** + +```cpp +//学生结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + +//值传递 +void printStudent(student stu ) +{ + stu.age = 28; + cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; +} + +//地址传递 +void printStudent2(student *stu) +{ + stu->age = 28; + cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age << " 分数:" << stu->score << endl; +} + +int main() { + + student stu = { "张三",18,100}; + //值传递 + printStudent(stu); + cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; + + cout << endl; + + //地址传递 + printStudent2(&stu); + cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl; + + system("pause"); + + return 0; +} +``` + +总结:如果不想修改主函数中的数据,用值传递,反之用地址传递 #### 8.7 结构体中 const使用场景 + +**作用:** 用const来防止误操作 + +**示例:** + +```cpp +//学生结构体定义 +struct student +{ + //成员列表 + string name; //姓名 + int age; //年龄 + int score; //分数 +}; + +//const使用场景 +void printStudent(const student *stu) //加const防止函数体中的误操作 +{ + //stu->age = 100; //操作失败,因为加了const修饰 + cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl; + +} + +int main() { + + student stu = { "张三",18,100 }; + + printStudent(&stu); + + system("pause"); + + return 0; +} +``` + +## 二、核心编程 + +C++中的面向对象编程(笑) + +## [📎C++核心编程.md](https://github.com/0voice/cpp_new_features/blob/main/C%2B%2B%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B.md) ### 1、内存分区模型 + +C++程序在执行时,将内存大方向划分为**4个区域** + +- 代码区:存放函数体的二进制代码,由操作系统进行管理的 +- 全局区:存放全局变量和静态变量以及常量 + +- 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等 +- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收 **内存四区意义:** + +不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程 + + +#### 1.1 程序运行前 + +在程序编译后,生成了exe可执行程序,**未执行该程序前**分为两个区域 + +- 代码区: + +- - 存放 CPU 执行的机器指令 + - 代码区是**共享**的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可 + +- - 代码区是**只读**的,使其只读的原因是防止程序意外地修改了它的指令 + +- 全局区: + +- - 全局变量和静态变量存放在此 + - 全局区还包含了常量区, 字符串常量和其他常量也存放在此 + +- - 该区域的数据在程序结束后由操作系统释放 **示例:** + +```cpp +//全局变量 +int g_a = 10; +int g_b = 10; + +//全局常量 +const int c_g_a = 10; +const int c_g_b = 10; + +int main() { + + //局部变量 + int a = 10; + int b = 10; + + //打印地址 + cout << "局部变量a地址为: " << (int)&a << endl; + cout << "局部变量b地址为: " << (int)&b << endl; + + cout << "全局变量g_a地址为: " << (int)&g_a << endl; + cout << "全局变量g_b地址为: " << (int)&g_b << endl; + + //静态变量 + static int s_a = 10; + static int s_b = 10; + + cout << "静态变量s_a地址为: " << (int)&s_a << endl; + cout << "静态变量s_b地址为: " << (int)&s_b << endl; + + cout << "字符串常量地址为: " << (int)&"hello world" << endl; + cout << "字符串常量地址为: " << (int)&"hello world1" << endl; + + cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl; + cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl; + + const int c_l_a = 10; + const int c_l_b = 10; + cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl; + cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl; + + system("pause"); + + return 0; +} +``` + +![img](https://cdn.nlark.com/yuque/0/2021/png/2797977/1627450152945-bd009327-0c74-498d-8950-d300ec297b88.png) + +总结: + +- C++中在程序运行前分为全局区和代码区 +- 代码区特点是共享和只读 + +- 全局区中存放全局变量、静态变量、常量 +- 常量区中存放 const修饰的全局常量 和 字符串常量 + +#### 1.2 程序运行后 + +**栈区:** + +- 由编译器自动分配释放, 存放函数的参数值,局部变量等 +- 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放 **示例:** + +```cpp +int * func() +{ + int a = 10; + return &a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + system("pause"); + + return 0; +} +``` + + +**堆区:** + +- 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收 +- 在C++中主要利用new在堆区开辟内存 + + +**示例:** + +```cpp +int* func() +{ + int* a = new int(10); + return a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + system("pause"); + + return 0; +} +``` **总结:** + +- 堆区数据由程序员管理开辟和释放 +- 堆区数据利用new关键字进行开辟内存 + +#### 1.3 new操作符 + +- C++中利用 new 操作符在堆区开辟数据 +- 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 delete + +- 语法:`new 数据类型` +- 利用new创建的数据,会返回该数据对应的类型的指针 **示例1: 基本语法** + +```cpp +int* func() +{ + int* a = new int(10); + return a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + //利用delete释放堆区数据 + delete p; + + //cout << *p << endl; //报错,释放的空间不可访问 + + system("pause"); + + return 0; +} +``` **示例2:开辟数组** + +```cpp +//堆区开辟数组 +int main() { + + int* arr = new int[10]; + + for (int i = 0; i < 10; i++) + { + arr[i] = i + 100; + } + + for (int i = 0; i < 10; i++) + { + cout << arr[i] << endl; + } + //释放数组 delete 后加 [] + delete[] arr; + + system("pause"); + + return 0; +} +``` ### 2、引用 + + +#### 2.1 引用的基本使用 + +**作用:** 给变量起别名 + +**语法:** `数据类型 &别名 = 原名` + +**示例:** + +```cpp +int main() { + + int a = 10; + int &b = a; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + + b = 100; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + + system("pause"); + + return 0; +} +``` #### 2.2 引用注意事项 + +- 引用必须初始化 +- 引用在初始化后,不可以改变 + + +**示例:** + +```cpp +int main() { + + int a = 10; + int b = 20; + //int &c; //错误,引用必须初始化 + int &c = a; //一旦初始化后,就不可以更改 + c = b; //这是赋值操作,不是更改引用 + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + cout << "c = " << c << endl; + + system("pause"); + + return 0; +} +``` + +#### 2.3 引用做函数参数 + +**作用:** 函数传参时,可以利用引用的技术让形参修饰实参 + +**优点:** 可以简化指针修改实参 + +**示例:** + +```cpp +//1. 值传递 +void mySwap01(int a, int b) { + int temp = a; + a = b; + b = temp; +} + +//2. 地址传递 +void mySwap02(int* a, int* b) { + int temp = *a; + *a = *b; + *b = temp; +} + +//3. 引用传递 +void mySwap03(int& a, int& b) { + int temp = a; + a = b; + b = temp; +} + +int main() { + + int a = 10; + int b = 20; + + mySwap01(a, b); + cout << "a:" << a << " b:" << b << endl; + + mySwap02(&a, &b); + cout << "a:" << a << " b:" << b << endl; + + mySwap03(a, b); + cout << "a:" << a << " b:" << b << endl; + + system("pause"); + + return 0; +} +``` + +总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单 + + +#### 2.4 引用做函数返回值 + + +**作用:** 引用是可以作为函数的返回值存在的 + +**注意:** **不要返回局部变量引用** + +**用法:** 函数调用作为左值 **示例:** + +```cpp +//返回局部变量引用 +int& test01() { + int a = 10; //局部变量 + return a; +} + +//返回静态变量引用 +int& test02() { + static int a = 20; + return a; +} + +int main() { + + //不能返回局部变量的引用 + int& ref = test01(); + cout << "ref = " << ref << endl; + cout << "ref = " << ref << endl; + + //如果函数做左值,那么必须返回引用 + int& ref2 = test02(); + cout << "ref2 = " << ref2 << endl; + cout << "ref2 = " << ref2 << endl; + + test02() = 1000; + + cout << "ref2 = " << ref2 << endl; + cout << "ref2 = " << ref2 << endl; + + system("pause"); + + return 0; +} +``` + +#### 2.5 引用的本质 + +本质:**引用的本质在c++内部实现是一个指针常量.** + +讲解示例: + +```cpp +//发现是引用,转换为 int* const ref = &a; +void func(int& ref){ + ref = 100; // ref是引用,转换为*ref = 100 +} +int main(){ + int a = 10; + + //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改 + int& ref = a; + ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20; + + cout << "a:" << a << endl; + cout << "ref:" << ref << endl; + + func(a); + return 0; +} +``` + +结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了 #### 2.6 常量引用 + +**作用:** 常量引用主要用来修饰形参,防止误操作 + +在函数形参列表中,可以加const修饰形参,防止形参改变实参 + +**示例:** + +```cpp +//引用使用的场景,通常用来修饰形参 +void showValue(const int& v) { + //v += 10; + cout << v << endl; +} + +int main() { + + //int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误 + //加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp; + const int& ref = 10; + + //ref = 100; //加入const后不可以修改变量 + cout << ref << endl; + + //函数中利用常量引用防止误操作修改实参 + int a = 10; + showValue(a); + + system("pause"); + + return 0; +} +``` + +### 3、函数 + +#### 3.1 函数默认参数 + +在C++中,函数的形参列表中的形参是可以有默认值的。 + +语法:`返回值类型 函数名 (参数= 默认值){}` + +**示例:** + +```cpp +int func(int a, int b = 10, int c = 10) { + return a + b + c; +} + +//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值 +//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数 +int func2(int a = 10, int b = 10); +int func2(int a, int b) { + return a + b; +} + +int main() { + + cout << "ret = " << func(20, 20) << endl; + cout << "ret = " << func(100) << endl; + + system("pause"); + + return 0; +} +``` + +#### 3.2 函数占位参数 + +C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置 + +**语法:** `返回值类型 函数名 (数据类型){}` + +**示例:** + +```cpp +//函数占位参数 ,占位参数也可以有默认参数 +void func(int a, int) { + cout << "this is func" << endl; +} + +int main() { + + func(10,10); //占位参数必须填补 + + system("pause"); + + return 0; +} +``` + +#### 3.3 函数重载 + +##### 3.3.1 函数重载概述 + +**作用:** 函数名可以相同,提高复用性 + + +**函数重载满足条件:** + +- 同一个作用域下 +- 函数名称相同 + +- 函数参数**类型不同** 或者 **个数不同** 或者 **顺序不同** + +**注意:** 函数的返回值不可以作为函数重载的条件 + +**示例:** + +```cpp +//函数重载需要函数都在同一个作用域下 +void func() +{ + cout << "func 的调用!" << endl; +} +void func(int a) +{ + cout << "func (int a) 的调用!" << endl; +} +void func(double a) +{ + cout << "func (double a)的调用!" << endl; +} +void func(int a ,double b) +{ + cout << "func (int a ,double b) 的调用!" << endl; +} +void func(double a ,int b) +{ + cout << "func (double a ,int b)的调用!" << endl; +} + +//函数返回值不可以作为函数重载条件 +//int func(double a, int b) +//{ +// cout << "func (double a ,int b)的调用!" << endl; +//} + + +int main() { + + func(); + func(10); + func(3.14); + func(10,3.14); + func(3.14 , 10); + + system("pause"); + + return 0; +} +``` + +##### 3.3.2 函数重载注意事项 + +- 引用作为重载条件 +- 函数重载碰到函数默认参数 **示例:** + +```cpp +//函数重载注意事项 +//1、引用作为重载条件 + +void func(int &a) +{ + cout << "func (int &a) 调用 " << endl; +} + +void func(const int &a) +{ + cout << "func (const int &a) 调用 " << endl; +} + + +//2、函数重载碰到函数默认参数 + +void func2(int a, int b = 10) +{ + cout << "func2(int a, int b = 10) 调用" << endl; +} + +void func2(int a) +{ + cout << "func2(int a) 调用" << endl; +} + +int main() { + + int a = 10; + func(a); //调用无const + func(10);//调用有const + + + //func2(10); //碰到默认参数产生歧义,需要避免 + + system("pause"); + + return 0; +} +``` diff --git "a/C++\346\240\270\345\277\203\347\274\226\347\250\213.md" "b/C++\346\240\270\345\277\203\347\274\226\347\250\213.md" new file mode 100644 index 0000000000000000000000000000000000000000..25a78b1dd3118e781386677ad5a76eb480905451 --- /dev/null +++ "b/C++\346\240\270\345\277\203\347\274\226\347\250\213.md" @@ -0,0 +1,3694 @@ +# C++核心编程 + +本阶段主要针对C++==面向对象==编程技术做详细讲解,探讨C++中的核心和精髓。 ## 1 内存分区模型 + +C++程序在执行时,将内存大方向划分为**4个区域** + +- 代码区:存放函数体的二进制代码,由操作系统进行管理的 +- 全局区:存放全局变量和静态变量以及常量 +- 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等 +- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收 **内存四区意义:** + +不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程 + +### 1.1 程序运行前 + +​ 在程序编译后,生成了exe可执行程序,**未执行该程序前**分为两个区域 + +​ **代码区:** + +​ 存放 CPU 执行的机器指令 + +​ 代码区是**共享**的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可 + +​ 代码区是**只读**的,使其只读的原因是防止程序意外地修改了它的指令 + +​ **全局区:** + +​ 全局变量和静态变量存放在此. + +​ 全局区还包含了常量区, 字符串常量和其他常量也存放在此. + +​ ==该区域的数据在程序结束后由操作系统释放==. +**示例:** + +```c++ +//全局变量 +int g_a = 10; +int g_b = 10; + +//全局常量 +const int c_g_a = 10; +const int c_g_b = 10; + +int main() { + + //局部变量 + int a = 10; + int b = 10; + + //打印地址 + cout << "局部变量a地址为: " << (int)&a << endl; + cout << "局部变量b地址为: " << (int)&b << endl; + + cout << "全局变量g_a地址为: " << (int)&g_a << endl; + cout << "全局变量g_b地址为: " << (int)&g_b << endl; + + //静态变量 + static int s_a = 10; + static int s_b = 10; + + cout << "静态变量s_a地址为: " << (int)&s_a << endl; + cout << "静态变量s_b地址为: " << (int)&s_b << endl; + + cout << "字符串常量地址为: " << (int)&"hello world" << endl; + cout << "字符串常量地址为: " << (int)&"hello world1" << endl; + + cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl; + cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl; + + const int c_l_a = 10; + const int c_l_b = 10; + cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl; + cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl; + + system("pause"); + + return 0; +} +``` + +打印结果: + +![1545017602518](assets/1545017602518.png) 总结: + +* C++中在程序运行前分为全局区和代码区 +* 代码区特点是共享和只读 +* 全局区中存放全局变量、静态变量、常量 +* 常量区中存放 const修饰的全局常量 和 字符串常量 + + +### 1.2 程序运行后 ​ **栈区:** + +​ 由编译器自动分配释放, 存放函数的参数值,局部变量等 + +​ 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放 **示例:** + +```c++ +int * func() +{ + int a = 10; + return &a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + system("pause"); + + return 0; +} +``` ​ **堆区:** + +​ 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收 + +​ 在C++中主要利用new在堆区开辟内存 + +**示例:** + +```c++ +int* func() +{ + int* a = new int(10); + return a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + system("pause"); + + return 0; +} +``` **总结:** + +堆区数据由程序员管理开辟和释放 + +堆区数据利用new关键字进行开辟内存 +### 1.3 new操作符 ​ C++中利用==new==操作符在堆区开辟数据 + +​ 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 ==delete== + +​ 语法:` new 数据类型` + +​ 利用new创建的数据,会返回该数据对应的类型的指针 **示例1: 基本语法** + +```c++ +int* func() +{ + int* a = new int(10); + return a; +} + +int main() { + + int *p = func(); + + cout << *p << endl; + cout << *p << endl; + + //利用delete释放堆区数据 + delete p; + + //cout << *p << endl; //报错,释放的空间不可访问 + + system("pause"); + + return 0; +} +``` **示例2:开辟数组** + +```c++ +//堆区开辟数组 +int main() { + + int* arr = new int[10]; + + for (int i = 0; i < 10; i++) + { + arr[i] = i + 100; + } + + for (int i = 0; i < 10; i++) + { + cout << arr[i] << endl; + } + //释放数组 delete 后加 [] + delete[] arr; + + system("pause"); + + return 0; +} + +``` + + +## 2 引用 + +### 2.1 引用的基本使用 + +**作用: **给变量起别名 + +**语法:** `数据类型 &别名 = 原名` **示例:** + +```C++ +int main() { + + int a = 10; + int &b = a; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + + b = 100; + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + + system("pause"); + + return 0; +} +``` ### 2.2 引用注意事项 + +* 引用必须初始化 +* 引用在初始化后,不可以改变 + +示例: + +```C++ +int main() { + + int a = 10; + int b = 20; + //int &c; //错误,引用必须初始化 + int &c = a; //一旦初始化后,就不可以更改 + c = b; //这是赋值操作,不是更改引用 + + cout << "a = " << a << endl; + cout << "b = " << b << endl; + cout << "c = " << c << endl; + + system("pause"); + + return 0; +} +``` + + +### 2.3 引用做函数参数 + +**作用:**函数传参时,可以利用引用的技术让形参修饰实参 + +**优点:**可以简化指针修改实参 **示例:** + +```C++ +//1. 值传递 +void mySwap01(int a, int b) { + int temp = a; + a = b; + b = temp; +} + +//2. 地址传递 +void mySwap02(int* a, int* b) { + int temp = *a; + *a = *b; + *b = temp; +} + +//3. 引用传递 +void mySwap03(int& a, int& b) { + int temp = a; + a = b; + b = temp; +} + +int main() { + + int a = 10; + int b = 20; + + mySwap01(a, b); + cout << "a:" << a << " b:" << b << endl; + + mySwap02(&a, &b); + cout << "a:" << a << " b:" << b << endl; + + mySwap03(a, b); + cout << "a:" << a << " b:" << b << endl; + + system("pause"); + + return 0; +} + +``` > 总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单 +### 2.4 引用做函数返回值 作用:引用是可以作为函数的返回值存在的 注意:**不要返回局部变量引用** + +用法:函数调用作为左值 **示例:** + +```C++ +//返回局部变量引用 +int& test01() { + int a = 10; //局部变量 + return a; +} + +//返回静态变量引用 +int& test02() { + static int a = 20; + return a; +} + +int main() { + + //不能返回局部变量的引用 + int& ref = test01(); + cout << "ref = " << ref << endl; + cout << "ref = " << ref << endl; + + //如果函数做左值,那么必须返回引用 + int& ref2 = test02(); + cout << "ref2 = " << ref2 << endl; + cout << "ref2 = " << ref2 << endl; + + test02() = 1000; + + cout << "ref2 = " << ref2 << endl; + cout << "ref2 = " << ref2 << endl; + + system("pause"); + + return 0; +} +``` + +​ +### 2.5 引用的本质 + +本质:**引用的本质在c++内部实现是一个指针常量.** + +讲解示例: + +```C++ +//发现是引用,转换为 int* const ref = &a; +void func(int& ref){ + ref = 100; // ref是引用,转换为*ref = 100 +} +int main(){ + int a = 10; + + //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改 + int& ref = a; + ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20; + + cout << "a:" << a << endl; + cout << "ref:" << ref << endl; + + func(a); + return 0; +} +``` + +结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了 +### 2.6 常量引用 **作用:**常量引用主要用来修饰形参,防止误操作 在函数形参列表中,可以加==const修饰形参==,防止形参改变实参 **示例:** ```C++ +//引用使用的场景,通常用来修饰形参 +void showValue(const int& v) { + //v += 10; + cout << v << endl; +} + +int main() { + + //int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误 + //加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp; + const int& ref = 10; + + //ref = 100; //加入const后不可以修改变量 + cout << ref << endl; + + //函数中利用常量引用防止误操作修改实参 + int a = 10; + showValue(a); + + system("pause"); + + return 0; +} +``` +## 3 函数提高 + +### 3.1 函数默认参数 在C++中,函数的形参列表中的形参是可以有默认值的。 + +语法:` 返回值类型 函数名 (参数= 默认值){}` **示例:** + +```C++ +int func(int a, int b = 10, int c = 10) { + return a + b + c; +} + +//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值 +//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数 +int func2(int a = 10, int b = 10); +int func2(int a, int b) { + return a + b; +} + +int main() { + + cout << "ret = " << func(20, 20) << endl; + cout << "ret = " << func(100) << endl; + + system("pause"); + + return 0; +} +``` ### 3.2 函数占位参数 C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置 **语法:** `返回值类型 函数名 (数据类型){}` 在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术 **示例:** + +```C++ +//函数占位参数 ,占位参数也可以有默认参数 +void func(int a, int) { + cout << "this is func" << endl; +} + +int main() { + + func(10,10); //占位参数必须填补 + + system("pause"); + + return 0; +} +``` +### 3.3 函数重载 + +#### 3.3.1 函数重载概述 **作用:**函数名可以相同,提高复用性 **函数重载满足条件:** + +* 同一个作用域下 +* 函数名称相同 +* 函数参数**类型不同** 或者 **个数不同** 或者 **顺序不同** **注意:** 函数的返回值不可以作为函数重载的条件 **示例:** + +```C++ +//函数重载需要函数都在同一个作用域下 +void func() +{ + cout << "func 的调用!" << endl; +} +void func(int a) +{ + cout << "func (int a) 的调用!" << endl; +} +void func(double a) +{ + cout << "func (double a)的调用!" << endl; +} +void func(int a ,double b) +{ + cout << "func (int a ,double b) 的调用!" << endl; +} +void func(double a ,int b) +{ + cout << "func (double a ,int b)的调用!" << endl; +} + +//函数返回值不可以作为函数重载条件 +//int func(double a, int b) +//{ +// cout << "func (double a ,int b)的调用!" << endl; +//} + + +int main() { + + func(); + func(10); + func(3.14); + func(10,3.14); + func(3.14 , 10); + + system("pause"); + + return 0; +} +``` +#### 3.3.2 函数重载注意事项 * 引用作为重载条件 +* 函数重载碰到函数默认参数 + +**示例:** + +```C++ +//函数重载注意事项 +//1、引用作为重载条件 + +void func(int &a) +{ + cout << "func (int &a) 调用 " << endl; +} + +void func(const int &a) +{ + cout << "func (const int &a) 调用 " << endl; +} + + +//2、函数重载碰到函数默认参数 + +void func2(int a, int b = 10) +{ + cout << "func2(int a, int b = 10) 调用" << endl; +} + +void func2(int a) +{ + cout << "func2(int a) 调用" << endl; +} + +int main() { + + int a = 10; + func(a); //调用无const + func(10);//调用有const + + + //func2(10); //碰到默认参数产生歧义,需要避免 + + system("pause"); + + return 0; +} +``` ## **4** 类和对象 C++面向对象的三大特性为:==封装、继承、多态== C++认为==万事万物都皆为对象==,对象上有其属性和行为 **例如:** + +​ 人可以作为对象,属性有姓名、年龄、身高、体重...,行为有走、跑、跳、吃饭、唱歌... + +​ 车也可以作为对象,属性有轮胎、方向盘、车灯...,行为有载人、放音乐、放空调... + +​ 具有相同性质的==对象==,我们可以抽象称为==类==,人属于人类,车属于车类 + +### 4.1 封装 + +#### 4.1.1 封装的意义 + +封装是C++面向对象三大特性之一 + +封装的意义: + +* 将属性和行为作为一个整体,表现生活中的事物 +* 将属性和行为加以权限控制 **封装意义一:** + +​ 在设计类的时候,属性和行为写在一起,表现事物 + +**语法:** `class 类名{ 访问权限: 属性 / 行为 };` **示例1:**设计一个圆类,求圆的周长 + +**示例代码:** + +```C++ +//圆周率 +const double PI = 3.14; + +//1、封装的意义 +//将属性和行为作为一个整体,用来表现生活中的事物 + +//封装一个圆类,求圆的周长 +//class代表设计一个类,后面跟着的是类名 +class Circle +{ +public: //访问权限 公共的权限 + + //属性 + int m_r;//半径 + + //行为 + //获取到圆的周长 + double calculateZC() + { + //2 * pi * r + //获取圆的周长 + return 2 * PI * m_r; + } +}; + +int main() { + + //通过圆类,创建圆的对象 + // c1就是一个具体的圆 + Circle c1; + c1.m_r = 10; //给圆对象的半径 进行赋值操作 + + //2 * pi * 10 = = 62.8 + cout << "圆的周长为: " << c1.calculateZC() << endl; + + system("pause"); + + return 0; +} +``` + +**示例2:**设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号 + +**示例2代码:** + +```C++ +//学生类 +class Student { +public: + void setName(string name) { + m_name = name; + } + void setID(int id) { + m_id = id; + } + + void showStudent() { + cout << "name:" << m_name << " ID:" << m_id << endl; + } +public: + string m_name; + int m_id; +}; + +int main() { + + Student stu; + stu.setName("德玛西亚"); + stu.setID(250); + stu.showStudent(); + + system("pause"); + + return 0; +} + +``` +**封装意义二:** + +类在设计时,可以把属性和行为放在不同的权限下,加以控制 + +访问权限有三种: 1. public 公共权限 +2. protected 保护权限 +3. private 私有权限 **示例:** + +```C++ +//三种权限 +//公共权限 public 类内可以访问 类外可以访问 +//保护权限 protected 类内可以访问 类外不可以访问 +//私有权限 private 类内可以访问 类外不可以访问 + +class Person +{ + //姓名 公共权限 +public: + string m_Name; + + //汽车 保护权限 +protected: + string m_Car; + + //银行卡密码 私有权限 +private: + int m_Password; + +public: + void func() + { + m_Name = "张三"; + m_Car = "拖拉机"; + m_Password = 123456; + } +}; + +int main() { + + Person p; + p.m_Name = "李四"; + //p.m_Car = "奔驰"; //保护权限类外访问不到 + //p.m_Password = 123; //私有权限类外访问不到 + + system("pause"); + + return 0; +} +``` #### 4.1.2 struct和class区别 在C++中 struct和class唯一的**区别**就在于 **默认的访问权限不同** + +区别: + +* struct 默认权限为公共 +* class 默认权限为私有 ```C++ +class C1 +{ + int m_A; //默认是私有权限 +}; + +struct C2 +{ + int m_A; //默认是公共权限 +}; + +int main() { + + C1 c1; + c1.m_A = 10; //错误,访问权限是私有 + + C2 c2; + c2.m_A = 10; //正确,访问权限是公共 + + system("pause"); + + return 0; +} +``` +#### 4.1.3 成员属性设置为私有 **优点1:**将所有成员属性设置为私有,可以自己控制读写权限 + +**优点2:**对于写权限,我们可以检测数据的有效性 **示例:** + +```C++ +class Person { +public: + + //姓名设置可读可写 + void setName(string name) { + m_Name = name; + } + string getName() + { + return m_Name; + } + + + //获取年龄 + int getAge() { + return m_Age; + } + //设置年龄 + void setAge(int age) { + if (age < 0 || age > 150) { + cout << "你个老妖精!" << endl; + return; + } + m_Age = age; + } + + //情人设置为只写 + void setLover(string lover) { + m_Lover = lover; + } + +private: + string m_Name; //可读可写 姓名 + + int m_Age; //只读 年龄 + + string m_Lover; //只写 情人 +}; + + +int main() { + + Person p; + //姓名设置 + p.setName("张三"); + cout << "姓名: " << p.getName() << endl; + + //年龄设置 + p.setAge(50); + cout << "年龄: " << p.getAge() << endl; + + //情人设置 + p.setLover("苍井"); + //cout << "情人: " << p.m_Lover << endl; //只写属性,不可以读取 + + system("pause"); + + return 0; +} +``` +**练习案例1:设计立方体类** + +设计立方体类(Cube) + +求出立方体的面积和体积 + +分别用全局函数和成员函数判断两个立方体是否相等。 ![1545533548532](assets/1545533548532.png) + + +**练习案例2:点和圆的关系** + +设计一个圆形类(Circle),和一个点类(Point),计算点和圆的关系。 ![1545533829184](assets/1545533829184.png) ### 4.2 对象的初始化和清理 * 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全 +* C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。 + +#### 4.2.1 构造函数和析构函数 + +对象的**初始化和清理**也是两个非常重要的安全问题 + +​ 一个对象或者变量没有初始状态,对其使用后果是未知 + +​ 同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题 c++利用了**构造函数**和**析构函数**解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。 + +对象的初始化和清理工作是编译器强制要我们做的事情,因此如果**我们不提供构造和析构,编译器会提供** + +**编译器提供的构造函数和析构函数是空实现。** * 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。 +* 析构函数:主要作用在于对象**销毁前**系统自动调用,执行一些清理工作。 + +**构造函数语法:**`类名(){}` + +1. 构造函数,没有返回值也不写void +2. 函数名称与类名相同 +3. 构造函数可以有参数,因此可以发生重载 +4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次 + +**析构函数语法:** `~类名(){}` + +1. 析构函数,没有返回值也不写void +2. 函数名称与类名相同,在名称前加上符号 ~ +3. 析构函数不可以有参数,因此不可以发生重载 +4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次 + +```C++ +class Person +{ +public: + //构造函数 + Person() + { + cout << "Person的构造函数调用" << endl; + } + //析构函数 + ~Person() + { + cout << "Person的析构函数调用" << endl; + } + +}; + +void test01() +{ + Person p; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + + +#### 4.2.2 构造函数的分类及调用 + +两种分类方式: + +​ 按参数分为: 有参构造和无参构造 + +​ 按类型分为: 普通构造和拷贝构造 + +三种调用方式: + +​ 括号法 + +​ 显示法 + +​ 隐式转换法 **示例:** + +```C++ +//1、构造函数分类 +// 按照参数分类分为 有参和无参构造 无参又称为默认构造函数 +// 按照类型分类分为 普通构造和拷贝构造 + +class Person { +public: + //无参(默认)构造函数 + Person() { + cout << "无参构造函数!" << endl; + } + //有参构造函数 + Person(int a) { + age = a; + cout << "有参构造函数!" << endl; + } + //拷贝构造函数 + Person(const Person& p) { + age = p.age; + cout << "拷贝构造函数!" << endl; + } + //析构函数 + ~Person() { + cout << "析构函数!" << endl; + } +public: + int age; +}; + +//2、构造函数的调用 +//调用无参构造函数 +void test01() { + Person p; //调用无参构造函数 +} + +//调用有参的构造函数 +void test02() { + + //2.1 括号法,常用 + Person p1(10); + //注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明 + //Person p2(); + + //2.2 显式法 + Person p2 = Person(10); + Person p3 = Person(p2); + //Person(10)单独写就是匿名对象 当前行结束之后,马上析构 + + //2.3 隐式转换法 + Person p4 = 10; // Person p4 = Person(10); + Person p5 = p4; // Person p5 = Person(p4); + + //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明 + //Person p5(p4); +} + +int main() { + + test01(); + //test02(); + + system("pause"); + + return 0; +} +``` +#### 4.2.3 拷贝构造函数调用时机 C++中拷贝构造函数调用时机通常有三种情况 + +* 使用一个已经创建完毕的对象来初始化一个新对象 +* 值传递的方式给函数参数传值 +* 以值方式返回局部对象 **示例:** + +```C++ +class Person { +public: + Person() { + cout << "无参构造函数!" << endl; + mAge = 0; + } + Person(int age) { + cout << "有参构造函数!" << endl; + mAge = age; + } + Person(const Person& p) { + cout << "拷贝构造函数!" << endl; + mAge = p.mAge; + } + //析构函数在释放内存之前调用 + ~Person() { + cout << "析构函数!" << endl; + } +public: + int mAge; +}; + +//1. 使用一个已经创建完毕的对象来初始化一个新对象 +void test01() { + + Person man(100); //p对象已经创建完毕 + Person newman(man); //调用拷贝构造函数 + Person newman2 = man; //拷贝构造 + + //Person newman3; + //newman3 = man; //不是调用拷贝构造函数,赋值操作 +} + +//2. 值传递的方式给函数参数传值 +//相当于Person p1 = p; +void doWork(Person p1) {} +void test02() { + Person p; //无参构造函数 + doWork(p); +} + +//3. 以值方式返回局部对象 +Person doWork2() +{ + Person p1; + cout << (int *)&p1 << endl; + return p1; +} + +void test03() +{ + Person p = doWork2(); + cout << (int *)&p << endl; +} + + +int main() { + + //test01(); + //test02(); + test03(); + + system("pause"); + + return 0; +} +``` + +#### 4.2.4 构造函数调用规则 + +默认情况下,c++编译器至少给一个类添加3个函数 + +1.默认构造函数(无参,函数体为空) + +2.默认析构函数(无参,函数体为空) + +3.默认拷贝构造函数,对属性进行值拷贝 构造函数调用规则如下: + +* 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造 + + +* 如果用户定义拷贝构造函数,c++不会再提供其他构造函数 示例: + +```C++ +class Person { +public: + //无参(默认)构造函数 + Person() { + cout << "无参构造函数!" << endl; + } + //有参构造函数 + Person(int a) { + age = a; + cout << "有参构造函数!" << endl; + } + //拷贝构造函数 + Person(const Person& p) { + age = p.age; + cout << "拷贝构造函数!" << endl; + } + //析构函数 + ~Person() { + cout << "析构函数!" << endl; + } +public: + int age; +}; + +void test01() +{ + Person p1(18); + //如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作 + Person p2(p1); + + cout << "p2的年龄为: " << p2.age << endl; +} + +void test02() +{ + //如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造 + Person p1; //此时如果用户自己没有提供默认构造,会出错 + Person p2(10); //用户提供的有参 + Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供 + + //如果用户提供拷贝构造,编译器不会提供其他构造函数 + Person p4; //此时如果用户自己没有提供默认构造,会出错 + Person p5(10); //此时如果用户自己没有提供有参,会出错 + Person p6(p5); //用户自己提供拷贝构造 +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` +#### 4.2.5 深拷贝与浅拷贝 深浅拷贝是面试经典问题,也是常见的一个坑 浅拷贝:简单的赋值拷贝操作 深拷贝:在堆区重新申请空间,进行拷贝操作 **示例:** + +```C++ +class Person { +public: + //无参(默认)构造函数 + Person() { + cout << "无参构造函数!" << endl; + } + //有参构造函数 + Person(int age ,int height) { + + cout << "有参构造函数!" << endl; + + m_age = age; + m_height = new int(height); + + } + //拷贝构造函数 + Person(const Person& p) { + cout << "拷贝构造函数!" << endl; + //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题 + m_age = p.m_age; + m_height = new int(*p.m_height); + + } + + //析构函数 + ~Person() { + cout << "析构函数!" << endl; + if (m_height != NULL) + { + delete m_height; + } + } +public: + int m_age; + int* m_height; +}; + +void test01() +{ + Person p1(18, 180); + + Person p2(p1); + + cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl; + + cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +> 总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题 +#### 4.2.6 初始化列表 **作用:** + +C++提供了初始化列表语法,用来初始化属性 **语法:**`构造函数():属性1(值1),属性2(值2)... {}` **示例:** + +```C++ +class Person { +public: + + ////传统方式初始化 + //Person(int a, int b, int c) { + // m_A = a; + // m_B = b; + // m_C = c; + //} + + //初始化列表方式初始化 + Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {} + void PrintPerson() { + cout << "mA:" << m_A << endl; + cout << "mB:" << m_B << endl; + cout << "mC:" << m_C << endl; + } +private: + int m_A; + int m_B; + int m_C; +}; + +int main() { + + Person p(1, 2, 3); + p.PrintPerson(); + + + system("pause"); + + return 0; +} +``` + +#### 4.2.7 类对象作为类成员 C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员 例如: + +```C++ +class A {} +class B +{ + A a; +} +``` B类中有对象A作为成员,A为对象成员 那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后? **示例:** + +```C++ +class Phone +{ +public: + Phone(string name) + { + m_PhoneName = name; + cout << "Phone构造" << endl; + } + + ~Phone() + { + cout << "Phone析构" << endl; + } + + string m_PhoneName; + +}; + + +class Person +{ +public: + + //初始化列表可以告诉编译器调用哪一个构造函数 + Person(string name, string pName) :m_Name(name), m_Phone(pName) + { + cout << "Person构造" << endl; + } + + ~Person() + { + cout << "Person析构" << endl; + } + + void playGame() + { + cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl; + } + + string m_Name; + Phone m_Phone; + +}; +void test01() +{ + //当类中成员是其他类对象时,我们称该成员为 对象成员 + //构造的顺序是 :先调用对象成员的构造,再调用本类构造 + //析构顺序与构造相反 + Person p("张三" , "苹果X"); + p.playGame(); + +} + + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + + +#### 4.2.8 静态成员 + +静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员 + +静态成员分为: * 静态成员变量 + * 所有对象共享同一份数据 + * 在编译阶段分配内存 + * 类内声明,类外初始化 +* 静态成员函数 + * 所有对象共享同一个函数 + * 静态成员函数只能访问静态成员变量 **示例1 :**静态成员变量 + +```C++ +class Person +{ + +public: + + static int m_A; //静态成员变量 + + //静态成员变量特点: + //1 在编译阶段分配内存 + //2 类内声明,类外初始化 + //3 所有对象共享同一份数据 + +private: + static int m_B; //静态成员变量也是有访问权限的 +}; +int Person::m_A = 10; +int Person::m_B = 10; + +void test01() +{ + //静态成员变量两种访问方式 + + //1、通过对象 + Person p1; + p1.m_A = 100; + cout << "p1.m_A = " << p1.m_A << endl; + + Person p2; + p2.m_A = 200; + cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据 + cout << "p2.m_A = " << p2.m_A << endl; + + //2、通过类名 + cout << "m_A = " << Person::m_A << endl; + + + //cout << "m_B = " << Person::m_B << endl; //私有权限访问不到 +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` **示例2:**静态成员函数 + +```C++ +class Person +{ + +public: + + //静态成员函数特点: + //1 程序共享一个函数 + //2 静态成员函数只能访问静态成员变量 + + static void func() + { + cout << "func调用" << endl; + m_A = 100; + //m_B = 100; //错误,不可以访问非静态成员变量 + } + + static int m_A; //静态成员变量 + int m_B; // +private: + + //静态成员函数也是有访问权限的 + static void func2() + { + cout << "func2调用" << endl; + } +}; +int Person::m_A = 10; + + +void test01() +{ + //静态成员变量两种访问方式 + + //1、通过对象 + Person p1; + p1.func(); + + //2、通过类名 + Person::func(); + + + //Person::func2(); //私有权限访问不到 +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` +### 4.3 C++对象模型和this指针 #### 4.3.1 成员变量和成员函数分开存储 在C++中,类内的成员变量和成员函数分开存储 + +只有非静态成员变量才属于类的对象上 ```C++ +class Person { +public: + Person() { + mA = 0; + } + //非静态成员变量占对象空间 + int mA; + //静态成员变量不占对象空间 + static int mB; + //函数也不占对象空间,所有函数共享一个函数实例 + void func() { + cout << "mA:" << this->mA << endl; + } + //静态成员函数也不占对象空间 + static void sfunc() { + } +}; + +int main() { + + cout << sizeof(Person) << endl; + + system("pause"); + + return 0; +} +``` #### 4.3.2 this指针概念 + +通过4.3.1我们知道在C++中成员变量和成员函数是分开存储的 + +每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码 + +那么问题是:这一块代码是如何区分那个对象调用自己的呢? c++通过提供特殊的对象指针,this指针,解决上述问题。**this指针指向被调用的成员函数所属的对象** this指针是隐含每一个非静态成员函数内的一种指针 + +this指针不需要定义,直接使用即可 this指针的用途: + +* 当形参和成员变量同名时,可用this指针来区分 +* 在类的非静态成员函数中返回对象本身,可使用return *this + +```C++ +class Person +{ +public: + + Person(int age) + { + //1、当形参和成员变量同名时,可用this指针来区分 + this->age = age; + } + + Person& PersonAddPerson(Person p) + { + this->age += p.age; + //返回对象本身 + return *this; + } + + int age; +}; + +void test01() +{ + Person p1(10); + cout << "p1.age = " << p1.age << endl; + + Person p2(10); + p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); + cout << "p2.age = " << p2.age << endl; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` +#### 4.3.3 空指针访问成员函数 C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针 如果用到this指针,需要加以判断保证代码的健壮性 **示例:** + +```C++ +//空指针访问成员函数 +class Person { +public: + + void ShowClassName() { + cout << "我是Person类!" << endl; + } + + void ShowPerson() { + if (this == NULL) { + return; + } + cout << mAge << endl; + } + +public: + int mAge; +}; + +void test01() +{ + Person * p = NULL; + p->ShowClassName(); //空指针,可以调用成员函数 + p->ShowPerson(); //但是如果成员函数中用到了this指针,就不可以了 +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` +#### 4.3.4 const修饰成员函数 **常函数:** + +* 成员函数后加const后我们称为这个函数为**常函数** +* 常函数内不可以修改成员属性 +* 成员属性声明时加关键字mutable后,在常函数中依然可以修改 **常对象:** + +* 声明对象前加const称该对象为常对象 +* 常对象只能调用常函数 **示例:** + +```C++ +class Person { +public: + Person() { + m_A = 0; + m_B = 0; + } + + //this指针的本质是一个指针常量,指针的指向不可修改 + //如果想让指针指向的值也不可以修改,需要声明常函数 + void ShowPerson() const { + //const Type* const pointer; + //this = NULL; //不能修改指针的指向 Person* const this; + //this->mA = 100; //但是this指针指向的对象的数据是可以修改的 + + //const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量 + this->m_B = 100; + } + + void MyFunc() const { + //mA = 10000; + } + +public: + int m_A; + mutable int m_B; //可修改 可变的 +}; + + +//const修饰对象 常对象 +void test01() { + + const Person person; //常量对象 + cout << person.m_A << endl; + //person.mA = 100; //常对象不能修改成员变量的值,但是可以访问 + person.m_B = 100; //但是常对象可以修改mutable修饰成员变量 + + //常对象访问成员函数 + person.MyFunc(); //常对象不能调用const的函数 + +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` ### 4.4 友元 生活中你的家有客厅(Public),有你的卧室(Private) + +客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去 + +但是呢,你也可以允许你的好闺蜜好基友进去。 在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术 友元的目的就是让一个函数或者类 访问另一个类中私有成员 友元的关键字为 ==friend== 友元的三种实现 + +* 全局函数做友元 +* 类做友元 +* 成员函数做友元 + +#### 4.4.1 全局函数做友元 + +```C++ +class Building +{ + //告诉编译器 goodGay全局函数 是 Building类的好朋友,可以访问类中的私有内容 + friend void goodGay(Building * building); + +public: + + Building() + { + this->m_SittingRoom = "客厅"; + this->m_BedRoom = "卧室"; + } + + +public: + string m_SittingRoom; //客厅 + +private: + string m_BedRoom; //卧室 +}; + + +void goodGay(Building * building) +{ + cout << "好基友正在访问: " << building->m_SittingRoom << endl; + cout << "好基友正在访问: " << building->m_BedRoom << endl; +} + + +void test01() +{ + Building b; + goodGay(&b); +} + +int main(){ + + test01(); + + system("pause"); + return 0; +} +``` #### 4.4.2 类做友元 ```C++ +class Building; +class goodGay +{ +public: + + goodGay(); + void visit(); + +private: + Building *building; +}; + + +class Building +{ + //告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容 + friend class goodGay; + +public: + Building(); + +public: + string m_SittingRoom; //客厅 +private: + string m_BedRoom;//卧室 +}; + +Building::Building() +{ + this->m_SittingRoom = "客厅"; + this->m_BedRoom = "卧室"; +} + +goodGay::goodGay() +{ + building = new Building; +} + +void goodGay::visit() +{ + cout << "好基友正在访问" << building->m_SittingRoom << endl; + cout << "好基友正在访问" << building->m_BedRoom << endl; +} + +void test01() +{ + goodGay gg; + gg.visit(); + +} + +int main(){ + + test01(); + + system("pause"); + return 0; +} +``` + +#### 4.4.3 成员函数做友元 ```C++ + +class Building; +class goodGay +{ +public: + + goodGay(); + void visit(); //只让visit函数作为Building的好朋友,可以发访问Building中私有内容 + void visit2(); + +private: + Building *building; +}; + + +class Building +{ + //告诉编译器 goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容 + friend void goodGay::visit(); + +public: + Building(); + +public: + string m_SittingRoom; //客厅 +private: + string m_BedRoom;//卧室 +}; + +Building::Building() +{ + this->m_SittingRoom = "客厅"; + this->m_BedRoom = "卧室"; +} + +goodGay::goodGay() +{ + building = new Building; +} + +void goodGay::visit() +{ + cout << "好基友正在访问" << building->m_SittingRoom << endl; + cout << "好基友正在访问" << building->m_BedRoom << endl; +} + +void goodGay::visit2() +{ + cout << "好基友正在访问" << building->m_SittingRoom << endl; + //cout << "好基友正在访问" << building->m_BedRoom << endl; +} + +void test01() +{ + goodGay gg; + gg.visit(); + +} + +int main(){ + + test01(); + + system("pause"); + return 0; +} +``` +### 4.5 运算符重载 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型 #### 4.5.1 加号运算符重载 作用:实现两个自定义数据类型相加的运算 ```C++ +class Person { +public: + Person() {}; + Person(int a, int b) + { + this->m_A = a; + this->m_B = b; + } + //成员函数实现 + 号运算符重载 + Person operator+(const Person& p) { + Person temp; + temp.m_A = this->m_A + p.m_A; + temp.m_B = this->m_B + p.m_B; + return temp; + } + + +public: + int m_A; + int m_B; +}; + +//全局函数实现 + 号运算符重载 +//Person operator+(const Person& p1, const Person& p2) { +// Person temp(0, 0); +// temp.m_A = p1.m_A + p2.m_A; +// temp.m_B = p1.m_B + p2.m_B; +// return temp; +//} + +//运算符重载 可以发生函数重载 +Person operator+(const Person& p2, int val) +{ + Person temp; + temp.m_A = p2.m_A + val; + temp.m_B = p2.m_B + val; + return temp; +} + +void test() { + + Person p1(10, 10); + Person p2(20, 20); + + //成员函数方式 + Person p3 = p2 + p1; //相当于 p2.operaor+(p1) + cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl; + + + Person p4 = p3 + 10; //相当于 operator+(p3,10) + cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl; + +} + +int main() { + + test(); + + system("pause"); + + return 0; +} +``` > 总结1:对于内置的数据类型的表达式的的运算符是不可能改变的 + +> 总结2:不要滥用运算符重载 #### 4.5.2 左移运算符重载 作用:可以输出自定义数据类型 ```C++ +class Person { + friend ostream& operator<<(ostream& out, Person& p); + +public: + + Person(int a, int b) + { + this->m_A = a; + this->m_B = b; + } + + //成员函数 实现不了 p << cout 不是我们想要的效果 + //void operator<<(Person& p){ + //} + +private: + int m_A; + int m_B; +}; + +//全局函数实现左移重载 +//ostream对象只能有一个 +ostream& operator<<(ostream& out, Person& p) { + out << "a:" << p.m_A << " b:" << p.m_B; + return out; +} + +void test() { + + Person p1(10, 20); + + cout << p1 << "hello world" << endl; //链式编程 +} + +int main() { + + test(); + + system("pause"); + + return 0; +} +``` > 总结:重载左移运算符配合友元可以实现输出自定义数据类型 +#### 4.5.3 递增运算符重载 作用: 通过重载递增运算符,实现自己的整型数据 ```C++ + +class MyInteger { + + friend ostream& operator<<(ostream& out, MyInteger myint); + +public: + MyInteger() { + m_Num = 0; + } + //前置++ + MyInteger& operator++() { + //先++ + m_Num++; + //再返回 + return *this; + } + + //后置++ + MyInteger operator++(int) { + //先返回 + MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++; + m_Num++; + return temp; + } + +private: + int m_Num; +}; + + +ostream& operator<<(ostream& out, MyInteger myint) { + out << myint.m_Num; + return out; +} + + +//前置++ 先++ 再返回 +void test01() { + MyInteger myInt; + cout << ++myInt << endl; + cout << myInt << endl; +} + +//后置++ 先返回 再++ +void test02() { + + MyInteger myInt; + cout << myInt++ << endl; + cout << myInt << endl; +} + +int main() { + + test01(); + //test02(); + + system("pause"); + + return 0; +} +``` > 总结: 前置递增返回引用,后置递增返回值 +#### 4.5.4 赋值运算符重载 c++编译器至少给一个类添加4个函数 + +1. 默认构造函数(无参,函数体为空) +2. 默认析构函数(无参,函数体为空) +3. 默认拷贝构造函数,对属性进行值拷贝 +4. 赋值运算符 operator=, 对属性进行值拷贝 + +如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题 + +**示例:** + +```C++ +class Person +{ +public: + + Person(int age) + { + //将年龄数据开辟到堆区 + m_Age = new int(age); + } + + //重载赋值运算符 + Person& operator=(Person &p) + { + if (m_Age != NULL) + { + delete m_Age; + m_Age = NULL; + } + //编译器提供的代码是浅拷贝 + //m_Age = p.m_Age; + + //提供深拷贝 解决浅拷贝的问题 + m_Age = new int(*p.m_Age); + + //返回自身 + return *this; + } + + + ~Person() + { + if (m_Age != NULL) + { + delete m_Age; + m_Age = NULL; + } + } + + //年龄的指针 + int *m_Age; + +}; + + +void test01() +{ + Person p1(18); + + Person p2(20); + + Person p3(30); + + p3 = p2 = p1; //赋值操作 + + cout << "p1的年龄为:" << *p1.m_Age << endl; + + cout << "p2的年龄为:" << *p2.m_Age << endl; + + cout << "p3的年龄为:" << *p3.m_Age << endl; +} + +int main() { + + test01(); + + //int a = 10; + //int b = 20; + //int c = 30; + + //c = b = a; + //cout << "a = " << a << endl; + //cout << "b = " << b << endl; + //cout << "c = " << c << endl; + + system("pause"); + + return 0; +} +``` +#### 4.5.5 关系运算符重载 **作用:**重载关系运算符,可以让两个自定义类型对象进行对比操作 **示例:** + +```C++ +class Person +{ +public: + Person(string name, int age) + { + this->m_Name = name; + this->m_Age = age; + }; + + bool operator==(Person & p) + { + if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) + { + return true; + } + else + { + return false; + } + } + + bool operator!=(Person & p) + { + if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) + { + return false; + } + else + { + return true; + } + } + + string m_Name; + int m_Age; +}; + +void test01() +{ + //int a = 0; + //int b = 0; + + Person a("孙悟空", 18); + Person b("孙悟空", 18); + + if (a == b) + { + cout << "a和b相等" << endl; + } + else + { + cout << "a和b不相等" << endl; + } + + if (a != b) + { + cout << "a和b不相等" << endl; + } + else + { + cout << "a和b相等" << endl; + } +} + + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +#### 4.5.6 函数调用运算符重载 * 函数调用运算符 () 也可以重载 +* 由于重载后使用的方式非常像函数的调用,因此称为仿函数 +* 仿函数没有固定写法,非常灵活 **示例:** + +```C++ +class MyPrint +{ +public: + void operator()(string text) + { + cout << text << endl; + } + +}; +void test01() +{ + //重载的()操作符 也称为仿函数 + MyPrint myFunc; + myFunc("hello world"); +} + + +class MyAdd +{ +public: + int operator()(int v1, int v2) + { + return v1 + v2; + } +}; + +void test02() +{ + MyAdd add; + int ret = add(10, 10); + cout << "ret = " << ret << endl; + + //匿名对象调用 + cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl; +} + +int main() { + + test01(); + test02(); + + system("pause"); + + return 0; +} +``` +### 4.6 继承 + +**继承是面向对象三大特性之一** + +有些类与类之间存在特殊的关系,例如下图中: + +![1544861202252](assets/1544861202252.png) + +我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。 + +这个时候我们就可以考虑利用继承的技术,减少重复代码 #### 4.6.1 继承的基本语法 例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同 + +接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处 **普通实现:** + +```C++ +//Java页面 +class Java +{ +public: + void header() + { + cout << "首页、公开课、登录、注册...(公共头部)" << endl; + } + void footer() + { + cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; + } + void left() + { + cout << "Java,Python,C++...(公共分类列表)" << endl; + } + void content() + { + cout << "JAVA学科视频" << endl; + } +}; +//Python页面 +class Python +{ +public: + void header() + { + cout << "首页、公开课、登录、注册...(公共头部)" << endl; + } + void footer() + { + cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; + } + void left() + { + cout << "Java,Python,C++...(公共分类列表)" << endl; + } + void content() + { + cout << "Python学科视频" << endl; + } +}; +//C++页面 +class CPP +{ +public: + void header() + { + cout << "首页、公开课、登录、注册...(公共头部)" << endl; + } + void footer() + { + cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; + } + void left() + { + cout << "Java,Python,C++...(公共分类列表)" << endl; + } + void content() + { + cout << "C++学科视频" << endl; + } +}; + +void test01() +{ + //Java页面 + cout << "Java下载视频页面如下: " << endl; + Java ja; + ja.header(); + ja.footer(); + ja.left(); + ja.content(); + cout << "--------------------" << endl; + + //Python页面 + cout << "Python下载视频页面如下: " << endl; + Python py; + py.header(); + py.footer(); + py.left(); + py.content(); + cout << "--------------------" << endl; + + //C++页面 + cout << "C++下载视频页面如下: " << endl; + CPP cp; + cp.header(); + cp.footer(); + cp.left(); + cp.content(); + +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` **继承实现:** + +```C++ +//公共页面 +class BasePage +{ +public: + void header() + { + cout << "首页、公开课、登录、注册...(公共头部)" << endl; + } + + void footer() + { + cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; + } + void left() + { + cout << "Java,Python,C++...(公共分类列表)" << endl; + } + +}; + +//Java页面 +class Java : public BasePage +{ +public: + void content() + { + cout << "JAVA学科视频" << endl; + } +}; +//Python页面 +class Python : public BasePage +{ +public: + void content() + { + cout << "Python学科视频" << endl; + } +}; +//C++页面 +class CPP : public BasePage +{ +public: + void content() + { + cout << "C++学科视频" << endl; + } +}; + +void test01() +{ + //Java页面 + cout << "Java下载视频页面如下: " << endl; + Java ja; + ja.header(); + ja.footer(); + ja.left(); + ja.content(); + cout << "--------------------" << endl; + + //Python页面 + cout << "Python下载视频页面如下: " << endl; + Python py; + py.header(); + py.footer(); + py.left(); + py.content(); + cout << "--------------------" << endl; + + //C++页面 + cout << "C++下载视频页面如下: " << endl; + CPP cp; + cp.header(); + cp.footer(); + cp.left(); + cp.content(); + + +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` **总结:** + +继承的好处:==可以减少重复的代码== + +class A : public B; + +A 类称为子类 或 派生类 + +B 类称为父类 或 基类 **派生类中的成员,包含两大部分**: + +一类是从基类继承过来的,一类是自己增加的成员。 + +从基类继承过过来的表现其共性,而新增的成员体现了其个性。 +#### 4.6.2 继承方式 继承的语法:`class 子类 : 继承方式 父类` **继承方式一共有三种:** + +* 公共继承 +* 保护继承 +* 私有继承 + +![img](assets/clip_image002.png) + +**示例:** + +```C++ +class Base1 +{ +public: + int m_A; +protected: + int m_B; +private: + int m_C; +}; + +//公共继承 +class Son1 :public Base1 +{ +public: + void func() + { + m_A; //可访问 public权限 + m_B; //可访问 protected权限 + //m_C; //不可访问 + } +}; + +void myClass() +{ + Son1 s1; + s1.m_A; //其他类只能访问到公共权限 +} + +//保护继承 +class Base2 +{ +public: + int m_A; +protected: + int m_B; +private: + int m_C; +}; +class Son2:protected Base2 +{ +public: + void func() + { + m_A; //可访问 protected权限 + m_B; //可访问 protected权限 + //m_C; //不可访问 + } +}; +void myClass2() +{ + Son2 s; + //s.m_A; //不可访问 +} + +//私有继承 +class Base3 +{ +public: + int m_A; +protected: + int m_B; +private: + int m_C; +}; +class Son3:private Base3 +{ +public: + void func() + { + m_A; //可访问 private权限 + m_B; //可访问 private权限 + //m_C; //不可访问 + } +}; +class GrandSon3 :public Son3 +{ +public: + void func() + { + //Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到 + //m_A; + //m_B; + //m_C; + } +}; +``` +#### 4.6.3 继承中的对象模型 **问题:**从父类继承过来的成员,哪些属于子类对象中? **示例:** + +```C++ +class Base +{ +public: + int m_A; +protected: + int m_B; +private: + int m_C; //私有成员只是被隐藏了,但是还是会继承下去 +}; + +//公共继承 +class Son :public Base +{ +public: + int m_D; +}; + +void test01() +{ + cout << "sizeof Son = " << sizeof(Son) << endl; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +利用工具查看: ![1545881904150](assets/1545881904150.png) 打开工具窗口后,定位到当前CPP文件的盘符 + +然后输入: cl /d1 reportSingleClassLayout查看的类名 所属文件名 效果如下图: ![1545882158050](assets/1545882158050.png) > 结论: 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到 + + +#### 4.6.4 继承中构造和析构顺序 子类继承父类后,当创建子类对象,也会调用父类的构造函数 问题:父类和子类的构造和析构顺序是谁先谁后? **示例:** + +```C++ +class Base +{ +public: + Base() + { + cout << "Base构造函数!" << endl; + } + ~Base() + { + cout << "Base析构函数!" << endl; + } +}; + +class Son : public Base +{ +public: + Son() + { + cout << "Son构造函数!" << endl; + } + ~Son() + { + cout << "Son析构函数!" << endl; + } + +}; + + +void test01() +{ + //继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反 + Son s; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` > 总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反 + + +#### 4.6.5 继承同名成员处理方式 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢? * 访问子类同名成员 直接访问即可 +* 访问父类同名成员 需要加作用域 **示例:** + +```C++ +class Base { +public: + Base() + { + m_A = 100; + } + + void func() + { + cout << "Base - func()调用" << endl; + } + + void func(int a) + { + cout << "Base - func(int a)调用" << endl; + } + +public: + int m_A; +}; + + +class Son : public Base { +public: + Son() + { + m_A = 200; + } + + //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 + //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域 + void func() + { + cout << "Son - func()调用" << endl; + } +public: + int m_A; +}; + +void test01() +{ + Son s; + + cout << "Son下的m_A = " << s.m_A << endl; + cout << "Base下的m_A = " << s.Base::m_A << endl; + + s.func(); + s.Base::func(); + s.Base::func(10); + +} +int main() { + + test01(); + + system("pause"); + return EXIT_SUCCESS; +} +``` + +总结: + +1. 子类对象可以直接访问到子类中同名成员 +2. 子类对象加作用域可以访问到父类同名成员 +3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数 +#### 4.6.6 继承同名静态成员处理方式 问题:继承中同名的静态成员在子类对象上如何进行访问? 静态成员和非静态成员出现同名,处理方式一致 - 访问子类同名成员 直接访问即可 +- 访问父类同名成员 需要加作用域 **示例:** + +```C++ +class Base { +public: + static void func() + { + cout << "Base - static void func()" << endl; + } + static void func(int a) + { + cout << "Base - static void func(int a)" << endl; + } + + static int m_A; +}; + +int Base::m_A = 100; + +class Son : public Base { +public: + static void func() + { + cout << "Son - static void func()" << endl; + } + static int m_A; +}; + +int Son::m_A = 200; + +//同名成员属性 +void test01() +{ + //通过对象访问 + cout << "通过对象访问: " << endl; + Son s; + cout << "Son 下 m_A = " << s.m_A << endl; + cout << "Base 下 m_A = " << s.Base::m_A << endl; + + //通过类名访问 + cout << "通过类名访问: " << endl; + cout << "Son 下 m_A = " << Son::m_A << endl; + cout << "Base 下 m_A = " << Son::Base::m_A << endl; +} + +//同名成员函数 +void test02() +{ + //通过对象访问 + cout << "通过对象访问: " << endl; + Son s; + s.func(); + s.Base::func(); + + cout << "通过类名访问: " << endl; + Son::func(); + Son::Base::func(); + //出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问 + Son::Base::func(100); +} +int main() { + + //test01(); + test02(); + + system("pause"); + + return 0; +} +``` + +> 总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名) +#### 4.6.7 多继承语法 C++允许**一个类继承多个类** 语法:` class 子类 :继承方式 父类1 , 继承方式 父类2...` 多继承可能会引发父类中有同名成员出现,需要加作用域区分 **C++实际开发中不建议用多继承** **示例:** + +```C++ +class Base1 { +public: + Base1() + { + m_A = 100; + } +public: + int m_A; +}; + +class Base2 { +public: + Base2() + { + m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确 + } +public: + int m_A; +}; + +//语法:class 子类:继承方式 父类1 ,继承方式 父类2 +class Son : public Base2, public Base1 +{ +public: + Son() + { + m_C = 300; + m_D = 400; + } +public: + int m_C; + int m_D; +}; + + +//多继承容易产生成员同名的情况 +//通过使用类名作用域可以区分调用哪一个基类的成员 +void test01() +{ + Son s; + cout << "sizeof Son = " << sizeof(s) << endl; + cout << s.Base1::m_A << endl; + cout << s.Base2::m_A << endl; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` > 总结: 多继承中如果父类中出现了同名情况,子类使用时候要加作用域 + + +#### 4.6.8 菱形继承 **菱形继承概念:** + +​ 两个派生类继承同一个基类 + +​ 又有某个类同时继承者两个派生类 + +​ 这种继承被称为菱形继承,或者钻石继承 **典型的菱形继承案例:** ![IMG_256](assets/clip_image002.jpg) **菱形继承问题:** 1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。 + +2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。 **示例:** + +```C++ +class Animal +{ +public: + int m_Age; +}; + +//继承前加virtual关键字后,变为虚继承 +//此时公共的父类Animal称为虚基类 +class Sheep : virtual public Animal {}; +class Tuo : virtual public Animal {}; +class SheepTuo : public Sheep, public Tuo {}; + +void test01() +{ + SheepTuo st; + st.Sheep::m_Age = 100; + st.Tuo::m_Age = 200; + + cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; + cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl; + cout << "st.m_Age = " << st.m_Age << endl; +} + + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` 总结: + +* 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义 +* 利用虚继承可以解决菱形继承问题 + + +### 4.7 多态 + +#### 4.7.1 多态的基本概念 **多态是C++面向对象三大特性之一** + +多态分为两类 + +* 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名 +* 动态多态: 派生类和虚函数实现运行时多态 静态多态和动态多态区别: + +* 静态多态的函数地址早绑定 - 编译阶段确定函数地址 +* 动态多态的函数地址晚绑定 - 运行阶段确定函数地址 下面通过案例进行讲解多态 ```C++ +class Animal +{ +public: + //Speak函数就是虚函数 + //函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。 + virtual void speak() + { + cout << "动物在说话" << endl; + } +}; + +class Cat :public Animal +{ +public: + void speak() + { + cout << "小猫在说话" << endl; + } +}; + +class Dog :public Animal +{ +public: + + void speak() + { + cout << "小狗在说话" << endl; + } + +}; +//我们希望传入什么对象,那么就调用什么对象的函数 +//如果函数地址在编译阶段就能确定,那么静态联编 +//如果函数地址在运行阶段才能确定,就是动态联编 + +void DoSpeak(Animal & animal) +{ + animal.speak(); +} +// +//多态满足条件: +//1、有继承关系 +//2、子类重写父类中的虚函数 +//多态使用: +//父类指针或引用指向子类对象 + +void test01() +{ + Cat cat; + DoSpeak(cat); + + + Dog dog; + DoSpeak(dog); +} + + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +总结: + +多态满足条件 + +* 有继承关系 +* 子类重写父类中的虚函数 + +多态使用条件 + +* 父类指针或引用指向子类对象 + +重写:函数返回值类型 函数名 参数列表 完全一致称为重写 +#### 4.7.2 多态案例一-计算器类 案例描述: + +分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类 多态的优点: + +* 代码组织结构清晰 +* 可读性强 +* 利于前期和后期的扩展以及维护 **示例:** + +```C++ +//普通实现 +class Calculator { +public: + int getResult(string oper) + { + if (oper == "+") { + return m_Num1 + m_Num2; + } + else if (oper == "-") { + return m_Num1 - m_Num2; + } + else if (oper == "*") { + return m_Num1 * m_Num2; + } + //如果要提供新的运算,需要修改源码 + } +public: + int m_Num1; + int m_Num2; +}; + +void test01() +{ + //普通实现测试 + Calculator c; + c.m_Num1 = 10; + c.m_Num2 = 10; + cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl; + + cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl; + + cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl; +} //多态实现 +//抽象计算器类 +//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护 +class AbstractCalculator +{ +public : + + virtual int getResult() + { + return 0; + } + + int m_Num1; + int m_Num2; +}; + +//加法计算器 +class AddCalculator :public AbstractCalculator +{ +public: + int getResult() + { + return m_Num1 + m_Num2; + } +}; + +//减法计算器 +class SubCalculator :public AbstractCalculator +{ +public: + int getResult() + { + return m_Num1 - m_Num2; + } +}; + +//乘法计算器 +class MulCalculator :public AbstractCalculator +{ +public: + int getResult() + { + return m_Num1 * m_Num2; + } +}; + + +void test02() +{ + //创建加法计算器 + AbstractCalculator *abc = new AddCalculator; + abc->m_Num1 = 10; + abc->m_Num2 = 10; + cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; + delete abc; //用完了记得销毁 + + //创建减法计算器 + abc = new SubCalculator; + abc->m_Num1 = 10; + abc->m_Num2 = 10; + cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; + delete abc; + + //创建乘法计算器 + abc = new MulCalculator; + abc->m_Num1 = 10; + abc->m_Num2 = 10; + cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; + delete abc; +} + +int main() { + + //test01(); + + test02(); + + system("pause"); + + return 0; +} +``` + +> 总结:C++开发提倡利用多态设计程序架构,因为多态优点很多 +#### 4.7.3 纯虚函数和抽象类 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容 因此可以将虚函数改为**纯虚函数** 纯虚函数语法:`virtual 返回值类型 函数名 (参数列表)= 0 ;` 当类中有了纯虚函数,这个类也称为==抽象类== **抽象类特点**: + + * 无法实例化对象 + * 子类必须重写抽象类中的纯虚函数,否则也属于抽象类 + +**示例:** + +```C++ +class Base +{ +public: + //纯虚函数 + //类中只要有一个纯虚函数就称为抽象类 + //抽象类无法实例化对象 + //子类必须重写父类中的纯虚函数,否则也属于抽象类 + virtual void func() = 0; +}; + +class Son :public Base +{ +public: + virtual void func() + { + cout << "func调用" << endl; + }; +}; + +void test01() +{ + Base * base = NULL; + //base = new Base; // 错误,抽象类无法实例化对象 + base = new Son; + base->func(); + delete base;//记得销毁 +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + + +#### 4.7.4 多态案例二-制作饮品 + +**案例描述:** + +制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶 ![1545985945198](assets/1545985945198.png) **示例:** + +```C++ +//抽象制作饮品 +class AbstractDrinking { +public: + //烧水 + virtual void Boil() = 0; + //冲泡 + virtual void Brew() = 0; + //倒入杯中 + virtual void PourInCup() = 0; + //加入辅料 + virtual void PutSomething() = 0; + //规定流程 + void MakeDrink() { + Boil(); + Brew(); + PourInCup(); + PutSomething(); + } +}; + +//制作咖啡 +class Coffee : public AbstractDrinking { +public: + //烧水 + virtual void Boil() { + cout << "煮农夫山泉!" << endl; + } + //冲泡 + virtual void Brew() { + cout << "冲泡咖啡!" << endl; + } + //倒入杯中 + virtual void PourInCup() { + cout << "将咖啡倒入杯中!" << endl; + } + //加入辅料 + virtual void PutSomething() { + cout << "加入牛奶!" << endl; + } +}; + +//制作茶水 +class Tea : public AbstractDrinking { +public: + //烧水 + virtual void Boil() { + cout << "煮自来水!" << endl; + } + //冲泡 + virtual void Brew() { + cout << "冲泡茶叶!" << endl; + } + //倒入杯中 + virtual void PourInCup() { + cout << "将茶水倒入杯中!" << endl; + } + //加入辅料 + virtual void PutSomething() { + cout << "加入枸杞!" << endl; + } +}; + +//业务函数 +void DoWork(AbstractDrinking* drink) { + drink->MakeDrink(); + delete drink; +} + +void test01() { + DoWork(new Coffee); + cout << "--------------" << endl; + DoWork(new Tea); +} + + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + + +#### 4.7.5 虚析构和纯虚析构 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码 解决方式:将父类中的析构函数改为**虚析构**或者**纯虚析构** 虚析构和纯虚析构共性: + +* 可以解决父类指针释放子类对象 +* 都需要有具体的函数实现 + +虚析构和纯虚析构区别: + +* 如果是纯虚析构,该类属于抽象类,无法实例化对象 虚析构语法: + +`virtual ~类名(){}` + +纯虚析构语法: + +` virtual ~类名() = 0;` + +`类名::~类名(){}` **示例:** + +```C++ +class Animal { +public: + + Animal() + { + cout << "Animal 构造函数调用!" << endl; + } + virtual void Speak() = 0; + + //析构函数加上virtual关键字,变成虚析构函数 + //virtual ~Animal() + //{ + // cout << "Animal虚析构函数调用!" << endl; + //} + + + virtual ~Animal() = 0; +}; + +Animal::~Animal() +{ + cout << "Animal 纯虚析构函数调用!" << endl; +} + +//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。 + +class Cat : public Animal { +public: + Cat(string name) + { + cout << "Cat构造函数调用!" << endl; + m_Name = new string(name); + } + virtual void Speak() + { + cout << *m_Name << "小猫在说话!" << endl; + } + ~Cat() + { + cout << "Cat析构函数调用!" << endl; + if (this->m_Name != NULL) { + delete m_Name; + m_Name = NULL; + } + } + +public: + string *m_Name; +}; + +void test01() +{ + Animal *animal = new Cat("Tom"); + animal->Speak(); + + //通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏 + //怎么解决?给基类增加一个虚析构函数 + //虚析构函数就是用来解决通过父类指针释放子类对象 + delete animal; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` 总结: + +​ 1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象 + +​ 2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构 + +​ 3. 拥有纯虚析构函数的类也属于抽象类 + + +#### 4.7.6 多态案例三-电脑组装 **案例描述:** 电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储) + +将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商 + +创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口 + +测试时组装三台不同的电脑进行工作 + +**示例:** + +```C++ +#include +using namespace std; + +//抽象CPU类 +class CPU +{ +public: + //抽象的计算函数 + virtual void calculate() = 0; +}; + +//抽象显卡类 +class VideoCard +{ +public: + //抽象的显示函数 + virtual void display() = 0; +}; + +//抽象内存条类 +class Memory +{ +public: + //抽象的存储函数 + virtual void storage() = 0; +}; + +//电脑类 +class Computer +{ +public: + Computer(CPU * cpu, VideoCard * vc, Memory * mem) + { + m_cpu = cpu; + m_vc = vc; + m_mem = mem; + } + + //提供工作的函数 + void work() + { + //让零件工作起来,调用接口 + m_cpu->calculate(); + + m_vc->display(); + + m_mem->storage(); + } + + //提供析构函数 释放3个电脑零件 + ~Computer() + { + + //释放CPU零件 + if (m_cpu != NULL) + { + delete m_cpu; + m_cpu = NULL; + } + + //释放显卡零件 + if (m_vc != NULL) + { + delete m_vc; + m_vc = NULL; + } + + //释放内存条零件 + if (m_mem != NULL) + { + delete m_mem; + m_mem = NULL; + } + } + +private: + + CPU * m_cpu; //CPU的零件指针 + VideoCard * m_vc; //显卡零件指针 + Memory * m_mem; //内存条零件指针 +}; + +//具体厂商 +//Intel厂商 +class IntelCPU :public CPU +{ +public: + virtual void calculate() + { + cout << "Intel的CPU开始计算了!" << endl; + } +}; + +class IntelVideoCard :public VideoCard +{ +public: + virtual void display() + { + cout << "Intel的显卡开始显示了!" << endl; + } +}; + +class IntelMemory :public Memory +{ +public: + virtual void storage() + { + cout << "Intel的内存条开始存储了!" << endl; + } +}; + +//Lenovo厂商 +class LenovoCPU :public CPU +{ +public: + virtual void calculate() + { + cout << "Lenovo的CPU开始计算了!" << endl; + } +}; + +class LenovoVideoCard :public VideoCard +{ +public: + virtual void display() + { + cout << "Lenovo的显卡开始显示了!" << endl; + } +}; + +class LenovoMemory :public Memory +{ +public: + virtual void storage() + { + cout << "Lenovo的内存条开始存储了!" << endl; + } +}; + + +void test01() +{ + //第一台电脑零件 + CPU * intelCpu = new IntelCPU; + VideoCard * intelCard = new IntelVideoCard; + Memory * intelMem = new IntelMemory; + + cout << "第一台电脑开始工作:" << endl; + //创建第一台电脑 + Computer * computer1 = new Computer(intelCpu, intelCard, intelMem); + computer1->work(); + delete computer1; + + cout << "-----------------------" << endl; + cout << "第二台电脑开始工作:" << endl; + //第二台电脑组装 + Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);; + computer2->work(); + delete computer2; + + cout << "-----------------------" << endl; + cout << "第三台电脑开始工作:" << endl; + //第三台电脑组装 + Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);; + computer3->work(); + delete computer3; + +} +``` +## 5 文件操作 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放 + +通过**文件可以将数据持久化** + +C++中对文件操作需要包含头文件 ==< fstream >== 文件类型分为两种: + +1. **文本文件** - 文件以文本的**ASCII码**形式存储在计算机中 +2. **二进制文件** - 文件以文本的**二进制**形式存储在计算机中,用户一般不能直接读懂它们 操作文件的三大类: + +1. ofstream:写操作 +2. ifstream: 读操作 +3. fstream : 读写操作 ### 5.1文本文件 + +#### 5.1.1写文件 + + 写文件步骤如下: + +1. 包含头文件 + + \#include + +2. 创建流对象 + + ofstream ofs; + +3. 打开文件 + + ofs.open("文件路径",打开方式); + +4. 写数据 + + ofs << "写入的数据"; + +5. 关闭文件 + + ofs.close(); + + ​ + +文件打开方式: + +| 打开方式 | 解释 | +| ----------- | -------------------------- | +| ios::in | 为读文件而打开文件 | +| ios::out | 为写文件而打开文件 | +| ios::ate | 初始位置:文件尾 | +| ios::app | 追加方式写文件 | +| ios::trunc | 如果文件存在先删除,再创建 | +| ios::binary | 二进制方式 | + +**注意:** 文件打开方式可以配合使用,利用|操作符 + +**例如:**用二进制方式写文件 `ios::binary | ios:: out` + +**示例:** + +```C++ +#include + +void test01() +{ + ofstream ofs; + ofs.open("test.txt", ios::out); + + ofs << "姓名:张三" << endl; + ofs << "性别:男" << endl; + ofs << "年龄:18" << endl; + + ofs.close(); +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +总结: + +* 文件操作必须包含头文件 fstream +* 读文件可以利用 ofstream ,或者fstream类 +* 打开文件时候需要指定操作文件的路径,以及打开方式 +* 利用<<可以向文件中写数据 +* 操作完毕,要关闭文件 +#### 5.1.2读文件 读文件与写文件步骤相似,但是读取方式相对于比较多 读文件步骤如下: + +1. 包含头文件 + + \#include + +2. 创建流对象 + + ifstream ifs; + +3. 打开文件并判断文件是否打开成功 + + ifs.open("文件路径",打开方式); + +4. 读数据 + + 四种方式读取 + +5. 关闭文件 + + ifs.close(); **示例:** + +```C++ +#include +#include +void test01() +{ + ifstream ifs; + ifs.open("test.txt", ios::in); + + if (!ifs.is_open()) + { + cout << "文件打开失败" << endl; + return; + } + + //第一种方式 + //char buf[1024] = { 0 }; + //while (ifs >> buf) + //{ + // cout << buf << endl; + //} + + //第二种 + //char buf[1024] = { 0 }; + //while (ifs.getline(buf,sizeof(buf))) + //{ + // cout << buf << endl; + //} + + //第三种 + //string buf; + //while (getline(ifs, buf)) + //{ + // cout << buf << endl; + //} + + char c; + while ((c = ifs.get()) != EOF) + { + cout << c; + } + + ifs.close(); + + +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +总结: + +- 读文件可以利用 ifstream ,或者fstream类 +- 利用is_open函数可以判断文件是否打开成功 +- close 关闭文件 + + +### 5.2 二进制文件 + +以二进制的方式对文件进行读写操作 + +打开方式要指定为 ==ios::binary== #### 5.2.1 写文件 + +二进制方式写文件主要利用流对象调用成员函数write + +函数原型 :`ostream& write(const char * buffer,int len);` + +参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数 **示例:** + +```C++ +#include +#include + +class Person +{ +public: + char m_Name[64]; + int m_Age; +}; + +//二进制文件 写文件 +void test01() +{ + //1、包含头文件 + + //2、创建输出流对象 + ofstream ofs("person.txt", ios::out | ios::binary); + + //3、打开文件 + //ofs.open("person.txt", ios::out | ios::binary); + + Person p = {"张三" , 18}; + + //4、写文件 + ofs.write((const char *)&p, sizeof(p)); + + //5、关闭文件 + ofs.close(); +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` + +总结: + +* 文件输出流对象 可以通过write函数,以二进制方式写数据 + + +#### 5.2.2 读文件 + +二进制方式读文件主要利用流对象调用成员函数read + +函数原型:`istream& read(char *buffer,int len);` + +参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数 + +示例: + +```C++ +#include +#include + +class Person +{ +public: + char m_Name[64]; + int m_Age; +}; + +void test01() +{ + ifstream ifs("person.txt", ios::in | ios::binary); + if (!ifs.is_open()) + { + cout << "文件打开失败" << endl; + } + + Person p; + ifs.read((char *)&p, sizeof(p)); + + cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl; +} + +int main() { + + test01(); + + system("pause"); + + return 0; +} +``` - 文件输入流对象 可以通过read函数,以二进制方式读数据 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..bc0390e98c98f2ad3ab7decdb82b4d57d7f850d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/cpp_11/001_meaning_keywords_README.md b/cpp_11/001_meaning_keywords_README.md new file mode 100644 index 0000000000000000000000000000000000000000..83b88ed7a013df8ee4068cbfee2cb0a8669d47be --- /dev/null +++ b/cpp_11/001_meaning_keywords_README.md @@ -0,0 +1,339 @@ +####

C++11含义变化或者新增含义关键字

+ +#####

auto

+ +C++11标准和C++98/03标准的auto是不同的。C++98/03标准中,auto表示自动储存类型 ;C++11标准中,auto表示由编译器静态判断其应有的类型。 + +C++98 auto + +```C++ +int a =10 ; //拥有自动生命期 +auto int b = 20 ;//拥有自动生命期 +static int c = 30 ;//延长了生命期 +``` + +C++11 auto + +auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype。 + +```C++ +int a = 10; +auto b = a;//自动推断类型,b为 int类型 +auto c = 1.9;//自动推断类型,c为double类型 +auto d = 1.2e12L;//自动推断类型,d 是 long double +``` + +
+ +#####

class

+ +C++11中对类(class)新增的特性: +* default/delete 控制默认函数 +* override /final 强制重写/禁止重写虚函数 +* 委托构造函数 Delegating constructors +* 继承的构造函数 Inheriting constructors +* 类内部成员的初始化 Non-static data member initializers +* 移动构造和移动赋值 + +
+ +#####

default

+ +在C+11中,对于defaulted函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,也可免除程序员手动定义该函数的工作量。 + +C++的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数,而需要程序员手动编写,加大了程序员的工作量。并且手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。 + +C++11标准引入了一个新特性:defaulted函数。程序员只需在函数声明后加上”=default;”,就可将该函数声明为defaulted函数,编译器将为显式声明的defaulted函数自动生成函数体。 + +defaulted函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。 + +defaulted函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。 + +```C++ +#include "default.hpp" +#include + +class Foo +{ + Foo(int x); // Custom constructor + Foo() = default; // The compiler will now provide a default constructor for class Foo as well +}; + + +struct A +{ + int x; + A(int x = 1) : x(x) {} // user-defined default constructor +}; + +struct B : A +{ + // B::B() is implicitly-defined, calls A::A() +}; + +struct C +{ + A a; + // C::C() is implicitly-defined, calls A::A() +}; + +struct D : A +{ + D(int y) : A(y) {} + // D::D() is not declared because another constructor exists +}; + +struct E : A +{ + E(int y) : A(y) {} + E() = default; // explicitly defaulted, calls A::A() +}; + +struct F +{ + int& ref; // reference member + const int c; // const member + // F::F() is implicitly defined as deleted +}; + +int test_default1() +{ + A a; + B b; + C c; + // D d; // compile error + E e; + // F f; // compile error + + return 0; +} + +/// +struct widget +{ + widget() = default; + + inline widget& operator=(const widget&); +}; + +// Notice that you can default a special member function outside the body of a class as long as it’s inlinable. +inline widget& widget::operator=(const widget&) = default; +``` + +
+ +#####

delete

+ +C++11 中,可在想要 “禁止使用” 的特殊成员函数声明后加 “= delete”,而需要保留的加 "= default" 或者不采取操作 +```C++ +class LeafOfTree{ +public: +  LeafOfTree() = default; + ~LeafOfTree() = default;   + LeafOfTree(const LeafOfTree&) = delete;  // mark copy ctor or copy assignment operator as deleted functions +  LeafOfTree & operator=(const LeafOfTree&) = delete; +}; +``` + +delete 的扩展 +C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数 + +
+ +#####

export

+ +C++11 中,不使用并保留该关键词 + +
+ +#####

extern

+ +外部模板 +```C++ +extern templatevoid(T t); +``` + +
+ +#####

inline

+ +C++11中引入了内联命名空间(inline namespace),它的特点就是不需要使用using语句就可以直接在外层命名空间使用该命名空间内部的内容,而且无需使用命名空间前缀。 +```C++ +inline namespace inline_namespacel{ + class Inlinel{ + public: + int iv; + }; +} +namespace inline_namespaceli + class Inline2{ + public: + double dv; + }; +} +``` + +内联命名空间的声明方法就是在原来的声明语法前面增加inline关键字。除此之外上面代码还有以下特点: + +两处声明的命名空间同名,它们同属一个命名空间。这是C++命名空间从来就有的特性。 + +第一次声明命名空间时使用了inline关键字,这叫显式内联;第二次没有使用inline关键字,但是由于第一次已经声明了inline,这里声明的还是内联命名空间。这种情况成为隐式内联。 + +内联命名空间声明之后,就可以在外层命名空间不适用前缀而直接使用它们了。 + +```C++ +namespace inline_test{ + inline namespace inline_namespace1{ + class Inlinel { + public : + int iv; + }; + } + namespace inline_namespace1{ + class Inline2{ + public : + double dv ; + }; + } + void test_inline_namespace(){ + Inlinel inl; + inl.iv = 5; + Inline2 in2;in2.dv = 2; + } +} +void test_inline_namespace2(){ + inline_test::Inlinel inl; + in1.iv = 5; + inline_test::Inline2 in2; + in2.dv = 2; +} +``` +上述代码中test_inline_namespace处在linline_namespace1的外层,所以可以直接使用Inline1和Inline2。test_inline_namespace2处在更外层,这时也只是需要使用外层命名空间inline_test前缀即可。 +看起来inline_namespace就像不存在一样。 + +
+ +#####

mutable

+ +C++11中的mutable是用来修改const函数中的不可修改类成员的缺陷 + +```C++ +class Log{ + +public: + // + void print(const std::string& str) const + { + printf("%s", str_cstr()); + //统计输出次数 + printNums++; + } + +private: + //这里必须声明为mutable + mutable int printNums; +} +``` + +
+ +#####

sizeof

+ +在标准C++,sizeof可以作用在对象以及类别上。但是不能够做以下的事: +```C++ +struct someType { otherType member; } ; +sizeof(SomeType: :member); //直接由someType型别取得非静态成员的大小,C++03不行。C++11允哥 +``` +这会传回OtherType的大小。C++03并不允许这样做,所以会引发编译错误。C++11将会允许这种使用。 + +```C++ +#include +using namespace std; +struct Empty{}; +struct Base{int a;}; +struct Derived:Base +{ + int b; +}; +struct Bit +{ + unsigned bit:1; +}; +int main() +{ + Empty e; + Derived d; + Base& b = d; + Bit bit; + cout << sizeof(e) << endl; + cout << sizeof(Empty) << endl; + cout << sizeof(&e) << endl; + cout << sizeof(Derived) << endl; + cout << sizeof(d) << endl; + cout << sizeof(void) << endl;//BAD + return 0; +} +``` + +
+ +#####

struct

+ +C++11 struct可以给每个成员变量赋予默认的初始值 +```C++ +struct Student{ + char* name = nullptr; + unsigned int age = 15; + int number = 21509111; +}; +``` +所有声明的新结构体对象就是默认上面的值。 + +
+ +#####

using

+ +* using 在 C++11之前主要用于名字空间、类型、函数与对象的引入,实际上是去除作用域的限制。 +```C++ +//引入名字空间 +using namespace std; +//引入类型 +using std::iostream; +//引入函数 +using std::to_string; +//引入对象 +using std::cout; +``` + +* 通过using引入函数可以解除函数隐藏 +“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: +1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆) +2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) +使用了using关键字,就可以避免1的情况,是的父类同名函数在子类中得以重载,不被隐藏 +```C++ +class Base{ +public: + void func() { cout << "in Base::func()" << endl; } + void func(int n) { cout << "in Base::func(int)" << endl;} +}; + +class Sub : public Base { +public: + using Base::func; //引入父类所有同名函数func,解除函数隐藏 + void func() { cout<<"in Sub::func()"<; //定义模板别名,注意typedef无法定义模板别名,因为typedef只能作用于具体类型而非模板 +``` + diff --git a/cpp_11/001_new_keywords_README.md b/cpp_11/001_new_keywords_README.md new file mode 100644 index 0000000000000000000000000000000000000000..a16db2809b8df3ab7a93aff404fc6ad9e16cde75 --- /dev/null +++ b/cpp_11/001_new_keywords_README.md @@ -0,0 +1,565 @@ +####

C++11新增关键字

+ +#####

thread_local

+ +thread_local是C++11增加的存储类指定符 + +C++中有4种存储周期: + * automatic + * static + * dynamic + * thread + +有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。 + +那么,哪些变量可以被声明为thread_local?可以是以下3类: + + * 命名空间下的全局变量 + * 类的static成员变量 + * 本地变量 + +thread_local案例 + +```C++ +#include +#include +#include +#include + +thread_local unsigned int rage = 1; +std::mutex cout_mutex; + +void increase_rage(const std::string& thread_name) { + ++rage; // 在锁外修改 OK ;这是线程局域变量 + std::lock_guard lock(cout_mutex); + std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; +} + +void test() { + thread_local int i = 0; + printf("id=%d, n=%d\n", std::this_thread::get_id(), i); + i++; +} + +void test2() { + test(); + test(); +} + +int main() { + std::thread a(increase_rage, "a"), b(increase_rage, "b"); + + { + std::lock_guard lock(cout_mutex); + std::cout << "Rage counter for main: " << rage << '\n'; + } + + a.join(); + b.join(); + + std::thread t1(test); + std::thread t2(test); + t1.join(); + t2.join(); + + std::thread t3(test2); + t3.join(); + + system("pause"); + return 0; +} +``` + +
+ +#####

static_assert

+ +```C++ +struct MyClass +{ + char m_value; +}; + +struct MyEmptyClass +{ + void func(); +}; + +// 确保MyEmptyClass是一个空类(没有任何非静态成员变量,也没有虚函数) +static_assert(std::is_empty::value, "empty class needed"); + +//确保MyClass是一个非空类 +static_assert(!std::is_empty::value, "non-empty class needed"); + +template +class MyTemplate +{ + // 确保模板参数T是一个非空类 + static_assert( + !std::is_empty::value, + "T should be n non-empty class" + ); + + // 确保模板参数U是一个空类 + static_assert( + std::is_empty::value, + "U should be an empty class" + ); + + // 确保模板参数V是从std::allocator直接或间接派生而来, + // 或者V就是std::allocator + static_assert( + std::is_base_of, V>::value, + "V should inherit from std::allocator" + ); + +}; + +// 仅当模板实例化时,MyTemplate里面的那三个static_assert才会真正被演算, +// 藉此检查模板参数是否符合期望 +template class MyTemplate>; +``` + +
+ +#####

nullptr

+ +nullptr关键字用于标识空指针,是std::nullptr_t类型的(constexpr)变量。它可以转换成任何指针类型和bool布尔类型(主要是为了兼容普通指针可以作为条件判断语句的写法),但是不能被转换为整数。 +```C++ +char *p1 = nullptr; // 正确 +int *p2 = nullptr; // 正确 +bool b = nullptr; // 正确. if(b)判断为false +int a = nullptr; // error +``` + +
+ +#####

noexcept

+ +noexcept有两类作用:noexcept指定符和noexcept运算符 + +* noexcept 指定符 +```C++ +void f() noexcept; // 函数 f() 不抛出 +void (*fp)() noexcept(false); // fp 指向可能抛出的函数 +void g(void pfa() noexcept); // g 接收指向不抛出的函数的指针 +// typedef int (*pf)() noexcept; // 错误 +``` + +* noexcept运算符 +```C++ +#include +#include +#include + +// noexcept 运算符 +void may_throw() {}; +void no_throw() noexcept {}; +auto lmay_throw = [] {}; +auto lno_throw = []() noexcept {}; + +class T { +}; +class T1 { +public: + ~T1() {} +}; +class T2 { +public: + ~T2() {} + int v; +}; +class T3 { +public: + ~T3() {} + std::vector v; +}; +class T4 { +public: + std::vector v; +}; + +int main() +{ + T t; + T1 t1; + T2 t2; + T3 t3; + T4 t4; + + std::vector vc; + + std::cout << std::boolalpha + << "Is may_throw() noexcept? " << noexcept(may_throw()) << '\n' + << "Is no_throw() noexcept? " << noexcept(no_throw()) << '\n' + << "Is lmay_throw() noexcept? " << noexcept(lmay_throw()) << '\n' + << "Is lno_throw() noexcept? " << noexcept(lno_throw()) << '\n' + << "Is ~T1() noexcept? " << noexcept(std::declval().~T1()) << '\n' + << '\n' + << '\n' + + << "Is T(rvalue T) noexcept? " << noexcept(T(std::declval())) << '\n' + << "Is T(lvalue T) noexcept? " << noexcept(T(t)) << '\n' + << '\n' + + << "Is T1(rvalue T1) noexcept? " << noexcept(T1(std::declval())) << '\n' + << "Is T1(lvalue T1) noexcept? " << noexcept(T1(t1)) << '\n' + << '\n' + + << "Is T2(rvalue T2) noexcept? " << noexcept(T2(std::declval())) << '\n' + << "Is T2(lvalue T2) noexcept? " << noexcept(T2(t2)) << '\n' + << '\n' + + << "Is T3(rvalue T3) noexcept? " << noexcept(T3(std::declval())) << '\n' + << "Is T3(lvalue T3) noexcept? " << noexcept(T3(t3)) << '\n' + << '\n' + + << "Is T4(rvalue T4) noexcept? " << noexcept(T4(std::declval())) << '\n' + << "Is T4(lvalue T4) noexcept? " << noexcept(T4(t4)) << '\n' + << '\n' + + << "Is std::vector(rvalue std::vector) noexcept? " << noexcept(std::vector(std::declval>())) << '\n' + << "Is std::vector(lvalue std::vector) noexcept? " << noexcept(std::vector(vc)) << '\n'; + + system("pause"); + return 0; +} +``` + +
+ +#####

decltype

+ +decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。 +decltype用法 +* 基本用法 +```C++ +int getSize(); + +int main(void) +{ + int tempA = 2; + + /*1.dclTempA为int*/ + decltype(tempA) dclTempA; + /*2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize,*/ + decltype(getSize()) dclTempB; + + return 0; +} +``` + +* 与const结合 +```C++ +double tempA = 3.0; + const double ctempA = 5.0; + const double ctempB = 6.0; + const double *const cptrTempA = &ctempA; + + /*1.dclTempA推断为const double(保留顶层const,此处与auto不同)*/ + decltype(ctempA) dclTempA = 4.1; + /*2.dclTempA为const double,不能对其赋值,编译不过*/ + dclTempA = 5; + /*3.dclTempB推断为const double * const*/ + decltype(cptrTempA) dclTempB = &ctempA; + /*4.输出为4(32位计算机)和5*/ + cout< + +#####

constexpr

+ +constexpr意义 +将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式(不会改变,在编译过程中就能得到计算结果的表达式)。是一种比const更强的约束,这样可以得到更好的效率和安全性。 + +constexpr用法 +* 修饰函数 +```C++ +/*1.如果size在编译时能确定,那么返回值就可以是constexpr,编译通过*/ +constexpr int getSizeA(int size) +{ + return 4*size; +} +/*2.编译通过,有告警:在constexpr中定义变量*/ +constexpr int getSizeB(int size) +{ + int index = 0; + return 4; +} +/*3.编译通过,有告警:在constexpr中定义变量(这个有点迷糊)*/ +constexpr int getSizeC(int size) +{ + constexpr int index = 0; + return 4; +} +/*4.编译通过,有告警:使用了if语句(使用switch也会告警)*/ +constexpr int getSizeD(int size) +{ + if(0) + {} + return 4; +} +/*5.定义变量并且没有初始化,编译不过*/ +constexpr int getSizeE(int size) +{ + int index; + return 4; +} +/*6.rand()为运行期函数,不能在编译期确定,编译不过*/ +constexpr int getSizeF(int size) +{ + return 4*rand(); +} +/*7.使用了for,编译不过*/ +constexpr int getSizeG(int size) +{ + for(;0;) + {} + return 4*rand(); +} +``` + +* 修改类型 +```C++ +int tempA; +cin>>tempA; + +const int ctempA = 4; +const int ctempB = tempA; +/*1.可以再编译器确定,编译通过*/ +constexpr int conexprA = 4; +constexpr int conexprB = conexprA + 1; +constexpr int conexprC = getSizeA(conexprA); +constexpr int conexprD = ctempA; +/*2.不能在编译期决定,编译不过*/ +constexpr int conexprE = tempA; +constexpr int conexprF = ctempB; +``` + +* 修饰指针 +```C++ +int g_tempA = 4; +const int g_conTempA = 4; +constexpr int g_conexprTempA = 4; + +int main(void) +{ + int tempA = 4; + const int conTempA = 4; + constexpr int conexprTempA = 4; + + /*1.正常运行,编译通过*/ + const int *conptrA = &tempA; + const int *conptrB = &conTempA; + const int *conptrC = &conexprTempA; + /*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ + constexpr int *conexprPtrA = &tempA; + constexpr int *conexprPtrB = &conTempA + constexpr int *conexprPtrC = &conexprTempA; + /*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/ + constexpr int *conexprPtrD = &g_tempA; + constexpr int *conexprPtrE = &g_conTempA + constexpr int *conexprPtrF = &g_conexprTempA; + /*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ + constexpr const int *conexprConPtrA = &tempA; + constexpr const int *conexprConPtrB = &conTempA; + constexpr const int *conexprConPtrC = &conexprTempA; + /*5.正常运行,编译通过*/ + constexpr const int *conexprConPtrD = &g_tempA; + constexpr const int *conexprConPtrE = &g_conTempA; + constexpr const int *conexprConPtrF = &g_conexprTempA; + + return 0; +} + ``` + +* 修饰引用 +```C++ +int g_tempA = 4; +const int g_conTempA = 4; +constexpr int g_conexprTempA = 4; + +int main(void) +{ + int tempA = 4; + const int conTempA = 4; + constexpr int conexprTempA = 4; + /*1.正常运行,编译通过*/ + const int &conptrA = tempA; + const int &conptrB = conTempA; + const int &conptrC = conexprTempA; + /*2.有两个问题:一是引用到局部变量,不能再编译器确定;二是conexprPtrB和conexprPtrC应该为constexpr const类型,编译不过*/ + constexpr int &conexprPtrA = tempA; + constexpr int &conexprPtrB = conTempA + constexpr int &conexprPtrC = conexprTempA; + /*3.第一个编译通过,后两个不通过,原因是因为conexprPtrE和conexprPtrF应该为constexpr const类型*/ + constexpr int &conexprPtrD = g_tempA; + constexpr int &conexprPtrE = g_conTempA; + constexpr int &conexprPtrF = g_conexprTempA; + /*4.正常运行,编译通过*/ + constexpr const int &conexprConPtrD = g_tempA; + constexpr const int &conexprConPtrE = g_conTempA; + constexpr const int &conexprConPtrF = g_conexprTempA; + + return 0; +} +``` + +
+ +#####

char16_t和char32_t

+ +char16_t和char32_t: + +产生原因: +随着编程人员日益的熟悉Unicode,类型wchar_t显然已经满足不了需求,在计算机系统上进行的编码字符和字符串编码时,仅仅使用Unicode码点显然是不够的。 +比如:如果在进行字符串编码时,如果有特定长度和符号特征的类型将很有帮助,而类型wchar_t的长度和符号特征随实现而已。 +因此C++11新增了类型char16_t,char32_t。 + +char16_t:无符号类型,长16位, +char32_t无符号类型,长32位 + +C++11使用前缀u表示char16_t字符常量和字符串常量如:u‘L’;u“lilili”; +C++11使用前缀U表示char32_t字符常量和字符串常量如:U'L';U"lilili"; + +类型char16_t与/u00F6形式的通用字符名匹配, +类型char32_t与/U0000222B形式的通用字符名匹配。 +前缀u和U分别指出字符字面值的类型为char16_t和char32_t。 + +注意: +如果你在VS中使用char16_t或者char32_t的话,不要加前缀u或者U只能加前缀L. + +
+ +#####

alignof和alignas

+ +C++11新引入操作符alignof, 对齐描述符alignas,基本对齐值 alignof(std::max_align_t) + +alignas可以接受常量表达式和类型作为参数,可以修饰变量、类的数据成员等,不能修饰位域和用register申明的变量。一般往大对齐。 + +```C++ +struct s3 +{ + char s; + double d; + int i; +}; + + +struct s11 +{ + alignas(16) char s; + int i; +}; + +struct s12 +{ + alignas(16) char s; + int i; +}; + + +// alignof +cout << "-------------------alignof---------------------" << endl; +// 基本对齐值 +cout << "alignof(std::max_align_t) " << alignof(std::max_align_t) << endl; +cout << endl; +cout << "-------basic type" << endl; +cout << "alignof(char) " << alignof(char) << endl; +cout << "alignof(int) " << alignof(int) << endl; +cout << "alignof(double) " << alignof(double) << endl; + +cout << endl; +cout << "-------struct" << endl; +cout << "alignof(s1) " << alignof(s1) << endl; +cout << "alignof(s2) " << alignof(s2) << endl; +cout << "alignof(s3) " << alignof(s3) << endl; + +cout << endl; +cout << endl; + +// alignas +cout << "-------------------alignas---------------------" << endl; +cout << "alignof(s1) " << alignof(s1) << endl; +cout << "alignof(s11) " << alignof(s11) << endl; +cout << "alignof(s12) " << alignof(s12) << endl; + +cout << "sizeof(s1) " << sizeof(s1) << endl; +cout << "sizeof(s11) " << sizeof(s11) << endl; +cout << "sizeof(s12) " << sizeof(s12) << endl; +``` + +//结果如下: +```C++ +-------------------alignof--------------------- +alignof(std::max_align_t) 8 + +-------basic type +alignof(char) 1 +alignof(int) 4 +alignof(double) 8 + +-------struct +alignof(s1) 4 +alignof(s2) 8 +alignof(s3) 8 + + +-------------------alignas--------------------- +alignof(s1) 4 +alignof(s11) 16 +alignof(s12) 16 +sizeof(s1) 4 +sizeof(s11) 16 +sizeof(s12) 16 +``` diff --git a/cpp_11/002_grammar_POD.cpp b/cpp_11/002_grammar_POD.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a505ab88fd91c9873f504cbe7d0394849148fcb2 --- /dev/null +++ b/cpp_11/002_grammar_POD.cpp @@ -0,0 +1,41 @@ +//一个对象既是普通类型(Trivial Type)又是标准布局类型(Standard-layout Type)那么这个对象就是POD类型。 +//为什么我们需要 POD 类型满足这些条件呢,POD 类型在源码层级的操作上兼容于 ANSI C。POD 对象与 C 语言中的 +//对象具有一些共同的特性,包括初始化、复制、内存布局与寻址: +//(1)可以使用字节赋值,比如用 memset、memcpy 对 POD 类型进行赋值操作; +//(2)对 C 内存布局兼容,POD 类型的数据可以使用 C 函数进行操作且总是安全的; +//(3)保证了静态初始化的安全有效,静态初始化可以提高性能,如将 POD 类型对象放入 BSS 段默认初始化为 0。 + + +//POD类型的二进制拷贝案例 + +#include +using namespace std; + +class A { +public: + int x; + double y; +}; + +int main() { + if (std::is_pod
::value) { + std::cout << "before" << std::endl; + A a; + a.x = 8; + a.y = 10.5; + std::cout << a.x << std::endl; + std::cout << a.y << std::endl; + + size_t size = sizeof(a); + char *p = new char[size]; + memcpy(p, &a, size); + A *pA = (A*)p; + + std::cout << "after" << std::endl; + std::cout << pA->x << std::endl; + std::cout << pA->y << std::endl; + + delete p; + } + return 0; +} diff --git a/cpp_11/002_grammar_SFINAE.cpp b/cpp_11/002_grammar_SFINAE.cpp new file mode 100644 index 0000000000000000000000000000000000000000..67f6a44c814db969c4dd1a066c31c7bdb56d0b23 --- /dev/null +++ b/cpp_11/002_grammar_SFINAE.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +namespace detail { struct inplace_t{}; } +void* operator new(std::size_t, void* p, detail::inplace_t) { + return p; +} + +// enabled via the return type +template +typename std::enable_if::value>::type + construct(T* t,Args&&... args) +{ + std::cout << "constructing trivially constructible T\n"; +} + +// enabled via a parameter +template +void destroy(T* t, + typename std::enable_if::value>::type* = 0) +{ + std::cout << "destroying trivially destructible T\n"; +} + +// enabled via a template parameter +template{} && + (std::is_class{} || std::is_union{}), + int>::type = 0> +void destroy(T* t) +{ + std::cout << "destroying non-trivially destructible T\n"; + t->~T(); +} +int main() +{ + std::aligned_union_t<0,int,std::string> u; + + construct(reinterpret_cast(&u)); + destroy(reinterpret_cast(&u)); + + construct(reinterpret_cast(&u),"Hello"); + destroy(reinterpret_cast(&u)); +} diff --git a/cpp_11/002_grammar_Uniform_initialization_syntax_and_semantics.cpp b/cpp_11/002_grammar_Uniform_initialization_syntax_and_semantics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b2948754fef78e637b38a2404b0e329e331f187 --- /dev/null +++ b/cpp_11/002_grammar_Uniform_initialization_syntax_and_semantics.cpp @@ -0,0 +1,40 @@ +// C++中的初始化风格,大体有如下形式: +// int a = 2; //"赋值风格"的初始化 +// int aa [] = { 2, 3 }; //用初始化列表进行的赋值风格的初始化 +// complex z(1, 2); //"函数风格"的初始化 + +// C++ 11 中,允许通过以花括号的形式来调用构造函数 +// int a = { 2 }; +// int aa [] = { 2, 3 }; +// complex z = { 1, 2 }; + +#include +using namespace std; + +class complex +{ +public: + complex(int x, int y) + :_x(x),_y(y){} + private: + int _x; + int _y; +}; + +complex func(const complex & com) +{ + return {1,2}; +} + +int main(int argc, char *argv[]) +{ + int a = 10; + int aa[] = {1,2,3}; + complex com(1,2); + int a_ = {1}; + int aa_[] = {1,2,3}; + complex com_ = {1,2}; + func({1,2}); + return 0; +} + diff --git a/cpp_11/002_grammar_align.cpp b/cpp_11/002_grammar_align.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80a7d1bf8ffd51061358c2b974a7ab407d399aa2 --- /dev/null +++ b/cpp_11/002_grammar_align.cpp @@ -0,0 +1,46 @@ +#include +#include + +template +struct MyAllocator +{ + char data[N]; + void* p; + std::size_t sz; + MyAllocator() : p(data), sz(N) {} + template + T* aligned_alloc(std::size_t a = alignof(T)) + { + if (std::align(a, sizeof(T), p, sz)) + { + T* result = reinterpret_cast(p); + p = (char*)p + sizeof(T); + sz -= sizeof(T); + return result; + } + return nullptr; + } +}; + +int main() +{ + MyAllocator<64> a; + + // allocate a char + char* p1 = a.aligned_alloc(); + if (p1) + *p1 = 'a'; + std::cout << "allocated a char at " << (void*)p1 << '\n'; + + // allocate an int + int* p2 = a.aligned_alloc(); + if (p2) + *p2 = 1; + std::cout << "allocated an int at " << (void*)p2 << '\n'; + + // allocate an int, aligned at 32-byte boundary + int* p3 = a.aligned_alloc(32); + if (p3) + *p3 = 2; + std::cout << "allocated an int at " << (void*)p3 << " (32 byte alignment)\n"; +} diff --git a/cpp_11/002_grammar_alignas.cpp b/cpp_11/002_grammar_alignas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99d03d1d041d2240b57e18aa22e52fc7bf193628 --- /dev/null +++ b/cpp_11/002_grammar_alignas.cpp @@ -0,0 +1,10 @@ +#include + +int main() +{ + + alignas(double) unsigned char c[1024]; //字符数组,但是却以double数据的形式对齐数据 + alginas(16) char d[100]; //以16字节对齐 + + return 0; +} diff --git a/cpp_11/002_grammar_aligned_storage.cpp b/cpp_11/002_grammar_aligned_storage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b72a5f68fea3fac92cc8d2ca1afd92dae7c8db5 --- /dev/null +++ b/cpp_11/002_grammar_aligned_storage.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +template +class static_vector +{ + // properly aligned uninitialized storage for N T's + typename std::aligned_storage::type data[N]; + std::size_t m_size = 0; + +public: + // Create an object in aligned storage + template void emplace_back(Args&&... args) + { + if( m_size >= N ) // possible error handling + throw std::bad_alloc{}; + + // construct value in memory of aligned storage + // using inplace operator new + new(&data[m_size]) T(std::forward(args)...); + ++m_size; + } + + // Access an object in aligned storage + const T& operator[](std::size_t pos) const + { + // note: needs std::launder as of C++17 + return *reinterpret_cast(&data[pos]); + } + + // Delete objects from aligned storage + ~static_vector() + { + for(std::size_t pos = 0; pos < m_size; ++pos) { + // note: needs std::launder as of C++17 + reinterpret_cast(&data[pos])->~T(); + } + } +}; + +int main() +{ + static_vector v1; + v1.emplace_back(5, '*'); + v1.emplace_back(10, '*'); + std::cout << v1[0] << '\n' << v1[1] << '\n'; +} diff --git a/cpp_11/002_grammar_alignment_of.cpp b/cpp_11/002_grammar_alignment_of.cpp new file mode 100644 index 0000000000000000000000000000000000000000..304e30c6cd366eb4301c56c3fbc552e58ec4cd70 --- /dev/null +++ b/cpp_11/002_grammar_alignment_of.cpp @@ -0,0 +1,12 @@ +// alignment_of example +#include +#include + +int main() { + std::cout << "alignment_of:" << std::endl; + std::cout << "char: " << std::alignment_of::value << std::endl; + std::cout << "int: " << std::alignment_of::value << std::endl; + std::cout << "int[20]: " << std::alignment_of::value << std::endl; + std::cout << "long long int: " << std::alignment_of::value << std::endl; + return 0; +} diff --git a/cpp_11/002_grammar_alignof.cpp b/cpp_11/002_grammar_alignof.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8546424da8609b5ccc2395281679f7636e558307 --- /dev/null +++ b/cpp_11/002_grammar_alignof.cpp @@ -0,0 +1,28 @@ +#include + +struct Foo { + int i; + float f; + char c; +}; + +// Note: `alignas(alignof(long double))` below can be simplified to simply +// `alignas(long double)` if desired. +struct alignas(alignof(long double)) Foo2 { + // put your definition here +}; + +struct Empty {}; + +struct alignas(64) Empty64 {}; + +int main() +{ + std::cout << "Alignment of" "\n" + "- char : " << alignof(char) << "\n" + "- pointer : " << alignof(int*) << "\n" + "- class Foo : " << alignof(Foo) << "\n" + "- class Foo2 : " << alignof(Foo2) << "\n" + "- empty class : " << alignof(Empty) << "\n" + "- alignas(64) Empty: " << alignof(Empty64) << "\n"; +} diff --git a/cpp_11/002_grammar_c99.cpp b/cpp_11/002_grammar_c99.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37fefd6eadb52bab005728b13383667b0a7193a3 --- /dev/null +++ b/cpp_11/002_grammar_c99.cpp @@ -0,0 +1,18 @@ +//C++对以下C99特性的支持纳入了新标准之中: +//1、C99中的预定义宏 +//2、__func__预定义标识符 +//3、_Pragma操作符 +//4、不定参数宏定义以及__VA_ARGS__ +//5、宽窄字符串连接 + +#include +using namespace std; + +int main() +{ + cout << "Standerd Clib" << __STDC_HOSTED__ << endl; + cout << "Standerd C" << __STDC__ << endl; + //cout << "C Standerd version " << __STDC_VERSION__ << endl; + //cout << "ISO/IEC" << __STDC_ISO_10646__ << endl; + return 0; +} diff --git a/cpp_11/002_grammar_constexpr.cpp b/cpp_11/002_grammar_constexpr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..851a4f2285060b424331d1c9e5bb8469b495665d --- /dev/null +++ b/cpp_11/002_grammar_constexpr.cpp @@ -0,0 +1,33 @@ +int g_tempA = 4; +const int g_conTempA = 4; +constexpr int g_conexprTempA = 4; + +int main(void) +{ + int tempA = 4; + const int conTempA = 4; + constexpr int conexprTempA = 4; + + /*1.正常运行,编译通过*/ + const int *conptrA = &tempA; + const int *conptrB = &conTempA; + const int *conptrC = &conexprTempA; + /*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ + //constexpr int *conexprPtrA = &tempA; + //constexpr int *conexprPtrB = &conTempA + //constexpr int *conexprPtrC = &conexprTempA; + /*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/ + constexpr int *conexprPtrD = &g_tempA; + constexpr int *conexprPtrE = &g_conTempA + constexpr int *conexprPtrF = &g_conexprTempA; + /*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ + //constexpr const int *conexprConPtrA = &tempA; + //constexpr const int *conexprConPtrB = &conTempA; + //constexpr const int *conexprConPtrC = &conexprTempA; + /*5.正常运行,编译通过*/ + constexpr const int *conexprConPtrD = &g_tempA; + constexpr const int *conexprConPtrE = &g_conTempA; + constexpr const int *conexprConPtrF = &g_conexprTempA; + + return 0; +} diff --git a/cpp_11/002_grammar_cpluscplus.h b/cpp_11/002_grammar_cpluscplus.h new file mode 100644 index 0000000000000000000000000000000000000000..165612c39fa9fc3f5149980c57e09f49888b5439 --- /dev/null +++ b/cpp_11/002_grammar_cpluscplus.h @@ -0,0 +1,11 @@ +//_cplusplus宏经常出现在C与C++混合编写的代码中, 一般放在头文件中,比如 + +#ifdef __cplusplus +extern "C" { +#endif + +//some code ... + +#ifdef __cplusplus +} +#endif diff --git a/cpp_11/002_grammar_cpp_11_pragma.cpp b/cpp_11/002_grammar_cpp_11_pragma.cpp new file mode 100644 index 0000000000000000000000000000000000000000..080af032915b4c373793d986506ffd95601c2194 --- /dev/null +++ b/cpp_11/002_grammar_cpp_11_pragma.cpp @@ -0,0 +1,3 @@ +int main() { + +} diff --git a/cpp_11/002_grammar_explicit.cpp b/cpp_11/002_grammar_explicit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..379fd0d30c0d304d3f8a19ec426d05c338616608 --- /dev/null +++ b/cpp_11/002_grammar_explicit.cpp @@ -0,0 +1,32 @@ +#include +using namespace std; + +class ConvertTo +{ +}; + +class Convertable +{ +public: + explicit operator ConvertTo () const + { + cout << "callCount : " << ++(m_nCallCnt) << endl; + return ConvertTo(); + } + + static int m_nCallCnt; +}; + +int Convertable::m_nCallCnt = 0; + +void Func(ConvertTo ct) { } + +int main() +{ + Convertable c; + ConvertTo ct(c); // 直接初始化,通过 +// ConvertTo ct2 = c; // 拷贝构造初始化,编译失败 + ConvertTo ct3 = static_cast(c); // 强制转化,通过 +// Func(c); // 拷贝构造初始化,编译失败 + system("pause"); +} diff --git a/cpp_11/002_grammar_extended_friend_syntax.cpp b/cpp_11/002_grammar_extended_friend_syntax.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ccf63ec51bd7c323bb413f2f612328620ac2db0 --- /dev/null +++ b/cpp_11/002_grammar_extended_friend_syntax.cpp @@ -0,0 +1,17 @@ +class Poly; +typedef Poly P; + +class LiLei +{ + friend class Poly; // C++98通过, C++11通过 +}; + +class Jim +{ + friend Poly; // C++98失败, C++11通过 +}; + +class HanMei +{ + friend P; // C++98失败, C++11通过 +}; diff --git a/cpp_11/002_grammar_extended_integer_types.cpp b/cpp_11/002_grammar_extended_integer_types.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6edf3f5555c202f2f1e622a01f1b667d365c060 --- /dev/null +++ b/cpp_11/002_grammar_extended_integer_types.cpp @@ -0,0 +1,11 @@ +//对于有符号的,下面的类型是等价的: +//long long、signed long long、long long int、signed long long int; 而unsigned long long 和 unsigned long long int 也是等价的。 + +//与 long long 整型相关的一共有3个: +//LONG_MIN、LONG_MAX 和ULONG_MAX, 它们分别代表了平台上最小的long long 值、最大的long long 值,以及最大的unsigned long long 值。 + +int main() { + long long int lli=-900000000000000LL; // 有符号的long long 变量lli + unsigned long long int ulli=-900000000000ULL; // 无符号的 unsigned long long 变量ulli + return 0; +} diff --git a/cpp_11/002_grammar_for_loop.cpp b/cpp_11/002_grammar_for_loop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24abe67983500aa941b2f709d7a49b9580e69da7 --- /dev/null +++ b/cpp_11/002_grammar_for_loop.cpp @@ -0,0 +1,42 @@ +#include +#include + +int main() +{ + //遍历字符串 + std::string str = “hello, world”; + for(auto ch : str) + { + std::cout << ch << std::endl; + } + //遍历str,输出每个字符,同时用上auto,简直是如虎添翼。(auto也是c++11的新特性) + + + //遍历数组 + int arr[] = {1, 2, 3, 4}; + for(auto i : arr) + { + std::cout<< i << std::endl; + } + //不用知道数组容器的大小,即可方便的遍历数组。 + + + //遍历stl 容器 + std::vector str_vec = {“i”, “like”, "google”}; + for(auto& it : str_vec) + { + it = “c++”; + } + //在这段程序中,可以返回引用值,通过引用可以修改容器内容。 + + + //遍历stl map + std::map hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}}; + for(auto it : hash_map) + { + std::cout << it.first << “\t” << it.second << std::endl; + } + //遍历map返回的是pair变量,不是迭代器。 + + return 0; +} diff --git a/cpp_11/002_grammar_initializer_lists01.cpp b/cpp_11/002_grammar_initializer_lists01.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e48e8eaead45354167f358b67fc435115ef9d40 --- /dev/null +++ b/cpp_11/002_grammar_initializer_lists01.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace std; + +//初始化列表 + +int main(int argc, char *argv[]) +{ + vector iv = {1,2,3,4,5}; + list li = {1,2,3,4,5}; + map mis = {{1,"c"},{2,"c++"}, + {3,"java"},{4,"scala"}, + {5,"python"}}; + mis.insert({6,"ruby"}); + // map::iterator itr = mis.begin(); + // for(; itr != mis.end(); ++itr) + // { + // cout<first<< itr->second< +#include +using namespace std; +template +class MyArray +{ +private: + vector m_Array; +public: + MyArray() { } + MyArray(const initializer_list& il) + { + for (auto x : il) + m_Array.push_back(x); + } +}; + +int main() +{ + MyArray foo = { 3, 4, 6, 9 }; + return 0; +} diff --git a/cpp_11/002_grammar_lamda.cpp b/cpp_11/002_grammar_lamda.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e98b9cd9a57b3b510a88137b8bee64912a39afb --- /dev/null +++ b/cpp_11/002_grammar_lamda.cpp @@ -0,0 +1,29 @@ +#include + +int main() { + int m = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5); + std::cout << "m:" << m << std::endl;    //输出m:16 + + std::cout << "n:" << [](int x, int y) { return x + y; }(5, 4) << std::endl; //输出n:9 + + auto gFunc = [](int x) -> function { return [=](int y) { return x + y; }; }; + auto lFunc = gFunc(4); + std::cout << lFunc(5) << std::endl; + + auto hFunc = [](const function& f, int z) { return f(z) + 1; }; + auto a = hFunc(gFunc(7), 8); + + int a = 111, b = 222; + auto func = [=, &b]()mutable { a = 22; b = 333; std::cout << "a:" << a << " b:" << b << std::endl; }; + + func(); + std::cout << "a:" << a << " b:" << b << std::endl; + + a = 333; + auto func2 = [=, &a] { a = 444; std::cout << "a:" << a << " b:" << b << std::endl; }; + func2(); + + auto func3 = [](int x) ->function { return [=](int y) { return x + y; }; };    + std::function f_display_42 = [](int x) { print_num(x); }; + f_display_42(44); +} diff --git a/cpp_11/002_grammar_lnline.cpp b/cpp_11/002_grammar_lnline.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31367c12f9341874903c8b2841b6d55b842255e4 --- /dev/null +++ b/cpp_11/002_grammar_lnline.cpp @@ -0,0 +1,17 @@ +namespace Parent +{ + + inline namespace Child1 + { + struct child1_data{int a;} ; + } + inline namespace Child2 + { + struct child2_data{int b;} ; + } + namespace child3 + { + child1_data data1; + child2_data data2; + } +} diff --git a/cpp_11/002_grammar_long_long.cpp b/cpp_11/002_grammar_long_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae36c3e151a0633a833196c1883e6f22fa381a0c --- /dev/null +++ b/cpp_11/002_grammar_long_long.cpp @@ -0,0 +1,15 @@ +/* +g++ -D__STDC_FORMAT_MACROS -o test_int64 -g -O0 002_grammar_long_long.cpp.cpp +*/ +#include +#include + +int main(int argc, char** argv){ + long long int lli=-900000000000000LL; // 有符号的long long 变量lli + unsigned long long int ulli=-900000000000ULL; // 无符号的 unsigned long long 变量ulli。 + + int64_t value = 0xFFFFFFFFFFFF; + printf("int64_t=%"PRId64", sizeof(int64_t)=%d\n", value, sizeof(int64_t)); + + return 0; +} diff --git a/cpp_11/002_grammar_max_align_t.cpp b/cpp_11/002_grammar_max_align_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73ec45f40e707e3c19ba56bdcfb68e85836381b1 --- /dev/null +++ b/cpp_11/002_grammar_max_align_t.cpp @@ -0,0 +1,6 @@ +#include +#include +int main() +{ + std::cout << alignof(std::max_align_t) << '\n'; +} diff --git a/cpp_11/002_grammar_move_semantics.cpp b/cpp_11/002_grammar_move_semantics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b5de595eb1c86f8d6a3941bdcd32cb6bf5f3e7d --- /dev/null +++ b/cpp_11/002_grammar_move_semantics.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +using namespace std; + +class MyString +{ +public: + static size_t CCtor; //统计调用拷贝构造函数的次数 + static size_t MCtor; //统计调用移动构造函数的次数 + static size_t CAsgn; //统计调用拷贝赋值函数的次数 + static size_t MAsgn; //统计调用移动赋值函数的次数 + +public: + // 构造函数 + MyString(const char* cstr=0){ + if (cstr) { + m_data = new char[strlen(cstr)+1]; + strcpy(m_data, cstr); + } + else { + m_data = new char[1]; + *m_data = '\0'; + } + } + + // 拷贝构造函数 + MyString(const MyString& str) { + CCtor ++; + m_data = new char[ strlen(str.m_data) + 1 ]; + strcpy(m_data, str.m_data); + } + // 移动构造函数 + MyString(MyString&& str) noexcept + :m_data(str.m_data) { + MCtor ++; + str.m_data = nullptr; //不再指向之前的资源了 + } + + // 拷贝赋值函数 =号重载 + MyString& operator=(const MyString& str){ + CAsgn ++; + if (this == &str) // 避免自我赋值!! + return *this; + + delete[] m_data; + m_data = new char[ strlen(str.m_data) + 1 ]; + strcpy(m_data, str.m_data); + return *this; + } + + // 移动赋值函数 =号重载 + MyString& operator=(MyString&& str) noexcept{ + MAsgn ++; + if (this == &str) // 避免自我赋值!! + return *this; + + delete[] m_data; + m_data = str.m_data; + str.m_data = nullptr; //不再指向之前的资源了 + return *this; + } + + ~MyString() { + delete[] m_data; + } + + char* get_c_str() const { return m_data; } +private: + char* m_data; +}; +size_t MyString::CCtor = 0; +size_t MyString::MCtor = 0; +size_t MyString::CAsgn = 0; +size_t MyString::MAsgn = 0; +int main() +{ + vector vecStr; + vecStr.reserve(1000); //先分配好1000个空间 + for(int i=0;i<1000;i++){ + vecStr.push_back(MyString("hello")); + } + cout << "CCtor = " << MyString::CCtor << endl; + cout << "MCtor = " << MyString::MCtor << endl; + cout << "CAsgn = " << MyString::CAsgn << endl; + cout << "MAsgn = " << MyString::MAsgn << endl; +} diff --git a/cpp_11/002_grammar_nullptr.cpp b/cpp_11/002_grammar_nullptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c787368d3b40acc43541fd424044b66a6e35030a --- /dev/null +++ b/cpp_11/002_grammar_nullptr.cpp @@ -0,0 +1,50 @@ +#include +using std::cout; +using std::endl; + +namespace myname { + +struct nullptr_t +{ + void * _; + + struct __nat {int __for_bool_;}; + + nullptr_t(int __nat::*) {} // 构造函数,参数可以是0(空指针),或者&__nat::__for_bool_ + + operator int __nat::*() const {return 0;} // 不理解为什么转换为bool型时会调用此转换函数 + // operator bool() const {return 0; /* or return false; */} // 为什么不这样实现? + + template + operator _Tp* () const {return 0;} // 转换为非成员变量指针、非成员函数指针的普通指针 + + template + operator _Tp _Up::* () const {return 0;} // 转换为成员变量指针或成员函数指针 + + friend bool operator==(nullptr_t, nullptr_t) {return true;} + friend bool operator!=(nullptr_t, nullptr_t) {return false;} + friend bool operator<(nullptr_t, nullptr_t) {return false;} + friend bool operator<=(nullptr_t, nullptr_t) {return true;} + friend bool operator>(nullptr_t, nullptr_t) {return false;} + friend bool operator>=(nullptr_t, nullptr_t) {return true;} +}; + +struct Test {}; + +} + +using namespace myname; + +inline nullptr_t __get_nullptr_t() {return nullptr_t(0);} +#define nullptr __get_nullptr_t() + +int main(int argc, char *argv[]) +{ + char *pch = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template nullptr_t::operator _Tp* () const + int *pint = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template nullptr_t::operator _Tp* () const + bool b = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => nullptr_t::operator int __nat::*() const + //int n = nullptr; // error: cannot convert 'myname::nullptr_t' to 'int' in initialization + int Test::*p = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template operator _Tp _Up::* () const + void (Test::*pfn)(int, char) = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template operator _Tp _Up::* () const + return 0; +} diff --git a/cpp_11/002_grammar_numeric_limits.cpp b/cpp_11/002_grammar_numeric_limits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8bc1ea4c97fe21067ac9f27800b98a87cee6d86 --- /dev/null +++ b/cpp_11/002_grammar_numeric_limits.cpp @@ -0,0 +1,65 @@ +#include "numeric_limits.hpp" +#include +#include + +int main() +{ + std::cout << std::boolalpha; + std::cout << "Minimum value for int: " << std::numeric_limits::min() << std::endl; + std::cout << "Maximum value for int: " << std::numeric_limits::max() << std::endl; + std::cout << "int is signed: " << std::numeric_limits::is_signed << std::endl; + std::cout << "Non-sign bits in int: " << std::numeric_limits::digits << std::endl; + std::cout << "int has infinity: " << std::numeric_limits::has_infinity << std::endl; + + std::cout << "Minimum value for float: " << std::numeric_limits::min() << std::endl; // min returns the smallest positive value the type can encode, not the lowest + std::cout << "Lowest value for float: " << std::numeric_limits::lowest() << std::endl; // the lowest value + std::cout << "Maximum value for float: " << std::numeric_limits::max() << std::endl; + std::cout << "float is signed: " << std::numeric_limits::is_signed << std::endl; + std::cout << "Non-sign bits in float: " << std::numeric_limits::digits << std::endl; + std::cout << "float has infinity: " << std::numeric_limits::has_infinity << std::endl; + + std::cout << "Minimum value for unsigned short: " << std::numeric_limits::min() << std::endl; + std::cout << "Maximum value for unsigned short: " << std::numeric_limits::max() << std::endl; + + std::cout << "is_specialized(float): " << std::numeric_limits::is_specialized << std::endl; + std::cout << "is_integer(float): " << std::numeric_limits::is_integer << std::endl; + std::cout << "is_exact(float): " << std::numeric_limits::is_exact << std::endl; + std::cout << "is_bounded(float): " << std::numeric_limits::is_bounded << std::endl; + std::cout << "is_modulo(float): " << std::numeric_limits::is_modulo << std::endl; + std::cout << "is_iec559(float): " << std::numeric_limits::is_iec559 << std::endl; + std::cout << "digits10(float): " << std::numeric_limits::digits10 << std::endl; + std::cout << "radix(float): " << std::numeric_limits::radix << std::endl; + std::cout << "min_exponent(float): " << std::numeric_limits::min_exponent << std::endl; + std::cout << "max_exponent(float): " << std::numeric_limits::max_exponent << std::endl; + std::cout << "min_exponent10(float): " << std::numeric_limits::min_exponent10 << std::endl; + std::cout << "max_exponent10(float): " << std::numeric_limits::max_exponent10 << std::endl; + std::cout << "epsilon(float): " << std::numeric_limits::epsilon() << std::endl; + std::cout << "round_style(float): " << std::numeric_limits::round_style << std::endl; + + std::cout << "The smallest nonzero denormalized value for float: " + << std::numeric_limits::denorm_min()<< std::endl; + std::cout << "The difference between 1 and the smallest value greater than 1 for float: " + << std::numeric_limits::epsilon()<< std::endl; + std::cout << "Whether float objects allow denormalized values: " + << std::numeric_limits::has_denorm << std::endl; + std::cout << "Whether float objects can detect denormalized loss: " + << std::numeric_limits::has_denorm_loss << std::endl; + std::cout << "Whether float objects have quiet_NaN: " + << std::numeric_limits::has_quiet_NaN << std::endl; + std::cout << "Whether float objects have a signaling_NaN: " + << std::numeric_limits::has_signaling_NaN << std::endl; + std::cout << "The base for type float is: " + << std::numeric_limits::radix << std::endl; + std::cout << "The maximum rounding error for type float is: " + << std::numeric_limits::round_error() << std::endl; + std::cout << "The rounding style for a double type is: " + << std::numeric_limits::round_style << std::endl; + std::cout << "The signaling NaN for type float is: " + << std::numeric_limits::signaling_NaN() << std::endl; + std::cout << "Whether float types can detect tinyness before rounding: " + << std::numeric_limits::tinyness_before << std::endl; + std::cout << "Whether float types have implemented trapping: " + << std::numeric_limits::traps << std::endl; + + return 0; +} diff --git a/cpp_11/002_grammar_pragma.cpp b/cpp_11/002_grammar_pragma.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bbe1dfb9a16aa19f3cfc0f0d19273207fda5575 --- /dev/null +++ b/cpp_11/002_grammar_pragma.cpp @@ -0,0 +1 @@ +__pragma("once") diff --git a/cpp_11/002_grammar_preventing_narrowing.cpp b/cpp_11/002_grammar_preventing_narrowing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cdbb0d45f1aa4b74784580f91e95c0e5c057ba23 --- /dev/null +++ b/cpp_11/002_grammar_preventing_narrowing.cpp @@ -0,0 +1,27 @@ +#include + +int main(int argc, char* argv[]) +{ + int a = 1.1; + int b{1}; + //int b{1,1}; + + float f1 = 1e40; + float f2{10.0}; + //float f2{1e40}; + + const int x = 1024, y = 1; + + int dd{x}; + char c = x; + // char d{x}; + + + char e = y; + char f{y}; + + return 0; +} + +//g++ -std=c++11 002_grammar_preventing_narrowing.cpp -o preventing_narrowing 编译 + diff --git a/cpp_11/002_grammar_random_device.cpp b/cpp_11/002_grammar_random_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..68dab396c5375598db64d1d1ad0c025b963f9ca5 --- /dev/null +++ b/cpp_11/002_grammar_random_device.cpp @@ -0,0 +1,29 @@ + +#include +#include +#include +#include + +std::vector randomGenerate(const unsigned low, const unsigned high) +{ + static std::default_random_engine e(time(0)); + static std::uniform_int_distribution u(low, high); + + std::vector vec; + for (int i = 0; i < 10; i++) + vec.push_back(u(e)); + return vec; +} + +int main() +{ + for (int i = 0; i < 10; i++) { + std::vector vec = randomGenerate(0, 30); + for (auto &i : vec) + std::cout << i << " "; + std::cout << std::endl; + } + + + return 0; +} diff --git a/cpp_11/002_grammar_raw_string.cpp b/cpp_11/002_grammar_raw_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2210ad368db078134c5825d922aa2fb68e372e79 --- /dev/null +++ b/cpp_11/002_grammar_raw_string.cpp @@ -0,0 +1,32 @@ +#include + +using std::cout; +using std::endl; +using namespace std; + +//原始字符串(raw string)就是字符表示的就是自己,引号和斜杠均无需\进行转义,这在需要输出很多引号和斜杠代码中很方便。 +//原始字符串是C++11新增的一个功能,程序中使用R"(a string)"来标识原始字符串: + +//原始字符串同时包含其它特点: +//1. 字符串中的换行符将在屏幕上如实显示。 +//2. 在表示字符串开头的"和(之间可以添加其它字符,不过必须在表示字符串结尾的)和"之间添加同样的字符。 + +string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754"; +string path2 = "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754"; +//更简洁的表示string path3 = R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)"; +string path4 = R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)"; + +int main(int argc, char *argv[]) +{ +  cout< +#include + +using namespace std; + +class MyString +{ +public: + MyString() :m_pData(NULL), m_nLen(0) + { + cout << "MyString()" << endl; + } + MyString(const char *pStr) // 允许隐式转换 + { + cout << "MyString(const char *pStr)" << endl; + m_nLen = strlen(pStr); + CopyData(pStr); + } + MyString(const MyString& other) + { + cout << "MyString(const MyString& other)" << endl; + if (!other.m_pData) + { + m_nLen = other.m_nLen; + DeleteData(); + CopyData(other.m_pData); + } + } + MyString& operator=(const MyString& other) + { + cout << "MyString& operator=(const MyString& other)" << endl; + if (this != &other) + { + m_nLen = other.m_nLen; + DeleteData(); + CopyData(other.m_pData); + } + + return *this; + } + + MyString(MyString&& other) + { + cout << "MyString(MyString&& other)" << endl; + m_nLen = other.m_nLen; + m_pData = other.m_pData; + other.m_pData = NULL; + } + + MyString& operator=(MyString&& other) + { + cout << "MyString& operator=(const MyString&& other)" << endl; + if (this != &other) + { + m_nLen = other.m_nLen; + m_pData = other.m_pData; + other.m_pData = NULL; + } + + return *this; + } + + ~MyString() + { + DeleteData(); + } + +private: + void CopyData(const char *pData) + { + if (pData) + { + m_pData = new char[m_nLen + 1]; + memcpy(m_pData, pData, m_nLen); + m_pData[m_nLen] = '\0'; + } + } + + void DeleteData() + { + if (m_pData != NULL) + { + delete[] m_pData; + m_pData = NULL; + } + } + +private: + char *m_pData; + size_t m_nLen; +}; + +MyString Fun() +{ + MyString str = "hello world"; + return str; +} +void main() +{ + MyString str1 = "hello"; + MyString str2(str1); + MyString str3 = Fun(); +} diff --git a/cpp_11/002_grammar_scoped_and_strongly_typed_enums.cpp b/cpp_11/002_grammar_scoped_and_strongly_typed_enums.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da4dfc5e2b5adfbaa10c9bdf635a193266473901 --- /dev/null +++ b/cpp_11/002_grammar_scoped_and_strongly_typed_enums.cpp @@ -0,0 +1,33 @@ +//强类型枚举以及c++11对原有枚举类型的扩展 +//声明强类型枚举,只需要在enum加上关键字class。 +//enum class Type { General, Light, Medium, Heavy }; + +//优点: +//(1)强作用域,强类型枚举成员的名称不会被输出到其父作用域空间 +//(2)转换限制,强类型枚举成员的值不可以与整形隐式地相互转换 +//(3)可以指定底层类型。强类型枚举默认的底层类型为int,但也可以显式地指定底层类型。 + +//比如: +//enum class Type: char { General, Light, Medium, Heavy }; + +#include +using namespace std; + +enum class Type { General, Light, Medium, Heavy }; +enum class Category { General = 1, Pistol, MachineGun, Cannon }; + +int main() { + Type t = Type::Light; + t = General; //编译失败,必须使用强类型名称 + + if (t == Category::General) //编译失败,必须使用Type中的General + cout << "General Weapon" << endl; + if (t > Type::General) //通过编译 + cout << "Not General Weapon" << endl; + if (t > 0) //编译失败,无法转换为int类型 + cout << "Not General Weapon" << endl; + if ((int)t > 0) //通过编译 + cout << "Not General Weapon" << endl; + + return 0; +} diff --git a/cpp_11/002_grammar_static_assert.cpp b/cpp_11/002_grammar_static_assert.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a87c270288eee2b960b37a644e1b95a6399fdd78 --- /dev/null +++ b/cpp_11/002_grammar_static_assert.cpp @@ -0,0 +1,19 @@ +#include +#include +using namespace std; + +template int bit_copy(T& a, U& b) +{ + assert(sizeof(b) == sizeof(a)); + //static_assert(sizeof(b) == sizeof(a), "template parameter size no equal!"); + memcpy(&a, &b, sizeof(b)); +}; + +int main() +{ + int varA = 0x2468; + double varB; + bit_copy(varA, varB); + getchar(); + return 0; +} diff --git a/cpp_11/002_grammar_stdref_stdcref.cpp b/cpp_11/002_grammar_stdref_stdcref.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0de2ad8781bd5fed7b1309e1e8d569e12d2bfadc --- /dev/null +++ b/cpp_11/002_grammar_stdref_stdcref.cpp @@ -0,0 +1,23 @@ +//std::ref 用于包装按引用传递的值。 +//std::cref 用于包装按const引用传递的值。 + +#include +#include +#include + +using namespace std; + +void foo( int &a) +{ + cout<<"thread :"<< a++ < +#include +#include +#include + +int func(int, int); +auto func2(int, int)->int; // 指定返回类型 + +template +auto sum(const T1 &t1, const T2 &t2)->decltype(t1+t2) // 指定返回类型 +{ + return t1 + t2; +} + +template +auto mul(const T1 &t1, const T2 &t2)->decltype(t1*t2) // 指定返回类型 +{ + return t1 * t2; +} + +void mytest() +{ + auto a = 3; + auto b = 4L; + auto pi = 3.14f; + + auto c = mul(sum(a, b), pi); + std::cout << c << std::endl; // 21.98 + + return; +} + +int main() +{ + mytest(); + + system("pause"); + return 0; +} diff --git a/cpp_11/002_grammar_unrestricted_union.cpp b/cpp_11/002_grammar_unrestricted_union.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1f0ec7a331470ab27da9be4e91b66d7336fa7d0 --- /dev/null +++ b/cpp_11/002_grammar_unrestricted_union.cpp @@ -0,0 +1,23 @@ +//非受限联合体:C++98中并不是所有数据类型都能够成为union的数据成员,不允许联合体拥有非POD(Plain Old Data)、静态或引用类型的成员。 +//C++11中取消了联合体对于数据成员的限制,任何非引用类型都可以成为联合体的数据成员,成为非受限联合体。 + +struct Student +{ + Student(bool g, int a): gender(g), age(a){} + bool gender; + int age; +}; + +union T +{ + Student s; //C++98下编译失败,不是一个POD类型 + int id; + char name[10]; +}; + +int main() +{ + return 0; +} + +//编译选项:g++ -std=c++98 union.cpp diff --git a/cpp_11/002_grammar_user_defined_literals.cpp b/cpp_11/002_grammar_user_defined_literals.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9511be5eee8ee93bb450d0dfc5bfea76af33ccf --- /dev/null +++ b/cpp_11/002_grammar_user_defined_literals.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +// used as conversion from degrees (input param) to radians (returned output) +constexpr long double operator"" _deg_to_rad ( long double deg ) +{ + long double radians = deg * std::numbers::pi_v / 180; + return radians; +} + +// used with custom type +struct mytype +{ + unsigned long long m; +}; +constexpr mytype operator"" _mytype ( unsigned long long n ) +{ + return mytype{n}; +} + +// used for side-effects +void operator"" _print ( const char* str ) +{ + std::cout << str << '\n'; +} + +#if __cpp_nontype_template_args < 201911 + +std::string operator"" _x2 ( const char* str, std::size_t ) +{ + return std::string{str} + str; +} + +#else // C++20 string literal operator template + +template +struct DoubleString +{ + char p[N*2-1]{}; + + constexpr DoubleString ( char const(&pp)[N] ) + { + std::ranges::copy(pp, p); + std::ranges::copy(pp, p + N - 1); + }; +}; + +template +constexpr auto operator"" _x2() +{ + return A.p; +} + +#endif // C++20 + +int main() +{ + double x_rad = 90.0_deg_to_rad; + std::cout << std::fixed << x_rad << '\n'; + + mytype y = 123_mytype; + std::cout << y.m << '\n'; + + 0x123ABC_print; + std::cout << "abc"_x2 << '\n'; +} diff --git a/cpp_11/003_class_Inheritance_constructor.cpp b/cpp_11/003_class_Inheritance_constructor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_class_Inheritance_constructor.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_class_delegating_constructors.cpp b/cpp_11/003_class_delegating_constructors.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9d1e7cd5758e4e1a88fefb8c3dabd8c04c02c5b --- /dev/null +++ b/cpp_11/003_class_delegating_constructors.cpp @@ -0,0 +1,16 @@ +class A +{ + private: + int a; + int b; + char c; + char d; + public: + A(int num0,int num1,char C):a(num0),b(num1),c(C){} + A(int num0,char C):A(num0,0,C){}//b默认初始化为0 + A(int num0):A(num0,'p'){b=1;}//b重新赋值为1 + void getMembers() + { + cout< +using namespace std; + +class Mem +{ +public: + Mem(int i) : m(i) + {} + + private: + int m; +}; + +class Group +{ +public: + Group() {} + Group(int a) : data(a) {} + Group(Mem m) : mem(m) {} + Group(int a, Mem m, string n) : data(a), mem(m), name(n) + {} + +private: + int data = 1; + Mem mem{0}; + string name{" Group"}; +}; diff --git a/cpp_11/003_class_move_assignment_operator.cpp b/cpp_11/003_class_move_assignment_operator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85c6002ea380bdf7d30a4a8108ec7ae6215726d5 --- /dev/null +++ b/cpp_11/003_class_move_assignment_operator.cpp @@ -0,0 +1,24 @@ +class HasPtr { +public: + HasPtr (const std::string &s = std::string () ): + ps (new std::string(s) ) , i(0) { } + //对ps指向的string,每个HasPtr对象都有自己的拷贝 + HasPtr(const HasPtr &p): + ps (new std::string (*p.ps)), i(p.i) { } + HasPtr& operator=(const HasPtr &); + ~HasPtr() { delete ps; } +private: + std::string *ps; + int i; +); + +class HasPtr { +public: + + //添加的移动构造函数 + HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) {p.ps = 0 ; } + //赋值运算符既是移动赋值运算符,也是拷贝赋值运算符 + HasPtr& operator= (HasPtr rhs) + { swap (*this, rhs) ; return *this; } + //其他成员的定义,同13.2.1节(第453页) +}; diff --git a/cpp_11/003_class_move_constructor.cpp b/cpp_11/003_class_move_constructor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f8f0d770ce4f79c0e3d26c4dc55aec891b3f9bf --- /dev/null +++ b/cpp_11/003_class_move_constructor.cpp @@ -0,0 +1,45 @@ +class MoveConstructor +{ +public: + MoveConstructor() :m_ptr(new int(0)) + { + std::cout << "construct" << std::endl; + } + + MoveConstructor(const MoveConstructor& a):m_ptr(new int(*a.m_ptr)) //深拷贝 + { + std::cout << "copy construct" << std::endl; + } + + MoveConstructor(MoveConstructor&& a) :m_ptr(a.m_ptr) + { + a.m_ptr = nullptr; + std::cout << "move construct:" << std::endl; + } + + ~MoveConstructor() + { + std::cout << "destruct" << std::endl; + delete m_ptr; + } + +private: + int* m_ptr; +}; + +MoveConstructor Get(bool flag) +{ + MoveConstructor a; + MoveConstructor b; + if (flag) + return a; + else + return b; +} + +int right_value_ref2() +{ + MoveConstructor a = Get(false); + + return 0; +} diff --git a/cpp_11/003_class_type_alias.cpp b/cpp_11/003_class_type_alias.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f9a16d1cb3cd78eebad831606b8b24abb1fd082 --- /dev/null +++ b/cpp_11/003_class_type_alias.cpp @@ -0,0 +1,11 @@ +//传统的定义类型别名的方法是使用关键字typedef: +typedef double wages; + +//也可以使用#define来定义别名(使用预处理器) +#define wages double + +//在C++11中使用关键字using来进行别名声明,假设我们现在定义了结构体MyStruct,那么我们可以使用using为Mystruct类型设置一个别名: +using MS=MyStruct; + +//在定义该结构体类型变量时,就可以使用MS作为类型名了 +MS TempStruct; diff --git a/cpp_11/003_class_type_property_init.cpp b/cpp_11/003_class_type_property_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_class_type_property_init.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_error_std_errc.cpp b/cpp_11/003_error_std_errc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f351e5162586d8186b70746301e9199106df2b23 --- /dev/null +++ b/cpp_11/003_error_std_errc.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() +{ + try { + std::thread().detach(); // detaching a not-a-thread + } catch (const std::system_error& e) { + std::cout << "Caught a system_error\n"; + if(e.code() == std::errc::invalid_argument) + std::cout << "The error condition is std::errc::invalid_argument\n"; + std::cout << "the error description is " << e.what() << '\n'; + } +} diff --git a/cpp_11/003_error_std_error_category.cpp b/cpp_11/003_error_std_error_category.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5376fb0afb696d6daf8c6a1d4791f7d8b66c9eb3 --- /dev/null +++ b/cpp_11/003_error_std_error_category.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +int main() +{ + std::error_condition econd = std::system_category().default_error_condition(EDOM); + std::cout << "Category: " << econd.category().name() << '\n' + << "Value: " << econd.value() << '\n' + << "Message: " << econd.message() << '\n'; + + econd = std::system_category().default_error_condition(10001); + std::cout << "Category: " << econd.category().name() << '\n' + << "Value: " << econd.value() << '\n' + << "Message: " << econd.message() << '\n'; +} diff --git a/cpp_11/003_error_std_error_code.cpp b/cpp_11/003_error_std_error_code.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46f9d3a964c998bb491478e615855864f470e8d3 --- /dev/null +++ b/cpp_11/003_error_std_error_code.cpp @@ -0,0 +1,31 @@ +#include +#include + +enum class YourErrorCode { + kSuccess = 0, // 别忘了 0 应该表示无错误 + kNetworkError, + kBadRequest, + kServerError, +}; + +// 特化模版,启用对应的重载 +namespace std { +template<> +struct is_error_code_enum: true_type {}; +} + +// 提供工厂函数 +// 工厂函数不必要写在 std 中 +std::error_code make_error_code(YourErrorCode code) +{ + return { + static_cast(code), + std::generic_category(), // 这里暂时用自带的 category + }; +} + +int main() +{ + std::error_code e = YourErrorCode::kBadRequest; + std::cout << e << '\n'; // 自带一个输出流的重载 +} diff --git a/cpp_11/003_error_std_error_condition.cpp b/cpp_11/003_error_std_error_condition.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ddbfb1f9af7a9874a899d31fffc248ac8a386f7 --- /dev/null +++ b/cpp_11/003_error_std_error_condition.cpp @@ -0,0 +1,63 @@ +enum class MyErrorCondition { + kChenggong, + kWangluoCuowu, + kQingqiuCuowu, + kFuwuqiCuowu, +}; + +class MyErrorCategory: public std::error_category +{ +public: + + static MyErrorCategory const& instance() { + static MyErrorCategory instance; + return instance; + } + + char const *name() const noexcept override { + return "MyErrorCategory"; + } + + std::string message(int code) const override { + return "Message"; + } + + bool equivalent(std::error_code const& code, int condition) const noexcept override { + + auto const& yourErrorCodeCategory = std::error_code(YourErrorCode{}).category(); + + if (code.category() == yourErrorCodeCategory) { + switch (static_cast(condition)) { + case MyErrorCondition::kChenggong: + return code == YourErrorCode::kSuccess; + case MyErrorCondition::kWangluoCuowu: + return code == YourErrorCode::kNetworkError; + case MyErrorCondition::kQingqiuCuowu: + return code == YourErrorCode::kBadRequest; + case MyErrorCondition::kFuwuqiCuowu: + return code == YourErrorCode::kServerError; + } + } + return false; + } +}; + +// error_condition 同样需要特化模版启动重载 +namespace std { + template<> + struct is_error_condition_enum: true_type {}; +} + +// error_condition 同样可以通过工厂函数构造 +std::error_condition make_error_condition(MyErrorCondition code) +{ + return {static_cast(code), MyErrorCategory::instance()}; +} + + +int main() +{ + std::error_code code = YourErrorCode::kNetworkError; + std::error_condition condition = MyErrorCondition::kWangluoCuowu; + std::cout << (code == condition) << '\n'; +} diff --git a/cpp_11/003_error_std_generic_category.cpp b/cpp_11/003_error_std_generic_category.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4bde675ed5e0e909155a0644e867db205753340a --- /dev/null +++ b/cpp_11/003_error_std_generic_category.cpp @@ -0,0 +1,11 @@ +#include +#include +#include +#include +int main() +{ + std::error_condition econd = std::generic_category().default_error_condition(EDOM); + std::cout << "Category: " << econd.category().name() << '\n' + << "Value: " << econd.value() << '\n' + << "Message: " << econd.message() << '\n'; +} diff --git a/cpp_11/003_error_std_system_error.cpp b/cpp_11/003_error_std_system_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..137e87ba12cda5c9a5d3ba75f511782b1f72c253 --- /dev/null +++ b/cpp_11/003_error_std_system_error.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +int main() +{ + try { + std::thread().detach(); // attempt to detach a non-thread + } catch(const std::system_error& e) { + std::cout << "Caught system_error with code " << e.code() + << " meaning " << e.what() << '\n'; + } +} diff --git a/cpp_11/003_exception_std_current_exception.cpp b/cpp_11/003_exception_std_current_exception.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5eac44e2fce355bb24e9ab7476d92cb454b7132b --- /dev/null +++ b/cpp_11/003_exception_std_current_exception.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +void handle_eptr(std::exception_ptr eptr) // 按值传递 ok +{ + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch(const std::exception& e) { + std::cout << "Caught exception \"" << e.what() << "\"\n"; + } +} + +int main() +{ + std::exception_ptr eptr; + try { + std::string().at(1); // 这生成一个 std::out_of_range + } catch(...) { + eptr = std::current_exception(); // 捕获 + } + handle_eptr(eptr); +} // std::out_of_range 的析构函数调用于此,在 ept 析构时 diff --git a/cpp_11/003_exception_std_exception_ptr.cpp b/cpp_11/003_exception_std_exception_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c08039ad2d365b448176ecc5be53dc99694f2072 --- /dev/null +++ b/cpp_11/003_exception_std_exception_ptr.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +void handle_eptr(std::exception_ptr eptr) // passing by value is ok +{ + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch(const std::exception& e) { + std::cout << "Caught exception \"" << e.what() << "\"\n"; + } +} + +int main() +{ + std::exception_ptr eptr; + try { + std::string().at(1); // this generates an std::out_of_range + } catch(...) { + eptr = std::current_exception(); // capture + } + handle_eptr(eptr); +} // destructor for std::out_of_range called here, when the eptr is destructed diff --git a/cpp_11/003_exception_std_make_exception_ptr.cpp b/cpp_11/003_exception_std_make_exception_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57fa375499ad79a88663fcee739fc2b1609a4246 --- /dev/null +++ b/cpp_11/003_exception_std_make_exception_ptr.cpp @@ -0,0 +1,15 @@ +// make_exception_ptr example +#include // std::cout +#include // std::make_exception_ptr, std::rethrow_exception +#include // std::logic_error + +int main () { + auto p = std::make_exception_ptr(std::logic_error("logic_error")); + + try { + std::rethrow_exception (p); + } catch (const std::exception& e) { + std::cout << "exception caught: " << e.what() << '\n'; + } + return 0; +} diff --git a/cpp_11/003_exception_std_nested_exception.cpp b/cpp_11/003_exception_std_nested_exception.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce6cc3adfcbe16e6769344132a386b79043095d0 --- /dev/null +++ b/cpp_11/003_exception_std_nested_exception.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +// prints the explanatory string of an exception. If the exception is nested, +// recurses to print the explanatory of the exception it holds +void print_exception(const std::exception& e, int level = 0) +{ + std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch(const std::exception& e) { + print_exception(e, level+1); + } catch(...) {} +} + +// sample function that catches an exception and wraps it in a nested exception +void open_file(const std::string& s) +{ + try { + std::ifstream file(s); + file.exceptions(std::ios_base::failbit); + } catch(...) { + std::throw_with_nested( std::runtime_error("Couldn't open " + s) ); + } +} + +// sample function that catches an exception and wraps it in a nested exception +void run() +{ + try { + open_file("nonexistent.file"); + } catch(...) { + std::throw_with_nested( std::runtime_error("run() failed") ); + } +} + +// runs the sample function above and prints the caught exception +int main() +{ + try { + run(); + } catch(const std::exception& e) { + print_exception(e); + } +} diff --git a/cpp_11/003_exception_std_rethrow_exception.cpp b/cpp_11/003_exception_std_rethrow_exception.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c08039ad2d365b448176ecc5be53dc99694f2072 --- /dev/null +++ b/cpp_11/003_exception_std_rethrow_exception.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +void handle_eptr(std::exception_ptr eptr) // passing by value is ok +{ + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch(const std::exception& e) { + std::cout << "Caught exception \"" << e.what() << "\"\n"; + } +} + +int main() +{ + std::exception_ptr eptr; + try { + std::string().at(1); // this generates an std::out_of_range + } catch(...) { + eptr = std::current_exception(); // capture + } + handle_eptr(eptr); +} // destructor for std::out_of_range called here, when the eptr is destructed diff --git a/cpp_11/003_exception_std_rethrow_if_nested.cpp b/cpp_11/003_exception_std_rethrow_if_nested.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce6cc3adfcbe16e6769344132a386b79043095d0 --- /dev/null +++ b/cpp_11/003_exception_std_rethrow_if_nested.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +// prints the explanatory string of an exception. If the exception is nested, +// recurses to print the explanatory of the exception it holds +void print_exception(const std::exception& e, int level = 0) +{ + std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch(const std::exception& e) { + print_exception(e, level+1); + } catch(...) {} +} + +// sample function that catches an exception and wraps it in a nested exception +void open_file(const std::string& s) +{ + try { + std::ifstream file(s); + file.exceptions(std::ios_base::failbit); + } catch(...) { + std::throw_with_nested( std::runtime_error("Couldn't open " + s) ); + } +} + +// sample function that catches an exception and wraps it in a nested exception +void run() +{ + try { + open_file("nonexistent.file"); + } catch(...) { + std::throw_with_nested( std::runtime_error("run() failed") ); + } +} + +// runs the sample function above and prints the caught exception +int main() +{ + try { + run(); + } catch(const std::exception& e) { + print_exception(e); + } +} diff --git a/cpp_11/003_exception_std_throw_with_nested.cpp b/cpp_11/003_exception_std_throw_with_nested.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce6cc3adfcbe16e6769344132a386b79043095d0 --- /dev/null +++ b/cpp_11/003_exception_std_throw_with_nested.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +// prints the explanatory string of an exception. If the exception is nested, +// recurses to print the explanatory of the exception it holds +void print_exception(const std::exception& e, int level = 0) +{ + std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n'; + try { + std::rethrow_if_nested(e); + } catch(const std::exception& e) { + print_exception(e, level+1); + } catch(...) {} +} + +// sample function that catches an exception and wraps it in a nested exception +void open_file(const std::string& s) +{ + try { + std::ifstream file(s); + file.exceptions(std::ios_base::failbit); + } catch(...) { + std::throw_with_nested( std::runtime_error("Couldn't open " + s) ); + } +} + +// sample function that catches an exception and wraps it in a nested exception +void run() +{ + try { + open_file("nonexistent.file"); + } catch(...) { + std::throw_with_nested( std::runtime_error("run() failed") ); + } +} + +// runs the sample function above and prints the caught exception +int main() +{ + try { + run(); + } catch(const std::exception& e) { + print_exception(e); + } +} diff --git a/cpp_11/003_function_CV_restricted.cpp b/cpp_11/003_function_CV_restricted.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_CV_restricted.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_function_object_template_mem_fn.cpp b/cpp_11/003_function_object_template_mem_fn.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_object_template_mem_fn.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_function_object_template_std_bad_function_call.cpp b/cpp_11/003_function_object_template_std_bad_function_call.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_object_template_std_bad_function_call.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_function_object_template_std_bind.cpp b/cpp_11/003_function_object_template_std_bind.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_object_template_std_bind.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_function_object_template_std_function.cpp b/cpp_11/003_function_object_template_std_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_object_template_std_function.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_function_references_qualifiers.cpp b/cpp_11/003_function_references_qualifiers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_function_references_qualifiers.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_regex_basic_regex.cpp b/cpp_11/003_regex_basic_regex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_regex_basic_regex.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_regex_basic_sub_match.cpp b/cpp_11/003_regex_basic_sub_match.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_regex_basic_sub_match.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_regex_match_results.cpp b/cpp_11/003_regex_match_results.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_regex_match_results.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_rtti_alignment_of.cpp b/cpp_11/003_rtti_alignment_of.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b8185fcd2c2e761e198a334ebd66b0baad1705c --- /dev/null +++ b/cpp_11/003_rtti_alignment_of.cpp @@ -0,0 +1,11 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::alignment_of::value << '\n'; + std::cout << std::alignment_of() << '\n'; // 另一种语法 + std::cout << std::alignment_of_v << '\n'; // c++17 另一种语法 +} diff --git a/cpp_11/003_rtti_extent.cpp b/cpp_11/003_rtti_extent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fde42ae7c2166d1bd997f89408c6a0554cd1a59a --- /dev/null +++ b/cpp_11/003_rtti_extent.cpp @@ -0,0 +1,17 @@ +#include +#include + +int main() +{ + std::cout << std::extent::value << '\n'; // < 默认维度为 0 + std::cout << std::extent::value << '\n'; + std::cout << std::extent::value << '\n'; + std::cout << std::extent::value << '\n'; + std::cout << std::extent::value << '\n'; + + const auto ext = std::extent{}; + std::cout << ext << '\n'; // < 隐式转换到 std::size_t + + const int ints[] = {1,2,3,4}; + std::cout << std::extent::value << '\n'; // < 数组大小 +} diff --git a/cpp_11/003_rtti_rank.cpp b/cpp_11/003_rtti_rank.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a489dbef367125e7e1bac71e3453bffa0f2e73a --- /dev/null +++ b/cpp_11/003_rtti_rank.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + std::cout << std::rank::value << '\n'; + std::cout << std::rank::value << '\n'; + std::cout << std::rank::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_has_unique_object_representations.cpp b/cpp_11/003_rtti_std_has_unique_object_representations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c3909f9cb60f35ed5e8640ca0d9114695111cfd --- /dev/null +++ b/cpp_11/003_rtti_std_has_unique_object_representations.cpp @@ -0,0 +1,24 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + int m1; +private: + int m2; +}; + +struct C { + virtual void foo(); +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_pod::value << '\n'; + std::cout << std::is_pod::value << '\n'; + std::cout << std::is_pod::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_has_virtual_destructor.cpp b/cpp_11/003_rtti_std_has_virtual_destructor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfd90cb74f6c51f1cfb9cfaae8b2e8deba5fca04 --- /dev/null +++ b/cpp_11/003_rtti_std_has_virtual_destructor.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +int main() +{ + std::cout << std::boolalpha + << "std::string has a virtual destructor? " + << std::has_virtual_destructor::value << '\n' + << "std::runtime_error has a virtual destructor? " + << std::has_virtual_destructor::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_abstract.cpp b/cpp_11/003_rtti_std_is_abstract.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae8ca7bc96e6ae9e0ea14d3c47a1a9584ca97981 --- /dev/null +++ b/cpp_11/003_rtti_std_is_abstract.cpp @@ -0,0 +1,25 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + virtual void foo(); +}; + +struct C { + virtual void foo() = 0; +}; + +struct D : C {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_abstract::value << '\n'; + std::cout << std::is_abstract::value << '\n'; + std::cout << std::is_abstract::value << '\n'; + std::cout << std::is_abstract::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_aggregate.cpp b/cpp_11/003_rtti_std_is_aggregate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e58d3a057d373b9bbffecdd4bed81ad250c71226 --- /dev/null +++ b/cpp_11/003_rtti_std_is_aggregate.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +// 于 p 所指向的未初始化内存构造 T +// 用聚合体的列表初始化,否则使用非列表初始化 +template +T* construct(T* p, Args&&... args) { + if constexpr(std::is_aggregate_v) { + return ::new (static_cast(p)) T{std::forward(args)...}; + } + else { + return ::new (static_cast(p)) T(std::forward(args)...); + } +} + +struct A { int x, y; }; +struct B { B(int, const char*) { } }; + +int main() { + std::aligned_union_t<1, A, B> storage; + A* a = construct(reinterpret_cast(&storage), 1, 2); + B* b = construct(reinterpret_cast(&storage), 1, "hello"); +} diff --git a/cpp_11/003_rtti_std_is_arithmetic.cpp b/cpp_11/003_rtti_std_is_arithmetic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..efc9de7340752001ea392278d4f2e9bdcde4d453 --- /dev/null +++ b/cpp_11/003_rtti_std_is_arithmetic.cpp @@ -0,0 +1,23 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << "A: " << std::is_arithmetic::value << '\n'; + std::cout << "bool: " << std::is_arithmetic::value << '\n'; + std::cout << "int: " << std::is_arithmetic::value << '\n'; + std::cout << "int const: " << std::is_arithmetic::value << '\n'; + std::cout << "int &: " << std::is_arithmetic::value << '\n'; + std::cout << "int *: " << std::is_arithmetic::value << '\n'; + std::cout << "float: " << std::is_arithmetic::value << '\n'; + std::cout << "float const: " << std::is_arithmetic::value << '\n'; + std::cout << "float &: " << std::is_arithmetic::value << '\n'; + std::cout << "float *: " << std::is_arithmetic::value << '\n'; + std::cout << "char: " << std::is_arithmetic::value << '\n'; + std::cout << "char const: " << std::is_arithmetic::value << '\n'; + std::cout << "char &: " << std::is_arithmetic::value << '\n'; + std::cout << "char *: " << std::is_arithmetic::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_array.cpp b/cpp_11/003_rtti_std_is_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d33520864fb18ce0ec967eab761e5476c83073ea --- /dev/null +++ b/cpp_11/003_rtti_std_is_array.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array::value << '\n'; + std::cout << std::is_array>::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_assignable.cpp b/cpp_11/003_rtti_std_is_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da8347162e5f8abd38e7b8018f2bbdd0f2272047 --- /dev/null +++ b/cpp_11/003_rtti_std_is_assignable.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Ex1 { int n; }; +int main() { + std::cout << std::boolalpha + << "int is assignable from int? " + << std::is_assignable::value << '\n' // 1 = 1; wouldn't compile + << "int& is assignable from int? " + << std::is_assignable::value << '\n' // int a; a = 1; works + << "int is assignable from double? " + << std::is_assignable::value << '\n' + << "int& is nothrow assignable from double? " + << std::is_nothrow_assignable::value << '\n' + << "string is assignable from double? " + << std::is_assignable::value << '\n' + << "Ex1& is trivially assignable from const Ex1&? " + << std::is_trivially_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_base_of.cpp b/cpp_11/003_rtti_std_is_base_of.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d18062994e9edf2734309c9572009fb256862199 --- /dev/null +++ b/cpp_11/003_rtti_std_is_base_of.cpp @@ -0,0 +1,17 @@ +#include +#include + +class A {}; + +class B : A {}; + +class C {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << "a2b: " << std::is_base_of::value << '\n'; + std::cout << "b2a: " << std::is_base_of::value << '\n'; + std::cout << "c2b: " << std::is_base_of::value << '\n'; + std::cout << "same type: " << std::is_base_of::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_bounded_array.cpp b/cpp_11/003_rtti_std_is_bounded_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ab095663d6dd91c6be788fe02707117da3b14d0 --- /dev/null +++ b/cpp_11/003_rtti_std_is_bounded_array.cpp @@ -0,0 +1,16 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_class.cpp b/cpp_11/003_rtti_std_is_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d162dcfbc30562f005bdc274673b6ce6734ddda --- /dev/null +++ b/cpp_11/003_rtti_std_is_class.cpp @@ -0,0 +1,17 @@ +#include +#include + +struct A {}; + +class B {}; + +enum class C {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_class::value << '\n'; + std::cout << std::is_class::value << '\n'; + std::cout << std::is_class::value << '\n'; + std::cout << std::is_class::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_compound.cpp b/cpp_11/003_rtti_std_is_compound.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef235c612d52526dd8c059a4f3c6d6b238819039 --- /dev/null +++ b/cpp_11/003_rtti_std_is_compound.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() { + class cls {}; + std::cout << (std::is_compound::value + ? "T is compound" + : "T is not a compound") << '\n'; + std::cout << (std::is_compound::value + ? "T is compound" + : "T is not a compound") << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_const.cpp b/cpp_11/003_rtti_std_is_const.cpp new file mode 100644 index 0000000000000000000000000000000000000000..149c014676277a42f7461cad0880e64a675c204f --- /dev/null +++ b/cpp_11/003_rtti_std_is_const.cpp @@ -0,0 +1,16 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha + << std::is_const_v << '\n' // false + << std::is_const_v << '\n' // true + << std::is_const_v /*false*/ + << " because the pointer itself can be changed but not the int pointed at\n" + << std::is_const_v /*true*/ + << " because the pointer itself can't be changed but the int pointed at can\n" + << std::is_const_v << '\n' // false + << std::is_const_v> << '\n' // true + ; +} diff --git a/cpp_11/003_rtti_std_is_constructible.cpp b/cpp_11/003_rtti_std_is_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f89b76a195a978a332f7c0be08f820d1a6b74d4e --- /dev/null +++ b/cpp_11/003_rtti_std_is_constructible.cpp @@ -0,0 +1,24 @@ +#include +#include + +class Foo { + int v1; + double v2; + public: + Foo(int n) : v1(n), v2() {} + Foo(int n, double f) noexcept : v1(n), v2(f) {} +}; + +int main() { + std::cout << "Foo is ...\n" << std::boolalpha + << "\tTrivially-constructible from const Foo&? " + << std::is_trivially_constructible::value << '\n' + << "\tTrivially-constructible from int? " + << std::is_trivially_constructible::value << '\n' + << "\tConstructible from int? " + << std::is_constructible::value << '\n' + << "\tNothrow-constructible from int? " + << std::is_nothrow_constructible::value << '\n' + << "\tNothrow-constructible from int and double? " + << std::is_nothrow_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_convertible.cpp b/cpp_11/003_rtti_std_is_convertible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fa3b11a94f526b3a75c38591b2c7099b18d2794 --- /dev/null +++ b/cpp_11/003_rtti_std_is_convertible.cpp @@ -0,0 +1,31 @@ +#include +#include + +class E { public: template E(T&&) { } }; + +int main() +{ + class A {}; + class B : public A {}; + class C {}; + class D { public: operator C() { return c; } C c; }; + + + bool b2a = std::is_convertible::value; + bool a2b = std::is_convertible::value; + bool b2c = std::is_convertible::value; + bool d2c = std::is_convertible::value; + + // 完美转发构造函数使类能从任何类型转换 + + bool everything2e = std::is_convertible::value; //< B, C, D 等 + + std::cout << std::boolalpha; + + std::cout << b2a << '\n'; + std::cout << a2b << '\n'; + std::cout << b2c << '\n'; + std::cout << d2c << '\n'; + std::cout << '\n'; + std::cout << everything2e << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_copy_assignable.cpp b/cpp_11/003_rtti_std_is_copy_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da0bda349fcfd85b0b4cd6027daffcf820acc51c --- /dev/null +++ b/cpp_11/003_rtti_std_is_copy_assignable.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +struct Foo { int n; }; +int main() { + std::cout << std::boolalpha + << "Foo is trivally copy-assignable? " + << std::is_trivially_copy_assignable::value << '\n' + << "int[2] is copy-assignable? " + << std::is_copy_assignable::value << '\n' + << "int is nothrow copy-assignable? " + << std::is_nothrow_copy_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_copy_constructible.cpp b/cpp_11/003_rtti_std_is_copy_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..741d7acdb92d3b3612e17680c8be24dc2e36dd9d --- /dev/null +++ b/cpp_11/003_rtti_std_is_copy_constructible.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡复制构造函数 +}; +struct Ex2 { + int n; + Ex2(const Ex2&) = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is copy-constructible? " + << std::is_copy_constructible::value << '\n' + << "Ex1 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is nothrow copy-constructible? " + << std::is_nothrow_copy_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_default_constructible.cpp b/cpp_11/003_rtti_std_is_default_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..813529cbdfd360f8e2fd2a46e0e850ba649897fb --- /dev/null +++ b/cpp_11/003_rtti_std_is_default_constructible.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡默认构造函数 +}; +struct Ex2 { + int n; + Ex2() = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is default-constructible? " + << std::is_default_constructible::value << '\n' + << "Ex1 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is nothrow default-constructible? " + << std::is_nothrow_default_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_destructible.cpp b/cpp_11/003_rtti_std_is_destructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b16776931fdc362a96f9256b4452bdf196a66d49 --- /dev/null +++ b/cpp_11/003_rtti_std_is_destructible.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Foo { + std::string str; + ~Foo() noexcept {}; +}; +struct Bar { + ~Bar() = default; +}; +int main() { + std::cout << std::boolalpha + << "std::string is destructible? " + << std::is_destructible::value << '\n' + << "Foo is nothrow destructible? " + << std::is_nothrow_destructible::value << '\n' + << "Bar is trivally destructible? " + << std::is_trivially_destructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_empty.cpp b/cpp_11/003_rtti_std_is_empty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e198e56238977abd039a72b4fd50d9798644b077 --- /dev/null +++ b/cpp_11/003_rtti_std_is_empty.cpp @@ -0,0 +1,40 @@ +#include +#include + +struct A {}; + +struct B { + int m; +}; + +struct C { + static int m; +}; + +struct D { + virtual ~D(); +}; + +union E {}; + +struct F { + [[no_unique_address]] E e; +}; + +struct G { + int:0; + // C++ 标准允许“作为特殊情况,无名的长度为零的位域指定下个位域在分配单元边界对齐。 + // 仅当声明无名位域时宽度可为零。” +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << "A " << std::is_empty::value << '\n'; + std::cout << "B " << std::is_empty::value << '\n'; + std::cout << "C " << std::is_empty::value << '\n'; + std::cout << "D " << std::is_empty::value << '\n'; + std::cout << "E " << std::is_empty::value << '\n'; + std::cout << "F " << std::is_empty::value << '\n'; // 结果依赖 ABI + std::cout << "G " << std::is_empty::value << '\n'; // 0 宽度的无名位域 +} diff --git a/cpp_11/003_rtti_std_is_enum.cpp b/cpp_11/003_rtti_std_is_enum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d74a65527fab8df233e566609e63c88800fbff18 --- /dev/null +++ b/cpp_11/003_rtti_std_is_enum.cpp @@ -0,0 +1,17 @@ +#include +#include + +class A {}; + +enum E {}; + +enum class Ec : int {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_enum::value << '\n'; + std::cout << std::is_enum::value << '\n'; + std::cout << std::is_enum::value << '\n'; + std::cout << std::is_enum::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_final.cpp b/cpp_11/003_rtti_std_is_final.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e7980118ee3d19a4725575ebf28bc4f60c1ff90 --- /dev/null +++ b/cpp_11/003_rtti_std_is_final.cpp @@ -0,0 +1,13 @@ +#include +#include + +class A {}; +class B final {}; + +int main() +{ + std::cout + << std::boolalpha + << std::is_final::value << '\n' + << std::is_final::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_floating_point.cpp b/cpp_11/003_rtti_std_is_floating_point.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a486716b4e3d2c41dd4ec928fc5247b5032042a --- /dev/null +++ b/cpp_11/003_rtti_std_is_floating_point.cpp @@ -0,0 +1,15 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_function.cpp b/cpp_11/003_rtti_std_is_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28bde9b36c9c1f23decdb321880eb52eb5383e0c --- /dev/null +++ b/cpp_11/003_rtti_std_is_function.cpp @@ -0,0 +1,28 @@ +#include +#include + +struct A { + int fun() const&; +}; + +template +struct PM_traits {}; + +template +struct PM_traits { + using member_type = U; +}; + +int f(); + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_function::value << '\n'; + std::cout << std::is_function::value << '\n'; + std::cout << std::is_function::value << '\n'; + std::cout << std::is_function::value << '\n'; + + using T = PM_traits::member_type; // T 为 int() const& + std::cout << std::is_function::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_fundamental.cpp b/cpp_11/003_rtti_std_is_fundamental.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc144edac6bc88ee7415628611a754865ccc8d93 --- /dev/null +++ b/cpp_11/003_rtti_std_is_fundamental.cpp @@ -0,0 +1,16 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << "A\t" << std::is_fundamental::value << '\n'; + std::cout << "int\t" << std::is_fundamental::value << '\n'; + std::cout << "int&\t" << std::is_fundamental::value << '\n'; + std::cout << "int*\t" << std::is_fundamental::value << '\n'; + std::cout << "float\t" << std::is_fundamental::value << '\n'; + std::cout << "float&\t" << std::is_fundamental::value << '\n'; + std::cout << "float*\t" << std::is_fundamental::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_integral.cpp b/cpp_11/003_rtti_std_is_integral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..deb623255d90807f61d336ea3258670e3f25708c --- /dev/null +++ b/cpp_11/003_rtti_std_is_integral.cpp @@ -0,0 +1,24 @@ +#include +#include + +class A {}; + +enum E : int {}; + +template +T f(T i) +{ + static_assert(std::is_integral::value, "Integral required."); + return i; +} + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_integral::value << '\n'; + std::cout << std::is_integral::value << '\n'; + std::cout << std::is_integral::value << '\n'; + std::cout << std::is_integral::value << '\n'; + std::cout << std::is_integral::value << '\n'; + std::cout << f(123) << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_lvalue_reference.cpp b/cpp_11/003_rtti_std_is_lvalue_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ddbf8b98e1ace9835433c48a7117ab9ad6ad507 --- /dev/null +++ b/cpp_11/003_rtti_std_is_lvalue_reference.cpp @@ -0,0 +1,15 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_lvalue_reference::value << '\n'; + std::cout << std::is_lvalue_reference::value << '\n'; + std::cout << std::is_lvalue_reference::value << '\n'; + std::cout << std::is_lvalue_reference::value << '\n'; + std::cout << std::is_lvalue_reference::value << '\n'; + std::cout << std::is_lvalue_reference::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_member_function_pointer.cpp b/cpp_11/003_rtti_std_is_member_function_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1618dd61067d4ba109b3aba8136138614d4520a --- /dev/null +++ b/cpp_11/003_rtti_std_is_member_function_pointer.cpp @@ -0,0 +1,13 @@ +#include + +class A { +public: + void member() { } +}; + +int main() +{ + // 若 A::member 是数据成员而非函数,则在编译时失败 + static_assert(std::is_member_function_pointer::value, + "A::member is not a member function."); +} diff --git a/cpp_11/003_rtti_std_is_member_object_pointer.cpp b/cpp_11/003_rtti_std_is_member_object_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56e61309535977a5711e9fb5c5d1681567410da8 --- /dev/null +++ b/cpp_11/003_rtti_std_is_member_object_pointer.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() { + class cls {}; + std::cout << (std::is_member_object_pointer::value + ? "T is member object pointer" + : "T is not a member object pointer") << '\n'; + std::cout << (std::is_member_object_pointer::value + ? "T is member object pointer" + : "T is not a member object pointer") << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_member_pointer.cpp b/cpp_11/003_rtti_std_is_member_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbc11bd8df095bbd6b4aca9873955cba445dd9d2 --- /dev/null +++ b/cpp_11/003_rtti_std_is_member_pointer.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() { + class cls {}; + std::cout << (std::is_member_pointer::value + ? "T is member pointer" + : "T is not a member pointer") << '\n'; + std::cout << (std::is_member_pointer::value + ? "T is member pointer" + : "T is not a member pointer") << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_move_assignable.cpp b/cpp_11/003_rtti_std_is_move_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9868f5b2f00f877a94302f47caa423ea3dc111f2 --- /dev/null +++ b/cpp_11/003_rtti_std_is_move_assignable.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +struct Foo { int n; }; +struct NoMove { + // 避免默认的移动赋值运算符的隐式定义 + // 然而,该类仍然可移动赋值,因为 + // 其复制赋值运算符能绑定到右值参数 + NoMove& operator=(const NoMove&) { return *this; } +}; +int main() { + std::cout << std::boolalpha + << "std::string is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n' + << "int[2] is move-assignable? " + << std::is_move_assignable::value << '\n' + << "Foo is trivally move-assignable? " + << std::is_trivially_move_assignable::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-assignable? " + << std::is_move_assignable::value << '\n' + << "NoMove is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_move_constructible.cpp b/cpp_11/003_rtti_std_is_move_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a56eada4794e3744135f44a32fcda87a4ea7d0d --- /dev/null +++ b/cpp_11/003_rtti_std_is_move_constructible.cpp @@ -0,0 +1,34 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡而不抛出的移动构造函数 +}; +struct Ex2 { + int n; + Ex2(Ex2&&) = default; // 平凡且不抛出 +}; +struct NoMove { + // 避免默认移动构造函数的隐式声明 + // 然而,该类仍为可移动构造因为复制构造函数能绑定到右值参数 + NoMove(const NoMove&) {} +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is move-constructible? " + << std::is_move_constructible::value << '\n' + << "Ex1 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex1 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n' + << "Ex2 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex2 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-constructible? " + << std::is_move_constructible::value << '\n' + << "NoMove is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_assignable.cpp b/cpp_11/003_rtti_std_is_nothrow_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da8347162e5f8abd38e7b8018f2bbdd0f2272047 --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_assignable.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Ex1 { int n; }; +int main() { + std::cout << std::boolalpha + << "int is assignable from int? " + << std::is_assignable::value << '\n' // 1 = 1; wouldn't compile + << "int& is assignable from int? " + << std::is_assignable::value << '\n' // int a; a = 1; works + << "int is assignable from double? " + << std::is_assignable::value << '\n' + << "int& is nothrow assignable from double? " + << std::is_nothrow_assignable::value << '\n' + << "string is assignable from double? " + << std::is_assignable::value << '\n' + << "Ex1& is trivially assignable from const Ex1&? " + << std::is_trivially_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_constructible.cpp b/cpp_11/003_rtti_std_is_nothrow_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f89b76a195a978a332f7c0be08f820d1a6b74d4e --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_constructible.cpp @@ -0,0 +1,24 @@ +#include +#include + +class Foo { + int v1; + double v2; + public: + Foo(int n) : v1(n), v2() {} + Foo(int n, double f) noexcept : v1(n), v2(f) {} +}; + +int main() { + std::cout << "Foo is ...\n" << std::boolalpha + << "\tTrivially-constructible from const Foo&? " + << std::is_trivially_constructible::value << '\n' + << "\tTrivially-constructible from int? " + << std::is_trivially_constructible::value << '\n' + << "\tConstructible from int? " + << std::is_constructible::value << '\n' + << "\tNothrow-constructible from int? " + << std::is_nothrow_constructible::value << '\n' + << "\tNothrow-constructible from int and double? " + << std::is_nothrow_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_copy_assignable.cpp b/cpp_11/003_rtti_std_is_nothrow_copy_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da0bda349fcfd85b0b4cd6027daffcf820acc51c --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_copy_assignable.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +struct Foo { int n; }; +int main() { + std::cout << std::boolalpha + << "Foo is trivally copy-assignable? " + << std::is_trivially_copy_assignable::value << '\n' + << "int[2] is copy-assignable? " + << std::is_copy_assignable::value << '\n' + << "int is nothrow copy-assignable? " + << std::is_nothrow_copy_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_copy_constructiblee.cpp b/cpp_11/003_rtti_std_is_nothrow_copy_constructiblee.cpp new file mode 100644 index 0000000000000000000000000000000000000000..741d7acdb92d3b3612e17680c8be24dc2e36dd9d --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_copy_constructiblee.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡复制构造函数 +}; +struct Ex2 { + int n; + Ex2(const Ex2&) = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is copy-constructible? " + << std::is_copy_constructible::value << '\n' + << "Ex1 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is nothrow copy-constructible? " + << std::is_nothrow_copy_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_default_constructible.cpp b/cpp_11/003_rtti_std_is_nothrow_default_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..813529cbdfd360f8e2fd2a46e0e850ba649897fb --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_default_constructible.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡默认构造函数 +}; +struct Ex2 { + int n; + Ex2() = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is default-constructible? " + << std::is_default_constructible::value << '\n' + << "Ex1 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is nothrow default-constructible? " + << std::is_nothrow_default_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_destructible.cpp b/cpp_11/003_rtti_std_is_nothrow_destructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b16776931fdc362a96f9256b4452bdf196a66d49 --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_destructible.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Foo { + std::string str; + ~Foo() noexcept {}; +}; +struct Bar { + ~Bar() = default; +}; +int main() { + std::cout << std::boolalpha + << "std::string is destructible? " + << std::is_destructible::value << '\n' + << "Foo is nothrow destructible? " + << std::is_nothrow_destructible::value << '\n' + << "Bar is trivally destructible? " + << std::is_trivially_destructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_move_assignable.cpp b/cpp_11/003_rtti_std_is_nothrow_move_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9868f5b2f00f877a94302f47caa423ea3dc111f2 --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_move_assignable.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +struct Foo { int n; }; +struct NoMove { + // 避免默认的移动赋值运算符的隐式定义 + // 然而,该类仍然可移动赋值,因为 + // 其复制赋值运算符能绑定到右值参数 + NoMove& operator=(const NoMove&) { return *this; } +}; +int main() { + std::cout << std::boolalpha + << "std::string is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n' + << "int[2] is move-assignable? " + << std::is_move_assignable::value << '\n' + << "Foo is trivally move-assignable? " + << std::is_trivially_move_assignable::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-assignable? " + << std::is_move_assignable::value << '\n' + << "NoMove is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_move_constructible.cpp b/cpp_11/003_rtti_std_is_nothrow_move_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a56eada4794e3744135f44a32fcda87a4ea7d0d --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_move_constructible.cpp @@ -0,0 +1,34 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡而不抛出的移动构造函数 +}; +struct Ex2 { + int n; + Ex2(Ex2&&) = default; // 平凡且不抛出 +}; +struct NoMove { + // 避免默认移动构造函数的隐式声明 + // 然而,该类仍为可移动构造因为复制构造函数能绑定到右值参数 + NoMove(const NoMove&) {} +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is move-constructible? " + << std::is_move_constructible::value << '\n' + << "Ex1 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex1 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n' + << "Ex2 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex2 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-constructible? " + << std::is_move_constructible::value << '\n' + << "NoMove is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_nothrow_swappable.cpp b/cpp_11/003_rtti_std_is_nothrow_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11c0b60f02549b9237bef9afcaf3ee33f23549c8 --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_swappable.cpp @@ -0,0 +1 @@ +暂无示例 diff --git a/cpp_11/003_rtti_std_is_nothrow_swappable_with.cpp b/cpp_11/003_rtti_std_is_nothrow_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11c0b60f02549b9237bef9afcaf3ee33f23549c8 --- /dev/null +++ b/cpp_11/003_rtti_std_is_nothrow_swappable_with.cpp @@ -0,0 +1 @@ +暂无示例 diff --git a/cpp_11/003_rtti_std_is_null_pointer.cpp b/cpp_11/003_rtti_std_is_null_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a85355e44a1b01918a0975f532fad4287a4fb6b --- /dev/null +++ b/cpp_11/003_rtti_std_is_null_pointer.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha + << std::is_null_pointer< decltype(nullptr) >::value << ' ' + << std::is_null_pointer< int* >::value << '\n' + << std::is_pointer< decltype(nullptr) >::value << ' ' + << std::is_pointer::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_object.cpp b/cpp_11/003_rtti_std_is_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31abfea08caf2d06e031daabc936f52ee9223bfe --- /dev/null +++ b/cpp_11/003_rtti_std_is_object.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() { + class cls {}; + std::cout << std::boolalpha; + std::cout << std::is_object::value << '\n'; + std::cout << std::is_object::value << '\n'; + std::cout << std::is_object::value << '\n'; + std::cout << std::is_object::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_pod.cpp b/cpp_11/003_rtti_std_is_pod.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c3909f9cb60f35ed5e8640ca0d9114695111cfd --- /dev/null +++ b/cpp_11/003_rtti_std_is_pod.cpp @@ -0,0 +1,24 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + int m1; +private: + int m2; +}; + +struct C { + virtual void foo(); +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_pod::value << '\n'; + std::cout << std::is_pod::value << '\n'; + std::cout << std::is_pod::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_pointer.cpp b/cpp_11/003_rtti_std_is_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..018c95fbda981ad0ed92aced606039ff25619119 --- /dev/null +++ b/cpp_11/003_rtti_std_is_pointer.cpp @@ -0,0 +1,17 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; + std::cout << std::is_pointer::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_polymorphic.cpp b/cpp_11/003_rtti_std_is_polymorphic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa5611e957f8371441069fa2988a1b0776779be2 --- /dev/null +++ b/cpp_11/003_rtti_std_is_polymorphic.cpp @@ -0,0 +1,20 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + virtual void foo(); +}; + +struct C : B {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_polymorphic::value << '\n'; + std::cout << std::is_polymorphic::value << '\n'; + std::cout << std::is_polymorphic::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_reference.cpp b/cpp_11/003_rtti_std_is_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..643b2b18431553bbf0d6e9b64ca4af9dfe492614 --- /dev/null +++ b/cpp_11/003_rtti_std_is_reference.cpp @@ -0,0 +1,15 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_reference::value << '\n'; + std::cout << std::is_reference::value << '\n'; + std::cout << std::is_reference::value << '\n'; + std::cout << std::is_reference::value << '\n'; + std::cout << std::is_reference::value << '\n'; + std::cout << std::is_reference::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_rvalue_reference.cpp b/cpp_11/003_rtti_std_is_rvalue_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc25b230c2dbde1ddde644eafaeda0f055157095 --- /dev/null +++ b/cpp_11/003_rtti_std_is_rvalue_reference.cpp @@ -0,0 +1,15 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_rvalue_reference::value << '\n'; + std::cout << std::is_rvalue_reference::value << '\n'; + std::cout << std::is_rvalue_reference::value << '\n'; + std::cout << std::is_rvalue_reference::value << '\n'; + std::cout << std::is_rvalue_reference::value << '\n'; + std::cout << std::is_rvalue_reference::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_same.cpp b/cpp_11/003_rtti_std_is_same.cpp new file mode 100644 index 0000000000000000000000000000000000000000..971375fe8ab898650f1b8da02461219f12d2188f --- /dev/null +++ b/cpp_11/003_rtti_std_is_same.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +void print_separator() +{ + std::cout << "-----\n"; +} + +int main() +{ + std::cout << std::boolalpha; + + // 一些实现定义状况 + std::cout << std::is_same::value << '\n'; + // 若 'int' 为 32 位则通常为 true + std::cout << std::is_same::value << '\n'; + // 若使用 ILP64 数据模型则可能为 true + + print_separator(); + + // 'float' 决非整数类型 + std::cout << std::is_same::value << '\n'; // false + + print_separator(); + + // 'int' 为隐式的 'signed' + std::cout << std::is_same::value << "\n"; // true + std::cout << std::is_same::value << "\n"; // false + std::cout << std::is_same::value << "\n"; // true + + print_separator(); + + // 不同于其他类型, 'char' 既非 'unsigned' 亦非 'signed' + std::cout << std::is_same::value << "\n"; // true + std::cout << std::is_same::value << "\n"; // false + std::cout << std::is_same::value << "\n"; // false +} diff --git a/cpp_11/003_rtti_std_is_scalar.cpp b/cpp_11/003_rtti_std_is_scalar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3aa6351ba79619d1f99254ae7922d5b2aa8fd18d --- /dev/null +++ b/cpp_11/003_rtti_std_is_scalar.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() { + class cls {}; + std::cout << (std::is_scalar::value + ? "T is a scalar" + : "T is not a scalar") << '\n'; + std::cout << (std::is_scalar::value + ? "T is a scalar" + : "T is not a scalar") << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_signed.cpp b/cpp_11/003_rtti_std_is_signed.cpp new file mode 100644 index 0000000000000000000000000000000000000000..262feaa88e5d934da3a966f9756612be9242014d --- /dev/null +++ b/cpp_11/003_rtti_std_is_signed.cpp @@ -0,0 +1,21 @@ +#include +#include + +class A {}; +enum B : int {}; +enum class C : int {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_signed::value << '\n'; + std::cout << std::is_signed::value << '\n'; + std::cout << std::is_signed::value << '\n'; + std::cout << std::is_signed::value << '\n'; + std::cout << std::is_signed::value << '\n'; + std::cout << std::is_signed::value << '\n'; + + // 简短的形式: + std::cout << std::is_signed() << '\n'; + std::cout << std::is_signed() << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_standard_layout.cpp b/cpp_11/003_rtti_std_is_standard_layout.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a845509d4c0c999ec0bd9e65c8abbe14bf4488f --- /dev/null +++ b/cpp_11/003_rtti_std_is_standard_layout.cpp @@ -0,0 +1,24 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + int m1; +private: + int m2; +}; + +struct C { + virtual void foo(); +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_standard_layout::value << '\n'; + std::cout << std::is_standard_layout::value << '\n'; + std::cout << std::is_standard_layout::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_swappable.cpp b/cpp_11/003_rtti_std_is_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11c0b60f02549b9237bef9afcaf3ee33f23549c8 --- /dev/null +++ b/cpp_11/003_rtti_std_is_swappable.cpp @@ -0,0 +1 @@ +暂无示例 diff --git a/cpp_11/003_rtti_std_is_swappable_with.cpp b/cpp_11/003_rtti_std_is_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11c0b60f02549b9237bef9afcaf3ee33f23549c8 --- /dev/null +++ b/cpp_11/003_rtti_std_is_swappable_with.cpp @@ -0,0 +1 @@ +暂无示例 diff --git a/cpp_11/003_rtti_std_is_trivial.cpp b/cpp_11/003_rtti_std_is_trivial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a92a888f493917d34de3908fcfca98701e4a769 --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivial.cpp @@ -0,0 +1,17 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + B() {} +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_trivial::value << '\n'; + std::cout << std::is_trivial::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_assignable.cpp b/cpp_11/003_rtti_std_is_trivially_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da8347162e5f8abd38e7b8018f2bbdd0f2272047 --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_assignable.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Ex1 { int n; }; +int main() { + std::cout << std::boolalpha + << "int is assignable from int? " + << std::is_assignable::value << '\n' // 1 = 1; wouldn't compile + << "int& is assignable from int? " + << std::is_assignable::value << '\n' // int a; a = 1; works + << "int is assignable from double? " + << std::is_assignable::value << '\n' + << "int& is nothrow assignable from double? " + << std::is_nothrow_assignable::value << '\n' + << "string is assignable from double? " + << std::is_assignable::value << '\n' + << "Ex1& is trivially assignable from const Ex1&? " + << std::is_trivially_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_constructible.cpp b/cpp_11/003_rtti_std_is_trivially_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f89b76a195a978a332f7c0be08f820d1a6b74d4e --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_constructible.cpp @@ -0,0 +1,24 @@ +#include +#include + +class Foo { + int v1; + double v2; + public: + Foo(int n) : v1(n), v2() {} + Foo(int n, double f) noexcept : v1(n), v2(f) {} +}; + +int main() { + std::cout << "Foo is ...\n" << std::boolalpha + << "\tTrivially-constructible from const Foo&? " + << std::is_trivially_constructible::value << '\n' + << "\tTrivially-constructible from int? " + << std::is_trivially_constructible::value << '\n' + << "\tConstructible from int? " + << std::is_constructible::value << '\n' + << "\tNothrow-constructible from int? " + << std::is_nothrow_constructible::value << '\n' + << "\tNothrow-constructible from int and double? " + << std::is_nothrow_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_copy_assignable.cpp b/cpp_11/003_rtti_std_is_trivially_copy_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da0bda349fcfd85b0b4cd6027daffcf820acc51c --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_copy_assignable.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +struct Foo { int n; }; +int main() { + std::cout << std::boolalpha + << "Foo is trivally copy-assignable? " + << std::is_trivially_copy_assignable::value << '\n' + << "int[2] is copy-assignable? " + << std::is_copy_assignable::value << '\n' + << "int is nothrow copy-assignable? " + << std::is_nothrow_copy_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_copy_constructible.cp b/cpp_11/003_rtti_std_is_trivially_copy_constructible.cp new file mode 100644 index 0000000000000000000000000000000000000000..741d7acdb92d3b3612e17680c8be24dc2e36dd9d --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_copy_constructible.cp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡复制构造函数 +}; +struct Ex2 { + int n; + Ex2(const Ex2&) = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is copy-constructible? " + << std::is_copy_constructible::value << '\n' + << "Ex1 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is nothrow copy-constructible? " + << std::is_nothrow_copy_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_copyable.cpp b/cpp_11/003_rtti_std_is_trivially_copyable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bdf2bfa8814d4fc6d7519f3cafad18c04c2a461 --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_copyable.cpp @@ -0,0 +1,22 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + B(const B&) {} +}; + +struct C { + virtual void foo(); +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_trivially_copyable::value << '\n'; + std::cout << std::is_trivially_copyable::value << '\n'; + std::cout << std::is_trivially_copyable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_default_constructible.cpp b/cpp_11/003_rtti_std_is_trivially_default_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..813529cbdfd360f8e2fd2a46e0e850ba649897fb --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_default_constructible.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡默认构造函数 +}; +struct Ex2 { + int n; + Ex2() = default; // 平凡且不抛出 +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is default-constructible? " + << std::is_default_constructible::value << '\n' + << "Ex1 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is nothrow default-constructible? " + << std::is_nothrow_default_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_destructible.cpp b/cpp_11/003_rtti_std_is_trivially_destructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b16776931fdc362a96f9256b4452bdf196a66d49 --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_destructible.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +struct Foo { + std::string str; + ~Foo() noexcept {}; +}; +struct Bar { + ~Bar() = default; +}; +int main() { + std::cout << std::boolalpha + << "std::string is destructible? " + << std::is_destructible::value << '\n' + << "Foo is nothrow destructible? " + << std::is_nothrow_destructible::value << '\n' + << "Bar is trivally destructible? " + << std::is_trivially_destructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_move_assignable.cpp b/cpp_11/003_rtti_std_is_trivially_move_assignable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9868f5b2f00f877a94302f47caa423ea3dc111f2 --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_move_assignable.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +struct Foo { int n; }; +struct NoMove { + // 避免默认的移动赋值运算符的隐式定义 + // 然而,该类仍然可移动赋值,因为 + // 其复制赋值运算符能绑定到右值参数 + NoMove& operator=(const NoMove&) { return *this; } +}; +int main() { + std::cout << std::boolalpha + << "std::string is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n' + << "int[2] is move-assignable? " + << std::is_move_assignable::value << '\n' + << "Foo is trivally move-assignable? " + << std::is_trivially_move_assignable::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-assignable? " + << std::is_move_assignable::value << '\n' + << "NoMove is nothrow move-assignable? " + << std::is_nothrow_move_assignable::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_trivially_move_constructible.cpp b/cpp_11/003_rtti_std_is_trivially_move_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a56eada4794e3744135f44a32fcda87a4ea7d0d --- /dev/null +++ b/cpp_11/003_rtti_std_is_trivially_move_constructible.cpp @@ -0,0 +1,34 @@ +#include +#include + +struct Ex1 { + std::string str; // 成员拥有非平凡而不抛出的移动构造函数 +}; +struct Ex2 { + int n; + Ex2(Ex2&&) = default; // 平凡且不抛出 +}; +struct NoMove { + // 避免默认移动构造函数的隐式声明 + // 然而,该类仍为可移动构造因为复制构造函数能绑定到右值参数 + NoMove(const NoMove&) {} +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is move-constructible? " + << std::is_move_constructible::value << '\n' + << "Ex1 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex1 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n' + << "Ex2 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex2 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-constructible? " + << std::is_move_constructible::value << '\n' + << "NoMove is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_unbounded_array.cpp b/cpp_11/003_rtti_std_is_unbounded_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6499f0a4b10afe2d44771eaab8c619559d985855 --- /dev/null +++ b/cpp_11/003_rtti_std_is_unbounded_array.cpp @@ -0,0 +1,16 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_union.cpp b/cpp_11/003_rtti_std_is_union.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c8f6616adc5eb1208b1eeac6170666cb73a853ad --- /dev/null +++ b/cpp_11/003_rtti_std_is_union.cpp @@ -0,0 +1,22 @@ +#include +#include + +struct A {}; + +typedef union { + int a; + float b; +} B; + +struct C { + B d; +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_union::value << '\n'; + std::cout << std::is_union::value << '\n'; + std::cout << std::is_union::value << '\n'; + std::cout << std::is_union::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_unsigned.cpp b/cpp_11/003_rtti_std_is_unsigned.cpp new file mode 100644 index 0000000000000000000000000000000000000000..632c63bea2a0dc7beedab704d70320e0b27b8722 --- /dev/null +++ b/cpp_11/003_rtti_std_is_unsigned.cpp @@ -0,0 +1,17 @@ +#include +#include + +class A {}; +enum B : unsigned {}; +enum class C : unsigned {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_unsigned::value << '\n'; + std::cout << std::is_unsigned::value << '\n'; + std::cout << std::is_unsigned::value << '\n'; + std::cout << std::is_unsigned::value << '\n'; + std::cout << std::is_unsigned::value << '\n'; + std::cout << std::is_unsigned::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_void.cpp b/cpp_11/003_rtti_std_is_void.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71a070f2f8f05465ce99da71ef35a3c2a98dda7a --- /dev/null +++ b/cpp_11/003_rtti_std_is_void.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_void::value << '\n'; + std::cout << std::is_void::value << '\n'; +} diff --git a/cpp_11/003_rtti_std_is_volatile.cpp b/cpp_11/003_rtti_std_is_volatile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d06589eb9574b02161070d3fa384f12971515177 --- /dev/null +++ b/cpp_11/003_rtti_std_is_volatile.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_volatile::value << '\n'; + std::cout << std::is_volatile::value << '\n'; +} diff --git a/cpp_11/003_std_atomic_bool.cpp b/cpp_11/003_std_atomic_bool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..302f32c79b4946a87a308d0312146f6403b949e6 --- /dev/null +++ b/cpp_11/003_std_atomic_bool.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_bool bCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("bCount:%d\r\n", bCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("bCount:%d\r\n", bCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = bCount.load(memory_order_relaxed); + printf("finally bCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_char.cpp b/cpp_11/003_std_atomic_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8165a67261df44fdfb715cd5d0af40701c604ed --- /dev/null +++ b/cpp_11/003_std_atomic_char.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_chat cCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("cCount:%d\r\n", cCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("cCount:%d\r\n", cCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = cCount.load(memory_order_relaxed); + printf("finally cCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_char16_t.cpp b/cpp_11/003_std_atomic_char16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31d42969874b072441303d125924379edaba2e00 --- /dev/null +++ b/cpp_11/003_std_atomic_char16_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_char16_t char16_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("char16_tCount:%d\r\n", char16_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("char16_tCount:%d\r\n", char16_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = char16_tCount.load(memory_order_relaxed); + printf("finally char16_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_char32_t.cpp b/cpp_11/003_std_atomic_char32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caf0fea94be2466816a3eff5aae0a9088b959eb9 --- /dev/null +++ b/cpp_11/003_std_atomic_char32_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_char32_t char32_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("char32_tCount:%d\r\n", char32_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("char32_tCount:%d\r\n", char32_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = char32_tCount.load(memory_order_relaxed); + printf("finally char32_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_char8_t.cpp b/cpp_11/003_std_atomic_char8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2dbc14a3fde1700c3dce41a54299add471c3965 --- /dev/null +++ b/cpp_11/003_std_atomic_char8_t.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_char8_t char8_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("char8_tCount:%d\r\n", char8_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("char8_tCount:%d\r\n", char8_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = char8_tCount.load(memory_order_relaxed); + printf("finally char8_tCount:%d\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_int.cpp b/cpp_11/003_std_atomic_int.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4244d4ef446886473a24fcbc9de7bfd4a747a383 --- /dev/null +++ b/cpp_11/003_std_atomic_int.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int iCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("iCount:%d\r\n", iCount++); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("iCount:%d\r\n", iCount--); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + //printf("finally iCount:%d\r\n", iCount); + int x = iCount.load(memory_order_relaxed); + printf("finally iCount:%d\r\n", x); +} + + +// g++ -std=c++11 -pthread -o out atomictest.cpp && ./out +iCount:-4 +iCount:-3 +iCount:-2 +iCount:-1 +finally iCount:0 diff --git a/cpp_11/003_std_atomic_long.cpp b/cpp_11/003_std_atomic_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae89148fa9c0d7a75a3c79ff11fa7c4fef9bddc7 --- /dev/null +++ b/cpp_11/003_std_atomic_long.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_long longCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("longCount:%ld\r\n", longCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("longCount:%ld\r\n", longCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = longCount.load(memory_order_relaxed); + printf("finally longCount:%ld\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_long_long.cpp b/cpp_11/003_std_atomic_long_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe3b7c08276ea5b15f870b87ec3b4a43528f3645 --- /dev/null +++ b/cpp_11/003_std_atomic_long_long.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +std::atomic data{10}; +std::array return_values{}; + +void do_work(int thread_num) +{ + long long val = data.fetch_add(1, std::memory_order_relaxed); + return_values[thread_num] = val; +} + +int main() +{ + { + std::jthread th0{do_work, 0}; + std::jthread th1{do_work, 1}; + std::jthread th2{do_work, 2}; + std::jthread th3{do_work, 3}; + std::jthread th4{do_work, 4}; + } + + std::cout << "Result : " << data << '\n'; + + for (long long val : return_values) { + std::cout << "Seen return value : " << val << std::endl; + } +} diff --git a/cpp_11/003_std_atomic_short.cpp b/cpp_11/003_std_atomic_short.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e92bc4884b31e821ecbbf19bf653c150299bd435 --- /dev/null +++ b/cpp_11/003_std_atomic_short.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_short shortCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("shortCount:%d\r\n", shortCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("shortCount:%d\r\n", shortCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = shortCount.load(memory_order_relaxed); + printf("finally shortCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_signed_char.cpp b/cpp_11/003_std_atomic_signed_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30748328632b2f69ac7b6673579f1c16c0bd2652 --- /dev/null +++ b/cpp_11/003_std_atomic_signed_char.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_schar scCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("cCount:%d\r\n", scCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("scCount:%d\r\n", scCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = scCount.load(memory_order_relaxed); + printf("finally scCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_int16_t.cpp b/cpp_11/003_std_atomic_std_int16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94139808aadbe0ef3010a46d7c87ae73a4d691a2 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int16_t.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int16_t int16_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int16_tCount:%d\r\n", int16_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int16_tCount:%d\r\n", int16_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int16_tCount.load(memory_order_relaxed); + printf("finally int16_tCount:%d\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_std_int32_t.cpp b/cpp_11/003_std_atomic_std_int32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa61fa5c7b779fe64820e883f793c062790bd28b --- /dev/null +++ b/cpp_11/003_std_atomic_std_int32_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int32_t int32_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int32_tCount:%d\r\n", int32_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int32_tCount:%d\r\n", int32_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int32_tCount.load(memory_order_relaxed); + printf("finally int32_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_int64_t.cpp b/cpp_11/003_std_atomic_std_int64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7ef7a2bd4ebbd3bddbaa145a6265184f08ce3f2 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int64_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int64_t int64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int64_tCount:%d\r\n", int64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int64_tCount:%d\r\n", int64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int64_tCount.load(memory_order_relaxed); + printf("finally int64_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_int8_t.cpp b/cpp_11/003_std_atomic_std_int8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae373d1748b3bfb471007e7546cdd3ad962095f7 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int8_t.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int8_t int8_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int8_tCount:%d\r\n", int8_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int8_tCount:%d\r\n", int8_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int8_tCount.load(memory_order_relaxed); + printf("finally int8_tCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_std_int_fast16_t.cpp b/cpp_11/003_std_atomic_std_int_fast16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33c1bb93ec325800b7f3649f50c1ba22f5c44b1f --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_fast16_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_fast16_t int_fast16_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast16_tCount:%d\r\n", int_fast16_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast16_tCount:%d\r\n", int_fast16_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_fast16_tCount.load(memory_order_relaxed); + printf("finally int_fast16_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_int_fast32_t.cpp b/cpp_11/003_std_atomic_std_int_fast32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fa361a4ccf87d40d529b6384093972f2f357733 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_fast32_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_fast32_t int_fast32_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast32_tCount:%d\r\n", int_fast32_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast32_tCount:%d\r\n", int_fast32_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_fast32_tCount.load(memory_order_relaxed); + printf("finally int_fast32_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_int_fast64_t.cpp b/cpp_11/003_std_atomic_std_int_fast64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..912854c6d027b0177d21f77fbe464019402c6eff --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_fast64_t.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_fast64_t int_fast64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast64_tCount:%d\r\n", int_fast64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast64_tCount:%d\r\n", int_fast64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_fast64_tCount.load(memory_order_relaxed); + printf("finally int_fast64_tCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_std_int_fast8_t.cpp b/cpp_11/003_std_atomic_std_int_fast8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21aff79215b0393cff7d162c6c2856754c077e70 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_fast8_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_fast8_t int_fast8_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast8_tCount:%d\r\n", int_fast8_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_fast8_tCount:%d\r\n", int_fast8_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_fast8_tCount.load(memory_order_relaxed); + printf("finally int_fast8_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_int_least16_t.cpp b/cpp_11/003_std_atomic_std_int_least16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e24294240bf748837058924b66b504fd904c51ba --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_least16_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_least16_t int_least16_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least16_t_tCount:%d\r\n", int_least16_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least16_t_tCount:%d\r\n", int_least16_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_least16_t_tCount.load(memory_order_relaxed); + printf("finally int_least16_t_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_int_least32_t.cpp b/cpp_11/003_std_atomic_std_int_least32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0239028fec6a8af018eb0372678e974b85ee34a --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_least32_t.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_least32_t int_least32_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least32_t_tCount:%d\r\n", int_least32_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least32_t_tCount:%d\r\n", int_least32_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_least32_t_tCount.load(memory_order_relaxed); + printf("finally int_least32_t_tCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_std_int_least64_t.cpp b/cpp_11/003_std_atomic_std_int_least64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ccc83a88a8eff3fe8b7fabbf27e958238d585d2 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_least64_t.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_least64_t int_least64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least64_tCount:%d\r\n", int_least64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least64_tCount:%d\r\n", int_least64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_least64_tCount.load(memory_order_relaxed); + printf("finally int_least64_tCount:%d\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_std_int_least8_t.cpp b/cpp_11/003_std_atomic_std_int_least8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e99ce3a84d4c10577c33306307be7ef151354d5 --- /dev/null +++ b/cpp_11/003_std_atomic_std_int_least8_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_int_least8_t int_least8_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least8_t_tCount:%d\r\n", int_least8_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("int_least8_t_tCount:%d\r\n", int_least8_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = int_least8_t_tCount.load(memory_order_relaxed); + printf("finally int_least8_t_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_intmax_t.cpp b/cpp_11/003_std_atomic_std_intmax_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_intmax_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_std_intptr_t.cpp b/cpp_11/003_std_atomic_std_intptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_intptr_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_std_ptrdiff_t.cpp b/cpp_11/003_std_atomic_std_ptrdiff_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_ptrdiff_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_std_size_t.cpp b/cpp_11/003_std_atomic_std_size_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_size_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_std_uint16_t.cpp b/cpp_11/003_std_atomic_std_uint16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..952a7b12ec560b45d0a05715a6ca88ec68b77c40 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint16_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint16_t uint16_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint16_tCount:%d\r\n", uint16_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint16_tCount:%d\r\n", uint16_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint16_tCount.load(memory_order_relaxed); + printf("finally uint16_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_uint32_t.cpp b/cpp_11/003_std_atomic_std_uint32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10351860327bd511f3b21afc8fc40d3dcb6d72bb --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint32_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint32_t uint32_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint32_tCount:%d\r\n", uint32_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint32_tCount:%d\r\n", uint32_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint32_tCount.load(memory_order_relaxed); + printf("finally uint32_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_uint64_t.cpp b/cpp_11/003_std_atomic_std_uint64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c4c7922dd955ec8cbdf99bbc510b9ede321b8bb --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint64_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint64_t uint64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint64_tCount:%d\r\n", uint64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint64_tCount:%d\r\n", uint64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint64_tCount.load(memory_order_relaxed); + printf("finally uint64_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_uint8_t.cpp b/cpp_11/003_std_atomic_std_uint8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..837ead6c223bf9d46b32443352dec5639396b11a --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint8_t.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint8_t uint8_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint8_tCount:%d\r\n", uint8_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint8_tCount:%d\r\n", uint8_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint8_tCount.load(memory_order_relaxed); + printf("finally uint8_tCount:%d\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_std_uint_fast16_t.cpp b/cpp_11/003_std_atomic_std_uint_fast16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51549074e13369b095c47d605cbdac4467636f89 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_fast16_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_fast16_t uint_fast16_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast16_tCount:%d\r\n", uint_fast16_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast16_tCount:%d\r\n", uint_fast16_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_fast16_tCount.load(memory_order_relaxed); + printf("finally uint_fast16_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_uint_fast32_t.cpp b/cpp_11/003_std_atomic_std_uint_fast32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..382d680e58821d0d0d09fb4427f0fcff5ee23707 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_fast32_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_fast32_t uint_fast32_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast32_tCount:%d\r\n", uint_fast32_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast32_tCount:%d\r\n", uint_fast32_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_fast32_tCount.load(memory_order_relaxed); + printf("finally uint_fast32_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_uint_fast64_t.cpp b/cpp_11/003_std_atomic_std_uint_fast64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ec0e725c54eca826c553123ab3ab237f89add94 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_fast64_t.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_fast64_t uint_fast64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast64_tCount:%d\r\n", uint_fast64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast64_tCount:%d\r\n", uint_fast64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_fast64_tCount.load(memory_order_relaxed); + printf("finally uint_fast64_tCount:%d\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_std_uint_fast8_t.cpp b/cpp_11/003_std_atomic_std_uint_fast8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97bed02a2dec2b7582693bb9c896d48be817eb04 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_fast8_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_fast8_t uint_fast8_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast8_tCount:%d\r\n", uint_fast8_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_fast8_tCount:%d\r\n", uint_fast8_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_fast8_tCount.load(memory_order_relaxed); + printf("finally uint_fast8_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_uint_least16_t.cpp b/cpp_11/003_std_atomic_std_uint_least16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57af746034233bd16b292765675aa6328dbd3145 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_least16_t.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_least16_t uint_least16_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least16_t_tCount:%d\r\n", uint_least16_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least16_t_tCount:%d\r\n", uint_least16_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_least16_t_tCount.load(memory_order_relaxed); + printf("finally uint_least16_t_tCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_std_uint_least32_t.cpp b/cpp_11/003_std_atomic_std_uint_least32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d8e493c69d21ac0bbb2cf650e015b179156c9e4 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_least32_t.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_least32_t uint_least32_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least32_t_tCount:%d\r\n", uint_least32_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least32_t_tCount:%d\r\n", uint_least32_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_least32_t_tCount.load(memory_order_relaxed); + printf("finally uint_least32_t_tCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_std_uint_least64_t.cpp b/cpp_11/003_std_atomic_std_uint_least64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b939f3d122605915b662f100049c71ed721beb2d --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_least64_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_least64_t uint_least64_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least64_tCount:%d\r\n", uint_least64_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least64_tCount:%d\r\n", uint_least64_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_least64_tCount.load(memory_order_relaxed); + printf("finally uint_least64_tCount:%d\r\n", x); +} \ No newline at end of file diff --git a/cpp_11/003_std_atomic_std_uint_least8_t.cpp b/cpp_11/003_std_atomic_std_uint_least8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec18e5e5492405a92894d54b8ed0a1f2c9fb61cf --- /dev/null +++ b/cpp_11/003_std_atomic_std_uint_least8_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint_least8_t uint_least8_t_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least8_t_tCount:%d\r\n", uint_least8_t_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uint_least8_t_tCount:%d\r\n", uint_least8_t_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uint_least8_t_tCount.load(memory_order_relaxed); + printf("finally uint_least8_t_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_std_uintmax_t.cpp b/cpp_11/003_std_atomic_std_uintmax_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uintmax_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_std_uintptr_t.cpp b/cpp_11/003_std_atomic_std_uintptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_std_atomic_std_uintptr_t.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_std_atomic_unsigned_char.cpp b/cpp_11/003_std_atomic_unsigned_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd94a45d1a644b66d59041b581e4b1fa65824402 --- /dev/null +++ b/cpp_11/003_std_atomic_unsigned_char.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uchar ucCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("ucCount:%d\r\n", ucCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("ucCount:%d\r\n", ucCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = ucCount.load(memory_order_relaxed); + printf("finally ucCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_unsigned_int.cpp b/cpp_11/003_std_atomic_unsigned_int.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40596e8ebabec5c5ee3a8a4bfae2e0d322e616e6 --- /dev/null +++ b/cpp_11/003_std_atomic_unsigned_int.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_uint uintCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("uintCount:%d\r\n", uintCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("uintCount:%d\r\n", uintCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = uintCount.load(memory_order_relaxed); + printf("finally uintCount:%d\r\n", x); +} + diff --git a/cpp_11/003_std_atomic_unsigned_long.cpp b/cpp_11/003_std_atomic_unsigned_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50eb204d52f462592b9caae31ea0632b3175a38a --- /dev/null +++ b/cpp_11/003_std_atomic_unsigned_long.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_ulong ulongCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("ulongCount:%ld\r\n", ulongCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("ulongCount:%ld\r\n", ulongCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = ulongCount.load(memory_order_relaxed); + printf("finally ulongCount:%ld\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_unsigned_long_long.cpp b/cpp_11/003_std_atomic_unsigned_long_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c64634bf0b8d4740f59717de35f0dbcf53a0c0cc --- /dev/null +++ b/cpp_11/003_std_atomic_unsigned_long_long.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_ullong ullCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("ullCount:%lld\r\n", ullCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("ullCount:%lld\r\n", ullCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = ullCount.load(memory_order_relaxed); + printf("finally ullCount:%ld\r\n", x); +} + + diff --git a/cpp_11/003_std_atomic_unsigned_short.cpp b/cpp_11/003_std_atomic_unsigned_short.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b47640c94fa6a8dfd1f389887088e0b51cb2106f --- /dev/null +++ b/cpp_11/003_std_atomic_unsigned_short.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_ushort ushortCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("ushortCount:%d\r\n", ushortCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("ushortCount:%d\r\n", ushortCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = ushortCount.load(memory_order_relaxed); + printf("finally ushortCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_atomic_wchar_t.cpp b/cpp_11/003_std_atomic_wchar_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fff2d1e17ac767f53b285cd1a1002637bc502fae --- /dev/null +++ b/cpp_11/003_std_atomic_wchar_t.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; + +atomic_wchar_t wchar_tCount(0); + +void threadfun1() +{ + for(int i =0; i< 1000; i++) + { + printf("wchar_tCount:%d\r\n", wchar_tCount); + } +} +void threadfun2() +{ + for(int i =0; i< 1000; i++) + { + printf("wchar_tCount:%d\r\n", wchar_tCount); + } +} + +int main() +{ + std::list lstThread; + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun1)); + } + for (int i=0; i< 100; i++) + { + lstThread.push_back(thread(threadfun2)); + } + + for (auto& th: lstThread) + { + th.join(); + } + + int x = wchar_tCount.load(memory_order_relaxed); + printf("finally wchar_tCount:%d\r\n", x); +} diff --git a/cpp_11/003_std_thread_async.cpp b/cpp_11/003_std_thread_async.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3209286f2a00e686c344978752605b815c0183db --- /dev/null +++ b/cpp_11/003_std_thread_async.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +using namespace std::chrono; + +std::string fetchDataFromDB(std::string recvData) { + //确保函数要5秒才能执行完成 + std::this_thread::sleep_for(seconds(5)); + + //处理创建数据库连接、获取数据等事情 + return "DB_" + recvData; +} + +std::string fetchDataFromFile(std::string recvData) { + //确保函数要5秒才能执行完成 + std::this_thread::sleep_for(seconds(5)); + + //处理获取文件数据 + return "File_" + recvData; +} + +int main() { + //获取开始时间 + system_clock::time_point start = system_clock::now(); + + //从数据库获取数据 + std::string dbData = fetchDataFromDB("Data"); + + //从文件获取数据 + std::string fileData = fetchDataFromFile("Data"); + + //获取结束时间 + auto end = system_clock::now(); + + auto diff = duration_cast(end - start).count(); + std::cout << "Total Time taken= " << diff << "Seconds" << std::endl; + + //组装数据 + std::string data = dbData + " :: " + fileData; + + //输出组装的数据 + std::cout << "Data = " << data << std::endl; + + return 0; +} diff --git a/cpp_11/003_std_thread_std_atomic.cpp b/cpp_11/003_std_thread_std_atomic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f1294db5212812fde00f63adb3902a2e3b7dcf0 --- /dev/null +++ b/cpp_11/003_std_thread_std_atomic.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +std::atomic_flag lock = ATOMIC_FLAG_INIT; + +void f(int n) +{ + for (int cnt = 0; cnt < 40; ++cnt) { + while (lock.test_and_set(std::memory_order_acquire)) { // acquire lock + // Since C++20, it is possible to update atomic_flag's + // value only when there is a chance to acquire the lock. + // See also: https://stackoverflow.com/questions/62318642 + #if defined(__cpp_lib_atomic_flag_test) + while (lock.test(std::memory_order_relaxed)) // test lock + #endif + ; // spin + } + static int out{}; + std::cout << n << ((++out % 40) == 0 ? '\n' : ' '); + lock.clear(std::memory_order_release); // release lock + } +} + +int main() +{ + std::vector v; + for (int n = 0; n < 10; ++n) { + v.emplace_back(f, n); + } + for (auto& t : v) { + t.join(); + } +} diff --git a/cpp_11/003_std_thread_std_call_once.cpp b/cpp_11/003_std_thread_std_call_once.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6478c0f864e942c4a7daa6d94cfe2a915936b71 --- /dev/null +++ b/cpp_11/003_std_thread_std_call_once.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +std::once_flag flag1, flag2; + +void simple_do_once() +{ + std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; }); +} + +void may_throw_function(bool do_throw) +{ + if (do_throw) { + std::cout << "throw: call_once will retry\n"; // this may appear more than once + throw std::exception(); + } + std::cout << "Didn't throw, call_once will not attempt again\n"; // guaranteed once +} + +void do_once(bool do_throw) +{ + try { + std::call_once(flag2, may_throw_function, do_throw); + } + catch (...) { + } +} + +int main() +{ + std::thread st1(simple_do_once); + std::thread st2(simple_do_once); + std::thread st3(simple_do_once); + std::thread st4(simple_do_once); + st1.join(); + st2.join(); + st3.join(); + st4.join(); + + std::thread t1(do_once, true); + std::thread t2(do_once, true); + std::thread t3(do_once, false); + std::thread t4(do_once, true); + t1.join(); + t2.join(); + t3.join(); + t4.join(); +} diff --git a/cpp_11/003_std_thread_std_cond_ition_variable.cpp b/cpp_11/003_std_thread_std_cond_ition_variable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ebf1052d2fbbb17bcabd3dc14f6ae088e4cd9cb2 --- /dev/null +++ b/cpp_11/003_std_thread_std_cond_ition_variable.cpp @@ -0,0 +1,40 @@ +#include // std::cout +#include // std::thread +#include // std::mutex, std::unique_lock +#include // std::condition_variable + +std::mutex mtx; // 全局互斥锁. +std::condition_variable cv; // 全局条件变量. +bool ready = false; // 全局标志位. + +void do_print_id(int id) +{ + std::unique_lock lck(mtx); + while (!ready) // 如果标志位不为 true, 则等待... + cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后, + // 线程被唤醒, 继续往下执行打印线程编号id. + std::cout << "thread " << id << '\n'; +} + +void go() +{ + std::unique_lock lck(mtx); + ready = true; // 设置全局标志位为 true. + cv.notify_all(); // 唤醒所有线程. +} + +int main() +{ + std::thread threads[10]; + // spawn 10 threads: + for (int i = 0; i < 10; ++i) + threads[i] = std::thread(do_print_id, i); + + std::cout << "10 threads ready to race...\n"; + go(); // go! + + for (auto & th:threads) + th.join(); + + return 0; +} diff --git a/cpp_11/003_std_thread_std_future.cpp b/cpp_11/003_std_thread_std_future.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a613b896685d9f452073660a74d33f34fdaf753 --- /dev/null +++ b/cpp_11/003_std_thread_std_future.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() +{ + // future from a packaged_task + std::packaged_task task([]{ return 7; }); // wrap the function + std::future f1 = task.get_future(); // get a future + std::thread t(std::move(task)); // launch on a thread + + // future from an async() + std::future f2 = std::async(std::launch::async, []{ return 8; }); + + // future from a promise + std::promise p; + std::future f3 = p.get_future(); + std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach(); + + std::cout << "Waiting..." << std::flush; + f1.wait(); + f2.wait(); + f3.wait(); + std::cout << "Done!\nResults are: " + << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n'; + t.join(); +} diff --git a/cpp_11/003_std_thread_std_lock.cpp b/cpp_11/003_std_thread_std_lock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3528d742aeb037e5ddbb3f55b2e9da8501849b91 --- /dev/null +++ b/cpp_11/003_std_thread_std_lock.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Employee { + Employee(std::string id) : id(id) {} + std::string id; + std::vector lunch_partners; + std::mutex m; + std::string output() const + { + std::string ret = "Employee " + id + " has lunch partners: "; + for( const auto& partner : lunch_partners ) + ret += partner + " "; + return ret; + } +}; + +void send_mail(Employee &, Employee &) +{ + // simulate a time-consuming messaging operation + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +void assign_lunch_partner(Employee &e1, Employee &e2) +{ + static std::mutex io_mutex; + { + std::lock_guard lk(io_mutex); + std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl; + } + + // use std::lock to acquire two locks without worrying about + // other calls to assign_lunch_partner deadlocking us + { + std::lock(e1.m, e2.m); + std::lock_guard lk1(e1.m, std::adopt_lock); + std::lock_guard lk2(e2.m, std::adopt_lock); +// Equivalent code (if unique_locks are needed, e.g. for condition variables) +// std::unique_lock lk1(e1.m, std::defer_lock); +// std::unique_lock lk2(e2.m, std::defer_lock); +// std::lock(lk1, lk2); +// Superior solution available in C++17 +// std::scoped_lock lk(e1.m, e2.m); + { + std::lock_guard lk(io_mutex); + std::cout << e1.id << " and " << e2.id << " got locks" << std::endl; + } + e1.lunch_partners.push_back(e2.id); + e2.lunch_partners.push_back(e1.id); + } + send_mail(e1, e2); + send_mail(e2, e1); +} + +int main() +{ + Employee alice("alice"), bob("bob"), christina("christina"), dave("dave"); + + // assign in parallel threads because mailing users about lunch assignments + // takes a long time + std::vector threads; + threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); + threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); + threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); + threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); + + for (auto &thread : threads) thread.join(); + std::cout << alice.output() << '\n' << bob.output() << '\n' + << christina.output() << '\n' << dave.output() << '\n'; +} diff --git a/cpp_11/003_std_thread_std_mutex.cpp b/cpp_11/003_std_thread_std_mutex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e576119370c0c57db7454fcd17892af0edff49bb --- /dev/null +++ b/cpp_11/003_std_thread_std_mutex.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +std::map g_pages; +std::mutex g_pages_mutex; + +void save_page(const std::string &url) +{ + // simulate a long page fetch + std::this_thread::sleep_for(std::chrono::seconds(2)); + std::string result = "fake content"; + + std::lock_guard guard(g_pages_mutex); + g_pages[url] = result; +} + +int main() +{ + std::thread t1(save_page, "http://foo"); + std::thread t2(save_page, "http://bar"); + t1.join(); + t2.join(); + + // safe to access g_pages without lock now, as the threads are joined + for (const auto &pair : g_pages) { + std::cout << pair.first << " => " << pair.second << '\n'; + } +} diff --git a/cpp_11/003_std_thread_std_thread.cpp b/cpp_11/003_std_thread_std_thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60f2821081e8a965d8de4ae1a3ef9a5a6c12ca22 --- /dev/null +++ b/cpp_11/003_std_thread_std_thread.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +void foo() +{ + // simulate expensive operation + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +void bar() +{ + // simulate expensive operation + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +int main() +{ + std::cout << "starting first helper...\n"; + std::thread helper1(foo); + + std::cout << "starting second helper...\n"; + std::thread helper2(bar); + + std::cout << "waiting for helpers to finish..." << std::endl; + helper1.join(); + helper2.join(); + + std::cout << "done!\n"; +} diff --git a/cpp_11/003_std_thread_std_thread_local.cpp b/cpp_11/003_std_thread_std_thread_local.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7638ed48158e27d5578cd507fcb8f228c060e037 --- /dev/null +++ b/cpp_11/003_std_thread_std_thread_local.cpp @@ -0,0 +1,42 @@ +#include + +thread_local int g_n = 1; + +void f() +{ + g_n++; + printf("id=%d, n=%d\n", std::this_thread::get_id(),g_n); +} + +void foo() +{ + thread_local int i=0; + printf("id=%d, n=%d\n", std::this_thread::get_id(), i); + i++; +} + +void f2() +{ + foo(); + foo(); +} + +int main() +{ + g_n++; + f(); + std::thread t1(f); + std::thread t2(f); + + t1.join(); + t2.join(); + + + f2(); + std::thread t4(f2); + std::thread t5(f2); + + t4.join(); + t5.join(); + return 0; +} diff --git a/cpp_11/003_std_thread_volatile.cpp b/cpp_11/003_std_thread_volatile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b7f5db3f663f06ed8285db3ed3252462535b132 --- /dev/null +++ b/cpp_11/003_std_thread_volatile.cpp @@ -0,0 +1,8 @@ +//volatile作用: 作为指令关键字,确保本条指令不会受到编译器的优化而省略,而且要求每次直接读值。 + +//定义: + +volatile int nTest; +//volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如: +//操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化, +//从而可以提供对特殊地址的稳定访问。 diff --git a/cpp_11/003_stl_auto_ptr.cpp b/cpp_11/003_stl_auto_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32ec8e635e11a38bf0108b215d7912fd9e289e51 --- /dev/null +++ b/cpp_11/003_stl_auto_ptr.cpp @@ -0,0 +1 @@ +弃用 diff --git a/cpp_11/003_stl_container_init.cpp b/cpp_11/003_stl_container_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b1c91a0478fef5e5d6dc7f6464c4464bb2230fc --- /dev/null +++ b/cpp_11/003_stl_container_init.cpp @@ -0,0 +1,17 @@ +//C++ 11 统一初始化方法 +//变量,数组,STL容器,类的构造的初始化都可以使用{}方法 +class test { + int a ,b,c[4]; + int *d = new int[3]{1,2,3,4};//C++ 11提供的独有的初始化方式 + vector vec = {1,2,3}; //c++ 11 独有的 + map _map = {{"hello",1},{"world",2}};//c++ 11 独有的 +public: + test(int i ,int j):a(i),b(j),c{2,3,4,2}{}; +}; + +int main(int argc, const char * argv[]) +{ + + test t{1,2};//初始化test类 + return 0; +} diff --git a/cpp_11/003_stl_emplace.cpp b/cpp_11/003_stl_emplace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e71ce4df788b88463b3bde4ca7fd30820b7e164 --- /dev/null +++ b/cpp_11/003_stl_emplace.cpp @@ -0,0 +1,160 @@ + +#include "emplace.hpp" +#include +#include +#include +#include +#include +#include + +namespace emplace_ { + +/ +// reference: http://www.cplusplus.com/reference/vector/vector/emplace_back/ +int test_emplace_1() +{ +{ + /* + template + void emplace_back (Args&&... args); + */ + std::vector myvector = { 10, 20, 30 }; + + myvector.emplace_back(100); + myvector.emplace_back(200); + + std::cout << "myvector contains:"; + for (auto& x : myvector) + std::cout << ' ' << x; + std::cout << '\n'; +} + +{ + /* + template + iterator emplace (const_iterator position, Args&&... args); + */ + std::vector myvector = { 10, 20, 30 }; + + auto it = myvector.emplace(myvector.begin() + 1, 100); + myvector.emplace(it, 200); + myvector.emplace(myvector.end(), 300); + + std::cout << "myvector contains:"; + for (auto& x : myvector) + std::cout << ' ' << x; + std::cout << '\n'; +} + + return 0; +} + +/// +// reference: http://en.cppreference.com/w/cpp/container/vector/emplace_back +namespace { +struct President { + std::string name; + std::string country; + int year; + + President(std::string p_name, std::string p_country, int p_year) + : name(std::move(p_name)), country(std::move(p_country)), year(p_year) + { + std::cout << "I am being constructed.\n"; + } + President(President&& other) + : name(std::move(other.name)), country(std::move(other.country)), year(other.year) + { + std::cout << "I am being moved.\n"; + } + President& operator=(const President& other) = default; +}; +} + +int test_emplace_2() +{ + /* + The following code uses emplace_back to append an object of type President to a std::vector. + It demonstrates how emplace_back forwards parameters to the President constructor and shows + how using emplace_back avoids the extra copy or move operation required when using push_back. + */ + std::vector elections; + std::cout << "emplace_back:\n"; + elections.emplace_back("Nelson Mandela", "South Africa", 1994); + + std::vector reElections; + std::cout << "\npush_back:\n"; + reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936)); + + std::cout << "\nContents:\n"; + for (President const& president : elections) { + std::cout << president.name << " was elected president of " + << president.country << " in " << president.year << ".\n"; + } + for (President const& president : reElections) { + std::cout << president.name << " was re-elected president of " + << president.country << " in " << president.year << ".\n"; + } + + return 0; +} + + +// reference: https://stackoverflow.com/questions/4303513/push-back-vs-emplace-back +int test_emplace_3() +{ + /* + template + pair emplace (Args&&... args); + */ + typedef std::tuple Complicated; + + std::map m; + int anInt = 4; + double aDouble = 5.0; + std::string aString = "C++"; + + // cross your finger so that the optimizer is really good + //m.insert(/*std::make_pair*/std::pair(4, Complicated(anInt, aDouble, aString))); + m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); + + // should be easier for the optimizer + m.emplace(6, Complicated(anInt, aDouble, aString)); + /* + std::piecewise_construct: This constant value is passed as the first argument to construct a pair object + to select the constructor form that constructs its members in place by forwarding the elements of two + tuple objects to their respective constructor. + */ + m.emplace(std::piecewise_construct, std::make_tuple(8), std::make_tuple(anInt, aDouble, aString)); + + return 0; +} + +// +// reference: https://corecplusplustutorial.com/difference-between-emplace_back-and-push_back-function/ +namespace { +class Dat { + int i; + std::string ss; + char c; + +public: + Dat(int ii, std::string s, char cc) :i(ii), ss(s), c(cc) { } + + ~Dat() { } +}; +} + +int test_emplace_4() +{ + std::vector vec; + vec.reserve(3); + + vec.push_back(Dat(89, "New", 'G')); // efficiency lesser + //vec.push_back(678, "Newer", 'O'); // error,push_back can’t accept three arguments + vec.emplace_back(890, "Newest", 'D'); // work fine, efficiency is also more + + return 0; +} + +} diff --git a/cpp_11/003_stl_std_array.cpp b/cpp_11/003_stl_std_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba64be01ff7ae108b4d1d15f89b31fee68d3ca86 --- /dev/null +++ b/cpp_11/003_stl_std_array.cpp @@ -0,0 +1,247 @@ +#include "array.hpp" +#include +#include +#include +#include + +/// +// https://msdn.microsoft.com/en-us/library/bb983093.aspx +int test_array_3() +{ + // array::assign: Replaces all elements + typedef std::array Myarray; + + Myarray c0 = { 0, 1, 2, 3 }; + + // display contents " 0 1 2 3" + for (Myarray::const_iterator it = c0.begin(); + it != c0.end(); ++it) + std::cout << " " << *it; + std::cout << std::endl; + + Myarray c1; + c1.assign(4); + + // display contents " 4 4 4 4" + for (Myarray::const_iterator it = c1.begin(); + it != c1.end(); ++it) + std::cout << " " << *it; // 4 4 4 4 + std::cout << std::endl; + + return 0; +} + +/ +// reference: http://en.cppreference.com/w/cpp/container/array +int test_array_2() +{ + // construction uses aggregate initialization + std::array a1{ { 1, 2, 3 } }; // double-braces required in C++11 (not in C++14) + std::array a2 = { 1, 2, 3 }; // never required after = + std::array a3 = { std::string("a"), "b" }; + + // container operations are supported + std::sort(a1.begin(), a1.end()); + std::reverse_copy(a2.begin(), a2.end(), std::ostream_iterator(std::cout, " ")); + std::cout << '\n'; + + // ranged for loop is supported + for (const auto& s : a3) + std::cout << s << ' '; + std::cout << '\n'; + + return 0; +} + +// +// reference: http://www.cplusplus.com/reference/array/array/ + +// default initialization (non-local = static storage): +std::array global; // zero-initialized: {0,0,0} + +int test_array_1() +{ +{ // array::array: The array classes are aggregate types, and thus have no custom constructors. + // default initialization (local = automatic storage): + std::array first; // uninitialized: {?,?,?} + + // initializer-list initializations: + std::array second = { 10, 20 }; // initialized as: {10,20,0} + std::array third = { 1, 2, 3 }; // initialized as: {1,2,3} + + // copy initialization: + std::array fourth = third; // copy: {1,2,3} + + std::cout << "The contents of fourth are:"; + for (auto x : fourth) std::cout << ' ' << x; + std::cout << '\n'; +} + +{ // array::at: Returns a reference to the element at position n in the array + std::array myarray; + + // assign some values: + for (int i = 0; i < 10; i++) myarray.at(i) = i + 1; + + // print content: + std::cout << "myarray contains:"; + for (int i = 0; i < 10; i++) + std::cout << ' ' << myarray.at(i); + std::cout << '\n'; +} + +{ // array::back: Returns a reference to the last element in the array container. + // array::front: Returns a reference to the first element in the array container. + std::array myarray = { 5, 19, 77 }; + + std::cout << "front is: " << myarray.front() << std::endl; // 5 + std::cout << "back is: " << myarray.back() << std::endl; // 77 + + myarray.back() = 50; + myarray.front() = 999; + + std::cout << "myarray now contains:"; + for (int& x : myarray) std::cout << ' ' << x; // 999 19 50 + std::cout << '\n'; +} + +{ // array::begin: Returns an iterator pointing to the first element in the array container. + // array::end: Returns an iterator pointing to the past-the-end element in the array container. + std::array myarray = { 2, 16, 77, 34, 50 }; + + std::cout << "myarray contains:"; + for (auto it = myarray.begin(); it != myarray.end(); ++it) + std::cout << ' ' << *it; + std::cout << '\n'; +} + +{ // array::cbegin: Returns a const_iterator pointing to the first element in the array container. + // array::cend: Returns a const_iterator pointing to the past-the-end element in the array container. + std::array myarray = { 2, 16, 77, 34, 50 }; + + std::cout << "myarray contains:"; + + for (auto it = myarray.cbegin(); it != myarray.cend(); ++it) + std::cout << ' ' << *it; // cannot modify *it + + std::cout << '\n'; +} + +{ // array::crbegin: Returns a const_reverse_iterator pointing to the last element in the array container. + // array::crend: Returns a const_reverse_iterator pointing to the theoretical element preceding + // the first element in the vector, which is considered its reverse end. + std::array myarray = { 10, 20, 30, 40, 50, 60 }; + + std::cout << "myarray backwards:"; + for (auto rit = myarray.crbegin(); rit < myarray.crend(); ++rit) + std::cout << ' ' << *rit; // cannot modify *rit + + std::cout << '\n'; +} + +{ // array::data: Returns a pointer to the first element in the array object. + const char* cstr = "Test string"; + std::array charray; + + std::memcpy(charray.data(), cstr, 12); + std::cout << charray.data() << '\n'; +} + +{ // array::empty: Returns a bool value indicating whether the array container is empty, i.e. whether its size is 0. + std::array first; + std::array second; + std::cout << "first " << (first.empty() ? "is empty" : "is not empty") << '\n'; + std::cout << "second " << (second.empty() ? "is empty" : "is not empty") << '\n'; +} + +{ // array::fill: Sets val as the value for all the elements in the array object. + std::array myarray; + + myarray.fill(5); + + std::cout << "myarray contains:"; + for (int& x : myarray) { std::cout << ' ' << x; } + + std::cout << '\n'; +} + +{ // array::max_size: Returns the maximum number of elements that the array container can hold + // array::size: Returns the number of elements in the array container. + std::array myints; + std::cout << "size of myints: " << myints.size() << '\n'; + std::cout << "max_size of myints: " << myints.max_size() << '\n'; + std::cout << "sizeof(myints): " << sizeof(myints) << std::endl; +} + +{ // array::operator[]: Returns a reference to the element at position n in the array container. + std::array myarray; + unsigned int i; + + // assign some values: + for (i = 0; i < 10; i++) myarray[i] = i; + + // print content + std::cout << "myarray contains:"; + for (i = 0; i < 10; i++) + std::cout << ' ' << myarray[i]; + std::cout << '\n'; +} + +{ // array::rbegin: Returns a reverse iterator pointing to the last element in the array container. + // array::rend: Returns a reverse iterator pointing to the theoretical element preceding + // the first element in the array (which is considered its reverse end). + std::array myarray = { 4, 26, 80, 14 }; + + std::cout << "myarray contains:"; + for (auto rit = myarray.rbegin(); rit < myarray.rend(); ++rit) + std::cout << ' ' << *rit; + + std::cout << '\n'; +} + +{ // array::swap: Exchanges the content of the array by the content of x, + // which is another array object of the same type (including the same size). + std::array first = { 10, 20, 30, 40, 50 }; + std::array second = { 11, 22, 33, 44, 55 }; + + first.swap(second); + + std::cout << "first:"; + for (int& x : first) std::cout << ' ' << x; + std::cout << '\n'; + + std::cout << "second:"; + for (int& x : second) std::cout << ' ' << x; + std::cout << '\n'; +} + +{ // std::get: Returns a reference to the Ith element of array arr + // std::tuple_element: Accesses the static type of the elements in an array object as if it was a tuple. + std::array myarray = { 10, 20, 30 }; + std::tuple mytuple(10, 20, 30); + + std::tuple_element<0, decltype(myarray)>::type myelement; // int myelement + + myelement = std::get<2>(myarray); + std::get<2>(myarray) = std::get<0>(myarray); + std::get<0>(myarray) = myelement; + + std::cout << "first element in myarray: " << std::get<0>(myarray) << "\n"; + std::cout << "first element in mytuple: " << std::get<0>(mytuple) << "\n"; +} + +{ // std::relational operators(array): Performs the appropriate comparison operation between the array containers lhs and rhs. + std::array a = { 10, 20, 30, 40, 50 }; + std::array b = { 10, 20, 30, 40, 50 }; + std::array c = { 50, 40, 30, 20, 10 }; + + if (a == b) std::cout << "a and b are equal\n"; + if (b != c) std::cout << "b and c are not equal\n"; + if (bb) std::cout << "c is greater than b\n"; + if (a <= b) std::cout << "a is less than or equal to b\n"; + if (a >= b) std::cout << "a is greater than or equal to b\n"; +} + + return 0; +} diff --git a/cpp_11/003_stl_std_atomic_bool.cpp b/cpp_11/003_stl_std_atomic_bool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_bool.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_char.cpp b/cpp_11/003_stl_std_atomic_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_char.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_char16_t.cpp b/cpp_11/003_stl_std_atomic_char16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_char16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_char32_t.cpp b/cpp_11/003_stl_std_atomic_char32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_char32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_char8_t.cpp b/cpp_11/003_stl_std_atomic_char8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_char8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_int.cpp b/cpp_11/003_stl_std_atomic_int.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_int.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_long.cpp b/cpp_11/003_stl_std_atomic_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_long.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_long_long.cpp b/cpp_11/003_stl_std_atomic_long_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_long_long.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_short.cpp b/cpp_11/003_stl_std_atomic_short.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_short.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_signed_char.cpp b/cpp_11/003_stl_std_atomic_signed_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_signed_char.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int16_t.cpp b/cpp_11/003_stl_std_atomic_std_int16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int32_t.cpp b/cpp_11/003_stl_std_atomic_std_int32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int64_t.cpp b/cpp_11/003_stl_std_atomic_std_int64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int8_t.cpp b/cpp_11/003_stl_std_atomic_std_int8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_fast16_t.cpp b/cpp_11/003_stl_std_atomic_std_int_fast16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_fast16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_fast32_t.cpp b/cpp_11/003_stl_std_atomic_std_int_fast32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_fast32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_fast64_t.cpp b/cpp_11/003_stl_std_atomic_std_int_fast64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_fast64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_fast8_t.cpp b/cpp_11/003_stl_std_atomic_std_int_fast8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_fast8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_least16_t.cpp b/cpp_11/003_stl_std_atomic_std_int_least16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_least16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_least32_t.cpp b/cpp_11/003_stl_std_atomic_std_int_least32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_least32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_least64_t.cpp b/cpp_11/003_stl_std_atomic_std_int_least64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_least64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_int_least8_t.cpp b/cpp_11/003_stl_std_atomic_std_int_least8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_int_least8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_intmax_t.cpp b/cpp_11/003_stl_std_atomic_std_intmax_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_intmax_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_intptr_t.cpp b/cpp_11/003_stl_std_atomic_std_intptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_intptr_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_ptrdiff_t.cpp b/cpp_11/003_stl_std_atomic_std_ptrdiff_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_ptrdiff_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_size_t.cpp b/cpp_11/003_stl_std_atomic_std_size_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_size_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint16_t.cpp b/cpp_11/003_stl_std_atomic_std_uint16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint32_t.cpp b/cpp_11/003_stl_std_atomic_std_uint32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint64_t.cpp b/cpp_11/003_stl_std_atomic_std_uint64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint8_t.cpp b/cpp_11/003_stl_std_atomic_std_uint8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_fast16_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_fast16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_fast16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_fast32_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_fast32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_fast32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_fast64_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_fast64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_fast64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_fast8_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_fast8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_fast8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_least16_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_least16_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_least16_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_least32_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_least32_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_least32_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_least64_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_least64_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_least64_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uint_least8_t.cpp b/cpp_11/003_stl_std_atomic_std_uint_least8_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uint_least8_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uintmax_t.cpp b/cpp_11/003_stl_std_atomic_std_uintmax_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uintmax_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_std_uintptr_t.cpp b/cpp_11/003_stl_std_atomic_std_uintptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_std_uintptr_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_unsigned_char.cpp b/cpp_11/003_stl_std_atomic_unsigned_char.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_unsigned_char.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_unsigned_int.cpp b/cpp_11/003_stl_std_atomic_unsigned_int.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_unsigned_int.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_unsigned_long.cpp b/cpp_11/003_stl_std_atomic_unsigned_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_unsigned_long.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_unsigned_long_long.cpp b/cpp_11/003_stl_std_atomic_unsigned_long_long.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_unsigned_long_long.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_unsigned_short.cpp b/cpp_11/003_stl_std_atomic_unsigned_short.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_unsigned_short.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_atomic_wchar_t.cpp b/cpp_11/003_stl_std_atomic_wchar_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_stl_std_atomic_wchar_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_stl_std_begin.cpp b/cpp_11/003_stl_std_begin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae67306075bd9e42829fb99636671ccd593a6eaa --- /dev/null +++ b/cpp_11/003_stl_std_begin.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + auto vi = std::begin(v); + std::cout << std::showpos << *vi << '\n'; + + int a[] = { -5, 10, 15 }; + auto ai = std::begin(a); + std::cout << *ai << '\n'; +} diff --git a/cpp_11/003_stl_std_end.cpp b/cpp_11/003_stl_std_end.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ed034a2d27d11dc072f73718418b15f68e06869 --- /dev/null +++ b/cpp_11/003_stl_std_end.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + if (std::find(std::begin(v), std::end(v), 5) != std::end(v)) { + std::cout << "found a 5 in vector v!\n"; + } + + int a[] = { 5, 10, 15 }; + if (std::find(std::begin(a), std::end(a), 5) != std::end(a)) { + std::cout << "found a 5 in array a!\n"; + } +} diff --git a/cpp_11/003_stl_std_forward_list.cpp b/cpp_11/003_stl_std_forward_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9873e1ba2f23596d03408b74b6c1c606854da640 --- /dev/null +++ b/cpp_11/003_stl_std_forward_list.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() +{ + std::forward_list numbers; + std::cout << "Initially, numbers.empty(): " << numbers.empty() << '\n'; + + numbers.push_front(42); + numbers.push_front(13317); + std::cout << "After adding elements, numbers.empty(): " << numbers.empty() << '\n'; +} diff --git a/cpp_11/003_stl_std_get.cpp b/cpp_11/003_stl_std_get.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffdf421bf7e84872c0af0dfbc5fa5bdf29cc7ab4 --- /dev/null +++ b/cpp_11/003_stl_std_get.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() +{ + auto t = std::make_tuple(1, "Foo", 3.14); + // index-based access + std::cout << "(" << std::get<0>(t) << ", " << std::get<1>(t) + << ", " << std::get<2>(t) << ")\n"; + // type-based access (C++14 or later) + std::cout << "(" << std::get(t) << ", " << std::get(t) + << ", " << std::get(t) << ")\n"; + // Note: std::tie and structured binding may also be used to decompose a tuple +} diff --git a/cpp_11/003_stl_std_hash_std_bitset.cpp b/cpp_11/003_stl_std_hash_std_bitset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5048466fa7e4b365e3062d8e1c87175671370b9c --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_bitset.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +int main() +{ + std::bitset<4> b1{0}, b2{42}; + std::bitset<8> b3{0}, b4{42}; + + std::hash> hash_fn4; + std::hash> hash_fn8; + using bin64 = std::bitset<64>; + + std::cout + << bin64{hash_fn4(b1)} << " = " << hash_fn4(b1) << '\n' + << bin64{hash_fn4(b2)} << " = " << hash_fn4(b2) << '\n' + << bin64{hash_fn8(b3)} << " = " << hash_fn8(b3) << '\n' + << bin64{hash_fn8(b4)} << " = " << hash_fn8(b4) << '\n'; +} diff --git a/cpp_11/003_stl_std_hash_std_error_code.cpp b/cpp_11/003_stl_std_hash_std_error_code.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_error_code.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_stl_std_hash_std_string.cpp b/cpp_11/003_stl_std_hash_std_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f7bd2b33d8e7decf78ecb1eff4f4af625ba8d69 --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_string.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include +using namespace std::literals; + +int main() +{ + auto sv = "Stand back! I've got jimmies!"sv; + std::string s(sv); + std::pmr::string pmrs(sv); // use default allocator + + std::cout << std::hash{}(sv) << '\n'; + std::cout << std::hash{}(s) << '\n'; + std::cout << std::hash{}(pmrs) << '\n'; +} diff --git a/cpp_11/003_stl_std_hash_std_type_index.cpp b/cpp_11/003_stl_std_hash_std_type_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_type_index.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_11/003_stl_std_hash_std_u16string.cpp b/cpp_11/003_stl_std_hash_std_u16string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9cadaae26e3ff8bebf4ebaa17dd9c31b8e9a1aaf --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_u16string.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +using namespace std::literals; + +int main() { + std::cout << "\"A\" #: " << std::hash{}("A"sv) << '\n'; + std::cout << "L\"B\" #: " << std::hash{}(L"B"sv) << '\n'; + std::cout << "u8\"C\" #: " << std::hash{}(u8"C"sv) << '\n'; + std::cout << "u\"D\" #: " << std::hash{}(u"D"sv) << '\n'; + std::cout << "U\"E\" #: " << std::hash{}(U"E"sv) << '\n'; + + // std::hash for string_view family makes it possible to keep these view-types + // in unordered_* associative containers, such as unordered_set. But ensure + // the lifespan of referenced strings is no less than lifespan of the container, + // i.e. no dangling references occurred. + + std::unordered_set stars{"Rigel"sv, "Capella"sv, "Vega"sv, "Arcturus"sv}; + + for (std::string_view const& s : stars) { + std::cout << s << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_11/003_stl_std_hash_std_u32string.cpp b/cpp_11/003_stl_std_hash_std_u32string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9cadaae26e3ff8bebf4ebaa17dd9c31b8e9a1aaf --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_u32string.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +using namespace std::literals; + +int main() { + std::cout << "\"A\" #: " << std::hash{}("A"sv) << '\n'; + std::cout << "L\"B\" #: " << std::hash{}(L"B"sv) << '\n'; + std::cout << "u8\"C\" #: " << std::hash{}(u8"C"sv) << '\n'; + std::cout << "u\"D\" #: " << std::hash{}(u"D"sv) << '\n'; + std::cout << "U\"E\" #: " << std::hash{}(U"E"sv) << '\n'; + + // std::hash for string_view family makes it possible to keep these view-types + // in unordered_* associative containers, such as unordered_set. But ensure + // the lifespan of referenced strings is no less than lifespan of the container, + // i.e. no dangling references occurred. + + std::unordered_set stars{"Rigel"sv, "Capella"sv, "Vega"sv, "Arcturus"sv}; + + for (std::string_view const& s : stars) { + std::cout << s << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_11/003_stl_std_hash_std_vector_bool.cpp b/cpp_11/003_stl_std_hash_std_vector_bool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de9ef94ab094f18cf9de4a3a5d05c281fe31b262 --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_vector_bool.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +using vb = std::vector; + +vb to_vector_bool(unsigned n) { + vb v; + do { + v.push_back(n & 1); + n >>= 1; + } while(n); + return v; +} + +auto print(const vb& v, bool new_line = true) { + for (std::cout << "{ "; const bool e : v) + std::cout << e << ' '; + std::cout << '}' << (new_line ? '\n' : ' '); +} + +int main() +{ + for (auto i{0U}; i != 8; ++i) { + std::cout << std::hex << std::uppercase; + vb v = to_vector_bool(i); + std::cout << std::hash{}(v) << ' ' << std::dec; + print(v); + } + + // std::hash for vector makes it possible to keep them in + // unordered_* associative containers, such as unordered_set. + + std::unordered_set v{ vb{0}, vb{0,0}, vb{1}, vb{1}, vb{1,0}, vb{1,1} }; + + for (vb const& e : v) { + print(e, 0); + } +} diff --git a/cpp_11/003_stl_std_hash_std_wstring.cpp b/cpp_11/003_stl_std_hash_std_wstring.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9cadaae26e3ff8bebf4ebaa17dd9c31b8e9a1aaf --- /dev/null +++ b/cpp_11/003_stl_std_hash_std_wstring.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +using namespace std::literals; + +int main() { + std::cout << "\"A\" #: " << std::hash{}("A"sv) << '\n'; + std::cout << "L\"B\" #: " << std::hash{}(L"B"sv) << '\n'; + std::cout << "u8\"C\" #: " << std::hash{}(u8"C"sv) << '\n'; + std::cout << "u\"D\" #: " << std::hash{}(u"D"sv) << '\n'; + std::cout << "U\"E\" #: " << std::hash{}(U"E"sv) << '\n'; + + // std::hash for string_view family makes it possible to keep these view-types + // in unordered_* associative containers, such as unordered_set. But ensure + // the lifespan of referenced strings is no less than lifespan of the container, + // i.e. no dangling references occurred. + + std::unordered_set stars{"Rigel"sv, "Capella"sv, "Vega"sv, "Arcturus"sv}; + + for (std::string_view const& s : stars) { + std::cout << s << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_11/003_stl_std_make_tuple.cpp b/cpp_11/003_stl_std_make_tuple.cpp new file mode 100644 index 0000000000000000000000000000000000000000..123644561c0925fc55d591dc6c53de78cdfd7c03 --- /dev/null +++ b/cpp_11/003_stl_std_make_tuple.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +std::tuple f() // this function returns multiple values +{ + int x = 5; + return std::make_tuple(x, 7); // return {x,7}; in C++17 +} + +int main() +{ + // heterogeneous tuple construction + int n = 1; + auto t = std::make_tuple(10, "Test", 3.14, std::ref(n), n); + n = 7; + std::cout << "The value of t is " << "(" + << std::get<0>(t) << ", " << std::get<1>(t) << ", " + << std::get<2>(t) << ", " << std::get<3>(t) << ", " + << std::get<4>(t) << ")\n"; + + // function returning multiple values + int a, b; + std::tie(a, b) = f(); + std::cout << a << " " << b << "\n"; +} diff --git a/cpp_11/003_stl_std_move.cpp b/cpp_11/003_stl_std_move.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4b2afc2816d28592d3f34de85aad08d8941d86a --- /dev/null +++ b/cpp_11/003_stl_std_move.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +int main() +{ + std::string str = "Hello"; + std::vector v; + //调用常规的拷贝构造函数,新建字符数组,拷贝数据 + v.push_back(str); + std::cout << "After copy, str is \"" << str << "\"\n"; + //调用移动构造函数,掏空str,掏空后,最好不要使用str + v.push_back(std::move(str)); + std::cout << "After move, str is \"" << str << "\"\n"; + std::cout << "The contents of the vector are \"" << v[0] + << "\", \"" << v[1] << "\"\n"; +} diff --git a/cpp_11/003_stl_std_shared_ptr.cpp b/cpp_11/003_stl_std_shared_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df0a5802ae39dcdf03b97785ade375922636b528 --- /dev/null +++ b/cpp_11/003_stl_std_shared_ptr.cpp @@ -0,0 +1,15 @@ +#include +#include + +void fun(std::shared_ptr sp) +{ + std::cout << "fun: sp.use_count() == " << sp.use_count() << '\n'; +} + +int main() +{ + auto sp1 = std::make_shared(5); + std::cout << "sp1.use_count() == " << sp1.use_count() << '\n'; + + fun(sp1); +} diff --git a/cpp_11/003_stl_std_tie.cpp b/cpp_11/003_stl_std_tie.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c520cb6453609b75607421ac8d473eea23fbc961 --- /dev/null +++ b/cpp_11/003_stl_std_tie.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +struct S { + int n; + std::string s; + float d; + bool operator<(const S& rhs) const + { + // compares n to rhs.n, + // then s to rhs.s, + // then d to rhs.d + return std::tie(n, s, d) < std::tie(rhs.n, rhs.s, rhs.d); + } +}; + +int main() +{ + std::set set_of_s; // S is LessThanComparable + + S value{42, "Test", 3.14}; + std::set::iterator iter; + bool inserted; + + // unpacks the return value of insert into iter and inserted + std::tie(iter, inserted) = set_of_s.insert(value); + + if (inserted) + std::cout << "Value was inserted successfully\n"; +} diff --git a/cpp_11/003_stl_std_unique_ptr.cpp b/cpp_11/003_stl_std_unique_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdeeebd1670cf47242d25335104fb17bb4783be8 --- /dev/null +++ b/cpp_11/003_stl_std_unique_ptr.cpp @@ -0,0 +1,16 @@ +struct Foo { + Foo(int _val) : val(_val) { std::cout << "Foo...\n"; } + ~Foo() { std::cout << "~Foo...\n"; } + int val; +}; + +int main() +{ + std::unique_ptr up1(new Foo(1)); + std::unique_ptr up2(new Foo(2)); + + up1.swap(up2); + + std::cout << "up1->val:" << up1->val << std::endl; + std::cout << "up2->val:" << up2->val << std::endl; +} diff --git a/cpp_11/003_stl_std_unordered_map.cpp b/cpp_11/003_stl_std_unordered_map.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80fbce99e7743843f5fa1309baa05018e4f65c6f --- /dev/null +++ b/cpp_11/003_stl_std_unordered_map.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +int main() +{ + // 创建三个 string 的 unordered_map (映射到 string ) + std::unordered_map u = { + {"RED","#FF0000"}, + {"GREEN","#00FF00"}, + {"BLUE","#0000FF"} + }; + + // 迭代并打印 unordered_map 的关键和值 + for( const auto& n : u ) { + std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; + } + + // 添加新入口到 unordered_map + u["BLACK"] = "#000000"; + u["WHITE"] = "#FFFFFF"; + + // 用关键输出值 + std::cout << "The HEX of color RED is:[" << u["RED"] << "]\n"; + std::cout << "The HEX of color BLACK is:[" << u["BLACK"] << "]\n"; + + return 0; +} diff --git a/cpp_11/003_stl_std_unordered_multimap.cpp b/cpp_11/003_stl_std_unordered_multimap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0064ec3a326244d6b5cfe3303a8418e29beb8dd4 --- /dev/null +++ b/cpp_11/003_stl_std_unordered_multimap.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +int main() +{ + std::unordered_multimap numbers; + std::cout << "Initially, numbers.empty(): " << numbers.empty() << '\n'; + + numbers.emplace(42, 13); + numbers.insert(std::make_pair(13317, 123)); + std::cout << "After adding elements, numbers.empty(): " << numbers.empty() << '\n'; +} diff --git a/cpp_11/003_stl_std_unordered_multiset.cpp b/cpp_11/003_stl_std_unordered_multiset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59fa5fe7e7fc508a08fd9f38aadbcf4d65bf6919 --- /dev/null +++ b/cpp_11/003_stl_std_unordered_multiset.cpp @@ -0,0 +1,25 @@ +#include +#include + +template Os& operator<<(Os& os, const Co& co) { + os << "{"; + for (auto const& i : co) { os << ' ' << i; } + return os << " } "; +} + +int main() +{ + std::unordered_multiset a1{3, 1, 3, 2}, a2{5, 4, 5}; + + auto it1 = std::next(a1.begin()); + auto it2 = std::next(a2.begin()); + + const int& ref1 = *(a1.begin()); + const int& ref2 = *(a2.begin()); + + std::cout << a1 << a2 << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n'; + a1.swap(a2); + std::cout << a1 << a2 << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n'; + + // 注意交换前指代一个容器中的元素的每个迭代器在交换后都指代同一元素。对于引用同为真。 +} diff --git a/cpp_11/003_stl_std_unordered_set.cpp b/cpp_11/003_stl_std_unordered_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66b76548a6d4f679dc1dc5d9f382a1c0cfd632b7 --- /dev/null +++ b/cpp_11/003_stl_std_unordered_set.cpp @@ -0,0 +1,14 @@ +#include +#include +int main() +{ + std::multiset c = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + // 从 c 擦除所有奇数 + for(auto it = c.begin(); it != c.end(); ) + if(*it % 2 == 1) + it = c.erase(it); + else + ++it; + for(int n : c) + std::cout << n << ' '; +} diff --git a/cpp_11/003_stl_std_weak_ptr.cpp b/cpp_11/003_stl_std_weak_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..819ef129aee4f8a24f1eab535322aed218887f44 --- /dev/null +++ b/cpp_11/003_stl_std_weak_ptr.cpp @@ -0,0 +1,28 @@ +#include +#include + +void observe(std::weak_ptr weak) +{ + if (auto observe = weak.lock()) { + std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n"; + } else { + std::cout << "\tobserve() unable to lock weak_ptr<>\n"; + } +} + +int main() +{ + std::weak_ptr weak; + std::cout << "weak_ptr<> not yet initialized\n"; + observe(weak); + + { + auto shared = std::make_shared(42); + weak = shared; + std::cout << "weak_ptr<> initialized with shared_ptr.\n"; + observe(weak); + } + + std::cout << "shared_ptr<> has been destructed due to scope exit.\n"; + observe(weak); +} diff --git a/cpp_11/003_template_alias.cpp b/cpp_11/003_template_alias.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_template_alias.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_template_aliases.cpp b/cpp_11/003_template_aliases.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49fee45e24bc13b52883bbd938d4bcf4a5a5fc6f --- /dev/null +++ b/cpp_11/003_template_aliases.cpp @@ -0,0 +1,13 @@ +//gcc (GCC) 7.3.0 +#include +#include +using namespace std; +template +using ptr=std::shared_ptr; + +int main() +{ + //这里借助模板别名,用ptr 来代替std::shared_ptr + ptr p=std::make_shared(); + return 0; +} diff --git a/cpp_11/003_template_angle_bracket.cpp b/cpp_11/003_template_angle_bracket.cpp new file mode 100644 index 0000000000000000000000000000000000000000..366c0e8b1be434f160812bf0fd76a6277f410eb8 --- /dev/null +++ b/cpp_11/003_template_angle_bracket.cpp @@ -0,0 +1,18 @@ +template +struct Foo +{ + typedef T type; +}; + +template +class A +{ + // ... +}; + +int main() +{ + Foo>::type xx; //C++11之前编译出错,要写成 Foo > + return 0; +} + diff --git a/cpp_11/003_template_default_template_parameters.cpp b/cpp_11/003_template_default_template_parameters.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8075134ee20ff59cfb9696297f02ef5379a25fe8 --- /dev/null +++ b/cpp_11/003_template_default_template_parameters.cpp @@ -0,0 +1,10 @@ +template +void f(T t = 0, U u = 0) {}; +void g() +{ + f(1, 'c'); // f(1, 'c') + f(1); // f(1, 0), 使用了默认模板参数double + f(); // 错误: T无法被推导出来 + f(); // f(0, 0), 使用了默认模板参数double + f(); // f(0, 0) +} diff --git a/cpp_11/003_template_external.cpp b/cpp_11/003_template_external.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_11/003_template_external.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_11/003_template_external_template.cpp b/cpp_11/003_template_external_template.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23629deb48c2993fa92617e37948fe3d6b5c313e --- /dev/null +++ b/cpp_11/003_template_external_template.cpp @@ -0,0 +1,17 @@ +// test.h +template +void fun(T) { } + +// test1.cpp +#include "test.h" +void test1() +{ + fun(1); +} + +// test2.cpp +#include "test.h" +void test2() +{ + fun(2); +} diff --git a/cpp_11/003_template_variable_parameter_template.cpp b/cpp_11/003_template_variable_parameter_template.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7258e613d2e9868a1ef921b1d88e61d747db5d29 --- /dev/null +++ b/cpp_11/003_template_variable_parameter_template.cpp @@ -0,0 +1,20 @@ +#include + +int end_fun(int& a){ + return a; +} + +template +int end_fun(T& arg,Args... args){ + return arg + end_fun(args...); +} + + +int main(int argc,char *argv[]){ + + char a = 'a'; + int b = 2; + int c = 3; + int res = end_fun(a,b,c); + std::cout<<"Res: "<C++11新特性 + +![C++11思维导图](https://www.0voice.com/uiwebsite/cpp_new_features/C++11_new_features.png) +----------- + diff --git a/cpp_14/001_initializer_list_crbegin.cpp b/cpp_14/001_initializer_list_crbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a940cdae7b3a30aabc71c0c82e8e0c6fa975b05 --- /dev/null +++ b/cpp_14/001_initializer_list_crbegin.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + auto vi = std::rbegin(v); // the type of `vi` is std::vector::reverse_iterator + std::cout << "*vi = " << *vi << '\n'; + + *std::rbegin(v) = 42; // OK: after assignment v[2] == 42 +// *std::crbegin(v) = 13; // error: the location is read-only + + int a[] = { -5, 10, 15 }; + auto ai = std::rbegin(a); // the type of `ai` is std::reverse_iterator + std::cout << "*ai = " << *ai << '\n'; + + auto il = { 3, 1, 4 }; + // the type of `it` below is std::reverse_iterator: + for (auto it = std::rbegin(il); it != std::rend(il); ++it) + std::cout << *it << ' '; +} diff --git a/cpp_14/001_initializer_list_crend.cpp b/cpp_14/001_initializer_list_crend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6d9755c1fb50d2c1d497ca0df72c3f07f73fda8 --- /dev/null +++ b/cpp_14/001_initializer_list_crend.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int main() +{ + int a[] = {4, 6, -3, 9, 10}; + std::cout << "Array backwards: "; + std::copy(std::rbegin(a), std::rend(a), std::ostream_iterator(std::cout, " ")); + + std::cout << "\nVector backwards: "; + std::vector v = {4, 6, -3, 9, 10}; + std::copy(std::rbegin(v), std::crend(v), std::ostream_iterator(std::cout, " ")); +} diff --git a/cpp_14/001_initializer_list_rbegin.cpp b/cpp_14/001_initializer_list_rbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/001_initializer_list_rbegin.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/001_initializer_list_rend.cpp b/cpp_14/001_initializer_list_rend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40a375714d7c2f494e0fabb1c36c87456d8c293a --- /dev/null +++ b/cpp_14/001_initializer_list_rend.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + auto il = { 3, 1, 4 }; + for (auto it = std::rbegin(il); it != std::rend(il); ++it) + std::cout << *it << ' '; +} diff --git a/cpp_14/001_iterator_make_reverse_iterator.cpp b/cpp_14/001_iterator_make_reverse_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2afffa027df046bbafff74bc9d06332e4022124d --- /dev/null +++ b/cpp_14/001_iterator_make_reverse_iterator.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +int main() { + std::vector v{ 1, 3, 10, 8, 22 }; + + std::sort(v.begin(), v.end()); + std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, ", ")); + + std::cout << '\n'; + + std::copy( + std::make_reverse_iterator(v.end()), + std::make_reverse_iterator(v.begin()), + std::ostream_iterator(std::cout, ", ")); +} diff --git a/cpp_14/001_stl_begin.cpp b/cpp_14/001_stl_begin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d18813d233388699b2bf26b8943d6d377ad1c5f7 --- /dev/null +++ b/cpp_14/001_stl_begin.cpp @@ -0,0 +1,13 @@ +#include + +using namespace std; + +int main() +{ + int ia[] = {0,1,2,3}; + int *beg = begin(ia); + int *last = end(ia); + cout << *beg << endl; + cout << *(last-1) << endl; + return 0; +} diff --git a/cpp_14/001_stl_cbegin.cpp b/cpp_14/001_stl_cbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ba249e0c712a53c14203b2bc39a63a038b8ab65 --- /dev/null +++ b/cpp_14/001_stl_cbegin.cpp @@ -0,0 +1,15 @@ +#include +#include + +using namespace std; + +int main () +{ + set myset= {"Java", "C++","SQL"}; + + // 显示内容: + for (auto it = myset.cbegin(); it != myset.cend(); ++it) + cout <<*it << '\n'; + + return 0; +} diff --git a/cpp_14/001_stl_cend.cpp b/cpp_14/001_stl_cend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ba249e0c712a53c14203b2bc39a63a038b8ab65 --- /dev/null +++ b/cpp_14/001_stl_cend.cpp @@ -0,0 +1,15 @@ +#include +#include + +using namespace std; + +int main () +{ + set myset= {"Java", "C++","SQL"}; + + // 显示内容: + for (auto it = myset.cbegin(); it != myset.cend(); ++it) + cout <<*it << '\n'; + + return 0; +} diff --git a/cpp_14/001_stl_crbegin.cpp b/cpp_14/001_stl_crbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63ca9273b7ca29f958b2868bbdf155b7d546b5e9 --- /dev/null +++ b/cpp_14/001_stl_crbegin.cpp @@ -0,0 +1,25 @@ +#include +using namespace std; + +int main() +{ + + // initialize container + map mp; + + // insert elements in random order + mp.insert({ 2, 30 }); + mp.insert({ 1, 40 }); + mp.insert({ 3, 60 }); + mp.insert({ 4, 20 }); + mp.insert({ 5, 50 }); + + // prints the elements + cout << "\nThe map in reverse order is:\n"; + cout << "KEY\tELEMENT\n"; + for (auto itr = mp.crbegin(); itr != mp.crend(); ++itr) { + cout << itr->first + << '\t' << itr->second << '\n'; + } + return 0; +} diff --git a/cpp_14/001_stl_crend.cpp b/cpp_14/001_stl_crend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63ca9273b7ca29f958b2868bbdf155b7d546b5e9 --- /dev/null +++ b/cpp_14/001_stl_crend.cpp @@ -0,0 +1,25 @@ +#include +using namespace std; + +int main() +{ + + // initialize container + map mp; + + // insert elements in random order + mp.insert({ 2, 30 }); + mp.insert({ 1, 40 }); + mp.insert({ 3, 60 }); + mp.insert({ 4, 20 }); + mp.insert({ 5, 50 }); + + // prints the elements + cout << "\nThe map in reverse order is:\n"; + cout << "KEY\tELEMENT\n"; + for (auto itr = mp.crbegin(); itr != mp.crend(); ++itr) { + cout << itr->first + << '\t' << itr->second << '\n'; + } + return 0; +} diff --git a/cpp_14/001_stl_end.cpp b/cpp_14/001_stl_end.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d18813d233388699b2bf26b8943d6d377ad1c5f7 --- /dev/null +++ b/cpp_14/001_stl_end.cpp @@ -0,0 +1,13 @@ +#include + +using namespace std; + +int main() +{ + int ia[] = {0,1,2,3}; + int *beg = begin(ia); + int *last = end(ia); + cout << *beg << endl; + cout << *(last-1) << endl; + return 0; +} diff --git a/cpp_14/001_stl_rbegin.cpp b/cpp_14/001_stl_rbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bbda420da32ef950807bf8c88968644d1bf0980 --- /dev/null +++ b/cpp_14/001_stl_rbegin.cpp @@ -0,0 +1,15 @@ +#include +using namespace std; + +int main() +{ + deque dq = { 'a', 'b', 'c', 'd', 'e' }; + + cout << "The deque in reverse order: "; + + // prints the elements in reverse order + for (auto it = dq.rbegin(); it != dq.rend(); ++it) + cout << *it << " "; + + return 0; +} diff --git a/cpp_14/001_stl_rend.cpp b/cpp_14/001_stl_rend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bbda420da32ef950807bf8c88968644d1bf0980 --- /dev/null +++ b/cpp_14/001_stl_rend.cpp @@ -0,0 +1,15 @@ +#include +using namespace std; + +int main() +{ + deque dq = { 'a', 'b', 'c', 'd', 'e' }; + + cout << "The deque in reverse order: "; + + // prints the elements in reverse order + for (auto it = dq.rbegin(); it != dq.rend(); ++it) + cout << *it << " "; + + return 0; +} diff --git a/cpp_14/001_type_traits_is_final.cpp b/cpp_14/001_type_traits_is_final.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e7980118ee3d19a4725575ebf28bc4f60c1ff90 --- /dev/null +++ b/cpp_14/001_type_traits_is_final.cpp @@ -0,0 +1,13 @@ +#include +#include + +class A {}; +class B final {}; + +int main() +{ + std::cout + << std::boolalpha + << std::is_final::value << '\n' + << std::is_final::value << '\n'; +} diff --git a/cpp_14/001_type_traits_is_null_pointer.cpp b/cpp_14/001_type_traits_is_null_pointer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e9a47810cf17e544194829ab3dd9889a57467c6 --- /dev/null +++ b/cpp_14/001_type_traits_is_null_pointer.cpp @@ -0,0 +1,29 @@ +#include +#include + +void f(int*) +{ + std::cout << "Pointer to integer overload\n"; +} + +void f(double*) +{ + std::cout << "Pointer to double overload\n"; +} + +void f(std::nullptr_t) +{ + std::cout << "null pointer overload\n"; +} + +int main() +{ + int* pi {}; double* pd {}; + + f(pi); + f(pd); + f(nullptr); // would be ambiguous without void f(nullptr_t) + // f(0); // ambiguous call: all three functions are candidates + // f(NULL); // ambiguous if NULL is an integral null pointer constant + // (as is the case in most implementations) +} diff --git a/cpp_14/001_utility_exchange.cpp b/cpp_14/001_utility_exchange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef5fe6f214bb274b270879dd05c5970143852680 --- /dev/null +++ b/cpp_14/001_utility_exchange.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +class stream +{ + public: + + using flags_type = int; + + public: + + flags_type flags() const + { return flags_; } + + /// 以 newf 替换 flags_ 并返回旧值。 + flags_type flags(flags_type newf) + { return std::exchange(flags_, newf); } + + private: + + flags_type flags_ = 0; +}; + +void f() { std::cout << "f()"; } + +int main() +{ + stream s; + + std::cout << s.flags() << '\n'; + std::cout << s.flags(12) << '\n'; + std::cout << s.flags() << "\n\n"; + + std::vector v; + + // 因为第二模板形参有默认值,故能以花括号初始化列器表为第二参数。 + // 下方表达式等价于 std::exchange(v, std::vector{1,2,3,4}); + + std::exchange(v, {1,2,3,4}); + + std::copy(begin(v),end(v), std::ostream_iterator(std::cout,", ")); + + std::cout << "\n\n"; + + void (*fun)(); + + // 模板形参的默认值亦使得能以通常函数为第二参数。 + // 下方表达式等价于 std::exchange(fun, static_cast(f)) + std::exchange(fun,f); + fun(); +} diff --git a/cpp_14/001_utility_integer_sequence.cpp b/cpp_14/001_utility_integer_sequence.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f099917dc1326247359f4c35fadce7c3895d8e3 --- /dev/null +++ b/cpp_14/001_utility_integer_sequence.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +template +decltype(auto) apply_impl(F f, const T& t, std::index_sequence) +{ + return f(std::get(t)...); +} + +template +decltype(auto) apply(F f, const T& t) +{ + return apply_impl(f, t, std::make_index_sequence::value>()); +} + + +int main() +{ + auto tp = std::make_tuple(1, 'A', 1.2); + + auto ld = [](int arg1, char arg2, double arg3)->int{ + std::cout << "call func(" << arg1 << ", " << arg2 << ", " << arg3 << ")" << std::endl; + return 0; + }; + + int ret= apply(ld, tp); +} diff --git a/cpp_14/002_type_traits_bool_constant.cpp b/cpp_14/002_type_traits_bool_constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_bool_constant.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_byte.cpp b/cpp_14/002_type_traits_byte.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_byte.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_conjunction.cpp b/cpp_14/002_type_traits_conjunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_conjunction.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_disjunction.cpp b/cpp_14/002_type_traits_disjunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_disjunction.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_invoke_result.cpp b/cpp_14/002_type_traits_invoke_result.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_invoke_result.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_aggregate.cpp b/cpp_14/002_type_traits_is_aggregate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_aggregate.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_invocable.cpp b/cpp_14/002_type_traits_is_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_invocable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_invocable_r.cpp b/cpp_14/002_type_traits_is_invocable_r.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_invocable_r.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_nothrow_invocable.cpp b/cpp_14/002_type_traits_is_nothrow_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_nothrow_invocable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_nothrow_invocable_r.cpp b/cpp_14/002_type_traits_is_nothrow_invocable_r.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_nothrow_invocable_r.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_nothrow_swappable.cpp b/cpp_14/002_type_traits_is_nothrow_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_nothrow_swappable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_nothrow_swappable_with.cpp b/cpp_14/002_type_traits_is_nothrow_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_nothrow_swappable_with.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_swappable.cpp b/cpp_14/002_type_traits_is_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_swappable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_is_swappable_with.cpp b/cpp_14/002_type_traits_is_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_is_swappable_with.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_ndisjunctionegation.cpp b/cpp_14/002_type_traits_ndisjunctionegation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_ndisjunctionegation.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/002_type_traits_void_t.cpp b/cpp_14/002_type_traits_void_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/002_type_traits_void_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_as_const.cpp b/cpp_14/003_utility_as_const.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_as_const.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place.cpp b/cpp_14/003_utility_in_place.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place_index.cpp b/cpp_14/003_utility_in_place_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place_index.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place_index_t.cpp b/cpp_14/003_utility_in_place_index_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place_index_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place_t.cpp b/cpp_14/003_utility_in_place_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place_type.cpp b/cpp_14/003_utility_in_place_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place_type.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/003_utility_in_place_type_t.cpp b/cpp_14/003_utility_in_place_type_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/003_utility_in_place_type_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/004_tuple_apply.cpp b/cpp_14/004_tuple_apply.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/004_tuple_apply.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/004_tuple_make_from_tuple.cpp b/cpp_14/004_tuple_make_from_tuple.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/004_tuple_make_from_tuple.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_bad_optional_access.cpp b/cpp_14/005_optional_bad_optional_access.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_bad_optional_access.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place.cpp b/cpp_14/005_optional_in_place.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place_index.cpp b/cpp_14/005_optional_in_place_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place_index.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place_index_t.cpp b/cpp_14/005_optional_in_place_index_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place_index_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place_t.cpp1 b/cpp_14/005_optional_in_place_t.cpp1 new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place_t.cpp1 @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place_type.cpp b/cpp_14/005_optional_in_place_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place_type.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_in_place_type_t.cpp b/cpp_14/005_optional_in_place_type_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_in_place_type_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_make_optional.cpp b/cpp_14/005_optional_make_optional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_make_optional.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_nullopt.cpp b/cpp_14/005_optional_nullopt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_nullopt.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_nullopt_t.cpp b/cpp_14/005_optional_nullopt_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_nullopt_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_optional.cpp b/cpp_14/005_optional_optional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_optional.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_std_hash.cpp b/cpp_14/005_optional_std_hash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_std_hash.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/005_optional_std_swap.cpp b/cpp_14/005_optional_std_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/005_optional_std_swap.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_get_if.cpp b/cpp_14/006_variant_get_if.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_get_if.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_holds_alternative.cpp b/cpp_14/006_variant_holds_alternative.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_holds_alternative.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_std_get.cpp b/cpp_14/006_variant_std_get.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_std_get.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_std_swap.cpp b/cpp_14/006_variant_std_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_std_swap.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_variant.cpp b/cpp_14/006_variant_variant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_variant.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/006_variant_visit.cpp b/cpp_14/006_variant_visit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_14/006_variant_visit.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_14/README.md b/cpp_14/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4e783a448a104b46fc997ae1a9d574153bb47f53 --- /dev/null +++ b/cpp_14/README.md @@ -0,0 +1,382 @@ +##

C++14新特性

+ +![C++14思维导图](https://www.0voice.com/uiwebsite/cpp_new_features/C++14_new_features.png) +----------- + +

函数返回值类型推导

+ +C++14对函数返回类型推导规则做了优化,先看一段代码: + +```CPP +#include + +using namespace std; + +auto func(int i) { + return i; +} + +int main() { + cout << func(4) << endl; + return 0; +} +``` +使用C++11编译: +```CPP +~/test$ g++ test.cc -std=c++11 +test.cc:5:16: error: ‘func’ function uses ‘auto’ type specifier without trailing return type + auto func(int i) { + ^ +test.cc:5:16: note: deduced return type only available with -std=c++14 or -std=gnu++14 +``` +上面的代码使用C++11是不能通过编译的,通过编译器输出的信息也可以看见这个特性需要到C++14才被支持。 + +返回值类型推导也可以用在模板中: + +```CPP +#include +using namespace std; + +template auto func(T t) { return t; } + +int main() { + cout << func(4) << endl; + cout << func(3.4) << endl; + return 0; +} +``` + +注意: + +)函数内如果有多个return语句,它们必须返回相同的类型,否则编译失败。 + +```CPP +auto func(bool flag) { + if (flag) return 1; + else return 2.3; // error +} +// inconsistent deduction for auto return type: ‘int’ and then ‘double’ +``` + +)如果return语句返回初始化列表,返回值类型推导也会失败 + +```CPP +auto func() { + return {1, 2, 3}; // error returning initializer list +} +``` + +) 如果函数是虚函数,不能使用返回值类型推导 +```CPP +struct A { + // error: virtual function cannot have deduced return type + virtual auto func() { return 1; } +} +``` + +) 返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义 +```CPP +auto f(); // declared, not yet defined +auto f() { return 42; } // defined, return type is int + +int main() { + cout << f() << endl; +} +``` + +)返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。 +```CPP +auto sum(int i) { + if (i == 1) + return i; // return int + else + return sum(i - 1) + i; // ok +} +``` + +
+
+ +

lambda参数auto

+ +在C++11中,lambda表达式参数需要使用具体的类型声明: + +```CPP +auto f = [] (int a) { return a; } +``` + +在C++14中,对此进行优化,lambda表达式参数可以直接是auto: + +```CPP +auto f = [] (auto a) { return a; }; +cout << f(1) << endl; +cout << f(2.3f) << endl; +``` + +
+
+ +

变量模板

+ +C++14支持变量模板: + +```CPP +template +constexpr T pi = T(3.1415926535897932385L); + +int main() { + cout << pi << endl; // 3 + cout << pi << endl; // 3.14159 + return 0; +} +``` + +
+
+ +

别名模板

+ +C++14也支持别名模板: + +```CPP +template +struct A { + T t; + U u; +}; + +template +using B = A; + +int main() { + B b; + b.t = 10; + b.u = 20; + cout << b.t << endl; + cout << b.u << endl; + return 0; +} +``` + +
+
+ +

constexpr的限制

+ +C++14相较于C++11对constexpr减少了一些限制: + +)C++11中constexpr函数可以使用递归,在C++14中可以使用局部变量和循环 + +```CPP +constexpr int factorial(int n) { // C++14 和 C++11均可 + return n <= 1 ? 1 : (n * factorial(n - 1)); +} +``` + +在C++14中可以这样做: +```CPP +constexpr int factorial(int n) { // C++11中不可,C++14中可以 + int ret = 0; + for (int i = 0; i < n; ++i) { + ret += i; + } + return ret; +} +``` + +)C++11中constexpr函数必须必须把所有东西都放在一个单独的return语句中,而constexpr则无此限制: +```CPP +constexpr int func(bool flag) { // C++14 和 C++11均可 + return 0; +} +``` + +在C++14中可以这样: +```CPP +constexpr int func(bool flag) { // C++11中不可,C++14中可以 + if (flag) return 1; + else return 0; +} +``` + +
+
+ +

[[deprecated]]标记

+ +C++14中增加了deprecated标记,修饰类、变、函数等,当程序中使用到了被其修饰的代码时,编译时被产生警告,用户提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用。 + +```CPP +struct [[deprecated]] A { }; + +int main() { + A a; + return 0; +} +``` + +当编译时,会出现如下警告: +```CPP +~/test$ g++ test.cc -std=c++14 +test.cc: In function ‘int main()’: +test.cc:11:7: warning: ‘A’ is deprecated [-Wdeprecated-declarations] + A a; + ^ +test.cc:6:23: note: declared here + struct [[deprecated]] A { + ``` + +
+
+ +

二进制字面量与整形字面量分隔符

+ +C++14引入了二进制字面量,也引入了分隔符,防止看起来眼花哈~ +```CPP +int a = 0b0001'0011'1010; +double b = 3.14'1234'1234'1234; +``` + +
+
+ +

std::make_unique

+ +C++11中有std::make_shared,却没有std::make_unique,在C++14已经改善。 + +```CPP +struct A {}; +std::unique_ptr
ptr = std::make_unique(); +``` + +
+
+ +

std::shared_timed_mutex与std::shared_lock

+ +C++14通过std::shared_timed_mutex和std::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不可以同时和读操作一起进行。 + +实现方式如下: + +```CPP +struct ThreadSafe { + mutable std::shared_timed_mutex mutex_; + int value_; + + ThreadSafe() { + value_ = 0; + } + + int get() const { + std::shared_lock loc(mutex_); + return value_; + } + + void increase() { + std::unique_lock lock(mutex_); + value_ += 1; + } +}; +``` + +
+
+ +

std::integer_sequence

+```CPP +template +void print_sequence(std::integer_sequence int_seq) +{ + std::cout << "The sequence of size " << int_seq.size() << ": "; + ((std::cout << ints << ' '), ...); + std::cout << '\n'; +} + +int main() { + print_sequence(std::integer_sequence{}); + return 0; +} +``` + +输出: + +```CPP +7 9 2 5 1 9 1 6 +``` + +std::integer_sequence和std::tuple的配合使用: + +```CPP +template +auto map_filter_tuple(F f, T& t) { + return std::make_tuple(f(std::get(t))...); +} + +template +auto map_filter_tuple(std::index_sequence, F f, T& t) { + return std::make_tuple(f(std::get(t))...); +} + +template +auto map_filter_tuple(F&& f, T& t) { + return map_filter_tuple(S{}, std::forward(f), t); +} +``` + +
+
+ +

std::exchange

+ +直接看代码吧: + +```CPP +int main() { + std::vector v; + std::exchange(v, {1,2,3,4}); + cout << v.size() << endl; + for (int a : v) { + cout << a << " "; + } + return 0; +} +``` +看样子貌似和std::swap作用相同,那它俩有什么区别呢? + +可以看下exchange的实现: + +```CPP +template +constexpr T exchange(T& obj, U&& new_value) { + T old_value = std::move(obj); + obj = std::forward(new_value); + return old_value; +} +``` + +可以看见new_value的值给了obj,而没有对new_value赋值! + +
+
+ +

std::quoted

+ +C++14引入std::quoted用于给字符串添加双引号,直接看代码: + +```CPP +int main() { + string str = "hello world"; + cout << str << endl; + cout << std::quoted(str) << endl; + return 0; +} +``` + +编译&输出: + +```CPP +~/test$ g++ test.cc -std=c++14 +~/test$ ./a.out +hello world +"hello world" +``` diff --git a/cpp_17/001_keywords_README.md b/cpp_17/001_keywords_README.md new file mode 100644 index 0000000000000000000000000000000000000000..6a876ad6164bb5d303037156e02aa64bac90f5e2 --- /dev/null +++ b/cpp_17/001_keywords_README.md @@ -0,0 +1,5 @@ +####

C++17含义变化或者新增含义关键字(meaning changed or new meaning added)

+ +#####
register
+ +register在C++17中已弃用 diff --git a/cpp_17/002_type_traits_byte.cpp b/cpp_17/002_type_traits_byte.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7aadbbfaabfc0cf6ff76a893abd8abc50dc81721 --- /dev/null +++ b/cpp_17/002_type_traits_byte.cpp @@ -0,0 +1,12 @@ +#include // for std::byte + +std::byte b1{0x3F}; +std::byte b2{0b1111'0000}; +std::byte b4[4] {b1, b2, std::byte{1}}; // 4 bytes (last is 0) + +if (b1 == b4[0]) +{ + b1 <<= 1; +} + +std::cout << std::to_integer(b1) << '\n'; // outputs: \T{126} diff --git a/cpp_17/002_type_traits_conjunction.cpp b/cpp_17/002_type_traits_conjunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b233f42e29aec4090cc0dfa7a66a37dfd4370fd --- /dev/null +++ b/cpp_17/002_type_traits_conjunction.cpp @@ -0,0 +1,21 @@ +#include +#include + +// 若所有 Ts... 都拥有等同于 T 的类型,则启用 func +template +std::enable_if_t...>> +func(T, Ts...) { + std::cout << "all types in pack are T\n"; +} + +// 否则 +template +std::enable_if_t...>> +func(T, Ts...) { + std::cout << "not all types in pack are T\n"; +} + +int main() { + func(1, 2, 3); + func(1, 2, "hello!"); +} diff --git a/cpp_17/002_type_traits_disjunction.cpp b/cpp_17/002_type_traits_disjunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c8b7fdf1d989e0f45833dba783e531fdb0bcee5e --- /dev/null +++ b/cpp_17/002_type_traits_disjunction.cpp @@ -0,0 +1,16 @@ +#include +#include + +using result0 = + std::disjunction, std::bool_constant, + std::bool_constant>; +using result1 = + std::disjunction, std::bool_constant, + std::bool_constant>; + +int main() +{ + std::cout << std::boolalpha; + std::cout << result0::value << '\n'; + std::cout << result1::value << '\n'; +} diff --git a/cpp_17/002_type_traits_integral_constant.cpp b/cpp_17/002_type_traits_integral_constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b996bb5482557825018413f153180028868d020f --- /dev/null +++ b/cpp_17/002_type_traits_integral_constant.cpp @@ -0,0 +1,29 @@ +#include + +int main() +{ + typedef std::integral_constant two_t; + typedef std::integral_constant four_t; + +// static_assert(std::is_same::value, +// "two_t and four_t are not equal!"); +// error: static assertion failed: "two_t and four_t are not equal!" + + static_assert(two_t::value*2 == four_t::value, + "2*2 != 4" + ); + + enum class my_e { e1, e2 }; + + typedef std::integral_constant my_e_e1; + typedef std::integral_constant my_e_e2; + + static_assert(my_e_e1() == my_e::e1); + +// static_assert(my_e_e1::value == my_e::e2, +// "my_e_e1::value != my_e::e2"); +// error: static assertion failed: "my_e_e1::value != my_e::e2" + + static_assert(std::is_same::value, + "my_e_e2 != my_e_e2"); +} diff --git a/cpp_17/002_type_traits_invoke_result.cpp b/cpp_17/002_type_traits_invoke_result.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55117cf682d13d8ad4367977d3e78a8b3205e7a5 --- /dev/null +++ b/cpp_17/002_type_traits_invoke_result.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +using namespace std; + +struct A { + double operator()(char, int&) {} + float operator()(int) { return 1.0; } +}; + +int func(const int& x){ return x;} + +struct CallObject { + + inline CallObject(const int& val) {} + + template + inline T operator()(const T& x)noexcept{return x;} +}; + +struct CObject { + int data; + + inline CObject(const int& val) :data(val) {} + inline int getData(const int& x)noexcept{return data+x;} +}; + +template +typename std::result_of::type f(T& t) +{ + std::cout << "overload of f for callable T\n"; + return t(0); +} + +template +int f(U u) +{ + std::cout << "overload of f for non-callable T\n"; + return u; +} + +int main() { + //普通函数: + static_assert(is_same::value, ""); //注意func并没有转为指针 + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + + static_assert((is_same::type>::value) == true); + + cout << typeid(invoke_result::type).name() << endl; //int + cout << typeid(invoke_result::type).name() << endl;//int + cout << typeid(invoke_result::type).name() << endl;//int + cout << typeid(invoke_result::type).name() << endl; //int + + cout << typeid(invoke_result::type).name() << endl;//int __cdecl(int const & __ptr64) + cout << typeid(invoke_result::type).name() << endl; //int __cdecl(int const & __ptr64) + + cout << typeid(invoke_result::type).name() << endl;//int + cout << typeid(invoke_result::type).name() << endl;//float + + //decltype(declval().getData(declval()));//错误?不明白请高手补充 + + cout << is_same().data)>::value << endl; + cout << typeid(invoke_result::type).name() << endl;//int + + //用char和int&参数调用A的结果是double + std::result_of::type x1 = 3.14; // double x1; + static_assert(std::is_same::value, ""); + + // std :: invoke_result使用不同的语法(不带括号) + std::invoke_result::type x2 = 3.14;// double x1; + static_assert(std::is_same::value, ""); + + //用int参数调用A的结果是float + std::result_of::type x3 = float(3.14); // float x3; + static_assert(std::is_same::value, ""); + + std::invoke_result::type x31 = float(3.14);// float x1; + static_assert(std::is_same::value, ""); + + // result_of可以与指向成员函数的指针一起使用,如下所示: + struct B { double Func(char, int&); }; + std::result_of::type x4 = 3.14; + static_assert(std::is_same::value, ""); + + cout << typeid(invoke_result::type).name() << endl;//double + + // B++11错误; calls the non-callable overload in B++14 + f(1);// 输出overload of f for non-callable T +} diff --git a/cpp_17/002_type_traits_is_aggregate.cpp b/cpp_17/002_type_traits_is_aggregate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9b7a8b4469f8c698915b47258b6a69741662e97 --- /dev/null +++ b/cpp_17/002_type_traits_is_aggregate.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +template +struct D : std::string, std::complex +{ + std::string data; +}; + +int main(void) +{ + D s{ {"hello"}, {4.5,6.7}, "world" }; // OK since C++17 + const auto result = std::is_aggregate::value; + std::cout << result << std::endl; + + return 0; +} diff --git a/cpp_17/002_type_traits_is_invocable.cpp b/cpp_17/002_type_traits_is_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81ea0fe36679fcb9de33dec3e8e75cff2a801f23 --- /dev/null +++ b/cpp_17/002_type_traits_is_invocable.cpp @@ -0,0 +1,18 @@ +#include + +auto func2(char) -> int (*)() +{ + return nullptr; +} + +int main() +{ + static_assert( std::is_invocable_v ); + static_assert( not std::is_invocable_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); +} diff --git a/cpp_17/002_type_traits_is_invocable_r.cpp b/cpp_17/002_type_traits_is_invocable_r.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81ea0fe36679fcb9de33dec3e8e75cff2a801f23 --- /dev/null +++ b/cpp_17/002_type_traits_is_invocable_r.cpp @@ -0,0 +1,18 @@ +#include + +auto func2(char) -> int (*)() +{ + return nullptr; +} + +int main() +{ + static_assert( std::is_invocable_v ); + static_assert( not std::is_invocable_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); +} diff --git a/cpp_17/002_type_traits_is_nothrow_invocable.cpp b/cpp_17/002_type_traits_is_nothrow_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81ea0fe36679fcb9de33dec3e8e75cff2a801f23 --- /dev/null +++ b/cpp_17/002_type_traits_is_nothrow_invocable.cpp @@ -0,0 +1,18 @@ +#include + +auto func2(char) -> int (*)() +{ + return nullptr; +} + +int main() +{ + static_assert( std::is_invocable_v ); + static_assert( not std::is_invocable_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); +} diff --git a/cpp_17/002_type_traits_is_nothrow_invocable_r.cpp b/cpp_17/002_type_traits_is_nothrow_invocable_r.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81ea0fe36679fcb9de33dec3e8e75cff2a801f23 --- /dev/null +++ b/cpp_17/002_type_traits_is_nothrow_invocable_r.cpp @@ -0,0 +1,18 @@ +#include + +auto func2(char) -> int (*)() +{ + return nullptr; +} + +int main() +{ + static_assert( std::is_invocable_v ); + static_assert( not std::is_invocable_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); +} diff --git a/cpp_17/002_type_traits_is_nothrow_swappable.cpp b/cpp_17/002_type_traits_is_nothrow_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b3c39455e841f3a1e58af35be21a45edbbc87a3 --- /dev/null +++ b/cpp_17/002_type_traits_is_nothrow_swappable.cpp @@ -0,0 +1,39 @@ +#include +#include + +struct A +{ + A(const A&); +}; + +struct B +{ +}; + +void swap(B&, B&); + +struct C +{ +}; + +void swap(C&, C&) noexcept; + +struct D +{ + D(const D&) noexcept; + D& operator=(const D&) noexcept; +}; + +int main() +{ + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable
::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; +} diff --git a/cpp_17/002_type_traits_is_nothrow_swappable_with.cpp b/cpp_17/002_type_traits_is_nothrow_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b3c39455e841f3a1e58af35be21a45edbbc87a3 --- /dev/null +++ b/cpp_17/002_type_traits_is_nothrow_swappable_with.cpp @@ -0,0 +1,39 @@ +#include +#include + +struct A +{ + A(const A&); +}; + +struct B +{ +}; + +void swap(B&, B&); + +struct C +{ +}; + +void swap(C&, C&) noexcept; + +struct D +{ + D(const D&) noexcept; + D& operator=(const D&) noexcept; +}; + +int main() +{ + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; + std::cout << "std::__is_nothrow_swappable::value = " + << std::__is_nothrow_swappable::value << '\n'; +} diff --git a/cpp_17/002_type_traits_is_swappable.cpp b/cpp_17/002_type_traits_is_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_17/002_type_traits_is_swappable.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_17/002_type_traits_is_swappable_with.cpp b/cpp_17/002_type_traits_is_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1bb76c6549ea6f28e8ad466f477f127940512ab --- /dev/null +++ b/cpp_17/002_type_traits_is_swappable_with.cpp @@ -0,0 +1,16 @@ +#include + +template +requires std::swappable_with +void mySwap(T& t, U& u) +{ + T temp = t; t = u; u = temp; +} + +int main() +{ + int x, y; + mySwap(x, y); + + return 0; +} diff --git a/cpp_17/002_type_traits_ndisjunctionegation.cpp b/cpp_17/002_type_traits_ndisjunctionegation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/002_type_traits_ndisjunctionegation.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/002_type_traits_void_t.cpp b/cpp_17/002_type_traits_void_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d05197cb8828dde4c1e8b0387af42e1f1e17b508 --- /dev/null +++ b/cpp_17/002_type_traits_void_t.cpp @@ -0,0 +1,22 @@ +struct NoInnerType +{ + int m_i; +}; + +struct HaveInnerType +{ + using type = int; //类型别名 + void myfunc() {} +}; + + +//泛化版本 +template > +struct HasTypeMem : std::false_type //struct 默认是public ,class默认是private继承 +{ +}; +//特化版本 +template +struct HasTypeMem > : std::true_type +{ +}; diff --git a/cpp_17/003_utility_as_const.cpp b/cpp_17/003_utility_as_const.cpp new file mode 100644 index 0000000000000000000000000000000000000000..010f11106160b348df9b6824ad4f9db6819a2da7 --- /dev/null +++ b/cpp_17/003_utility_as_const.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +int main() +{ + std::string mutableString = "Hello World!"; + auto&& constRef = std::as_const(mutableString); + +// mutableString.clear(); // OK +// constRef.clear(); // error: 'constRef' is 'const' qualified, + // but 'clear' is not marked const + + assert( &constRef == &mutableString ); + assert( &std::as_const( mutableString ) == &mutableString ); + + using ExprType = std::remove_reference_t; + + static_assert(std::is_same_v, std::string>, + "ExprType should be some kind of string." ); + static_assert(!std::is_same_v, + "ExprType shouldn't be a mutable string." ); +} diff --git a/cpp_17/003_utility_in_place.cpp b/cpp_17/003_utility_in_place.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d41d0a84e20299667fec9dab6003ac82aced9bbe --- /dev/null +++ b/cpp_17/003_utility_in_place.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +// optional can be used as the return type of a factory that may fail +std::optional create(bool b) { + if (b) + return "Godzilla"; + return {}; +} + +// std::nullopt can be used to create any (empty) std::optional +auto create2(bool b) { + return b ? std::optional{"Godzilla"} : std::nullopt; +} + +// std::reference_wrapper may be used to return a reference +auto create_ref(bool b) { + static std::string value = "Godzilla"; + return b ? std::optional>{value} + : std::nullopt; +} + +int main() +{ + std::cout << "create(false) returned " + << create(false).value_or("empty") << '\n'; + + // optional-returning factory functions are usable as conditions of while and if + if (auto str = create2(true)) { + std::cout << "create2(true) returned " << *str << '\n'; + } + + if (auto str = create_ref(true)) { + // using get() to access the reference_wrapper's value + std::cout << "create_ref(true) returned " << str->get() << '\n'; + str->get() = "Mothra"; + std::cout << "modifying it changed it to " << str->get() << '\n'; + } +} diff --git a/cpp_17/003_utility_in_place_index.cpp b/cpp_17/003_utility_in_place_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/003_utility_in_place_index.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/003_utility_in_place_index_t.cpp b/cpp_17/003_utility_in_place_index_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/003_utility_in_place_index_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/003_utility_in_place_t.cpp b/cpp_17/003_utility_in_place_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/003_utility_in_place_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/003_utility_in_place_type.cpp b/cpp_17/003_utility_in_place_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/003_utility_in_place_type.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/003_utility_in_place_type_t.cpp b/cpp_17/003_utility_in_place_type_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/003_utility_in_place_type_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/004_tuple_apply.cpp b/cpp_17/004_tuple_apply.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f410610af3d1b3b1cc57899daa7366034d3742ec --- /dev/null +++ b/cpp_17/004_tuple_apply.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +int add(int first, int second) { return first + second; } + +template +T add_generic(T first, T second) { return first + second; } + +auto add_lambda = [](auto first, auto second) { return first + second; }; + +template +std::ostream& operator<<(std::ostream& os, std::tuple const& theTuple) +{ + std::apply + ( + [&os](Ts const&... tupleArgs) + { + os << '['; + std::size_t n{0}; + ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...); + os << ']'; + }, theTuple + ); + return os; +} + +int main() +{ + // OK + std::cout << std::apply(add, std::pair(1, 2)) << '\n'; + + // Error: can't deduce the function type + // std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n'; + + // OK + std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n'; + + // advanced example + std::tuple myTuple(25, "Hello", 9.31f, 'c'); + std::cout << myTuple << '\n'; + +} diff --git a/cpp_17/004_tuple_make_from_tuple.cpp b/cpp_17/004_tuple_make_from_tuple.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce134a1941f337158dbe89ec328d1a74bb21dba0 --- /dev/null +++ b/cpp_17/004_tuple_make_from_tuple.cpp @@ -0,0 +1,14 @@ +#include +#include + +struct Foo { + Foo(int first, float second, int third) { + std::cout << first << ", " << second << ", " << third << "\n"; + } +}; + +int main() +{ + auto tuple = std::make_tuple(42, 3.14f, 0); + std::make_from_tuple(std::move(tuple)); +} diff --git a/cpp_17/005_optional_bad_optional_access.cpp b/cpp_17/005_optional_bad_optional_access.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_17/005_optional_bad_optional_access.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_17/005_optional_in_place.cpp b/cpp_17/005_optional_in_place.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_in_place_index.cpp b/cpp_17/005_optional_in_place_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place_index.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_in_place_index_t.cpp b/cpp_17/005_optional_in_place_index_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place_index_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_in_place_t.cpp b/cpp_17/005_optional_in_place_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_in_place_type.cpp b/cpp_17/005_optional_in_place_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place_type.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_in_place_type_t.cpp b/cpp_17/005_optional_in_place_type_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_17/005_optional_in_place_type_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_17/005_optional_make_optional.cpp b/cpp_17/005_optional_make_optional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c351e0f140275111c34439d9aa37e87254e23e76 --- /dev/null +++ b/cpp_17/005_optional_make_optional.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include + +int main() +{ + auto op1 = std::make_optional>({'a','b','c'}); + std::cout << "op1: "; + for (char c: op1.value()){ + std::cout << c << ","; + } + auto op2 = std::make_optional>(5, 2); + std::cout << "\nop2: "; + for (int i: *op2){ + std::cout << i << ","; + } + std::string str{"hello world"}; + auto op3 = std::make_optional(std::move(str)); + std::cout << "\nop3: " << quoted(op3.value_or("empty value")) << '\n'; + std::cout << "str: " << std::quoted(str) << '\n'; +} diff --git a/cpp_17/005_optional_nullopt.cpp b/cpp_17/005_optional_nullopt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2672929d81c6ff4e6fa23945f3ec3d8a1fc7bc75 --- /dev/null +++ b/cpp_17/005_optional_nullopt.cpp @@ -0,0 +1,10 @@ +std::vector> a; +std::optional>& test(int b) +{ + a.clear(); + a.push_back(b); + if(b) + return a; + else + return std::nullopt; +} diff --git a/cpp_17/005_optional_nullopt_t.cpp b/cpp_17/005_optional_nullopt_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3f67463debb509bc60f08167bd891600b09d10e --- /dev/null +++ b/cpp_17/005_optional_nullopt_t.cpp @@ -0,0 +1,3 @@ +struct nullopt_t { + explicit constexpr nullopt_t(int) {} +}; diff --git a/cpp_17/005_optional_optional.cpp b/cpp_17/005_optional_optional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d41d0a84e20299667fec9dab6003ac82aced9bbe --- /dev/null +++ b/cpp_17/005_optional_optional.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +// optional can be used as the return type of a factory that may fail +std::optional create(bool b) { + if (b) + return "Godzilla"; + return {}; +} + +// std::nullopt can be used to create any (empty) std::optional +auto create2(bool b) { + return b ? std::optional{"Godzilla"} : std::nullopt; +} + +// std::reference_wrapper may be used to return a reference +auto create_ref(bool b) { + static std::string value = "Godzilla"; + return b ? std::optional>{value} + : std::nullopt; +} + +int main() +{ + std::cout << "create(false) returned " + << create(false).value_or("empty") << '\n'; + + // optional-returning factory functions are usable as conditions of while and if + if (auto str = create2(true)) { + std::cout << "create2(true) returned " << *str << '\n'; + } + + if (auto str = create_ref(true)) { + // using get() to access the reference_wrapper's value + std::cout << "create_ref(true) returned " << str->get() << '\n'; + str->get() = "Mothra"; + std::cout << "modifying it changed it to " << str->get() << '\n'; + } +} diff --git a/cpp_17/005_optional_std_hash.cpp b/cpp_17/005_optional_std_hash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05645f0e55452d94c9d1dd16e2619f8fbabe9683 --- /dev/null +++ b/cpp_17/005_optional_std_hash.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +struct S { + std::string first_name; + std::string last_name; +}; +bool operator==(const S& lhs, const S& rhs) { + return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name; +} + +// custom hash can be a standalone function object: +struct MyHash +{ + std::size_t operator()(S const& s) const noexcept + { + std::size_t h1 = std::hash{}(s.first_name); + std::size_t h2 = std::hash{}(s.last_name); + return h1 ^ (h2 << 1); // or use boost::hash_combine + } +}; + +// custom specialization of std::hash can be injected in namespace std +namespace std +{ + template<> struct hash + { + std::size_t operator()(S const& s) const noexcept + { + std::size_t h1 = std::hash{}(s.first_name); + std::size_t h2 = std::hash{}(s.last_name); + return h1 ^ (h2 << 1); // or use boost::hash_combine + } + }; +} + +int main() +{ + std::string str = "Meet the new boss..."; + std::size_t str_hash = std::hash{}(str); + std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << '\n'; + + S obj = { "Hubert", "Farnsworth" }; + // using the standalone function object + std::cout << "hash(" << std::quoted(obj.first_name) << ", " + << std::quoted(obj.last_name) << ") = " + << MyHash{}(obj) << " (using MyHash)\n" << std::setw(31) << "or " + << std::hash{}(obj) << " (using injected std::hash specialization)\n"; + + // custom hash makes it possible to use custom types in unordered containers + // The example will use the injected std::hash specialization above, + // to use MyHash instead, pass it as a second template argument + std::unordered_set names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"} }; + for(auto& s: names) + std::cout << std::quoted(s.first_name) << ' ' << std::quoted(s.last_name) << '\n'; +} diff --git a/cpp_17/005_optional_std_swap.cpp b/cpp_17/005_optional_std_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09ad7b751828f80bf22633c89e5b58099e3f6882 --- /dev/null +++ b/cpp_17/005_optional_std_swap.cpp @@ -0,0 +1,15 @@ +#include +#include + +int main() +{ + int a = 5, b = 3; + + // before + std::cout << a << ' ' << b << '\n'; + + std::swap(a,b); + + // after + std::cout << a << ' ' << b << '\n'; +} diff --git a/cpp_17/006_variant_bad_variant_access.cpp b/cpp_17/006_variant_bad_variant_access.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a5eb944bd5b327c74cfec6b6b8a8ca1eebb52385 --- /dev/null +++ b/cpp_17/006_variant_bad_variant_access.cpp @@ -0,0 +1,14 @@ +#include +#include + +int main() +{ + std::variant v; + v = 12; + try { + std::get(v); + } + catch(const std::bad_variant_access& e) { + std::cout << e.what() << '\n'; + } +} diff --git a/cpp_17/006_variant_get_if.cpp b/cpp_17/006_variant_get_if.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e68c894ce8c161c910176d58f24b76b61a98ce6 --- /dev/null +++ b/cpp_17/006_variant_get_if.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() +{ + std::variant v{12}; + + if(auto pval = std::get_if(&v)) + std::cout << "variant value: " << *pval << '\n'; + else + std::cout << "failed to get value!" << '\n'; +} diff --git a/cpp_17/006_variant_holds_alternative.cpp b/cpp_17/006_variant_holds_alternative.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84657abe7bc27d7eb021b5a32ef14f582e85e9d7 --- /dev/null +++ b/cpp_17/006_variant_holds_alternative.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +int main() +{ + std::variant v = "abc"; + std::cout << std::boolalpha + << "variant holds int? " + << std::holds_alternative(v) << '\n' + << "variant holds string? " + << std::holds_alternative(v) << '\n'; +} diff --git a/cpp_17/006_variant_monostate.cpp b/cpp_17/006_variant_monostate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..226cc1258753d6780300c0edbe6d4b61d74f4e7e --- /dev/null +++ b/cpp_17/006_variant_monostate.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +struct S +{ + S(int i) : i(i) {} + int i; +}; + +int main() { + + // Without the monostate type this declaration will fail. + // This is because S is not default-constructible. + + std::variant var; + assert(var.index() == 0); + + try { + std::get(var); // throws! We need to assign a value + } + catch(const std::bad_variant_access& e) { + std::cout << e.what() << '\n'; + } + + var = 12; + + std::cout << std::get(var).i << '\n'; +} diff --git a/cpp_17/006_variant_std_get.cpp b/cpp_17/006_variant_std_get.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d375842c093d25e789f4615823282887e32849d --- /dev/null +++ b/cpp_17/006_variant_std_get.cpp @@ -0,0 +1,18 @@ +#include +#include + +int main() +{ + std::variant v{12}, w; + int i = std::get(v); + w = std::get(v); + w = std::get<0>(v); // same effect as the previous line + +// std::get(v); // error: no double in [int, float] +// std::get<3>(v); // error: valid index values are 0 and 1 + + try { + std::get(w); // w contains int, not float: will throw + } + catch (std::bad_variant_access&) {} +} diff --git a/cpp_17/006_variant_std_hash.cpp b/cpp_17/006_variant_std_hash.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05645f0e55452d94c9d1dd16e2619f8fbabe9683 --- /dev/null +++ b/cpp_17/006_variant_std_hash.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +struct S { + std::string first_name; + std::string last_name; +}; +bool operator==(const S& lhs, const S& rhs) { + return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name; +} + +// custom hash can be a standalone function object: +struct MyHash +{ + std::size_t operator()(S const& s) const noexcept + { + std::size_t h1 = std::hash{}(s.first_name); + std::size_t h2 = std::hash{}(s.last_name); + return h1 ^ (h2 << 1); // or use boost::hash_combine + } +}; + +// custom specialization of std::hash can be injected in namespace std +namespace std +{ + template<> struct hash + { + std::size_t operator()(S const& s) const noexcept + { + std::size_t h1 = std::hash{}(s.first_name); + std::size_t h2 = std::hash{}(s.last_name); + return h1 ^ (h2 << 1); // or use boost::hash_combine + } + }; +} + +int main() +{ + std::string str = "Meet the new boss..."; + std::size_t str_hash = std::hash{}(str); + std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << '\n'; + + S obj = { "Hubert", "Farnsworth" }; + // using the standalone function object + std::cout << "hash(" << std::quoted(obj.first_name) << ", " + << std::quoted(obj.last_name) << ") = " + << MyHash{}(obj) << " (using MyHash)\n" << std::setw(31) << "or " + << std::hash{}(obj) << " (using injected std::hash specialization)\n"; + + // custom hash makes it possible to use custom types in unordered containers + // The example will use the injected std::hash specialization above, + // to use MyHash instead, pass it as a second template argument + std::unordered_set names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"} }; + for(auto& s: names) + std::cout << std::quoted(s.first_name) << ' ' << std::quoted(s.last_name) << '\n'; +} diff --git a/cpp_17/006_variant_std_swap.cpp b/cpp_17/006_variant_std_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09ad7b751828f80bf22633c89e5b58099e3f6882 --- /dev/null +++ b/cpp_17/006_variant_std_swap.cpp @@ -0,0 +1,15 @@ +#include +#include + +int main() +{ + int a = 5, b = 3; + + // before + std::cout << a << ' ' << b << '\n'; + + std::swap(a,b); + + // after + std::cout << a << ' ' << b << '\n'; +} diff --git a/cpp_17/006_variant_variant.cpp b/cpp_17/006_variant_variant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7453bad41278a0180db4145e12c85ddf4d1aa643 --- /dev/null +++ b/cpp_17/006_variant_variant.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int main() +{ + std::variant v, w; + v = 42; // v contains int + int i = std::get(v); + assert(42 == i); // succeeds + w = std::get(v); + w = std::get<0>(v); // same effect as the previous line + w = v; // same effect as the previous line + +// std::get(v); // error: no double in [int, float] +// std::get<3>(v); // error: valid index values are 0 and 1 + + try { + std::get(w); // w contains int, not float: will throw + } + catch (const std::bad_variant_access& ex) { + std::cout << ex.what() << '\n'; + } + + using namespace std::literals; + + std::variant x("abc"); + // converting constructors work when unambiguous + x = "def"; // converting assignment also works when unambiguous + + std::variant y("abc"); + // casts to void const * when passed a char const * + assert(std::holds_alternative(y)); // succeeds + y = "xyz"s; + assert(std::holds_alternative(y)); // succeeds +} diff --git a/cpp_17/006_variant_variant_alternative.cpp b/cpp_17/006_variant_variant_alternative.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e799d4595a96d30e5e295b36d6d9a2dd1976cf7 --- /dev/null +++ b/cpp_17/006_variant_variant_alternative.cpp @@ -0,0 +1,16 @@ +#include +#include + +using my_variant = std::variant; +static_assert(std::is_same_v + >); +static_assert(std::is_same_v + >); +// cv-qualification on the variant type propagates to the extracted alternative type. +static_assert(std::is_same_v + >); + +int main() +{ + std::cout << "All static assertions passed.\n"; +} diff --git a/cpp_17/006_variant_variant_alternative_t.cpp b/cpp_17/006_variant_variant_alternative_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e799d4595a96d30e5e295b36d6d9a2dd1976cf7 --- /dev/null +++ b/cpp_17/006_variant_variant_alternative_t.cpp @@ -0,0 +1,16 @@ +#include +#include + +using my_variant = std::variant; +static_assert(std::is_same_v + >); +static_assert(std::is_same_v + >); +// cv-qualification on the variant type propagates to the extracted alternative type. +static_assert(std::is_same_v + >); + +int main() +{ + std::cout << "All static assertions passed.\n"; +} diff --git a/cpp_17/006_variant_variant_npos.cpp b/cpp_17/006_variant_variant_npos.cpp new file mode 100644 index 0000000000000000000000000000000000000000..691b1ad4415c4dfa13ef15f8b847c1e887de86e9 --- /dev/null +++ b/cpp_17/006_variant_variant_npos.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +struct Demon +{ + Demon(int) {} + Demon(const Demon&) { throw std::domain_error("copy ctor"); } + Demon& operator= (const Demon&) = default; +}; + +int main() +{ + std::variant var{42}; + std::cout + << std::boolalpha + << "index == npos: " << (var.index() == std::variant_npos) << '\n'; + + try { var = Demon{666}; } catch (const std::domain_error& ex) + { + std::cout + << "Exception: " << ex.what() << '\n' + << "index == npos: " << (var.index() == std::variant_npos) << '\n' + << "valueless: " << var.valueless_by_exception() << '\n'; + } +} diff --git a/cpp_17/006_variant_variant_size.cpp b/cpp_17/006_variant_variant_size.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7453bad41278a0180db4145e12c85ddf4d1aa643 --- /dev/null +++ b/cpp_17/006_variant_variant_size.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int main() +{ + std::variant v, w; + v = 42; // v contains int + int i = std::get(v); + assert(42 == i); // succeeds + w = std::get(v); + w = std::get<0>(v); // same effect as the previous line + w = v; // same effect as the previous line + +// std::get(v); // error: no double in [int, float] +// std::get<3>(v); // error: valid index values are 0 and 1 + + try { + std::get(w); // w contains int, not float: will throw + } + catch (const std::bad_variant_access& ex) { + std::cout << ex.what() << '\n'; + } + + using namespace std::literals; + + std::variant x("abc"); + // converting constructors work when unambiguous + x = "def"; // converting assignment also works when unambiguous + + std::variant y("abc"); + // casts to void const * when passed a char const * + assert(std::holds_alternative(y)); // succeeds + y = "xyz"s; + assert(std::holds_alternative(y)); // succeeds +} diff --git a/cpp_17/006_variant_variant_size_v.cpp b/cpp_17/006_variant_variant_size_v.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1c17e7bae0ba4e01fe520ff0bf4ee966e008e06 --- /dev/null +++ b/cpp_17/006_variant_variant_size_v.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +static_assert(std::variant_size_v> == 0); +static_assert(std::variant_size_v> == 1); +static_assert(std::variant_size_v> == 2); +static_assert(std::variant_size_v> == 3); +static_assert(std::variant_size_v> == 3); +static_assert(std::variant_size_v> == 2); +static_assert(std::variant_size_v> == 2); +static_assert(std::variant_size_v>> == 1); + +int main() { + std::puts("All static assertions passed."); +} diff --git a/cpp_17/006_variant_visit.cpp b/cpp_17/006_variant_visit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec1528139f3e0358974650f92b94bacaea9a1a2c --- /dev/null +++ b/cpp_17/006_variant_visit.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include +#include + +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...)->overloaded; + +int main() { + std::vector> vec = { 10, 15l, 1.5, "hello" }; + for (auto& v : vec) { + std::visit(overloaded{ + [](auto arg) { std::cout << arg << ' '; }, + [](double arg) { std::cout << std::fixed << arg << ' '; }, + [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }, + }, v); + } +} diff --git a/cpp_17/007_any_any.cpp b/cpp_17/007_any_any.cpp new file mode 100644 index 0000000000000000000000000000000000000000..570c8d41a467b87ad06f4f7028b3b1136743022e --- /dev/null +++ b/cpp_17/007_any_any.cpp @@ -0,0 +1,45 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha; + + // any type + std::any a = 1; + std::cout << a.type().name() << ": " << std::any_cast(a) << '\n'; + a = 3.14; + std::cout << a.type().name() << ": " << std::any_cast(a) << '\n'; + a = true; + std::cout << a.type().name() << ": " << std::any_cast(a) << '\n'; + + // bad cast + try + { + a = 1; + std::cout << std::any_cast(a) << '\n'; + } + catch (const std::bad_any_cast& e) + { + std::cout << e.what() << '\n'; + } + + // has value + a = 1; + if (a.has_value()) + { + std::cout << a.type().name() << '\n'; + } + + // reset + a.reset(); + if (!a.has_value()) + { + std::cout << "no value\n"; + } + + // pointer to contained data + a = 1; + int* i = std::any_cast(&a); + std::cout << *i << "\n"; +} diff --git a/cpp_17/007_any_any_cast.cpp b/cpp_17/007_any_any_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27c1b8ce57d1f481ecc2d7dd345b74229fe778a5 --- /dev/null +++ b/cpp_17/007_any_any_cast.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +int main() +{ + // simple example + + auto a = std::any(12); + + std::cout << std::any_cast(a) << '\n'; + + try { + std::cout << std::any_cast(a) << '\n'; + } + catch(const std::bad_any_cast& e) { + std::cout << e.what() << '\n'; + } + + // pointer example + + if (int* i = std::any_cast(&a)) { + std::cout << "a is int: " << *i << '\n'; + } else if (std::string* s = std::any_cast(&a)) { + std::cout << "a is std::string: " << *s << '\n'; + } else { + std::cout << "a is another type or unset\n"; + } + + // advanced example + + a = std::string("hello"); + + auto& ra = std::any_cast(a); //< reference + ra[1] = 'o'; + + std::cout << "a: " + << std::any_cast(a) << '\n'; //< const reference + + auto b = std::any_cast(std::move(a)); //< rvalue reference + + // Note: 'b' is a move-constructed std::string, + // 'a' is left in valid but unspecified state + + std::cout << "a: " << *std::any_cast(&a) //< pointer + << "b: " << b << '\n'; +} diff --git a/cpp_17/007_any_bad_any_cast.cpp b/cpp_17/007_any_bad_any_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84faced4a4074f88b6df548af5b42017940fe294 --- /dev/null +++ b/cpp_17/007_any_bad_any_cast.cpp @@ -0,0 +1,16 @@ +#include +#include + +struct Foo { virtual ~Foo() {} }; +struct Bar { virtual ~Bar() {} }; + +int main() +{ + Bar b; + try { + Foo& f = dynamic_cast(b); + } catch(const std::bad_cast& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/cpp_17/007_any_make_any.cpp b/cpp_17/007_any_make_any.cpp new file mode 100644 index 0000000000000000000000000000000000000000..673fb3c1b4e60cb424a3cb71d171a26d133dd94b --- /dev/null +++ b/cpp_17/007_any_make_any.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +int main() +{ + auto a0 = std::make_any("Hello, std::any!\n"); + auto a1 = std::make_any>(0.1, 2.3); + + std::cout << std::any_cast(a0); + std::cout << std::any_cast&>(a1) << '\n'; + + using lambda = std::function; + + // Put a lambda into std::any. Attempt #1 (failed). + std::any a2 = [] { std::cout << "Lambda #1.\n"; }; + std::cout << "a2.type() = \"" << a2.type().name() << "\"\n"; + + // any_cast casts to but actual type is not + // a std::function..., but ~ main::{lambda()#1}, and it is + // unique for each lambda. So, this throws... + try { + std::any_cast(a2)(); + } + catch (std::bad_any_cast const& ex) { + std::cout << ex.what() << '\n'; + } + + // Put a lambda into std::any. Attempt #2 (successful). + auto a3 = std::make_any([] { std::cout << "Lambda #2.\n"; }); + std::cout << "a3.type() = \"" << a3.type().name() << "\"\n"; + std::any_cast(a3)(); +} diff --git a/cpp_17/007_any_std_swap.cpp b/cpp_17/007_any_std_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09ad7b751828f80bf22633c89e5b58099e3f6882 --- /dev/null +++ b/cpp_17/007_any_std_swap.cpp @@ -0,0 +1,15 @@ +#include +#include + +int main() +{ + int a = 5, b = 3; + + // before + std::cout << a << ' ' << b << '\n'; + + std::swap(a,b); + + // after + std::cout << a << ' ' << b << '\n'; +} diff --git a/cpp_17/008_charconv_chars_format.cpp b/cpp_17/008_charconv_chars_format.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6d5d419297dac3c5a9cb9f03e01706d4179843d --- /dev/null +++ b/cpp_17/008_charconv_chars_format.cpp @@ -0,0 +1,6 @@ +enum class chars_format { + scientific = /*unspecified*/, + fixed = /*unspecified*/, + hex = /*unspecified*/, + general = fixed | scientific +}; diff --git a/cpp_17/008_charconv_from_chars.cpp b/cpp_17/008_charconv_from_chars.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ed05e48e577499c5b8fb25eb8ed14d0495195ec --- /dev/null +++ b/cpp_17/008_charconv_from_chars.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +int main() +{ + std::array str{"42 xyz "}; + int result; + if(auto [p, ec] = std::from_chars(str.data(), str.data()+str.size(), result); + ec == std::errc()) + std::cout << result << "\n" "p -> \"" << p << "\"\n"; + + std::string_view sv{"24 abc "}; + if(auto [p, ec] = std::from_chars(data(sv), data(sv)+size(sv), result); + ec == std::errc()) + std::cout << result << "\n" "p -> \"" << p << "\"\n"; +} diff --git a/cpp_17/008_charconv_to_chars.cpp b/cpp_17/008_charconv_to_chars.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eafb98c3e6ff77276d392ead7b65a0885f4a1bdc --- /dev/null +++ b/cpp_17/008_charconv_to_chars.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include + +int main() +{ + std::array str; + + if(auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), 42); + ec == std::errc()) + std::cout << std::string_view + (str.data(), ptr); // C++20, uses string_view(first, last) + // (str.data(), ptr - str.data()); // C++17, uses string_view(ptr, length) +} diff --git a/cpp_17/009_initializer_list_data.cpp b/cpp_17/009_initializer_list_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f73c59fea85085e8fa3635f16dd67509cff5b8f6 --- /dev/null +++ b/cpp_17/009_initializer_list_data.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::string s {"Hello world!\n"}; + + char a[20]; // storage for a C-style string + std::strcpy(a, std::data(s)); + // [s.data(), s.data() + s.size()] is guaranteed to be an NTBS since C++11 + + std::cout << a; +} diff --git a/cpp_17/009_initializer_list_empty.cpp b/cpp_17/009_initializer_list_empty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be5b2ded512a2879cfbd5bfb3f423bf24cccc215 --- /dev/null +++ b/cpp_17/009_initializer_list_empty.cpp @@ -0,0 +1,32 @@ +#include +#include + +template +void print(const T& container) +{ + if ( std::empty(container) ) + { + std::cout << "Empty\n"; + } + else + { + std::cout << "Elements:"; + for ( const auto& element : container ) + std::cout << ' ' << element; + std::cout << '\n'; + } +} + +int main() +{ + std::vector c = { 1, 2, 3 }; + print(c); + c.clear(); + print(c); + + int array[] = { 4, 5, 6 }; + print(array); + + auto il = { 7, 8, 9 }; + print(il); +} diff --git a/cpp_17/010_map_extract.cpp b/cpp_17/010_map_extract.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfaeab7bf7ed1d3d431b2e8612b5742dccdf21e5 --- /dev/null +++ b/cpp_17/010_map_extract.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +#include +int main() +{ + using namespace std::literals; + std::unordered_map m; + + m.try_emplace("a", "a"s); + m.try_emplace("b", "abcd"); + m.try_emplace("c", 10, 'c'); + m.try_emplace("c", "Won't be inserted"); + + for (const auto &p : m) { + std::cout << p.first << " => " << p.second << '\n'; + } +} diff --git a/cpp_17/010_map_insert_or_assign.cpp b/cpp_17/010_map_insert_or_assign.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0eaeb5c35f2f408cf8e3ba4655364bbb5e653226 --- /dev/null +++ b/cpp_17/010_map_insert_or_assign.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +auto print_node = [](const auto &node) { + std::cout << "[" << node.first << "] = " << node.second << '\n'; +}; + +auto print_result = [](auto const &pair) { + std::cout << (pair.second ? "inserted: " : "assigned: "); + print_node(*pair.first); +}; + +int main() +{ + std::map myMap; + + print_result( myMap.insert_or_assign("a", "apple" ) ); + print_result( myMap.insert_or_assign("b", "banana" ) ); + print_result( myMap.insert_or_assign("c", "cherry" ) ); + print_result( myMap.insert_or_assign("c", "clementine") ); + + for (const auto &node : myMap) { print_node(node); } +} diff --git a/cpp_17/010_map_merge.cpp b/cpp_17/010_map_merge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f3865f1486ca3687dedc37169fa673b125f874b --- /dev/null +++ b/cpp_17/010_map_merge.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +int main() +{ + std::map ma {{1, "apple"}, {5, "pear"}, {10, "banana"}}; + std::map mb {{2, "zorro"}, {4, "batman"}, {5, "X"}, {8, "alpaca"}}; + std::map u; + u.merge(ma); + std::cout << "ma.size(): " << ma.size() << '\n'; + u.merge(mb); + std::cout << "mb.size(): " << mb.size() << '\n'; + std::cout << "mb.at(5): " << mb.at(5) << '\n'; + for(auto const &kv: u) + std::cout << kv.first << ", " << kv.second << '\n'; +} diff --git a/cpp_17/010_map_try_emplace.cpp b/cpp_17/010_map_try_emplace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5d904b818c8299d06bf752bb44696debc297af7 --- /dev/null +++ b/cpp_17/010_map_try_emplace.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +#include +int main() +{ + using namespace std::literals; + std::map m; + + m.try_emplace("a", "a"s); + m.try_emplace("b", "abcd"); + m.try_emplace("c", 10, 'c'); + m.try_emplace("c", "Won't be inserted"); + + for (const auto &p : m) { + std::cout << p.first << " => " << p.second << '\n'; + } +} diff --git a/cpp_17/011_unordered_map_extract.cpp b/cpp_17/011_unordered_map_extract.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f9a9716a70b8648e558a4d091e1830821f6d455 --- /dev/null +++ b/cpp_17/011_unordered_map_extract.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +int main() +{ + std::unordered_map cont{{1, 'a'}, {2, 'b'}, {3, 'c'}}; + + auto print = [](std::pair& n) { + std::cout << " " << n.first << '(' << n.second << ')'; + }; + + std::cout << "Start:"; + std::for_each(cont.begin(), cont.end(), print); + std::cout << '\n'; + + // Extract node handle and change key + auto nh = cont.extract(1); + nh.key() = 4; + + std::cout << "After extract and before insert:"; + std::for_each(cont.begin(), cont.end(), print); + std::cout << '\n'; + + // Insert node handle back + cont.insert(move(nh)); + + std::cout << "End:"; + std::for_each(cont.begin(), cont.end(), print); + std::cout << '\n'; +} diff --git a/cpp_17/011_unordered_map_insert_or_assign.cpp b/cpp_17/011_unordered_map_insert_or_assign.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60c14fab018f2653994ccad70433a04b239ddc16 --- /dev/null +++ b/cpp_17/011_unordered_map_insert_or_assign.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +auto print_node = [](const auto &node) { + std::cout << "[" << node.first << "] = " << node.second << '\n'; +}; + +auto print_result = [](auto const &pair) { + std::cout << (pair.second ? "inserted: " : "assigned: "); + print_node(*pair.first); +}; + +int main() +{ + std::unordered_map myMap; + + print_result( myMap.insert_or_assign("a", "apple" ) ); + print_result( myMap.insert_or_assign("b", "banana" ) ); + print_result( myMap.insert_or_assign("c", "cherry" ) ); + print_result( myMap.insert_or_assign("c", "clementine") ); + + for (const auto &node : myMap) { print_node(node); } +} diff --git a/cpp_17/011_unordered_map_merge.cpp b/cpp_17/011_unordered_map_merge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..beeff9e87a7bbfbecd57e3801ca1ca6c52c98d6f --- /dev/null +++ b/cpp_17/011_unordered_map_merge.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +// print out a std::pair +template +Os& operator<<(Os& os, const std::pair& p) { + return os << '{' << p.first << ", " << p.second << '}'; +} + +// print out an associative container +template +Os& operator<<(Os& os, const std::unordered_map& v) { + os << '[' << v.size() << "] { "; + bool o{}; + for (const auto& e : v) + os << (o ? ", " : (o = 1, "")) << e; + return os << " }\n"; +} + +int main() +{ + std::unordered_map + p{ {"C", 3}, {"B", 2}, {"A", 1}, {"A", 0} }, + q{ {"E", 6}, {"E", 7}, {"D", 5}, {"A", 4} }; + + std::cout << "p: " << p << "q: " << q; + + p.merge(q); + + std::cout << "p.merge(q);\n" << "p: " << p << "q: " << q; +} diff --git a/cpp_17/011_unordered_map_try_emplace.cpp b/cpp_17/011_unordered_map_try_emplace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfaeab7bf7ed1d3d431b2e8612b5742dccdf21e5 --- /dev/null +++ b/cpp_17/011_unordered_map_try_emplace.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +#include +int main() +{ + using namespace std::literals; + std::unordered_map m; + + m.try_emplace("a", "a"s); + m.try_emplace("b", "abcd"); + m.try_emplace("c", 10, 'c'); + m.try_emplace("c", "Won't be inserted"); + + for (const auto &p : m) { + std::cout << p.first << " => " << p.second << '\n'; + } +} diff --git a/cpp_17/012_stl_data.cpp b/cpp_17/012_stl_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f73c59fea85085e8fa3635f16dd67509cff5b8f6 --- /dev/null +++ b/cpp_17/012_stl_data.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::string s {"Hello world!\n"}; + + char a[20]; // storage for a C-style string + std::strcpy(a, std::data(s)); + // [s.data(), s.data() + s.size()] is guaranteed to be an NTBS since C++11 + + std::cout << a; +} diff --git a/cpp_17/012_stl_empty.cpp b/cpp_17/012_stl_empty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be5b2ded512a2879cfbd5bfb3f423bf24cccc215 --- /dev/null +++ b/cpp_17/012_stl_empty.cpp @@ -0,0 +1,32 @@ +#include +#include + +template +void print(const T& container) +{ + if ( std::empty(container) ) + { + std::cout << "Empty\n"; + } + else + { + std::cout << "Elements:"; + for ( const auto& element : container ) + std::cout << ' ' << element; + std::cout << '\n'; + } +} + +int main() +{ + std::vector c = { 1, 2, 3 }; + print(c); + c.clear(); + print(c); + + int array[] = { 4, 5, 6 }; + print(array); + + auto il = { 7, 8, 9 }; + print(il); +} diff --git a/cpp_17/012_stl_size.cpp b/cpp_17/012_stl_size.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d65ccbf7c6201645f38b5c181756ab8482ce1b47 --- /dev/null +++ b/cpp_17/012_stl_size.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + std::cout << std::size(v) << '\n'; + + int a[] = { -5, 10, 15 }; + std::cout << std::size(a) << '\n'; + + // since C++20 the signed size (ssize) can avail + auto i = std::ssize(v); + for (--i; i != -1; --i) { + std::cout << v[i] << (i ? ' ' : '\n'); + } + std::cout << "i = " << i << '\n'; +} diff --git a/cpp_17/README.md b/cpp_17/README.md new file mode 100644 index 0000000000000000000000000000000000000000..598bd99366b003747613e10250683133b798f215 --- /dev/null +++ b/cpp_17/README.md @@ -0,0 +1,4 @@ +##

C++17新特性

+ +![C++17思维导图](https://www.0voice.com/uiwebsite/cpp_new_features/C++17_new_features.png) +----------- diff --git a/cpp_20/001_keywords_README.md b/cpp_20/001_keywords_README.md new file mode 100644 index 0000000000000000000000000000000000000000..29db4304b2ebd807e1466934af2dbebb4c4601cb --- /dev/null +++ b/cpp_20/001_keywords_README.md @@ -0,0 +1,363 @@ +####

C++20新增关键字

+ +#####

char8_t

+ +char8_t - UTF-8 字符表示的类型,要求大到足以表示任何 UTF-8 编码单元( 8 位)。它与 unsigned char 具有相同的大小、符号性和对齐(从而与 char 和 signed char 具有相同的大小和对齐),但它是独立的类型。 + +#####

concept

+ +C++20引进了概念(Concepts)这一新特性。 +概念是指给一组要求(Requirements)所起的名字。概念是一种具名谓词。 +使用这些要求和概念可以给函数和类模板的参数加上约束(Constraints)。 + +######
引入概念的目的
+ +* 约束成为模板界面的一部分 +* 基于概念的重载成为可能 +* 模板的出错信息更加友好 +* 没有约束的 auto 和有约束的概念得到统一 + +######
约束的种类
+ +约束有三种类型 +* 合取(conjunction) + 约束的合取:使用 && 运算符 +* 析取(disjunction) + 约束的析取:使用 || 运算符 +* 原子约束(atomic constraint) + +######
Requires 子句
+ +Requires 子句由关键字 requires 后加上常量表达式构成,用于指定约束。 + +######
Requires 表达式
+ +Requires 表达式是 bool 类型的右值表达式,用于表达约束。其形式为 +requires ( 形参列表(可选) ) { 要求序列 } + +要求序列中的要求有以下四种形式: + +* 简单要求(simple requirement) + * 简单要求是任意表达式语句。 +* 类型要求(type requirement) + * 类型要求是关键字 typename 加一个类型名。 +* 复合要求(compound requirement) + * { 表达式 } noexcept(可选) 返回类型要求(可选) ; + * 返回类型要求 - -> 类型约束 +* 嵌套要求(nested requirement) + * reuires 约束表达式 ; + +######
Concepts的定义
+```C++ +template < template-parameter-list > +concept concept-name = constraint-expression; +``` +其中,constraint-expression是一个可以被eval为bool的表达式或者编译期函数。 在使用定义好的concept时,constraint-expression会根据上面template-parameter-list传入的类型,执行编译期计算,判断使用该concept的模板定义是否满足。 如果不满足,则编译期会给定一个具有明确语义的错误,即 这个concept没有匹配成功啦啦这种。 注意到,上述匹配的行为都是在编译期完成的,因此concept其实是zero-cost的。 举个例子来描述一下,最基本的concept的定义。 +```C++ +// 一个永远都能匹配成功的concept +template +concept always_satisfied = true; + +// 一个约束T只能是整数类型的concept,整数类型包括 char, unsigned char, short, ushort, int, unsinged int, long等。 +template +concept integral = std::is_integral_v; + +// 一个约束T只能是整数类型,并且是有符号的concept +template +concept signed_integral = integral && std::is_signed_v; +``` +接下来,我们再简单示例一下如何使用一个concept +```C++ +// 任意类型都能匹配成功的约束,因此mul只要支持乘法运算符的类型都可以匹配成功。 +template +T mul(T a, T b) { + return a * b; +} + +// 整型才能匹配add函数的T +template +T add(T a, T b) { + return a + b; +} + +// 有符号整型才能匹配subtract函数的T +template +T subtract(T a, T b) { + return a - b; +} + +int main() { + mul(1, 2); // 匹配成功, T => int + mul(1.0f, 2.0f); // 匹配成功,T => float + + add(1, -2); // 匹配成功, T => int + add(1.0f, 2.0f); // 匹配失败, T => float,而T必须是整型 + subtract(1U, 2U); // 匹配失败,T => unsigned int,而T必须是有符号整型 + subtract(1, 2); // 匹配成功, T => int +} +``` + +######
Concept的使用方法
+与auto关键字的一些结合方式 +```C++ +// 约束函数模板方法1 +template +void f(T v); + +// 约束函数模板方法2 +template +requires my_concept +void f(T v); + +// 约束函数模板方法3 +template +void f(T v) requires my_concept; + +// 直接约束C++14的auto的函数参数 +void f(my_concept auto v); + +// 约束模板的auto参数 +template +void g(); + +// 约束auto变量 +my_concept auto foo = ...; +``` + +Concept当然也可以用在lambda函数上,使用方法跟上面一样 +```C++ +// 约束lambda函数的方法1 +auto f = [] (T v) { + // ... +}; +// 约束lambda函数的方法2 +auto f = [] requires my_concept (T v) { + // ... +}; +// 约束lambda函数的方法3 +auto f = [] (T v) requires my_concept { + // ... +}; +// auto函数参数约束 +auto f = [](my_concept auto v) { + // ... +}; +// auto模板参数约束 +auto g = [] () { + // ... +}; +``` +######
concept的组合(与或非)
+concept的本质是一个模板的编译期的bool变量,因此它可以使用C++的与或非三个操作符。例如,我们可以在定义concept的时候,使用其他concept或者表达式,进行逻辑操作。 +```C++ +template +concept Integral = std::is_integral::value; +template +concept SignedIntegral = Integral && std::is_signed::value; +template +concept UnsignedIntegral = Integral && !SignedIntegral; +``` +当然,我们也可以在使用concept的时候使用 逻辑操作符。 +```C++ +template +requires Integral && std::is_signed_v +T add(T a, T b); +``` + +#####

requires

+ +######
requires关键字的其他用法
+ +requires关键字不仅能用在concept的使用上,也可以用在定义中。 例如 +```C++ +// requires用在使用concept时 +template + requires my_concept +void f(T); + +// requires用在concept的定义,它表达了类型T的参数f,必须符合大括号内的模式,也就是能被调用。 +// 也就是它是一个函数或者一个重载了operator()的类型 +template +concept callable = requires (T f) { f(); }; + +template + requires requires (T x) { x + x; } // `requires` 同时使用在concept的定义和使用上 +T add(T a, T b) { + return a + b; +} +`` +requires的语法理解:requires后接的东西本质上是一个表达式 +```C++ +// requires后面接的是一个正在被eval的concept,用在上面的concept的使用中。 +requires evaled-concept + +// 本质上,concept在evaluate时,是一个编译期返回结果为bool的表达式。这种其实等价于上面那种。 +requires expression + +// 例如 下面这种就是requires后直接接个bool表达式了 +template +requires std::is_integral_v +T add(T a, T b) { + return a + b; +} +``` + +######
使用requires关键字进行约束嵌套或组合
+ +为了提高concept定义的能力,requires支持用大括号的语法,进行多个约束分开表达,这些约束之间的关系是与的关系。 + +requires的这种方式的语法形式是 +```C++ +requires { requirement-seq } +requires ( parameter-list(optional) ) { requirement-seq } +``` +这里每个requirement-seq是可以由多行约束组成,每一行之间以分号分隔。 这些约束的形式有以下几种 + +* 简单约束(Simple Requirements) +* 类型约束(Type Requirements) +* 复合约束(Compound Requirements) +* 嵌套约束(Nested Requirements) + +1) 简单约束 +简单约束就是一个任意的表达式,编译器对这个约束的检查就是检查这个表达式是否是合法的。注意,不是说这个表达式在编译期运行返回true或者false。而是这个表达式是否合法。 例如 +```C++ +template +concept Addable = +requires (T a, T b) { + a + b; // "the expression a+b is a valid expression that will compile" +}; + +// example constraint from the standard library (ranges TS) +template +concept Swappable = requires(T&& t, U&& u) { + swap(std::forward(t), std::forward(u)); + swap(std::forward(u), std::forward(t)); +}; +``` +2) 类型约束 +类型的约束是类似模板里面的参数一样,在typename后接一个类型。这个约束表达的含义是该类型在该concept进行evaluate时,必须是存在的。 如下面的例子: +```C++ +struct foo { + int foo; +}; + +struct bar { + using value = int; + value data; +}; + +struct baz { + using value = int; + value data; +}; + +// Using SFINAE, enable if `T` is a `baz`. +template >> +struct S {}; + +template +using Ref = T&; + +template +concept C = requires { + // Requirements on type `T`: + typename T::value; // A) has an inner member named `value` + typename S; // B) must have a valid class template specialization for `S` + typename Ref; // C) must be a valid alias template substitution +}; + +template +void g(T a); + +g(foo{}); // ERROR: Fails requirement A. +g(bar{}); // ERROR: Fails requirement B. +g(baz{}); // PASS. +``` + +3) 复合约束 +复合约束用于约束表达式的返回值的类型。它的写法形式为: +```C++ +// 这里 ->和type-constraint是可选的. +{expression} noexcept(optional) -> type-constraint; +``` + +这里的约束的行为主要有三点,并且约束进行evaluate的顺序按照以下顺序 + +* 模板类型代换到表达式中是否使得表达式合法 +* 如果用了noexcept,表达式必须不能可能抛出异常. +* 如果用了->后的类型约束, 则按照以下步骤进行evaluate +* 代换模板类型到 type-constraint中, +* 并且 decltype((expression))的类型必须满足type-constraint的约束. + +上述步骤任何一个失败,则evaluate的结果是false. + +```C++ +template +concept C = requires(T x) { + {*x} -> typename T::inner; // the type of the expression `*x` is convertible to `T::inner` + {x + 1} -> std::same_as; // the expression `x + 1` satisfies `std::same_as` + {x * 1} -> T; // the type of the expression `x * 1` is convertible to `T` +}; +``` + +4) 嵌套约束 + +requires内部还可以嵌套requires. 这种方式被称为嵌套的约束.它的形式为 +```C++ +requires constraint-expression ; +``` +例如 +```C++ +template +concept Semiregular = DefaultConstructible && + CopyConstructible && Destructible && CopyAssignable && +requires(T a, size_t n) { + requires Same; // nested: "Same<...> evaluates to true" + { a.~T() } noexcept; // compound: "a.~T()" is a valid expression that doesn't throw + requires Same; // nested: "Same<...> evaluates to true" + requires Same; // nested + { delete new T }; // compound + { delete new T[n] }; // compound +}; +``` + + +#####

consteval

+ +consteval关键字,用来修饰函数时常量值的表达式,而且是强制性的。如果函数本身不是常量值的表达式的话则会编译失败。 +constexpr修饰函数时其实只是告诉编译器该函数可以按常量值的表达式去优化,但是如果函数本身不是常量值的表达式的话依然能够编译通过。 +```C++ + +constexpr int add100_constexpr(int n) { + return n + 100; +} + +consteval int add100_consteval(int n) { + return n + 100; +} + +void test() { + constexpr int c_constexpr = add100_consteval(200); + int x = 200; + // int d_consteval = add100_consteval(x); // 编译失败 + int d_constexpr = add100_constexpr(x); //编译成功,constexpr并非强制限定为常量表达式 +} +``` + +#####

co_await

+ +co_await可以挂起和恢复函数的执行。 + +#####

co_yield

+ +co_yield可以在不结束协程的情况下从协程返回一些值。因此,可以用它来编写无终止条件的生成器函数。 + +#####

co_return

+ +co_return允许从协程返回一些值,需要自行定制。 + +####

C++20含义变化或者新增含义关键字

+ +#####
export
+ +C++20不使用并保留该关键词。 diff --git a/cpp_20/002_rtti_basic_common_reference.cpp b/cpp_20/002_rtti_basic_common_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/002_rtti_basic_common_reference.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/002_rtti_common_reference.cpp b/cpp_20/002_rtti_common_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f574d722960d61deaf10fe7c036d3069c55c179 --- /dev/null +++ b/cpp_20/002_rtti_common_reference.cpp @@ -0,0 +1,2 @@ +using CR = std::iter_common_reference_t>; +std::ranges::sort(r, std::less{}); diff --git a/cpp_20/002_rtti_is_bounded_array.cpp b/cpp_20/002_rtti_is_bounded_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ab095663d6dd91c6be788fe02707117da3b14d0 --- /dev/null +++ b/cpp_20/002_rtti_is_bounded_array.cpp @@ -0,0 +1,16 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_bounded_array_v
<< '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; + std::cout << std::is_bounded_array_v << '\n'; +} diff --git a/cpp_20/002_rtti_is_constant_evaluated.cpp b/cpp_20/002_rtti_is_constant_evaluated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ae52ea4f176fa487e9174104087f66c9c47b3c3 --- /dev/null +++ b/cpp_20/002_rtti_is_constant_evaluated.cpp @@ -0,0 +1,14 @@ +#include + +int foo(int arg) { + if (std::is_constant_evaluated()) { + return 1; + } else { + return 0; + } +} + +int main() { + const auto b = foo(0); + return b; +} diff --git a/cpp_20/002_rtti_is_corresponding_member.cpp b/cpp_20/002_rtti_is_corresponding_member.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca792f0e9fc7423c1d2317089095c6be1d82a274 --- /dev/null +++ b/cpp_20/002_rtti_is_corresponding_member.cpp @@ -0,0 +1,17 @@ +#include +#include + +struct Foo { int x; }; +struct Bar { int y; double z; }; + +struct Baz : Foo, Bar {}; // not standard-layout + +int main() +{ + std::cout << std::boolalpha + << std::is_same_v << '\n' + << std::is_same_v << '\n' + << std::is_corresponding_member(&Foo::x, &Bar::y) << '\n' + << std::is_corresponding_member(&Baz::x, &Baz::y) << '\n' + << std::is_corresponding_member(&Baz::x, &Baz::y) << '\n'; +} diff --git a/cpp_20/002_rtti_is_layout_compatible.cpp b/cpp_20/002_rtti_is_layout_compatible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42172ffa0d2acd53f396b85f51286574867782bd --- /dev/null +++ b/cpp_20/002_rtti_is_layout_compatible.cpp @@ -0,0 +1,28 @@ +#include +#include + +struct Foo { + int x; + char y; +}; + +class Bar { + const int u = 42; + volatile char v = '*'; +}; + +enum E0 : int {}; +enum class E1 : int {}; + +int main() +{ + std::cout << std::boolalpha + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n' + << std::is_layout_compatible_v << '\n'; +} diff --git a/cpp_20/002_rtti_is_nothrow_convertible.cpp b/cpp_20/002_rtti_is_nothrow_convertible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0385150fc4e912593af5265a1b451bfaf9eb3d0a --- /dev/null +++ b/cpp_20/002_rtti_is_nothrow_convertible.cpp @@ -0,0 +1,31 @@ +#include +#include + +class E { public: template E(T&&) { } }; + +int main() +{ + class A {}; + class B : public A {}; + class C {}; + class D { public: operator C() { return c; } C c; }; + + + bool b2a = std::is_convertible::value; + bool a2b = std::is_convertible::value; + bool b2c = std::is_convertible::value; + bool d2c = std::is_convertible::value; + + // A Perfect Forwarding constructor make the class 'convert' from everything + + bool everything2e = std::is_convertible::value; //< B, C, D, etc + + std::cout << std::boolalpha; + + std::cout << b2a << '\n'; + std::cout << a2b << '\n'; + std::cout << b2c << '\n'; + std::cout << d2c << '\n'; + std::cout << '\n'; + std::cout << everything2e << '\n'; +} diff --git a/cpp_20/002_rtti_is_pointer_interconvertible_base_of.cpp b/cpp_20/002_rtti_is_pointer_interconvertible_base_of.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0385150fc4e912593af5265a1b451bfaf9eb3d0a --- /dev/null +++ b/cpp_20/002_rtti_is_pointer_interconvertible_base_of.cpp @@ -0,0 +1,31 @@ +#include +#include + +class E { public: template E(T&&) { } }; + +int main() +{ + class A {}; + class B : public A {}; + class C {}; + class D { public: operator C() { return c; } C c; }; + + + bool b2a = std::is_convertible::value; + bool a2b = std::is_convertible::value; + bool b2c = std::is_convertible::value; + bool d2c = std::is_convertible::value; + + // A Perfect Forwarding constructor make the class 'convert' from everything + + bool everything2e = std::is_convertible::value; //< B, C, D, etc + + std::cout << std::boolalpha; + + std::cout << b2a << '\n'; + std::cout << a2b << '\n'; + std::cout << b2c << '\n'; + std::cout << d2c << '\n'; + std::cout << '\n'; + std::cout << everything2e << '\n'; +} diff --git a/cpp_20/002_rtti_is_pointer_interconvertible_with_class.cpp b/cpp_20/002_rtti_is_pointer_interconvertible_with_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bd4fc86baa0639317280760cacf94b40ad821bb --- /dev/null +++ b/cpp_20/002_rtti_is_pointer_interconvertible_with_class.cpp @@ -0,0 +1,30 @@ +#include +#include + +struct A { + int m; +}; + +struct B { + B(B const&) {} +}; + +struct C { + virtual void foo(); +}; + +struct D { + int m; + + D(D const&) = default; // -> trivially copyable + D(int x): m(x+1) {} +}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_trivially_copyable::value << '\n'; + std::cout << std::is_trivially_copyable::value << '\n'; + std::cout << std::is_trivially_copyable::value << '\n'; + std::cout << std::is_trivially_copyable::value << '\n'; +} diff --git a/cpp_20/002_rtti_is_unbounded_array.cpp b/cpp_20/002_rtti_is_unbounded_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6499f0a4b10afe2d44771eaab8c619559d985855 --- /dev/null +++ b/cpp_20/002_rtti_is_unbounded_array.cpp @@ -0,0 +1,16 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; + std::cout << std::is_unbounded_array_v << '\n'; +} diff --git a/cpp_20/002_rtti_remove_cvref.cpp b/cpp_20/002_rtti_remove_cvref.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40e211c84f0206792a5f40b2b7f5a9f92a380cf8 --- /dev/null +++ b/cpp_20/002_rtti_remove_cvref.cpp @@ -0,0 +1,14 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha + << std::is_same_v, int> << '\n' + << std::is_same_v, int> << '\n' + << std::is_same_v, int> << '\n' + << std::is_same_v, int> << '\n' + << std::is_same_v, int[2]> << '\n' + << std::is_same_v, int[2]> << '\n' + << std::is_same_v, int(int)> << '\n'; +} diff --git a/cpp_20/002_rtti_type_identity.cpp b/cpp_20/002_rtti_type_identity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e522262f730c780b745be6c39dab28fd551fdbcd --- /dev/null +++ b/cpp_20/002_rtti_type_identity.cpp @@ -0,0 +1,17 @@ +#include +#include + +template +T foo(T a, T b) { + return a + b; +} + +template +T bar(T a, std::type_identity_t b) { + return a + b; +} + +int main() { + // foo(4.2, 1); // error, deduced conflicting types for 'T' + std::cout << bar(4.2, 1) << '\n'; // OK, calls bar +} diff --git a/cpp_20/003_rtti_coroutine_handle.cpp b/cpp_20/003_rtti_coroutine_handle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a66b31da09c1795d96f0ae887c4615744ed90a17 --- /dev/null +++ b/cpp_20/003_rtti_coroutine_handle.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/003_rtti_coroutine_traits.cpp b/cpp_20/003_rtti_coroutine_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5a1bb8b668a7592b2d6906b604509041a73095a --- /dev/null +++ b/cpp_20/003_rtti_coroutine_traits.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +auto switch_to_new_thread(std::jthread& out) { + struct awaitable { + std::jthread* p_out; + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<> h) { + std::jthread& out = *p_out; + if (out.joinable()) + throw std::runtime_error("Output jthread parameter not empty"); + out = std::jthread([h] { h.resume(); }); + // Potential undefined behavior: accessing potentially destroyed *this + // std::cout << "New thread ID: " << p_out->get_id() << '\n'; + std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK + } + void await_resume() {} + }; + return awaitable{&out}; +} + +struct task{ + struct promise_type { + task get_return_object() { return {}; } + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; +}; + +task resuming_on_new_thread(std::jthread& out) { + std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n'; + co_await switch_to_new_thread(out); + // awaiter destroyed here + std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n'; +} + +int main() { + std::jthread out; + resuming_on_new_thread(out); +} diff --git a/cpp_20/003_rtti_noop_coroutine.cpp b/cpp_20/003_rtti_noop_coroutine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26fd6ebf7604a31ba501905865e3251dc134fe95 --- /dev/null +++ b/cpp_20/003_rtti_noop_coroutine.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +template +struct task { + struct promise_type { + auto get_return_object() { + return task(std::coroutine_handle::from_promise(*this)); + } + std::suspend_always initial_suspend() { return {}; } + struct final_awaiter { + bool await_ready() noexcept { return false; } + void await_resume() noexcept {} + std::coroutine_handle<> await_suspend(std::coroutine_handle h) noexcept { + // final_awaiter::await_suspend is called when the execution of the + // current coroutine (referred to by 'h') is about to finish. + // If the current coroutine was resumed by another coroutine via + // co_await get_task(), a handle to that coroutine has been stored + // as h.promise().previous. In that case, return the handle to resume + // the previous coroutine. + // Otherwise, return noop_coroutine(), whose resumption does nothing. + + auto previous = h.promise().previous; + if (previous) { + return previous; + } else { + return std::noop_coroutine(); + } + } + }; + final_awaiter final_suspend() noexcept { return {}; } + void unhandled_exception() { throw; } + void return_value(T value) { result = std::move(value); } + T result; + std::coroutine_handle<> previous; + }; + + task(std::coroutine_handle h) : coro(h) {} + task(task&& t) = delete; + ~task() { coro.destroy(); } + + struct awaiter { + bool await_ready() { return false; } + T await_resume() { return std::move(coro.promise().result); } + auto await_suspend(std::coroutine_handle<> h) { + coro.promise().previous = h; + return coro; + } + std::coroutine_handle coro; + }; + awaiter operator co_await() { return awaiter{coro}; } + T operator()() { + coro.resume(); + return std::move(coro.promise().result); + } +private: + std::coroutine_handle coro; +}; + +task get_random() { + std::cout << "in get_random()\n"; + co_return 4; +} +task test() { + task v = get_random(); + task u = get_random(); + std::cout << "in test()\n"; + int x = (co_await v + co_await u); + co_return x; +} + +int main() { + task t = test(); + int result = t(); + std::cout << result << '\n'; +} diff --git a/cpp_20/003_rtti_noop_coroutine_handle.cpp b/cpp_20/003_rtti_noop_coroutine_handle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a66b31da09c1795d96f0ae887c4615744ed90a17 --- /dev/null +++ b/cpp_20/003_rtti_noop_coroutine_handle.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/003_rtti_noop_coroutine_promise.cpp b/cpp_20/003_rtti_noop_coroutine_promise.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a66b31da09c1795d96f0ae887c4615744ed90a17 --- /dev/null +++ b/cpp_20/003_rtti_noop_coroutine_promise.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/003_rtti_suspend_always.cpp b/cpp_20/003_rtti_suspend_always.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d8da692711c1e5474efb8e36004ceb2dd38c0ff0 --- /dev/null +++ b/cpp_20/003_rtti_suspend_always.cpp @@ -0,0 +1,13 @@ +struct promise_type { + int current_value; + static wrapper get_return_object_on_allocation_failure() noexcept { return wrapper{nullptr}; } + wrapper get_return_object() { return wrapper{handle::from_promise(*this)}; } + auto initial_suspend() { return std::suspend_always{}; } + auto final_suspend() noexcept { return std::suspend_always{}; } + void unhandled_exception() { std::terminate(); } + void return_void() {} + auto yield_value(int value) { + current_value = value; + return std::suspend_always{}; + } +}; diff --git a/cpp_20/003_rtti_suspend_never.cpp b/cpp_20/003_rtti_suspend_never.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44021af2e23ae5149474bb0b3f34f913c3cc9729 --- /dev/null +++ b/cpp_20/003_rtti_suspend_never.cpp @@ -0,0 +1,44 @@ +template +struct myfuture: std::future +{ + myfuture(std::future && a):std::future(std::move(a)) + { + std::cout<<"myfuture"< +requires(!std::is_void_v && !std::is_reference_v) +struct std::coroutine_traits, Args...> +{ + struct promise_type:std::promise + { + std::suspend_never initial_suspend() const noexcept { std::cout<<"initial_suspend"<set_exception(std::current_exception()); } + myfuture get_return_object() noexcept + { + std::cout<<"get_return_object"<get_future(); + } + + void return_value(const T &value) noexcept(std::is_nothrow_copy_constructible_v) + { + std::cout<<"return_value"<set_value(value); + } + void return_value(T &&value) noexcept(std::is_nothrow_move_constructible_v) + { + this->set_value(std::move(value)); + } + }; +}; +myfuture co_fun(float a, float b) +{ + std::cout << "============co_fun"< +struct common_cmpcat_base { using type = void; }; +template<> +struct common_cmpcat_base<0u> { using type = std::strong_ordering; }; +template<> +struct common_cmpcat_base<2u> { using type = std::partial_ordering; }; +template<> +struct common_cmpcat_base<4u> { using type = std::weak_ordering; }; +template<> +struct common_cmpcat_base<6u> { using type = std::partial_ordering; }; + +} // namespace detail + +template +struct common_comparison_category : + detail::common_cmpcat_base<(0u | ... | + (std::is_same_v ? 0u : + std::is_same_v ? 4u : + std::is_same_v ? 2u : 1u) + )> {}; diff --git a/cpp_20/004_rtti_compare_compare_partial_order_fallback.cpp b/cpp_20/004_rtti_compare_compare_partial_order_fallback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a66b31da09c1795d96f0ae887c4615744ed90a17 --- /dev/null +++ b/cpp_20/004_rtti_compare_compare_partial_order_fallback.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/004_rtti_compare_compare_strong_order_fallback.cpp b/cpp_20/004_rtti_compare_compare_strong_order_fallback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a66b31da09c1795d96f0ae887c4615744ed90a17 --- /dev/null +++ b/cpp_20/004_rtti_compare_compare_strong_order_fallback.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/004_rtti_compare_compare_three_way.cpp b/cpp_20/004_rtti_compare_compare_three_way.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f641ab836fd53a992eed9ab21b280c2377ec6d5f --- /dev/null +++ b/cpp_20/004_rtti_compare_compare_three_way.cpp @@ -0,0 +1,30 @@ +#include +#include + +struct Rational_2 { + int num; + int den; // > 0 +}; + +constexpr std::weak_ordering operator<=>(Rational_2 lhs, Rational_2 rhs) +{ + return lhs.num * rhs.den <=> rhs.num * lhs.den; +} + +void print(std::weak_ordering value) +{ + if (value == 0) + std::cout << "equal\n"; + else if (value < 0) + std::cout << "less\n"; + else + std::cout << "greater\n"; +} + +int main() +{ + Rational_2 c{6,5}; + Rational_2 d{8,7}; + print(c <=> d); + print(std::compare_three_way{}(c,d)); +} diff --git a/cpp_20/004_rtti_compare_compare_three_way_result.cpp b/cpp_20/004_rtti_compare_compare_three_way_result.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79ca8b58de52c1da86b673c0a0250d3200d4554a --- /dev/null +++ b/cpp_20/004_rtti_compare_compare_three_way_result.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +template +void print_cmp_type() +{ + if constexpr (std::is_same_v) + std::cout << "strong ordering\n"; + else if constexpr (std::is_same_v) + std::cout << "weak ordering\n"; + else if constexpr (std::is_same_v) + std::cout << "partial ordering\n"; + else + std::cout << "illegal comparison result type\n"; +} + +int main() +{ + print_cmp_type>(); + print_cmp_type>(); +} diff --git a/cpp_20/004_rtti_compare_compare_weak_order_fallback.cpp b/cpp_20/004_rtti_compare_compare_weak_order_fallback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a09f19aa81c2ee9bafdbb8ed3e08ac09b90a311 --- /dev/null +++ b/cpp_20/004_rtti_compare_compare_weak_order_fallback.cpp @@ -0,0 +1,52 @@ +#include +#include + +// does not support <=> +struct Rational_1 { + int num; + int den; // > 0 +}; + +inline constexpr bool operator<(Rational_1 lhs, Rational_1 rhs) +{ + return lhs.num * rhs.den < rhs.num * lhs.den; +} + +inline constexpr bool operator==(Rational_1 lhs, Rational_1 rhs) +{ + return lhs.num * rhs.den == rhs.num * lhs.den; +} + +// supports <=> +struct Rational_2 { + int num; + int den; // > 0 +}; + +inline constexpr std::weak_ordering operator<=>(Rational_2 lhs, Rational_2 rhs) +{ + return lhs.num * rhs.den <=> rhs.num * lhs.den; +} + +void print(std::weak_ordering value) +{ + if (value == 0) + std::cout << "equal\n"; + else if (value < 0) + std::cout << "less\n"; + else + std::cout << "greater\n"; +} + +int main() +{ + Rational_1 a{1, 2}; + Rational_1 b{3, 4}; +// print(a <=> b); // doesn't work + print(std::compare_weak_order_fallback(a, b)); // works, defaults to < and == + + Rational_2 c{6, 5}; + Rational_2 d{8, 7}; + print(c <=> d); // works + print(std::compare_weak_order_fallback(c, d)); // works +} diff --git a/cpp_20/004_rtti_compare_is_eq.cpp b/cpp_20/004_rtti_compare_is_eq.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_eq.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_is_gt.cpp b/cpp_20/004_rtti_compare_is_gt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_gt.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_is_gteq.cpp b/cpp_20/004_rtti_compare_is_gteq.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_gteq.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_is_lt.cpp b/cpp_20/004_rtti_compare_is_lt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_lt.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_is_lteq.cpp b/cpp_20/004_rtti_compare_is_lteq.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_lteq.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_is_neq.cpp b/cpp_20/004_rtti_compare_is_neq.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/004_rtti_compare_is_neq.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/004_rtti_compare_partial_order.cpp b/cpp_20/004_rtti_compare_partial_order.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/004_rtti_compare_partial_order.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/004_rtti_compare_partial_ordering.cpp b/cpp_20/004_rtti_compare_partial_ordering.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35f523d91411846361a98ef7a0230d5229a21368 --- /dev/null +++ b/cpp_20/004_rtti_compare_partial_ordering.cpp @@ -0,0 +1,23 @@ +void test_compare() +{ + struct ts { + int id; + float f; + std::partial_ordering operator<=>(const ts& that) const + { + if(id<0 || that.id<0) return std::partial_ordering::unordered; + if(f>that.f) return std::partial_ordering::greater; + if(fthat)==0; + } + }; + + ts ta = {2, 1.0f}; + ts tb={3, 1.0f}; + std::cout<< (ta==tb) <tb) < +#include +#include + +template +class Generator { +public: + struct promise_type { + Generator get_return_object() { + return Generator{Handle::from_promise(*this)}; + } + static std::suspend_always initial_suspend() noexcept { + return {}; + } + static std::suspend_always final_suspend() noexcept { + return {}; + } + std::suspend_always yield_value(T value) noexcept { + current_value = std::move(value); + return {}; + } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] + static void unhandled_exception() { + throw; + } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : + m_coroutine{coroutine} + {} + + Generator() = default; + ~Generator() { + if (m_coroutine) { + m_coroutine.destroy(); + } + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : + m_coroutine{other.m_coroutine} + { + other.m_coroutine = {}; + } + Generator& operator=(Generator&& other) noexcept { + if (this != &other) { + if (m_coroutine) { + m_coroutine.destroy(); + } + m_coroutine = other.m_coroutine; + other.m_coroutine = {}; + } + return *this; + } + + // Range-based for loop support. + class Iter { + public: + void operator++() { + m_coroutine.resume(); + } + const T& operator*() const { + return *m_coroutine.promise().current_value; + } + bool operator==(std::default_sentinel_t) const { + return !m_coroutine || m_coroutine.done(); + } + + explicit Iter(const Handle coroutine) : + m_coroutine{coroutine} + {} + + private: + Handle m_coroutine; + }; + + Iter begin() { + if (m_coroutine) { + m_coroutine.resume(); + } + return Iter{m_coroutine}; + } + std::default_sentinel_t end() { + return {}; + } + +private: + Handle m_coroutine; +}; + +template +Generator range(T first, const T last) { + while (first < last) { + co_yield first++; + } +} + +int main() { + for (const char i : range(65, 91)) { + std::cout << i << ' '; + } + std::cout << '\n'; +} diff --git a/cpp_20/004_rtti_compare_std_coroutine_traits.cpp b/cpp_20/004_rtti_compare_std_coroutine_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b161602ace8509a3afe453cd4da12d2dbc73b96 --- /dev/null +++ b/cpp_20/004_rtti_compare_std_coroutine_traits.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +// Enable the use of std::future as a coroutine type +// by using a std::promise as the promise type. +template +requires(!std::is_void_v && !std::is_reference_v) +struct std::coroutine_traits, Args...> { + struct promise_type : std::promise { + std::future get_return_object() noexcept { + return this->get_future(); + } + + std::suspend_never initial_suspend() const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + + void return_value(const T &value) + noexcept(std::is_nothrow_copy_constructible_v) { + this->set_value(value); + } + void return_value(T &&value) + noexcept(std::is_nothrow_move_constructible_v) { + this->set_value(std::move(value)); + } + void unhandled_exception() noexcept { + this->set_exception(std::current_exception()); + } + }; +}; + +// Same for std::future. +template +struct std::coroutine_traits, Args...> { + struct promise_type : std::promise { + std::future get_return_object() noexcept { + return this->get_future(); + } + + std::suspend_never initial_suspend() const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + + void return_void() noexcept { + this->set_value(); + } + void unhandled_exception() noexcept { + this->set_exception(std::current_exception()); + } + }; +}; + +// Allow co_await'ing std::future and std::future +// by naively spawning a new thread for each co_await. +template +auto operator co_await(std::future future) noexcept +requires(!std::is_reference_v) { + struct awaiter : std::future { + bool await_ready() const noexcept { + using namespace std::chrono_literals; + return this->wait_for(0s) != std::future_status::timeout; + } + void await_suspend(std::coroutine_handle<> cont) const { + std::thread([this, cont] { + this->wait(); + cont(); + }).detach(); + } + T await_resume() { return this->get(); } + }; + return awaiter{std::move(future)}; +} + +// Utilize the infrastructure we have established. +std::future compute() { + int a = co_await std::async([] { return 6; }); + int b = co_await std::async([] { return 7; }); + co_return a * b; +} + +std::future fail() { + throw std::runtime_error("bleah"); + co_return; +} + +int main() { + std::cout << compute().get() << '\n'; + + try { + fail().get(); + } catch (const std::runtime_error &e) { + std::cout << "error: " << e.what() << '\n'; + } +} diff --git a/cpp_20/004_rtti_compare_strong_order.cpp b/cpp_20/004_rtti_compare_strong_order.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c324f90204d94772120c6058b5d2c3ca414ff99 --- /dev/null +++ b/cpp_20/004_rtti_compare_strong_order.cpp @@ -0,0 +1,39 @@ +class Base { +public: + auto operator<=>(const Base&) const = default; +}; +std::strong_ordering operator <=>(const std::string& a, const std::string& b) { + int cmp = a.compare(b); + if (cmp < 0) return std::strong_ordering::less; + else if (cmp > 0) return std::strong_ordering::greater; + else return std::strong_ordering::equivalent; +} +class TotallyOrdered : Base { + std::string tax_id; + std::string first_name; + std::string last_name; +public: + TotallyOrdered(const std::string& id, const std::string& first, const std::string& last) + :tax_id(id), first_name(first), last_name(last) {} + // 定制 operator<=>,因为我们想先比较姓 + std::strong_ordering operator<=>(const TotallyOrdered& that) const { + if (auto cmp = (Base&)(*this) <=> (Base&)that; cmp != 0) return cmp; + if (auto cmp = last_name <=> that.last_name; cmp != 0) return cmp; + if (auto cmp = first_name <=> that.first_name; cmp != 0) return cmp; + return tax_id <=> that.tax_id; + } + // ……非比较函数…… +}; +void test_compare02() { + // 编译器生成全部四个关系运算符 + TotallyOrdered to1{ "1", "first1", "last1" }, to2{ "2", "first2", "last2" }; + std::set s; // ok + s.insert(to1); // ok + s.insert(to2); + if (to1 <= to2) { // ok,调用一次 <=> + std::cout << "to1 <= to2\n"; + } + else { + std::cout << "!(to1 <= to2)\n"; + } +} diff --git a/cpp_20/004_rtti_compare_strong_ordering.cpp b/cpp_20/004_rtti_compare_strong_ordering.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff791117de055d7f2a12146f074469c47d3a551d --- /dev/null +++ b/cpp_20/004_rtti_compare_strong_ordering.cpp @@ -0,0 +1,8 @@ +#include +int main() +{ + std::strong_ordering so = 55 <=> 10; + + so < 0; // Fine + so < 1; // Fails +} diff --git a/cpp_20/004_rtti_compare_three_way_comparable.cpp b/cpp_20/004_rtti_compare_three_way_comparable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f641ab836fd53a992eed9ab21b280c2377ec6d5f --- /dev/null +++ b/cpp_20/004_rtti_compare_three_way_comparable.cpp @@ -0,0 +1,30 @@ +#include +#include + +struct Rational_2 { + int num; + int den; // > 0 +}; + +constexpr std::weak_ordering operator<=>(Rational_2 lhs, Rational_2 rhs) +{ + return lhs.num * rhs.den <=> rhs.num * lhs.den; +} + +void print(std::weak_ordering value) +{ + if (value == 0) + std::cout << "equal\n"; + else if (value < 0) + std::cout << "less\n"; + else + std::cout << "greater\n"; +} + +int main() +{ + Rational_2 c{6,5}; + Rational_2 d{8,7}; + print(c <=> d); + print(std::compare_three_way{}(c,d)); +} diff --git a/cpp_20/004_rtti_compare_three_way_comparable_with.cpp b/cpp_20/004_rtti_compare_three_way_comparable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f73b77e80fac3f5576457d08cca980d30c195856 --- /dev/null +++ b/cpp_20/004_rtti_compare_three_way_comparable_with.cpp @@ -0,0 +1,10 @@ +struct compare_three_way { + template + requires three_way_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489 + constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const + noexcept(noexcept(_STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right))) /* strengthened */ { + return _STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right); + } + + using is_transparent = int; +}; diff --git a/cpp_20/004_rtti_compare_weak_order.cpp b/cpp_20/004_rtti_compare_weak_order.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/004_rtti_compare_weak_order.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/004_rtti_compare_weak_ordering.cpp b/cpp_20/004_rtti_compare_weak_ordering.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee2a52c41b6ddfcb894a9739dc07cd46ce88dd40 --- /dev/null +++ b/cpp_20/004_rtti_compare_weak_ordering.cpp @@ -0,0 +1,11 @@ +// any type that is weakly ordered +struct Weak { + bool operator==(Weak const&) const; + std::weak_ordering operator<=>(Weak const&) const; +}; + +struct Foo { + Weak w; + int i; + auto operator<=>(Foo const&) const = default; +}; diff --git a/cpp_20/005_rtti_concepts_ranges_swap.cpp b/cpp_20/005_rtti_concepts_ranges_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0cf39309bf152c44b95911994ed369f30e4d0ea --- /dev/null +++ b/cpp_20/005_rtti_concepts_ranges_swap.cpp @@ -0,0 +1,26 @@ + +#include +#include +#include +#include +using namespace std; + +int main() +{ + vector v; + + srand(time(0)); + for(int i=0;i<10;i++) + { + v.push_back(i+1); + } + + //使用了lambda表达式 + for_each(v.begin(),v.end(),[](int n){cout< +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/006_utility_cmp_greater_equal.cpp b/cpp_20/006_utility_cmp_greater_equal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e581f1952a6ded9d979bc0176ab3443b56466188 --- /dev/null +++ b/cpp_20/006_utility_cmp_greater_equal.cpp @@ -0,0 +1,49 @@ +template< class T, class U > +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/006_utility_cmp_less.cpp b/cpp_20/006_utility_cmp_less.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e581f1952a6ded9d979bc0176ab3443b56466188 --- /dev/null +++ b/cpp_20/006_utility_cmp_less.cpp @@ -0,0 +1,49 @@ +template< class T, class U > +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/006_utility_cmp_less_equal.cpp b/cpp_20/006_utility_cmp_less_equal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e581f1952a6ded9d979bc0176ab3443b56466188 --- /dev/null +++ b/cpp_20/006_utility_cmp_less_equal.cpp @@ -0,0 +1,49 @@ +template< class T, class U > +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/006_utility_cmp_not_equal.cpp b/cpp_20/006_utility_cmp_not_equal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e581f1952a6ded9d979bc0176ab3443b56466188 --- /dev/null +++ b/cpp_20/006_utility_cmp_not_equal.cpp @@ -0,0 +1,49 @@ +template< class T, class U > +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/006_utility_in_range.cpp b/cpp_20/006_utility_in_range.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e581f1952a6ded9d979bc0176ab3443b56466188 --- /dev/null +++ b/cpp_20/006_utility_in_range.cpp @@ -0,0 +1,49 @@ +template< class T, class U > +constexpr bool cmp_equal( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t == u; + else if constexpr (std::is_signed_v) + return t < 0 ? false : UT(t) == u; + else + return u < 0 ? false : t == UU(u); +} + +template< class T, class U > +constexpr bool cmp_not_equal( T t, U u ) noexcept +{ + return !cmp_equal(t, u); +} + +template< class T, class U > +constexpr bool cmp_less( T t, U u ) noexcept +{ + using UT = std::make_unsigned_t; + using UU = std::make_unsigned_t; + if constexpr (std::is_signed_v == std::is_signed_v) + return t < u; + else if constexpr (std::is_signed_v) + return t < 0 ? true : UT(t) < u; + else + return u < 0 ? false : t < UU(u); +} + +template< class T, class U > +constexpr bool cmp_greater( T t, U u ) noexcept +{ + return cmp_less(u, t); +} + +template< class T, class U > +constexpr bool cmp_less_equal( T t, U u ) noexcept +{ + return !cmp_greater(t, u); +} + +template< class T, class U > +constexpr bool cmp_greater_equal( T t, U u ) noexcept +{ + return !cmp_less(t, u); +} diff --git a/cpp_20/007_format_basic_format_arg.cpp b/cpp_20/007_format_basic_format_arg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_basic_format_arg.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_basic_format_args.cpp b/cpp_20/007_format_basic_format_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_basic_format_args.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_basic_format_context.cpp b/cpp_20/007_format_basic_format_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_basic_format_context.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_basic_format_parse_context.cpp b/cpp_20/007_format_basic_format_parse_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..afa546e440e2f8419d6b67d5e8343c30685e0dcc --- /dev/null +++ b/cpp_20/007_format_basic_format_parse_context.cpp @@ -0,0 +1,22 @@ +struct Box { + int value1; + int value2; +}; + +template +struct std::formatter { + using _Pc = std::basic_format_parse_context<_CharT>; + typename _Pc::iterator parse(_Pc& _ParseCtx) + { // 此函数用来解析字符串 + // 相关代码逻辑可参考 https://zh.cppreference.com/w/cpp/utility/format/formatter + // 以及查看源码 format 文件的 _Formatter_base 类 + // C++内置类型的特化formatter均继承自此基类 + // 本人也在啃此源码中,但最近没时间,打算留待假期 + } + + template + typename _FormatContext::iterator format(const Box& v, _FormatContext& format_context) + { + // + } +} diff --git a/cpp_20/007_format_format.cpp b/cpp_20/007_format_format.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47360742af54407eb0d75a9ccf9dc802762b1f4a --- /dev/null +++ b/cpp_20/007_format_format.cpp @@ -0,0 +1,25 @@ +#include +#include + +// 类型 T 的包装 +template +struct Box { + T value; +}; + +// 能用被包装值的格式说明格式化包装 Box +template +struct std::formatter, CharT> : std::formatter { + // 从基类继承 parse() + + // 通过以被包装值调用基类实现定义 format() + template + auto format(Box t, FormatContext& fc) { + return std::formatter::format(t.value, fc); + } +}; + +int main() { + Box v = { 42 }; + std::cout << std::format("{:#x}", v); +} diff --git a/cpp_20/007_format_format_args.cpp b/cpp_20/007_format_format_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_format_args.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_format_context.cpp b/cpp_20/007_format_format_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_format_context.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_format_error.cpp b/cpp_20/007_format_format_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_format_error.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_format_parse_context.cpp b/cpp_20/007_format_format_parse_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_format_parse_context.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_format_to.cpp b/cpp_20/007_format_format_to.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cfab18eaf48409678ae3ff94c2fb9556440278d --- /dev/null +++ b/cpp_20/007_format_format_to.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +auto main() -> int +{ + std::string buffer; + + std::format_to( + std::back_inserter(buffer), //< OutputIt + "Hello, C++{}!\n", //< fmt + "20"); //< arg + std::cout << buffer; + buffer.clear(); + + std::format_to( + std::back_inserter(buffer), //< OutputIt + "Hello, {0}::{1}!{2}", //< fmt + "std", //< arg {0} + "format_to()", //< arg {1} + "\n", //< arg {2} + "extra param(s)..."); //< unused + std::cout << buffer; + + std::wstring wbuffer; + std::format_to( + std::back_inserter(wbuffer),//< OutputIt + L"Hello, {2}::{1}!{0}", //< fmt + L"\n", //< arg {0} + L"format_to()", //< arg {1} + L"std", //< arg {2} + L"...is not..." //< unused + L"...an error!"); //< unused + std::wcout << wbuffer; +} diff --git a/cpp_20/007_format_format_to_n.cpp b/cpp_20/007_format_format_to_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb4317c8f34b66ee53f2e41fdae9ef167154028e --- /dev/null +++ b/cpp_20/007_format_format_to_n.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +int main() +{ + char buffer[64]; + + const auto result = + std::format_to_n(buffer, std::size(buffer), + "Hubble's H{0} {1} {2} km/sec/mpc.", + "\u2080", "\u2245", 71); + + std::cout << "Buffer: \"" << std::string_view{buffer, result.size} << "\"\n" + << "Buffer size = " << std::size(buffer) << '\n' + << "Untruncated output size = " << result.size << '\n'; +} diff --git a/cpp_20/007_format_formatted_size.cpp b/cpp_20/007_format_formatted_size.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee8c2d71e8e5fd237ac8ab7c1a34a3d9e577fe22 --- /dev/null +++ b/cpp_20/007_format_formatted_size.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +int main() +{ + using namespace std::literals::string_view_literals; + + constexpr auto fmt_str { "Hubble's H{0} {1} {2:*^4} miles/sec/mpc."sv }; + constexpr auto sub_zero { "₀"sv }; // { "\u2080"sv } => { 0xe2, 0x82, 0x80 }; + constexpr auto aprox_equ { "≅"sv }; // { "\u2245"sv } => { 0xe2, 0x89, 0x85 }; + constexpr int Ho { 42 }; // H₀ + + + const auto min_buffer_size = std::formatted_size(fmt_str, sub_zero, aprox_equ, Ho); + + std::cout << "Min buffer size = " << min_buffer_size << '\n'; + + // Use std::vector as dynamic buffer. Note: buffer does not include the trailing '\0'. + std::vector buffer(min_buffer_size); + + std::format_to_n(buffer.data(), buffer.size(), fmt_str, sub_zero, aprox_equ, Ho); + + std::cout << "Buffer: \"" << std::string_view{buffer.data(), min_buffer_size} << "\"\n"; + + // Or we can print the buffer directly by adding the trailing '\0'. + buffer.push_back('\0'); + std::cout << "Buffer: \"" << buffer.data() << "\"\n"; +} diff --git a/cpp_20/007_format_formatter.cpp b/cpp_20/007_format_formatter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47360742af54407eb0d75a9ccf9dc802762b1f4a --- /dev/null +++ b/cpp_20/007_format_formatter.cpp @@ -0,0 +1,25 @@ +#include +#include + +// 类型 T 的包装 +template +struct Box { + T value; +}; + +// 能用被包装值的格式说明格式化包装 Box +template +struct std::formatter, CharT> : std::formatter { + // 从基类继承 parse() + + // 通过以被包装值调用基类实现定义 format() + template + auto format(Box t, FormatContext& fc) { + return std::formatter::format(t.value, fc); + } +}; + +int main() { + Box v = { 42 }; + std::cout << std::format("{:#x}", v); +} diff --git a/cpp_20/007_format_make_format_args.cpp b/cpp_20/007_format_make_format_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_make_format_args.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_make_wformat_args.cpp b/cpp_20/007_format_make_wformat_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_make_wformat_args.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_vformat.cpp b/cpp_20/007_format_vformat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_vformat.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_vformat_to.cpp b/cpp_20/007_format_vformat_to.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_vformat_to.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/007_format_visit_format_arg.cpp b/cpp_20/007_format_visit_format_arg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_visit_format_arg.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_wformat_args.cpp b/cpp_20/007_format_wformat_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_wformat_args.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_wformat_context.cpp b/cpp_20/007_format_wformat_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/007_format_wformat_context.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/007_format_wformat_parse_context.cpp b/cpp_20/007_format_wformat_parse_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130ad39d4603e62003cf3dcfeeba2dc2b3a64877 --- /dev/null +++ b/cpp_20/007_format_wformat_parse_context.cpp @@ -0,0 +1 @@ +正在整理中... diff --git a/cpp_20/008_memory_destroy.cpp b/cpp_20/008_memory_destroy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da69832a5f8c12e8acb0bf5e4948bc5f66cbfa3c --- /dev/null +++ b/cpp_20/008_memory_destroy.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +struct Tracer { + int value; + ~Tracer() { std::cout << value << " destructed\n"; } +}; + +int main() +{ + alignas(Tracer) unsigned char buffer[sizeof(Tracer) * 8]; + + for (int i = 0; i < 8; ++i) + new(buffer + sizeof(Tracer) * i) Tracer{i}; // 手工构造对象 + + auto ptr = std::launder(reinterpret_cast(buffer)); + + std::destroy(ptr, ptr + 8); +} diff --git a/cpp_20/008_memory_destroy_at.cpp b/cpp_20/008_memory_destroy_at.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da69832a5f8c12e8acb0bf5e4948bc5f66cbfa3c --- /dev/null +++ b/cpp_20/008_memory_destroy_at.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +struct Tracer { + int value; + ~Tracer() { std::cout << value << " destructed\n"; } +}; + +int main() +{ + alignas(Tracer) unsigned char buffer[sizeof(Tracer) * 8]; + + for (int i = 0; i < 8; ++i) + new(buffer + sizeof(Tracer) * i) Tracer{i}; // 手工构造对象 + + auto ptr = std::launder(reinterpret_cast(buffer)); + + std::destroy(ptr, ptr + 8); +} diff --git a/cpp_20/008_memory_destroy_n.cpp b/cpp_20/008_memory_destroy_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..793d9bc20fd72cddeac057a58ef3ad635880194a --- /dev/null +++ b/cpp_20/008_memory_destroy_n.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +struct Tracer { + int value; + ~Tracer() { std::cout << value << " destructed\n"; } +}; + +int main() +{ + alignas(Tracer) unsigned char buffer[sizeof(Tracer) * 8]; + + for (int i = 0; i < 8; ++i) + new(buffer + sizeof(Tracer) * i) Tracer{i}; //manually construct objects + + auto ptr = std::launder(reinterpret_cast(buffer)); + + std::ranges::destroy_n(ptr, 8); +} diff --git a/cpp_20/008_memory_uninitialized_default_construct.cpp b/cpp_20/008_memory_uninitialized_default_construct.cpp new file mode 100644 index 0000000000000000000000000000000000000000..351c58c9ae11fec7ad2d3feea83d403521aa40d4 --- /dev/null +++ b/cpp_20/008_memory_uninitialized_default_construct.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +int main() +{ + struct S { std::string m{ "Default value" }; }; + + constexpr int n {3}; + alignas(alignof(S)) unsigned char mem[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(mem)}; + auto last {first + n}; + + std::uninitialized_default_construct(first, last); + + for (auto it {first}; it != last; ++it) { + std::cout << it->m << '\n'; + } + + std::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_default_construct + // generally does not zero-fill the given uninitialized memory area. + int v[] { 1, 2, 3, 4 }; + const int original[] { 1, 2, 3, 4 }; + std::uninitialized_default_construct(std::begin(v), std::end(v)); + // for (const int i : v) { std::cout << i << ' '; } + // Maybe undefined behavior, pending CWG 1997. + std::cout << + (std::memcmp(v, original, sizeof(v)) == 0 ? "Unmodified\n" : "Modified\n"); + // The result is unspecified. +} diff --git a/cpp_20/008_memory_uninitialized_default_construct_n.cpp b/cpp_20/008_memory_uninitialized_default_construct_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea713840916602b2c9eeb4dca1492cf49c4879bf --- /dev/null +++ b/cpp_20/008_memory_uninitialized_default_construct_n.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +int main() +{ + struct S { std::string m{ "Default value" }; }; + + constexpr int n {3}; + alignas(alignof(S)) unsigned char mem[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(mem)}; + auto last = std::uninitialized_default_construct_n(first, n); + + for (auto it {first}; it != last; ++it) { + std::cout << it->m << '\n'; + } + + std::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_default_construct_n + // generally does not zero-initialize the given uninitialized memory area. + int v[] { 1, 2, 3, 4 }; + const int original[] { 1, 2, 3, 4 }; + std::uninitialized_default_construct_n(std::begin(v), std::size(v)); + // for (const int i : v) { std::cout << i << ' '; } + // Maybe undefined behavior, pending CWG 1997. + std::cout << + (std::memcmp(v, original, sizeof(v)) == 0 ? "Unmodified\n" : "Modified\n"); + // The result is unspecified. +} diff --git a/cpp_20/008_memory_uninitialized_move.cpp b/cpp_20/008_memory_uninitialized_move.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38667af54727584b6bab64184a0ec991ea648c3e --- /dev/null +++ b/cpp_20/008_memory_uninitialized_move.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +void print(auto rem, auto first, auto last) { + for (std::cout << rem; first != last; ++first) + std::cout << std::quoted(*first) << ' '; + std::cout << '\n'; +} + +int main() { + std::string in[] { "Home", "Work!" }; + print("initially, in: ", std::begin(in), std::end(in)); + + if ( + constexpr auto sz = std::size(in); + void* out = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz) + ) { + try { + auto first {static_cast(out)}; + auto last {first + sz}; + std::uninitialized_move(std::begin(in), std::end(in), first); + + print("after move, in: ", std::begin(in), std::end(in)); + print("after move, out: ", first, last); + + std::destroy(first, last); + } + catch (...) { + std::cout << "Exception!\n"; + } + std::free(out); + } +} diff --git a/cpp_20/008_memory_uninitialized_move_n.cpp b/cpp_20/008_memory_uninitialized_move_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb5ab7579d6e053dd58fdf5c47b79f349ea7aa63 --- /dev/null +++ b/cpp_20/008_memory_uninitialized_move_n.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +void print(auto rem, auto first, auto last) { + for (std::cout << rem; first != last; ++first) + std::cout << std::quoted(*first) << ' '; + std::cout << '\n'; +} + +int main() { + std::string in[] { "One", "Definition", "Rule" }; + print("initially, in: ", std::begin(in), std::end(in)); + + if ( + constexpr auto sz = std::size(in); + void* out = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz) + ) { + try { + auto first {static_cast(out)}; + auto last {first + sz}; + std::uninitialized_move_n(std::begin(in), sz, first); + + print("after move, in: ", std::begin(in), std::end(in)); + print("after move, out: ", first, last); + + std::destroy(first, last); + } + catch (...) { + std::cout << "Exception!\n"; + } + std::free(out); + } +} diff --git a/cpp_20/008_memory_uninitialized_value_construct.cpp b/cpp_20/008_memory_uninitialized_value_construct.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5f5e474ee83b69793dc0b8693a64ee145523934 --- /dev/null +++ b/cpp_20/008_memory_uninitialized_value_construct.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +int main() +{ + struct S { std::string m{ "Default value" }; }; + + constexpr int n {3}; + alignas(alignof(S)) unsigned char mem[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(mem)}; + auto last {first + n}; + + std::uninitialized_value_construct(first, last); + + for (auto it {first}; it != last; ++it) { + std::cout << it->m << '\n'; + } + + std::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_value_construct + // zero-fills the given uninitialized memory area. + int v[] { 1, 2, 3, 4 }; + for (const int i : v) { std::cout << i << ' '; } + std::cout << '\n'; + std::uninitialized_value_construct(std::begin(v), std::end(v)); + for (const int i : v) { std::cout << i << ' '; } + std::cout << '\n'; +} diff --git a/cpp_20/008_memory_uninitialized_value_construct_n.cpp b/cpp_20/008_memory_uninitialized_value_construct_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..248d6a78c77eec54ef15970dee716941487cf05e --- /dev/null +++ b/cpp_20/008_memory_uninitialized_value_construct_n.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +int main() +{ + struct S { std::string m{ "Default value" }; }; + + constexpr int n {3}; + alignas(alignof(S)) unsigned char mem[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(mem)}; + auto last = std::uninitialized_value_construct_n(first, n); + + for (auto it {first}; it != last; ++it) { + std::cout << it->m << '\n'; + } + + std::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_value_construct_n + // zero-initializes the given uninitialized memory area. + int v[] { 1, 2, 3, 4 }; + for (const int i : v) { std::cout << i << ' '; } + std::cout << '\n'; + std::uninitialized_value_construct_n(std::begin(v), std::size(v)); + for (const int i : v) { std::cout << i << ' '; } + std::cout << '\n'; +} diff --git a/cpp_20/009_memory_resource_get_default_resource.cpp b/cpp_20/009_memory_resource_get_default_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_get_default_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_memory_resource.cpp b/cpp_20/009_memory_resource_memory_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_memory_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_monotonic_buffer_resource.cpp b/cpp_20/009_memory_resource_monotonic_buffer_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_monotonic_buffer_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_new_delete_resource.cpp b/cpp_20/009_memory_resource_new_delete_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_new_delete_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_null_memory_resource.cpp b/cpp_20/009_memory_resource_null_memory_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_null_memory_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_polymorphic_allocator.cpp b/cpp_20/009_memory_resource_polymorphic_allocator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_polymorphic_allocator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_pool_options.cpp b/cpp_20/009_memory_resource_pool_options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_pool_options.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_set_default_resource.cpp b/cpp_20/009_memory_resource_set_default_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_set_default_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_synchronized_pool_resource.cpp b/cpp_20/009_memory_resource_synchronized_pool_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_synchronized_pool_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/009_memory_resource_unsynchronized_pool_resource.cpp b/cpp_20/009_memory_resource_unsynchronized_pool_resource.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/009_memory_resource_unsynchronized_pool_resource.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_assignable_from.cpp b/cpp_20/010_concepts_assignable_from.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3db1e8d80d1308f5ea9444b506461f659e864d91 --- /dev/null +++ b/cpp_20/010_concepts_assignable_from.cpp @@ -0,0 +1,19 @@ +#include + +template +class A +{ +public: + const A& operator= (const A& other) { return *this; } +}; + +int main () +{ + A d1, d2; d1 = d2; //this works fine + + std::cout << std::boolalpha + << "Is double assignable? " << std::assignable_from << '\n' //Says true, as it should + << "Is A assignable? " << std::assignable_from&, A> << '\n'; //Says false + + return 0; +} diff --git a/cpp_20/010_concepts_boolean_testable.cpp b/cpp_20/010_concepts_boolean_testable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_boolean_testable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_common_reference_with.cpp b/cpp_20/010_concepts_common_reference_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_common_reference_with.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_common_with.cpp b/cpp_20/010_concepts_common_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_common_with.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_constructible_from.cpp b/cpp_20/010_concepts_constructible_from.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f89b76a195a978a332f7c0be08f820d1a6b74d4e --- /dev/null +++ b/cpp_20/010_concepts_constructible_from.cpp @@ -0,0 +1,24 @@ +#include +#include + +class Foo { + int v1; + double v2; + public: + Foo(int n) : v1(n), v2() {} + Foo(int n, double f) noexcept : v1(n), v2(f) {} +}; + +int main() { + std::cout << "Foo is ...\n" << std::boolalpha + << "\tTrivially-constructible from const Foo&? " + << std::is_trivially_constructible::value << '\n' + << "\tTrivially-constructible from int? " + << std::is_trivially_constructible::value << '\n' + << "\tConstructible from int? " + << std::is_constructible::value << '\n' + << "\tNothrow-constructible from int? " + << std::is_nothrow_constructible::value << '\n' + << "\tNothrow-constructible from int and double? " + << std::is_nothrow_constructible::value << '\n'; +} diff --git a/cpp_20/010_concepts_convertible_to.cpp b/cpp_20/010_concepts_convertible_to.cpp new file mode 100644 index 0000000000000000000000000000000000000000..893f5dbea22698b35f81ad7b5e8362a2e0b77034 --- /dev/null +++ b/cpp_20/010_concepts_convertible_to.cpp @@ -0,0 +1,10 @@ +struct From; +struct To { + explicit To(From) = delete; +}; +struct From { + operator To(); +}; + +static_assert(std::is_convertible_v); +static_assert(not std::convertible_to); diff --git a/cpp_20/010_concepts_copy_constructible.cpp b/cpp_20/010_concepts_copy_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cb87a38a5135842577a249de4b92e9fc9e177ea --- /dev/null +++ b/cpp_20/010_concepts_copy_constructible.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // member has a non-trivial copy ctor +}; +struct Ex2 { + int n; + Ex2(const Ex2&) = default; // trivial and non-throwing +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is copy-constructible? " + << std::is_copy_constructible::value << '\n' + << "Ex1 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is trivially copy-constructible? " + << std::is_trivially_copy_constructible::value << '\n' + << "Ex2 is nothrow copy-constructible? " + << std::is_nothrow_copy_constructible::value << '\n'; +} diff --git a/cpp_20/010_concepts_copyable.cpp b/cpp_20/010_concepts_copyable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_copyable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_default_initializable.cpp b/cpp_20/010_concepts_default_initializable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c07728290a065917b033bbb12c900c898dfc275 --- /dev/null +++ b/cpp_20/010_concepts_default_initializable.cpp @@ -0,0 +1,21 @@ +#include +#include + +struct Ex1 { + std::string str; // member has a non-trivial default ctor +}; +struct Ex2 { + int n; + Ex2() = default; // trivial and non-throwing +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is default-constructible? " + << std::is_default_constructible::value << '\n' + << "Ex1 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is trivially default-constructible? " + << std::is_trivially_default_constructible::value << '\n' + << "Ex2 is nothrow default-constructible? " + << std::is_nothrow_default_constructible::value << '\n'; +} diff --git a/cpp_20/010_concepts_derived_from.cpp b/cpp_20/010_concepts_derived_from.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2e8504c891d9946d66575ab51f5563288b55b3a --- /dev/null +++ b/cpp_20/010_concepts_derived_from.cpp @@ -0,0 +1,21 @@ +#include + +class A {}; + +class B: public A {}; + +class C: private A{}; + +int main() { + // std::derived_from == true only for public inheritance or exact same class + static_assert( std::derived_from == true ); // same class: true + static_assert( std::derived_from == false ); // same primitive type: false + static_assert( std::derived_from == true ); // public inheritance: true + static_assert( std::derived_from == false ); // private inheritance: false + + // std::is_base_of == true also for private inheritance + static_assert( std::is_base_of_v == true ); // same class: true + static_assert( std::is_base_of_v == false ); // same primitive type: false + static_assert( std::is_base_of_v == true ); // public inheritance: true + static_assert( std::is_base_of_v == true ); // private inheritance: true +} diff --git a/cpp_20/010_concepts_destructible.cpp b/cpp_20/010_concepts_destructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a780cc9734ce2b48be2d4e451d32c15ba0dc2e92 --- /dev/null +++ b/cpp_20/010_concepts_destructible.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +struct Foo { + std::string str; + ~Foo() noexcept {}; +}; +struct Bar { + ~Bar() = default; +}; +int main() { + std::cout << std::boolalpha + << "std::string is destructible? " + << std::is_destructible::value << '\n' + << "Foo is trivially destructible? " + << std::is_trivially_destructible_v << '\n' + << "Foo is nothrow destructible? " + << std::is_nothrow_destructible() << '\n' + << "Bar is trivially destructible? " + << std::is_trivially_destructible{} << '\n'; +} diff --git a/cpp_20/010_concepts_equality_comparable.cpp b/cpp_20/010_concepts_equality_comparable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9c5e4e37c11dbcd6b58b76a945af2001bf48fa5 --- /dev/null +++ b/cpp_20/010_concepts_equality_comparable.cpp @@ -0,0 +1,16 @@ +class strong_int { +public: + using integral_type = int; + + explicit strong_int( integral_type ) noexcept; + + explicit operator integral_type() const noexcept; + + constexpr auto operator<=>( const strong_int& ) const noexcept; + +private: + integral_type m_value; +}; + +constexpr auto operator<=>( const strong_int&, const int& ) noexcept; +constexpr auto operator<=>( const int&, const strong_int& ) noexcept; diff --git a/cpp_20/010_concepts_equality_comparable_with.cpp b/cpp_20/010_concepts_equality_comparable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9c5e4e37c11dbcd6b58b76a945af2001bf48fa5 --- /dev/null +++ b/cpp_20/010_concepts_equality_comparable_with.cpp @@ -0,0 +1,16 @@ +class strong_int { +public: + using integral_type = int; + + explicit strong_int( integral_type ) noexcept; + + explicit operator integral_type() const noexcept; + + constexpr auto operator<=>( const strong_int& ) const noexcept; + +private: + integral_type m_value; +}; + +constexpr auto operator<=>( const strong_int&, const int& ) noexcept; +constexpr auto operator<=>( const int&, const strong_int& ) noexcept; diff --git a/cpp_20/010_concepts_equivalence_relation.cpp b/cpp_20/010_concepts_equivalence_relation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_equivalence_relation.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_floating_point.cpp b/cpp_20/010_concepts_floating_point.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a486716b4e3d2c41dd4ec928fc5247b5032042a --- /dev/null +++ b/cpp_20/010_concepts_floating_point.cpp @@ -0,0 +1,15 @@ +#include +#include + +class A {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; + std::cout << std::is_floating_point::value << '\n'; +} diff --git a/cpp_20/010_concepts_integral.cpp b/cpp_20/010_concepts_integral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f109a560818d97c7e73a92ab035fb8cbfb94d10 --- /dev/null +++ b/cpp_20/010_concepts_integral.cpp @@ -0,0 +1,18 @@ +#include +#include + +void print(std::integral auto i) { + std::cout << "Integral: " << i << '\n'; +} + +void print(auto x) { + std::cout << "Non-integral: " << x << '\n'; +} + +int main() +{ + print( 'o' ); static_assert( std::integral ); + print( 007 ); static_assert( std::integral ); + print( 2e2 ); static_assert( !std::integral ); + print("∫∫∫"); static_assert( !std::integral ); +} diff --git a/cpp_20/010_concepts_invocable.cpp b/cpp_20/010_concepts_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81ea0fe36679fcb9de33dec3e8e75cff2a801f23 --- /dev/null +++ b/cpp_20/010_concepts_invocable.cpp @@ -0,0 +1,18 @@ +#include + +auto func2(char) -> int (*)() +{ + return nullptr; +} + +int main() +{ + static_assert( std::is_invocable_v ); + static_assert( not std::is_invocable_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); + static_assert( std::is_invocable_r_v ); + static_assert( not std::is_invocable_r_v ); +} diff --git a/cpp_20/010_concepts_movable.cpp b/cpp_20/010_concepts_movable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_movable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_move_constructible.cpp b/cpp_20/010_concepts_move_constructible.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acedd969e167805a94ddccda21bac6303e7b7ba4 --- /dev/null +++ b/cpp_20/010_concepts_move_constructible.cpp @@ -0,0 +1,35 @@ +#include +#include + +struct Ex1 { + std::string str; // member has a non-trivial but non-throwing move ctor +}; +struct Ex2 { + int n; + Ex2(Ex2&&) = default; // trivial and non-throwing +}; +struct NoMove { + // prevents implicit declaration of default move constructor + // however, the class is still move-constructible because its + // copy constructor can bind to an rvalue argument + NoMove(const NoMove&) {} +}; + +int main() { + std::cout << std::boolalpha << "Ex1 is move-constructible? " + << std::is_move_constructible::value << '\n' + << "Ex1 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex1 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n' + << "Ex2 is trivially move-constructible? " + << std::is_trivially_move_constructible::value << '\n' + << "Ex2 is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; + + std::cout << std::boolalpha + << "NoMove is move-constructible? " + << std::is_move_constructible::value << '\n' + << "NoMove is nothrow move-constructible? " + << std::is_nothrow_move_constructible::value << '\n'; +} diff --git a/cpp_20/010_concepts_predicate.cpp b/cpp_20/010_concepts_predicate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_predicate.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_regular.cpp b/cpp_20/010_concepts_regular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_regular.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_regular_invocable.cpp b/cpp_20/010_concepts_regular_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_regular_invocable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_relation.cpp b/cpp_20/010_concepts_relation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_relation.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_same_as.cpp b/cpp_20/010_concepts_same_as.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d220373acb1cf82617f86a1c3df7fbc8af6082e6 --- /dev/null +++ b/cpp_20/010_concepts_same_as.cpp @@ -0,0 +1,16 @@ +#include +#include + +template +concept IsAnyOf = (std::same_as || ...); + +template +concept IsPrintable = std::integral || std::floating_point || + IsAnyOf>>, char, wchar_t>; + +void println(IsPrintable auto const ... arguments) +{ + (std::wcout << ... << arguments) << '\n'; +} + +int main() { println("Example: ", 3.14, " : ", 42, " : [", 'a', L'-', L"Z]"); } diff --git a/cpp_20/010_concepts_semiregular.cpp b/cpp_20/010_concepts_semiregular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb2dff71567c165402cf2a703b3dc4e889211fdd --- /dev/null +++ b/cpp_20/010_concepts_semiregular.cpp @@ -0,0 +1,16 @@ +#include +#include + +template +struct Single { + T value; +}; + +int main() +{ + Single myInt1{4}; + Single myInt2; + myInt2 = myInt1; + + std::cout << myInt1.value << ' ' << myInt2.value << '\n'; +} diff --git a/cpp_20/010_concepts_signed_integral.cpp b/cpp_20/010_concepts_signed_integral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ded002290f42c791f9c3b6d638c2a50a9187312 --- /dev/null +++ b/cpp_20/010_concepts_signed_integral.cpp @@ -0,0 +1,23 @@ +#include +#include + +void print(std::signed_integral auto i) { + std::cout << "Signed integral: " << i << '\n'; +} + +void print(std::unsigned_integral auto u) { + std::cout << "Unsigned integral: " << u << '\n'; +} + +void print(auto x) { + std::cout << "Non-integral: " << x << '\n'; +} + +int main() { + print(42); // signed + print(0xFull); // unsigned + print(true); // unsigned + print('A'); // platform-dependent + print(4e-2); // non-integral (hex-float) + print("∫∫∫"); // non-integral +} diff --git a/cpp_20/010_concepts_strict_weak_order.cpp b/cpp_20/010_concepts_strict_weak_order.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_strict_weak_order.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_swappable.cpp b/cpp_20/010_concepts_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_swappable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_swappable_with.cpp b/cpp_20/010_concepts_swappable_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1bb76c6549ea6f28e8ad466f477f127940512ab --- /dev/null +++ b/cpp_20/010_concepts_swappable_with.cpp @@ -0,0 +1,16 @@ +#include + +template +requires std::swappable_with +void mySwap(T& t, U& u) +{ + T temp = t; t = u; u = temp; +} + +int main() +{ + int x, y; + mySwap(x, y); + + return 0; +} diff --git a/cpp_20/010_concepts_totally_ordered.cpp b/cpp_20/010_concepts_totally_ordered.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_totally_ordered.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_totally_ordered_with.cpp b/cpp_20/010_concepts_totally_ordered_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/010_concepts_totally_ordered_with.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/010_concepts_unsigned_integral.cpp b/cpp_20/010_concepts_unsigned_integral.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70fa2c0aa6b52124c760115cbbb8e31615bf7fa7 --- /dev/null +++ b/cpp_20/010_concepts_unsigned_integral.cpp @@ -0,0 +1,17 @@ +#include +#include +template +concept gcdint = std::unsigned_integral && !std::is_same_v, bool>; + +template +constexpr std::common_type_t gcd(T1 a, T2 b) +{ + return b ? gcd(b, a % b) : a; +} +int main() +{ + std::cout << gcd(37u, 666u) << std::endl; + std::cout << gcd(0u, false) << std::endl; + return 0; +} + diff --git a/cpp_20/011_memory_assume_aligned.cpp b/cpp_20/011_memory_assume_aligned.cpp new file mode 100644 index 0000000000000000000000000000000000000000..503ec94f70fda4c2f74a22b1b5450717be82fb40 --- /dev/null +++ b/cpp_20/011_memory_assume_aligned.cpp @@ -0,0 +1,5 @@ +void f(int* p) { + int* p1 = std::assume_aligned<256>(p); + // 用 p1 而非 p ,以确保从对齐假设受益。 + // 然而,若 p 未对齐则程序有未定义行为,无关乎是否使用 p1 。 +} diff --git a/cpp_20/011_memory_construct_at.cpp b/cpp_20/011_memory_construct_at.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_construct_at.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_make_obj_using_allocator.cpp b/cpp_20/011_memory_make_obj_using_allocator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_make_obj_using_allocator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_no-throw-forward-iterator.cpp b/cpp_20/011_memory_no-throw-forward-iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_no-throw-forward-iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_no-throw-forward-range.cpp b/cpp_20/011_memory_no-throw-forward-range.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_no-throw-forward-range.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_no-throw-input-iterator.cpp b/cpp_20/011_memory_no-throw-input-iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_no-throw-input-iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_no-throw-input-range.cpp b/cpp_20/011_memory_no-throw-input-range.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_no-throw-input-range.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_no-throw-sentinel-for.cpp b/cpp_20/011_memory_no-throw-sentinel-for.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_no-throw-sentinel-for.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_ranges_construct_at.cpp b/cpp_20/011_memory_ranges_construct_at.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98d8833249267243cf0c8495fee5c0ad4bef430a --- /dev/null +++ b/cpp_20/011_memory_ranges_construct_at.cpp @@ -0,0 +1,26 @@ +#include +#include + +struct S { + int x; + float y; + double z; + + S(int x, float y, double z) : x{x}, y{y}, z{z} { std::cout << "S::S();\n"; } + + ~S() { std::cout << "S::~S();\n"; } + + void print() const { + std::cout << "S { x=" << x << "; y=" << y << "; z=" << z << "; };\n"; + } +}; + +int main() +{ + alignas(S) unsigned char buf[sizeof(S)]; + + S* ptr = std::ranges::construct_at(reinterpret_cast(buf), 42, 2.71828f, 3.1415); + ptr->print(); + + std::ranges::destroy_at(ptr); +} diff --git a/cpp_20/011_memory_ranges_destroy.cpp b/cpp_20/011_memory_ranges_destroy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e4674bdc4ea1883660691324847cfed99360434 --- /dev/null +++ b/cpp_20/011_memory_ranges_destroy.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +struct Tracer { + int value; + ~Tracer() { std::cout << value << " destructed\n"; } +}; + +int main() +{ + alignas(Tracer) unsigned char buffer[sizeof(Tracer) * 8]; + + for (int i = 0; i < 8; ++i) + new(buffer + sizeof(Tracer) * i) Tracer{i}; //manually construct objects + + auto ptr = std::launder(reinterpret_cast(buffer)); + + std::ranges::destroy(ptr, ptr + 8); +} diff --git a/cpp_20/011_memory_ranges_destroy_at.cpp b/cpp_20/011_memory_ranges_destroy_at.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98d8833249267243cf0c8495fee5c0ad4bef430a --- /dev/null +++ b/cpp_20/011_memory_ranges_destroy_at.cpp @@ -0,0 +1,26 @@ +#include +#include + +struct S { + int x; + float y; + double z; + + S(int x, float y, double z) : x{x}, y{y}, z{z} { std::cout << "S::S();\n"; } + + ~S() { std::cout << "S::~S();\n"; } + + void print() const { + std::cout << "S { x=" << x << "; y=" << y << "; z=" << z << "; };\n"; + } +}; + +int main() +{ + alignas(S) unsigned char buf[sizeof(S)]; + + S* ptr = std::ranges::construct_at(reinterpret_cast(buf), 42, 2.71828f, 3.1415); + ptr->print(); + + std::ranges::destroy_at(ptr); +} diff --git a/cpp_20/011_memory_ranges_destroy_n.cpp b/cpp_20/011_memory_ranges_destroy_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..793d9bc20fd72cddeac057a58ef3ad635880194a --- /dev/null +++ b/cpp_20/011_memory_ranges_destroy_n.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +struct Tracer { + int value; + ~Tracer() { std::cout << value << " destructed\n"; } +}; + +int main() +{ + alignas(Tracer) unsigned char buffer[sizeof(Tracer) * 8]; + + for (int i = 0; i < 8; ++i) + new(buffer + sizeof(Tracer) * i) Tracer{i}; //manually construct objects + + auto ptr = std::launder(reinterpret_cast(buffer)); + + std::ranges::destroy_n(ptr, 8); +} diff --git a/cpp_20/011_memory_ranges_uninitialized_copy.cpp b/cpp_20/011_memory_ranges_uninitialized_copy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ff989431652b384c42d06c8367785bc99bc6669 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_copy.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +int main() +{ + const char* v[] { "This", "is", "an", "example", }; + + if (const auto sz{std::size(v)}; + void* pbuf = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz)) + { + try + { + auto first {static_cast(pbuf)}; + auto last {first + sz}; + std::ranges::uninitialized_copy(std::begin(v), std::end(v), first, last); + + std::cout << "{ "; + for (auto it {first}; it != last; ++it) + std::cout << std::quoted(*it) << ", "; + std::cout << "};\n"; + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "uninitialized_copy exception\n"; + } + std::free(pbuf); + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_copy_n.cpp b/cpp_20/011_memory_ranges_uninitialized_copy_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c83c6545cb554492deb8209f8e8a1eee4ca3dca2 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_copy_n.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +int main() +{ + const char* stars[] { "Procyon", "Spica", "Pollux", "Deneb", "Polaris", }; + + constexpr int n {4}; + alignas(alignof(std::string)) char out[n * sizeof(std::string)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last {first + n}; + auto ret {std::ranges::uninitialized_copy_n(std::begin(stars), n, first, last)}; + + std::cout << "{ "; + for (auto it {first}; it != ret.out; ++it) + std::cout << std::quoted(*it) << ", "; + std::cout << "};\n"; + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "uninitialized_copy_n exception\n"; + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_default_construct.cpp b/cpp_20/011_memory_ranges_uninitialized_default_construct.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11548af7be3c5e14886f4923b0ef396b2b9ad635 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_default_construct.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +int main() +{ + struct S { std::string m{ "▄▀▄▀▄▀▄▀" }; }; + + constexpr int n {4}; + alignas(alignof(S)) char out[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last {first + n}; + + std::ranges::uninitialized_default_construct(first, last); + + auto count {1}; + for (auto it {first}; it != last; ++it) { + std::cout << count++ << ' ' << it->m << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) { std::cout << "Exception!\n"; } + + // Notice that for "trivial types" the uninitialized_default_construct + // generally does not zero-fill the given uninitialized memory area. + constexpr char etalon[] { 'A', 'B', 'C', 'D', '\n' }; + char v[] { 'A', 'B', 'C', 'D', '\n' }; + std::ranges::uninitialized_default_construct(std::begin(v), std::end(v)); + if (std::memcmp(v, etalon, sizeof(v)) == 0) { + std::cout << " "; + // Maybe undefined behavior, pending CWG 1997: + // for (const char c : v) { std::cout << c << ' '; } + for (const char c : etalon) { std::cout << c << ' '; } + } else { + std::cout << "Unspecified\n"; + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_default_construct_n.cpp b/cpp_20/011_memory_ranges_uninitialized_default_construct_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0260ae011eb4f7788a6a560a746123c2726eae1d --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_default_construct_n.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +int main() +{ + struct S { std::string m{ "█▓▒░ █▓▒░ " }; }; + + constexpr int n {4}; + alignas(alignof(S)) char out[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last = std::ranges::uninitialized_default_construct_n(first, n); + + auto count {1}; + for (auto it {first}; it != last; ++it) { + std::cout << count++ << ' ' << it->m << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) { std::cout << "Exception!\n"; } + + // Notice that for "trivial types" the uninitialized_default_construct_n + // generally does not zero-fill the given uninitialized memory area. + constexpr int etalon[] { 1, 2, 3, 4, 5, 6 }; + int v[] { 1, 2, 3, 4, 5, 6 }; + std::ranges::uninitialized_default_construct_n(std::begin(v), std::size(v)); + if (std::memcmp(v, etalon, sizeof(v)) == 0) { + // Maybe undefined behavior, pending CWG 1997: + // for (const int i : v) { std::cout << i << ' '; } + for (const int i : etalon) { std::cout << i << ' '; } + } else { + std::cout << "Unspecified!"; + } + std::cout << '\n'; +} diff --git a/cpp_20/011_memory_ranges_uninitialized_fill.cpp b/cpp_20/011_memory_ranges_uninitialized_fill.cpp new file mode 100644 index 0000000000000000000000000000000000000000..653095cf9753a07c3cc86dfd2307ab085c01ebbf --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_fill.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() +{ + constexpr int n {4}; + alignas(alignof(std::string)) char out[n * sizeof(std::string)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last {first + n}; + std::ranges::uninitialized_fill(first, last, "▄▀▄▀▄▀▄▀"); + + int count {1}; + for (auto it {first}; it != last; ++it) { + std::cout << count++ << ' ' << *it << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_fill_n.cpp b/cpp_20/011_memory_ranges_uninitialized_fill_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56f401d33559592ddb52f0f9d6507f0b62fd11ef --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_fill_n.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +int main() +{ + constexpr int n {3}; + alignas(alignof(std::string)) char out[n * sizeof(std::string)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last = std::ranges::uninitialized_fill_n(first, n, "cppreference"); + + for (auto it {first}; it != last; ++it) { + std::cout << *it << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_move.cpp b/cpp_20/011_memory_ranges_uninitialized_move.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af243d16702df2da6c218138c00febd8f417d811 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_move.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +void print(auto rem, auto first, auto last) { + for (std::cout << rem; first != last; ++first) + std::cout << std::quoted(*first) << ' '; + std::cout << '\n'; +} + +int main() { + std::string in[] { "Home", "World" }; + print("initially, in: ", std::begin(in), std::end(in)); + + if ( + constexpr auto sz = std::size(in); + void* out = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz) + ) { + try { + auto first {static_cast(out)}; + auto last {first + sz}; + std::ranges::uninitialized_move(std::begin(in), std::end(in), first, last); + + print("after move, in: ", std::begin(in), std::end(in)); + print("after move, out: ", first, last); + + std::ranges::destroy(first, last); + } + catch (...) { + std::cout << "Exception!\n"; + } + std::free(out); + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_move_n.cpp b/cpp_20/011_memory_ranges_uninitialized_move_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a69965bdb01ae080fd94bfcf4d32e329ef1bd99 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_move_n.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +void print(auto rem, auto first, auto last) { + for (std::cout << rem; first != last; ++first) + std::cout << std::quoted(*first) << ' '; + std::cout << '\n'; +} + +int main() { + std::string in[] { "No", "Diagnostic", "Required", }; + print("initially, in: ", std::begin(in), std::end(in)); + + if ( + constexpr auto sz = std::size(in); + void* out = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz) + ) { + try { + auto first {static_cast(out)}; + auto last {first + sz}; + std::ranges::uninitialized_move_n(std::begin(in), sz, first, last); + + print("after move, in: ", std::begin(in), std::end(in)); + print("after move, out: ", first, last); + + std::ranges::destroy(first, last); + } + catch (...) { + std::cout << "Exception!\n"; + } + std::free(out); + } +} diff --git a/cpp_20/011_memory_ranges_uninitialized_value_construct.cpp b/cpp_20/011_memory_ranges_uninitialized_value_construct.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5532929853d7f3d0366fc73a61367b6e3cffb87 --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_value_construct.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +int main() +{ + struct S { std::string m{ "▄▀▄▀▄▀▄▀" }; }; + + constexpr int n {4}; + alignas(alignof(S)) char out[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last {first + n}; + + std::ranges::uninitialized_value_construct(first, last); + + auto count {1}; + for (auto it {first}; it != last; ++it) { + std::cout << count++ << ' ' << it->m << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_value_construct + // zero-fills the given uninitialized memory area. + int v[] { 0, 1, 2, 3 }; + std::cout << ' '; + for (const int i : v) { std::cout << ' ' << static_cast(i + 'A'); } + std::cout << "\n "; + std::ranges::uninitialized_value_construct(std::begin(v), std::end(v)); + for (const int i : v) { std::cout << ' ' << static_cast(i + 'A'); } + std::cout << '\n'; +} diff --git a/cpp_20/011_memory_ranges_uninitialized_value_construct_n.cpp b/cpp_20/011_memory_ranges_uninitialized_value_construct_n.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3beef01034c529f2c0f0a77ba84ffa8d762e2fab --- /dev/null +++ b/cpp_20/011_memory_ranges_uninitialized_value_construct_n.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +int main() +{ + struct S { std::string m{ "█▓▒░ █▓▒░ █▓▒░ " }; }; + + constexpr int n {4}; + alignas(alignof(S)) char out[n * sizeof(S)]; + + try + { + auto first {reinterpret_cast(out)}; + auto last = std::ranges::uninitialized_value_construct_n(first, n); + + auto count {1}; + for (auto it {first}; it != last; ++it) { + std::cout << count++ << ' ' << it->m << '\n'; + } + + std::ranges::destroy(first, last); + } + catch(...) + { + std::cout << "Exception!\n"; + } + + // Notice that for "trivial types" the uninitialized_value_construct_n + // zero-initializes the given uninitialized memory area. + int v[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::cout << ' '; + for (const int i : v) { std::cout << i << ' '; } + std::cout << "\n "; + std::ranges::uninitialized_value_construct_n(std::begin(v), std::size(v)); + for (const int i : v) { std::cout << i << ' '; } + std::cout << '\n'; +} diff --git a/cpp_20/011_memory_to_address.cpp b/cpp_20/011_memory_to_address.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad359e4ea2429f325f9738f92464bf596f52d486 --- /dev/null +++ b/cpp_20/011_memory_to_address.cpp @@ -0,0 +1,95 @@ +template +class P1 { +public: + explicit P1(T* p) + : p_(p) { } + + T* operator->() const noexcept { + return p_; + } + +private: + T* p_; +}; + +template +class P2 { +public: + explicit P2(T* p) + : p_(p) { } + + P1 operator->() const noexcept { + return p_; + } + +private: + P1 p_; +}; + +template +class P3 { +public: + explicit P3(T* p) + : p_(p) { } + + T* get() const noexcept { + return p_; + } + +private: + T* p_; +}; + +namespace std { + + template + struct pointer_traits > { + static T* to_address(const P3& p) noexcept { + return p.get(); + } + }; +} // std + +template +class P4 { +public: + explicit P4(T* p) + : p_(p) { } + + T* operator->() const noexcept { + return nullptr; + } + + T* get() const noexcept { + return p_; + } + +private: + int* p_; +}; + +namespace std { + + template + struct pointer_traits > { + static T* to_address(const P4& p) noexcept { + return p.get(); + } + }; + +} // std + +void test_to_address02() { + int i = 0; + assert(std::to_address(&i) == &i); + int* p = &i; + assert(std::to_address(p) == &i); + P1 p1(&i); + assert(std::to_address(p1) == &i); + P2 p2(&i); + assert(std::to_address(p2) == &i); + P3 p3(&i); + assert(std::to_address(p3) == &i); + P4 p4(&i); + assert(std::to_address(p4) == &i); +} diff --git a/cpp_20/011_memory_uninitialized_construct_using_allocator.cpp b/cpp_20/011_memory_uninitialized_construct_using_allocator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/011_memory_uninitialized_construct_using_allocator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/011_memory_uses_allocator_construction_args.cpp b/cpp_20/011_memory_uses_allocator_construction_args.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b81c6c420e9e8022276b22496fe358ad4136208a --- /dev/null +++ b/cpp_20/011_memory_uses_allocator_construction_args.cpp @@ -0,0 +1,3 @@ +return std::uses_allocator_construction_args(alloc, + std::piecewise_construct, std::tuple<>{}, std::tuple<>{} +); diff --git a/cpp_20/012_chrono_ambiguous_local_time.cpp b/cpp_20/012_chrono_ambiguous_local_time.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_ambiguous_local_time.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_choose.cpp b/cpp_20/012_chrono_choose.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_choose.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_clock_cast.cpp b/cpp_20/012_chrono_clock_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_clock_cast.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_clock_time_conversion.cpp b/cpp_20/012_chrono_clock_time_conversion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_clock_time_conversion.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_current_zone.cpp b/cpp_20/012_chrono_current_zone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_current_zone.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_day.cpp b/cpp_20/012_chrono_day.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_day.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_file_clock.cpp b/cpp_20/012_chrono_file_clock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_file_clock.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_get_tzdb.cpp b/cpp_20/012_chrono_get_tzdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_get_tzdb.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_get_tzdb_list.cpp b/cpp_20/012_chrono_get_tzdb_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_get_tzdb_list.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_gps_clock.cpp b/cpp_20/012_chrono_gps_clock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_gps_clock.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_is_am.cpp b/cpp_20/012_chrono_is_am.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_is_am.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_is_clock.cpp b/cpp_20/012_chrono_is_clock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fb3deb25fe714edcd32364fccd6d307399eb50a --- /dev/null +++ b/cpp_20/012_chrono_is_clock.cpp @@ -0,0 +1,15 @@ + +template +struct is_clock : std::false_type {}; + +template + requires + requires { + typename T::rep; + typename T::period; + typename T::duration; + typename T::time_point; + T::is_steady; + T::now(); + } +struct is_clock : std::true_type {}; diff --git a/cpp_20/012_chrono_is_clock_v.cpp b/cpp_20/012_chrono_is_clock_v.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_is_clock_v.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_is_pm.cpp b/cpp_20/012_chrono_is_pm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_is_pm.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_last_spec.cpp b/cpp_20/012_chrono_last_spec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_last_spec.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_leap_second.cpp b/cpp_20/012_chrono_leap_second.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_leap_second.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_local_info.cpp b/cpp_20/012_chrono_local_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_local_info.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_local_t.cpp b/cpp_20/012_chrono_local_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_local_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_locate_zone.cpp b/cpp_20/012_chrono_locate_zone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_locate_zone.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_make12.cpp b/cpp_20/012_chrono_make12.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_make12.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_make24.cpp b/cpp_20/012_chrono_make24.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_make24.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_month.cpp b/cpp_20/012_chrono_month.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_month.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_month_day.cpp b/cpp_20/012_chrono_month_day.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_month_day.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_month_day_last.cpp b/cpp_20/012_chrono_month_day_last.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_month_day_last.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_month_weekday.cpp b/cpp_20/012_chrono_month_weekday.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_month_weekday.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_month_weekday_last.cpp b/cpp_20/012_chrono_month_weekday_last.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_month_weekday_last.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_nonexistent_local_time.cpp b/cpp_20/012_chrono_nonexistent_local_time.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_nonexistent_local_time.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_operator.cpp b/cpp_20/012_chrono_operator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_operator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_parse.cpp b/cpp_20/012_chrono_parse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_parse.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_reload_tzdb.cpp b/cpp_20/012_chrono_reload_tzdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_reload_tzdb.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_remote_version.cpp b/cpp_20/012_chrono_remote_version.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_remote_version.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_sys_info.cpp b/cpp_20/012_chrono_sys_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_sys_info.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_tai_clock.cpp b/cpp_20/012_chrono_tai_clock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_tai_clock.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_time_of_day.cpp b/cpp_20/012_chrono_time_of_day.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_time_of_day.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_time_zone.cpp b/cpp_20/012_chrono_time_zone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_time_zone.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_time_zone_link.cpp b/cpp_20/012_chrono_time_zone_link.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_time_zone_link.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_tzdb.cpp b/cpp_20/012_chrono_tzdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_tzdb.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_tzdb_list.cpp b/cpp_20/012_chrono_tzdb_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_tzdb_list.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_utc_clock.cpp b/cpp_20/012_chrono_utc_clock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_utc_clock.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_weekday.cpp b/cpp_20/012_chrono_weekday.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_weekday.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_weekday_indexed.cpp b/cpp_20/012_chrono_weekday_indexed.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_weekday_indexed.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_weekday_last.cpp b/cpp_20/012_chrono_weekday_last.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_weekday_last.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year.cpp b/cpp_20/012_chrono_year.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year_month.cpp b/cpp_20/012_chrono_year_month.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year_month.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year_month_day.cpp b/cpp_20/012_chrono_year_month_day.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year_month_day.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year_month_day_last.cpp b/cpp_20/012_chrono_year_month_day_last.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year_month_day_last.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year_month_weekday.cpp b/cpp_20/012_chrono_year_month_weekday.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year_month_weekday.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_year_month_weekday_last.cpp b/cpp_20/012_chrono_year_month_weekday_last.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_year_month_weekday_last.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_zoned_time.cpp b/cpp_20/012_chrono_zoned_time.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_zoned_time.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/012_chrono_zoned_traits.cpp b/cpp_20/012_chrono_zoned_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/012_chrono_zoned_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/013_string_ends_with.cpp b/cpp_20/013_string_ends_with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3e2b364519e82fdcdfc2d865efdb333faaad2350 --- /dev/null +++ b/cpp_20/013_string_ends_with.cpp @@ -0,0 +1,24 @@ +#include +#include +using namespace std; + +int startsWith(string s, string sub){ + return s.find(sub)==0?1:0; +} + +int endsWith(string s,string sub){ + return s.rfind(sub)==(s.length()-sub.length())?1:0; +} + +int main(){ + string str = "helloWorld"; + string preStr = "he"; + string sufStr = "rld"; + if(startsWith(str,preStr)){ + cout< +#include +using namespace std; + +int startsWith(string s, string sub){ + return s.find(sub)==0?1:0; +} + +int endsWith(string s,string sub){ + return s.rfind(sub)==(s.length()-sub.length())?1:0; +} + +int main(){ + string str = "helloWorld"; + string preStr = "he"; + string sufStr = "rld"; + if(startsWith(str,preStr)){ + cout< +std::array to_array(const V& v) +{ + assert(v.size() == N); + std::array d; + std::copy(v.begin(), v.end(), d.data()); + return d; +} diff --git a/cpp_20/017_vector_erase.cpp b/cpp_20/017_vector_erase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c77aaf05314b93bcba3bbf3033f5da29200cd253 --- /dev/null +++ b/cpp_20/017_vector_erase.cpp @@ -0,0 +1,25 @@ +#include +#include +using namespace std; + +// Function to demo erase() +void eraseDemo(string str) +{ + // Deletes all characters + str.erase(); + + cout << "After erase():"; + cout << str; +} + +// Driver code +int main() +{ + string str("Hello World!"); + + cout << "Before erase():"; + cout << str << endl; + eraseDemo(str); + + return 0; +} diff --git a/cpp_20/017_vector_erase_if.cpp b/cpp_20/017_vector_erase_if.cpp new file mode 100644 index 0000000000000000000000000000000000000000..685e12de4f555b4be32b729bbca4057dace52086 --- /dev/null +++ b/cpp_20/017_vector_erase_if.cpp @@ -0,0 +1,27 @@ +#include +#include + +template +inline Os& operator<<(Os& os, Container const& cont) +{ + os << "{"; + for (const auto& item : cont) { + os << "{" << item.first << ", " << item.second << "}"; + } + return os << "}"; +} + +int main() +{ + std::map data {{1, 'a'},{2, 'b'},{3, 'c'},{4, 'd'}, + {5, 'e'},{4, 'f'},{5, 'g'},{5, 'g'}}; + std::cout << "Original:\n" << data << '\n'; + + const auto count = std::erase_if(data, [](const auto& item) { + auto const& [key, value] = item; + return (key & 1) == 1; + }); + + std::cout << "Erase items with odd keys:\n" << data << '\n' + << count << " items removed.\n"; +} diff --git a/cpp_20/018_map_contains.cpp b/cpp_20/018_map_contains.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f67c5023426ebda827b6fbdc3ad76381b24230b --- /dev/null +++ b/cpp_20/018_map_contains.cpp @@ -0,0 +1,13 @@ +#include +#include + +int main() +{ + std::set example = {1, 2, 3, 4}; + + if (example.contains(2)) { + std::cout << "Found\n"; + } else { + std::cout << "Not found\n"; + } +} diff --git a/cpp_20/018_map_erase_if.cpp b/cpp_20/018_map_erase_if.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/018_map_erase_if.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/019_unordered_map_contains.cpp b/cpp_20/019_unordered_map_contains.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/019_unordered_map_contains.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/019_unordered_map_erase_if.cpp b/cpp_20/019_unordered_map_erase_if.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc59b502234e0b9779cb712e17a8f0445e5dccb9 --- /dev/null +++ b/cpp_20/019_unordered_map_erase_if.cpp @@ -0,0 +1,9 @@ +auto old_size = c.size(); +for (auto i = c.begin(), last = c.end(); i != last; ) { + if (pred(*i)) { + i = c.erase(i); + } else { + ++i; + } +} +return old_size - c.size(); diff --git a/cpp_20/020_span_back.cpp b/cpp_20/020_span_back.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fc5e16ea92d63920afc87e4d004f45d04c2ad96 --- /dev/null +++ b/cpp_20/020_span_back.cpp @@ -0,0 +1,19 @@ +#include // std::cout +#include // std::back_inserter +#include // std::vector +#include // std::copy + +int main () { + std::vector foo,bar; + for (int i=1; i<=5; i++) + { foo.push_back(i); bar.push_back(i*10); } + + std::copy (bar.begin(),bar.end(),back_inserter(foo)); + + std::cout << "foo contains:"; + for ( std::vector::iterator it = foo.begin(); it!= foo.end(); ++it ) + std::cout << ' ' << *it; + std::cout << '\n'; + + return 0; +} diff --git a/cpp_20/020_span_begin.cpp b/cpp_20/020_span_begin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f2032a574d1883536dfb15c6232280f7e1406d6 --- /dev/null +++ b/cpp_20/020_span_begin.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + auto vi = std::begin(v); + std::cout << *vi << '\n'; + + int a[] = { -5, 10, 15 }; + auto ai = std::begin(a); + std::cout << *ai << '\n'; +} diff --git a/cpp_20/020_span_dynamic_extent.cpp b/cpp_20/020_span_dynamic_extent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2430f6c27e4309b13578c094bb7006fb7c898552 --- /dev/null +++ b/cpp_20/020_span_dynamic_extent.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + auto print = [](std::string_view const name, std::size_t ex) { + std::cout << name << ", "; + if (ex == std::dynamic_extent) { + std::cout << "dynamic extent\n"; + } else { + std::cout << "static extent = " << ex << '\n'; + } + }; + + int a[]{1,2,3,4,5}; + + std::span span1{a}; + print("span1", span1.extent); + + std::span span2{a}; + print("span2", span2.extent); + + std::array ar{1,2,3,4,5}; + std::span span3{ar}; + print("span3", span3.extent); + + std::vector v{1,2,3,4,5}; + std::span span4{v}; + print("span4", span4.extent); +} diff --git a/cpp_20/020_span_end.cpp b/cpp_20/020_span_end.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ed034a2d27d11dc072f73718418b15f68e06869 --- /dev/null +++ b/cpp_20/020_span_end.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + if (std::find(std::begin(v), std::end(v), 5) != std::end(v)) { + std::cout << "found a 5 in vector v!\n"; + } + + int a[] = { 5, 10, 15 }; + if (std::find(std::begin(a), std::end(a), 5) != std::end(a)) { + std::cout << "found a 5 in array a!\n"; + } +} diff --git a/cpp_20/020_span_front.cpp b/cpp_20/020_span_front.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c59f5fff0dcaad4641934e9b677f1cf3f0ec499 --- /dev/null +++ b/cpp_20/020_span_front.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +int main() +{ + std::vector v{1,2,3,4,5}; + std::deque d; + std::copy(v.begin(), v.end(), + std::front_insert_iterator>(d)); // or std::front_inserter(d) + for(int n : d) + std::cout << n << ' '; + std::cout << '\n'; +} diff --git a/cpp_20/020_span_rbegin.cpp b/cpp_20/020_span_rbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e9c833b1814d885eb35af6a983f5bb7220d669e --- /dev/null +++ b/cpp_20/020_span_rbegin.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::vector v = { 3, 1, 4 }; + auto vi = std::rbegin(v); + std::cout << *vi << '\n'; + + int a[] = { -5, 10, 15 }; + auto ai = std::rbegin(a); + std::cout << *ai << '\n'; +} diff --git a/cpp_20/020_span_rend.cpp b/cpp_20/020_span_rend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f992ceea77f197cf6a7c15940401aa8734d0ba2d --- /dev/null +++ b/cpp_20/020_span_rend.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int main() +{ + int a[] = {4, 6, -3, 9, 10}; + std::cout << "Array backwards: "; + std::copy(std::rbegin(a), std::rend(a), std::ostream_iterator(std::cout, " ")); + + std::cout << "\nVector backwards: "; + std::vector v = {4, 6, -3, 9, 10}; + std::copy(std::rbegin(v), std::rend(v), std::ostream_iterator(std::cout, " ")); +} diff --git a/cpp_20/021_std_bidirectional_iterator.cpp b/cpp_20/021_std_bidirectional_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_bidirectional_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_bidirectional_iterator_tag.cpp b/cpp_20/021_std_bidirectional_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_bidirectional_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_contiguous_iterator.cpp b/cpp_20/021_std_contiguous_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_contiguous_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_contiguous_iterator_tag.cpp b/cpp_20/021_std_contiguous_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_contiguous_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_forward_iterator.cpp b/cpp_20/021_std_forward_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_forward_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_forward_iterator_tag.cpp b/cpp_20/021_std_forward_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_forward_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_incrementable.cpp b/cpp_20/021_std_incrementable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_incrementable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_indirectly_readable.cpp b/cpp_20/021_std_indirectly_readable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_indirectly_readable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_indirectly_readable_traits.cpp b/cpp_20/021_std_indirectly_readable_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_indirectly_readable_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_indirectly_writable.cpp b/cpp_20/021_std_indirectly_writable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_indirectly_writable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_input_iterator.cpp b/cpp_20/021_std_input_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_input_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_input_iterator_tag.cpp b/cpp_20/021_std_input_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_input_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_input_or_output_iterator.cpp b/cpp_20/021_std_input_or_output_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_input_or_output_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_iter_common_reference_t.cpp b/cpp_20/021_std_iter_common_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_iter_common_reference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_iter_difference_t.cpp b/cpp_20/021_std_iter_difference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_iter_difference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_iter_reference_t.cpp b/cpp_20/021_std_iter_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48c544837de0368385fe795708eaae6c86ae90a1 --- /dev/null +++ b/cpp_20/021_std_iter_reference_t.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + std::list l(10); + + std::iota(l.begin(), l.end(), -4); + std::vector> v(l.begin(), l.end()); + + // can't use shuffle on a list (requires random access), but can use it on a vector + std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()}); + + std::cout << "Contents of the list: "; + for (int n : l){ + std::cout << n << ' '; + } + + std::cout << "\nContents of the list, as seen through a shuffled vector: "; + for (int i : v){ + std::cout << i << ' '; + } + + std::cout << "\n\nDoubling the values in the initial list...\n\n"; + for (int& i : l) { + i *= 2; + } + + std::cout << "Contents of the list, as seen through a shuffled vector: "; + for (int i : v){ + std::cout << i << ' '; + } +} diff --git a/cpp_20/021_std_iter_rvalue_reference_t.cpp b/cpp_20/021_std_iter_rvalue_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_iter_rvalue_reference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_iter_value_t.cpp b/cpp_20/021_std_iter_value_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_iter_value_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_iterator_traits.cpp b/cpp_20/021_std_iterator_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_iterator_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_output_iterator.cpp b/cpp_20/021_std_output_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_output_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_output_iterator_tag.cpp b/cpp_20/021_std_output_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_output_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_random_access_iterator.cpp b/cpp_20/021_std_random_access_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_random_access_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_random_access_iterator_tag.cpp b/cpp_20/021_std_random_access_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_random_access_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_sentinel_for.cpp b/cpp_20/021_std_sentinel_for.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_sentinel_for.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_sized_sentinel_for.cpp b/cpp_20/021_std_sized_sentinel_for.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_sized_sentinel_for.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/021_std_weakly_incrementable.cpp b/cpp_20/021_std_weakly_incrementable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/021_std_weakly_incrementable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/022_ranges_iter_move.cpp b/cpp_20/022_ranges_iter_move.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/022_ranges_iter_move.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/022_ranges_iter_swap.cpp b/cpp_20/022_ranges_iter_swap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/022_ranges_iter_swap.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_bidirectional_iterator.cpp b/cpp_20/023_std_bidirectional_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_bidirectional_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_bidirectional_iterator_tag.cpp b/cpp_20/023_std_bidirectional_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_bidirectional_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_contiguous_iterator.cpp b/cpp_20/023_std_contiguous_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_contiguous_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_contiguous_iterator_tag.cpp b/cpp_20/023_std_contiguous_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_contiguous_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_forward_iterator.cpp b/cpp_20/023_std_forward_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_forward_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_forward_iterator_tag.cpp b/cpp_20/023_std_forward_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_forward_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_incrementable.cpp b/cpp_20/023_std_incrementable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_incrementable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_incrementable_traits.cpp b/cpp_20/023_std_incrementable_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_incrementable_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_indirectly_readable.cpp b/cpp_20/023_std_indirectly_readable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_indirectly_readable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_indirectly_readable_traits.cpp b/cpp_20/023_std_indirectly_readable_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_indirectly_readable_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_indirectly_writable.cpp b/cpp_20/023_std_indirectly_writable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_indirectly_writable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_input_iterator.cpp b/cpp_20/023_std_input_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_input_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_input_iterator_tag.cpp b/cpp_20/023_std_input_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_input_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_input_or_output_iterator.cpp b/cpp_20/023_std_input_or_output_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_input_or_output_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iter_common_reference_t.cpp b/cpp_20/023_std_iter_common_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iter_common_reference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iter_difference_t.cpp b/cpp_20/023_std_iter_difference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iter_difference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iter_reference_t.cpp b/cpp_20/023_std_iter_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iter_reference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iter_rvalue_reference_t.cpp b/cpp_20/023_std_iter_rvalue_reference_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iter_rvalue_reference_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iter_value_t.cpp b/cpp_20/023_std_iter_value_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iter_value_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_iterator_traits.cpp b/cpp_20/023_std_iterator_traits.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_iterator_traits.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_output_iterator.cpp b/cpp_20/023_std_output_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_output_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_output_iterator_tag.cpp b/cpp_20/023_std_output_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_output_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_random_access_iterator.cpp b/cpp_20/023_std_random_access_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_random_access_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_random_access_iterator_tag.cpp b/cpp_20/023_std_random_access_iterator_tag.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_random_access_iterator_tag.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_sentinel_for.cpp b/cpp_20/023_std_sentinel_for.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_sentinel_for.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_sized_sentinel_for.cpp b/cpp_20/023_std_sized_sentinel_for.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_sized_sentinel_for.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/023_std_weakly_incrementable.cpp b/cpp_20/023_std_weakly_incrementable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/023_std_weakly_incrementable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_common_iterator.cpp b/cpp_20/024_iterator_common_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_common_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_counted_iterator.cpp b/cpp_20/024_iterator_counted_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_counted_iterator.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_default_sentinel_t.cpp b/cpp_20/024_iterator_default_sentinel_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_default_sentinel_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirect_binary_predicate.cpp b/cpp_20/024_iterator_indirect_binary_predicate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirect_binary_predicate.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirect_equivalence_relation.cpp b/cpp_20/024_iterator_indirect_equivalence_relation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirect_equivalence_relation.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirect_result_t.cpp b/cpp_20/024_iterator_indirect_result_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirect_result_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirect_strict_weak_order.cpp b/cpp_20/024_iterator_indirect_strict_weak_order.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirect_strict_weak_order.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirect_unary_predicate.cpp b/cpp_20/024_iterator_indirect_unary_predicate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirect_unary_predicate.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_comparable.cpp b/cpp_20/024_iterator_indirectly_comparable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_comparable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_copyable.cpp b/cpp_20/024_iterator_indirectly_copyable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_copyable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_copyable_storable.cpp b/cpp_20/024_iterator_indirectly_copyable_storable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_copyable_storable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_movable.cpp b/cpp_20/024_iterator_indirectly_movable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_movable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_movable_storable.cpp b/cpp_20/024_iterator_indirectly_movable_storable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_movable_storable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_regular_unary_invocable.cpp b/cpp_20/024_iterator_indirectly_regular_unary_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_regular_unary_invocable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_swappable.cpp b/cpp_20/024_iterator_indirectly_swappable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_swappable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_indirectly_unary_invocable.cpp b/cpp_20/024_iterator_indirectly_unary_invocable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_indirectly_unary_invocable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_mergeable.cpp b/cpp_20/024_iterator_mergeable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_mergeable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_move_sentinel.cpp b/cpp_20/024_iterator_move_sentinel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_move_sentinel.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_permutable.cpp b/cpp_20/024_iterator_permutable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_permutable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_projected.cpp b/cpp_20/024_iterator_projected.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_projected.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_sortable.cpp b/cpp_20/024_iterator_sortable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_sortable.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/024_iterator_unreachable_sentinel_t.cpp b/cpp_20/024_iterator_unreachable_sentinel_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/024_iterator_unreachable_sentinel_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/025_iterator_ranges_advanc.cpp b/cpp_20/025_iterator_ranges_advanc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/025_iterator_ranges_advanc.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/025_iterator_ranges_distance.cpp b/cpp_20/025_iterator_ranges_distance.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/025_iterator_ranges_distance.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/025_iterator_ranges_next.cpp b/cpp_20/025_iterator_ranges_next.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/025_iterator_ranges_next.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/025_iterator_ranges_prev.cpp b/cpp_20/025_iterator_ranges_prev.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/025_iterator_ranges_prev.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/026_std_ssize.cpp b/cpp_20/026_std_ssize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/026_std_ssize.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_begin.cpp b/cpp_20/027_ranges_ranges_begin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_begin.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_cbegin.cpp b/cpp_20/027_ranges_ranges_cbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_cbegin.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_cdata.cpp b/cpp_20/027_ranges_ranges_cdata.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_cdata.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_cend.cpp b/cpp_20/027_ranges_ranges_cend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_cend.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_crbegin.cpp b/cpp_20/027_ranges_ranges_crbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_crbegin.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_crend.cpp b/cpp_20/027_ranges_ranges_crend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_crend.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_data.cpp b/cpp_20/027_ranges_ranges_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_data.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_empty.cpp b/cpp_20/027_ranges_ranges_empty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_empty.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_end.cpp b/cpp_20/027_ranges_ranges_end.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_end.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_rbegin.cpp b/cpp_20/027_ranges_ranges_rbegin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_rbegin.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_rend.cpp b/cpp_20/027_ranges_ranges_rend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_rend.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_size.cpp b/cpp_20/027_ranges_ranges_size.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_size.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/027_ranges_ranges_ssize.cpp b/cpp_20/027_ranges_ranges_ssize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_20/027_ranges_ranges_ssize.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_20/README.md b/cpp_20/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4e25015bad427f172e3ab53d8abc9eb31081aae0 --- /dev/null +++ b/cpp_20/README.md @@ -0,0 +1,10 @@ +##

C++20新特性

+ +C++20思维导图正在更新中... +
+敬请期待! + + +----------- diff --git a/cpp_23/001_rtti_is_scoped_enum.cpp b/cpp_23/001_rtti_is_scoped_enum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80ed94d99746b438a481d8df4adb9cae375d26f7 --- /dev/null +++ b/cpp_23/001_rtti_is_scoped_enum.cpp @@ -0,0 +1,20 @@ +#include +#include + +class A {}; + +enum E {}; + +enum struct Es { oz }; + +enum class Ec : int {}; + +int main() +{ + std::cout << std::boolalpha; + std::cout << std::is_scoped_enum_v
<< '\n'; + std::cout << std::is_scoped_enum_v << '\n'; + std::cout << std::is_scoped_enum_v << '\n'; + std::cout << std::is_scoped_enum_v << '\n'; + std::cout << std::is_scoped_enum_v << '\n'; +} diff --git a/cpp_23/002_utility_to_underlying.cpp b/cpp_23/002_utility_to_underlying.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a2be74a1d6e1fcd5dbe74598024ebf8f21966d3 --- /dev/null +++ b/cpp_23/002_utility_to_underlying.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +int main() +{ + enum class E1 : char { e }; + static_assert(std::is_same_v); + enum struct E2 : long { e }; + static_assert(std::is_same_v); + enum E3 : unsigned { e }; + static_assert(std::is_same_v); + + enum class ColorMask : std::uint32_t { + red = 0xFF, green = (red << 8), blue = (green << 8), alpha = (blue << 8) + }; + std::cout << std::hex << std::uppercase << std::setfill('0') + << std::setw(8) << std::to_underlying(ColorMask::red) << '\n' + << std::setw(8) << std::to_underlying(ColorMask::green) << '\n' + << std::setw(8) << std::to_underlying(ColorMask::blue) << '\n' + << std::setw(8) << std::to_underlying(ColorMask::alpha) << '\n'; + +// std::underlying_type_t x = ColorMask::alpha; // Error: no known conversion + [[maybe_unused]] + std::underlying_type_t y = std::to_underlying(ColorMask::alpha); // OK +} diff --git a/cpp_23/003_stacktrace_basic_stacktrace.cpp b/cpp_23/003_stacktrace_basic_stacktrace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6686db04214d490f93e11423f479e0b532936bda --- /dev/null +++ b/cpp_23/003_stacktrace_basic_stacktrace.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main() +{ + std::cout << std::boolalpha; + std::stacktrace bktr; + std::cout << "Initially, bktr.empty(): " << bktr.empty() << '\n'; + + bktr = std::stacktrace::current(); + std::cout << "After getting entries, bktr.empty(): " << bktr.empty() << '\n'; +} diff --git a/cpp_23/003_stacktrace_stacktrace_entry.cpp b/cpp_23/003_stacktrace_stacktrace_entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ff022d5032914ec5351a90890d7ec3cef5e32eb --- /dev/null +++ b/cpp_23/003_stacktrace_stacktrace_entry.cpp @@ -0,0 +1,52 @@ +#include +#include +#include + +void log(const std::string_view message, + const std::source_location location = + std::source_location::current()) +{ + std::cout << "file: " + << location.file_name() << "(" + << location.line() << ":" + << location.column() << ") `" + << location.function_name() << "`: " + << message << '\n'; +} + +template void fun(T x) +{ + log(x); +} + +int main(int, char*[]) +{ + log("Hello world!"); + fun("Hello C++20!"); +} +#include +#include +#include + +void log(const std::string_view message, + const std::source_location location = + std::source_location::current()) +{ + std::cout << "file: " + << location.file_name() << "(" + << location.line() << ":" + << location.column() << ") `" + << location.function_name() << "`: " + << message << '\n'; +} + +template void fun(T x) +{ + log(x); +} + +int main(int, char*[]) +{ + log("Hello world!"); + fun("Hello C++20!"); +} diff --git a/cpp_23/004_memory_allocate_at_least.cpp b/cpp_23/004_memory_allocate_at_least.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_allocate_at_least.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/004_memory_allocation_result.cpp b/cpp_23/004_memory_allocation_result.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_allocation_result.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/004_memory_inout_ptr.cpp b/cpp_23/004_memory_inout_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_inout_ptr.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/004_memory_inout_ptr_t.cpp b/cpp_23/004_memory_inout_ptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_inout_ptr_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/004_memory_out_ptr.cpp b/cpp_23/004_memory_out_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_out_ptr.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/004_memory_out_ptr_t.cpp b/cpp_23/004_memory_out_ptr_t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/004_memory_out_ptr_t.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/005_string_contains.cpp b/cpp_23/005_string_contains.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/005_string_contains.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/005_string_view_contains.cpp b/cpp_23/005_string_view_contains.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/cpp_23/005_string_view_contains.cpp @@ -0,0 +1 @@ +1 diff --git a/cpp_23/README.md b/cpp_23/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8216345d0f9f2c6662de78a6de3a07ce2bd75edc --- /dev/null +++ b/cpp_23/README.md @@ -0,0 +1,10 @@ +##

C++23新特性

+ +C++23思维导图正在更新中... +
+敬请期待! + + +----------- diff --git "a/\343\200\214Notes\343\200\215C++14\346\226\260\347\211\271\346\200\247\346\265\205\350\260\210.md" "b/\343\200\214Notes\343\200\215C++14\346\226\260\347\211\271\346\200\247\346\265\205\350\260\210.md" new file mode 100644 index 0000000000000000000000000000000000000000..056419ca66dc1ec799b4bd2b0aa2299ba329eb04 --- /dev/null +++ "b/\343\200\214Notes\343\200\215C++14\346\226\260\347\211\271\346\200\247\346\265\205\350\260\210.md" @@ -0,0 +1,374 @@ +> 原文链接:https://liuxizai.ac.cn/post/notes-cpp14/ + +基于 Ubuntu 20.04 的 NOI 2.0 发布后,我们或许有机会开始使用 C++14。 + +这篇 Blog 将对 OI 中可能会使用到的 C++14 的新特性进行简要总结。 + +> 由于大部分 OIer 被 CCF 迫害可能是 C++98 转 C++14,文中部分特性实际上是来源于 C++11 标准的。 +> +> 另一个重要原因是,C++14 并不是 C++ 的一个主要版本(主要版本:C++03 C++11 C++17),其被认为是 C++11 一个更加完善的版本,C++11 这一更新经历了整整8年时间,引入了大量更改,可以说是 C++98 以来最重要的更新,所以提到 C++11 的特性确实很有必要。 + +这篇 Blog 以简洁易懂为主要目标,为此可能有些地方会在不影响理解的情况下使用一些不准确的表达,这些地方往往都有脚注,可以通过我给出的链接查看更详细的内容。 + +## constexpr + +C++ 中一直存在着常量表达式的概念,在 C++98 中我们可以这样定义一个常量 + +```cpp +const int SIZE = 100005; +``` + +常量将无法修改,并且这也是编译器的一个优化机会,编译器往往会在编译时就处理好常量表达式的值,并在程序中硬编码结果,也就是不会在程序运行时再去计算表达式的值。 + +```cpp +const int MOD = 1e9 + 7; + +// source code +ans = ans % MOD; + +// after compilation +ans = ans % 1000000007; // yes, 1e9 + 7 has been calculated +ans = ans % (1e9 + 7); // no +``` + +另外,常量可以用来初始化数组 + +```cpp +int len = 10; +int a[len]; // compile error + +const int len = 10; +int a[len]; // ok +``` + +C++11 中的 `constexpr` 关键字更进一步放宽了常量表达式的限制 + +```cpp +const int f() { return 10; } +int a[f()+5]; // compile error + +constexpr int f() { return 10; } +int a[f()+5]; // ok +``` + +你可能已经发现了,C++11 允许将函数作为常量表达式,但是 C++11 要求函数必须恰由一条 `return` 语句组成,而 C++14 解除了这一限制,但你仍需要保证函数中没有: + +- `goto` 语句 + +- 未进行初始化的变量定义 + + ```cpp + int a; // no + int a = 10; // yes + ``` + +- 非字面类型的变量定义(算术类型如 `int` 属于字面类型,而自定义类型如 `string` 属于非字面类型) + +为了方便 OIer 理解,以上内容并不完全准确。 + +## lambda + +C++11 中允许使用匿名函数,其能够内联于语句中 + +```cpp +struct node { int x, y; }; +std::vector arr; + +// C++98 +bool cmp(node a, node b) { return a.x < b.x; } +std::sort(arr.begin(), arr.end(), cmp); + +// C++11 +std::sort(arr.begin(), arr.end(), [](node a, node b){ return a.x < b.x; }); +``` + +两种写法效果都是一样的。 + +具体地说,`lambda` 表达式的语法为 + +```cpp +[捕获](形参){函数体} +``` + +其中需要具体讲解的是捕获这一部分。 + +捕获分为这样几个类型: + +- `[]` - 空捕获列表,`lambda` 表达式只能够使用非局部变量。 +- `[names]` - `names` 是一个逗号分割的名字列表,这些名字为匿名函数所在的局部变量,这些局部变量将被拷贝(也就是说在函数中修改其值后并不会影响到其本身),如果 `name` 前面使用了 `&`,将会使用引用的方式捕获。 +- `[&]` - 隐式的以引用方式捕获所有匿名函数使用的局部变量。 +- `[=]` - 隐式的以值方式(即拷贝)捕获所有匿名函数使用的局部变量。 +- `[&, list]` - `list` 是一个逗号分割的列表,列表中的变量以值方式捕获,其他局部变量隐式的以引用方式捕获。 +- `[=, list]` - `list` 是一个逗号分割的列表,列表中的变量以引用方式捕获,其他局部变量隐式的以值方式捕获。 + +C++14 标准中规定了泛型 `lambda`,由于过于复杂,选择不将其写入 Blog。 + +另外,你会发现匿名函数没有规定返回值,编译器将会自行判断函数的返回值,如果需要指定函数返回值,可以使用以下语法 + +```cpp +[捕获](形参)->返回值类型 {函数体} +``` + +## 变量模板(variable template) + +C++14 允许通过变量模板定义一族变量。 + +```cpp +template // variable template +const T pi = T(3.14159265); + +template // function template +T circleArea(T r){ + return pi * r * r; // variable template instantiation +} +``` + +## 聚合初始化(aggregate initialization) + +聚合初始化是 C++11 中列表初始化的一种形式。 + +首先,聚合体是下列类型之一: + +- 数组类型 +- 满足一下条件的类类型(常为struct) + - 没有私有或受保护的非静态数据成员(在类中声明的非 `static` 数据成员) + - 没有用户提供的构造函数 + - 没有虚成员函数 + +你可以像这样进行聚合初始化 + +```cpp +struct node{ + int a, b; + int c[3]; + int d; +}; +node nd = {2, 3, 5, 6, 3, 4}; +``` + +这样初始化过后 + +```cpp +a = 2; +b = 3; +c = {5, 6, 3}; +d = 4; +``` + +可以发现聚合初始化是按照地址顺序依次进行的,所以对于类中的数组成员可以很方便的进行初始化,当然这也意味着聚合初始化无法直接指定一些成员进行初始化。 + +> 在 C++20 中允许进行指派初始化器的聚合初始化,即可以指定成员进行初始化 + +另一个很重要的特性,聚合初始化是递归进行的,也就是说其允许嵌套 + +```cpp +struct A{ + struct B{ + int a; + int b; + int c; + }; + B d; + int e; + vector f; +}; +``` + +这样一个结构体我们仍然可以使用聚合初始化 + +```cpp +A a = {{1, 2, 3}, 4, {5, 6}}; +``` + +初始化结果如下 + +```cpp +d.a = 1; +d.b = 2; +d.c = 3; +e = 4; +f = {5, 6}; +``` + +在 C++11 中,聚合初始化要求类成员没有默认初始化器(`int a = 10`),但在 C++14 中允许我们这么做,所以另外很重要的一点是,当聚合初始化与默认初始化器结合时,到底会产生怎么样的结果。 + +举个例子说明 + +```cpp +struct A { + struct B { + int a = 21; + int b; + int c = 22; + int d; + int e = 23; + }; + B b1 = { 11, 12 }; + B b2 = { 11, 12, 13 }; + int x; +}; +``` + +接下来进行聚合初始化 + +```cpp +A a = { { 1, 2, 3, 4 }, { 1 }, 5 }; +``` + +你会得到这样的结果 + +```cpp +b1.a = 1; +b1.b = 2; +b1.c = 3; +b1.d = 4; +b1.e = 23; +b2.a = 1; +b2.b = 0; +b2.c = 22; +b2.d = 0; +b2.e = 23; +x = 5; +``` + +你会发现,`b2`的初始化好像失效了,否则我们应该得到这样的结果 + +```cpp +b2.a = 1; +b2.b = 12; +b2.c = 13; +b2.d = 0; +b2.e = 23; +``` + +初始化器提供的值比类成员少时,根据 N3605,C++14 会采用如下策略 + +- 从成员的默认初始化器进行初始化 +- 如果没有默认初始化器,用一个空初始化器列表进行初始化 + +那么,我们在对 `a` 进行聚合初始化时, 实际上为 `b2` 提供了值 `{1}`,所以 `b2` 的初始化器**完全失效**,接下来,`b2.a` 从聚合初始化中的到了值,其他成员没有得到值,所以隐式的按照 N3605 进行初始化。 + +这正是我们得到的结果。 + +## auto + +`auto` 于 C++11 引入作为占位类型说明符,其能够从初始化器自动推导变量类型。 + +```cpp +auto a = 12; // int +auto b = 2 + 4 * 7; // int +auto c = 0.17; // double +auto d = a; // int +auto e = a + c; // double +复制代码 +``` + +C++14 还允许使用 `auto` 自动推断函数返回值类型 + +```cpp +auto f() { return 2 + 3; } // int +``` + +如下写法将会被推导为列表初始化器 + +```cpp +auto g = {1, 2, 3, 4, 5}; // std::initializer_list +auto h{1, 2, 3, 4, 5}; // std::initializer_list +``` + +> 第二种写法在 C++17 中被弃用 + +另外,`auto` 还常用于无名类型,如 `lambda` 表达式类型 + +```cpp +auto lambda = []() { return 9 + 12; } +std::cout << lambda() << std::endl; // 21 +``` + +需要注意的是,`auto` 说明符要求变量必须拥有初始化器 + +```cpp +auto x; // compile error +auto y = 10; // ok +``` + +> `auto x;` 这种写法在 C 中被允许。 + +如果想要了解更多,可以参考 cppreference。 + +## 基于范围的 for 循环(range-based for loop) + +C++11 规定了基于范围的 `for` 循环,其在一个范围上执行 `for` 循环,是传统 `for` 循环一个更加可读的等价版本,OI 中常用于图遍历。 + +其语法如下 + +```cpp +[属性-可选] +for(范围声明: 范围表达式){ + 循环语句 +} +``` + +> 属性:属性说明符序列,不在 Blog 中进行说明,几乎不会用到。 + +- 范围声明:一个具名变量的声明,类型为范围表达式中元素的类型或其引用,一般使用 `auto` 对其类型进行推导。 +- 范围表达式:一个序列(数组,或是定义了 `begin` 和 `end` 的对象,如 `vector`),或是一个花括号列表初始化器(如 `{1, 2, 3, 4, 5}`)。 +- 循环语句:常规函数体。 + +基于范围的 `for` 循环可以用这样的常规 `for` 循环替代 + +```cpp +for(auto __begin = 首表达式, __end = 尾表达式; __begin != __end; __begin++){ + 范围声明 = *__begin; + 循环语句 +} +``` + +其中,对于数组 `a[]`,其首表达式为 `a`,尾表达式为 `(a + __bound)`,`__bound` 为数组长度,我们要求数组是有确定长度的。 + +对于定义了 `begin` 和 `end` 的对象 `b`,其首表达式为 `b.begin()`,尾表达式为 `b.end()`。 + +否则,通过实参依赖查找进行查找。 + +一些实际使用的例子 + +```cpp +vector g[10005]; +for(auto v: g[u]){ + /* something here */ +} + +int a[] = {1, 2, 3, 4, 5}; +for(auto &x: a){ + x++; + std::cout << x << ' '; +} +// after - a: {2, 3, 4, 5, 6} + +for(auto x: {1, 3, 5, 7}){ + std::cout << x << ' '; +} +``` + +## 变参数模板(variadic template) + +在我看来无比实用的特性之一,你可以在我的 `template` 中找到这样一个函数 + +```cpp +void input() {} +template +void input(Type& arg, Types&... args){ + arg = read(); + input(args...); +} +``` + +这就是一个变参数模板的使用案例,你可以通过 `input()` 函数一次性对任意个变量通过快读进行读入。 + +```cpp +int x, y, z; +input(x); // ok +input(x, y, z); // ok +``` + +常用的变参数模板格式和上面大同小异,都是通过递归调用,`input(Type& arg, Types&... args)` 递归变参函数,`input(args...)` 就是在进行递归调用,我们当然需要给这样一个递归函数一个终止条件,`input()` 被称为基础函数,递归变参数函数最终在这里停止。 diff --git "a/\345\220\220\350\241\200\346\225\264\347\220\206\357\274\232C++11\346\226\260\347\211\271\346\200\247.md" "b/\345\220\220\350\241\200\346\225\264\347\220\206\357\274\232C++11\346\226\260\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..8a73e15df1b9d64637af744ee7c4b920ae58f879 --- /dev/null +++ "b/\345\220\220\350\241\200\346\225\264\347\220\206\357\274\232C++11\346\226\260\347\211\271\346\200\247.md" @@ -0,0 +1,941 @@ +### auto & decltype + +关于C++11新特性,最先提到的肯定是类型推导,C++11引入了auto和decltype关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。 + +- auto:让编译器在编译器就推导出变量的类型,可以通过=右边的类型推导出变量的类型。 + +```c++ +auto a = 10; // 10是int型,可以自动推导出a是int +``` + +- decltype:相对于auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算。 + +```c++ +cont int &i = 1;int a = 2;decltype(i) b = 2; // b是const int& +``` + +### 左值右值 + +众所周知C++11新增了右值引用,这里涉及到很多概念: + +- 左值:可以取地址并且有名字的东西就是左值。 +- 右值:不能取地址的没有名字的东西就是右值。 + +- 纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。 +- 将亡值:可以理解为即将要销毁的值。 + +- 左值引用:对左值进行引用的类型。 +- 右值引用:对右值进行引用的类型。 + +- 移动语义:转移资源所有权,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。 +- 完美转发:可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。 + +- 返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。 + +### 列表初始化 + +在C++11中可以直接在变量名后面加上初始化列表来进行对象的初始化。 + +### std::function & std::bind & lambda表达式 + +c++11新增了std::function、std::bind、lambda表达式等封装使函数调用更加方便。 + +### 模板的改进 + +C++11关于模板有一些细节的改进: + +- 模板的右尖括号 +- 模板的别名 + +- 函数模板的默认模板参数 + +### 并发 + +c++11关于并发引入了好多好东西,有: + +- std::thread相关 +- std::mutex相关 + +- std::lock相关 +- std::atomic相关 + +- std::call_once相关 +- volatile相关 + +- std::condition_variable相关 +- std::future相关 + +- async相关 + +### 智能指针 + +很多人谈到c++,说它特别难,可能有一部分就是因为c++的内存管理,不像java那样有虚拟机动态的管理内存,在程序运行过程中可能就会出现内存泄漏,然而这种问题其实都可以通过c++11引入的智能指针来解决,这种内存管理还是c++语言的优势,因为尽在掌握。 + +c++11引入了三种智能指针: + +- std::shared_ptr +- std::weak_ptr + +- std::unique_ptr + +### 基于范围的for循环 + +看代码 + +```c++ +vector vec; + +for (auto iter = vec.begin(); iter != vec.end(); iter++) { // before c++11 + cout << *iter << endl; +} + +for (int i : vec) { // c++11基于范围的for循环 + cout << "i" << endl; +} +``` + +### 委托构造函数 + +委托构造函数允许在同一个类中一个构造函数调用另外一个构造函数,可以在变量初始化时简化操作,通过代码来感受下委托构造函数的妙处: + +不使用委托构造函数: + +```c++ +struct A { + A(){} + A(int a) { a_ = a; } + + A(int a, int b) { // 好麻烦 + a_ = a; + b_ = b; + } + + A(int a, int b, int c) { // 好麻烦 + a_ = a; + b_ = b; + c_ = c; + } + + int a_; + int b_; + int c_; +}; +``` + +使用委托构造函数: + +```c++ +struct A { + A(){} + A(int a) { a_ = a; } + + A(int a, int b) : A(a) { b_ = b; } + + A(int a, int b, int c) : A(a, b) { c_ = c; } + + int a_; + int b_; + int c_; +}; +``` + +初始化变量是不是方便了许多。 + +### 继承构造函数 + +继承构造函数可以让派生类直接使用基类的构造函数,如果有一个派生类,希望派生类采用和基类一样的构造方式,可以直接使用基类的构造函数,而不是再重新写一遍构造函数,老规矩,看代码: + +不使用继承构造函数: + +```c++ +struct Base { + Base() {} + Base(int a) { a_ = a; } + + Base(int a, int b) : Base(a) { b_ = b; } + + Base(int a, int b, int c) : Base(a, b) { c_ = c; } + + int a_; + int b_; + int c_; +}; + +struct Derived : Base { + Derived() {} + Derived(int a) : Base(a) {} // 好麻烦 + Derived(int a, int b) : Base(a, b) {} // 好麻烦 + Derived(int a, int b, int c) : Base(a, b, c) {} // 好麻烦 +}; +int main() { + Derived a(1, 2, 3); + return 0; +} +``` + +使用继承构造函数: + +```c++ +struct Base { + Base() {} + Base(int a) { a_ = a; } + + Base(int a, int b) : Base(a) { b_ = b; } + + Base(int a, int b, int c) : Base(a, b) { c_ = c; } + + int a_; + int b_; + int c_; +}; + +struct Derived : Base { + using Base::Base; +}; + +int main() { + Derived a(1, 2, 3); + return 0; +} +``` + +只需要使用using Base::Base继承构造函数,就免去了很多重写代码的麻烦。 + +### nullptr + +nullptr是c++11用来表示空指针新引入的常量值,在c++中如果表示空指针语义时建议使用nullptr而不要使用NULL,因为NULL本质上是个int型的0,其实不是个指针。举例: + +```c++ +void func(void *ptr) { + cout << "func ptr" << endl; +} + +void func(int i) { + cout << "func i" << endl; +} + +int main() { + func(NULL); // 编译失败,会产生二义性 + func(nullptr); // 输出func ptr + return 0; +} +``` + +### final & override + +c++11关于继承新增了两个关键字,final用于修饰一个类,表示禁止该类进一步派生和虚函数的进一步重载,override用于修饰派生类中的成员函数,标明该函数重写了基类函数,如果一个函数声明了override但父类却没有这个虚函数,编译报错,使用override关键字可以避免开发者在重写基类函数时无意产生的错误。 + +示例代码1: + +```c++ +struct Base { + virtual void func() { + cout << "base" << endl; + } +}; + +struct Derived : public Base{ + void func() override { // 确保func被重写 + cout << "derived" << endl; + } + + void fu() override { // error,基类没有fu(),不可以被重写 + } +}; +``` + +示例代码2: + +```c++ +struct Base final { + virtual void func() { + cout << "base" << endl; + } +}; + +struct Derived : public Base{ // 编译失败,final修饰的类不可以被继承 + void func() override { + cout << "derived" << endl; + } +}; +``` + +### default + +c++11引入default特性,多数时候用于声明构造函数为默认构造函数,如果类中有了自定义的构造函数,编译器就不会隐式生成默认构造函数,如下代码: + +```c++ +struct A { + int a; + A(int i) { a = i; } +}; + +int main() { + A a; // 编译出错 + return 0; +} +``` + +上面代码编译出错,因为没有匹配的构造函数,因为编译器没有生成默认构造函数,而通过default,程序员只需在函数声明后加上“=default;”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体,如下: + +```c++ +struct A { + A() = default; + int a; + A(int i) { a = i; } +}; + +int main() { + A a; + return 0; +} +``` + +编译通过。 + +### delete + +c++中,如果开发人员没有定义特殊成员函数,那么编译器在需要特殊成员函数时候会隐式自动生成一个默认的特殊成员函数,例如拷贝构造函数或者拷贝赋值操作符,如下代码: + +```c++ +struct A { + A() = default; + int a; + A(int i) { a = i; } +}; + +int main() { + A a1; + A a2 = a1; // 正确,调用编译器隐式生成的默认拷贝构造函数 + A a3; + a3 = a1; // 正确,调用编译器隐式生成的默认拷贝赋值操作符 +} +``` + +有时候想禁止对象的拷贝与赋值,可以使用delete修饰,如下: + +```c++ +struct A { + A() = default; + A(const A&) = delete; + A& operator=(const A&) = delete; + int a; + A(int i) { a = i; } +}; + +int main() { + A a1; + A a2 = a1; // 错误,拷贝构造函数被禁用 + A a3; + a3 = a1; // 错误,拷贝赋值操作符被禁用 +} +``` + +delele函数在c++11中很常用,std::unique_ptr就是通过delete修饰来禁止对象的拷贝的。 + +### explicit + +explicit专用于修饰构造函数,表示只能显式构造,不可以被隐式转换,根据代码看explicit的作用: + +不用explicit: + +```c++ +struct A { + A(int value) { // 没有explicit关键字 + cout << "value" << endl; + } +}; + +int main() { + A a = 1; // 可以隐式转换 + return 0; +} +``` + +使用explicit: + +```c++ +struct A { + explicit A(int value) { + cout << "value" << endl; + } +}; + +int main() { + A a = 1; // error,不可以隐式转换 + A aa(2); // ok + return 0; +} +``` + +### const + +因为要讲后面的constexpr,所以这里简单介绍下const。 + +const字面意思为只读,可用于定义变量,表示变量是只读的,不可以更改,如果更改,编译期间就会报错。 + +主要用法如下: + +1. 用于定义常量,const的修饰的变量不可更改。 + +```c++ +const int value = 5; +``` + +1. 指针也可以使用const,这里有个小技巧,从右向左读,即可知道const究竟修饰的是指针还是指针所指向的内容。 + +```c++ +char *const ptr; // 指针本身是常量 +const char* ptr; // 指针指向的变量为常量 +``` + +1. 在函数参数中使用const,一般会传递类对象时会传递一个const的引用或者指针,这样可以避免对象的拷贝,也可以防止对象被修改。 + +```c++ +class A{}; +void func(const A& a); +``` + +1. const修饰类的成员变量,表示是成员常量,不能被修改,可以在初始化列表中被赋值。 + +```c++ +class A { + const int value = 5; +}; +class B { + const int value; + B(int v) : value(v){} +}; +``` + +1. 修饰类成员函数,表示在该函数内不可以修改该类的成员变量。 + +```c++ +class A{ + void func() const; +}; +``` + +1. 修饰类对象,类对象只能调用该对象的const成员函数。 + +```c++ +class A { + void func() const; +}; +const A a; +a.func(); +``` + +### constexpr + +constexpr是c++11新引入的关键字,用于编译时的常量和常量函数,这里直接介绍constexpr和const的区别: + +两者都代表可读,const只表示read only的语义,只保证了运行时不可以被修改,但它修饰的仍然有可能是个动态变量,而constexpr修饰的才是真正的常量,它会在编译期间就会被计算出来,整个运行过程中都不可以被改变,constexpr可以用于修饰函数,这个函数的返回值会尽可能在编译期间被计算出来当作一个常量,但是如果编译期间此函数不能被计算出来,那它就会当作一个普通函数被处理。如下代码: + +```c++ +#include +using namespace std; + +constexpr int func(int i) { + return i + 1; +} + +int main() { + int i = 2; + func(i);// 普通函数 + func(2);// 编译期间就会被计算出来 +} +``` + +### enum class + +c++11新增有作用域的枚举类型,看代码 + +不带作用域的枚举代码: + +```c++ +enum AColor { + kRed, + kGreen, + kBlue +}; + +enum BColor { + kWhite, + kBlack, + kYellow +}; + +int main() { + if (kRed == kWhite) { + cout << "red == white" << endl; + } + return 0; +} +``` + +如上代码,不带作用域的枚举类型可以自动转换成整形,且不同的枚举可以相互比较,代码中的红色居然可以和白色比较,这都是潜在的难以调试的bug,而这种完全可以通过有作用域的枚举来规避。 + +有作用域的枚举代码: + +```c++ +enum class AColor { + kRed, + kGreen, + kBlue +}; + +enum class BColor { + kWhite, + kBlack, + kYellow +}; + +int main() { + if (AColor::kRed == BColor::kWhite) { // 编译失败 + cout << "red == white" << endl; + } + return 0; +} +``` + +使用带有作用域的枚举类型后,对不同的枚举进行比较会导致编译失败,消除潜在bug,同时带作用域的枚举类型可以选择底层类型,默认是int,可以改成char等别的类型。 + +```c++ +enum class AColor : char { + kRed, + kGreen, + kBlue +}; +``` + +平时编程过程中使用枚举,一定要使用有作用域的枚举取代传统的枚举。 + +### 非受限联合体 + +c++11之前union中数据成员的类型不允许有非POD类型,而这个限制在c++11被取消,允许数据成员类型有非POD类型,看代码: + +```c++ +struct A { + int a; + int *b; +}; + +union U { + A a; // 非POD类型 c++11之前不可以这样定义联合体 + int b; +}; +``` + +对于什么是POD类型,大家可以自行查下资料,大体上可以理解为对象可以直接memcpy的类型。 + +### sizeof + +c++11中sizeof可以用的类的数据成员上,看代码: + +c++11前: + +```c++ +struct A { + int data[10]; + int a; +}; + +int main() { + A a; + cout << "size " << sizeof(a.data) << endl; + return 0; +} +``` + +c++11后: + +```c++ +struct A { + int data[10]; + int a; +}; + +int main() { + cout << "size " << sizeof(A::data) << endl; + return 0; +} +``` + +想知道类中数据成员的大小在c++11中是不是方便了许多,而不需要定义一个对象,在计算对象的成员大小。 + +### assertion + +```c++ +static_assert(true/false, message); +``` + +c++11引入static_assert声明,用于在编译期间检查,如果第一个参数值为false,则打印message,编译失败。 + +### 自定义字面量 + +c++11可以自定义字面量,平时c++中都或多或少使用过chrono中的时间,例如: + +```c++ +std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100ms +std::this_thread::sleep_for(std::chrono::seconds(100)); // 100s +``` + +其实没必要这么麻烦,也可以这么写: + +```c++ +std::this_thread::sleep_for(100ms); // c++14里可以这么使用,这里只是举个自定义字面量使用的例子 +std::this_thread::sleep_for(100s); +``` + +这就是自定义字面量的使用,示例如下: + +```c++ +struct mytype { + unsigned long long value; +}; +constexpr mytype operator"" _mytype ( unsigned long long n ) { + return mytype{n}; +} +mytype mm = 123_mytype; +cout << mm.value << endl; +``` + +关于自定义字面量,可以看下chrono的源代码。 + +### 内存对齐 + +#### 什么是内存对齐 + +理论上计算机对于任何变量的访问都可以从任意位置开始,然而实际上系统会对这些变量的存放地址有限制,通常将变量首地址设为某个数N的倍数,这就是内存对齐。 + +#### 为什么要内存对齐 + +1. 硬件平台限制,内存以字节为单位,不同硬件平台不一定支持任何内存地址的存取,一般可能以双字节、4字节等为单位存取内存,为了保证处理器正确存取数据,需要进行内存对齐。 +2. 提高CPU内存访问速度,一般处理器的内存存取粒度都是N的整数倍,假如访问N大小的数据,没有进行内存对齐,有可能就需要两次访问才可以读取出数据,而进行内存对齐可以一次性把数据全部读取出来,提高效率。 + +在c++11之前如果想创建内存对齐需要: + +```c++ +void align_cpp11_before(){ + static char data[sizeof(void *) + sizeof(A)]; + const uintptr_t kAlign = sizeof(void *) - 1; + char *align_ptr = + reinterpret_cast(reinterpret_cast(data + kAlign) & ~kAlign); + A *attr = new (align_ptr) A; +} +``` + +c++11关于内存对齐新增了一些函数: + +```c++ +void align_cpp11_after() +{ + static std::aligned_storage::type data; + A *attr = new (&data) A; +} +``` + +还有:alignof()、std::alignment_of()、alignas()。 + +### thread_local + +c++11引入thread_local,用thread_local修饰的变量具有thread周期,每一个线程都拥有并只拥有一个该变量的独立实例,一般用于需要保证线程安全的函数中。 + +```c++ +#include +#include + +class A { + public: + A() {} + ~A() {} + + void test(const std::string &name) { + thread_local int count = 0; + ++count; + std::cout << name << ": " << count << std::endl; + } +}; + +void func(const std::string &name) { + A a1; + a1.test(name); + a1.test(name); + A a2; + a2.test(name); + a2.test(name); +} + +int main() { + std::thread(func, "thread1").join(); + std::thread(func, "thread2").join(); + return 0; +} +``` + +输出: + +```c++ +thread1: 1 +thread1: 2 +thread1: 3 +thread1: 4 +thread2: 1 +thread2: 2 +thread2: 3 +thread2: 4 +``` + +验证上述说法,对于一个线程私有变量,一个线程拥有且只拥有一个该实例,类似于static。 + +### 基础数值类型 + +c++11新增了几种数据类型:long long、char16_t、char32_t等 + +### 随机数功能 + +c++11关于随机数功能则较之前丰富了很多,典型的可以选择概率分布类型,先看如下代码: + +```c++ +#include + +#include +#include + +using namespace std; + +int main() { + std::default_random_engine random(time(nullptr)); + + std::uniform_int_distribution int_dis(0, 100); // 整数均匀分布 + std::uniform_real_distribution real_dis(0.0, 1.0); // 浮点数均匀分布 + + for (int i = 0; i < 10; ++i) { + cout << int_dis(random) << ' '; + } + cout << endl; + + for (int i = 0; i < 10; ++i) { + cout << real_dis(random) << ' '; + } + cout << endl; + + return 0; +} +``` + +输出: + +```c++ +38 100 93 7 66 0 68 99 41 7 +0.232202 0.617716 0.959241 0.970859 0.230406 0.430682 0.477359 0.971858 0.0171148 0.64863 +``` + +代码中举例的是整数均匀分布和浮点数均匀分布,c++11提供的概率分布类型还有好多,例如伯努利分布、正态分布等,具体可以见最后的参考资料。 + +### 正则表达式 + +c++11引入了regex库更好的支持正则表达式,见代码: + +```c++ +#include +#include +#include +#include + +int main() { + std::string s = "I know, I'll use2 regular expressions."; + // 忽略大小写 + std::regex self_regex("REGULAR EXPRESSIONS", std::regex_constants::icase); + if (std::regex_search(s, self_regex)) { + std::cout << "Text contains the phrase 'regular expressions'\n"; + } + + std::regex word_regex("(\\w+)"); // 匹配字母数字等字符 + auto words_begin = std::sregex_iterator(s.begin(), s.end(), word_regex); + auto words_end = std::sregex_iterator(); + + std::cout << "Found " << std::distance(words_begin, words_end) << " words\n"; + + const int N = 6; + std::cout << "Words longer than " << N << " characters:\n"; + for (std::sregex_iterator i = words_begin; i != words_end; ++i) { + std::smatch match = *i; + std::string match_str = match.str(); + if (match_str.size() > N) { + std::cout << " " << match_str << '\n'; + } + } + + std::regex long_word_regex("(\\w{7,})"); + // 超过7个字符的单词用[]包围 + std::string new_s = std::regex_replace(s, long_word_regex, "[$&]"); + std::cout << new_s << '\n'; +} +``` + +### chrono + +c++11关于时间引入了chrono库,源于boost,功能强大,chrono主要有三个点: + +- duration +- time_point + +- clocks + +### duration + +std::chrono::duration表示一段时间,常见的单位有s、ms等,示例代码: + +```c++ +// 拿休眠一段时间举例,这里表示休眠100ms +std::this_thread::sleep_for(std::chrono::milliseconds(100)); +``` + +sleep_for里面其实就是std::chrono::duration,表示一段时间,实际是这样: + +```c++ +typedef duration milliseconds; +typedef duration seconds; +``` + +duration具体模板如下: + +```c++ +template > class duration; +``` + +Rep表示一种数值类型,用来表示Period的数量,比如int、float、double,Period是ratio类型,用来表示【用秒表示的时间单位】比如second,常用的duration已经定义好了,在std::chrono::duration下: + +- ratio<3600, 1>:hours +- ratio<60, 1>:minutes + +- ratio<1, 1>:seconds +- ratio<1, 1000>:microseconds + +- ratio<1, 1000000>:microseconds +- ratio<1, 1000000000>:nanosecons + +ratio的具体模板如下: + +```c++ +template class ratio; +``` + +N代表分子,D代表分母,所以ratio表示一个分数,可以自定义Period,比如ratio<2, 1>表示单位时间是2秒。 + +#### time_point + +表示一个具体时间点,如2020年5月10日10点10分10秒,拿获取当前时间举例: + +```c++ +std::chrono::time_point Now() { + return std::chrono::high_resolution_clock::now(); +} +// std::chrono::high_resolution_clock为高精度时钟,下面会提到 +``` + +clocks + +时钟,chrono里面提供了三种时钟: + +- steady_clock +- system_clock + +- high_resolution_clock + +#### steady_clock + +稳定的时间间隔,表示相对时间,相对于系统开机启动的时间,无论系统时间如何被更改,后一次调用now()肯定比前一次调用now()的数值大,可用于计时。 + +#### system_clock + +表示当前的系统时钟,可以用于获取当前时间: + +```c++ +int main() { + using std::chrono::system_clock; + system_clock::time_point today = system_clock::now(); + + std::time_t tt = system_clock::to_time_t(today); + std::cout << "today is: " << ctime(&tt); + + return 0; +} +// today is: Sun May 10 09:48:36 2020 +``` + +#### high_resolution_clock + +high_resolution_clock表示系统可用的最高精度的时钟,实际上就是system_clock或者steady_clock其中一种的定义,官方没有说明具体是哪个,不同系统可能不一样,看gcc chrono源码中high_resolution_clock是steady_clock的typedef。 + +### 新增数据结构 + +- std::forward_list:单向链表,只可以前进,在特定场景下使用,相比于std::list节省了内存,提高了性能 + +```c++ +std::forward_list fl = {1, 2, 3, 4, 5}; +for (const auto &elem : fl) { + cout << elem; +} +``` + +- std::unordered_set:基于hash表实现的set,内部不会排序,使用方法和set类似 +- std::unordered_map:基于hash表实现的map,内部不会排序,使用方法和set类似 + +- std::array:数组,在越界访问时抛出异常,建议使用std::array替代普通的数组 +- std::tuple:元组类型,类似pair,但比pair扩展性好 + +```c++ +typedef std::tuple Mytuple; +Mytuple t(0, 1, 2, 3); +std::cout << "0 " << std::get<0>(t); +std::cout << "1 " << std::get<1>(t); +std::cout << "2 " << std::get<2>(t); +std::cout << "3 " << std::get<3>(t); +``` + +### 新增算法 + +- all_of:检测表达式是否对范围[first, last)中所有元素都返回true,如果都满足,则返回true + +```c++ +std::vector v(10, 2); +if (std::all_of(v.cbegin(), v.cend(), [](int i) { return i % 2 == 0; })) { + std::cout << "All numbers are even\n"; +} +``` + +- any_of:检测表达式是否对范围[first, last)中至少一个元素返回true,如果满足,则返回true,否则返回false,用法和上面一样 +- none_of:检测表达式是否对范围[first, last)中所有元素都不返回true,如果都不满足,则返回true,否则返回false,用法和上面一样 + +- find_if_not:找到第一个不符合要求的元素迭代器,和find_if相反 +- copy_if:复制满足条件的元素 + +- itoa:对容器内的元素按序递增 + +```c++ +std::vector l(10); +std::iota(l.begin(), l.end(), 19); // 19为初始值 +for (auto n : l) std::cout << n << ' '; +// 19 20 21 22 23 24 25 26 27 28 +``` + +- minmax_element:返回容器内最大元素和最小元素位置 + +```c++ +int main() { + std::vector v = {3, 9, 1, 4, 2, 5, 9}; + + auto result = std::minmax_element(v.begin(), v.end()); + std::cout << "min element at: " << *(result.first) << '\n'; + std::cout << "max element at: " << *(result.second) << '\n'; + return 0; +} +// min element at: 1 +// max element at: 9 +``` + +- is_sorted、is_sorted_until:返回容器内元素是否已经排好序。 diff --git "a/\345\234\250c++\351\241\271\347\233\256\344\270\255\344\275\240\345\277\205\351\241\273\347\234\237\346\255\243\344\275\277\347\224\250\347\232\20415\344\270\252c++\347\211\271\346\200\247.md" "b/\345\234\250c++\351\241\271\347\233\256\344\270\255\344\275\240\345\277\205\351\241\273\347\234\237\346\255\243\344\275\277\347\224\250\347\232\20415\344\270\252c++\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..24f9e75d8b9c217338ecc960e69bcc3cdf10b926 --- /dev/null +++ "b/\345\234\250c++\351\241\271\347\233\256\344\270\255\344\275\240\345\277\205\351\241\273\347\234\237\346\255\243\344\275\277\347\224\250\347\232\20415\344\270\252c++\347\211\271\346\200\247.md" @@ -0,0 +1,144 @@ +> 本文转载自:https://cppdepend.com/blog/?p=319 + +During the last few years, we talk about the “C++ Renaissance”. We have to admit that Microsoft was a major part of this movement, I remember this [video](http://channel9.msdn.com/Shows/Going+Deep/Craig-Symonds-and-Mohsen-Agsen-C-Renaissance) where Craig Symonds and Mohsen Agsen talked about it. + +In 2011 Microsoft announced in many articles the comeback of C++, and Microsoft C++ experts like Herb Sutter did many conferences to explain why C++ was back and mostly recommended the use of Modern C++. At the same time, the standard C++11 was approved and we began to talk about C++ as a new language. + +By 2011, C++ had been in use for more than 30 years. It was not easy to convince developers that the new C++ actually simplified many frustrating facets of C++ usage and that there was a new modern way to improve the C++ Code. + +For the C++ developers who do not yet switch to C++11, they can use clang-tidy to get suggestions on where they can modernize the codebase or try the [CppDepend modernization feature](https://www.cppdepend.com/Modernize). And here are the 15 features mostly used in the C++ open source libraries. + +Let’s take as example [Folly](https://github.com/facebook/folly) released six years ago by Facebook, Folly is a large collection of reusable C++ library components that internally at Facebook are used extensively. And here’s the motivations from their website behind its utility: + +> Folly (acronymed loosely after Facebook Open Source Library) is a library of C++11 components designed with practicality and efficiency in mind. It complements (as opposed to competing against) offerings such as Boost and of course `std`. In fact, we embark on defining our own component only when something we need is either not available, or does not meet the needed performance profile. + +Let’s explore from its source code 15 C++11 features: + +### **1- auto** + +C++11 introduces type inference capability using the auto keyword, which means that the compiler infers the type of a variable at the point of declaration. Folly uses auto for almost all its variable declarations, here’s an example from its source code + +[![c1](http://www.javadepend.com/Blog/wp-content/uploads/c1.png)](http://www.javadepend.com/Blog/wp-content/uploads/c1.png) + +Using the auto keyword permits to spend less time having to write out things the compiler already knows. + +### **2- nullptr** + +The constant 0 has had the double role of constant integer and null pointer constant.C++11 corrects this by introducing a new keyword to serve as a distinguished null pointer constant:nulptr + +In the Folly source code, all null pointers are represented by the new keyword nullptr, there ‘s no place where the constant 0 is used. + +### **3- shared_ptr** + +The smart pointer is not a new concept, many libraries implemented it many years ago, the popular one is [boost::shared_ptr](http://www.boost.org/doc/libs/1_50_0/libs/smart_ptr/shared_ptr.htm), what’s new is its standardization, and no need anymore to use an external library to work with smart pointers. + +Folly uses extensively the standardized shared pointer, only a few raw pointers remain in its source code. + +### **4- Strongly-typed enums** + +“Traditional” enums in C++ export their enumerators in the surrounding scope, which can lead to name collisions, if two different enums in the same have scope define enumerators with the same name, + +C++11 introduces the enum class keywords. They no longer export their enumerators in the surrounding scope. Moreover, we can also now inherit from an enum. + +[![c2](http://www.javadepend.com/Blog/wp-content/uploads/c2.png)](http://www.javadepend.com/Blog/wp-content/uploads/c2.png) + + + +### **5- static assert** + +C++11 introduces a new way to test assertions at compile-time, using the new keyword static_assert, this feature is very useful to add conditions to the template parameters, as shown in this template class from Folly source code: + +[![c3](http://www.javadepend.com/Blog/wp-content/uploads/c3.png)](http://www.javadepend.com/Blog/wp-content/uploads/c3.png) + +### 6- Variadic template + +The variadic template is a template, which can take an arbitrary number of template arguments of any type. Both the classes & functions can be variadic. Folly defines many variadic templates, here are two variadic template functions from the Folly source code: + +[![c4](http://www.javadepend.com/Blog/wp-content/uploads/c4.png)](http://www.javadepend.com/Blog/wp-content/uploads/c4.png) + +### **7- Range-based for loops** + +C++11 augmented the for statement to support the “foreach” paradigm of iterating over collections. it makes the code more simple and cleaner. Folly uses extensively this feature, here’s an example for Folly + +[![c6](http://www.javadepend.com/Blog/wp-content/uploads/c6.png)](http://www.javadepend.com/Blog/wp-content/uploads/c6.png) + +### **8-Initializer lists** + +In C++03 Initializer lists concern only arrays, in C++11 are not just for arrays anymore. The mechanism for accepting a **{}**-list is a function (often a constructor) accepting an argument of type **std::initializer_list**. Here’s an example of function accepting std::initializer_list as an argument + +[![c7](http://www.javadepend.com/Blog/wp-content/uploads/c7.png)](http://www.javadepend.com/Blog/wp-content/uploads/c7.png) + +And here’s how it’s invoked + +[![c8](http://www.javadepend.com/Blog/wp-content/uploads/c8.png)](http://www.javadepend.com/Blog/wp-content/uploads/c8.png) + +### **9- noexcept** + +If a function cannot throw an exception or if the program isn’t written to handle exceptions thrown by a function, that function can be declared **noexcept.** + +Here’s an example from Folly source code + +[![c9](http://www.javadepend.com/Blog/wp-content/uploads/c9.png)](http://www.javadepend.com/Blog/wp-content/uploads/c9.png) + +### **10- move** + +C++11 has introduced the concept of rvalue references (specified with &&) to differentiate a reference to an lvalue or an rvalue. An lvalue is an object that has a name, while an rvalue is an object that does not have a name (a temporary object). The move semantics allow modifying rvalues. + +For that C++11 introduces two new special member functions: the *move constructor* and the *move assignment operator*. + +[![c12](http://www.javadepend.com/Blog/wp-content/uploads/c12.png)](http://www.javadepend.com/Blog/wp-content/uploads/c12.png) + +[![c10](http://www.javadepend.com/Blog/wp-content/uploads/c10.png)](http://www.javadepend.com/Blog/wp-content/uploads/c10.png) + +Here’s a [good document](http://www.stroustrup.com/move.pdf) that explains better the benefits of move semantics. + +### **11-lambda** + +C++11 provides the ability to create anonymous functions, called lambda functions, you can refer [here](http://www.stroustrup.com/C++11FAQ.html#lambda) for more details about this new feature. + +Folly uses it in many functions, here’s an example from its source code: + +[![c14](http://www.javadepend.com/Blog/wp-content/uploads/c14.png)](http://www.javadepend.com/Blog/wp-content/uploads/c14.png) + +### **12- Explicitly defaulted and deleted special member functions** + +In C++03, the compiler provides, for classes that do not provide them for themselves, a default constructor, a copy constructor, a copy assignment operator (`operator=`), and a destructor. The programmer can override these defaults by defining custom versions. + +However, there is very little control over the creation of these defaults. Making a class inherently non-copyable, for example, requires declaring a private copy constructor and copy assignment operator and not defining them. + +In C++11, certain features can be explicitly disabled. For example, the following type is non-copyable, which makes the code more simple and clean. + +[![c15](http://www.javadepend.com/Blog/wp-content/uploads/c15.png)](http://www.javadepend.com/Blog/wp-content/uploads/c15.png) + +### **13- override identifier** + +In C++03, it is possible to accidentally create a new virtual function, when one intended to override a base class function. + +The `override` special identifier means that the compiler will check the base class(es) to see if there is a virtual function with this exact signature. And if there is not, the compiler will indicate an error. + +Folly uses extensively this new feature: + +[![c18](http://www.javadepend.com/Blog/wp-content/uploads/c18.png)](http://www.javadepend.com/Blog/wp-content/uploads/c18.png) + + + +### **14- std::thread** + +A thread class (`std::thread`) is provided which takes a function object — and an optional series of arguments to pass to it — to run in the new thread. + +In C++11 working with threads is more simplified, here’s from Folly source code the new standard way to defines a new thread: + +[![c20](http://www.javadepend.com/Blog/wp-content/uploads/c20.png)](http://www.javadepend.com/Blog/wp-content/uploads/c20.png) + +### **15- Unordered containers** + +An unordered container is a kind of hash table. C++11 offers four standard ones: + +- unordered_map +- unordered_set +- unordered_multimap +- unordered_multiset + +Folly uses in many places these new containers + +[![c21](http://www.javadepend.com/Blog/wp-content/uploads/c21.png)](http://www.javadepend.com/Blog/wp-content/uploads/c21.png) diff --git "a/\345\246\202\344\275\225\345\234\250 C++11 \344\270\255\344\275\277\347\224\250 Lambda \350\241\250\350\276\276\345\274\217.md" "b/\345\246\202\344\275\225\345\234\250 C++11 \344\270\255\344\275\277\347\224\250 Lambda \350\241\250\350\276\276\345\274\217.md" new file mode 100644 index 0000000000000000000000000000000000000000..2408c9b731f13530b962f54cec42a6d1cb188173 --- /dev/null +++ "b/\345\246\202\344\275\225\345\234\250 C++11 \344\270\255\344\275\277\347\224\250 Lambda \350\241\250\350\276\276\345\274\217.md" @@ -0,0 +1,232 @@ +> 原文链接:https://www.oracle.com/cn/servers/technologies/howto-use-lambda-exp-cpp11.html +### Lambda 表达式不仅具有函数指针的灵活性,还可以通过捕获局部变量提高可扩展性。本文介绍 Lambda 表达式的语法和用法。 + +### 简介 + +Lambda 可能是最新的 C++11 标准的典型特性之一。Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。 + +当一个函数需要将另一个函数用作参数时,可以使用 Lambda。例如,C qsort() 函数接受一个指向比较函数的指针,如清单 1 所示。 + +```c++ +#include + #include + static int intcompare(const void *p1, const void *p2) + { + int i = *((int *)p1); + int j = *((int *)p2); + return (i < j) ; + } + int main() + { + int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + qsort((void *)a, 10, sizeof (int), intcompare); + for (int i = 0; i < 10; i++) { printf("%d ", a[i]); } + printf("\n"); + return 0; } +``` + +**清单 1** + +清单 1 中的代码有以下几点不足: + +- 比较函数需要单独声明。这增加了将错误的比较函数传递给 qsort() 操作的风险。 +- 比较函数接受 void * 参数,因此缺失了某种程度的类型检查。 +- 比较函数看不到任何局部作用的变量。因此,如果存在其他影响排序的因素,必须在更大范围内声明。 + +清单 2 显示重新编写后的清单 1 中的示例,将 C++ std::sort() 算法与 lambda 表达式结合使用。由于 std::sort() 是一个模板,因此会保留所有类型信息。注意如何在通常出现函数名的位置编写 lambda 表达式。 + +```c++ +#include + int main() + { + int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; +std::sort( a, &a[10], [](int x, int y){ return x < y; } ); + for(int i=0; i<10; i++) { printf("%i ", a[i]); } + printf("\n"); + return 0; + } +``` + +**清单 2** + +### Lambda 表达式的基本语法 + +Lambda 表达式本质上与函数声明非常类似。我们可以提取清单 2 中的 lambda 表达式,详加说明。提取的 lambda 表达式如清单 3 所示: + +```c++ +[](int x, int y){ return x < y ; } +``` + +**清单 3** + +如果我们将 lambda 表达式比作函数,可以看到它与函数名对应的是一对空的方括号,即*捕获表达式*。这些括号表示后面跟着一个 lambda 表达式。这些方括号不必为空;稍后将讨论其内容。 + +如果 lambda 主体只含一个返回类型,则暗示返回的表达式类型为 lambda 返回类型。如果要显式指定返回类型,需使用新的 C++11 语法表示函数声明中的后置返回类型。对于返回类型 T 的普通函数,您可以这样编写: + +```c++ +auto foo(...) -> T { ... } +``` + +对于 lambda,您需要要这样编写: + +```c++ +[] (...) -> T { ... } +``` + +lambda 表达式的其余部分与常规 C 或 C++ 函数主体类似。 + +### 将 Lambda 传递到函数指针 + +C++11 标准库中有一个名为 function 的模板,它可以接受指定类型的函数或者具有匹配的返回类型和参数列表的 lambda。这将产生一个指向函数类型的指针,例如,清单 4 可用作函数参数类型,接受 int 参数,返回 void。您可以向其传递任何类似匹配函数或 lambda 的内容。 + +```c++ +std::function +``` + +**清单 4** + +清单 5 显示的函数扫描一个数组,对每个元素应用一个给定函数。 + +```c++ +void scan( int* a, int length, std::function process ) + { + for(int i=0; ivoid { ... } ); +``` + +**清单 6** + +### Lambda 表达式中的变量捕获 + +到目前为止,我们对 lambda 表达式的处理基本与标准函数调用类似:传入参数,返回结果。然而,在函数主体中声明的 lambda 表达式还是可以捕获在声明 lambda 处可见的函数的任何局部变量。 + +假设我们需要使用函数 scan(),但希望 process 函数只对大于某个阈值的值起作用。我们不能修改 scan(),不能让 scan() 向 process 函数传递多个参数。但如果我们将一个 lambda 表达式传递给 scan() 函数,则可以从其环境捕获一个局部变量。 + +在清单 7 中,我们将希望捕获的变量放在方括号中,即放在捕获表达式中。这实际上向 lambda 表达式中额外传递了一个参数,但无需更改 scan 函数的定义。就像传递参数给函数一样,我们实际上是在函数的调用点捕获值 threshold 的副本,这称为*通过值捕获*。 + +```c++ +#include + void scan( int* a, int length, std::function process) + { + for(int i=0; ithreshold) { printf("%i ", v); } } + ); + printf("\n"); + return 0; } +``` + +**清单 7** + +有一个简写形式 [=],表示“通过值捕获每个变量”。在清单 8 中,我们将函数调用重新编写为使用这种更短的表达式。 + +```c++ +scan(a, 10, [=](int v) { if (v>threshold) { printf("%i ", v); } }); +``` + +**清单 8** + +**注**:通过值捕获变量意味着生成局部副本。如果有多个局部变量,全部捕获可能会导致 lambda 产生显著开销。 + +但有些情况下,我们希望修改捕获的变量。例如,假设我们要计算最大值并将其存储在变量 max 中。在这种情况下,我们不想使用该变量值的副本,而是希望使用该变量的引用,这样,我们就可以在模板中修改该变量。这称为*通过引用捕获变量*。清单 9 显示了这样一个示例。 + +```c++ +#include + void scan(int * a, int length, std::function func) + { + for(int i=0; imax) {max = v;} +if (v>threshold) { printf("%i ", v); } }); + printf("\n"); + printf("Max = %i\n",max); + return 0; + } +``` + +**清单 9** + +同样,也有一个简写形式 [&],用于应通过引用捕获每个变量的情况。 + +### Lambda 表达式、函数对象和函子 + +虽然 lambda 表达式是 C++11 的新特性,但用这种方式访问现有语言特性的确很方便。lambda 表达式是*函数对象* 的速记表示法。函数对象是一个具有成员 operator()()(函数调用运算符)的类类型对象,因此可以像函数一样调用。函数对象类型被称作*函子*。清单 10 显示了一个函子的示例。 + +```c++ +class compare_ints { + public: + compare_ints(int j, int k ) : l(j), r(k) { } + bool operator()() { return l < r; } + private: + int l, r; }; +``` + +**清单 10** + +您可以创建一个 compare_ints 对象,用两个整型值初始化,如果第一个值小于第二个值,使用函数调用运算符返回 true: + +```c++ +compare_ints comp(j, k); + bool less_than = comp(); +``` + +也可以动态创建一个临时对象,然后直接使用: + +```c++ +bool less_than = compare_ints(j, k)(); +``` + +使用 lambda 表达式不必创建和命名函子类即可达到这种效果。编译器为您创建一个匿名函子,如清单 11 所示。 + +```c++ +auto comp = [](int j, int k) { return j < k; }; +bool less_than = comp(l,r); +``` + +**清单 11** + +在清单 11 中,comp 是匿名函子类型的对象。 + +您也可以动态执行此操作: + +```c++ +bool less_than = [l,r]() { return l < r; }(); +``` + +### 总结 + +Lambda 表达式是一种非常强大的 C++ 扩展。它们不仅具有函数指针的灵活性,还可以通过捕获局部变量提高可扩展性。 + +显然,与 C++11 中广泛的模板特性结合时,lambda 表达式会变得更加 有用,这种情况在按 C++11 标准编写的代码中会经常遇到。 diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260\357\274\232C++ 11\346\226\260\347\211\271\346\200\247.md" "b/\345\255\246\344\271\240\347\254\224\350\256\260\357\274\232C++ 11\346\226\260\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..0fc6657717544f7dfd46044a3b98e44d9f3cd3b3 --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260\357\274\232C++ 11\346\226\260\347\211\271\346\200\247.md" @@ -0,0 +1,1740 @@ +# C++11新特性之auto和decltype知识点 + +C++11引入了auto和decltype关键字,使用它们可以在编译期就推导出变量或者表达式的类型,方便开发者编码的同时也简化了代码。 + +## auto + +auto可以让编译器在编译器就推导出变量的类型,看代码: + +```c++ +auto a = 10; // 10是int型,可以自动推导出a是int +int i = 10;auto b = i; // b是int型 +auto d = 2.0; // d是double型 +``` + +这就是auto的基本用法,可以通过=右边的类型推导出变量的类型。 + +### auto推导规则 + +直接看代码 + +代码1: + +```c++ +int i = 10; +auto a = i, &b = i, *c = &i; // a是int,b是i的引用,c是i的指针,auto就相当于int +auto d = 0, f = 1.0; // error,0和1.0类型不同,对于编译器有二义性,没法推导 +auto e; // error,使用auto必须马上初始化,否则无法推导类型 +``` + +代码2: + +```c++ +void func(auto value) {} // error,auto不能用作函数参数 + +class A { + auto a = 1; // error,在类中auto不能用作非静态成员变量 + static auto b = 1; // error,这里与auto无关,正常static int b = 1也不可以 + static const auto int c = 1; // ok +}; + +void func2() { + int a[10] = {0}; + auto b = a; // ok + auto c[10] = a; // error,auto不能定义数组,可以定义指针 + vector d; + vector f = d; // error,auto无法推导出模板参数 +} +``` + +auto的限制: + +- auto的使用必须马上初始化,否则无法推导出类型 +- auto在一行定义多个变量时,各个变量的推导不能产生二义性,否则编译失败 + +- auto不能用作函数参数 +- 在类中auto不能用作非静态成员变量 + +- auto不能定义数组,可以定义指针 +- auto无法推导出模板参数 + +再看这段代码: + +```c++ +int i = 0; +auto *a = &i; // a是int* +auto &b = i; // b是int& +auto c = b; // c是int,忽略了引用 + +const auto d = i; // d是const int +auto e = d; // e是int + +const auto& f = e; // f是const int& +auto &g = f; // g是const int& +``` + +首先,介绍下,这里的cv是指const 和volatile + +推导规则 + +- 在不声明为引用或指针时,auto会忽略等号右边的引用类型和cv限定 +- 在声明为引用或者指针时,auto会保留等号右边的引用和cv属性 + +### 什么时候使用auto? + +这里没有绝对答案,在不影响代码代码可读性的前提下尽可能使用auto是蛮好的,复杂类型就使用auto,int、double这种就没有必要使用auto了,看下面这段代码: + +```c++ +auto func = [&] { + cout << "xxx"; +}; // 对于func难道不使用auto吗,反正是不关心lambda表达式究竟是什么类型。 + +auto asyncfunc = std::async(std::launch::async, func); +// 对于asyncfunc难道不使用auto吗,懒得写std::futurexxx等代码,而且也记不住它返回的究竟是什么... +``` + +## decltype + +上面介绍auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算,上代码: + +```c++ +int func() { return 0; } +decltype(func()) i; // i为int类型 + +int x = 0; +decltype(x) y; // y是int类型 +decltype(x + y) z; // z是int类型 +``` + +注意:decltype不会像auto一样忽略引用和cv属性,decltype会保留表达式的引用和cv属性 + +```c++ +cont int &i = 1; +int a = 2; +decltype(i) b = 2; // b是const int& +``` + +### decltype推导规则 + +对于decltype(exp)有 + +- exp是表达式,decltype(exp)和exp类型相同 +- exp是函数调用,decltype(exp)和函数返回值类型相同 + +- 其它情况,若exp是左值,decltype(exp)是exp类型的左值引用 + +```c++ +int a = 0, b = 0; +decltype(a + b) c = 0; // c是int,因为(a+b)返回一个右值 +decltype(a += b) d = c;// d是int&,因为(a+=b)返回一个左值 + +d = 20; +cout << "c " << c << endl; // 输出c 20 +``` + +## auto和decltype的配合使用 + +auto和decltype一般配合使用在推导函数返回值的类型问题上。 + +下面这段代码 + +```c++ +template +return_value add(T t, U u) { // t和v类型不确定,无法推导出return_value类型 + return t + u; +} +``` + +上面代码由于t和u类型不确定,那如何推导出返回值类型呢,可能会想到这种 + +```c++ +template +decltype(t + u) add(T t, U u) { // t和u尚未定义 + return t + u; +} +``` + +这段代码在C++11上是编译不过的,因为在decltype(t +u)推导时,t和u尚未定义,就会编译出错,所以有了下面的叫做返回类型后置的配合使用方法: + +```c++ +template +auto add(T t, U u) -> decltype(t + u) { + return t + u; +} +``` + +返回值后置类型语法就是为了解决函数返回值类型依赖于参数但却难以确定返回值类型的问题。 + +# C++11新特性之左值引用、右值引用、移动语义、完美转发 + +C++11新增了右值引用,谈右值引用也可以扩展一些相关概念: + +- 左值 +- 右值 + +- 纯右值 +- 将亡值 + +- 左值引用 +- 右值引用 + +- 移动语义 +- 完美转发 + +- 返回值优化 + +### 左值、右值 + +概念1: + +左值:可以放到等号左边的东西叫左值。 + +右值:不可以放到等号左边的东西就叫右值。 + +概念2: + +左值:可以取地址并且有名字的东西就是左值。 + +右值:不能取地址的没有名字的东西就是右值。 + +举例: + +```c++ +int a = b + c; +``` + +a是左值,有变量名,可以取地址,也可以放到等号左边, 表达式b+c的返回值是右值,没有名字且不能取地址,&(b+c)不能通过编译,而且也不能放到等号左边。 + +```c++ +int a = 4; // a是左值,4作为普通字面量是右值 +``` + +左值一般有: + +- 函数名和变量名 +- 返回左值引用的函数调用 + +- 前置自增自减表达式++i、--i +- 由赋值表达式或赋值运算符连接的表达式(a=b, a += b等) + +- 解引用表达式*p +- 字符串字面值"abcd" + +### 纯右值、将亡值 + +纯右值和将亡值都属于右值。 + +#### 纯右值 + +运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。 + +举例: + +- 除字符串字面值外的字面值 +- 返回非引用类型的函数调用 + +- 后置自增自减表达式i++、i-- +- 算术表达式(a+b, a*b, a&&b, a==b等) + +- 取地址表达式等(&a) + +#### 将亡值 + +将亡值是指C++11新增的和右值引用相关的表达式,通常指将要被移动的对象、T&&函数的返回值、std::move函数的返回值、转换为T&&类型转换函数的返回值,将亡值可以理解为即将要销毁的值,通过“盗取”其它变量内存空间方式获取的值,在确保其它变量不再被使用或者即将被销毁时,可以避免内存空间的释放和分配,延长变量值的生命周期,常用来完成移动构造或者移动赋值的特殊任务。 + +举例: + +```c++ +class A { + xxx; +}; +A a; +auto c = std::move(a); // c是将亡值 +auto d = static_cast(a); // d是将亡值 +``` + +#### 左值引用、右值引用 + +根据名字大概就可以猜到意思,左值引用就是对左值进行引用的类型,右值引用就是对右值进行引用的类型,他们都是引用,都是对象的一个别名,并不拥有所绑定对象的堆存,所以都必须立即初始化。 + +```c++ +type &name = exp; // 左值引用 +type &&name = exp; // 右值引用 +``` + +左值引用 + +看代码: + +```c++ +int a = 5; +int &b = a; // b是左值引用 +b = 4; +int &c = 10; // error,10无法取地址,无法进行引用 +const int &d = 10; // ok,因为是常引用,引用常量数字,这个常量数字会存储在内存中,可以取地址 +``` + +可以得出结论:对于左值引用,等号右边的值必须可以取地址,如果不能取地址,则会编译失败,或者可以使用const引用形式,但这样就只能通过引用来读取输出,不能修改数组,因为是常量引用。 + +右值引用 + +如果使用右值引用,那表达式等号右边的值需要时右值,可以使用std::move函数强制把左值转换为右值。 + +```c++ +int a = 4; +int &&b = a; // error, a是左值 +int &&c = std::move(a); // ok +``` + +### 移动语义 + +谈移动语义前,首先需要了解深拷贝与浅拷贝的概念 + +#### 深拷贝、浅拷贝 + +直接拿代码举例: + +```c++ +class A { +public: + A(int size) : size_(size) { + data_ = new int[size]; + } + A(){} + A(const A& a) { + size_ = a.size_; + data_ = a.data_; + cout << "copy " << endl; + } + ~A() { + delete[] data_; + } + int *data_; + int size_; +}; +int main() { + A a(10); + A b = a; + cout << "b " << b.data_ << endl; + cout << "a " << a.data_ << endl; + return 0; +} +``` + +上面代码中,两个输出的是相同的地址,a和b的data_指针指向了同一块内存,这就是浅拷贝,只是数据的简单赋值,那再析构时data_内存会被释放两次,导致程序出问题,这里正常会出现double free导致程序崩溃的,这样的程序肯定是有隐患的,如何消除这种隐患呢,可以使用如下深拷贝: + +```c++ +class A { +public: + A(int size) : size_(size) { + data_ = new int[size]; + } + A(){} + A(const A& a) { + size_ = a.size_; + data_ = new int[size_]; + cout << "copy " << endl; + } + ~A() { + delete[] data_; + } + int *data_; + int size_; +}; +int main() { + A a(10); + A b = a; + cout << "b " << b.data_ << endl; + cout << "a " << a.data_ << endl; + return 0; +} +``` + +深拷贝就是再拷贝对象时,如果被拷贝对象内部还有指针引用指向其它资源,自己需要重新开辟一块新内存存储资源,而不是简单的赋值。 + +移动语义可以理解为转移所有权,之前的拷贝是对于别人的资源,自己重新分配一块内存存储复制过来的资源,而对于移动语义,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用,通过C++11新增的移动语义可以省去很多拷贝负担,怎么利用移动语义呢,是通过移动构造函数。 + +```c++ +class A { +public: + A(int size) : size_(size) { + data_ = new int[size]; + } + A(){} + A(const A& a) { + size_ = a.size_; + data_ = new int[size_]; + cout << "copy " << endl; + } + A(A&& a) { + this->data_ = a.data_; + a.data_ = nullptr; + cout << "move " << endl; + } + ~A() { + if (data_ != nullptr) { + delete[] data_; + } + } + int *data_; + int size_; +}; +int main() { + A a(10); + A b = a; + A c = std::move(a); // 调用移动构造函数 + return 0; +} +``` + +如果不使用std::move(),会有很大的拷贝代价,使用移动语义可以避免很多无用的拷贝,提供程序性能,C++所有的STL都实现了移动语义,方便使用。例如: + +```c++ +std::vector vecs; +... +std::vector vecm = std::move(vecs); // 免去很多拷贝 +``` + +注意:移动语义仅针对于那些实现了移动构造函数的类的对象,对于那种基本类型int、float等没有任何优化作用,还是会拷贝,因为它们实现没有对应的移动构造函数。 + +### 完美转发 + +完美转发指可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参,转发函数实参是左值那目标函数实参也是左值,转发函数实参是右值那目标函数实参也是右值。那如何实现完美转发呢,答案是使用std::forward()。 + +```c++ +void PrintV(int &t) { + cout << "lvalue" << endl; +} + +void PrintV(int &&t) { + cout << "rvalue" << endl; +} + +template +void Test(T &&t) { + PrintV(t); + PrintV(std::forward(t)); + + PrintV(std::move(t)); +} + +int main() { + Test(1); // lvalue rvalue rvalue + int a = 1; + Test(a); // lvalue lvalue rvalue + Test(std::forward(a)); // lvalue rvalue rvalue + Test(std::forward(a)); // lvalue lvalue rvalue + Test(std::forward(a)); // lvalue rvalue rvalue + return 0; +} +``` + +分析 + +- Test(1):1是右值,模板中T &&t这种为万能引用,右值1传到Test函数中变成了右值引用,但是调用PrintV()时候,t变成了左值,因为它变成了一个拥有名字的变量,所以打印lvalue,而PrintV(std::forward(t))时候,会进行完美转发,按照原来的类型转发,所以打印rvalue,PrintV(std::move(t))毫无疑问会打印rvalue。 +- Test(a):a是左值,模板中T &&这种为万能引用,左值a传到Test函数中变成了左值引用,所以有代码中打印。 + +- Test(std::forward(a)):转发为左值还是右值,依赖于T,T是左值那就转发为左值,T是右值那就转发为右值。 + +### 返回值优化 + +返回值优化(RVO)是一种C++编译优化技术,当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。 + +那什么时候编译器会进行返回值优化呢? + +- return的值类型与函数的返回值类型相同 +- return的是一个局部对象 + +看几个例子: + +示例1: + +```c++ +std::vector return_vector(void) { + std::vector tmp {1,2,3,4,5}; + return tmp; +} +std::vector &&rval_ref = return_vector(); +``` + +不会触发RVO,拷贝构造了一个临时的对象,临时对象的生命周期和rval_ref绑定,等价于下面这段代码: + +```c++ +const std::vector& rval_ref = return_vector(); +``` + +示例2: + +```c++ +std::vector&& return_vector(void) { + std::vector tmp {1,2,3,4,5}; + return std::move(tmp); +} + +std::vector &&rval_ref = return_vector(); +``` + +这段代码会造成运行时错误,因为rval_ref引用了被析构的tmp。讲道理来说这段代码是错的,自己运行过程中却成功了,继续向下看什么时候会触发RVO。 + +示例3: + +```c++ +std::vector return_vector(void) { + std::vector tmp {1,2,3,4,5}; + return std::move(tmp); +} + +std::vector &&rval_ref = return_vector(); +``` + +和示例1类似,std::move一个临时对象是没有必要的,也会忽略掉返回值优化。 + +最好的代码: + +```c++ +std::vector return_vector(void) { + std::vector tmp {1,2,3,4,5}; + return tmp; +} + +std::vector rval_ref = return_vector(); +``` + +这段代码会触发RVO,不拷贝也不移动,不生成临时对象。 + +# C++11新特性之列表初始化 + +C++11新增了列表初始化的概念。 + +在C++11中可以直接在变量名后面加上初始化列表来进行对象的初始化。 + +```c++ +struct A { + public: + A(int) {} + private: + A(const A&) {} +}; +int main() { + A a(123); + A b = 123; // error + A c = { 123 }; + A d{123}; // c++11 + + int e = {123}; + int f{123}; // c++11 + + return 0; +} +``` + +列表初始化也可以用在函数的返回值上 + +```c++ +std::vector func() { + return {}; +} +``` + +### 列表初始化的一些规则 + +首先说下聚合类型可以进行直接列表初始化,这里需要了解什么是聚合类型: + +1. 类型是一个普通数组,如int[5],char[],double[]等 +2. 类型是一个类,且满足以下条件: + +- - 没有用户声明的构造函数 + - 没有用户提供的构造函数(允许显示预置或弃置的构造函数) + +- - 没有私有或保护的非静态数据成员 + - 没有基类 + +- - 没有虚函数 + - 没有{}和=直接初始化的非静态数据成员 + +- - 没有默认成员初始化器 + +```c++ +struct A { + int a; + int b; + int c; + A(int, int){} +}; +int main() { + A a{1, 2, 3};// error,A有自定义的构造函数,不能列表初始化 +} +``` + +上述代码类A不是聚合类型,无法进行列表初始化,必须以自定义的构造函数来构造对象。 + +```c++ +struct A { + int a; + int b; + virtual void func() {} // 含有虚函数,不是聚合类 +}; + +struct Base {}; +struct B : public Base { // 有基类,不是聚合类 + int a; + int b; +}; + +struct C { + int a; + int b = 10; // 有等号初始化,不是聚合类 +}; + +struct D { + int a; + int b; + private: + int c; // 含有私有的非静态数据成员,不是聚合类 +}; + +struct E { + int a; + int b; + E() : a(0), b(0) {} // 含有默认成员初始化器,不是聚合类 +}; +``` + +上面列举了一些不是聚合类的例子,对于一个聚合类型,使用列表初始化相当于对其中的每个元素分别赋值;对于非聚合类型,需要先自定义一个对应的构造函数,此时列表初始化将调用相应的构造函数。 + +### std::initializer_list + +平时开发使用STL过程中可能发现它的初始化列表可以是任意长度,大家有没有想过它是怎么实现的呢,答案是std::initializer_list,看下面这段示例代码: + +```c++ +struct CustomVec { + std::vector data; + CustomVec(std::initializer_list list) { + for (auto iter = list.begin(); iter != list.end(); ++iter) { + data.push_back(*iter); + } + } +}; +``` + +这个std::initializer_list其实也可以作为函数参数。 + +注意:std::initializer_list,它可以接收任意长度的初始化列表,但是里面必须是相同类型T,或者都可以转换为T。 + +### 列表初始化的好处 + +列表初始化的好处如下: + +1. 方便,且基本上可以替代括号初始化 +2. 可以使用初始化列表接受任意长度 + +1. 可以防止类型窄化,避免精度丢失的隐式类型转换 + +什么是类型窄化,列表初始化通过禁止下列转换,对隐式转化加以限制: + +- 从浮点类型到整数类型的转换 +- 从 long double 到 double 或 float 的转换,以及从 double 到 float 的转换,除非源是常量表达式且不发生溢出 + +- 从整数类型到浮点类型的转换,除非源是其值能完全存储于目标类型的常量表达式 +- 从整数或无作用域枚举类型到不能表示原类型所有值的整数类型的转换,除非源是其值能完全存储于目标类型的常量表达式 + +示例: + +```c++ +int main() { + int a = 1.2; // ok + int b = {1.2}; // error + + float c = 1e70; // ok + float d = {1e70}; // error + + float e = (unsigned long long)-1; // ok + float f = {(unsigned long long)-1}; // error + float g = (unsigned long long)1; // ok + float h = {(unsigned long long)1}; // ok + + const int i = 1000; + const int j = 2; + char k = i; // ok + char l = {i}; // error + + char m = j; // ok + char m = {j}; // ok,因为是const类型,这里如果去掉const属性,也会报错 +} +``` + +打印如下: + +```c++ +test.cc:24:17: error: narrowing conversion of ‘1.2e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing] + int b = {1.2}; + ^ +test.cc:27:20: error: narrowing conversion of ‘1.0000000000000001e+70’ from ‘double’ to ‘float’ inside { } [-Wnarrowing] + float d = {1e70}; + +test.cc:30:38: error: narrowing conversion of ‘18446744073709551615’ from ‘long long unsigned int’ to ‘float’ inside { } [-Wnarrowing] + float f = {(unsigned long long)-1}; + ^ +test.cc:36:14: warning: overflow in implicit constant conversion [-Woverflow] + char k = i; + ^ +test.cc:37:16: error: narrowing conversion of ‘1000’ from ‘int’ to ‘char’ inside { } [-Wnarrowing] + char l = {i}; +``` + +# C++11新特性std::function和lambda表达式 + +c++11新增了`std::function`、`std::bind`、`lambda`表达式等封装使函数调用更加方便。 + +## `std::function` + +讲`std::function`前首先需要了解下什么是可调用对象 + +满足以下条件之一就可称为可调用对象: + +- 是一个函数指针 +- 是一个具有`operator()`成员函数的类对象(传说中的仿函数),lambda表达式 + +- 是一个可被转换为函数指针的类对象 +- 是一个类成员(函数)指针 + +- bind表达式或其它函数对象 + +而`std::function`就是上面这种可调用对象的封装器,可以把`std::function`看做一个函数对象,用于表示函数这个抽象概念。`std::function`的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为`std::function`的目标,若`std::function`不含目标,则称它为空,调用空的`std::function`的目标会抛出`std::bad_function_call`异常。 + +使用参考如下实例代码: + +```c++ +std::function f; // 这里表示function的对象f的参数是int,返回值是void +#include +#include + +struct Foo { + Foo(int num) : num_(num) {} + void print_add(int i) const { std::cout << num_ + i << '\n'; } + int num_; +}; + +void print_num(int i) { std::cout << i << '\n'; } + +struct PrintNum { + void operator()(int i) const { std::cout << i << '\n'; } +}; + +int main() { + // 存储自由函数 + std::function f_display = print_num; + f_display(-9); + + // 存储 lambda + std::function f_display_42 = []() { print_num(42); }; + f_display_42(); + + // 存储到 std::bind 调用的结果 + std::function f_display_31337 = std::bind(print_num, 31337); + f_display_31337(); + + // 存储到成员函数的调用 + std::function f_add_display = &Foo::print_add; + const Foo foo(314159); + f_add_display(foo, 1); + f_add_display(314159, 1); + + // 存储到数据成员访问器的调用 + std::function f_num = &Foo::num_; + std::cout << "num_: " << f_num(foo) << '\n'; + + // 存储到成员函数及对象的调用 + using std::placeholders::_1; + std::function f_add_display2 = std::bind(&Foo::print_add, foo, _1); + f_add_display2(2); + + // 存储到成员函数和对象指针的调用 + std::function f_add_display3 = std::bind(&Foo::print_add, &foo, _1); + f_add_display3(3); + + // 存储到函数对象的调用 + std::function f_display_obj = PrintNum(); + f_display_obj(18); +} +``` + +从上面可以看到`std::function`的使用方法,当给`std::function`填入合适的参数表和返回值后,它就变成了可以容纳所有这一类调用方式的函数封装器。`std::function`还可以用作回调函数,或者在C++里如果需要使用回调那就一定要使用`std::function`,特别方便。 + +## `std::bind` + +使用`std::bind`可以将可调用对象和参数一起绑定,绑定后的结果使用`std::function`进行保存,并延迟调用到任何需要的时候。 + +`std::bind`通常有两大作用: + +- 将可调用对象与参数一起绑定为另一个`std::function`供调用 +- 将n元可调用对象转成m(m < n)元可调用对象,绑定一部分参数,这里需要使用`std::placeholders` + +具体示例: + +```c++ +#include +#include +#include + +void f(int n1, int n2, int n3, const int& n4, int n5) { + std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << std::endl; +} + +int g(int n1) { return n1; } + +struct Foo { + void print_sum(int n1, int n2) { std::cout << n1 + n2 << std::endl; } + int data = 10; +}; + +int main() { + using namespace std::placeholders; // 针对 _1, _2, _3... + + // 演示参数重排序和按引用传递 + int n = 7; + // ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数) + auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n); + n = 10; + f1(1, 2, 1001); // 1 为 _1 所绑定, 2 为 _2 所绑定,不使用 1001 + // 进行到 f(2, 42, 1, n, 7) 的调用 + + // 嵌套 bind 子表达式共享占位符 + auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); + f2(10, 11, 12); // 进行到 f(12, g(12), 12, 4, 5); 的调用 + + // 绑定指向成员函数指针 + Foo foo; + auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1); + f3(5); + + // 绑定指向数据成员指针 + auto f4 = std::bind(&Foo::data, _1); + std::cout << f4(foo) << std::endl; + + // 智能指针亦能用于调用被引用对象的成员 + std::cout << f4(std::make_shared(foo)) << std::endl; +} +``` + +## `lambda`表达式 + +lambda表达式可以说是c++11引用的最重要的特性之一,它定义了一个匿名函数,可以捕获一定范围的变量在函数内部使用,一般有如下语法形式: + +```c++ +auto func = [capture] (params) opt -> ret { func_body; }; +``` + +其中`func`是可以当作`lambda`表达式的名字,作为一个函数使用,`capture`是捕获列表,`params`是参数表,`opt`是函数选项(mutable之类), ret是返回值类型,func_body是函数体。 + +一个完整的lambda表达式: + +```c++ +auto func1 = [](int a) -> int { return a + 1; }; +auto func2 = [](int a) { return a + 2; }; +cout << func1(1) << " " << func2(2) << endl; +``` + +如上代码,很多时候lambda表达式返回值是很明显的,c++11允许省略表达式的返回值定义。 + +`lambda`表达式允许捕获一定范围内的变量: + +- `[]`不捕获任何变量 +- `[&]`引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用 + +- `[=]`值捕获,捕获外部作用域所有变量,在函数内内有个副本使用 +- `[=, &a]`值捕获外部作用域所有变量,按引用捕获a变量 + +- `[a]`只值捕获a变量,不捕获其它变量 +- `[this]`捕获当前类中的this指针 + +lambda表达式示例代码: + +```c++ +int a = 0; +auto f1 = [=](){ return a; }; // 值捕获a +cout << f1() << endl; + +auto f2 = [=]() { return a++; }; // 修改按值捕获的外部变量,error +auto f3 = [=]() mutable { return a++; }; +``` + +代码中的f2是编译不过的,因为修改了按值捕获的外部变量,其实lambda表达式就相当于是一个仿函数,仿函数是一个有`operator()`成员函数的类对象,这个`operator()`默认是`const`的,所以不能修改成员变量,而加了`mutable`,就是去掉`const`属性。 + +还可以使用lambda表达式自定义stl的规则,例如自定义sort排序规则: + +```c++ +struct A { + int a; + int b; +}; + +int main() { + vector
vec; + std::sort(vec.begin(), vec.end(), [](const A &left, const A &right) { return left.a < right.a; }); +} +``` + +## 总结 + +`std::function`和`std::bind`在平时编程过程中封装函数更加的方便,而lambda表达式将这种方便发挥到了极致,可以在需要的时间就地定义匿名函数,不再需要定义类或者函数等,在自定义STL规则时候也非常方便,让代码更简洁,更灵活,提高开发效率。 + +# C++11新特性之模板改进 + +C++11关于模板有一些细节的改进: + +- 模板的右尖括号 +- 模板的别名 + +- 函数模板的默认模板参数 + +### 模板的右尖括号 + +C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误。 + +### 模板的别名 + +C++11引入了using,可以轻松的定义别名,而不是使用繁琐的typedef。 + +```c++ +int main() { + std::vector> a; // error + std::vector > b; // ok +} +``` + +使用using明显简洁并且易读,大家可能之前也见过使用typedef定义函数指针之类的操作。 + +```c++ +typedef void (*func)(int, int); +using func = void (*)(int, int); // 起码比typedef容易看的懂 +``` + +上面的代码使用using起码比typedef容易看的懂一些,但是我还是看不懂,因为我从来不用这种来表示函数指针,用std::function()、std::bind()、std::placeholder()、lambda表达式它不香吗。 + +### 函数模板的默认模板参数 + +C++11之前只有类模板支持默认模板参数,函数模板是不支持默认模板参数的,C++11后都支持。 + +```c++ +template +class A { + T value; +}; + +template // error +class A { + T value; +}; +``` + +类模板的默认模板参数必须从右往左定义,而函数模板则没有这个限制。 + +```c++ +template +R func1(U val) { + return val; +} + +template +R func2(U val) { + return val; +} + +int main() { + cout << func1(99.9) << endl; // 99 + cout << func1(99.9) << endl; // 99.9 + cout << func1(99.9) << endl; // 99.9 + cout << func1(99.9) << endl; // 99 + cout << func2(99.9) << endl; // 99 + cout << func1(99.9) << endl; // 99.9 + cout << func2(99.9) << endl; // 99.9 + cout << func2(99.9) << endl; // 99 + return 0; +} +``` + +# C++11新特性之线程相关知识点 + +c++11关于并发引入了好多新东西,这里按照如下顺序介绍: + +- std::thread相关 +- std::mutex相关 + +- std::lock相关 +- std::atomic相关 + +- std::call_once相关 +- volatile相关 + +- std::condition_variable相关 +- std::future相关 + +- async相关 + +## std::thread相关 + +c++11之前可能使用pthread_xxx来创建线程,繁琐且不易读,c++11引入了std::thread来创建线程,支持对线程join或者detach。直接看代码: + +```c++ +#include +#include + +using namespace std; + +int main() { + auto func = []() { + for (int i = 0; i < 10; ++i) { + cout << i << " "; + } + cout << endl; + }; + std::thread t(func); + if (t.joinable()) { + t.detach(); + } + auto func1 = [](int k) { + for (int i = 0; i < k; ++i) { + cout << i << " "; + } + cout << endl; + }; + std::thread tt(func1, 20); + if (tt.joinable()) { // 检查线程可否被join + tt.join(); + } + return 0; +} +``` + +上述代码中,函数func和func1运行在线程对象t和tt中,从刚创建对象开始就会新建一个线程用于执行函数,调用join函数将会阻塞主线程,直到线程函数执行结束,线程函数的返回值将会被忽略。如果不希望线程被阻塞执行,可以调用线程对象的detach函数,表示将线程和线程对象分离。 + +如果没有调用join或者detach函数,假如线程函数执行时间较长,此时线程对象的生命周期结束调用析构函数清理资源,这时可能会发生错误,这里有两种解决办法,一个是调用join(),保证线程函数的生命周期和线程对象的生命周期相同,另一个是调用detach(),将线程和线程对象分离,这里需要注意,如果线程已经和对象分离,那就再也无法控制线程什么时候结束了,不能再通过join来等待线程执行完。 + +这里可以对thread进行封装,避免没有调用join或者detach可导致程序出错的情况出现: + +```c++ +class ThreadGuard { + public: + enum class DesAction { join, detach }; + + ThreadGuard(std::thread&& t, DesAction a) : t_(std::move(t)), action_(a){}; + + ~ThreadGuard() { + if (t_.joinable()) { + if (action_ == DesAction::join) { + t_.join(); + } else { + t_.detach(); + } + } + } + + ThreadGuard(ThreadGuard&&) = default; + ThreadGuard& operator=(ThreadGuard&&) = default; + + std::thread& get() { return t_; } + + private: + std::thread t_; + DesAction action_; +}; + +int main() { + ThreadGuard t(std::thread([]() { + for (int i = 0; i < 10; ++i) { + std::cout << "thread guard " << i << " "; + } + std::cout << std::endl;}), ThreadGuard::DesAction::join); + return 0; +} +``` + +c++11还提供了获取线程id,或者系统cpu个数,获取thread native_handle,使得线程休眠等功能 + +```c++ +std::thread t(func); +cout << "当前线程ID " << t.get_id() << endl; +cout << "当前cpu个数 " << std::thread::hardware_concurrency() << endl; +auto handle = t.native_handle();// handle可用于pthread相关操作 +std::this_thread::sleep_for(std::chrono::seconds(1)); +``` + +## std::mutex相关 + +std::mutex是一种线程同步的手段,用于保存多线程同时操作的共享数据。 + +mutex分为四种: + +- std::mutex:独占的互斥量,不能递归使用,不带超时功能 +- std::recursive_mutex:递归互斥量,可重入,不带超时功能 + +- std::timed_mutex:带超时的互斥量,不能递归 +- std::recursive_timed_mutex:带超时的互斥量,可以递归使用 + +拿一个std::mutex和std::timed_mutex举例,别的都是类似的使用方式: + +std::mutex: + +```c++ +#include +#include +#include + +using namespace std; +std::mutex mutex_; + +int main() { + auto func1 = [](int k) { + mutex_.lock(); + for (int i = 0; i < k; ++i) { + cout << i << " "; + } + cout << endl; + mutex_.unlock(); + }; + std::thread threads[5]; + for (int i = 0; i < 5; ++i) { + threads[i] = std::thread(func1, 200); + } + for (auto& th : threads) { + th.join(); + } + return 0; +} +``` + +std::timed_mutex: + +```c++ +#include +#include +#include +#include + +using namespace std; +std::timed_mutex timed_mutex_; + +int main() { + auto func1 = [](int k) { + timed_mutex_.try_lock_for(std::chrono::milliseconds(200)); + for (int i = 0; i < k; ++i) { + cout << i << " "; + } + cout << endl; + timed_mutex_.unlock(); + }; + std::thread threads[5]; + for (int i = 0; i < 5; ++i) { + threads[i] = std::thread(func1, 200); + } + for (auto& th : threads) { + th.join(); + } + return 0; +} +``` + +## std::lock相关 + +这里主要介绍两种RAII方式的锁封装,可以动态的释放锁资源,防止线程由于编码失误导致一直持有锁。 + +c++11主要有std::lock_guard和std::unique_lock两种方式,使用方式都类似,如下: + +```c++ +#include +#include +#include +#include + +using namespace std; +std::mutex mutex_; + +int main() { + auto func1 = [](int k) { + // std::lock_guard lock(mutex_); + std::unique_lock lock(mutex_); + for (int i = 0; i < k; ++i) { + cout << i << " "; + } + cout << endl; + }; + std::thread threads[5]; + for (int i = 0; i < 5; ++i) { + threads[i] = std::thread(func1, 200); + } + for (auto& th : threads) { + th.join(); + } + return 0; +} +``` + +std::lock_gurad相比于std::unique_lock更加轻量级,少了一些成员函数,std::unique_lock类有unlock函数,可以手动释放锁,所以条件变量都配合std::unique_lock使用,而不是std::lock_guard,因为条件变量在wait时需要有手动释放锁的能力,具体关于条件变量后面会讲到。 + +## std::atomic相关 + +c++11提供了原子类型std::atomic,理论上这个T可以是任意类型,但是平时只存放整形,别的还真的没用过,整形有这种原子变量已经足够方便,就不需要使用std::mutex来保护该变量啦。看一个计数器的代码: + +```c++ +struct OriginCounter { // 普通的计数器 + int count; + std::mutex mutex_; + void add() { + std::lock_guard lock(mutex_); + ++count; + } + + void sub() { + std::lock_guard lock(mutex_); + --count; + } + + int get() { + std::lock_guard lock(mutex_); + return count; + } +}; + +struct NewCounter { // 使用原子变量的计数器 + std::atomic count; + void add() { + ++count; + // count.store(++count);这种方式也可以 + } + + void sub() { + --count; + // count.store(--count); + } + + int get() { + return count.load(); + } +}; +``` + +是不是使用原子变量更加方便了呢? + +## std::call_once相关 + +c++11提供了std::call_once来保证某一函数在多线程环境中只调用一次,它需要配合std::once_flag使用,直接看使用代码: + +```c++ +std::once_flag onceflag; + +void CallOnce() { + std::call_once(onceflag, []() { + cout << "call once" << endl; + }); +} + +int main() { + std::thread threads[5]; + for (int i = 0; i < 5; ++i) { + threads[i] = std::thread(CallOnce); + } + for (auto& th : threads) { + th.join(); + } + return 0; +} +``` + +## volatile相关 + +貌似把volatile放在并发里介绍不太合适,但是貌似很多人都会把volatile和多线程联系在一起,一起介绍下。 + +volatile通常用来建立内存屏障,volatile修饰的变量,编译器对访问该变量的代码通常不再进行优化,看下面代码: + +```c++ +int *p = xxx; +int a = *p; +int b = *p; +``` + +a和b都等于p指向的值,一般编译器会对此做优化,把*p的值放入寄存器,就是传说中的工作内存(不是主内存),之后a和b都等于寄存器的值,但是如果中间p地址的值改变,内存上的值改变啦,但a,b还是从寄存器中取的值(不一定,看编译器优化结果),这就不符合需求,所以在此对p加volatile修饰可以避免进行此类优化。 + +注意:volatile不能解决多线程安全问题,针对特种内存才需要使用volatile,它和atomic的特点如下:• std::atomic用于多线程访问的数据,且不用互斥量,用于并发编程中• volatile用于读写操作不可以被优化掉的内存,用于特种内存中 + +## std::condition_variable相关 + +条件变量是c++11引入的一种同步机制,它可以阻塞一个线程或者个线程,直到有线程通知或者超时才会唤醒正在阻塞的线程,条件变量需要和锁配合使用,这里的锁就是上面介绍的std::unique_lock。 + +这里使用条件变量实现一个CountDownLatch: + +```c++ +class CountDownLatch { + public: + explicit CountDownLatch(uint32_t count) : count_(count); + + void CountDown() { + std::unique_lock lock(mutex_); + --count_; + if (count_ == 0) { + cv_.notify_all(); + } + } + + void Await(uint32_t time_ms = 0) { + std::unique_lock lock(mutex_); + while (count_ > 0) { + if (time_ms > 0) { + cv_.wait_for(lock, std::chrono::milliseconds(time_ms)); + } else { + cv_.wait(lock); + } + } + } + + uint32_t GetCount() const { + std::unique_lock lock(mutex_); + return count_; + } + + private: + std::condition_variable cv_; + mutable std::mutex mutex_; + uint32_t count_ = 0; +}; +``` + +关于条件变量其实还涉及到通知丢失和虚假唤醒问题,因为不是本文的主题,这里暂不介绍,大家有需要可以留言。 + +## std::future相关 + +c++11关于异步操作提供了future相关的类,主要有std::future、std::promise和std::packaged_task,std::future比std::thread高级些,std::future作为异步结果的传输通道,通过get()可以很方便的获取线程函数的返回值,std::promise用来包装一个值,将数据和future绑定起来,而std::packaged_task则用来包装一个调用对象,将函数和future绑定起来,方便异步调用。而std::future是不可以复制的,如果需要复制放到容器中可以使用std::shared_future。 + +### std::promise与std::future配合使用 + +```c++ +#include +#include +#include +#include + +using namespace std; + +void func(std::future& fut) { + int x = fut.get(); + cout << "value: " << x << endl; +} + +int main() { + std::promise prom; + std::future fut = prom.get_future(); + std::thread t(func, std::ref(fut)); + prom.set_value(144); + t.join(); + return 0; +} +``` + +### std::packaged_task与std::future配合使用 + +```c++ +#include +#include +#include +#include + +using namespace std; + +int func(int in) { + return in + 1; +} + +int main() { + std::packaged_task task(func); + std::future fut = task.get_future(); + std::thread(std::move(task), 5).detach(); + cout << "result " << fut.get() << endl; + return 0; +} +``` + +### 三者之间的关系 + +std::future用于访问异步操作的结果,而std::promise和std::packaged_task在future高一层,它们内部都有一个future,promise包装的是一个值,packaged_task包装的是一个函数,当需要获取线程中的某个值,可以使用std::promise,当需要获取线程函数返回值,可以使用std::packaged_task。 + +## async相关 + +async是比future,packaged_task,promise更高级的东西,它是基于任务的异步操作,通过async可以直接创建异步的任务,返回的结果会保存在future中,不需要像packaged_task和promise那么麻烦,关于线程操作应该优先使用async,看一段使用代码: + +```c++ +#include +#include +#include +#include + +using namespace std; + +int func(int in) { return in + 1; } + +int main() { + auto res = std::async(func, 5); + // res.wait(); + cout << res.get() << endl; // 阻塞直到函数返回 + return 0; +} +``` + +使用async异步执行函数是不是方便多啦。 + +async具体语法如下: + +```c++ +async(std::launch::async | std::launch::deferred, func, args...); +``` + +第一个参数是创建策略: + +- std::launch::async表示任务执行在另一线程 +- std::launch::deferred表示延迟执行任务,调用get或者wait时才会执行,不会创建线程,惰性执行在当前线程。 + +如果不明确指定创建策略,以上两个都不是async的默认策略,而是未定义,它是一个基于任务的程序设计,内部有一个调度器(线程池),会根据实际情况决定采用哪种策略。 + +若从 std::async 获得的 std::future 未被移动或绑定到引用,则在完整表达式结尾, std::future的析构函数将阻塞直至异步计算完成,实际上相当于同步操作: + +```c++ +std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f() +std::async(std::launch::async, []{ g(); }); // f() 完成前不开始 +``` + +注意:关于async启动策略这里以cppreference为主。 + +有时候如果想真正执行异步操作可以对async进行封装,强制使用std::launch::async策略来调用async。 + +```c++ +template +inline auto ReallyAsync(F&& f, Args&&... params) { + return std::async(std::launch::async, std::forward(f), std::forward(params)...); +} +``` + +## 总结 + +• std::thread使线程的创建变得非常简单,还可以获取线程id等信息。 +• std::mutex通过多种方式保证了线程安全,互斥量可以独占,也可以重入,还可以设置互斥量的超时时间,避免一直阻塞等锁。 +• std::lock通过RAII技术方便了加锁和解锁调用,有std::lock_guard和std::unique_lock。 +• std::atomic提供了原子变量,更方便实现实现保护,不需要使用互斥量 +• std::call_once保证函数在多线程环境下只调用一次,可用于实现单例。 +• volatile常用于读写操作不可以被优化掉的内存中。 +• std::condition_variable提供等待的同步机制,可阻塞一个或多个线程,等待其它线程通知后唤醒。 +• std::future用于异步调用的包装和返回值。 +• async更方便的实现了异步调用,异步调用优先使用async取代创建线程。 + +# C++11 的异步操作-async + +C++11中增加了async,如它的名字一样,这个关键字就是用来创建异步操作的,c++11中有个更常用的异步操作,叫做线程thread,那么thread和async有什么区别呢?以及async的优势是什么?应该怎么使用? + +### C++11 使用 std::async创建异步程序 + +C++11中增加了线程,可以非常方便的创建线程,它的基本用法是这样的: + +```c++ +void f(int n); +std::thread t(f, n + 1); +t.join(); +``` + +但是线程毕竟是属于比较低层次的东西,有时候使用有些不便,比如希望获取线程函数的返回结果的时候,就不能直接通过 `thread.join()`得到结果,这时就必须定义一个变量,在线程函数中去给这个变量赋值,然后join,最后得到结果,这个过程是比较繁琐的。 + +c++11还提供了异步接口`std::async`,通过这个异步接口可以很方便的获取线程函数的执行结果。`std::async`会自动创建一个线程去调用线程函数,它返回一个`std::future`,这个future中存储了线程函数返回的结果,当需要线程函数的结果时,直接从future中获取,非常方便。 + +其实std::async提供的便利可不仅仅是这一点,它首先解耦了线程的创建和执行,可以在需要的时候获取异步操作的结果;其次它还提供了线程的创建策略(比如可以通过延迟加载的方式去创建线程),可以以多种方式去创建线程。在介绍`async`具体用法以及为什么要用`std::async`代替线程的创建之前,先看看`std::future`、`std::promise`和 `std::packaged_task`。 + +### std::future + +std::future是一个非常有用也很有意思的东西,简单说std::future提供了一种访问异步操作结果的机制。从字面意思来理解, 它表示未来,因为一个异步操作是不可能马上就获取操作结果的,只能在未来某个时候获取,但是可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。future_status有三种状态: + +- deferred:异步操作还没开始 +- ready:异步操作已经完成 + +- timeout:异步操作超时 + +```c++ +//查询future的状态 +std::future_status status; +do { + status = future.wait_for(std::chrono::seconds(1)); + if (status == std::future_status::deferred) { + std::cout << "deferred\n"; + } else if (status == std::future_status::timeout) { + std::cout << "timeout\n"; + } else if (status == std::future_status::ready) { + std::cout << "ready!\n"; +} while (status != std::future_status::ready); +``` + +获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,`wait`只是等待异步操作完成,没有返回值,`wait_for`是超时等待返回结果。 + +### std::promise + +std::promise为获取线程函数中的某个值提供便利,在线程函数中给外面传进来的promise赋值,当线程函数执行完成之后就可以通过promis获取该值了,值得注意的是取值是间接的通过promise内部提供的future来获取的。它的基本用法: + +```c++ +std::promise pr; +std::thread t([](std::promise& p){ + p.set_value_at_thread_exit(9); +},std::ref(pr)); +std::future f = pr.get_future(); +auto r = f.get(); +``` + +### std::packaged_task + +std::packaged_task它包装了一个可调用的目标(如function, lambda expression, bind expression, or another function object),以便异步调用,它和promise在某种程度上有点像,promise保存了一个共享状态的值,而`packaged_task`保存的是一 个函数。它的基本用法: + +```c++ +std::packaged_task task([](){ return 7; }); +std::thread t1(std::ref(task)); +std::future f1 = task.get_future(); +auto r1 = f1.get(); +``` + +### std::promise、std::packaged_task和std::future的关系 + +看了`std::async`相关的几个对象`std::future`、`std::promise`和`std::packaged_task`,其中 `std::promise`和`std::packaged_task`的结果最终都是通过其内部的future返回出来的,看看他们之间的关系到底是怎样的,`std::future`提供了一个访问异步操作结果的机制,它和线程是一个级别的属于低层次的对象,在它之上高一层的是`std::packaged_task`和`std::promise`,他们内部都有future以便访问异步操作结果,`std::packaged_task`包装的是一个异步操作,而`std::promise`包装的是一个值,都是为了方便异步操作的,因为有时需要获取线程中的某个值,这时就用`std::promise`,而有时需要获一个异步操作的返回值,这时就用`std::packaged_task`。 + +那 `std::promise`和`std::packaged_task`之间又是什么关系呢?说他们没关系也没关系,说他们有关系也有关系,都取决于如何使用他们了,可以将一个异步操作的结果保存到`std::promise`中。 + +### 为什么要用`std::async`代替线程的创建 + +`std::async`是为了让开发者的少费点脑子的,它让这三个对象默契的工作。大概的工作过程是这样的:`std::async`先将异步操作用`std::packaged_task`包 装起来,然后将异步操作的结果放到`std::promise`中,这个过程就是创造未来的过程。外面再通过`future.get/wait`来获取这个未来的结果! + +现在来看看`std::async`的原型 + +`async(std::launch::async | std::launch::deferred, f, args...)` 第一个参数是线程的创建策略,有两种策略,默认的策略是立即创建线程: + +`std::launch::async`:在调用async就开始创建线程。 + +`std::launch::deferred`:延迟加载方式创建线程。调用async时不创建线程,直到调用了`future`的`get`或者`wait`时才创建线程。 + +第二个参数是线程函数,第三个参数是线程函数的参数。 + +### `std::async`基本用法 + +```c++ +std::future f1 = std::async(std::launch::async, []() { + return 8; + }); +cout << f1.get() << endl; //output: 8 +std::future f2 = std::async(std::launch::async, []() { + cout << 8 << endl; + //return 8; + }); +f2.wait(); //output: 8 +std::future future = std::async(std::launch::async, []() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + return 8; + }); +std::cout << "waiting...\n"; +//Test12(); +std::future_status status; +Sleep(3000); +do { + status = future.wait_for(std::chrono::seconds(1)); + if (status == std::future_status::deferred) { + std::cout << "deferred\n"; + } + else if (status == std::future_status::timeout) { + std::cout << "timeout\n"; + } + else if (status == std::future_status::ready) { + std::cout << "ready!\n"; + } +} while (status != std::future_status::ready); +std::cout << "result is " << future.get() << '\n'; +``` + +可能的结果:waiting... timeout timeout ready! result is 8 + +### 总结 + +`std::async`是更高层次上的异步操作,它的存在可以使开发者不用关注线程创建内部细节,就能方便的获取异步执行状态和结果,还可以指定线程创建策略,应该用`std::async`替代线程的创建,让它成为做异步操作的首选。 + +# C++11新特性之智能指针 + +c++11引入了三种智能指针: + +- std::shared_ptr +- std::weak_ptr + +- std::unique_ptr + +## shared_ptr + +shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。 + +使用方法如下: + +```c++ +struct ClassWrapper { + ClassWrapper() { + cout << "construct" << endl; + data = new int[10]; + } + ~ClassWrapper() { + cout << "deconstruct" << endl; + if (data != nullptr) { + delete[] data; + } + } + void Print() { + cout << "print" << endl; + } + int* data; +}; + +void Func(std::shared_ptr ptr) { + ptr->Print(); +} + +int main() { + auto smart_ptr = std::make_shared(); + auto ptr2 = smart_ptr; // 引用计数+1 + ptr2->Print(); + Func(smart_ptr); // 引用计数+1 + smart_ptr->Print(); + ClassWrapper *p = smart_ptr.get(); // 可以通过get获取裸指针 + p->Print(); + return 0; +} +``` + +智能指针还可以自定义删除器,在引用计数为0的时候自动调用删除器来释放对象的内存,代码如下: + +```c++ +std::shared_ptr ptr(new int, [](int *p){ delete p; }); +``` + +关于shared_ptr有几点需要注意: + +• 不要用一个裸指针初始化多个shared_ptr,会出现double_free导致程序崩溃 + +• 通过shared_from_this()返回this指针,不要把this指针作为shared_ptr返回出来,因为this指针本质就是裸指针,通过this返回可能 会导致重复析构,不能把this指针交给智能指针管理。 + +```c++ +class A { + shared_ptr GetSelf() { + return shared_from_this(); + // return shared_ptr(this); 错误,会导致double free + } +}; +``` + +- 尽量使用make_shared,少用new。 +- 不要delete get()返回来的裸指针。 + +- 不是new出来的空间要自定义删除器。 +- 要避免循环引用,循环引用导致内存永远不会被释放,造成内存泄漏。 + +```c++ +using namespace std; +struct A; +struct B; + +struct A { + std::shared_ptr bptr; + ~A() { + cout << "A delete" << endl; + } +}; + +struct B { + std::shared_ptr aptr; + ~B() { + cout << "B delete" << endl; + } +}; + +int main() { + auto aaptr = std::make_shared(); + auto bbptr = std::make_shared(); + aaptr->bptr = bbptr; + bbptr->aptr = aaptr; + return 0; +} +``` + +上面代码,产生了循环引用,导致aptr和bptr的引用计数为2,离开作用域后aptr和bptr的引用计数-1,但是永远不会为0,导致指针永远不会析构,产生了内存泄漏,如何解决这种问题呢,答案是使用weak_ptr。 + +## weak_ptr + +weak_ptr是用来监视shared_ptr的生命周期,它不管理shared_ptr内部的指针,它的拷贝的析构都不会影响引用计数,纯粹是作为一个旁观者监视shared_ptr中管理的资源是否存在,可以用来返回this指针和解决循环引用问题。 + +- 作用1:返回this指针,上面介绍的shared_from_this()其实就是通过weak_ptr返回的this指针。 +- 作用2:解决循环引用问题。 + +```c++ +struct A; +struct B; + +struct A { + std::shared_ptr bptr; + ~A() { + cout << "A delete" << endl; + } + void Print() { + cout << "A" << endl; + } +}; + +struct B { + std::weak_ptr aptr; // 这里改成weak_ptr + ~B() { + cout << "B delete" << endl; + } + void PrintA() { + if (!aptr.expired()) { // 监视shared_ptr的生命周期 + auto ptr = aptr.lock(); + ptr->Print(); + } + } +}; + +int main() { + auto aaptr = std::make_shared(); + auto bbptr = std::make_shared(); + aaptr->bptr = bbptr; + bbptr->aptr = aaptr; + bbptr->PrintA(); + return 0; +} +``` + +输出: + +```c++ +A +A delete +B delete +``` + +## unique_ptr + +std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝: + +```c++ +using namespace std; + +struct A { + ~A() { + cout << "A delete" << endl; + } + void Print() { + cout << "A" << endl; + } +}; + + +int main() { + auto ptr = std::unique_ptr(new A); + auto tptr = std::make_unique(); // error, c++11还不行,需要c++14 + std::unique_ptr tem = ptr; // error, unique_ptr不允许移动 + ptr->Print(); + return 0; +} +``` + diff --git "a/\346\200\273\347\273\223\345\275\222\347\272\263\357\274\232C++17\346\226\260\347\211\271\346\200\247.md" "b/\346\200\273\347\273\223\345\275\222\347\272\263\357\274\232C++17\346\226\260\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..80df05658ba2ede85a2c87abd0460b9eddf75506 --- /dev/null +++ "b/\346\200\273\347\273\223\345\275\222\347\272\263\357\274\232C++17\346\226\260\347\211\271\346\200\247.md" @@ -0,0 +1,705 @@ +## 关键字 + +### constexpr + +扩展constexpr使用范围,可用于if语句中,也可用于lambda表达式中。 + +```c++ +#include + +template +constexpr void foo() +{ + //在编译期进行判断,if和else语句不生成代码 + if constexpr (ok == true) + { + //当ok为true时,下面的else块不生成汇编代码 + std::cout << "ok" << std::endl; + } + else + { + //当ok为false时,上面的if块不生成汇编代码 + std::cout << "not ok" << std::endl; + } +} + +int main() +{ + foo();//输出ok,并且汇编代码中只有std::cout << "ok" << std::endl;这一句 + foo();//输出not ok,并且汇编代码中只有std::cout << "not ok" << std::endl;这一句 + return 0; +} +``` + +### static_assert + +扩展static_assert用法,静态断言的显示文本可选。 + +如: + +```c++ +static_assert(true, ""); +static_assert(true);//c++17支持 +``` + +### typename + +扩展用法,允许出现在模板的模板的参数中。 + +首先回顾一下typename的用法,①用于模板中,表示模板参数为类型;②用于声明某名字是变量名 + +如 + +```c++ +struct A +{ + typedef int Example; +}; +//第一种用法:声明模板参数为类型 +template +struct B { }; + +struct C +{ + typedef typename A::Example E;//第二种用法:声明某名字为一种类型 +}; + +int main() +{ + typename A::Example e;//第二种用法:声明某名字为一种类型 + return 0; +} +``` + +新特性下的typename用法, + +如: + +```c++ +#include +#include + +template +struct A +{ + int num; + A() + { + std::cout << "A Construct" << std::endl; + std::cout << "template typename is: " << typeid (T).name() << std::endl; + } +}; +//此处的T可省略,X代表模板类型,T和X前的typename可替换成class +template typename X> +struct B +{ + X e; + B() { std::cout << "B Construct" << std::endl; } +}; + +int main() +{ + A> a; + std::cout << "***************************" << std::endl; + B b; + return 0; +} +``` + +### inline + +Inline 变量, inline变量可以让变量有多于一次的定义。C++17之前,我们定义全局变量, 总需要将变量定义在cpp文件中,然后在通过extern关键字来告诉编译器 这个变量已经在其他地方定义过了。 inline变量出现后,我们可以直接将全局变量定义在头文件中,而不用担心出现redefine的错误信息。 + +扩展用法,可用于定义内联变量,功能与内联函数相似。inline可避免函数或变量多重定义的问题,如果已定义相同的函数或变量(且该函数或变量声明为inline),编译器会自动链接到该函数或变量。 + +如(不发生错误): + +```c++ +// test.h +inline void print() +{ + std::cout << "hello world" << std::endl; +} + +inline int num = 0; +// func.h +include "test.h" +inline void add(int arg) +{ + num += arg; + print(); +} +// main.cpp +include "func.h" +int main() +{ + num = 0; + print(); + add(10); + return 0; +} +``` + +### auto + +从c++11开始,auto关键字能够通过初始化器推导出变量的类型。在c++14中,auto关键字的能力进一步提升,能够通过return语句推导出函数的返回类型。 + +使用auto关键字能够提高编码效率,同时能够简化重构流程。但是,C++11中的auto推导,往往结果与预期的不同。 + +c++11 中为了支持统一初始化,引入了新的统一初始化语法,如下所示。 + +```c++ +// c++11 +auto x3{ 1, 2 }; // std::initializer_list +auto x4 = { 3 }; // decltype(x4) is std::initializer_list +auto x5{ 3 }; // std::initializer_list +``` + +这三种方式初始化的变量,最终类型推导的结果都是 std::initializer_list , 而不是我们认为的int。 这是因为 + +当用于auto声明变量的表达式是{}括起来的,推导的型别就会变成 std::initializer_list。 + +在C++17中,对auto表达式推导的规则进行了改变 + +```c++ +// c++17 +auto x3{ 1, 2 }; // error: not a single element +auto x4 = { 3 }; // decltype(x4) is std::initializer_list +auto x5{ 3 }; // decltype(x5) is int +``` + +对比发现, auto x5{3}, 会直接将变量推导成 x5, 而 x3{1, 2} 这种方式也会编译失败。auto推导的规则变得更加直观。 + +## 语法 + +### lambda表达式 + +lambda也是c++11中引入的,在C++11中,lambda表达式只能用捕获this,this是当前对象的一个只读的引用。 + +在C++17中,可以捕获this, this是当前对象的一个拷贝,捕获当前对象的拷贝,能够确保当前对象释放后, + +lambda表达式能安全的调用this中的变量和方法。 + +### 条件表达式中支持初始化语句 + +c++17中支持在 if 或者switch 语句中进行初始化, 这个能力的出现能够让代码更加的简洁。 + +```c++ +// c++17之前 +map c = { {1,"a"}}; +{ + auto res = c.insert(make_pair(2, "b")); + if(!res.second) { + cout << "key 1 exist" << endl; + } else { + cout << "insert success, value:" << res.first->second << endl; + } +} +``` + +上面的一段代码,由于res是一个临时变量,不想影响到后面的代码,所以用一对花括号限制了其作用域。但是如果使用c++17的语法, + +在if条件中初始化res,则代码就会显得更加简洁 + +```c++ +// c++17 +map c = { {1,"a"}}; +if(auto res = c.insert(make_pair(2, "b")); !res.second ) { + cout << "key 1 exist" << endl; +} else { + cout << "insert success, value:" << res.first->second << endl; +} +``` + +### 折叠表达式 + +用于变长参数模板的解包,只支持各种运算符(和操作符),分左、右折叠 + +如: +```c++ +#include + +template +auto sum(T ... arg) +{ + return (arg + ...);//右折叠 +} + +template +double sum_strong(T ... arg) +{ + return (arg + ... + 0);//右折叠 +} + +template +double sub1(T ... arg) +{ + return (arg - ...);//右折叠 +} + +template +double sub2(T ... arg) +{ + return (... - arg);//左折叠 +} + +int main() +{ + int s1 = sum(1, 2, 2, 4, 5);//解包:((((1+)2+)3+)4+)5 = 15 + double s2 = sum(1.1, 2.2, 3.3, 4.4, 5.5, 6.6); + double s3 = sum(1, 2.2, 3, 4.4, 5); + + double s4 = sub1(5, 2, 1, 1);//解包:((((5-)2-)1-)1) = 1 + double s5 = sub2(5, 2, 1, 1);//解包:(5-(2-(1-(1)))) = 3 + + double s6 = sum_strong();//s6 = 0 + + std::string str1("he"); + std::string str2("ll"); + std::string str3("o "); + std::string str4("world"); + std::string str5 = sum(str1, str2, str3, str4);//str5 = "hello world" + return 0; +} +``` + +### 结构化绑定 + +用一对包含一个或多个变量的中括号,表示结构化绑定,但是使用结构化绑定时,须用auto关键字,即绑定时声明变量 + +例子: + +```c++ +/* + * 例子:多值返回 + */ +struct S +{ + double num1; + long num2; +}; + +S foo(int arg1, double arg2) +{ + double result1 = arg1 * arg2; + long result2 = arg2 / arg1; + return {result1, result2};//返回结构体S对象 +}; + +int main() +{ + auto [num1, num2] = foo(10, 20.2);//自动推导num1为double,num2为long + return 0; +} +``` + +### 允许非类型模板参数进行常量计算 + +非类型模板参数可传入类的静态成员 + +如: + +```c++ +class MyClass +{ +public: + static int a; +}; + +template +void foo() {} + +int main() +{ + foo<&MyClass::a>(); + return 0; +} +``` + +### 条件分支语句初始化 + +在if和switch中可进行初始化 + +如: + +```c++ +template +void foo(int &ok) +{ + if constexpr (ok = 10; value > 0) + { + + } +} + +int main() +{ + int num = 0; + if(int i = 0; i == 0) + { + + } + foo<10>(num); + switch(int k = 10; k) + { + case 0:break; + case 1:break; + default:break; + } + return 0; +} +``` + +### 聚合初始化 + +在初始化对象时,可用花括号进行对其成员进行赋值 + +如: + +```c++ +struct MyStruct1 +{ + int a; + int b; +}; + +struct MyStruct2 +{ + int a; + MyStruct1 ms; +}; + +int main() +{ + MyStruct1 a{10}; + MyStruct2 b{10, 20}; + MyStruct2 c{1, {}}; + MyStruct2 d{ {}, {}}; + MyStruct2 e{ {}, {1, 2}}; + return 0; +} +``` + +### 嵌套命名空间 + +简化多层命名空间的写法 + +如: + +```c++ +//传统写法 +namespace A +{ + namespace B + { + namespace C + { + + }; + }; +}; +//新写法 +namespace A::B::C +{ + +}; +``` + +### lambda表达式捕获*this的值 + +lambda表达式可捕获*this的值,但this及其成员为只读 + +如: + +```c++ +struct MyStruct { + double ohseven = 100.7; + auto f() { + return [this] { + return [*this] { + this->ohseven = 200.2;//错误,只读变量不可赋值 + return ohseven;//正确 + }; + }(); + } + auto g() { + return []{ + return [*this]{};//错误,外层lambda表达式没有捕获this + }(); + } +}; +``` + +### 枚举[类]对象的构造 + +可以给枚举[类]对象赋值 + +如: + +```c++ +enum MyEnum { value }; +MyEnum me {10};//错误:不能用int右值初始化MyEnum类型对象 + +enum byte : unsigned char { }; +byte b { 42 }; //正确 +byte c = { 42 }; //错误:不能用int右值初始化byte类型对象 +byte d = byte{ 42 }; //正确,其值与b相等 +byte e { -1 }; //错误:常量表达式-1不能缩小范围为byte类型 + +struct A { byte b; }; +A a1 = { { 42 } }; //错误:不能用int右值初始化byte类型对象 +A a2 = { byte{ 42 } }; //正确 + +void f(byte); +f({ 42 }); //错误:无类型说明符 + +enum class Handle : unsigned int { value = 0 }; +Handle h { 42 }; //正确 +``` + +### 十六进制单精度浮点数字面值 + +以0x前缀开头的十六进制数,以f后缀的单精度浮点数,合并,就有了十六进制的单精度浮点数 + +如: + +```c++ +int main() +{ + float value = 0x1111f; + return 0; +} +``` + +### 基于对齐内存的动态内存分配 + +谈到动态内存分配,少不了new和delete运算符,新标准中的new和delete运算符新增了按照对齐内存值来分配、释放内存空间的功能(即一个新的带对齐内存值的new、delete运算符重载) + +函数原型: + +```c++ +void* operator new(std::size_t size, std::align_val_t alignment); +void* operator new[](std::size_t size, std::align_val_t alignment); +void operator delete(void*, std::size_t size, std::align_val_t alignment); +``` + +### 细化表达式的计算顺序 + +为了支持泛型编程和重载运算符的广泛使用,新特性将计算顺序进行的细化 + +如以下争议代码段: + +```c++ +#include + +int main() +{ + std::map tmp; + //对于std::map的[]运算符重载函数,在使用[]新增key时,std::map就已经插入了一个新的键值对 + tmp[0] = tmp.size();//此处不知道插入的是{0, 0}还是{0, 1} + return 0; +} +``` + +为了解决该情况,新计算顺序规则为: + +1. 后缀表达式从左到右求值。这包括函数调用和成员选择表达式。 +2. 赋值表达式从右向左求值。这包括复合赋值。 +3. 从左到右计算移位操作符的操作数。 + +### 模板类的模板参数自动推导 + +定义模板类的对象时,可以不指定模板参数,但必须要在构造函数中能推导出模板参数 + +如: + +```c++ +template struct A { + explicit A(const T&, ...) noexcept {} // #1 + A(T&&, ...){} // #2 +}; + +int i; + +A a1 = { i, i }; //错误,不能根据#1推导为右值引用,也不能通过#1实现复制初始化 +A a2{i, i}; //正确,调用#1初始化成功,a2推导为A类型 +A a3{0, i}; //正确,调用#2初始化成功,a2推导为A类型 +A a4 = {0, i}; //正确,调用#2初始化成功,a2推导为A类型 + +template A(const T&, const T&) -> A; // #3 +template explicit A(T&&, T&&) -> A; // #4 + +A a5 = {0, 1}; //错误,#1和#2构造函数结果相同(即冲突)。根据#3推导为A类型 +A a6{0, 1}; //正确,通过#2推断为A类型 +A a7 = {0, i}; //错误,不能将非静态左值引用绑定到右值。根据#3推导为A类型 +A a8{0, i}; //错误,不能将非静态左值引用绑定到右值。根据#3推导为A类型 + +template +struct B { + + template + using TA = T;//定义别名 + + template + B(U, TA);//构造函数 +}; + +B b{(int*)0, (char*)0}; //正确,推导为B类型 +``` + +### 简化重复命名空间的属性列表 + +如: + +```c++ +[[ using CC: opt(1), debug ]] void f() {} +//作用相同于 [[ CC::opt(1), CC::debug ]] void f() {} +``` + +### 不支持、非标准的属性 + +在添加属性列表时,编译器会忽略不支持的非标准的属性,不会发出警告和错误。 + +### 改写与继承构造函数 + +在类的继承体系中,构造函数的自动调用是一个令人头疼的问题。新特性引入继承与改写构造函数的用法。 + +例: + +```c++ +#include + +struct B1 +{ + B1(int) { std::cout << "B1" << std::endl; } +}; + +struct D1 : B1 { + using B1::B1;//表示继承B1的构造函数 +}; + +D1 d1(0); //正确,委托基类构造函数进行初始化,调用B1::B1(int) +``` + +## 宏 + +### __has_include + +判断有没有包含某文件 + +如: + +```c++ +int main() +{ +#if __has_include() + printf("hehe"); +#endif +#if __has_include("iostream") + std::cout << "hehe" << std::endl; +#endif +return 0; +} +``` + +## 属性 + +### fallthrough + +用于switch语句块内,表示会执行下一个case或default + +如: + +```cpp +int main() +{ + int ok1, ok2; + switch (0) + { + case 0: + ok1 = 0; + [[fallthrough]]; + case 1: + ok2 = 1; + [[fallthrough]]; + } + return 0; +} +``` + +### nodiscard + +可用于类声明、函数声明、枚举声明中,表示函数的返回值没有被接收,在编译时会出现警告。 + +如: + +```cpp +[[nodiscard]] class A {}; //该属性在这其实没用 +[[nodiscard]] enum class B {}; //该属性在这其实没用 +class C {}; + +[[nodiscard]] int foo() +{ return 10; } + +[[nodiscard]] A func1() { return A(); } +[[nodiscard]] B func2() { return B(); } +[[nodiscard]] C func3() { return C(); } + +int main() +{ + foo();//warning: ignoring return value + func1();//warning: ignoring return value + func2();//warning: ignoring return value + func3();//warning: ignoring return value + return 0; +} +``` + +### maybe_unused + +可用于类、typedef、变量、非静态数据成员、函数、枚举或枚举值中。用于抑制编译器对没用实体的警告。即加上该属性后,对某一实体不会发出“没有用”的警告。 + +用法例子: + +```cpp +[[maybe_unused]] class A {}; +[[maybe_unused]] enum B {}; +[[maybe_unused]] int C; +[[maybe_unused]] void fun(); +``` + +## 数据类型 + +c++17的标准库也进行了扩充, 新增了下面几种数据类型: + +### std::variant + +std::variant是类型安全的联合体,是一个加强版的 union,variant支持更加复杂的数据类型,例如map,string等等 + +### std::optional + +std::optional表示一个可能存在的值。 当我们通过函数创建一个对象时,通常使用通过函数返回错误码,而通过出参返回对象本身。 + +如果通过optional返回创建的实例,就会变得更加直观, + +std::optional 提供了下面几个方法: + +has_value() // 检查对象是否有值 value() // 返回对象的值,值不存在时则抛出 std::bad_optional_access 异常 value_or() // 值存在时返回值,不存在时返回默认值 + +### std::any + +一个类型安全的可以保存任何值的容器 + +### std::string_view + +string_view我最早使用的是boost版本的,c++17中的string_view 和 boost类似。 + +string_view可以理解成原始字符串一个只读引用。 string_view 本身没有申请额外的内存来存储原始字符串的data, + +仅仅保存了原始字符串地址和长度等信息。 在很多情况下,我们只是临时处理字符串,本不需要对原始字符串的一份拷贝。 + +使用string_view可以减少不必要的内存拷贝,可以提高程序性能。相比使用字符串指针,string_view做了更好的封装。 + +需要注意的是,string_view 由于没有原始字符串的所有权,使用string_view 一定要注意原始字符串的生命周期。 + +当原始的字符串已经销毁,则不能再调用string_view。 diff --git "a/\346\257\217\344\270\252c++\345\274\200\345\217\221\344\272\272\345\221\230\351\203\275\345\272\224\350\257\245\344\275\277\347\224\250\347\232\20410\344\270\252c++ 11\347\211\271\346\200\247.md" "b/\346\257\217\344\270\252c++\345\274\200\345\217\221\344\272\272\345\221\230\351\203\275\345\272\224\350\257\245\344\275\277\347\224\250\347\232\20410\344\270\252c++ 11\347\211\271\346\200\247.md" new file mode 100644 index 0000000000000000000000000000000000000000..a6f449668ec574c66336fbcebe0be6516f08cd49 --- /dev/null +++ "b/\346\257\217\344\270\252c++\345\274\200\345\217\221\344\272\272\345\221\230\351\203\275\345\272\224\350\257\245\344\275\277\347\224\250\347\232\20410\344\270\252c++ 11\347\211\271\346\200\247.md" @@ -0,0 +1,634 @@ +> 本文转载自:https://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer +## 目录 + +- auto +- nullptr +- Range-Based for Loops +- Override and Final +- Strongly-typed Enums +- Smart Pointers +- Lambdas +- Non-Member begin() and end() +- static_assert and Type Traits +- Move Semantics + +## auto + +Before C++11, the `auto` keyword was used for storage duration specification. In the new standard, its purpose was changed towards type inference. `auto` is now a sort of placeholder for a type, telling the compiler it has to deduce the actual type of a variable that is being declared from its initializer. It can be used when declaring variables in different scopes such as namespaces, blocks or initialization statement of `for` loops. + +```c++ +auto i = 42; // i is an int +auto l = 42LL; // l is an long long +auto p = new foo(); // p is a foo* +``` + +Using `auto` usually means less code (unless your type is `int` which is one letter shorter). Think of iterators in STL that you always had to write while iterating over containers. It makes obsolete creating `typedef`s just for the sake of simplicity. + +```c++ +std::map> map; +for(auto it = begin(map); it != end(map); ++it) +{ +} +``` + +You should note that `auto` cannot be used as the return type of a function. However, you can use `auto` in place of the return type of function, but in this case the function must have a trailing return type. In this case, `auto` does not tell the compiler it has to infer the type, it only instructs it to look for the return type at the end of the function. In the example below, the return type of function `compose` is the return type of operator+ that sums values of types `T1` and `T2`. + +```c++ +template +auto compose(T1 t1, T2 t2) -> decltype(t1 + t2) +{ + return t1+t2; +} +auto v = compose(2, 3.14); // v's type is double +``` + +## nullptr + +Zero used to be the value of `null` pointers, and that has drawbacks due to the implicit conversion to integral types. The keyword `nullptr` denotes a value of type `std::nullptr_t` that represents the `null` pointer literal. Implicit conversions exists from `nullptr` to `null` pointer value of any pointer type and any pointer-to-member types, but also to `bool` (as `false`). But no implicit conversion to integral types exist. + +```c++ +void foo(int* p) {} + +void bar(std::shared_ptr p) {} + +int* p1 = NULL; +int* p2 = nullptr; +if(p1 == p2) +{ +} + +foo(nullptr); +bar(nullptr); + +bool f = nullptr; +int i = nullptr; // error: A native nullptr can only be converted to bool or, + // using reinterpret_cast, to an integral type +``` + +For backward compatibility, `0` is still a valid `null` pointer value. + +## 基于范围的循环 + +C++11 augmented the `for` statement to support the "`foreach`" paradigm of iterating over collections. In the new form, it is possible to iterate over C-like arrays, initializer lists and anything for which the non-member `begin()` and `end()` functions are overloaded. + +This for each `for` is useful when you just want to get and do something with the elements of a collection/array and don't care about indexes, iterators or number of elements. + +```c++ +std::map> map; +std::vector v; +v.push_back(1); +v.push_back(2); +v.push_back(3); +map["one"] = v; + +for(const auto& kvp : map) +{ + std::cout << kvp.first << std::endl; + + for(auto v : kvp.second) + { + std::cout << v << std::endl; + } +} + +int arr[] = {1,2,3,4,5}; +for(int& e : arr) +{ + e = e*e; +} +``` + +## Override 和 Final + +I always found the `virtual` methods badly designed in C++ because there wasn't (and still isn't) a mandatory mechanism to mark `virtual` methods as overridden in derived classes. The `virtual` keyword is optional and that makes reading code a bit harder, because you may have to look through the top of the hierarchy to check if the method is `virtual`. I have always used, and encouraged people to use the `virtual` keyword on derived classes also, to make the code easier to read. However, there are subtle errors that can still arise. Take for instance, the following example: + +```c++ +class B +{ +public: + virtual void f(short) {std::cout << "B::f" << std::endl;} +}; + +class D : public B +{ +public: + virtual void f(int) {std::cout << "D::f" << std::endl;} +}; +``` + +`D::f` is supposed to override `B::f`. However, the signatures differ, one takes a `short`, one takes an `int`, therefore `B::f` is just another method with the same name (and overload) and not an override. You may call `f()` through a pointer to `B` and expect to print `D::f`, but it's printing `B::f`. + +Here is another subtle error: the parameters are the same, but the method in the base class is marked `const`, while the method in the derived is not. + +```c++ +class B +{ +public: + virtual void f(int) const {std::cout << "B::f " << std::endl;} +}; + +class D : public B +{ +public: + virtual void f(int) {std::cout << "D::f" << std::endl;} +}; +``` + +Again, these two are overloads and not overrides, so if you call `f()` through a pointer to `B`, it will print `B::f` and not `D::f`. + +Fortunately, there is now a way to describe your intentions. Two new special identifiers (not keywords) have been added: `override`, to indicate that a method is supposed to be an override of a `virtual` method in a base class, and `final`, to indicate that a derived class shall not override a `virtual` method. The first example would become: + +```c++ +class B +{ +public: + virtual void f(short) {std::cout << "B::f" << std::endl;} +}; + +class D : public B +{ +public: + virtual void f(int) override {std::cout << "D::f" << std::endl;} +}; +``` + +This now triggers a compiler error (the same error you'd get for the second example too, if using the `override` specifier): + +```c++ +'D::f' : method with override specifier 'override' did not override any base class methods +``` + +On the other hand, if you intend to make a method impossible to override any more (down the hierarchy), mark it as `final`. That can be in the base class, or any derived class. If it's in a derived classe, you can use both the `override` and `final` specifiers. + +```c++ +class B +{ +public: + virtual void f(int) {std::cout << "B::f" << std::endl;} +}; + +class D : public B +{ +public: + virtual void f(int) override final {std::cout << "D::f" << std::endl;} +}; + +class F : public D +{ +public: + virtual void f(int) override {std::cout << "F::f" << std::endl;} +}; +``` + +```c++ +function declared as 'final' cannot be overridden by 'F::f' +``` + +## 强类型枚举 + +"Traditional" `enum`s in C++ have some drawbacks: they export their enumerators in the surrounding scope (which can lead to name collisions, if two different `enum`s in the same have scope define enumerators with the same name), they are implicitly converted to integral types and cannot have a user-specified underlying type. + +These issues have been fixed in C++ 11 with the introduction of a new category of `enum`s, called strongly-typed enums. They are specified with the `enum class` keywords. They no longer export their enumerators in the surrounding scope, are no longer implicitly converted to integral types and can have a user-specified underlying type (a feature also added for traditional `enum`s). + +```c++ +enum class Options {None, One, All}; +Options o = Options::All; +``` + +## 智能指针 + +There have been tons of articles written on this subject, therefore I just want to mention the smart pointers with reference counting and auto releasing of owned memory that are available: + +- [unique_ptr](http://en.cppreference.com/w/cpp/memory/unique_ptr): should be used when ownership of a memory resource does not have to be shared (it doesn't have a copy constructor), but it can be transferred to another `unique_ptr` (move constructor exists). +- [shared_ptr](http://en.cppreference.com/w/cpp/memory/shared_ptr): should be used when ownership of a memory resource should be shared (hence the name). +- [weak_ptr](http://en.cppreference.com/w/cpp/memory/weak_ptr): holds a reference to an object managed by a `shared_ptr`, but does not contribute to the reference count; it is used to break dependency cycles (think of a tree where the parent holds an owning reference (`shared_ptr`) to its children, but the children also must hold a reference to the parent; if this second reference was also an owning one, a cycle would be created and no object would ever be released). + +On the other hand, the `auto_ptr` is obsolete and should no longer be used. + +When you should use `unique_ptr` and when you should use `shared_ptr` depends on the ownership requirements and I recommend reading this [discussion](http://stackoverflow.com/questions/15648844/using-smart-pointers-for-class-members). + +The first example below shows `unique_ptr`. If you want to transfer ownership of an object to another `unique_ptr`, use `std::move` (I'll discuss this function in the last paragraph). After the ownership transfer, the smart pointer that ceded the ownership becomes `null` and `get()` returns `nullptr`. + +```c++ +void foo(int* p) +{ + std::cout << *p << std::endl; +} +std::unique_ptr p1(new int(42)); +std::unique_ptr p2 = std::move(p1); // transfer ownership + +if(p1) + foo(p1.get()); + +(*p2)++; + +if(p2) + foo(p2.get()); +``` + +The second example shows `shared_ptr`. Usage is similar, though the semantics are different since ownership is shared. + +```c++ +void foo(int* p) +{ +} +void bar(std::shared_ptr p) +{ + ++(*p); +} +std::shared_ptr p1(new int(42)); +std::shared_ptr p2 = p1; + +bar(p1); +foo(p2.get()); +``` + +The first declaration is equivalent to this one. + +```c++ +auto p3 = std::make_shared(42); +``` + +[make_shared](http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared) is a non-member function and has the advantage of allocating memory for the shared object and the smart pointer with a single allocation, as opposed to the explicit construction of a `shared_ptr` via the contructor, that requires at least two allocations. In addition to possible overhead, there can be situations where memory leaks can occur because of that. In the next example, memory leaks could occur if `seed()` throws an error. + +```c++ +void foo(std::shared_ptr p, int init) +{ + *p = init; +} +foo(std::shared_ptr(new int(42)), seed()); +``` + +No such problem exists if using `make_shared`. The third sample shows usage of `weak_ptr`. Notice that you must always get a `shared_ptr` to the referred object by calling `lock()`, in order to access the object. + +```c++ +auto p = std::make_shared(42); +std::weak_ptr wp = p; + +{ + auto sp = wp.lock(); + std::cout << *sp << std::endl; +} + +p.reset(); + +if(wp.expired()) + std::cout << "expired" << std::endl; +``` + +If you try to lock on an expired `weak_ptr` (the object is weakly reference has been released), you get an empty `shared_ptr`. + +## 匿名函数 + +Anonymous functions, called lambda, have been added to C++ and quickly rose to prominence. It is a powerful feature borrowed from functional programming, that in turn enabled other features or powered libraries. You can use lambdas wherever a function object or a functor or a `std::function` is expected. You can read about the syntax [here](http://msdn.microsoft.com/en-us/library/dd293603.aspx). + +```c++ +std::vector v; +v.push_back(1); +v.push_back(2); +v.push_back(3); + +std::for_each(std::begin(v), std::end(v), [](int n) {std::cout << n << std::endl;}); + +auto is_odd = [](int n) {return n%2==1;}; +auto pos = std::find_if(std::begin(v), std::end(v), is_odd); +if(pos != std::end(v)) + std::cout << *pos << std::endl; +``` + +A bit trickier are recursive lambdas. Imagine a lambda that represents a Fibonacci function. If you attempt to write it using `auto`, you get a compilation error: + +```c++ +auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);}; +``` + +```c++ +error C3533: 'auto &': a parameter cannot have a type that contains 'auto' +error C3531: 'fib': a symbol whose type contains 'auto' must have an initializer +error C3536: 'fib': cannot be used before it is initialized +error C2064: term does not evaluate to a function taking 1 arguments +``` + +The problem is `auto` means the type of the object is inferred from its initializer, yet the initializer contains a reference to it, therefore needs to know its type. This is a cyclic problem. The key is to break this dependency cycle and explicitly specify the function's type using `std::function`. + +```c++ +std::function lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n-1) + lfib(n-2);}; +``` + +## 非成员begin()和end() + +You probably noticed that I have used in the samples above non-member `begin()` and `end()` functions. These are a new addition to the standard library, promoting uniformity, consistency and enabling more generic programming. They work with all STL containers, but more than that, they are overloadable, so they can be extended to work with any type. Overloads for C-like arrays are also provided. + +Let's take, for instance, the previous example where I was printing a vector and then looking for its first odd element. If the `std::vector` was instead a C-like array, the code might have looked like this: + +```c++ +int arr[] = {1,2,3}; +std::for_each(&arr[0], &arr[0]+sizeof(arr)/sizeof(arr[0]), [](int n) + {std::cout << n << std::endl;}); + +auto is_odd = [](int n) {return n%2==1;}; +auto begin = &arr[0]; +auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]); +auto pos = std::find_if(begin, end, is_odd); +if(pos != end) + std::cout << *pos << std::endl; +``` + +With non-member `begin()` and `end()`, it could be put as this: + +```c++ +int arr[] = {1,2,3}; +std::for_each(std::begin(arr), std::end(arr), [](int n) {std::cout << n << std::endl;}); + +auto is_odd = [](int n) {return n%2==1;}; +auto pos = std::find_if(std::begin(arr), std::end(arr), is_odd); +if(pos != std::end(arr)) + std::cout << *pos << std::endl; +``` + +This is basically identical code to the `std::vector` version. That means we can write a single generic method for all types supported by `begin()` and `end()`. + +```c++ +template +void bar(Iterator begin, Iterator end) +{ + std::for_each(begin, end, [](int n) {std::cout << n << std::endl;}); + + auto is_odd = [](int n) {return n%2==1;}; + auto pos = std::find_if(begin, end, is_odd); + if(pos != end) + std::cout << *pos << std::endl; +} + +template +void foo(C c) +{ + bar(std::begin(c), std::end(c)); +} + +template +void foo(T(&arr)[N]) +{ + bar(std::begin(arr), std::end(arr)); +} + +int arr[] = {1,2,3}; +foo(arr); + +std::vector v; +v.push_back(1); +v.push_back(2); +v.push_back(3); +foo(v); +``` + +## static_assert和Type Traits + +`static_assert` performs an assertion check at compile-time. If the assertion is `true`, nothing happens. If the assertion is `false`, the compiler displays the specified error message. + +```c++ +template +class Vector +{ + static_assert(Size < 3, "Size is too small"); + T _points[Size]; +}; + +int main() +{ + Vector a1; + Vector a2; + return 0; +} +``` ```c++ +error C2338: Size is too small +see reference to class template instantiation 'Vector' being compiled + with + [ + T=double, + Size=2 + ] +``` + +`static_assert` becomes more useful when used together with type traits. These are a series of classes that provide information about types at compile time. They are available in the [](http://www.cplusplus.com/reference/type_traits/) header. There are several categories of classes in this header: helper classes, for creating compile-time constants, type traits classes, to get type information at compile time, and type transformation classes, for getting new types by applying transformation on existing types. + +In the following example, function `add` is supposed to work only with integral types. + +```c++ +template +auto add(T1 t1, T2 t2) -> decltype(t1 + t2) +{ + return t1 + t2; +} +``` + +However, there are no compiler errors if one writes: + +```c++ +std::cout << add(1, 3.14) << std::endl; +std::cout << add("one", 2) << std::endl; +``` + +The program actually prints `4.14` and "`e`". But if we add some compile-time asserts, both these lines would generate compiler errors. + +```c++ +template +auto add(T1 t1, T2 t2) -> decltype(t1 + t2) +{ + static_assert(std::is_integral::value, "Type T1 must be integral"); + static_assert(std::is_integral::value, "Type T2 must be integral"); + + return t1 + t2; +} +``` + +```c++ +error C2338: Type T2 must be integral +see reference to function template instantiation 'T2 add(T1,T2)' being compiled + with + [ + T2=double, + T1=int + ] +error C2338: Type T1 must be integral +see reference to function template instantiation 'T1 add(T1,T2)' being compiled + with + [ + T1=const char *, + T2=int + ] +``` + +## 转移语义 + +This is yet another important and well covered topic from C++11, that one could write a series of articles, not just a paragraph. Therefore, I will not get into too many details, but encourage you to find additional readings, if you're not already familiar with the topic. + +C++11 has introduced the concept of `rvalue` references (specified with `&&`) to differentiate a reference to an `lvalue` or an `rvalue`. An `lvalue` is an object that has a name, while an `rvalue` is an object that does not have a name (a temporary object). The move semantics allow modifying `rvalue`s (previously considered immutable and indistinguishable from `const T&` types). + +A C++ class/struct used to have some implicit member functions: default constructor (only if another constructor is not explicitly defined) and copy constructor, a destructor and a copy assignment operator. The copy constructor and the copy assignment operator perform a bit-wise (or shallow) copy, i.e., copying the variables bitwise. That means if you have a class that contains pointers to some objects, they just copy the value of the pointers and not the objects they point to. This might be OK in some cases, but for many cases, you actually want a deep-copy, meaning that you want to copy the objects pointers refer to, and not the values of the pointers. In this case, you have to explicitly write copy constructor and copy assignment operator to perform a deep-copy. + +What if the object you initialize or copy from is an rvalue (a temporary). You still have to copy its value, but soon after the rvalue goes away. That means an overhead of operations, including allocations and memory copying that after all, should not be necessary. + +Enter the move constructor and move assignment operator. These two special functions take a T&& argument, which is an `rvalue`. Knowing that fact, they can modify the object, such as "stealing" the objects their pointers refer to. For instance, a container implementation (such as a vector or a queue) may have a pointer to an array of elements. When an object is instantiating from a temporary, instead of allocating another array, copying the values from the temporary, and then deleting the memory from the temporary when that is destroyed, we just copy the value of the pointer that refers to the allocated array, thus saving an allocation, copying a sequence of elements, and a later de-allocation. + +The following example shows a dummy buffer implementation. The buffer is identified by a name (just for the sake of showing a point revealed below), has a pointer (wrapper in an `std::unique_ptr`) to an array of elements of type `T` and variable that tells the size of the array. + +```c++ +template +class Buffer +{ + std::string _name; + size_t _size; + std::unique_ptr _buffer; + +public: + // default constructor + Buffer(): + _size(16), + _buffer(new T[16]) + {} + + // constructor + Buffer(const std::string& name, size_t size): + _name(name), + _size(size), + _buffer(new T[size]) + {} + + // copy constructor + Buffer(const Buffer& copy): + _name(copy._name), + _size(copy._size), + _buffer(new T[copy._size]) + { + T* source = copy._buffer.get(); + T* dest = _buffer.get(); + std::copy(source, source + copy._size, dest); + } + + // copy assignment operator + Buffer& operator=(const Buffer& copy) + { + if(this != ©) + { + _name = copy._name; + + if(_size != copy._size) + { + _buffer = nullptr; + _size = copy._size; + _buffer = _size > 0 > new T[_size] : nullptr; + } + + T* source = copy._buffer.get(); + T* dest = _buffer.get(); + std::copy(source, source + copy._size, dest); + } + + return *this; + } + + // move constructor + Buffer(Buffer&& temp): + _name(std::move(temp._name)), + _size(temp._size), + _buffer(std::move(temp._buffer)) + { + temp._buffer = nullptr; + temp._size = 0; + } + + // move assignment operator + Buffer& operator=(Buffer&& temp) + { + assert(this != &temp); // assert if this is not a temporary + + _buffer = nullptr; + _size = temp._size; + _buffer = std::move(temp._buffer); + + _name = std::move(temp._name); + + temp._buffer = nullptr; + temp._size = 0; + + return *this; + } +}; + +template +Buffer getBuffer(const std::string& name) +{ + Buffer b(name, 128); + return b; +} +int main() +{ + Buffer b1; + Buffer b2("buf2", 64); + Buffer b3 = b2; + Buffer b4 = getBuffer("buf4"); + b1 = getBuffer("buf5"); + return 0; +} +``` + +The default copy constructor and copy assignment operator should look familiar. What's new to C++11 is the move constructor and move assignment operator, implemented in the spirit of the aforementioned move semantics. If you run this code, you'll see that when b4 is constructed, the move constructor is called. Also, when `b1` is assigned a value, the move assignment operator is called. The reason is the value returned by `getBuffer()` is a temporary, i.e., an `rvalue`. + +You probably noticed the use of [std::move](http://en.cppreference.com/w/cpp/utility/move) in the move constructor, when initializing the name variable and the pointer to the buffer. The name is actually a `string`, and `std::string` also implements move semantics. Same for the `std::unique_ptr`. However, if we just said `_name(temp._name)` the copy constructor would have been called. For `_buffer`, that would not have been even possible because `std::unique_ptr` does not have a copy constructor. But why wasn't the move constructor for `std::string` called in this case? Because even if the object the move constructor for `Buffer` is called with is an `rvalue`, inside the constructor, it is actually an `lvalue`. Why? Because it has a name, "`temp`" and a named object is an `lvalue`. To make it again an `rvalue` (and be able to invoke the appropriate move constructor), one must use `std::move`. This function just turns an `lvalue` reference into an `rvalue` reference. + +**UPDATE**: Though the purpose of this example was to show how move constructor and move assignment operator should be implemented, the exact details of an implementation may vary. An alternative implementation was provided by [Member 7805758](http://www.codeproject.com/script/Membership/View.aspx?mid=7805758) in the comments. To be easier to see it, I will show it here: + +```c++ +template +class Buffer +{ + std::string _name; + size_t _size; + std::unique_ptr _buffer; + +public: + // constructor + Buffer(const std::string& name = "", size_t size = 16): + _name(name), + _size(size), + _buffer(size? new T[size] : nullptr) + {} + + // copy constructor + Buffer(const Buffer& copy): + _name(copy._name), + _size(copy._size), + _buffer(copy._size? new T[copy._size] : nullptr) + { + T* source = copy._buffer.get(); + T* dest = _buffer.get(); + std::copy(source, source + copy._size, dest); + } + + // copy assignment operator + Buffer& operator=(Buffer copy) + { + swap(*this, copy); + return *this; + } + + // move constructor + Buffer(Buffer&& temp):Buffer() + { + swap(*this, temp); + } + + friend void swap(Buffer& first, Buffer& second) noexcept + { + using std::swap; + swap(first._name , second._name); + swap(first._size , second._size); + swap(first._buffer, second._buffer); + } +}; +``` + +## 总结 + +There are many more things to say about C++11; this was just one of many possible beginnings. This article presented a series of core language and standard library features that every C++ developer should use. However, I recommend you additional readings, at least for some of these features. diff --git "a/\346\267\261\345\205\245\347\220\206\350\247\243C++11.md" "b/\346\267\261\345\205\245\347\220\206\350\247\243C++11.md" new file mode 100644 index 0000000000000000000000000000000000000000..1750664dc349d08eefbc63d6d6dc9e6c4e0a8bce --- /dev/null +++ "b/\346\267\261\345\205\245\347\220\206\350\247\243C++11.md" @@ -0,0 +1,519 @@ +# 前言 + +## 说到C++11,应该想到什么? + +- 什么是 lambda,及怎么样使用它是最好的? +- decltype 和 auto 类型推导有什么关系? + +- 什么是移动语义,以及(右值引用)是如何解决转发问题的? +- default/deleted 函数以及 override 是怎么回事? + +- 异常描述符被什么替代了? noexcept 是如何工作的? +- 什么是原子类型以及新的内存模型? + +- 如何在 C++11 中做并行编程? ## 语言的哪些关键字和C++11有关? + +- alignas +- alignof decltype + +- auto(重新定义) +- static_assert + +- using(重新定义) +- noexcept + +- export(弃用,不过未来可能留作他用) +- nullptr + +- constexpr +- thread_local # 保证稳定性和兼容性 + +## 将C99标准纳入C++11 + +### 确定编译环境的预定义宏 + +- __STDC_HOSTED__:编译器的目标系统环境中是否包含完整的C库 +- __STDC__:编译器对于标准C库的实现是否和C标准一致(是否定义、如何定义由编译器决定) + +- __STDC_VERSION__:编译器支持的C标准的版本(是否定义、如何定义由编译器决定) +- __STDC_ISO_10646__:yyyymmL格式的整数常量,表示C++编译环境符合某个版本的ISO/IEC 10646标准 ### __func__ 预定义标识符 + +函数中可以使用__func__标识符函数名称,编译器会在函数定义开始时隐式地插入__func__标识符定义: + +static const char* __func__ = "<函数名称>"; ### _Pragma 操作符 + +在之前的C/C++标准中已经规定了可以使用#pragma预处理指令向编译器传递信息,比如可以通过在头文件首行放置#pragma once来告诉编译器这个头文件只应该被include一次。 + +C++11中,规定了一个新的操作符_Pragma,它的作用与#pragma完全相同,_Pragma("once")就相当于#pragma once。但是,_Pragma由于是一个操作符而不是预处理指令,它的使用更为灵活,可以在宏中展开。 ### 不定参数宏定义以及 __VA_ARGS__ + +C99规定宏定义中,在参数列表的最后,可以使用...省略参数定义,而__VA_ARGS__可以用来替换被省略号代表的字符串。 ### 宽窄字符串连接 ## long long整形 + +## 扩展的整形 + +## 宏__cplusplus + +一般会和extern "C"配合使用,来让一个头文件即可以同时被include到C和C++中编译,使用extern "C"避免C++对符号名称进行重整,确保C++编译器编译出的目标文件中的符号名维持原样。 + +```c++ +#ifdef __cplusplus +extern "C" { +#endif +// 一些代码 +#ifdef __cplusplus +} +#endif +``` ## 静态断言 + +标准C库assert.h中,提供了assert函数来进行运行时的断言,表明某些情况一定不会发生。(在 C++ 中,程序员也可以定义宏 NDEBUG 来禁用 assert 宏。这对发布程序来说还是必 要的。因为程序用户对程序退出总是敏感的,而且部分的程序错误也未必会导致程序全部功 能失效。那么通过定义 NDEBUG 宏发布程序就可以尽量避免程序退出的状况。而当程序有 问题时,通过没有定义宏 NDEBUG 的版本,程序员则可以比较容易地找到出问题的位置。) + +如果希望在预处理时确定某些情况一定不会发生,也可以使用#if进行判断,使用#error终止编译流程并给出错误提示。 + +C++11引入static_assert来补充编译时断言。之前Boost库也实现了类似的功能,它是利用编译器在编译时会检查除以0这个特性,如果条件为假就构建一个除以0的语句迫使编译器报错来实现的,虽然可以达成编译时检查的目的,但报错信息比较不明确。使用C++11的static_assert,在断言失败时,可以得到明确的错误提示。 ## noexcept修饰符与noexcept操作符 + +### 作为修饰符 + +noexcept作为修饰符可以用来表明函数是否会抛出异常,noexcept修饰符后面可以跟随一个常量表达式,当常量表达式可以转为true时表示不会抛出异常,false则表示会抛出异常。被noexcept修饰符标记为不会抛出异常的函数如果运行时抛出异常,会直接导致程序调用std::terminate终止执行,异常不会继续沿栈传播。 + +### 作为操作符 + +noexcept作为操作符可以用来判断某个表达式是否会抛出异常。该操作符帮助我们在进行泛型编程时通过noexcept操作符判断一个依赖模板参数的表达式是否会抛出异常。 + +```c++ +template +void fun() noexcept(noexcept(T())) {} +``` ### 替代废弃的throw + +C++98曾经定义了throw修饰符,用来表明表达式是否会抛出异常,一个空的throw()就和noexcept(true)表达的意思一样,但throw要求明确指出会抛出哪些类型的异常,实际编程中大家很少需要了解异常类型,只希望了解是否会抛出异常,因此新的noexcept(false)用于替代throw(<异常类型>)。 ## 快速初始化成员变量 + +C++98中,允许直接在类的静态常量整型成员声明时使用“=”对其初始化,这种声明+初始化的做法被叫做“就地”声明。这种声明方式很便捷,但仅能对【静态】【常量】【整型】成员进行这样的声明,使用场景很少,语法也不一致。 + +C++11中允许对非静态成员进行就地的初始化,不再必须在构造函数中通过initializer-list进行初始化了。可以使用等号或者一对花括号{}进行就地初始化,其他形式无法通过编译。 + +在成员同时使用就地初始化,并且在构造函数的初始化列表中进行初始化时,最终仅会以初始化列表为准对其进行初始化。 ## 非静态成员的sizeof + +C++98中,sizeof可以作用于类的静态成员,但对于非静态成员,必须通过一个类实例来引用,因此之前为了获取一个非静态成员的大小,同时避免创建无用的类实例,通常会使用下面的技巧: + +sizeof(((People*)0)->hand); + +C++11后,sizeof可以直接作用于类成员表达式了,上面的技巧可以简化成: + +sizeof(People::hand) ## 扩展的friend语法 + +在C++98,如果要指定另一个类是当前类的友元,必须要使用friend class/struct <友元类>这样的写法,并且无法使用模板参数作为友元类,C++11允许省略class,而且可以使用模板参数作为友元类。 + +https://zh.cppreference.com/w/cpp/language/friend ## final/override + +final用于在继承关系的中间终止一个虚函数被子类重载的可能性。override用于显式声明某个函数是父类虚函数的重载,提升了代码可读性和健壮性(因为virtual修饰符只有在顶级父类声明虚函数时才是必须的,子类为同名虚函数添加virtual修饰符会被忽略,而override修饰符会被编=‘译器检查以确保函数的确重载了虚函数)。它们都在函数声明的参数列表之后。 + +为了尽可能兼容已有程序,C++11没有将final和override规定为关键字,因此可以在代码中使用final,override变量,但最好别这么做。 ## 模板函数的默认模板参数 + +C++98允许为类模板参数指定默认参数,但却禁止为函数模板参数指定默认参数,语法上不一致,逻辑上不合理。 + +C++11放宽了这一限制,可以为函数模板指定默认模板参数了。 ## 外部模板 + +C++中,如果多个源文件都使用了同一个模板,会对该模板进行多次实例化,但最后编译时,编译器会仅保留一份模板实例化产生的代码。在一些比较大的项目中,冗余的模板实例化会明显拖慢编译速度,可以使用“外部模板”技术告诉编译器不需要对模板进行实例化,在某一个源文件中仅进行一次显式实例化。 + +template void fun(int); // 显示地实例化 + +extern template void fun(int); // 外部模板的声明 ## 局部和匿名类型作为模板实参 + +C++98禁止局部或者匿名的类型作为模板参数,这个限制没什么道理,因此C++11放宽了该限制。 ```c++ +// C++98中,只有A或者a可以作为模板参数 +struct A {int a;} a; +// 匿名类型 +typedef struct {int a;} B; +// 匿名类型变量 +struct {int a;} b; + +void test() { + // 局部类型 + struct C {int a;} c; +} +``` # 通用为本,专用为末 + +## 继承构造函数 + +继承关系中,子类可以自动或得父类的成员和接口,但构造函数无法自动地被子类继承。因为通常子类也有自己的成员,我们要定义子类自己的构造函数,在子类构造函数中去调用父类构造函数以及初始化自己的成员。 + +但是,如果子类中没有任何成员,或者其成员都用C++11的新特性“快速初始化成员变量”进行了初始化乃至于没有必要再用构造函数初始化了,那这时候我们很可能希望直接将父类的构造函数继承到子类,毕竟这时候只需要初始化父类成员。C++11允许我们使用using <父类名>::<父类名>来将所有父类构造函数引入子类,被using引入的父类构造函数是隐式声明的(也就是说,只有用到的函数才会被生成,以节省生成的代码)。 + +(书中这一节很多描述都和XCode实验现象对不上,很可能是因为成书时还无实验环境,导致描述有误) ## 委派构造函数 + +可以在构造函数的initializer-list中调用另一个构造函数来完成构造,这种将构造委托给另一个构造函数的行为 就叫委派构造函数。 + +一旦在initializer-list中进行了委派构造,就不能再用正常的initializer-list初始化成员变量了。因此,通常被委派的构造函数会负责初始化类的所有成员变量。 ## 右值引用 + +可以使用两个引用符号 && 来声明并定义一个右值引用。和左值引用一样,右值引用的声明和定义必须在一块。 + +在C++98中,已经对右值有了一些描述,C++11对右值进行了更进一步的定义:右值就是将亡值。 + +**C++98中,可以使用常量左值引用来引用右值**,比如: + +const MyCls &myRef = getTemp(); + +这样的常量左值引用的确引用了getTemp返回的临时变量,延长了它的声明周期,但由于C++98对于右值的定义是“不可被改变的常量”,因此之前只能使用对const的引用来引用右值。 + +C++11改变了对右值的定义,因此使用C++11的右值引用方式引用的右值,其内容可以被修改。 ### 移动语意 + +在C++中,如果自定义类中含有指针,通常需要自定义拷贝构造函数和赋值运算符,在对指针进行赋值时,要为指针开辟新的堆内存,并将原指针内容拷贝过来,不要使用编译器生成的默认函数。因为默认函数在赋值时通通采用浅拷贝,会导致两个对象的指针指向同一地址,几乎一定会导致野指针问题。 + +但是,有时候我们拷贝或赋值时,比如a = b,其中b如果是一个右值,那么直接将b的指针赋值给a,并且阻止b对象在析构函数被调用时释放指针内存,是更合适的做法。因此如果b是一个右值,这意味着b马上就要被析构了,与其为a的指针开辟一片内存,不如直接利用b的指针现在使用的内存。**这种做法就被称作“移动”b的资源到a,也就是“移动语意”。** + +C++11中可以通过声明移动构造函数/赋值函数实现移动语意,这样的函数和普通函数的区别在于它们接受的参数类型是右值引用,因此当这样的函数被调用时,可以确保被引用的值马上就要被销毁,可以直接移动其资源。 + +移动构造函数应该是不会抛出异常的,因为如果移动到一半被终止了,会导致对象的一部分指针成员变成悬挂指针。标准库提供了move_if_noexcept函数,它会判断对象是否实现了noexcept的移动构造函数,如果实现了才返回右值引用,不然就返回左值引用,避免使用移动语意,退化为使用普通的拷贝。 ### 完美转发 + +#### 右值引用的问题 + +有了右值引用,看起来我们可以完美地实现移动语意了,但是,需要留意的是,我们在将右值赋给一个右值引用后,这个右值引用其实会被当成一个左值引用(毕竟移动语意本身就要求对右值引用进行修改)!类似的,右值引用的成员也是一个左值。 + +因此,在访问右值引用,或者在访问右值引用的成员时,必须将其转换成右值引用,否则就会被当成普通的左值引用。 + +```c++ +// 像这样的声明赋值没有意义,实际上,a依然会成为一个左值引用 +// A &&a = getTemp(); +A &a = getTemp(); +acceptRValueRef(std::move(a)); // OK,这里使用move把一个被当作左值引用的右值引用转成右值引用 +accestRValueRef(std::forward(a)); // OK,forward也能起到转为右值引用的作用 +``` + +这个现象要求我们在创建移动构造函数时,必须要使用标准库中提供的std::move对右值引用的每一个成员转为右值引用,来保证移动语意。std::move会将其参数转化为一个右值引用。之所以可以进行这样的转换,是因为我们已经知道了拥有成员的对象是一个右值引用,既然成员对象的拥有者本身马上就要被销毁,那么成员对象也一定马上就会被销毁,此时将成员对象转为左值处理才是正确的。 + +在将右值引用传入参数为右值引用的函数时,编译器会报错,因为右值引用实际上一旦被赋给引用变量,就会被当成左值引用。要让编译器重新将其重新当成一个右值引用,必须使用std::move,std::forward将其转成右值引用。 #### 引用折叠 + +为了在模板编程时,让模板能够同时处理左值和右值引用,C++11引入了引用折叠的规则: ```c++ +using MyClsLRef = MyCls&; +using MyClsRRef = MyCls&&; + +// C++11中被引用折叠规则理解为左值引用 +MyClsLRef&& lRef = getMyCls(); +// 下面两行是一样的,其中第一行在C++11中被引用折叠规则理解为右值引用 +MyClsRRef&& rRef = getMyCls(); +MyClsRRef rRef = getMyCls(); + +// 利用引用折叠规则,可以在模板编写中将参数声明为左值引用类型,这样的模板函数实际上可以同时接收 +// 左值引用和右值引用 +template +void test(T&& t) { ... } +// 当T是一个右值引用时,T&&&&被折叠成右值引用 +// 当T是一个左值引用时,T&&&被折叠成左值引用 +// 不用考虑T不是一个引用,会有这样的考虑说明对C++不够熟悉,函数参数被声明为引用,传进来的肯定是引用 +``` 除了std::move,标准库还提供了std::forward,它的作用其实和std::move有重叠,都可以用来将变量转换为右值引用的。只不过它被规定应该专门用于“转发”场景,并且在调用时必须指定模板参数,从而可以利用引用折叠规则,将参数的左右值引用保留下来: + +```c++ +A getTemp() { + return A(); +} + +// 转发函数 +void forwardToTest(A&& a) { + // do something +// test(a); 无法通过编译,因为一旦右值引用被赋给变量,这个变量就表现成了左值引用 + test(std::forward(a)); +} + +// 转发函数 +template +void forwardToTestTemplate(T&& a) { +// test(a); 同样无法通过编译 + test(std::forward(a)); +} + +template +void test(T&& a) { + +} + + +int main() { + forwardToTest(getTemp()); + forwardToTestTemplate(getTemp()); + A a; +// forwardToTest(a); 无法通过编译,因为a不是右值引用 + forwardToTestTemplate(a); // 可以通过编译,因为模板方法有引用折叠规则 +} +``` ### move和forward的区别 + +- move调用时不需要提供模板参数,它仅被用于将参数强制转为右值引用; +- forward调用时必须要提供模板参数,通常会提供这样的模板参数:forward,这样的好处是T如果被声明为左值,转换后还是左值,T如果被声明为右值,转换后还是右值。 ## explicit 显示转换操作符 + +默认情况下,C++编译器会在函数调用的参数和函数声明不匹配时,想方设法地将参数转为匹配的类型,让函数调用能够通过,这中间会检查: + +- 实参的类型转换运算符,如果有转换为目标类型的转换运算符就调用; +- 目标类型的构造函数,看是否有接收实参类型的构造函数,如果有就调用; + +有时这很方便,但更多场景下这样的行为只会导致语意上的混乱。为了避免编译器的隐式转换,可以使用explicit修饰类型转换运算符或构造函数,这样编译器就不会尝试使用对应函数进行转换。 ## initializer_list 初始化列表 + +### 如何使用初始化列表 + +C++98中,仅允许使用initializer-list初始化数组,C++11扩展了initializer-list的概念,使得普通类型也可以使用initializer-list初始化(不要把它和类的成员初始化搞混,它们的确都叫initializer-list,要区分时,可以将类的成员初始化叫做member initializer list): ```c++ +int a[]={1,3,5};//C++98通过,C++11通过 +int b[]{2,4,6};//C++98失败,C++11通过 +vector c{1,3,5};//C++98失败,C++11通过 +map d = {{1,1.0f},{2,2.0f},{5,3.2f}};//C++98失败,C++11通过 +``` 如果要让自定义的类支持这种初始化方式,只要声明一个接收在中定义的initializer_list类型的构造函数就可以了。该类型是一个Iterable类型,可以使用begin, end等标准遍历方法。 ### 防止类型收窄 + +使用初始化列表还可以促使编译器检查类型收窄的情况。初始化列表是目前唯一一种检查类型收窄的方式(不过事实上现在的大多数编译器在没有使用初始化列表时也会检查类型收窄并给出警告,但使用初始化列表编译器会直接给出错误)。 ## POD类型 + +POD,也就是Plain Ordinary Data,纯数据类型。 + +C++11对POD的定义是:平凡的,且是标准布局的。定义上,比C++98更宽容了,C++98只规定了C风格的struct是POD,但从POD的定义上,只要类对象布局是标准的,这样的类应该都是POD类。 + +(但是,对POD定义得更宽容似乎并没有什么意义?C++11更多的是对于哪些情况会导致对象布局变化进行了更进一步的明确,只有不导致对象布局变化的类定义才是POD类。) ## union 非受限联合体 + +C++11将C++98对union的一些限制移除了。 + +- 在C++98,union中只能包含基础类型和POD类型,并且不能包含静态方法,但在C++11中,union中可以包含任意非引用类型。 +- C++11中,如果union任何一个成员拥有非平凡的构造函数,那么编译器就不会为union生成默认构造函数。 + +- C++11中,允许在类定义使用union声明成员变量,用这种方式声明的union不需要有类型名称,被称为匿名的非受限联合体,此时联合体内的所有成员都会自动的成为类的“变长成员”,即实际上它们共享同一处内存,应该只使用它们中的某一个。 ## 用户定义字面量 + +### 字面量操作符 + +- 接收字符串:<用户类型> operator "" _<后缀字符>(const char* col, size_t size) +- 接收整数:<用户类型> operator "" _<后缀字符>(unsigned long long val) + +- 接收整数,但整数越界,此时会传入'\0'结尾的char*:<用户类型> operator "" _<后缀字符>(const char*) +- 接收浮点:<用户类型> operator "" _<后缀字符>(long double val) + +- 接收字符:<用户类型> operator "" _<后缀字符>(char val) ## 内联名字空间 + +C++11规定可以在namespace前加上inline,将名字空间默认导出到声明名字空间的作用域。 + +这样的行为和C++98就有的匿名名字空间非常类似,除了内联名字空间有自己的名字以外。不过,它们被创建出来的目的是不同的: + +- 匿名名字空间在C++98中用于替代static修饰符,因为C++为类引入了static成员后,static的语意变得非常模糊且矛盾,因此在原本使用static声明文件作用域的变量的地方,可以改成使用匿名名字空间来包围这些变量起到同样的效果; +- 内联名字空间则是被标准库用于和宏配合使用,根据当前编译环境决定默认导出同一个功能的哪一个版本的实现,这样做的好处是不关心具体实现的用户可以直接使用默认导出的功能,而了解更全面的细节的用户也可以使用名字空间来指定使用的是哪一个版本的功能。 ## 使用using声明模板别名 + +在C++11中,已经可以使用using完全替代typedef了。 + +using不仅有更清晰的语意,还可以部分声明模板参数: ```c++ +template using StringObjectMap = std::map; +StringObjectMap myMap; +``` ## SFINAE规则 + +SFINAE,就是Substitution Failure Is Not An Error。 + +指的是,编译器在尝试模板参数匹配时,只要能够最终找到合适的匹配,中间尝试过的任何匹配失败都不会报错。 + +只不过,C++98对于模板参数中使用表达式的情况支持的不友好,C++11明确了任何在编译期合法的表达式都能够作为模板参数,比如下面的这个例子就在C++98中无法通过编译,而在C++11中可以: ```c++ +template struct A{};char xxx(int);char xxx(float);template A f(T){} +``` # 新手易学,老兵易用 + +## 右尖括号的改进 + +C++98曾经规定应该把 >> 优先判定为右移操作符,但这个规定在C++11被取消了,C++11规定编译器可以自行智能地判断>>是否是右移操作符。 ## auto类型推导 + +在C++98中,auto其实是用于声明一个变量具有“自动存储期”,但实际上除了static以外的变量都是默认具有自动存储期的,因此过去的auto几乎没有人使用。 + +C++11中,auto被赋予了新的含义,以前的auto含义被取消了。auto成为了新的类型指示符,auto声明的变量的类型必须在编译期由编译器推导出来。 ## decltype + +decltype是在编译时对表达式进行类型推导,推导出的类型可用于定义新的变量。decltype主要是为了解决泛型编程中由于泛型参数类型不确定,导致和泛型参数相关的表达式类型无法确定的问题的。比如: + +t1是泛型Type1类型,t2是泛型Type2类型,由于开发泛型代码时无法确定Type1和Type2的类型,自然无法确定t1 + t2的类型,但该类型其实是可以由编译器推导出来的,decltype如今即可用在此处: + +auto add(t1, t2) -> decltype(t1 + t2) {return t1 + t2;} + +或者可以声明原本无法声明的变量:decltype(t1 + t2) addResult = t1 + t2;(当然,auto也可以完成此工作)。 有时会将decltype和auto搭配使用:decltype(auto)。这是因为一方面,我们希望依赖C++11的类型推导能力(auto的作用),但另一方面,又想保留cv限定符和引用(decltype的作用)(auto的类型推导规则和模板类似,如果不把auto声明为auto&或者auto*,auto就会被视为不具备cv限定符的值类型,如果auto被声明为auto&或者auto*,auto推导出的类型才会保留cv限定符)。但是使用这样的写法时要小心:decltype在推导类型时,如果表达式是一个简单的名字,它会推导出名字的类型,但如果表达式不只是一个名字,比如decltype((x)),那么即使x只是一个int,该decltype也会推导出引用类型:int&。 ## 追踪返回类型 + +auto func(char* a,int b) -> int; ## 基于范围的for循环 + +自定义集合类型要想支持这样的for循环,需要实现begin, end, ++, ==四个函数。 # 提高类型安全 + +## 强类型枚举 + +C++98: enum X {...}; + +C++11: enum class X {...}; ## 堆内存管理:智能指针与垃圾回收 # 提高性能及操作硬件的能力 + +## 常量表达式 + +C++11规定使用constexpr修饰符来修饰常量表达式。常量表达式可以是函数或者值。常量表达式函数中不可以出现非常量表达式。常量表达式可以在编译期使用,但如果常量表达式并没有一定要在编译期被计算出来,标准规定编译器在这种情况下可以将常量表达式编译成普通的表达式,表达式会在运行时被计算。 ## 变长模板 + +过去,C++可以使用C风格的方法来实现可变参函数,但这种实现方式是类型不安全的。 + +现在,C++11为可变参函数提出了更合理(类型安全)的解决方案:变长模板。可以使用变长模板来声明变长模板函数或者变长模板类。 ### 模板参数包与递归 + +使用template 这种方式可以声明一个变长模板参数,使用Elements...这种方式可以将变长模板参数展开成实际的多个参数类型; + +不定长的变长模板类可以通过模板类的递归来解包: ```c++ +template class tuple; // 变长模板声明 +// 以下是两个模板偏特化定义,利用模板偏特化会被优先匹配的规则,让变长模板参数递归地被解包 +// 对于类型,可以使用递归的继承 +template +class tuple : private tuple { + Head head; +} +template <> class tuple {}; + +// 对于函数,可以使用递归的函数调用 +// 下面实现一个更强大的Printf,不论%后面跟的是什么符号,这个Printf总是会打印正确的类型 +void Printf(const char* s) { + while (*s) { + if (*s == '%' && ++s != '%') { + throw runtime_error("invalide format"); + } + cout *s++; + } +} + +template +void Printf(char* s, T value, Args... args) { + while(*s) { + if (*s == '%' && *s++ != '%') { + cout << value; + return Printf(++s, args...); + } + cout << *s++; + } + // 若百分号的数量和参数数量对不上,就抛异常 + throw runtime_error("extra arguments provided"); +} +``` ### 进阶 + +#### 引用类型 + +定义了模板参数包后,还可以在展开模板参数包时使用引用标记:Args&&...,这样的写法是合法的; + +#### 特殊展开 + +解包时,有些非常特殊的规则,需要特别说明一下: ```c++ +template class MyCls: private A... {}; +// 上面的表达式在解包时会解包成多继承: +T t; // t的类型是:class MyCls: private A, A + +template class MyCls: private A {}; +// 而这个表达式在解包时,会在泛型参数表达式中直接展开 +T t; // t的类型是:class MyCls: private A + +template void test(Args... args) { + // 下面这个会被展开成call(a(arg1), a(arg2), ...) + call(a(args)...); + // 而下面这个会被展开成call(a(arg1, arg2, ...)) + call(a(args...)); +} +``` + +#### 获取变长参数包长度 + +可以使用sizeof...获取模板参数包的长度; #### 模板的模板(的模板的模板...) + +变长参数的模板类型本身也可以是一个模板,这一点和以前的非变长模板参数一样。 + +## 原子类型和原子操作 + +C++11以前,已经有很多使用多线程能力的C++程序了,但之前语言本身并没有定义任何同多线程有关的内容,这些多线程能力来自于多线程接口pthread。pthread是一套C的接口。 + +通常情况下,如果我们不需要太精细的互斥控制,可以直接使用posix提供的mutex互斥锁API,而如果想达到更优化的性能,可能会考虑为不同处理器编写内敛汇编代码。 + +C++11标准为多线程程序在标准库中添加了原子类型,并允许指定原子类型的内存访问顺序一致性,让开发者可以不必操心操作系统和处理器的底层细节,也可以获得最优化的性能。 ## 线程局部存储 + +C++11定义了thread_local关键字来定义线程局部存储变量,这样的变量生命期是线程启动到线程结束,除了本线程外,没有其他线程可以访问到这样的变量。 + +C++11仅规定了线程局部存储的行为,而没有规定其具体实现,不同的编译器在不同的环境中可能会有不同的实现方式。 ## 快速退出:quick_exit, at_quick_exit + +在过去,大体上有三种退出程序的方式:terminate(), abort(), exit()。 + +- terminate是有未处理的异常时会被调用的方法,可以使用set_terminate方法更改默认行为,terminate默认调用abort; +- abort是进程不得不终止时,被调用的函数,它会向本进程发送一个SIGABRT信号,该信号默认会导致操作系统直接释放该进程的所有资源并终止进程; + +- exit是进程自发调用的退出函数,它代表着进程运行到了某个节点,该退出了,它会导致每一个自动变量的析构函数被调用(是的,仅自动变量,也就是栈变量会被调用析构函数,至于单例什么的需要自己处理),并调用at_exit注册的函数,然后才会回收进程; + +C++11新增了quick_exit标准方法,该方法语意上和exit一样,都是程序自发终止,但和exit不同的是,它不会进行本进程的清理工作,在多线程环境下也不会先等待线程结束,而是直接让操作系统终止进程并回收资源。 # 为改变思想方式而改变 + +## 指针空值-nullptr + +C++11定义了nullptr_t和nullptr,前者是类型,后者是该类型的值。 + +nullptr可以隐式转换为任何指针,但无法被隐式转换为bool类型,无法使用if (nullptr)这样的表达式;此外它所属的nullptr_t是一个基础类型,nullptr无法被推导为T*这样的模板参数。 ## 默认函数的控制 + +C++11规定可以使用 = default 来使编译器生成默认版本的成员函数,可以由编译器生成的函数包括: + +- 空构造函数 +- 拷贝构造函数 + +- 拷贝赋值函数 +- 移动构造函数 + +- 移动拷贝函数 +- 析构函数 + +此外,编译器还为所有自定义类型提供以下全局默认操作符函数: + +- operator, +- operator& + +- operator&& +- operator* + +- operator-> +- operator->* + +- operator new +- operator delete ## lambda函数 + +lambda语法: + +[<捕获外部变量>]<(可选)mutable>(<(可选)参数列表>) -> <(可选)返回值> {<函数体>} + +- 捕获外部变量:变量需要用&或者=开头来引用,直接写&或者=后面不跟变量表示捕获所有外部变量,=表示按值捕获,&表示引用捕获; +- mutable:labmda默认是内联的const函数,不可以修改任何捕获的外部的按值捕获的变量(因为目前lambda的语意其实和仿函数完全一致,在仿函数中,所有捕获的外部变量都是仿函数类的成员,因此const函数不可以修改类成员,到lambda这里变成了lambda不可以修改捕获变量),但是引用捕获的变量则可以修改(这个行为也是和仿函数一致的,const函数内可以调用任意成员引用的方法,修改其属性,因为将引用声明成const,和将指针声明成const类似,都仅仅是禁止修改引用本身,但并没有限制对引用或者指针指向的变量进行修改)。如果希望lambda不是一个const函数,就要添加mutable声明; + +- 参数列表: +- 返回值:当能够从函数体中推测出明确的返回值类型时,可以忽略; + +- 函数体: # 融入实际应用 + +## 对齐支持 + +C和C++都是具备直接操作底层硬件能力的语言,因此某些开发者会对数据结构的对齐方式特别关注。 + +C++11规定了alignof和alignas关键字,前者可以查询某个类型的对齐方式(一般来说都是32位-4字节或者64位-8字节对齐),而后者可以规定某个自定义类的对齐方式。比如如果我们想要使用内联汇编的向量指令(可以同时处理4组处理器位数的数据)来优化处理速度,就可能想要将数据对齐到4*处理器位数的位置处。 + +需要注意的是,之前很多编译器也规定了指定数据对齐方式的方式,比如GNU就规定可以使用如下方式规定对齐字节:__attribute__((__aligned__(8))); + +需要注意的是,虽然标准规定了指定对齐的方式,但每个平台具体支持对齐到多少是不确定的。如果使用alignas(2^64),那显然是不合法的。不幸的是,目前标准似乎没办法查询每个平台支持的最大对齐字节。不过一般来说我们也用不到太大的对齐字节。 ## 通用属性 + +有时C/C++提供的语言能力无法完全满足开发者的需求,编译器厂商为了解决这些问题,提供了一系列的语言扩展来扩展C/C++的语法。这其中最常见的就是“属性”,可以告诉编译器一个符号的额外信息,让编译器做一些语言规范之外的处理。 + +- GNU使用__attribute__((<属性列表>))来声明属性; +- Windows使用__declspec(<属性列表>)来声明属性 + +C++11也规定了类似的属性,之所以在语言中添加属性,是为了避免再给C++增加更多的关键字。C++11的属性和之前各个平台的编译器实现的属性的目的是一致的,它们提供的能力都是一般用不到,可以忽略的能力,语言规范不会考虑使用关键字来实现这些能力,因此将它们定义到通用属性里。C++规定通用属性的写法是:[[<属性列表>]],这样的通用属性可以用来修饰任何语言元素。不过目前C++11只定义了两个通用属性:[[noreturn]]和[[carries_dependency]]。 ## Unicode支持 + +### 字符集和编码 + +ASCII码是最早的编码,使用7位二进制位来表示所有英文字母和在英文排版印刷中用到的字符,后来ISO和Unicode组织共同制定了一套能够唯一的表示世界上所有字符的标准字符集,称为ISO/Unicode字符集或者Unicode字符集。Unicode规定了每个字符在整个字符集中的具体值(范围0x0-0x10FFFFF),但并没有规定计算机中如何存储这样的值,UTF-8 UTF-16 UTF-32是Unicode字符集事实上的编码标准。 + +UTF-8使用1~6字节的变长编码方式来编码Unicode,由于UTF-8较为节约存储空间,因此使用的比较广泛。 + +![img](https://cdn.nlark.com/yuque/0/2019/png/244132/1566061035371-9f65fec1-f23c-4906-b61f-44faa172864d.png) + +GB2312早于Unicode被定义,是和Unicode不同的一种编码(不过Unicode汉字部分编码其实就是GB2312的变种),采用2字节表示一个中文字符,和Unicode不一样的是,GB2312既是字符集,又是字符编码。 + +### C++中的Unicode支持 + +C++98已经规定了wchar_t类型,但是C++98对wchar_t的定义不够明确,不同的编译器中wchar_t的位数不一致,导致移植性问题。 + +C++11重新规定了char16_t char32_t,用于存储UTF-16 UTF-32编码的Unicode数据,UTF-8的数据则直接使用char来存储。C++中可以在字符串常量前加前缀来让编译器产生不同编码的数据: + +- u8 - UTF8 +- u - UTF-16 + +- U - UTF-32 +- L - wchar_t + +之所以没有为UTF-8规定类型,是因为UTF-16和UTF-32都是定长编码,而UTF-8是变长编码(有误,过去某段时间Unicode还比较少,当时UTF16编码Unicode的确是事实上的定长编码,但现在Unicode字符集已经收录了更多字符,早已超出了UTF-16的表示范围,UTF-16已经成为了事实上的变长编码,一些历史程序如果还假定UTF-16是定长编码的话,遇到超出UTF-16表示范围的字符时就会出问题。),变长编码会导致很多算法变得极其复杂(比如无法确定一个utf_8[]中的第N个字符究竟被存储在数组中的哪个位置)。对于语言来说,定长编码处理起来更自然,且增加的内存占用和减少的程序设计复杂度也大体可以认为相互抵消,可以使用定长编码进行处理,需要保存时再存成变长编码以节省存储空间。 + +C++中,影响Unicode字符能够正确保存和输出的因素有以下三点: + +- 文件编码 +- 编译器编码设置 + +- 输出设备 + +为了确保得到正确的输出,需要确保源文件的编码同系统编码一致、并且用于输出的设备支持被输出的编码(比如不少shell就只支持UTF-8编码,非UTF-8编码的会直接输出十六进制的编码值)。 + +### 标准库支持 + +C++11新增了几个字符类型,也同步地在标准库中新增了字符类型的转换函数。