├── .gitignore ├── README.md ├── cpp_source ├── ch01 │ ├── Sales_item.h │ ├── ch01.cpp │ └── data │ │ ├── add │ │ ├── add_item │ │ ├── book_sales │ │ ├── mysum │ │ └── occurs ├── ch02 │ ├── Sales_item.h │ └── ch02.cpp ├── ch03 │ └── ch3.cpp ├── ch04 │ └── ch04.cpp ├── ch05 │ └── ch5.cpp ├── ch06 │ ├── Chapter6.h │ ├── ch6.cpp │ ├── fact.cpp │ └── factMain.cpp ├── ch07 │ ├── ex_7_41 │ ├── ex_7_41.cpp │ ├── ex_7_41.h │ ├── ex_7_41_main.cpp │ ├── screen │ ├── screen.cpp │ └── screen.h ├── ch08 │ └── ch8.cpp ├── ch09 │ ├── date │ └── date.cpp ├── ch10 │ ├── biggies │ ├── biggies.cpp │ ├── inserter │ └── inserter.cpp ├── ch11 │ ├── ex_11_3 │ ├── ex_11_3.cpp │ ├── ex_11_4 │ └── ex_11_4.cpp ├── ch12 │ ├── ex_12_27 │ ├── ex_12_27.cpp │ ├── ex_12_27.h │ ├── ex_12_27_main.cpp │ └── storyDataFile.txt ├── ch13 │ ├── ex_13_13 │ ├── ex_13_13.cpp │ ├── ex_13_34_36_37.cpp │ └── ex_13_34_36_37.h ├── ch14 │ ├── ex_14_44 │ └── ex_14_44.cpp ├── ch15 │ ├── ex_15_26 │ │ ├── bulk_quote.cpp │ │ ├── bulk_quote.h │ │ ├── disc_quote.cpp │ │ ├── disc_quote.h │ │ ├── limit_quote.cpp │ │ ├── limit_quote.h │ │ ├── main.cpp │ │ ├── quote.cpp │ │ └── quote.h │ └── text_query │ │ ├── StrBlob.h │ │ ├── andquery.cpp │ │ ├── andquery.h │ │ ├── binaryquery.cpp │ │ ├── binaryquery.h │ │ ├── main.cpp │ │ ├── notquery.cpp │ │ ├── notquery.h │ │ ├── orquery.cpp │ │ ├── orquery.h │ │ ├── query.cpp │ │ ├── query.h │ │ ├── query_base.cpp │ │ ├── query_base.h │ │ ├── queryresult.cpp │ │ ├── queryresult.h │ │ ├── storyDataFile.txt │ │ ├── textquery.cpp │ │ ├── textquery.h │ │ ├── wordquery.cpp │ │ └── wordquery.h ├── ch16 │ ├── ex_16_51 │ └── ex_16_51.cpp └── ch17 │ ├── ex_17_4.cpp │ ├── ex_17_4_SalesData.cpp │ └── ex_17_4_SalesData.h ├── excersize ├── ch01.md ├── ch02.md ├── ch03.md ├── ch04.md ├── ch05.md ├── ch06.md ├── ch07.md ├── ch08.md ├── ch09.md ├── ch10.md ├── ch11.md ├── ch12.md ├── ch13.md ├── ch14.md ├── ch15.md ├── ch16.md ├── ch17.md ├── ch18.md └── ch19.md └── notes ├── ch01.md ├── ch02.md ├── ch03.md ├── ch04.md ├── ch05.md ├── ch06.md ├── ch07.md ├── ch08.md ├── ch09.md ├── ch10.md ├── ch11.md ├── ch12.md ├── ch13.md ├── ch14.md ├── ch15.md ├── ch16.md ├── ch17.md ├── ch18.md └── ch19.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-debug 3 | test_in_file 4 | test_out_file 5 | CMakeLists.txt 6 | main 7 | .DS_Store 8 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cpp Primer 笔记 & 答案 2 | 3 | ## 简介 4 | 5 | 《C++ Primer 中文版(第 5 版)》学习仓库,包括**笔记**和**课后练习答案**。 6 | 7 | ## 环境 8 | 9 | - system: ubuntu 16.04 10 | - IDE: VS Code 11 | - compiler: g++ 12 | 13 | [豆瓣链接](https://book.douban.com/subject/25708312/) 14 | 15 | ## 目录 16 | 17 | - 第1章 : 开始 [笔记](./notes/ch01.md) [练习](./excersize/ch01.md) 18 | - 第 I 部分 : C++基础 19 | - 第2章 : 变量和基本类型 [笔记](./notes/ch02.md) [练习](./excersize/ch02.md) 20 | - 第3章 : 字符串、向量和数组 [笔记](./notes/ch03.md) [练习](./excersize/ch03.md) 21 | - 第4章 : 表达式 [笔记](./notes/ch04.md) [练习](./excersize/ch04.md) 22 | - 第5章 : 语句 [笔记](./notes/ch05.md) [练习](./excersize/ch05.md) 23 | - 第6章 : 函数 [笔记](./notes/ch06.md) [练习](./excersize/ch06.md) 24 | - 第7章 : 类 [笔记](./notes/ch07.md) [练习](./excersize/ch07.md) 25 | - 第 II 部分 : C++标准库 26 | - 第8章 : IO库 [笔记](./notes/ch08.md) [练习](./excersize/ch08.md) 27 | - 第9章 : 顺序容器 [笔记](./notes/ch09.md) [练习](./excersize/ch09.md) 28 | - 第10章 : 泛型算法 [笔记](./notes/ch10.md) [练习](./excersize/ch10.md) 29 | - 第11章 : 关联容器 [笔记](./notes/ch11.md) [练习](./excersize/ch11.md) 30 | - 第12章 : 动态内存 [笔记](./notes/ch12.md) [练习](./excersize/ch12.md) 31 | - 第 III 部分 : 类设计者的工具 32 | - 第13章 : 拷贝控制 [笔记](./notes/ch13.md) [练习](./excersize/ch13.md) 33 | - 第14章 : 重载与类型转换 [笔记](./notes/ch14.md) [练习](./excersize/ch14.md) 34 | - 第15章 : 面向对象程序设计 [笔记](./notes/ch15.md) [练习](./excersize/ch15.md) 35 | - 第16章 : 模版与泛型编程 [笔记](./notes/ch16.md) [练习](./excersize/ch16.md) 36 | - 第 IV 部分 : 高级主题 37 | - 第17章 : 标准库与特殊设施 [笔记](./notes/ch17.md) [练习](./excersize/ch17.md) 38 | - 第18章 : 用于大型程序的工具 [笔记](./notes/ch18.md) [练习](./excersize/ch18.md) 39 | - 第19章 : 特殊工具与技术 [笔记](./notes/ch19.md) [练习](./excersize/ch19.md) 40 | 41 | ## 参考 42 | 43 | - [C++ Primer 5 Answers(C++11/14)](https://github.com/Mooophy/Cpp-Primer) 44 | - [《C++ Primer》第五版中文版习题答案](https://github.com/huangmingchuan/Cpp_Primer_Answers) 45 | 46 | ## 参与贡献 47 | 48 | 本仓库由多位小伙伴一起参与编写,欢迎大家对本仓库进行补充,一起帮大家更好地理解这本“大部头”。 49 | 50 | ![](https://contrib.rocks/image?repo=applenob/Cpp_Primer_Practice) 51 | -------------------------------------------------------------------------------- /cpp_source/ch01/Sales_item.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. 3 | * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the 4 | * copyright and warranty notices given in that book: 5 | * 6 | * "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo." 7 | * 8 | * 9 | * "The authors and publisher have taken care in the preparation of this book, 10 | * but make no expressed or implied warranty of any kind and assume no 11 | * responsibility for errors or omissions. No liability is assumed for 12 | * incidental or consequential damages in connection with or arising out of the 13 | * use of the information or programs contained herein." 14 | * 15 | * Permission is granted for this code to be used for educational purposes in 16 | * association with the book, given proper citation if and when posted or 17 | * reproduced.Any commercial use of this code requires the explicit written 18 | * permission of the publisher, Addison-Wesley Professional, a division of 19 | * Pearson Education, Inc. Send your request for permission, stating clearly 20 | * what code you would like to use, and in what specific way, to the following 21 | * address: 22 | * 23 | * Pearson Education, Inc. 24 | * Rights and Permissions Department 25 | * One Lake Street 26 | * Upper Saddle River, NJ 07458 27 | * Fax: (201) 236-3290 28 | */ 29 | 30 | /* This file defines the Sales_item class used in chapter 1. 31 | * The code used in this file will be explained in 32 | * Chapter 7 (Classes) and Chapter 14 (Overloaded Operators) 33 | * Readers shouldn't try to understand the code in this file 34 | * until they have read those chapters. 35 | */ 36 | 37 | #ifndef SALESITEM_H 38 | // we're here only if SALESITEM_H has not yet been defined 39 | #define SALESITEM_H 40 | 41 | // Definition of Sales_item class and related functions goes here 42 | #include 43 | #include 44 | 45 | class Sales_item { 46 | // these declarations are explained section 7.2.1, p. 270 47 | // and in chapter 14, pages 557, 558, 561 48 | friend std::istream& operator>>(std::istream&, Sales_item&); 49 | friend std::ostream& operator<<(std::ostream&, const Sales_item&); 50 | friend bool operator<(const Sales_item&, const Sales_item&); 51 | friend bool 52 | operator==(const Sales_item&, const Sales_item&); 53 | public: 54 | // constructors are explained in section 7.1.4, pages 262 - 265 55 | // default constructor needed to initialize members of built-in type 56 | Sales_item() = default; 57 | Sales_item(const std::string &book): bookNo(book) { } 58 | Sales_item(std::istream &is) { is >> *this; } 59 | public: 60 | // operations on Sales_item objects 61 | // member binary operator: left-hand operand bound to implicit this pointer 62 | Sales_item& operator+=(const Sales_item&); 63 | 64 | // operations on Sales_item objects 65 | std::string isbn() const { return bookNo; } 66 | double avg_price() const; 67 | // private members as before 68 | private: 69 | std::string bookNo; // implicitly initialized to the empty string 70 | unsigned units_sold = 0; // explicitly initialized 71 | double revenue = 0.0; 72 | }; 73 | 74 | // used in chapter 10 75 | inline 76 | bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) 77 | { return lhs.isbn() == rhs.isbn(); } 78 | 79 | // nonmember binary operator: must declare a parameter for each operand 80 | Sales_item operator+(const Sales_item&, const Sales_item&); 81 | 82 | inline bool 83 | operator==(const Sales_item &lhs, const Sales_item &rhs) 84 | { 85 | // must be made a friend of Sales_item 86 | return lhs.units_sold == rhs.units_sold && 87 | lhs.revenue == rhs.revenue && 88 | lhs.isbn() == rhs.isbn(); 89 | } 90 | 91 | inline bool 92 | operator!=(const Sales_item &lhs, const Sales_item &rhs) 93 | { 94 | return !(lhs == rhs); // != defined in terms of operator== 95 | } 96 | 97 | // assumes that both objects refer to the same ISBN 98 | Sales_item& Sales_item::operator+=(const Sales_item& rhs) 99 | { 100 | units_sold += rhs.units_sold; 101 | revenue += rhs.revenue; 102 | return *this; 103 | } 104 | 105 | // assumes that both objects refer to the same ISBN 106 | Sales_item 107 | operator+(const Sales_item& lhs, const Sales_item& rhs) 108 | { 109 | Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return 110 | ret += rhs; // add in the contents of (|rhs|) 111 | return ret; // return (|ret|) by value 112 | } 113 | 114 | std::istream& 115 | operator>>(std::istream& in, Sales_item& s) 116 | { 117 | double price; 118 | in >> s.bookNo >> s.units_sold >> price; 119 | // check that the inputs succeeded 120 | if (in) 121 | s.revenue = s.units_sold * price; 122 | else 123 | s = Sales_item(); // input failed: reset object to default state 124 | return in; 125 | } 126 | 127 | std::ostream& 128 | operator<<(std::ostream& out, const Sales_item& s) 129 | { 130 | out << s.isbn() << " " << s.units_sold << " " 131 | << s.revenue << " " << s.avg_price(); 132 | return out; 133 | } 134 | 135 | double Sales_item::avg_price() const 136 | { 137 | if (units_sold) 138 | return revenue/units_sold; 139 | else 140 | return 0; 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /cpp_source/ch01/ch01.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer 3 | // chapter 1 4 | // 开始 5 | 6 | #include 7 | #include "Sales_item.h" 8 | 9 | void basic_io(){ 10 | std::cout << "Enter two numbers:" << std::endl; 11 | int v1, v2; 12 | std::cin >> v1 >> v2; 13 | std::cout << "The sum of " << v1 << " and " << v2 14 | << " is " << v1 + v2 << std::endl; 15 | } 16 | 17 | void basic_while(){ 18 | int sum = 0, val = 1; 19 | while (val <= 10){ 20 | sum += val; 21 | ++val; 22 | } 23 | std::cout << "Sum of 1 to 10 inclusive is " 24 | << sum << std::endl; 25 | } 26 | 27 | void basic_for(){ 28 | int sum = 0; 29 | for (int val = 1; val <= 10; ++val){ 30 | sum += val; 31 | } 32 | std::cout << "Sum of 1 to 10 inclusive is " 33 | << sum << std::endl; 34 | } 35 | 36 | void basic_if(){ 37 | std::cout << "Enter two numbers:" << std::endl; 38 | int v1, v2; 39 | std::cin >> v1 >> v2; 40 | int lower, upper; 41 | if (v1<=v2){ 42 | lower = v1; 43 | upper = v2; 44 | } else { 45 | lower = v2; 46 | upper = v1; 47 | } 48 | int sum = 0; 49 | for (int val = lower; val <= upper; ++val) 50 | sum += val; 51 | std::cout << "Sum of " << lower 52 | << " to " << upper 53 | << " inclusive is " 54 | << sum << std::endl; 55 | } 56 | 57 | void basic_cin(){ 58 | int sum = 0, value; 59 | while (std::cin >> value) 60 | sum += value; 61 | std::cout << "Sum is: " << sum << std::endl; 62 | } 63 | 64 | void q_1_3(){ 65 | std::cout << "Hello, World" << std::endl; 66 | } 67 | 68 | void q_1_4(){ 69 | std::cout << "Enter two numbers:" << std::endl; 70 | int v1 = 0, v2 = 0; 71 | std::cin >> v1 >> v2; 72 | std::cout << "The product of " << v1 << " and " << v2 73 | << " is " << v1 * v2 << std::endl; 74 | } 75 | 76 | void q_1_5(){ 77 | std::cout << "Enter two numbers:" << std::endl; 78 | int v1 = 0, v2 = 0; 79 | std::cin >> v1 >> v2; 80 | std::cout << "The product of "; 81 | std::cout << v1; 82 | std::cout << " and "; 83 | std::cout << v2; 84 | std::cout << " is "; 85 | std::cout << v1 * v2; 86 | std::cout << std::endl; 87 | } 88 | 89 | void q_1_9(){ 90 | int sum = 0, val = 50; 91 | while (val <= 100){ 92 | sum += val; 93 | val += 1; 94 | } 95 | std::cout << "Sum of 50 to 100 inclusive is " 96 | << sum << std::endl; 97 | } 98 | 99 | void q_1_10(){ 100 | int val = 10; 101 | while (val >= 0){ 102 | std::cout << val << " "; 103 | val -= 1; 104 | } 105 | std::cout << std::endl; 106 | } 107 | 108 | void q_1_11(){ 109 | int start = 0, end = 0; 110 | std::cout << "Please input two num: "; 111 | std::cin >> start >> end; 112 | if (start <= end) { 113 | while (start <= end){ 114 | std::cout << start << " "; 115 | ++start; 116 | } 117 | std::cout << std::endl; 118 | } 119 | else{ 120 | std::cout << "start should be smaller than end !!!"; 121 | } 122 | } 123 | 124 | void q_1_16(){ 125 | int sum = 0; 126 | for (int value = 0; std::cin >> value; ) 127 | sum += value; 128 | std::cout << sum << std::endl; 129 | } 130 | 131 | void count_num(){ 132 | // 统计输入中每个值连续出现了多少次 133 | int currVal = 0, val = 0; 134 | if (std::cin >> currVal){ 135 | int cnt = 1; 136 | while (std::cin >> val){ 137 | if (val == currVal) 138 | ++cnt; 139 | else { 140 | std::cout << currVal << " occurs " 141 | << cnt << " times " << std::endl; 142 | currVal = val; 143 | cnt = 1; 144 | } 145 | } 146 | std::cout << currVal << " occurs " 147 | << cnt << " times " << std::endl; 148 | } 149 | } 150 | 151 | void q_1_20(){ 152 | for (Sales_item item; std::cin >> item; std::cout << item << std::endl); 153 | } 154 | 155 | void q_1_21(){ 156 | Sales_item item_1; 157 | Sales_item item_2; 158 | std::cin >> item_1; 159 | std::cout << item_1 << std::endl; 160 | std::cin >> item_2; 161 | std::cout << item_2 << std::endl; 162 | std::cout << "sum of sale items: " << item_1 + item_2 << std::endl; 163 | } 164 | 165 | void q_1_22(){ 166 | Sales_item sum_item; 167 | std::cin >> sum_item; 168 | std::cout << sum_item << std::endl; 169 | for (Sales_item item; std::cin >> item; std::cout << item << std::endl){ 170 | sum_item += item; 171 | } 172 | std::cout << "sum of sale items: " << sum_item << std::endl; 173 | } 174 | 175 | void q_1_23(){ 176 | Sales_item total; 177 | if (std::cin >> total){ 178 | Sales_item trans; 179 | while (std::cin >> trans){ 180 | if (total.isbn() == trans.isbn()) { 181 | total += trans; 182 | } 183 | else { 184 | std::cout << total << std::endl; 185 | total = trans; 186 | } 187 | } 188 | std::cout << total << std::endl; 189 | } 190 | else { 191 | std::cerr << "No data?!" << std::endl; 192 | return; 193 | } 194 | } 195 | 196 | int main() { 197 | // basic_io(); 198 | // basic_while(); 199 | // basic_for(); 200 | // basic_if(); 201 | // basic_cin(); 202 | // q_1_3(); 203 | // q_1_4(); 204 | // q_1_5(); 205 | // /* 正常注释 /* 嵌套注释 */ 正常注释*/ 206 | // std::cout << /* "*/" /* "/*" */; 207 | // q_1_9(); 208 | // q_1_10(); 209 | // q_1_11(); 210 | // q_1_16(); 211 | // count_num(); 212 | // q_1_20(); 213 | // q_1_21(); 214 | // q_1_22(); 215 | q_1_23(); 216 | return 0; 217 | } -------------------------------------------------------------------------------- /cpp_source/ch01/data/add: -------------------------------------------------------------------------------- 1 | 3 7 2 | -------------------------------------------------------------------------------- /cpp_source/ch01/data/add_item: -------------------------------------------------------------------------------- 1 | 0-201-78345-X 3 20.00 2 | 0-201-78345-X 2 25.00 3 | -------------------------------------------------------------------------------- /cpp_source/ch01/data/book_sales: -------------------------------------------------------------------------------- 1 | 0-201-70353-X 4 24.99 2 | 0-201-82470-1 4 45.39 3 | 0-201-88954-4 2 15.00 4 | 0-201-88954-4 5 12.00 5 | 0-201-88954-4 7 12.00 6 | 0-201-88954-4 2 12.00 7 | 0-399-82477-1 2 45.39 8 | 0-399-82477-1 3 45.39 9 | 0-201-78345-X 3 20.00 10 | 0-201-78345-X 2 25.00 11 | -------------------------------------------------------------------------------- /cpp_source/ch01/data/mysum: -------------------------------------------------------------------------------- 1 | 3 4 5 6 2 | -------------------------------------------------------------------------------- /cpp_source/ch01/data/occurs: -------------------------------------------------------------------------------- 1 | 42 42 42 42 42 55 55 62 100 100 100 2 | -------------------------------------------------------------------------------- /cpp_source/ch02/Sales_item.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. 3 | * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the 4 | * copyright and warranty notices given in that book: 5 | * 6 | * "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo." 7 | * 8 | * 9 | * "The authors and publisher have taken care in the preparation of this book, 10 | * but make no expressed or implied warranty of any kind and assume no 11 | * responsibility for errors or omissions. No liability is assumed for 12 | * incidental or consequential damages in connection with or arising out of the 13 | * use of the information or programs contained herein." 14 | * 15 | * Permission is granted for this code to be used for educational purposes in 16 | * association with the book, given proper citation if and when posted or 17 | * reproduced.Any commercial use of this code requires the explicit written 18 | * permission of the publisher, Addison-Wesley Professional, a division of 19 | * Pearson Education, Inc. Send your request for permission, stating clearly 20 | * what code you would like to use, and in what specific way, to the following 21 | * address: 22 | * 23 | * Pearson Education, Inc. 24 | * Rights and Permissions Department 25 | * One Lake Street 26 | * Upper Saddle River, NJ 07458 27 | * Fax: (201) 236-3290 28 | */ 29 | 30 | /* This file defines the Sales_item class used in chapter 1. 31 | * The code used in this file will be explained in 32 | * Chapter 7 (Classes) and Chapter 14 (Overloaded Operators) 33 | * Readers shouldn't try to understand the code in this file 34 | * until they have read those chapters. 35 | */ 36 | 37 | #ifndef SALESITEM_H 38 | // we're here only if SALESITEM_H has not yet been defined 39 | #define SALESITEM_H 40 | 41 | // Definition of Sales_item class and related functions goes here 42 | #include 43 | #include 44 | 45 | class Sales_item { 46 | // these declarations are explained section 7.2.1, p. 270 47 | // and in chapter 14, pages 557, 558, 561 48 | friend std::istream& operator>>(std::istream&, Sales_item&); 49 | friend std::ostream& operator<<(std::ostream&, const Sales_item&); 50 | friend bool operator<(const Sales_item&, const Sales_item&); 51 | friend bool 52 | operator==(const Sales_item&, const Sales_item&); 53 | public: 54 | // constructors are explained in section 7.1.4, pages 262 - 265 55 | // default constructor needed to initialize members of built-in type 56 | Sales_item() = default; 57 | Sales_item(const std::string &book): bookNo(book) { } 58 | Sales_item(std::istream &is) { is >> *this; } 59 | public: 60 | // operations on Sales_item objects 61 | // member binary operator: left-hand operand bound to implicit this pointer 62 | Sales_item& operator+=(const Sales_item&); 63 | 64 | // operations on Sales_item objects 65 | std::string isbn() const { return bookNo; } 66 | double avg_price() const; 67 | // private members as before 68 | private: 69 | std::string bookNo; // implicitly initialized to the empty string 70 | unsigned units_sold = 0; // explicitly initialized 71 | double revenue = 0.0; 72 | }; 73 | 74 | // used in chapter 10 75 | inline 76 | bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) 77 | { return lhs.isbn() == rhs.isbn(); } 78 | 79 | // nonmember binary operator: must declare a parameter for each operand 80 | Sales_item operator+(const Sales_item&, const Sales_item&); 81 | 82 | inline bool 83 | operator==(const Sales_item &lhs, const Sales_item &rhs) 84 | { 85 | // must be made a friend of Sales_item 86 | return lhs.units_sold == rhs.units_sold && 87 | lhs.revenue == rhs.revenue && 88 | lhs.isbn() == rhs.isbn(); 89 | } 90 | 91 | inline bool 92 | operator!=(const Sales_item &lhs, const Sales_item &rhs) 93 | { 94 | return !(lhs == rhs); // != defined in terms of operator== 95 | } 96 | 97 | // assumes that both objects refer to the same ISBN 98 | Sales_item& Sales_item::operator+=(const Sales_item& rhs) 99 | { 100 | units_sold += rhs.units_sold; 101 | revenue += rhs.revenue; 102 | return *this; 103 | } 104 | 105 | // assumes that both objects refer to the same ISBN 106 | Sales_item 107 | operator+(const Sales_item& lhs, const Sales_item& rhs) 108 | { 109 | Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return 110 | ret += rhs; // add in the contents of (|rhs|) 111 | return ret; // return (|ret|) by value 112 | } 113 | 114 | std::istream& 115 | operator>>(std::istream& in, Sales_item& s) 116 | { 117 | double price; 118 | in >> s.bookNo >> s.units_sold >> price; 119 | // check that the inputs succeeded 120 | if (in) 121 | s.revenue = s.units_sold * price; 122 | else 123 | s = Sales_item(); // input failed: reset object to default state 124 | return in; 125 | } 126 | 127 | std::ostream& 128 | operator<<(std::ostream& out, const Sales_item& s) 129 | { 130 | out << s.isbn() << " " << s.units_sold << " " 131 | << s.revenue << " " << s.avg_price(); 132 | return out; 133 | } 134 | 135 | double Sales_item::avg_price() const 136 | { 137 | if (units_sold) 138 | return revenue/units_sold; 139 | else 140 | return 0; 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /cpp_source/ch02/ch02.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer 3 | // chapter 2 4 | // 变量和基本类型 5 | 6 | #include 7 | #include "Sales_item.h" 8 | 9 | void basic_class(){ 10 | Sales_item book; 11 | std::cin >> book; 12 | std::cout << book << std::endl; 13 | } 14 | 15 | void q_2_3(){ 16 | unsigned u = 10, u2 = 42; 17 | std::cout << u2 - u << std::endl; 18 | std::cout << u - u2 << std::endl; 19 | int i = 10, i2 = 42; 20 | std::cout << i2 - i << std::endl; 21 | std::cout << i - i2 << std::endl; 22 | std::cout << i - u << std::endl; 23 | std::cout << u - i << std::endl; 24 | } 25 | 26 | // 重写 1.5.1 1.5.2 1.6 27 | 28 | // 1.5.1 29 | 30 | #include 31 | #include 32 | 33 | struct Sale_data 34 | { 35 | std::string bookNo; 36 | unsigned units_sold = 0; 37 | double revenue = 0.0; 38 | }; 39 | 40 | int q_1_5_1() 41 | { 42 | Sale_data book; 43 | double price; 44 | std::cin >> book.bookNo >> book.units_sold >> price; 45 | book.revenue = book.units_sold * price; 46 | std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price; 47 | 48 | return 0; 49 | } 50 | 51 | // 1.5.2 52 | int q_1_5_2() 53 | { 54 | Sale_data book1, book2; 55 | double price1, price2; 56 | std::cin >> book1.bookNo >> book1.units_sold >> price1; 57 | std::cin >> book2.bookNo >> book2.units_sold >> price2; 58 | book1.revenue = book1.units_sold * price1; 59 | book2.revenue = book2.units_sold * price2; 60 | 61 | if (book1.bookNo == book2.bookNo) 62 | { 63 | unsigned totalCnt = book1.units_sold + book2.units_sold; 64 | double totalRevenue = book1.revenue + book2.revenue; 65 | std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " "; 66 | if (totalCnt != 0) 67 | std::cout << totalRevenue / totalCnt << std::endl; 68 | else 69 | std::cout << "(no sales)" << std::endl; 70 | return 0; 71 | } 72 | else 73 | { 74 | std::cerr << "Data must refer to same ISBN" << std::endl; 75 | return -1; // indicate failure 76 | } 77 | } 78 | 79 | // 1.6 80 | int q_1_6() 81 | { 82 | Sale_data total; 83 | double totalPrice; 84 | if (std::cin >> total.bookNo >> total.units_sold >> totalPrice) 85 | { 86 | total.revenue = total.units_sold * totalPrice; 87 | 88 | Sale_data trans; 89 | double transPrice; 90 | while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice) 91 | { 92 | trans.revenue = trans.units_sold * transPrice; 93 | 94 | if (total.bookNo == trans.bookNo) 95 | { 96 | total.units_sold += trans.units_sold; 97 | total.revenue += trans.revenue; 98 | } 99 | else 100 | { 101 | std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; 102 | if (total.units_sold != 0) 103 | std::cout << total.revenue / total.units_sold << std::endl; 104 | else 105 | std::cout << "(no sales)" << std::endl; 106 | 107 | total.bookNo = trans.bookNo; 108 | total.units_sold = trans.units_sold; 109 | total.revenue = trans.revenue; 110 | } 111 | } 112 | 113 | std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; 114 | if (total.units_sold != 0) 115 | std::cout << total.revenue / total.units_sold << std::endl; 116 | else 117 | std::cout << "(no sales)" << std::endl; 118 | 119 | return 0; 120 | } 121 | else 122 | { 123 | std::cerr << "No data?!" << std::endl; 124 | return -1; // indicate failure 125 | } 126 | } 127 | 128 | 129 | int main(){ 130 | // q_2_3(); 131 | // q_1_5_1(); 132 | // q_1_5_2(); 133 | q_1_6(); 134 | return 0; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /cpp_source/ch03/ch3.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer 3 | // chapter 3 4 | // 字符串、向量和数组 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using std::cin; 13 | using std::cout; 14 | using std::endl; 15 | using std::string; 16 | using std::vector; 17 | using std::bitset; 18 | 19 | void basic_getline(){ 20 | string line; 21 | while (getline(cin, line)) 22 | cout << line << endl; 23 | } 24 | 25 | void basic_string(){ 26 | string s = "Test String."; 27 | cout << "Size: " << s.size() << endl 28 | << "Empty: " << s.empty() << endl 29 | << "char at index 0:" << s[0] << endl; 30 | for(string::size_type i=0; i v; 38 | for (int i = 0; i != 10; i++){ 39 | v.push_back(i); 40 | } 41 | for (vector::iterator iter = v.begin(); 42 | iter != v.end(); ++iter){ 43 | *iter = 0; 44 | } 45 | // 不能直接输出vector 46 | } 47 | 48 | void basic_bieset(){ 49 | bitset<16> bitvec1(0xffff); 50 | string bit_str = "1100"; 51 | bitset<32> bitvec2(bit_str); //从右到左读取字符串,只能是string对象,不能是字面值 52 | bitset<16> bitvec3(bit_str, 0, 3); // 只要前三位 53 | cout << bitvec1 << endl 54 | << bitvec2 << endl 55 | << bitvec3 << endl 56 | << bitvec2.test(3) <<" " < v; 62 | int i; 63 | while (cin >> i){ 64 | v.push_back(i); 65 | } 66 | } 67 | 68 | void q_3_17(){ 69 | vector v; 70 | string s; 71 | while (cin >> s){ 72 | v.push_back(s); 73 | } 74 | for (auto &str : v) 75 | { 76 | for (auto &c : str) 77 | { 78 | c = toupper(c); 79 | } 80 | } 81 | for (auto str : v){ 82 | cout << str << endl; 83 | } 84 | } 85 | 86 | 87 | void q_3_23(){ 88 | vector v(10, 1); 89 | for (auto it=v.begin(); it!=v.end(); it++){ 90 | *it *= 2; 91 | } 92 | for (auto one : v){ 93 | cout << one < 6 | 7 | using namespace std; 8 | 9 | int q_4_28() 10 | { 11 | cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl; 12 | 13 | cout << "char:\t\t" << sizeof(char) << " bytes" << endl; 14 | cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl; 15 | cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl; 16 | cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl; 17 | 18 | cout << "short:\t\t" << sizeof(short) << " bytes" << endl; 19 | cout << "int:\t\t" << sizeof(int) << " bytes" << endl; 20 | cout << "long:\t\t" << sizeof(long) << " bytes" << endl; 21 | cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl; 22 | 23 | cout << "float:\t\t" << sizeof(float) << " bytes" << endl; 24 | cout << "double:\t\t" << sizeof(double) << " bytes" << endl; 25 | cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl; 26 | 27 | return 0; 28 | } 29 | 30 | int main(){ 31 | q_4_28(); 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /cpp_source/ch05/ch5.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer on 17-9-18. 3 | // chapter 5 4 | // 语句 5 | 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | int divide(int a, int b){ 11 | if (b == 0){ 12 | // 抛出异常 13 | throw runtime_error("b cannot be 0!"); 14 | } 15 | else{ 16 | return a / b; 17 | } 18 | } 19 | 20 | int main(){ 21 | int a = 1, b = 0, res; 22 | try{ 23 | res = divide(a, b); 24 | cout << res << endl; 25 | }catch(runtime_error err){ 26 | cout << err.what() << endl; 27 | } 28 | return 0; 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /cpp_source/ch06/Chapter6.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer on 19-1-20. 3 | // 4 | 5 | #ifndef CPP_PRIMER_PRACTICE_CHAPTER6_H 6 | #define CPP_PRIMER_PRACTICE_CHAPTER6_H 7 | 8 | int fact(int val); 9 | int func(); 10 | 11 | template 12 | T abs(T i) 13 | { 14 | return i >= 0 ? i : -i; 15 | } 16 | 17 | #endif //CPP_PRIMER_PRACTICE_CHAPTER6_H 18 | -------------------------------------------------------------------------------- /cpp_source/ch06/ch6.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer on 17-9-19. 3 | // chapter 06 4 | // 函数 5 | 6 | #include 7 | #include 8 | 9 | using namespace std 10 | 11 | int fact(int i) 12 | { 13 | return i > 1 ? i * fact(i - 1) : 1; 14 | } 15 | 16 | void interactive_fact() 17 | { 18 | string const prompt = "Enter a number within [1, 13) :\n"; 19 | string const out_of_range = "Out of range, please try again.\n"; 20 | for (int i; cout << prompt, cin >> i; ) 21 | { 22 | if (i < 1 || i > 12) 23 | { 24 | cout << out_of_range; 25 | continue; 26 | } 27 | cout << fact(i) << endl; 28 | } 29 | } 30 | 31 | bool str_subrange(const string &str1, const string &str2){ 32 | if(str1.size()==str2.size()) 33 | return str1==str2; 34 | string::size_type size={min(str1.size(),str2.size())}; 35 | string::size_type i=0; 36 | while(i!=size){ 37 | if(str1[i]!=str2[i]) 38 | return ; //error! no return value! 39 |   } 40 | } 41 | 42 | int main() 43 | { 44 | // interactive_fact(); 45 | str_subrange(); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /cpp_source/ch06/fact.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer on 19-1-20. 3 | // 4 | 5 | #include "Chapter6.h" 6 | #include 7 | 8 | int fact(int val) 9 | { 10 | if (val == 0 || val == 1) return 1; 11 | else return val * fact(val-1); 12 | } 13 | 14 | int func() 15 | { 16 | int n, ret = 1; 17 | std::cout << "input a number: "; 18 | std::cin >> n; 19 | while (n > 1) ret *= n--; 20 | return ret; 21 | } 22 | -------------------------------------------------------------------------------- /cpp_source/ch06/factMain.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by cer on 19-1-20. 3 | // 4 | 5 | 6 | #include "Chapter6.h" 7 | #include 8 | 9 | int main() 10 | { 11 | std::cout << "5! is " << fact(5) << std::endl; 12 | std::cout << func() << std::endl; 13 | std::cout << abs(-9.78) << std::endl; 14 | } 15 | -------------------------------------------------------------------------------- /cpp_source/ch07/ex_7_41: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch07/ex_7_41 -------------------------------------------------------------------------------- /cpp_source/ch07/ex_7_41.cpp: -------------------------------------------------------------------------------- 1 | #include "ex_7_41.h" 2 | 3 | // constructor 4 | Sales_data::Sales_data(std::istream &is) : Sales_data() 5 | { 6 | std::cout << "Sales_data(istream &is)" << std::endl; 7 | read(is, *this); 8 | } 9 | 10 | // member functions. 11 | Sales_data& Sales_data::combine(const Sales_data& rhs) 12 | { 13 | units_sold += rhs.units_sold; 14 | revenue += rhs.revenue; 15 | return *this; 16 | } 17 | 18 | // friend functions 19 | std::istream &read(std::istream &is, Sales_data &item) 20 | { 21 | double price = 0; 22 | is >> item.bookNo >> item.units_sold >> price; 23 | item.revenue = price * item.units_sold; 24 | return is; 25 | } 26 | 27 | std::ostream &print(std::ostream &os, const Sales_data &item) 28 | { 29 | os << item.isbn() << " " << item.units_sold << " " << item.revenue; 30 | return os; 31 | } 32 | 33 | Sales_data add(const Sales_data &lhs, const Sales_data &rhs) 34 | { 35 | Sales_data sum = lhs; 36 | sum.combine(rhs); 37 | return sum; 38 | } 39 | -------------------------------------------------------------------------------- /cpp_source/ch07/ex_7_41.h: -------------------------------------------------------------------------------- 1 | #ifndef CP5_ex7_41_h 2 | #define CP5_ex7_41_h 3 | 4 | #include 5 | #include 6 | 7 | class Sales_data { 8 | friend std::istream &read(std::istream &is, Sales_data &item); 9 | friend std::ostream &print(std::ostream &os, const Sales_data &item); 10 | friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs); 11 | 12 | public: 13 | Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p) 14 | { std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl; } 15 | 16 | Sales_data() : Sales_data("", 0, 0.0f) 17 | { std::cout << "Sales_data()" << std::endl; } 18 | 19 | Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f) 20 | { std::cout << "Sales_data(const std::string&)" << std::endl; } 21 | 22 | Sales_data(std::istream &is); 23 | 24 | std::string isbn() const { return bookNo; } 25 | Sales_data& combine(const Sales_data&); 26 | 27 | private: 28 | inline double avg_price() const; 29 | 30 | private: 31 | std::string bookNo; 32 | unsigned units_sold = 0; 33 | double revenue = 0.0; 34 | }; 35 | 36 | inline 37 | double Sales_data::avg_price() const 38 | { 39 | return units_sold ? revenue/units_sold : 0; 40 | } 41 | 42 | // declarations for nonmember parts of the Sales_data interface. 43 | std::istream &read(std::istream &is, Sales_data &item); 44 | std::ostream &print(std::ostream &os, const Sales_data &item); 45 | Sales_data add(const Sales_data &lhs, const Sales_data &rhs); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /cpp_source/ch07/ex_7_41_main.cpp: -------------------------------------------------------------------------------- 1 | #include "ex_7_41.h" 2 | using std::cout; using std::endl; 3 | 4 | int main() 5 | { 6 | cout << "1. default way: " << endl; 7 | cout << "----------------" << endl; 8 | Sales_data s1; 9 | 10 | cout << "\n2. use std::string as parameter: " << endl; 11 | cout << "----------------" << endl; 12 | Sales_data s2("CPP-Primer-5th"); 13 | 14 | cout << "\n3. complete parameters: " << endl; 15 | cout << "----------------" << endl; 16 | Sales_data s3("CPP-Primer-5th", 3, 25.8); 17 | 18 | cout << "\n4. use istream as parameter: " << endl; 19 | cout << "----------------" << endl; 20 | Sales_data s4(std::cin); 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /cpp_source/ch07/screen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch07/screen -------------------------------------------------------------------------------- /cpp_source/ch07/screen.cpp: -------------------------------------------------------------------------------- 1 | # include "screen.h" 2 | 3 | int main() 4 | { 5 | Screen myScreen(5, 5, 'X'); 6 | myScreen.move(4, 0).set('#').display(std::cout); 7 | std::cout << "\n"; 8 | myScreen.display(std::cout); 9 | std::cout << "\n"; 10 | 11 | return 0; 12 | } -------------------------------------------------------------------------------- /cpp_source/ch07/screen.h: -------------------------------------------------------------------------------- 1 | #ifndef CH07_screen 2 | #define CH07_screen 3 | 4 | #include 5 | #include 6 | 7 | class Screen { 8 | public: 9 | using pos = std::string::size_type; 10 | 11 | Screen() = default; // 1 12 | Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2 13 | Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3 14 | 15 | char get() const { return contents[cursor]; } 16 | char get(pos r, pos c) const { return contents[r*width+c]; } 17 | inline Screen& move(pos r, pos c); 18 | inline Screen& set(char c); 19 | inline Screen& set(pos r, pos c, char ch); 20 | 21 | const Screen& display(std::ostream &os) const { do_display(os); return *this; } 22 | Screen& display(std::ostream &os) { do_display(os); return *this; } 23 | 24 | private: 25 | void do_display(std::ostream &os) const { os << contents; } 26 | 27 | private: 28 | pos cursor = 0; 29 | pos height = 0, width = 0; 30 | std::string contents; 31 | }; 32 | 33 | inline Screen& Screen::move(pos r, pos c) 34 | { 35 | cursor = r*width + c; 36 | return *this; 37 | } 38 | 39 | inline Screen& Screen::set(char c) 40 | { 41 | contents[cursor] = c; 42 | return *this; 43 | } 44 | 45 | inline Screen& Screen::set(pos r, pos c, char ch) 46 | { 47 | contents[r*width+c] = ch; 48 | return *this; 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /cpp_source/ch08/ch8.cpp: -------------------------------------------------------------------------------- 1 | // chapter 08 2 | // 标准IO库 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | void loading(){ 11 | for (int i = 0; i != 11; i++){ 12 | cout << "loading " << (i * 10) << "%\r" << flush; 13 | sleep(1); 14 | } 15 | cout << "loaded! " << endl; 16 | } 17 | 18 | void file_io(){ 19 | string in_file_name = "test_in_file"; 20 | string out_file_name = "test_out_file"; 21 | ifstream infile(in_file_name.c_str()); 22 | ofstream outfile(out_file_name.c_str()); 23 | if (!infile){ 24 | cerr << "err: unable to open input file: " 25 | << in_file_name << endl; 26 | } 27 | else{ 28 | // 按词读取 29 | // string word; 30 | // while(infile >> word) { 31 | // cout << word << endl; 32 | // } 33 | // 按行读取 34 | string line; 35 | while(getline(infile,line)){ 36 | cout << line << endl; 37 | // 按行写入 38 | outfile << line << endl; 39 | } 40 | } 41 | 42 | infile.close(); 43 | outfile.close(); 44 | } 45 | 46 | int main(){ 47 | file_io(); 48 | return 0; 49 | } -------------------------------------------------------------------------------- /cpp_source/ch09/date: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch09/date -------------------------------------------------------------------------------- /cpp_source/ch09/date.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | class My_date{ 7 | private: 8 | unsigned year, month, day; 9 | public: 10 | My_date(const string &s){ 11 | 12 | unsigned tag; 13 | unsigned format; 14 | format = tag = 0; 15 | 16 | // 1/1/1900 17 | if(s.find_first_of("/")!= string :: npos) 18 | { 19 | format = 0x01; 20 | } 21 | 22 | // January 1, 1900 or Jan 1, 1900 23 | if((s.find_first_of(',') >= 4) && s.find_first_of(',')!= string :: npos){ 24 | format = 0x10; 25 | } 26 | else{ // Jan 1 1900 27 | if(s.find_first_of(' ') >= 3 28 | && s.find_first_of(' ')!= string :: npos){ 29 | format = 0x10; 30 | tag = 1; 31 | } 32 | } 33 | 34 | switch(format){ 35 | 36 | case 0x01: 37 | day = stoi(s.substr(0, s.find_first_of("/"))); 38 | month = stoi(s.substr(s.find_first_of("/") + 1, s.find_last_of("/")- s.find_first_of("/"))); 39 | year = stoi(s.substr(s.find_last_of("/") + 1, 4)); 40 | 41 | break; 42 | 43 | case 0x10: 44 | if( s.find("Jan") < s.size() ) month = 1; 45 | if( s.find("Feb") < s.size() ) month = 2; 46 | if( s.find("Mar") < s.size() ) month = 3; 47 | if( s.find("Apr") < s.size() ) month = 4; 48 | if( s.find("May") < s.size() ) month = 5; 49 | if( s.find("Jun") < s.size() ) month = 6; 50 | if( s.find("Jul") < s.size() ) month = 7; 51 | if( s.find("Aug") < s.size() ) month = 8; 52 | if( s.find("Sep") < s.size() ) month = 9; 53 | if( s.find("Oct") < s.size() ) month =10; 54 | if( s.find("Nov") < s.size() ) month =11; 55 | if( s.find("Dec") < s.size() ) month =12; 56 | 57 | char chr = ','; 58 | if(tag == 1){ 59 | chr = ' '; 60 | } 61 | day = stoi(s.substr(s.find_first_of("123456789"), s.find_first_of(chr) - s.find_first_of("123456789"))); 62 | 63 | year = stoi(s.substr(s.find_last_of(' ') + 1, 4)); 64 | break; 65 | } 66 | } 67 | 68 | void print(){ 69 | cout << "day:" << day << " " << "month: " << month << " " << "year: " << year; 70 | } 71 | }; 72 | int main() 73 | { 74 | My_date d("Jan 1 1900"); 75 | d.print(); 76 | return 0; 77 | } -------------------------------------------------------------------------------- /cpp_source/ch10/biggies: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch10/biggies -------------------------------------------------------------------------------- /cpp_source/ch10/biggies.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // from ex 10.9 7 | void elimdups(std::vector &vs) 8 | { 9 | std::sort(vs.begin(), vs.end()); 10 | auto new_end = std::unique(vs.begin(), vs.end()); 11 | vs.erase(new_end, vs.end()); 12 | } 13 | 14 | void biggies(std::vector &vs, std::size_t sz) 15 | { 16 | using std::string; 17 | 18 | elimdups(vs); 19 | 20 | // sort by size, but maintain alphabetical order for same size. 21 | std::stable_sort(vs.begin(), vs.end(), [](string const& lhs, string const& rhs){ 22 | return lhs.size() < rhs.size(); 23 | }); 24 | 25 | // get an iterator to the first one whose size() is >= sz 26 | auto wc = std::find_if(vs.begin(), vs.end(), [sz](string const& s){ 27 | return s.size() >= sz; 28 | }); 29 | 30 | // print the biggies 31 | std::for_each(wc, vs.end(), [](const string &s){ 32 | std::cout << s << " "; 33 | }); 34 | } 35 | 36 | int main() 37 | { 38 | // ex10.16 39 | std::vector v 40 | { 41 | "1234","1234","1234","hi~", "alan", "alan", "cp" 42 | }; 43 | std::cout << "ex10.16: "; 44 | biggies(v, 3); 45 | std::cout << std::endl; 46 | 47 | return 0; 48 | } -------------------------------------------------------------------------------- /cpp_source/ch10/inserter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch10/inserter -------------------------------------------------------------------------------- /cpp_source/ch10/inserter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using std::list; using std::copy; using std::cout; using std::endl; 7 | 8 | template 9 | void print(Sequence const& seq) 10 | { 11 | for (const auto& i: seq) 12 | std::cout << i << " "; 13 | std::cout << std::endl; 14 | } 15 | 16 | int main() 17 | { 18 | std::vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 19 | 20 | // uses inserter 21 | list lst1; 22 | copy(vec.cbegin(), vec.cend(), inserter(lst1, lst1.begin())); 23 | print(lst1); 24 | 25 | // uses back_inserter 26 | list lit2; 27 | copy(vec.cbegin(), vec.cend(), back_inserter(lit2)); 28 | print(lit2); 29 | 30 | // uses front_inserter 31 | list lst3; 32 | copy(vec.cbegin(), vec.cend(), front_inserter(lst3)); 33 | print(lst3); 34 | } -------------------------------------------------------------------------------- /cpp_source/ch11/ex_11_3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch11/ex_11_3 -------------------------------------------------------------------------------- /cpp_source/ch11/ex_11_3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main(){ 8 | map word_count; 9 | string tmp; 10 | while (cin >> tmp){ 11 | word_count[tmp] += 1; 12 | } 13 | for (const auto& elem : word_count) 14 | std::cout << elem.first << " : " << elem.second << endl; 15 | return 0; 16 | } -------------------------------------------------------------------------------- /cpp_source/ch11/ex_11_4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch11/ex_11_4 -------------------------------------------------------------------------------- /cpp_source/ch11/ex_11_4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void word_count_pro(std::map& m) 8 | { 9 | std::string word; 10 | while (std::cin >> word) 11 | { 12 | for (auto& ch : word) 13 | ch = tolower(ch); 14 | 15 | word.erase(std::remove_if(word.begin(), word.end(), ispunct), 16 | word.end()); 17 | ++m[word]; 18 | } 19 | for (const auto& e : m) std::cout << e.first << " : " << e.second << "\n"; 20 | } 21 | 22 | int main() 23 | { 24 | std::map m; 25 | word_count_pro(m); 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /cpp_source/ch12/ex_12_27: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch12/ex_12_27 -------------------------------------------------------------------------------- /cpp_source/ch12/ex_12_27.cpp: -------------------------------------------------------------------------------- 1 | #include "ex_12_27.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | TextQuery::TextQuery(ifstream& ifs) : file(new vector) 10 | { 11 | string text; 12 | while (getline(ifs, text)) 13 | { 14 | file->push_back(text); // 保存文本 15 | int n = file->size() - 1; // 当前行号 16 | istringstream line(text); 17 | string word; 18 | while (line >> word) 19 | { 20 | auto &lines = wm[word]; 21 | if (!lines) 22 | lines.reset(new set); // 如果第一次遇到这个单词,则新建一个set 23 | lines->insert(n); 24 | } 25 | } 26 | } 27 | 28 | QueryResult TextQuery::query(const string& s) const 29 | { 30 | static shared_ptr > nodata(new set); 31 | // 使用find而不是下标运算符来查找单词,避免将单词添加到wm中 32 | auto loc = wm.find(s); 33 | if (loc == wm.end()) 34 | return QueryResult(s, nodata, file); // 未找到 35 | else 36 | return QueryResult(s, loc->second, file); 37 | } 38 | 39 | std::ostream& print(std::ostream& os, const QueryResult& qr) 40 | { 41 | // 如果找到了单词,打印出现次数和所有出现的位置 42 | os << qr.sought << " occurs " << qr.lines->size() << " " 43 | << "time" << (qr.lines->size() > 1 ? "s" : "") << endl; 44 | // 打印单词出现的每一行 45 | for (auto num : *qr.lines) 46 | os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl; 47 | return os; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /cpp_source/ch12/ex_12_27.h: -------------------------------------------------------------------------------- 1 | #ifndef EX12_27_H 2 | #define EX12_27_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class QueryResult; //为了定义函数query的返回类型,这个定义是必须的 12 | 13 | class TextQuery 14 | { 15 | public: 16 | // 行号的类型 17 | using line_no = std::vector::size_type; 18 | TextQuery(std::ifstream&); 19 | QueryResult query(const std::string& s) const; 20 | 21 | private: 22 | std::shared_ptr > file; // 输入文件 23 | // 每个单词到它所在的行号的集合的映射 24 | std::map > > wm; 25 | }; 26 | 27 | class QueryResult 28 | { 29 | public: 30 | friend std::ostream& print(std::ostream&, const QueryResult&); 31 | QueryResult(std::string s, 32 | std::shared_ptr > p, 33 | std::shared_ptr > f) : 34 | sought(s), lines(p), file(f) 35 | {} 36 | 37 | private: 38 | std::string sought; // 查询的单词 39 | std::shared_ptr > lines; // 出现的行号 40 | std::shared_ptr > file; // 输入文件 41 | }; 42 | 43 | std::ostream& print(std::ostream&, const QueryResult&); 44 | 45 | #endif -------------------------------------------------------------------------------- /cpp_source/ch12/ex_12_27_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ex_12_27.h" 5 | 6 | using namespace std; 7 | 8 | void runQueries(ifstream& infile) 9 | { 10 | // infile是一个ifstream,指向我们要处理的文件 11 | TextQuery tq(infile); 12 | // 与用户交互:提示用户输入要查询的单词,完成查询并打印结果 13 | while (true) 14 | { 15 | cout << "enter word to look for, or q to quit: "; 16 | string s; 17 | // 若遇到了文件尾或者用户输入了‘q’时循环终止 18 | if (!(cin >> s) || s == "q") break; 19 | // 指向查询并打印结果 20 | print(cout, tq.query(s)) << endl; 21 | } 22 | } 23 | 24 | int main() 25 | { 26 | ifstream ifs("storyDataFile.txt"); 27 | runQueries(ifs); 28 | return 0; 29 | } -------------------------------------------------------------------------------- /cpp_source/ch12/storyDataFile.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | This is a funny trip for learning c++. 3 | I am Javen Chen. 4 | Nice to see you. -------------------------------------------------------------------------------- /cpp_source/ch13/ex_13_13: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch13/ex_13_13 -------------------------------------------------------------------------------- /cpp_source/ch13/ex_13_13.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct X { 6 | X() { std::cout << "X()" << std::endl; } 7 | X(const X&) { std::cout << "X(const X&)" << std::endl; } 8 | X& operator=(const X&) { std::cout << "X& operator=(const X&)" << std::endl; return *this; } 9 | ~X() { std::cout << "~X()" << std::endl; } 10 | }; 11 | 12 | void f(const X &rx, X x) 13 | { 14 | std::cout << "code: std::vector vec;" << std::endl; 15 | std::vector vec; 16 | std::cout << "code: vec.reserve(2);" << std::endl; 17 | vec.reserve(2); 18 | std::cout << "code: vec.push_back(rx);" << std::endl; 19 | vec.push_back(rx); 20 | std::cout << "code: vec.push_back(x);" << std::endl; 21 | vec.push_back(x); 22 | } 23 | 24 | int main() 25 | { 26 | std::cout << "code: X *px = new X;" << std::endl; 27 | X *px = new X; 28 | std::cout << "code: f(*px, *px);" << std::endl; 29 | f(*px, *px); 30 | std::cout << "code: delete px;" << std::endl; 31 | delete px; 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /cpp_source/ch13/ex_13_34_36_37.cpp: -------------------------------------------------------------------------------- 1 | #include "ex_13_34_36_37.h" 2 | #include 3 | 4 | void swap(Message &lhs, Message &rhs) 5 | { 6 | using std::swap; 7 | lhs.remove_from_Folders(); // Use existing member function to avoid duplicate code. 8 | rhs.remove_from_Folders(); // Use existing member function to avoid duplicate code. 9 | 10 | swap(lhs.folders, rhs.folders); 11 | swap(lhs.contents, rhs.contents); 12 | 13 | lhs.add_to_Folders(lhs); // Use existing member function to avoid duplicate code. 14 | rhs.add_to_Folders(rhs); // Use existing member function to avoid duplicate code. 15 | } 16 | 17 | // Message Implementation 18 | 19 | void Message::save(Folder &f) 20 | { 21 | addFldr(&f); // Use existing member function to avoid duplicate code. 22 | f.addMsg(this); 23 | } 24 | 25 | void Message::remove(Folder &f) 26 | { 27 | remFldr(&f); // Use existing member function to avoid duplicate code. 28 | f.remMsg(this); 29 | } 30 | 31 | void Message::add_to_Folders(const Message &m) 32 | { 33 | for (auto f : m.folders) 34 | f->addMsg(this); 35 | } 36 | 37 | Message::Message(const Message &m) 38 | : contents(m.contents), folders(m.folders) 39 | { 40 | add_to_Folders(m); 41 | } 42 | 43 | void Message::remove_from_Folders() 44 | { 45 | for (auto f : folders) 46 | f->remMsg(this); 47 | // The book added one line here: folders.clear(); but I think it is redundant and more importantly, it will cause a bug: 48 | // - In Message::operator=, in the case of self-assignment, it first calls remove_from_Folders() and its folders.clear() 49 | // clears the data member of lhs(rhs), and there is no way we can assign it back to lhs. 50 | // Refer to: http://stackoverflow.com/questions/29308115/protection-again-self-assignment 51 | // - Why is it redundant? As its analogous function Message::add_to_Folders(), Message::remove_from_Folders() should ONLY 52 | // take care of the bookkeeping in Folders but not touch the Message's own data members - makes it much clearer and easier 53 | // to use. As you can see in the 2 places where we call Message::remove_from_Folders(): in Message::operator=, folders.clear() 54 | // introduces a bug as illustrated above; in the destructor ~Message(), the member "folders" will be destroyed anyways, why do 55 | // we need to clear it first? 56 | } 57 | 58 | Message::~Message() 59 | { 60 | remove_from_Folders(); 61 | } 62 | 63 | Message &Message::operator=(const Message &rhs) 64 | { 65 | remove_from_Folders(); 66 | contents = rhs.contents; 67 | folders = rhs.folders; 68 | add_to_Folders(rhs); 69 | return *this; 70 | } 71 | 72 | void Message::print_debug() 73 | { 74 | std::cout << contents << std::endl; 75 | } 76 | 77 | // Folder Implementation 78 | 79 | void swap(Folder &lhs, Folder &rhs) 80 | { 81 | using std::swap; 82 | lhs.remove_from_Message(); 83 | rhs.remove_from_Message(); 84 | 85 | swap(lhs.msgs, rhs.msgs); 86 | 87 | lhs.add_to_Message(lhs); 88 | rhs.add_to_Message(rhs); 89 | } 90 | 91 | void Folder::add_to_Message(const Folder &f) 92 | { 93 | for (auto m : f.msgs) 94 | m->addFldr(this); 95 | } 96 | 97 | Folder::Folder(const Folder &f) 98 | : msgs(f.msgs) 99 | { 100 | add_to_Message(f); 101 | } 102 | 103 | void Folder::remove_from_Message() 104 | { 105 | for (auto m : msgs) 106 | m->remFldr(this); 107 | } 108 | 109 | Folder::~Folder() 110 | { 111 | remove_from_Message(); 112 | } 113 | 114 | Folder &Folder::operator=(const Folder &rhs) 115 | { 116 | remove_from_Message(); 117 | msgs = rhs.msgs; 118 | add_to_Message(rhs); 119 | return *this; 120 | } 121 | 122 | void Folder::print_debug() 123 | { 124 | for (auto m : msgs) 125 | std::cout << m->contents << " "; 126 | std::cout << std::endl; 127 | } 128 | 129 | int main() 130 | { 131 | return 0; 132 | } -------------------------------------------------------------------------------- /cpp_source/ch13/ex_13_34_36_37.h: -------------------------------------------------------------------------------- 1 | #ifndef CP5_ex13_34_36_37_h 2 | #define CP5_ex13_34_36_37_h 3 | 4 | #include 5 | #include 6 | 7 | class Folder; 8 | 9 | class Message { 10 | friend void swap(Message &, Message &); 11 | friend class Folder; 12 | public: 13 | explicit Message(const std::string &str = ""):contents(str) { } 14 | Message(const Message&); 15 | Message& operator=(const Message&); 16 | ~Message(); 17 | void save(Folder&); 18 | void remove(Folder&); 19 | 20 | void print_debug(); 21 | 22 | private: 23 | std::string contents; 24 | std::set folders; 25 | 26 | void add_to_Folders(const Message&); 27 | void remove_from_Folders(); 28 | 29 | void addFldr(Folder *f) { folders.insert(f); } 30 | void remFldr(Folder *f) { folders.erase(f); } 31 | }; 32 | 33 | void swap(Message&, Message&); 34 | 35 | class Folder { 36 | friend void swap(Folder &, Folder &); 37 | friend class Message; 38 | public: 39 | Folder() = default; 40 | Folder(const Folder &); 41 | Folder& operator=(const Folder &); 42 | ~Folder(); 43 | 44 | void print_debug(); 45 | 46 | private: 47 | std::set msgs; 48 | 49 | void add_to_Message(const Folder&); 50 | void remove_from_Message(); 51 | 52 | void addMsg(Message *m) { msgs.insert(m); } 53 | void remMsg(Message *m) { msgs.erase(m); } 54 | }; 55 | 56 | void swap(Folder &, Folder &); 57 | 58 | #endif // MESSAGE -------------------------------------------------------------------------------- /cpp_source/ch14/ex_14_44: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch14/ex_14_44 -------------------------------------------------------------------------------- /cpp_source/ch14/ex_14_44.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int add(int i, int j) { return i + j; } 7 | auto mod = [](int i, int j) { return i % j; }; 8 | struct Div { int operator ()(int i, int j) const { return i / j; } }; 9 | 10 | auto binops = std::map> 11 | { 12 | { "+", add }, // function pointer 13 | { "-", std::minus() }, // library functor 14 | { "/", Div() }, // user-defined functor 15 | { "*", [](int i, int j) { return i*j; } }, // unnamed lambda 16 | { "%", mod } // named lambda object 17 | }; 18 | 19 | 20 | int main() 21 | { 22 | while (std::cout << "Pls enter as: num operator num :\n", true) 23 | { 24 | int lhs, rhs; std::string op; 25 | std::cin >> lhs >> op >> rhs; 26 | std::cout << binops[op](lhs, rhs) << std::endl; 27 | } 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/bulk_quote.cpp: -------------------------------------------------------------------------------- 1 | #include "bulk_quote.h" 2 | 3 | double Bulk_quote::net_price(std::size_t n) const 4 | { 5 | return n * price * ( n >= quantity ? 1 - discount : 1); 6 | } 7 | 8 | void Bulk_quote::debug() const 9 | { 10 | std::cout //<< "data members of this class:\n" 11 | << "min_qty= " << quantity << " " 12 | << "discount= " << this->discount<< " \n"; 13 | } -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/bulk_quote.h: -------------------------------------------------------------------------------- 1 | #ifndef BULK_QUOTE_H 2 | #define BULK_QUOTE_H 3 | #include "disc_quote.h" 4 | 5 | class Bulk_quote : public Disc_quote 6 | { 7 | 8 | public: 9 | Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; } 10 | Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : 11 | Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; } 12 | 13 | // copy constructor 14 | Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq) 15 | { std::cout << "Bulk_quote : copy constructor\n"; } 16 | 17 | // move constructor 18 | //page 535, " In a constructor, noexcept appears between the parameter list and the : that begins the constructor initializer list" 19 | Bulk_quote(Bulk_quote&& bq) noexcept : Disc_quote(std::move(bq)) 20 | { 21 | std::cout << "Bulk_quote : move constructor\n"; 22 | } 23 | 24 | // copy =() 25 | Bulk_quote& operator =(const Bulk_quote& rhs) 26 | { 27 | Disc_quote::operator =(rhs); 28 | std::cout << "Bulk_quote : copy =()\n"; 29 | 30 | return *this; 31 | } 32 | 33 | 34 | // move =() 35 | Bulk_quote& operator =(Bulk_quote&& rhs) noexcept 36 | { 37 | Disc_quote::operator =(std::move(rhs)); 38 | std::cout << "Bulk_quote : move =()\n"; 39 | 40 | return *this; 41 | } 42 | 43 | double net_price(std::size_t n) const override; 44 | void debug() const override; 45 | 46 | ~Bulk_quote() override 47 | { 48 | std::cout << "destructing Bulk_quote\n"; 49 | } 50 | }; 51 | 52 | 53 | 54 | #endif // BULK_QUOTE_H -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/disc_quote.cpp: -------------------------------------------------------------------------------- 1 | #include "disc_quote.h" -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/disc_quote.h: -------------------------------------------------------------------------------- 1 | #ifndef DISC_QUOTE_H 2 | #define DISC_QUOTE_H 3 | 4 | #include "quote.h" 5 | class Disc_quote : public Quote 6 | { 7 | friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs); 8 | public: 9 | Disc_quote() { std::cout << "default constructing Disc_quote\n"; } 10 | 11 | Disc_quote(const std::string& b, double p, std::size_t q, double d) : 12 | Quote(b, p), quantity(q), discount(d) 13 | { 14 | std::cout << "Disc_quote : constructor taking 4 parameters.\n"; 15 | } 16 | 17 | // copy constructor 18 | Disc_quote(const Disc_quote& dq) : 19 | Quote(dq), quantity(dq.quantity), discount(dq.discount) 20 | { 21 | std::cout << "Disc_quote : copy constructor.\n"; 22 | } 23 | 24 | // move constructor 25 | Disc_quote(Disc_quote&& dq) noexcept : 26 | Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount)) 27 | { 28 | std::cout << "Disc_quote : move constructor.\n"; 29 | } 30 | 31 | // copy =() 32 | Disc_quote& operator =(const Disc_quote& rhs) 33 | { 34 | Quote::operator =(rhs); 35 | this->quantity = rhs.quantity; 36 | this->discount = rhs.discount; 37 | 38 | std::cout << "Disc_quote : copy =()\n"; 39 | 40 | return *this; 41 | } 42 | 43 | // move =() 44 | Disc_quote& operator =(Disc_quote&& rhs) noexcept 45 | { 46 | if (*this != rhs) 47 | { 48 | Quote::operator =(std::move(rhs)); 49 | this->quantity = std::move(rhs.quantity); 50 | this->discount = std::move(rhs.discount); 51 | } 52 | std::cout << "Disc_quote : move =()\n"; 53 | 54 | return *this; 55 | } 56 | 57 | virtual double net_price(std::size_t n) const override = 0; 58 | 59 | ~Disc_quote() 60 | { 61 | std::cout << "destructing Dis_quote\n"; 62 | } 63 | 64 | protected: 65 | std::size_t quantity = 3; 66 | double discount = 0.0; 67 | }; 68 | 69 | bool inline 70 | operator !=(const Disc_quote& lhs, const Disc_quote& rhs) 71 | { 72 | return Quote(lhs) != Quote(rhs) 73 | && 74 | lhs.quantity != rhs.quantity 75 | && 76 | lhs.discount != rhs.discount; 77 | } 78 | 79 | #endif // DISC_QUOTE_H -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/limit_quote.cpp: -------------------------------------------------------------------------------- 1 | #include "limit_quote.h" 2 | 3 | 4 | void Limit_quote::debug() const 5 | { 6 | std::cout //<< "data members of this class:\n" 7 | << "max_qty= " << this->quantity << " " 8 | << "discount= " << this->discount<< " \n"; 9 | } -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/limit_quote.h: -------------------------------------------------------------------------------- 1 | #ifndef LIMIT_QUOTE_H 2 | #define LIMIT_QUOTE_H 3 | 4 | #include "disc_quote.h" 5 | 6 | class Limit_quote : public Disc_quote 7 | { 8 | public: 9 | Limit_quote() = default; 10 | Limit_quote(const std::string& b, double p, std::size_t max, double disc): 11 | Disc_quote(b, p, max, disc) { } 12 | 13 | double net_price(std::size_t n) const override 14 | { return n * price * (n < quantity ? 1 - discount : 1 ); } 15 | 16 | void debug() const override; 17 | }; 18 | 19 | #endif // LIMIT_QUOTE_H -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "quote.h" 5 | #include "bulk_quote.h" 6 | #include "limit_quote.h" 7 | #include "disc_quote.h" 8 | 9 | 10 | int main() 11 | { 12 | Bulk_quote bq1; 13 | Bulk_quote bq2("ss", 2.05, 12, 0.3); 14 | bq2 = std::move(bq2); 15 | 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/quote.cpp: -------------------------------------------------------------------------------- 1 | #include "quote.h" 2 | 3 | void Quote::debug() const 4 | { 5 | std::cout //<< "data members of this class:\n" 6 | << "bookNo= " <bookNo << " " 7 | << "price= " <price<< " \n"; 8 | } -------------------------------------------------------------------------------- /cpp_source/ch15/ex_15_26/quote.h: -------------------------------------------------------------------------------- 1 | #ifndef QUOTE_H 2 | #define QUOTE_H 3 | 4 | #include 5 | #include 6 | 7 | class Quote 8 | { 9 | friend bool operator !=(const Quote& lhs, const Quote& rhs); 10 | public: 11 | Quote() { std::cout << "default constructing Quote\n"; } 12 | Quote(const std::string &b, double p) : 13 | bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; } 14 | 15 | // copy constructor 16 | Quote(const Quote& q) : bookNo(q.bookNo), price(q.price) 17 | { std::cout << "Quote: copy constructing\n"; } 18 | 19 | // move constructor 20 | Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)) 21 | { std::cout << "Quote: move constructing\n"; } 22 | 23 | // copy = 24 | Quote& operator =(const Quote& rhs) 25 | { 26 | if(*this != rhs) 27 | { 28 | bookNo = rhs.bookNo; 29 | price = rhs.price; 30 | } 31 | std::cout << "Quote: copy =() \n"; 32 | 33 | return *this; 34 | } 35 | 36 | // move = 37 | Quote& operator =(Quote&& rhs) noexcept 38 | { 39 | if(*this != rhs) 40 | { 41 | bookNo = std::move(rhs.bookNo); 42 | price = std::move(rhs.price); 43 | } 44 | std::cout << "Quote: move =!!!!!!!!! \n"; 45 | 46 | return *this; 47 | } 48 | 49 | std::string isbn() const { return bookNo; } 50 | virtual double net_price(std::size_t n) const { return n * price; } 51 | virtual void debug() const; 52 | 53 | virtual ~Quote() 54 | { 55 | std::cout << "destructing Quote\n"; 56 | } 57 | 58 | private: 59 | std::string bookNo; 60 | 61 | protected: 62 | double price = 10.0; 63 | }; 64 | 65 | bool inline 66 | operator !=(const Quote& lhs, const Quote& rhs) 67 | { 68 | return lhs.bookNo != rhs.bookNo 69 | && 70 | lhs.price != rhs.price; 71 | } 72 | 73 | #endif // QUOTE_H -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/StrBlob.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains code from "C++ Primer, Fifth Edition", by Stanley B. 3 | * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the 4 | * 5 | * 6 | * 7 | * "The authors and publisher have taken care in the preparation of this book, 8 | * but make no expressed or implied warranty of any kind and assume no 9 | * responsibility for errors or omissions. No liability is assumed for 10 | * incidental or consequential damages in connection with or arising out of the 11 | * use of the information or programs contained herein." 12 | * 13 | * Permission is granted for this code to be used for educational purposes in 14 | * association with the book, given proper citation if and when posted or 15 | * reproduced. Any commercial use of this code requires the explicit written 16 | * permission of the publisher, Addison-Wesley Professional, a division of 17 | * Pearson Education, Inc. Send your request for permission, stating clearly 18 | * what code you would like to use, and in what specific way, to the following 19 | * address: 20 | * 21 | * Pearson Education, Inc. 22 | * Rights and Permissions Department 23 | * One Lake Street 24 | * Upper Saddle River, NJ 07458 25 | * Fax: (201) 236-3290 26 | */ 27 | 28 | #ifndef STRBLOB_H 29 | #define STRBLOB_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // forward declaration needed for friend declaration in StrBlob 38 | class StrBlobPtr; 39 | 40 | class StrBlob 41 | { 42 | friend class StrBlobPtr; 43 | public: 44 | typedef std::vector::size_type size_type; 45 | 46 | // constructors 47 | StrBlob() : data(std::make_shared>()) { } 48 | StrBlob(std::initializer_list il); 49 | 50 | // size operations 51 | size_type size() const { return data->size(); } 52 | bool empty() const { return data->empty(); } 53 | 54 | // add and remove elements 55 | void push_back(const std::string &t) { data->push_back(t); } 56 | void pop_back(); 57 | 58 | // element access 59 | std::string& front(); 60 | std::string& back(); 61 | 62 | // interface to StrBlobPtr 63 | StrBlobPtr begin(); // can't be defined until StrBlobPtr is 64 | StrBlobPtr end(); 65 | private: 66 | std::shared_ptr> data; 67 | // throws msg if data[i] isn't valid 68 | void check(size_type i, const std::string &msg) const; 69 | }; 70 | 71 | // constructor 72 | inline 73 | StrBlob::StrBlob(std::initializer_list il): 74 | data(std::make_shared>(il)) { } 75 | 76 | // StrBlobPtr throws an exception on attempts to access a nonexistent element 77 | class StrBlobPtr 78 | { 79 | friend bool eq(const StrBlobPtr&, const StrBlobPtr&); 80 | public: 81 | StrBlobPtr(): curr(0) { } 82 | StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { } 83 | 84 | // newly overloaded why? 85 | StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { } 86 | 87 | std::string& deref() const; 88 | StrBlobPtr& incr(); // prefix version 89 | StrBlobPtr& decr(); // prefix version 90 | private: 91 | // check returns a shared_ptr to the vector if the check succeeds 92 | std::shared_ptr> 93 | check(std::size_t, const std::string&) const; 94 | 95 | // store a weak_ptr, which means the underlying vector might be destroyed 96 | std::weak_ptr> wptr; 97 | std::size_t curr; // current position within the array 98 | }; 99 | 100 | inline 101 | std::string& StrBlobPtr::deref() const 102 | { 103 | auto p = check(curr, "dereference past end"); 104 | return (*p)[curr]; // (*p) is the vector to which this object points 105 | } 106 | 107 | inline 108 | std::shared_ptr> 109 | StrBlobPtr::check(std::size_t i, const std::string &msg) const 110 | { 111 | auto ret = wptr.lock(); // is the vector still around? 112 | if (!ret) 113 | throw std::runtime_error("unbound StrBlobPtr"); 114 | 115 | if (i >= ret->size()) 116 | throw std::out_of_range(msg); 117 | return ret; // otherwise, return a shared_ptr to the vector 118 | } 119 | 120 | // prefix: return a reference to the incremented object 121 | inline 122 | StrBlobPtr& StrBlobPtr::incr() 123 | { 124 | // if curr already points past the end of the container, can't increment it 125 | check(curr, "increment past end of StrBlobPtr"); 126 | ++curr; // advance the current state 127 | return *this; 128 | } 129 | 130 | inline 131 | StrBlobPtr& StrBlobPtr::decr() 132 | { 133 | // if curr is zero, decrementing it will yield an invalid subscript 134 | --curr; // move the current state back one element} 135 | check(-1, "decrement past begin of StrBlobPtr"); 136 | return *this; 137 | } 138 | 139 | // begin and end members for StrBlob 140 | inline 141 | StrBlobPtr 142 | StrBlob::begin() 143 | { 144 | return StrBlobPtr(*this); 145 | } 146 | 147 | inline 148 | StrBlobPtr 149 | StrBlob::end() 150 | { 151 | auto ret = StrBlobPtr(*this, data->size()); 152 | return ret; 153 | } 154 | 155 | // named equality operators for StrBlobPtr 156 | inline 157 | bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) 158 | { 159 | auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); 160 | // if the underlying vector is the same 161 | if (l == r) 162 | // then they're equal if they're both null or 163 | // if they point to the same element 164 | return (!r || lhs.curr == rhs.curr); 165 | else 166 | return false; // if they point to difference vectors, they're not equal 167 | } 168 | 169 | inline 170 | bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) 171 | { 172 | return !eq(lhs, rhs); 173 | } 174 | #endif 175 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/andquery.cpp: -------------------------------------------------------------------------------- 1 | #include "andquery.h" 2 | 3 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/andquery.h: -------------------------------------------------------------------------------- 1 | #ifndef ANDQUERY_H 2 | #define ANDQUERY_H 3 | 4 | 5 | #include "binaryquery.h" 6 | 7 | class AndQuery : public BinaryQuery 8 | { 9 | friend Query operator&(const Query&, const Query&); 10 | AndQuery(const Query& left, const Query& right): 11 | BinaryQuery(left, right, "&") 12 | { 13 | std::cout << "AndQuery::AndQuery()\n"; 14 | } 15 | 16 | // @note: inherits rep and define eval 17 | 18 | QueryResult eval(const TextQuery &) const override 19 | { 20 | // this is just a placeholder rather than the real definition 21 | } 22 | }; 23 | 24 | inline Query operator& (const Query& lhs, const Query& rhs) 25 | { 26 | return std::shared_ptr(new AndQuery(lhs, rhs)); 27 | } 28 | 29 | #endif // ANDQUERY_H 30 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/binaryquery.cpp: -------------------------------------------------------------------------------- 1 | #include "binaryquery.h" 2 | 3 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/binaryquery.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARYQUERY_H 2 | #define BINARYQUERY_H 3 | 4 | #include "query_base.h" 5 | #include "query.h" 6 | 7 | 8 | 9 | /** 10 | * @brief The BinaryQuery class 11 | *An abstract class holds data needed by the query types that operate on two operands 12 | */ 13 | class BinaryQuery : public Query_base 14 | { 15 | protected: 16 | BinaryQuery(const Query&l, const Query& r, std::string s): 17 | lhs(l), rhs(r), opSym(s) 18 | { 19 | std::cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n"; 20 | } 21 | 22 | // @note: abstract class: BinaryQuery doesn't define eval 23 | 24 | std::string rep() const override 25 | { 26 | std::cout << "BinaryQuery::rep()\n"; 27 | return "(" + lhs.rep() + " " 28 | + opSym + " " 29 | + rhs.rep() + ")"; 30 | } 31 | 32 | Query lhs, rhs; 33 | std::string opSym; 34 | }; 35 | 36 | #endif // BINARYQUERY_H 37 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "queryresult.h" 9 | #include "textquery.h" 10 | #include "query_base.h" 11 | #include "query.h" 12 | #include "andquery.h" 13 | #include "orquery.h" 14 | 15 | 16 | 17 | int main() 18 | { 19 | std::ifstream file("storyDataFile.txt"); 20 | 21 | TextQuery tQuery(file); 22 | 23 | Query q = Query("hello") | Query("am"); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/notquery.cpp: -------------------------------------------------------------------------------- 1 | #include "notquery.h" 2 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/notquery.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTQUERY_H 2 | #define NOTQUERY_H 3 | #include "query_base.h" 4 | #include "query.h" 5 | 6 | 7 | /** 8 | * @brief The NotQuery class 9 | * 10 | *The ~ operator generates a NotQuery, which holds a Query, 11 | *which it negates. 12 | */ 13 | class NotQuery : public Query_base 14 | { 15 | friend Query operator~(const Query& operand); 16 | NotQuery(const Query& q): query(q) 17 | { 18 | std::cout << "NotQuery::NotQuery()\n"; 19 | } 20 | 21 | // virtuals: 22 | std::string rep() const override 23 | { 24 | std::cout << "NotQuery::rep()\n"; 25 | return "~(" + query.rep() + ")"; 26 | } 27 | 28 | QueryResult eval(const TextQuery &) const override; 29 | 30 | Query query; 31 | }; 32 | 33 | inline Query operator~(const Query& operand) 34 | { 35 | return std::shared_ptr(new NotQuery(operand)); 36 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 37 | // note : There is an imlplicit conversion here. 38 | // The Query constructor that takes shared_ptr is not 39 | // "explicit", thus the compiler allows this conversion. 40 | } 41 | 42 | #endif // NOTQUERY_H 43 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/orquery.cpp: -------------------------------------------------------------------------------- 1 | #include "orquery.h" 2 | 3 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/orquery.h: -------------------------------------------------------------------------------- 1 | #ifndef ORQUERY_H 2 | #define ORQUERY_H 3 | 4 | #include "binaryquery.h" 5 | 6 | class OrQuery :public BinaryQuery 7 | { 8 | friend Query operator|(const Query&, const Query&); 9 | OrQuery(const Query& left, const Query& right): 10 | BinaryQuery(left, right, "|") 11 | { 12 | std::cout << "OrQuery::OrQuery\n"; 13 | } 14 | 15 | QueryResult eval(const TextQuery& )const override 16 | { 17 | //place holder 18 | } 19 | }; 20 | 21 | inline Query operator|(const Query &lhs, const Query& rhs) 22 | { 23 | return std::shared_ptr(new OrQuery(lhs, rhs)); 24 | } 25 | 26 | #endif // ORQUERY_H 27 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/query.cpp: -------------------------------------------------------------------------------- 1 | #include "query.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/query.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERY_H 2 | #define QUERY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "query_base.h" 8 | #include "queryresult.h" 9 | #include "textquery.h" 10 | #include "wordquery.h" 11 | 12 | 13 | 14 | /** 15 | * @brief interface class to manage the Query_base inheritance hierachy 16 | * Query类对外提供接口,同时隐藏了Quer_base的继承体系。 17 | * 每个Query对象都有一个指向Query_base对象的shared_ptr。 18 | */ 19 | class Query 20 | { 21 | friend Query operator~(const Query&); 22 | friend Query operator|(const Query&, const Query&); 23 | friend Query operator&(const Query&, const Query&); 24 | public: 25 | // build a new WordQuery 26 | Query(const std::string& s) : q(new WordQuery(s)) 27 | { 28 | std::cout << "Query::Query(const std::string& s) where s="+s+"\n"; 29 | } 30 | 31 | // interface functions: call the corresponding Query_base operatopns 32 | QueryResult eval(const TextQuery& t) const 33 | { return q->eval(t); } 34 | std::string rep() const 35 | { 36 | std::cout << "Query::rep() \n"; 37 | return q->rep(); 38 | } 39 | 40 | private: 41 | // constructor only for friends 42 | Query(std::shared_ptr query) : 43 | q(query) 44 | { 45 | std::cout << "Query::Query(std::shared_ptr query)\n"; 46 | } 47 | std::shared_ptr q; 48 | }; 49 | 50 | inline std::ostream& 51 | operator << (std::ostream& os, const Query& query) 52 | { 53 | // make a virtual call through its Query_base pointer to rep(); 54 | return os << query.rep(); 55 | } 56 | 57 | #endif // QUERY_H 58 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/query_base.cpp: -------------------------------------------------------------------------------- 1 | #include "query_base.h" 2 | 3 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/query_base.h: -------------------------------------------------------------------------------- 1 | #ifndef QUERY_BASE_H 2 | #define QUERY_BASE_H 3 | #include "textquery.h" 4 | #include "queryresult.h" 5 | 6 | /** 7 | * @brief abstract class acts as a base class for all concrete query types 8 | * all members are private. 9 | */ 10 | class Query_base 11 | { 12 | friend class Query; 13 | protected: 14 | using line_no = TextQuery::line_no; // used in the eval function 15 | virtual ~Query_base() = default; 16 | 17 | private: 18 | // returns QueryResult that matches this query 19 | virtual QueryResult eval(const TextQuery&) const = 0; 20 | 21 | // a string representation of this query 22 | virtual std::string rep() const = 0; 23 | }; 24 | 25 | #endif // QUERY_BASE_H 26 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/queryresult.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * @file queryresult.cpp 3 | * @author Alan.W 4 | * @date 30 DEC 2013 5 | * @remark using class StrBlob 6 | ***************************************************************************/ 7 | 8 | 9 | #include "queryresult.h" 10 | 11 | 12 | /** 13 | * @brief print the result to the output stream specified. 14 | * @note class QueryResult's friend 15 | */ 16 | std::ostream 17 | &print(std::ostream &os, const QueryResult &qr) 18 | { 19 | os << qr.sought << " occurs " << qr.sp_lines->size() << " " 20 | << "times" << "\n"; 21 | 22 | // print each line in which the word appears 23 | for ( auto &index : *qr.sp_lines) 24 | { 25 | os << "\t(line " << index + 1 << ") "; 26 | const StrBlobPtr wp(qr.file, index); 27 | os << wp.deref() << "\n"; 28 | } 29 | return os; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/queryresult.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * @file queryresult.h 3 | * @author Alan.W 4 | * @date 30 DEC 2013 5 | * @remark using class StrBlob 6 | ***************************************************************************/ 7 | 8 | // 9 | // Exercise 12.33: 10 | // In Chapter 15 we’ll extend our query system and will need some additional 11 | // members in the QueryResult class. 12 | // 13 | // Add members named begin and end that 14 | // return iterators into the set of line numbers returned by a given query, 15 | // and a member named get_file that returns a shared_ptr to the file in the 16 | // QueryResult object. 17 | // 18 | 19 | #ifndef QUERYRESULT_H 20 | #define QUERYRESULT_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "textquery.h" 28 | 29 | /** 30 | * @brief Query Result 31 | */ 32 | class QueryResult 33 | { 34 | friend std::ostream& print(std::ostream&, const QueryResult&); 35 | 36 | public: 37 | // constructor 38 | QueryResult(std::string s, 39 | std::shared_ptr> sp_l, 40 | StrBlob f) : 41 | sought(s), sp_lines(sp_l), file(f) { } 42 | 43 | // added for ex12.33 44 | // ? Think about wether the "const"s here are expected. 45 | const StrBlob& get_file() const{ return file; } 46 | 47 | std::set::iterator 48 | begin() { return sp_lines->begin(); } 49 | 50 | std::set::iterator 51 | end() { return sp_lines->end(); } 52 | 53 | 54 | 55 | private: 56 | // three data members 57 | std::string sought; 58 | std::shared_ptr> sp_lines; 59 | StrBlob file; 60 | 61 | }; 62 | 63 | /** 64 | * @brief print the result to the output stream specified. 65 | */ 66 | std::ostream& 67 | print(std::ostream&, const QueryResult &); 68 | #endif // QUERYRESULT_H 69 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/storyDataFile.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | This is a funny trip for learning c++. 3 | I am Javen Chen. 4 | Nice to see you. -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/textquery.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * @file textquery.cpp 3 | * @author Alan.W 4 | * @date 30 DEC 2013 5 | * @remark The TextQuery class using StrBlob 6 | ***************************************************************************/ 7 | 8 | // 9 | // Exercise 12.32: 10 | // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a 11 | // vector to hold the input file. 12 | // Relevant post on Stack Overflow: 13 | // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m 14 | // 15 | 16 | #include "textquery.h" 17 | #include "queryresult.h" 18 | #include 19 | #include 20 | #include 21 | 22 | /** 23 | * @brief constructor using StrBlob. 24 | */ 25 | TextQuery::TextQuery(std::ifstream &fin) : 26 | file(StrBlob()), 27 | wordMap(std::map>>()) 28 | { 29 | std::string line; 30 | 31 | // each line 32 | while(std::getline(fin, line)) 33 | { 34 | file.push_back(line); 35 | int n = file.size() - 1; // the current line number 36 | 37 | // each word 38 | std::stringstream lineSteam(line); 39 | std::string word; 40 | while(lineSteam >> word) 41 | { 42 | std::shared_ptr>& 43 | sp_lines = wordMap[word]; 44 | // if null 45 | if(!sp_lines) 46 | { 47 | sp_lines.reset(new std::set); 48 | } 49 | sp_lines->insert(n); 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * @brief do a query opertion and return QueryResult object. 56 | */ 57 | QueryResult 58 | TextQuery::query(const std::string &sought) const 59 | { 60 | // dynamicaly allocated set used for the word does not appear. 61 | static std::shared_ptr> noData(new std::set); 62 | 63 | // fetch the iterator to the matching element in the map. 64 | //std::map>>::const_iterator 65 | auto iter = wordMap.find(sought); 66 | if(iter == wordMap.end()) 67 | return QueryResult(sought, noData, file); 68 | else 69 | return QueryResult(sought, iter->second, file); 70 | } 71 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/textquery.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * @file textquery.h 3 | * @author Alan.W 4 | * @date 30 DEC 2013 5 | * @remark The TextQuery class using StrBlob 6 | ***************************************************************************/ 7 | 8 | // 9 | // Exercise 12.32: 10 | // Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a 11 | // vector to hold the input file. 12 | // Relevant post on Stack Overflow: 13 | // http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m 14 | // 15 | 16 | 17 | #ifndef TEXTQUERY_H 18 | #define TEXTQUERY_H 19 | 20 | #include "StrBlob.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class QueryResult; 28 | 29 | /** 30 | * @brief The TextQuery class using StrBlob 31 | */ 32 | class TextQuery 33 | { 34 | public: 35 | typedef StrBlob::size_type line_no; 36 | 37 | // constructor 38 | TextQuery(std::ifstream& fin); 39 | 40 | // query operation 41 | QueryResult query(const std::string&) const; 42 | 43 | private: 44 | // data members 45 | StrBlob file; 46 | 47 | std::map>> wordMap; 49 | 50 | }; 51 | 52 | #endif // TEXTQUERY_H 53 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/wordquery.cpp: -------------------------------------------------------------------------------- 1 | #include "wordquery.h" 2 | 3 | -------------------------------------------------------------------------------- /cpp_source/ch15/text_query/wordquery.h: -------------------------------------------------------------------------------- 1 | #ifndef WORDQUERY_H 2 | #define WORDQUERY_H 3 | 4 | #include "query_base.h" 5 | 6 | /** 7 | * @brief The WordQuery class 8 | *The only class that actually performs a query on the given TextQuery object. 9 | *No public members defined in this class. All operation are through the friend 10 | *class Query. 11 | */ 12 | class WordQuery : public Query_base 13 | { 14 | // class Query uses the WordQuery constructor 15 | friend class Query; 16 | WordQuery(const std::string& s): 17 | query_word(s) 18 | { 19 | std::cout << "WordQuery::WordQuery(" + s + ")\n"; 20 | } 21 | 22 | 23 | // virtuals: 24 | QueryResult eval(const TextQuery& t) const override 25 | { return t.query(query_word); } 26 | std::string rep() const override 27 | { 28 | std::cout << "WodQuery::rep()\n"; 29 | return query_word; 30 | } 31 | 32 | 33 | std::string query_word; 34 | }; 35 | 36 | #endif // WORDQUERY_H 37 | -------------------------------------------------------------------------------- /cpp_source/ch16/ex_16_51: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/Cpp_Primer_Practice/e306f90bebd682a6a2bd6d210fd82b4783e9e872/cpp_source/ch16/ex_16_51 -------------------------------------------------------------------------------- /cpp_source/ch16/ex_16_51.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | template 6 | void foo(const T &t, const Args& ... rest){ 7 | cout << "sizeof...(Args): " << sizeof...(Args) << endl; 8 | cout << "sizeof...(rest): " << sizeof...(rest) << endl; 9 | }; 10 | 11 | void test_param_packet(){ 12 | int i = 0; 13 | double d = 3.14; 14 | string s = "how now brown cow"; 15 | 16 | foo(i, s, 42, d); 17 | foo(s, 42, "hi"); 18 | foo(d, s); 19 | foo("hi"); 20 | } 21 | 22 | int main(){ 23 | test_param_packet(); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /cpp_source/ch17/ex_17_4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ex_17_4_SalesData.h" 10 | 11 | using namespace std; 12 | 13 | // matches有三个成员:1.一个书店的索引。2.指向书店中元素的迭代器。3.指向书店中元素的迭代器。 14 | typedef tuple::size_type, 15 | vector::const_iterator, 16 | vector::const_iterator> 17 | matches; 18 | 19 | // files保存每家书店的销售记录 20 | // findBook返回一个vector,每家销售了给定书籍的书店在其中都有一项 21 | vector findBook(const vector> &files, 22 | const string &book) 23 | { 24 | vector ret; //初始化为空vector 25 | // 对每家书店,查找给定书籍匹配的记录范围 26 | for (auto it = files.cbegin; it != files.cend(); ++it) 27 | { 28 | // 查找具有相同ISBN的Sales_data范围,found是一个迭代器pair 29 | auto found = equal_range(it->cbegin(), it->cend(), book, compareIsbn); 30 | if (found.first != found.second) // 此书店销售了给定书籍 31 | // 记住此书店的索引及匹配的范围 32 | ret.push_back(make_tuple(it - files.cbegin(), found.first, found.second)); 33 | } 34 | return ret; //如果未找到匹配记录,ret为空 35 | } 36 | 37 | void reportResults(istream &in, ostream &os, 38 | const vector > &files){ 39 | string s; //要查找的书 40 | while (in >> s){ 41 | auto trans = findBook(files, s); 42 | if (trans.empty()){ 43 | cout << s << " not found in any stores" << endl; 44 | continue; // 获得下一本要查找的书 45 | } 46 | for (const auto &store : trans) // 对每家销售了给定书籍的书店 47 | // get返回store中tuple的指定的成员 48 | os << "store " << get<0>(store) << " sales: " 49 | << accumulate(get<1>(store), get<2>(store), Sales_data(s)) 50 | << endl; 51 | } 52 | } 53 | 54 | int main(){ 55 | return 0; 56 | } -------------------------------------------------------------------------------- /cpp_source/ch17/ex_17_4_SalesData.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using std::istream; using std::ostream; 3 | 4 | #include "ex_17_4_SalesData.h" 5 | 6 | Sales_data::Sales_data(std::istream &is) 7 | { 8 | // read will read a transaction from is into this object 9 | read(is, *this); 10 | } 11 | 12 | double 13 | Sales_data::avg_price() const { 14 | if (units_sold) 15 | return revenue/units_sold; 16 | else 17 | return 0; 18 | } 19 | 20 | // add the value of the given Sales_data into this object 21 | Sales_data& 22 | Sales_data::combine(const Sales_data &rhs) 23 | { 24 | units_sold += rhs.units_sold; // add the members of rhs into 25 | revenue += rhs.revenue; // the members of ``this'' object 26 | return *this; // return the object on which the function was called 27 | } 28 | // = Sales_data 29 | Sales_data &Sales_data::operator =(const Sales_data &rhs) 30 | { 31 | this->bookNo = rhs.bookNo; 32 | this->revenue = rhs.revenue; 33 | this->units_sold = rhs.units_sold; 34 | 35 | return *this; 36 | } 37 | 38 | // =string 39 | Sales_data &Sales_data::operator =(const std::string &rhs) 40 | { 41 | *this= Sales_data(rhs); 42 | return *this; 43 | } 44 | 45 | // += 46 | Sales_data &Sales_data::operator +=(const Sales_data &rhs) 47 | { 48 | this->revenue += rhs.revenue; 49 | this->units_sold += rhs.units_sold; 50 | 51 | return *this; 52 | } 53 | 54 | Sales_data 55 | add(const Sales_data &lhs, const Sales_data &rhs) 56 | { 57 | Sales_data sum = lhs; // copy data members from lhs into sum 58 | sum.combine(rhs); // add data members from rhs into sum 59 | return sum; 60 | } 61 | 62 | // transactions contain ISBN, number of copies sold, and sales price 63 | istream& 64 | read(istream &is, Sales_data &item) 65 | { 66 | double price = 0; 67 | is >> item.bookNo >> item.units_sold >> price; 68 | item.revenue = price * item.units_sold; 69 | return is; 70 | } 71 | 72 | ostream& 73 | print(ostream &os, const Sales_data &item) 74 | { 75 | os << item.isbn() << " " << item.units_sold << " " 76 | << item.revenue << " " << item.avg_price(); 77 | return os; 78 | } 79 | 80 | // added 10.Jan 2014 81 | std::ostream & 82 | operator <<(std::ostream &os, const Sales_data &item) 83 | { 84 | os << item.isbn() << " " << item.units_sold << " " 85 | << item.revenue << " " << item.avg_price(); 86 | 87 | return os; 88 | } 89 | 90 | // added 12.Jan 2014 91 | std::istream& 92 | operator >>(std::istream &is, Sales_data &s) 93 | { 94 | double price; 95 | 96 | // read input 97 | is >> s.bookNo >> s.units_sold >> price; 98 | 99 | // if successful, write into the object, give the object default state otherwise. 100 | if(is) 101 | s.revenue = s.units_sold * price; 102 | else 103 | s = Sales_data(); 104 | 105 | return is; 106 | } -------------------------------------------------------------------------------- /cpp_source/ch17/ex_17_4_SalesData.h: -------------------------------------------------------------------------------- 1 | #ifndef SALES_DATA_H 2 | #define SALES_DATA_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | class Sales_data 9 | { 10 | // friends 11 | friend Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs); 12 | 13 | friend std::ostream& 14 | operator << (std::ostream& os, const Sales_data& s); 15 | 16 | friend std::istream& 17 | operator >> (std::istream& is, Sales_data& s); 18 | 19 | friend Sales_data add(const Sales_data&, const Sales_data&); 20 | friend std::ostream &print(std::ostream&, const Sales_data&); 21 | friend std::istream &read(std::istream&, Sales_data&); 22 | 23 | public: 24 | // constructors 25 | Sales_data() = default; 26 | Sales_data(const std::string &s): bookNo(s) { } 27 | Sales_data(const std::string &s, unsigned n, double p): 28 | bookNo(s), units_sold(n), revenue(p*n) { } 29 | Sales_data(const Sales_data &s ): 30 | bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue) 31 | { } 32 | 33 | Sales_data(Sales_data&& s): 34 | bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue) 35 | { } 36 | 37 | ~Sales_data(){ } 38 | Sales_data(std::istream &); 39 | 40 | std::string isbn() const { return bookNo; } 41 | Sales_data& combine(const Sales_data&); 42 | 43 | // assignments 44 | Sales_data& operator =(const Sales_data& rhs); 45 | Sales_data& operator =(const std::string& rhs); 46 | Sales_data& operator +=(const Sales_data& rhs); 47 | 48 | // conversion 49 | explicit operator std::string () const { return bookNo; } 50 | explicit operator double () const { return revenue; } 51 | 52 | double avg_price() const; 53 | private: 54 | std::string bookNo; 55 | unsigned units_sold = 0; 56 | double revenue = 0.0; 57 | }; 58 | 59 | 60 | // overloaded operators added 10.Jan.2014 for ex14.2 61 | inline Sales_data 62 | operator+(const Sales_data& lhs, const Sales_data& rhs) 63 | { 64 | Sales_data sum = lhs; 65 | sum += rhs; 66 | 67 | return sum; 68 | } 69 | 70 | std::ostream& 71 | operator << (std::ostream& os, const Sales_data& item); 72 | 73 | std::istream& 74 | operator >> (std::istream& is, Sales_data& s); 75 | 76 | // nonmember Sales_data interface functions 77 | Sales_data add(const Sales_data&, const Sales_data&); 78 | std::ostream &print(std::ostream&, const Sales_data&); 79 | std::istream &read(std::istream&, Sales_data&); 80 | 81 | // used in future chapters 82 | inline 83 | bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) 84 | { 85 | return lhs.isbn() < rhs.isbn(); 86 | } 87 | #endif -------------------------------------------------------------------------------- /excersize/ch01.md: -------------------------------------------------------------------------------- 1 | # 第一章 开始 2 | 3 | ## 练习1.1 4 | 查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第2页的main程序。 5 | 6 | 解: 7 | - ``g++ --std=c++11 ch1.cpp -o main`` 8 | - ``./main`` 9 | 10 | ## 练习1.2 11 | 改写程序,让它返回-1。返回值-1通常被当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main返回的错误标识。 12 | 13 | 解: 14 | - 在ubuntu下,使用g++,返回-1,``./main``没有发现任何异常。 15 | - ``echo $?``,返回255。 16 | 17 | ## 练习1.3 18 | 编写程序,在标准输出上打印Hello, World。 19 | 20 | 解: 21 | ```cpp 22 | #include 23 | 24 | int main() 25 | { 26 | std::cout << "Hello, World" << std::endl; 27 | return 0; 28 | } 29 | ``` 30 | 31 | ## 练习1.4 32 | 我们的程序使用加法运算符`+`来将两个数相加。编写程序使用乘法运算符`*`,来打印两个数的积。 33 | 34 | 解: 35 | ```cpp 36 | #include 37 | 38 | int main() 39 | { 40 | std::cout << "Enter two numbers:" << std::endl; 41 | int v1 = 0, v2 = 0; 42 | std::cin >> v1 >> v2; 43 | std::cout << "The product of " << v1 << " and " << v2 44 | << " is " << v1 * v2 << std::endl; 45 | } 46 | ``` 47 | 48 | ## 练习1.5 49 | 我们将所有的输出操作放在一条很长的语句中,重写程序,将每个运算对象的打印操作放在一条独立的语句中。 50 | 51 | 解: 52 | ```cpp 53 | #include 54 | 55 | int main() 56 | { 57 | std::cout << "Enter two numbers:" << std::endl; 58 | int v1 = 0, v2 = 0; 59 | std::cin >> v1 >> v2; 60 | std::cout << "The product of "; 61 | std::cout << v1; 62 | std::cout << " and "; 63 | std::cout << v2; 64 | std::cout << " is "; 65 | std::cout << v1 * v2; 66 | std::cout << std::endl; 67 | } 68 | ``` 69 | 70 | ## 练习1.6 71 | 解释下面程序片段是否合法。 72 | ```cpp 73 | std::cout << "The sum of " << v1; 74 | << " and " << v2; 75 | << " is " << v1 + v2 << std::endl; 76 | ``` 77 | 如果程序是合法的,它的输出是什么?如果程序不合法,原因何在?应该如何修正? 78 | 79 | 解: 80 | 81 | 程序不合法,有多余的分号,修改如下: 82 | ```cpp 83 | std::cout << "The sum of " << v1 84 | << " and " << v2 85 | << " is " << v1 + v2 << std::endl; 86 | ``` 87 | 88 | ## 练习1.7 89 | 编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。 90 | 91 | 解: 92 | ```cpp 93 | /* 正常注释 /* 嵌套注释 */ 正常注释*/ 94 | ``` 95 | 错误信息: 96 | ``` 97 | /* 正常注释 /* 嵌套注释 */ 正常注释*/ 98 | ^ 99 | ch1.cpp:97:37: error: stray ‘\255’ in program 100 | ch1.cpp:97:37: error: stray ‘\243’ in program 101 | ch1.cpp:97:37: error: stray ‘\345’ in program 102 | ch1.cpp:97:37: error: stray ‘\270’ in program 103 | ch1.cpp:97:37: error: stray ‘\270’ in program 104 | ch1.cpp:97:37: error: stray ‘\346’ in program 105 | ch1.cpp:97:37: error: stray ‘\263’ in program 106 | ch1.cpp:97:37: error: stray ‘\250’ in program 107 | ch1.cpp:97:37: error: stray ‘\351’ in program 108 | ch1.cpp:97:37: error: stray ‘\207’ in program 109 | ch1.cpp:97:37: error: stray ‘\212’ in program 110 | ch1.cpp: In function ‘int main()’: 111 | ch1.cpp:97:50: error: expected primary-expression before ‘/’ token 112 | /* 正常注释 /* 嵌套注释 */ 正常注释*/ 113 | ^ 114 | ch1.cpp:98:5: error: expected primary-expression before ‘return’ 115 | return 0; 116 | ^ 117 | ``` 118 | 119 | ## 练习1.8 120 | 指出下列哪些输出语句是合法的(如果有的话): 121 | ```cpp 122 | std::cout << "/*"; 123 | std::cout << "*/"; 124 | std::cout << /* "*/" */; 125 | std::cout << /* "*/" /* "/*" */; 126 | ``` 127 | 预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案(编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。 128 | 129 | 解: 130 | 131 | 只有第三句编译出错,改成如下即可: 132 | ```cpp 133 | std::cout << /* "*/" */"; 134 | ``` 135 | 第四句等价于输出 `" /* "`。 136 | 137 | ## 练习1.9 138 | 139 | 编写程序,使用`while`循环将50到100整数相加。 140 | 141 | 解: 142 | ```cpp 143 | #include 144 | 145 | int main() 146 | { 147 | int sum = 0, val = 50; 148 | while (val <= 100){ 149 | sum += val; 150 | val += 1; 151 | } 152 | std::cout << "Sum of 50 to 100 inclusive is " 153 | << sum << std::endl; 154 | } 155 | ``` 156 | 157 | ## 练习1.10 158 | 除了`++`运算符将运算对象的值增加1之外,还有一个递减运算符`--`实现将值减少1.编写程序与,使用递减运算符在循环中按递减顺序打印出10到0之间的整数。 159 | 160 | 解: 161 | ```cpp 162 | #include 163 | 164 | int main() 165 | { 166 | int val = 10; 167 | while (val >= 0){ 168 | std::cout << val << " "; 169 | val -= 1; 170 | } 171 | std::cout << std::endl; 172 | } 173 | ``` 174 | 175 | ## 练习1.11 176 | 编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数。 177 | 178 | 解: 179 | ```cpp 180 | #include 181 | 182 | int main() 183 | { 184 | int start = 0, end = 0; 185 | std::cout << "Please input two num: "; 186 | std::cin >> start >> end; 187 | if (start <= end) { 188 | while (start <= end){ 189 | std::cout << start << " "; 190 | ++start; 191 | } 192 | std::cout << std::endl; 193 | } 194 | else{ 195 | std::cout << "start should be smaller than end !!!"; 196 | } 197 | } 198 | ``` 199 | 200 | ## 练习1.12 201 | 202 | 下面的for循环完成了什么功能?sum的终值是多少? 203 | ```cpp 204 | int sum = 0; 205 | for (int i = -100; i <= 100; ++i) 206 | sum += i; 207 | ``` 208 | 209 | 解: 210 | 211 | 从-100加到100,sum的终值是0。 212 | 213 | ## 练习1.13 214 | 使用for循环重做1.4.1节中的所有练习(练习1.9到1.11)。 215 | 216 | 解: 217 | 218 | ### 练习1.9 219 | 220 | ```cpp 221 | #include 222 | 223 | int main() 224 | { 225 | int sum = 0; 226 | for (int val = 50; val <= 100; ++val){ 227 | sum += val; 228 | } 229 | std::cout << "Sum of 50 to 100 inclusive is " 230 | << sum << std::endl; 231 | } 232 | ``` 233 | 234 | ### 练习1.10 235 | 236 | ```cpp 237 | #include 238 | 239 | int main() 240 | { 241 | for (int val = 10; val >=0; --val){ 242 | std::cout << val << " "; 243 | } 244 | std::cout << std::endl; 245 | } 246 | ``` 247 | 248 | ### 练习1.11 249 | 250 | ```cpp 251 | #include 252 | 253 | int main() 254 | { 255 | int start = 0, end = 0; 256 | std::cout << "Please input two num: "; 257 | std::cin >> start >> end; 258 | if (start <= end) { 259 | for (; start <= end; ++start){ 260 | std::cout << start << " "; 261 | } 262 | std::cout << std::endl; 263 | } 264 | else{ 265 | std::cout << "start should be smaller than end !!!"; 266 | } 267 | } 268 | ``` 269 | 270 | ## 练习1.14 271 | 对比for循环和while循环,两种形式的优缺点各是什么? 272 | 273 | 解: 274 | ``` 275 | The main difference between the `for`'s and the `while`'s is a matter of pragmatics: 276 | we usually use `for` when there is a known number of iterations, 277 | and use `while` constructs when the number of iterations in not known in advance. 278 | The `while` vs `do ... while` issue is also of pragmatics, 279 | the second executes the instructions once at start, 280 | and afterwards it behaves just like the simple `while`. 281 | ``` 282 | 283 | ## 练习1.15 284 | 编写程序,包含第14页“再探编译”中讨论的常见错误。熟悉编译器生成的错误信息。 285 | 286 | 解: 287 | 288 | 编译器可以检查出的错误有: 289 | - 语法错误 290 | - 类型错误 291 | - 声明错误 292 | 293 | ## 练习1.16 294 | 编写程序,从cin读取一组数,输出其和。 295 | 296 | 解: 297 | 298 | ```cpp 299 | #include 300 | 301 | int main() 302 | { 303 | int sum = 0; 304 | for (int value = 0; std::cin >> value; ) 305 | sum += value; 306 | std::cout << sum << std::endl; 307 | return 0; 308 | } 309 | ``` 310 | 311 | ## 练习1.17 312 | 如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的? 313 | 314 | ## 练习1.18 315 | 编译并运行本节的程序,给它输入全都相等的值。再次运行程序,输入没有重复的值。 316 | 317 | 解: 318 | 319 | 全部重复: 320 | ``` 321 | 1 1 1 1 1 322 | 1 occurs 5 times 323 | ``` 324 | 325 | 没有重复: 326 | ``` 327 | 1 2 3 4 5 328 | 1 occurs 1 times 329 | 2 occurs 1 times 330 | 3 occurs 1 times 331 | 4 occurs 1 times 332 | 5 occurs 1 times 333 | ``` 334 | 335 | ## 练习1.19 336 | 修改你为1.4.1节练习1.11(第11页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况。 337 | 338 | 解: 339 | 340 | ```cpp 341 | #include 342 | 343 | int main() 344 | { 345 | int start = 0, end = 0; 346 | std::cout << "Please input two num: "; 347 | std::cin >> start >> end; 348 | if (start <= end) { 349 | while (start <= end){ 350 | std::cout << start << " "; 351 | ++start; 352 | } 353 | std::cout << std::endl; 354 | } 355 | else{ 356 | std::cout << "start should be smaller than end !!!"; 357 | } 358 | } 359 | ``` 360 | 361 | ## 练习1.20 362 | 在网站http://www.informit.com/title/032174113 上,第1章的代码目录包含了头文件 Sales_item.h。将它拷贝到你自己的工作目录中。用它编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上。 363 | 364 | 解: 365 | 366 | ```cpp 367 | #include 368 | #include "Sales_item.h" 369 | 370 | int main() 371 | { 372 | for (Sales_item item; std::cin >> item; std::cout << item << std::endl); 373 | return 0; 374 | } 375 | ``` 376 | 377 | 命令: 378 | ``` 379 | ./main < data/add_item 380 | ``` 381 | 382 | 输出: 383 | ``` 384 | 0-201-78345-X 3 60 20 385 | 0-201-78345-X 2 50 25 386 | ``` 387 | 388 | ## 练习1.21 389 | 编写程序,读取两个 ISBN 相同的 Sales_item 对象,输出他们的和。 390 | 391 | 解: 392 | 393 | ```cpp 394 | #include 395 | #include "Sales_item.h" 396 | 397 | int main() 398 | { 399 | Sales_item item_1; 400 | Sales_item item_2; 401 | std::cin >> item_1; 402 | std::cout << item_1 << std::endl; 403 | std::cin >> item_2; 404 | std::cout << item_2 << std::endl; 405 | std::cout << "sum of sale items: " << item_1 + item_2 << std::endl; 406 | return 0; 407 | } 408 | ``` 409 | 410 | 命令: 411 | ``` 412 | ./main < data/add_item 413 | ``` 414 | 415 | 输出: 416 | ``` 417 | 0-201-78345-X 3 60 20 418 | 0-201-78345-X 2 50 25 419 | sum of sale items: 0-201-78345-X 5 110 22 420 | ``` 421 | 422 | ## 练习1.22 423 | 编写程序,读取多个具有相同 ISBN 的销售记录,输出所有记录的和。 424 | 425 | 解: 426 | 427 | ```cpp 428 | #include 429 | #include "Sales_item.h" 430 | 431 | int main() 432 | { 433 | Sales_item sum_item; 434 | std::cin >> sum_item; 435 | std::cout << sum_item << std::endl; 436 | for (Sales_item item; std::cin >> item; std::cout << item << std::endl){ 437 | sum_item += item; 438 | } 439 | std::cout << "sum of sale items: " << sum_item << std::endl; 440 | return 0; 441 | } 442 | ``` 443 | 444 | 命令: 445 | ``` 446 | ./main < data/add_item 447 | ``` 448 | 449 | 输出: 450 | ``` 451 | 0-201-78345-X 3 60 20 452 | 0-201-78345-X 2 50 25 453 | sum of sale items: 0-201-78345-X 5 110 22 454 | ``` 455 | 456 | ## 练习1.23 457 | 编写程序,读取多条销售记录,并统计每个 ISBN(每本书)有几条销售记录。 458 | 459 | ## 练习1.24 460 | 输入表示多个 ISBN 的多条销售记录来测试上一个程序,每个 ISBN 的记录应该聚在一起。 461 | 462 | 解: 463 | 464 | ```cpp 465 | #include 466 | #include "Sales_item.h" 467 | 468 | int main() 469 | { 470 | Sales_item total; 471 | if (std::cin >> total){ 472 | Sales_item trans; 473 | while (std::cin >> trans){ 474 | if (total.isbn() == trans.isbn()) { 475 | total += trans; 476 | } 477 | else { 478 | std::cout << total << std::endl; 479 | total = trans; 480 | } 481 | } 482 | std::cout << total << std::endl; 483 | } 484 | else { 485 | std::cerr << "No data?!" << std::endl; 486 | return -1; 487 | } 488 | return 0; 489 | } 490 | ``` 491 | 492 | 命令: 493 | ``` 494 | ./main < data/book_sales 495 | ``` 496 | 497 | 输出: 498 | ``` 499 | 0-201-70353-X 4 99.96 24.99 500 | 0-201-82470-1 4 181.56 45.39 501 | 0-201-88954-4 16 198 12.375 502 | 0-399-82477-1 5 226.95 45.39 503 | 0-201-78345-X 5 110 22 504 | ``` 505 | ## 练习1.25 506 | 借助网站上的`Sales_item.h`头文件,编译并运行本节给出的书店程序。 507 | -------------------------------------------------------------------------------- /excersize/ch08.md: -------------------------------------------------------------------------------- 1 | # 第八章 IO库 2 | 3 | ## 练习8.1 4 | > 编写函数,接受一个`istream&`参数,返回值类型也是`istream&`。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。 5 | 6 | 解: 7 | 8 | ```cpp 9 | std::istream& func(std::istream &is) 10 | { 11 | std::string buf; 12 | while (is >> buf) 13 | std::cout << buf << std::endl; 14 | is.clear(); 15 | return is; 16 | } 17 | ``` 18 | 19 | ## 练习8.2 20 | > 测试函数,调用参数为`cin`。 21 | 22 | 解: 23 | 24 | ```cpp 25 | #include 26 | using std::istream; 27 | 28 | istream& func(istream &is) 29 | { 30 | std::string buf; 31 | while (is >> buf) 32 | std::cout << buf << std::endl; 33 | is.clear(); 34 | return is; 35 | } 36 | 37 | int main() 38 | { 39 | istream& is = func(std::cin); 40 | std::cout << is.rdstate() << std::endl; 41 | return 0; 42 | } 43 | ``` 44 | 45 | ## 练习8.3 46 | > 什么情况下,下面的`while`循环会终止? 47 | 48 | ```cpp 49 | while (cin >> i) /* ... */ 50 | ``` 51 | 52 | 解: 53 | 54 | 如`badbit`、`failbit`、`eofbit` 的任一个被置位,那么检测流状态的条件会失败。 55 | 56 | ## 练习8.4 57 | > 编写函数,以读模式打开一个文件,将其内容读入到一个`string`的`vector`中,将每一行作为一个独立的元素存于`vector`中。 58 | 59 | 解: 60 | 61 | ```cpp 62 | void ReadFileToVec(const string& fileName, vector& vec) 63 | { 64 | ifstream ifs(fileName); 65 | if (ifs) 66 | { 67 | string buf; 68 | while (getline(ifs, buf)) 69 | vec.push_back(buf); 70 | } 71 | } 72 | ``` 73 | 74 | ## 练习8.5 75 | > 重写上面的程序,将每个单词作为一个独立的元素进行存储。 76 | 解: 77 | 78 | ```cpp 79 | void ReadFileToVec(const string& fileName, vector& vec) 80 | { 81 | ifstream ifs(fileName); 82 | if (ifs) 83 | { 84 | string buf; 85 | while (ifs >> buf) 86 | vec.push_back(buf); 87 | } 88 | } 89 | ``` 90 | 91 | ## 练习8.6 92 | > 重写7.1.1节的书店程序,从一个文件中读取交易记录。将文件名作为一个参数传递给`main`。 93 | 94 | 解: 95 | 96 | ```cpp 97 | #include 98 | #include 99 | 100 | #include "../ch07/ex7_26.h" 101 | using std::ifstream; using std::cout; using std::endl; using std::cerr; 102 | 103 | int main(int argc, char **argv) 104 | { 105 | ifstream input(argv[1]); 106 | 107 | Sales_data total; 108 | if (read(input, total)) 109 | { 110 | Sales_data trans; 111 | while (read(input, trans)) 112 | { 113 | if (total.isbn() == trans.isbn()) 114 | total.combine(trans); 115 | else 116 | { 117 | print(cout, total) << endl; 118 | total = trans; 119 | } 120 | } 121 | print(cout, total) << endl; 122 | } 123 | else 124 | { 125 | cerr << "No data?!" << endl; 126 | } 127 | 128 | return 0; 129 | } 130 | ``` 131 | 132 | ## 练习8.7 133 | > 修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给`main`函数。 134 | 135 | 解: 136 | 137 | ```cpp 138 | #include 139 | #include 140 | 141 | #include "../ch07/ex7_26.h" 142 | using std::ifstream; using std::ofstream; using std::endl; using std::cerr; 143 | 144 | int main(int argc, char **argv) 145 | { 146 | ifstream input(argv[1]); 147 | ofstream output(argv[2]); 148 | 149 | Sales_data total; 150 | if (read(input, total)) 151 | { 152 | Sales_data trans; 153 | while (read(input, trans)) 154 | { 155 | if (total.isbn() == trans.isbn()) 156 | total.combine(trans); 157 | else 158 | { 159 | print(output, total) << endl; 160 | total = trans; 161 | } 162 | } 163 | print(output, total) << endl; 164 | } 165 | else 166 | { 167 | cerr << "No data?!" << endl; 168 | } 169 | 170 | return 0; 171 | } 172 | ``` 173 | 174 | ## 练习8.8 175 | > 修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。 176 | 177 | 解: 178 | 179 | ```cpp 180 | #include 181 | #include 182 | 183 | #include "../ch07/ex7_26.h" 184 | using std::ifstream; using std::ofstream; using std::endl; using std::cerr; 185 | 186 | int main(int argc, char **argv) 187 | { 188 | ifstream input(argv[1]); 189 | ofstream output(argv[2], ofstream::app); 190 | 191 | Sales_data total; 192 | if (read(input, total)) 193 | { 194 | Sales_data trans; 195 | while (read(input, trans)) 196 | { 197 | if (total.isbn() == trans.isbn()) 198 | total.combine(trans); 199 | else 200 | { 201 | print(output, total) << endl; 202 | total = trans; 203 | } 204 | } 205 | print(output, total) << endl; 206 | } 207 | else 208 | { 209 | cerr << "No data?!" << endl; 210 | } 211 | 212 | return 0; 213 | } 214 | ``` 215 | 216 | 217 | ## 练习8.9 218 | > 使用你为8.1.2节第一个练习所编写的函数打印一个`istringstream`对象的内容。 219 | 220 | 解: 221 | 222 | ```cpp 223 | #include 224 | #include 225 | using std::istream; 226 | 227 | istream& func(istream &is) 228 | { 229 | std::string buf; 230 | while (is >> buf) 231 | std::cout << buf << std::endl; 232 | is.clear(); 233 | return is; 234 | } 235 | 236 | int main() 237 | { 238 | std::istringstream iss("hello"); 239 | func(iss); 240 | return 0; 241 | } 242 | 243 | ``` 244 | 245 | ## 练习8.10 246 | > 编写程序,将来自一个文件中的行保存在一个`vector`中。然后使用一个`istringstream`从`vector`读取数据元素,每次读取一个单词。 247 | 248 | 解: 249 | 250 | ```cpp 251 | #include 252 | #include 253 | #include 254 | #include 255 | #include 256 | 257 | using std::vector; using std::string; using std::ifstream; using std::istringstream; using std::cout; using std::endl; using std::cerr; 258 | 259 | int main() 260 | { 261 | ifstream ifs("../data/book.txt"); 262 | if (!ifs) 263 | { 264 | cerr << "No data?" << endl; 265 | return -1; 266 | } 267 | 268 | vector vecLine; 269 | string line; 270 | while (getline(ifs, line)) 271 | vecLine.push_back(line); 272 | 273 | for (auto &s : vecLine) 274 | { 275 | istringstream iss(s); 276 | string word; 277 | while (iss >> word) 278 | cout << word << endl; 279 | } 280 | 281 | return 0; 282 | } 283 | ``` 284 | 285 | ## 练习8.11 286 | > 本节的程序在外层`while`循环中定义了`istringstream`对象。如果`record`对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将`record`的定义移到`while`循环之外,验证你设想的修改方法是否正确。 287 | 288 | 解: 289 | 290 | ```cpp 291 | #include 292 | #include 293 | #include 294 | #include 295 | using std::vector; using std::string; using std::cin; using std::istringstream; 296 | 297 | struct PersonInfo { 298 | string name; 299 | vector phones; 300 | }; 301 | 302 | int main() 303 | { 304 | string line, word; 305 | vector people; 306 | istringstream record; 307 | while (getline(cin, line)) 308 | { 309 | PersonInfo info; 310 | record.clear(); 311 | record.str(line); 312 | record >> info.name; 313 | while (record >> word) 314 | info.phones.push_back(word); 315 | people.push_back(info); 316 | } 317 | 318 | for (auto &p : people) 319 | { 320 | std::cout << p.name << " "; 321 | for (auto &s : p.phones) 322 | std::cout << s << " "; 323 | std::cout << std::endl; 324 | } 325 | 326 | return 0; 327 | } 328 | ``` 329 | 330 | ## 练习8.12 331 | > 我们为什么没有在`PersonInfo`中使用类内初始化? 332 | 333 | 解: 334 | 335 | 因为这里只需要聚合类就够了,所以没有必要在`PersionInfo`中使用类内初始化。 336 | 337 | ## 练习8.13 338 | > 重写本节的电话号码程序,从一个命名文件而非`cin`读取数据。 339 | 340 | 解: 341 | 342 | ```cpp 343 | #include 344 | #include 345 | #include 346 | #include 347 | #include 348 | 349 | using std::vector; using std::string; using std::cin; using std::istringstream; 350 | using std::ostringstream; using std::ifstream; using std::cerr; using std::cout; using std::endl; 351 | using std::isdigit; 352 | 353 | struct PersonInfo { 354 | string name; 355 | vector phones; 356 | }; 357 | 358 | bool valid(const string& str) 359 | { 360 | return isdigit(str[0]); 361 | } 362 | 363 | string format(const string& str) 364 | { 365 | return str.substr(0,3) + "-" + str.substr(3,3) + "-" + str.substr(6); 366 | } 367 | 368 | int main() 369 | { 370 | ifstream ifs("../data/phonenumbers.txt"); 371 | if (!ifs) 372 | { 373 | cerr << "no phone numbers?" << endl; 374 | return -1; 375 | } 376 | 377 | string line, word; 378 | vector people; 379 | istringstream record; 380 | while (getline(ifs, line)) 381 | { 382 | PersonInfo info; 383 | record.clear(); 384 | record.str(line); 385 | record >> info.name; 386 | while (record >> word) 387 | info.phones.push_back(word); 388 | people.push_back(info); 389 | } 390 | 391 | for (const auto &entry : people) 392 | { 393 | ostringstream formatted, badNums; 394 | for (const auto &nums : entry.phones) 395 | if (!valid(nums)) badNums << " " << nums; 396 | else formatted << " " << format(nums); 397 | if (badNums.str().empty()) 398 | cout << entry.name << " " << formatted.str() << endl; 399 | else 400 | cerr << "input error: " << entry.name 401 | << " invalid number(s) " << badNums.str() << endl; 402 | } 403 | 404 | return 0; 405 | } 406 | ``` 407 | 408 | ## 练习8.14 409 | > 我们为什么将`entry`和`nums`定义为`const auto&`? 410 | 411 | 解: 412 | 413 | 它们都是类类型,因此使用引用避免拷贝。 414 | 在循环当中不会改变它们的值,因此用`const`。 -------------------------------------------------------------------------------- /excersize/ch18.md: -------------------------------------------------------------------------------- 1 | ## 练习18.1 2 | 3 | > 在下列 `throw` 语句中异常对象的类型是什么? 4 | ```cpp 5 | (a) range_error r("error"); 6 | throw r; 7 | (b) exception *p = &r; 8 | throw *p; 9 | ``` 10 | 11 | 解: 12 | 13 | - (a): `range_error` 14 | - (b): `exception` 15 | 16 | 17 | ## 练习18.2 18 | 19 | > 当在指定的位置发生了异常时将出现什么情况? 20 | ```cpp 21 | void exercise(int *b, int *e) 22 | { 23 | vector v(b, e); 24 | int *p = new int[v.size()]; 25 | ifstream in("ints"); 26 | //此处发生异常 27 | } 28 | ``` 29 | 30 | 解: 31 | 32 | 指针`p`指向的内容不会被释放,将造成内存泄漏。 33 | 34 | ## 练习18.3 35 | 36 | > 要想让上面的代码在发生异常时能正常工作,有两种解决方案。请描述这两种方法并实现它们。 37 | 38 | 解: 39 | 40 | 方法一:不使用指针,使用对象: 41 | 42 | ```cpp 43 | struct intArray 44 | { 45 | intArray() : p(nullptr) { } 46 | explicit intArray(std::size_t s): 47 | p(new int[s]) { } 48 | 49 | 50 | ~intArray() 51 | { 52 | delete[] p; 53 | } 54 | 55 | // data meber 56 | int *p; 57 | }; 58 | 59 | intArray p(v.size()); 60 | ``` 61 | 62 | 方法二:使用智能指针: 63 | 64 | ```cpp 65 | std::shared_ptr p(new int[v.size()], [](int *p) { delete[] p; }); 66 | ``` 67 | 68 | ## 练习18.4 69 | 70 | > 查看图18.1所示的继承体系,说明下面的 `try` 块有何错误并修改它。 71 | ```cpp 72 | try { 73 | // 使用 C++ 标准库 74 | } catch (exception) { 75 | // ... 76 | } catch (const runtime_error &re) { 77 | // ... 78 | } catch (overflow_error eobj) { /* ... */ } 79 | ``` 80 | 81 | 解: 82 | 83 | 细化的异常类型应该写在前面: 84 | 85 | ```cpp 86 | try { 87 | // 使用 C++ 标准库 88 | } catch (overflow_error eobj) { 89 | // ... 90 | } catch (const runtime_error &re) { 91 | // ... 92 | } catch (exception) { /* ... */ } 93 | ``` 94 | 95 | ## 练习18.5 96 | 97 | > 修改下面的`main`函数,使其能捕获图18.1所示的任何异常类型: 98 | ```cpp 99 | int main(){ 100 | // 使用 C++标准库 101 | } 102 | ``` 103 | 处理代码应该首先打印异常相关的错误信息,然后调用 `abort` 终止函数。 104 | 105 | 解: 106 | 107 | 略 108 | 109 | ## 练习18.6 110 | 111 | > 已知下面的异常类型和 `catch` 语句,书写一个 `throw` 表达式使其创建的异常对象能被这些 `catch` 语句捕获: 112 | ```cpp 113 | (a) class exceptionType { }; 114 | catch(exceptionType *pet) { } 115 | (b) catch(...) { } 116 | (c) typedef int EXCPTYPE; 117 | catch(EXCPTYPE) { } 118 | ``` 119 | 120 | 解: 121 | 122 | ```cpp 123 | (a): throw exceptionType(); 124 | (b): throw expection(); 125 | (c): EXCPTYPE e = 1; throw e; 126 | ``` 127 | 128 | ## 练习18.7 129 | 130 | > 根据第16章的介绍定义你自己的 `Blob` 和 `BlobPtr`,注意将构造函数写成函数`try`语句块。 131 | 132 | 解: 133 | 134 | 略 135 | 136 | ## 练习18.8 137 | 138 | > 回顾你之前编写的各个类,为它们的构造函数和析构函数添加正确的异常说明。如果你认为某个析构函数可能抛出异常,尝试修改代码使得该析构函数不会抛出异常。 139 | 140 | 解: 141 | 142 | 略 143 | 144 | ## 练习18.9 145 | 146 | > 定义本节描述的书店程序异常类,然后为 `Sales_data` 类重新编写一个复合赋值运算符并令其抛出一个异常。 147 | 148 | ## 练习18.10 149 | 150 | > 编写程序令其对两个 `ISBN` 编号不相同的对象执行 `Sales_data` 的加法运算。为该程序编写两个不同的版本:一个处理异常,另一个不处理异常。观察并比较这两个程序的行为,用心体会当出现了一个未被捕获的异常时程序会发生什么情况。 151 | 152 | 解: 153 | 154 | 略 155 | 156 | ## 练习18.11 157 | 158 | > 为什么 `what` 函数不应该抛出异常? 159 | 160 | 解: 161 | 162 | 略 163 | 164 | ## 练习18.12 165 | 166 | > 将你为之前各章练习编写的程序放置在各自的命名空间中。也就是说,命名空间chapter15包含`Query`程序的代码,命名空间chapter10包含`TextQuery`的代码;使用这种结构重新编译`Query`代码实例。 167 | 168 | 解: 169 | 170 | 略 171 | 172 | ## 练习18.13 173 | 174 | > 什么时候应该使用未命名的命名空间? 175 | 176 | 解: 177 | 178 | 需要定义一系列静态的变量的时候。 179 | 180 | 参考:https://stackoverflow.com/questions/154469/unnamed-anonymous-namespaces-vs-static-functions 181 | 182 | ## 练习18.14 183 | 184 | > 假设下面的 `operator*` 声明的是嵌套的命名空间 `mathLib::MatrixLib` 的一个成员: 185 | ```cpp 186 | namespace mathLib { 187 | namespace MatrixLib { 188 | class matrix { /* ... */ }; 189 | matrix operator* (const matrix &, const matrix &); 190 | // ... 191 | } 192 | } 193 | ``` 194 | 请问你应该如何在全局作用域中声明该运算符? 195 | 196 | 解: 197 | 198 | ```cpp 199 | mathLib::MatrixLib::matrix mathLib::MatrixLib::operator* (const mathLib::MatrixLib::matrix &, const mathLib::MatrixLib::matrix &); 200 | ``` 201 | 202 | ## 练习18.15 203 | 204 | > 说明 `using` 指示与 `using` 声明的区别。 205 | 206 | 解: 207 | 208 | - 一条`using`声明语句一次只引入命名空间的一个成员。 209 | - `using` 指示使得某个特定的命名空间中所有的名字都可见。 210 | 211 | 有点像python中的`import`: 212 | 213 | ```python 214 | from lib import func 215 | from lib import * 216 | ``` 217 | 218 | ## 练习18.16 219 | 220 | > 假定在下面的代码中标记为“位置1”的地方是对命名空间 Exercise 中所有成员的`using`声明,请解释代码的含义。如果这些`using`声明出现在“位置2”又会怎样呢?将`using`声明变为`using`指示,重新回答之前的问题。 221 | ```cpp 222 | namespace Exercise { 223 | int ivar = 0; 224 | double dvar = 0; 225 | const int limit = 1000; 226 | } 227 | int ivar = 0; 228 | //位置1 229 | void main() { 230 | //位置2 231 | double dvar = 3.1416; 232 | int iobj = limit + 1; 233 | ++ivar; 234 | ++::ivar; 235 | } 236 | ``` 237 | 238 | 解: 239 | 240 | 略 241 | 242 | ## 练习18.17 243 | 244 | > 实际编写代码检验你对上一题的回答是否正确。 245 | 246 | 解: 247 | 248 | 略 249 | 250 | ## 练习18.18 251 | 252 | > 已知有下面的 `swap` 的典型定义,当 `mem1` 是一个 `string` 时程序使用 `swap` 的哪个版本?如果 `mem1` 是 `int` 呢?说明在这两种情况下名字查找的过程。 253 | ```cpp 254 | void swap(T v1, T v2) 255 | { 256 | using std::swap; 257 | swap(v1.mem1, v2.mem1); 258 | //交换类型的其他成员 259 | } 260 | ``` 261 | 262 | 解: 263 | 264 | `std::swap`是一个模板函数,如果是`string`会找到`string`版本;反之如果是`int`会找到`int`版本。 265 | 266 | ## 练习18.19 267 | 268 | > 如果对 `swap` 的调用形如 `std::swap(v1.mem1, v2.mem1)` 将会发生什么情况? 269 | 270 | 解: 271 | 272 | 会直接调用`std`版的`swap`,但对后面的调用无影响。 273 | 274 | ## 练习18.20 275 | 276 | > 在下面的代码中,确定哪个函数与`compute`调用匹配。列出所有候选函数和可行函数,对于每个可行函数的实参与形参的匹配过程来说,发生了哪种类型转换? 277 | ```cpp 278 | namespace primerLib { 279 | void compute(); 280 | void compute(const void *); 281 | } 282 | using primerLib::compute; 283 | void compute(int); 284 | void compute(double, double = 3.4); 285 | void compute(char*, char* = 0); 286 | void f() 287 | { 288 | compute(0); 289 | } 290 | ``` 291 | 292 | 解: 293 | 294 | 略 295 | 296 | ## 练习18.21 297 | 298 | > 解释下列声明的含义,在它们当作存在错误吗?如果有,请指出来并说明错误的原因。 299 | 300 | ```cpp 301 | (a) class CADVehicle : public CAD, Vehicle { ... }; 302 | (b) class DbiList : public List, public List { ... }; 303 | (c) class iostream : public istream, public ostream { ... }; 304 | ``` 305 | 306 | ## 练习18.22 307 | 308 | > 已知存在如下所示的类的继承体系,其中每个类都定义了一个默认构造函数: 309 | 310 | ```cpp 311 | class A { ... }; 312 | class B : public A { ... }; 313 | class C : public B { ... }; 314 | class X { ... }; 315 | class Y { ... }; 316 | class Z : public X, public Y { ... }; 317 | class MI : public C, public Z { ... }; 318 | ``` 319 | 对于下面的定义来说,构造函数的执行顺序是怎样的? 320 | ```cpp 321 | MI mi; 322 | ``` 323 | 324 | ## 练习18.23 325 | 326 | > 使用练习18.22的继承体系以及下面定义的类 `D`,同时假定每个类都定义了默认构造函数,请问下面的哪些类型转换是不被允许的? 327 | ```cpp 328 | class D : public X, public C { ... }; 329 | p *pd = new D; 330 | (a) X *px = pd; 331 | (b) A *pa = pd; 332 | (c) B *pb = pd; 333 | (d) C *pc = pd; 334 | ``` 335 | 336 | ## 练习18.24 337 | 338 | > 在第714页,我们使用一个指向 `Panda` 对象的 `Bear` 指针进行了一系列调用,假设我们使用的是一个指向 `Panda` 对象的 `ZooAnimal` 指针将会发生什么情况,请对这些调用语句逐一进行说明。 339 | 340 | ## 练习18.25 341 | 342 | > 假设我们有两个基类 `Base1` 和 `Base2` ,它们各自定义了一个名为 `print` 的虚成员和一个虚析构函数。从这两个基类中文名派生出下面的类,它们都重新定义了 `print` 函数: 343 | ```cpp 344 | class D1 : public Base1 { /* ... */}; 345 | class D2 : public Base2 { /* ... */}; 346 | class MI : public D1, public D2 { /* ... */}; 347 | ``` 348 | 通过下面的指针,指出在每个调用中分别使用了哪个函数: 349 | ```cpp 350 | Base1 *pb1 = new MI; 351 | Base2 *pb2 = new MI; 352 | D1 *pd1 = new MI; 353 | D2 *pd2 = new MI; 354 | (a) pb1->print(); 355 | (b) pd1->print(); 356 | (c) pd2->print(); 357 | (d) delete pb2; 358 | (e) delete pd1; 359 | (f) delete pd2; 360 | ``` 361 | 362 | ```cpp 363 | struct Base1 { 364 | void print(int) const; 365 | protected: 366 | int ival; 367 | double dval; 368 | char cval; 369 | private: 370 | int *id; 371 | }; 372 | struct Base2 { 373 | void print(double) const; 374 | protected: 375 | double fval; 376 | private: 377 | double dval; 378 | }; 379 | struct Derived : public Base1 { 380 | void print(std::string) const; 381 | protected: 382 | std::string sval; 383 | double dval; 384 | }; 385 | struct MI : public Derived, public Base2 { 386 | void print(std::vector); 387 | protected: 388 | int *ival; 389 | std::vector dvec; 390 | }; 391 | ``` 392 | 393 | ## 练习18.26 394 | 395 | > 已知如上所示的继承体系,下面对`print`的调用为什么是错误的?适当修改`MI`,令其对`print`的调用可以编译通过并正确执行。 396 | ```cpp 397 | MI mi; 398 | mi.print(42); 399 | ``` 400 | 401 | ## 练习18.27 402 | 403 | > 已知如上所示的继承体系,同时假定为MI添加了一个名为`foo`的函数: 404 | ```cpp 405 | int ival; 406 | double dval; 407 | void MI::foo(double cval) 408 | { 409 | int dval; 410 | //练习中的问题发生在此处 411 | } 412 | (a) 列出在MI::foo中可见的所有名字。 413 | (b) 是否存在某个可见的名字是继承自多个基类的? 414 | (c) 将Base1的dval成员与Derived 的dval 成员求和后赋给dval的局部实例。 415 | (d) 将MI::dvec的最后一个元素的值赋给Base2::fval。 416 | (e) 将从Base1继承的cval赋给从Derived继承的sval的第一个字符。 417 | ``` 418 | 419 | ## 练习18.28 420 | 421 | > 已知存在如下的继承体系,在 `VMI` 类的内部哪些继承而来的成员无须前缀限定符就能直接访问?哪些必须有限定符才能访问?说明你的原因。 422 | ```cpp 423 | struct Base { 424 | void bar(int); 425 | protected: 426 | int ival; 427 | }; 428 | struct Derived1 : virtual public Base { 429 | void bar(char); 430 | void foo(char); 431 | protected: 432 | char cval; 433 | }; 434 | struct Derived2 : virtual public Base { 435 | void foo(int); 436 | protected: 437 | int ival; 438 | char cval; 439 | }; 440 | class VMI : public Derived1, public Derived2 { }; 441 | ``` 442 | 443 | ## 练习18.29 444 | 445 | > 已知有如下所示的类继承关系: 446 | ```cpp 447 | class Class { ... }; 448 | class Base : public Class { ... }; 449 | class D1 : virtual public Base { ... }; 450 | class D2 : virtual public Base { ... }; 451 | class MI : public D1, public D2 { ... }; 452 | class Final : public MI, public Class { ... }; 453 | (a) 当作用于一个Final对象时,构造函数和析构函数的执行次序分别是什么? 454 | (b) 在一个Final对象中有几个Base部分?几个Class部分? 455 | (c) 下面的哪些赋值运算符将造成编译错误? 456 | Base *pb; Class *pc; MI *pmi; D2 *pd2; 457 | (a) pb = new Class; 458 | (b) pc = new Final; 459 | (c) pmi = pb; 460 | (d) pd2 = pmi; 461 | ``` 462 | 463 | ## 练习18.30 464 | 465 | > 在`Base`中定义一个默认构造函数、一个拷贝构造函数和一个接受`int`形参的构造函数。在每个派生类中分别定义这三种构造函数,每个构造函数应该使用它的形参初始化其`Base`部分。 -------------------------------------------------------------------------------- /excersize/ch19.md: -------------------------------------------------------------------------------- 1 | ## 练习19.1 2 | 3 | > 使用 malloc 编写你自己的 operator new(sizt_t)函数,使用 free 编写operator delete(void *)函数。 4 | 5 | ## 练习19.2 6 | 7 | > 默认情况下,allocator 类使用 operator new 获取存储空间,然后使用 operator delete 释放它。利用上一题中的两个函数重新编译并运行你的 StrVec 程序。 8 | 9 | ## 练习19.3 10 | 11 | > 已知存在如下的类继承体系,其中每个类分别定义了一个公有的默认构造函数和一个析构函数: 12 | ```cpp 13 | class A { /* ... */}; 14 | class B : public A { /* ... */}; 15 | class C : public B { /* ... */}; 16 | class D : public B, public A { /* ... */}; 17 | ``` 18 | 下面哪个 dynamic_cast 将失败? 19 | ```cpp 20 | (a) A *pa = new C; 21 | B *pb = dynamic_cast(pa); 22 | (b) B *pb = new B; 23 | C *pc = dynamic_cast(pb); 24 | (c) A *pa = new D; 25 | B *pb = dynamic_cast(pa); 26 | ``` 27 | 28 | ## 练习19.4 29 | 30 | > 使用上一个练习定义的类改写下面的代码,将表达式*pa 转换成类型C&: 31 | ```cpp 32 | if (C *pc = dynamic_cast(pa)) 33 | { 34 | //使用C的成员 35 | } else { 36 | //使用A的成员 37 | } 38 | ``` 39 | 40 | ## 练习19.5 41 | 42 | > 在什么情况下你应该用 dynamic_cast 替代虚函数? 43 | 44 | ## 练习19.6 45 | 46 | > 编写一条表达式将 Query_base 指针动态转换为 AndQuery 指针。分别使用 AndQuery 的对象以及其他类型的对象测试转换是否有效。打印一条表示类型转换是否成功的信息,确保实际输出的结果与期望的一致。 47 | 48 | ## 练习19.7 49 | 50 | > 编写与上一个练习类似的转换,这一次将 Query_base 对象转换为 AndQuery 的引用。重复上面的测试过程,确保转换能正常工作。 51 | 52 | ## 练习19.8 53 | 54 | > 编写一条 typeid 表达式检查两个 Query_base 对象是否指向同一种类型。再检查该类型是否是 AndQuery。 55 | 56 | ## 练习19.9 57 | 58 | > 编写与本节最后一个程序类似的代码,令其打印你的编译器为一些常见类型所起的名字。如果你得到的输出结果与本书类似,尝试编写一个函数将这些字符串翻译成人们更容易读懂的形式。 59 | 60 | ## 练习19.10 61 | 62 | > 已知存在如下的类继承体系,其中每个类定义了一个默认公有的构造函数和一个虚析构函数。下面的语句将打印哪些类型名字? 63 | ```cpp 64 | class A { /* ... */ }; 65 | class B : public A { /* ... */ }; 66 | class C : public B { /*...*/ }; 67 | (a) A *pa = new C; 68 | cout << typeid(pa).name() << endl; 69 | (b) C cobj; 70 | A& ra = cobj; 71 | cout << typeid(&ra).name() << endl; 72 | (c) B *px = new B; 73 | A& ra = *px; 74 | cout << typeid(ra).name() << endl; 75 | ``` 76 | 77 | ## 练习19.11 78 | 79 | > 普通的数据指针和指向数据成员的指针有何区别? 80 | 81 | ## 练习19.12 82 | 83 | > 定义一个成员指针,令其可以指向 Screen 类的 cursor 成员。通过该指针获得 Screen::cursor 的值。 84 | 85 | ## 练习19.13 86 | 87 | > 定义一个类型,使其可以表示指向 Sales_data 类的 bookNo 成员的指针。 88 | 89 | ## 练习19.14 90 | 91 | > 下面的代码合法吗?如果合法,代码的含义是什么?如果不合法,解释原因。 92 | ```cpp 93 | auto pmf = &Screen::get_cursor; 94 | pmf = &Screen::get; 95 | ``` 96 | 97 | ## 练习19.15 98 | 99 | > 普通函数指针和指向成员函数的指针有何区别? 100 | 101 | ## 练习19.16 102 | 103 | > 声明一个类型别名,令其作为指向 Sales_data 的 avg_price 成员的指针的同义词。 104 | 105 | ## 练习19.17 106 | 107 | > 为 Screen 的所有成员函数类型各定义一个类型别名。 108 | 109 | ## 练习19.18 110 | 111 | > 编写一个函数,使用 count_if 统计在给定的 vector 中有多少个空 string。 112 | 113 | ## 练习19.19 114 | 115 | > 编写一个函数,令其接受vector并查找平均价格高于某个值的第一个元素。 116 | 117 | ## 练习19.20 118 | 119 | > 将你的 QueryResult 类嵌套在 TextQuery 中,然后重新运行12.3.2节中使用了 TextQuery 的程序。 120 | 121 | ## 练习19.21 122 | 123 | > 编写你自己的 Token 类。 124 | 125 | ## 练习19.22 126 | 127 | > 为你的 Token 类添加一个 Sales_data 类型的成员。 128 | 129 | ## 练习19.23 130 | 131 | > 为你的 Token 类添加移动构造函数和移动赋值运算符。 132 | 133 | ## 练习19.24 134 | 135 | > 如果我们将一个 Token 对象付给它自己将发生什么情况? 136 | 137 | ## 练习19.25 138 | 139 | > 编写一系列赋值运算符,令其分别接收 union 中各种类型的值。 140 | 141 | ## 练习19.26 142 | 143 | > 说明下列声明语句的含义并判断它们是否合法: 144 | ```cpp 145 | extern "C" int compute(int *, int); 146 | extern "C" double compute(double *, double); 147 | ``` -------------------------------------------------------------------------------- /notes/ch01.md: -------------------------------------------------------------------------------- 1 | # 第一章 开始 2 | 3 | ## 熟悉编译器 4 | 5 | **g++**: 6 | 7 | - 编译:`g++ --std=c++11 ch01.cpp -o main` 8 | - 运行:`./prog1` 9 | - 查看运行状态:`echo $?` 10 | - 编译多个文件:`g++ ch2.cpp Sales_item.cc -o main` 11 | 12 | 输入 `g++ --help`,查看编译器选项: 13 | 14 | ``` 15 | Usage: g++ [options] file... 16 | Options: 17 | -pass-exit-codes Exit with highest error code from a phase 18 | --help Display this information 19 | --target-help Display target specific command line options 20 | --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...] 21 | Display specific types of command line options 22 | (Use '-v --help' to display command line options of sub-processes) 23 | --version Display compiler version information 24 | -dumpspecs Display all of the built in spec strings 25 | -dumpversion Display the version of the compiler 26 | -dumpmachine Display the compiler's target processor 27 | -print-search-dirs Display the directories in the compiler's search path 28 | -print-libgcc-file-name Display the name of the compiler's companion library 29 | -print-file-name= Display the full path to library 30 | -print-prog-name= Display the full path to compiler component 31 | -print-multiarch Display the target's normalized GNU triplet, used as 32 | a component in the library path 33 | -print-multi-directory Display the root directory for versions of libgcc 34 | -print-multi-lib Display the mapping between command line options and 35 | multiple library search directories 36 | -print-multi-os-directory Display the relative path to OS libraries 37 | -print-sysroot Display the target libraries directory 38 | -print-sysroot-headers-suffix Display the sysroot suffix used to find headers 39 | -Wa, Pass comma-separated on to the assembler 40 | -Wp, Pass comma-separated on to the preprocessor 41 | -Wl, Pass comma-separated on to the linker 42 | -Xassembler Pass on to the assembler 43 | -Xpreprocessor Pass on to the preprocessor 44 | -Xlinker Pass on to the linker 45 | -save-temps Do not delete intermediate files 46 | -save-temps= Do not delete intermediate files 47 | -no-canonical-prefixes Do not canonicalize paths when building relative 48 | prefixes to other gcc components 49 | -pipe Use pipes rather than intermediate files 50 | -time Time the execution of each subprocess 51 | -specs= Override built-in specs with the contents of 52 | -std= Assume that the input sources are for 53 | --sysroot= Use as the root directory for headers 54 | and libraries 55 | -B Add to the compiler's search paths 56 | -v Display the programs invoked by the compiler 57 | -### Like -v but options quoted and commands not executed 58 | -E Preprocess only; do not compile, assemble or link 59 | -S Compile only; do not assemble or link 60 | -c Compile and assemble, but do not link 61 | -o Place the output into 62 | -pie Create a position independent executable 63 | -shared Create a shared library 64 | -x Specify the language of the following input files 65 | Permissible languages include: c c++ assembler none 66 | 'none' means revert to the default behavior of 67 | guessing the language based on the file's extension 68 | 69 | ``` 70 | 71 | 输入 `g++ -v --help`可以看到更完整的指令。 72 | 例如还有些常用的: 73 | ``` 74 | -h FILENAME, -soname FILENAME: Set internal name of shared library 75 | -I PROGRAM, --dynamic-linker PROGRAM: Set PROGRAM as the dynamic linker to use 76 | -l LIBNAME, --library LIBNAME: Search for library LIBNAME 77 | -L DIRECTORY, --library-path DIRECTORY: Add DIRECTORY to library search path 78 | ``` 79 | 80 | **获得程序状态**: 81 | 82 | - windows: ``echo %ERRORLEVEL%`` 83 | - UNIX: ``echo $?`` 84 | 85 | ## IO 86 | 87 | - ```#include ``` 88 | - ```std::cout << "hello"``` 89 | - ```std::cin >> v1``` 90 | 91 | 记住`>>`和`<<`返回的结果都是左操作数,也就是输入流和输出流本身。 92 | 93 | **endl**:这是一个被称为**操纵符**(manipulator)的特殊值,效果是结束当前行,并将设备关联的缓冲区(buffer)中的内容刷到设备中。 94 | 95 | UNIX和Mac下键盘输入文件结束符:`ctrl+d`,Windows下:`ctrl+z` 96 | 97 | **头文件**:类的类型一般存储在头文件中,标准库的头文件使用`<>`,非标准库的头文件使用`""`。申明写在`.h`文件,定义实现写在`.cpp`文件。 98 | 99 | **避免多次包含同一头文件**: 100 | 101 | ```cpp 102 | #ifndef SALESITEM_H 103 | #define SALESITEM_H 104 | // Definition of Sales_itemclass and related functions goes here 105 | #endif 106 | ``` 107 | 108 | **成员函数(类方法)**:使用`.`调用。 109 | 110 | **命名空间(namespace)**:使用作用域运算符`::`调用。 111 | 112 | ## 注释 113 | 114 | - 单行注释: `//` 115 | - 多行注释: `/**/`。编译器将`/*`和`*/`之间的内容都作为注释内容忽略。注意不能嵌套。 116 | ```cpp 117 | #define SALESITEM_H 118 | /* 119 | * 多行注释格式 120 | * 每一行加一个* 121 | */ 122 | ``` 123 | 124 | ## while语句 125 | 126 | 循环执行,(直到条件(condition)为假。 127 | 128 | ## for语句 129 | 130 | 循环头由三部分组成: 131 | 132 | - 一个初始化语句(init-statement) 133 | - 一个循环条件(condition) 134 | - 一个表达式(expression) 135 | 136 | ## 使用文件重定向 137 | 138 | ``./main outfile`` 139 | 140 | -------------------------------------------------------------------------------- /notes/ch02.md: -------------------------------------------------------------------------------- 1 | # 第二章 变量和基本类型 2 | 3 | 任何常用的编程语言都具备一组公共的语法特征,最基本的特征包括: 4 | 5 | - 整型、字符型等内置类型 6 | - 变量,用来为对象命名 7 | - 表达式和语句,用于操作上述数据类型的具体值 8 | - if 或 while 等控制结构,有选择地执行一些语句或重复地执行一些语句 9 | - 函数,用于定义可供随时调用的计算单元 10 | 11 | 大多数编程语言通过两种方式来进一步补充其基本特征: 12 | 13 | - 自定义数据类型,实现对语言的扩展 14 | - 将一些有用的功能封装成库函数 15 | 16 | ### 基本内置类型 17 | 18 | **基本算数类型**: 19 | 20 | | 类型 | 含义 | 最小尺寸| 21 | |---|---|---| 22 | | `bool` | 布尔类型 | 8bits | 23 | | `char`| 字符 | 8bits | 24 | | `wchar_t` | 宽字符 | 16bits | 25 | | `char16_t` | Unicode字符 | 16bits | 26 | | `char32_t` | Unicode字符 | 32bits | 27 | | `short` | 短整型 | 16bits | 28 | | `int` | 整型 | 16bits (在32位机器中是32bits) | 29 | | `long` | 长整型 | 32bits | 30 | | `long long` | 长整型 | 64bits (是在C++11中新定义的) | 31 | | `float` | 单精度浮点数 | 6位有效数字 | 32 | | `double` | 双精度浮点数 | 10位有效数字 | 33 | | `long double` | 扩展精度浮点数 | 10位有效数字 | 34 | 35 | 36 | ### 如何选择类型 37 | 38 | - 1.当明确知晓数值不可能是负数时,选用无符号类型; 39 | - 2.使用`int`执行整数运算。一般`long`的大小和`int`一样,而`short`常常显得太小。除非超过了`int`的范围,选择`long long`。 40 | - 3.算术表达式中不要使用`char`或`bool`。 41 | - 4.浮点运算选用`double`。 42 | 43 | ### 类型转换 44 | 45 | - 非布尔型赋给布尔型,初始值为0则结果为false,否则为true。 46 | - 布尔型赋给非布尔型,初始值为false结果为0,初始值为true结果为1。 47 | 48 | ### 字面值常量 49 | 50 | - 一个形如`42`的值被称作**字面值常量**(literal)。 51 | - 整型和浮点型字面值。 52 | - 字符和字符串字面值。 53 | - 使用空格连接,继承自C。 54 | - 字符字面值:单引号, `'a'` 55 | - 字符串字面值:双引号, `"Hello World"` 56 | - 分多行书写字符串。 57 | ```c++ 58 | std:cout<<"wow, a really, really long string" 59 | "literal that spans two lines" < 字符串型实际上时常量字符构成的数组,结尾处以`'\0'`结束,所以字符串类型实际上长度比内容多1。 66 | 67 | ## 变量 68 | 69 | **变量**提供一个**具名**的、可供程序操作的存储空间。 `C++`中**变量**和**对象**一般可以互换使用。 70 | 71 | ### 变量定义(define) 72 | 73 | - **定义形式**:类型说明符(type specifier) + 一个或多个变量名组成的列表。如`int sum = 0, value, units_sold = 0;` 74 | - **初始化**(initialize):对象在创建时获得了一个特定的值。 75 | - **初始化不是赋值!**: 76 | - 初始化 = 创建变量 + 赋予初始值 77 | - 赋值 = 擦除对象的当前值 + 用新值代替 78 | - **列表初始化**:使用花括号`{}`,如`int units_sold{0};` 79 | - 默认初始化:定义时没有指定初始值会被默认初始化;**在函数体内部的内置类型变量将不会被初始化**。 80 | - 建议初始化每一个内置类型的变量。 81 | 82 | ### 变量的**声明**(declaration) vs **定义**(define) 83 | - 为了支持分离式编译,`C++`将声明和定义区分开。**声明**使得名字为程序所知。**定义**负责创建与名字关联的实体。 84 | - **extern**:只是说明变量定义在其他地方。 85 | - 只声明而不定义: 在变量名前添加关键字 `extern`,如`extern int i;`。但如果包含了初始值,就变成了定义:`extern double pi = 3.14;` 86 | - 变量只能被定义一次,但是可以多次声明。定义只出现在一个文件中,其他文件使用该变量时需要对其声明。 87 | - 名字的**作用域**(namescope)`{}` 88 | - **第一次使用变量时再定义它**。 89 | - 嵌套的作用域 90 | - 同时存在全局和局部变量时,已定义局部变量的作用域中可用`::reused`显式访问全局变量reused。 91 | - **但是用到全局变量时,尽量不适用重名的局部变量。** 92 | 93 | #### 变量命名规范 94 | 1. 需体现实际意义 95 | 2. 变量名用小写字母 96 | 3. 自定义类名用大写字母开头:Sales_item 97 | 4. 标识符由多个单词组成,中间须有明确区分:student_loan或studentLoan,不要用studentloan。 98 | 99 | ## 左值和右值 100 | 101 | - **左值**(l-value)**可以**出现在赋值语句的左边或者右边,比如变量; 102 | - **右值**(r-value)**只能**出现在赋值语句的右边,比如常量。 103 | 104 | 105 | ## 复合类型 106 | 107 | ### 引用 108 | 109 | > 一般说的引用是指的左值引用 110 | - **引用**:引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如`int &refVal = val;`。 111 | - 引用必须初始化。 112 | - 引用和它的初始值是**绑定bind**在一起的,而**不是拷贝**。一旦定义就不能更改绑定为其他的对象 113 | 114 | ### 指针 115 | 116 | > int *p; //**指向int型对象**的指针 117 | 118 | - 是一种 `"指向(point to)"`另外一种类型的复合类型。 119 | 120 | - **定义**指针类型: `int *ip1;`,**从右向左读有助于阅读**,`ip1`是指向`int`类型的指针。 121 | 122 | - 指针存放某个对象的**地址**。 123 | 124 | - 获取对象的地址: `int i=42; int *p = &i;`。 `&`是**取地址符**。 125 | 126 | - 指针的类型与所指向的对象类型必须一致(均为同一类型int、double等) 127 | 128 | - 指针的值的四种状态: 129 | - 1.指向一个对象; 130 | - 2.指向紧邻对象的下一个位置; 131 | - 3.空指针; 132 | - 4.无效指针。 133 | - >**对无效指针的操作均会引发错误,第二种和第三种虽为有效的,但理论上是不被允许的** 134 | 135 | - 指针访问对象: `cout << *p;`输出p指针所指对象的数据, `*`是**解引用符**。 136 | 137 | - 空指针不指向任何对象。使用`int *p=nullptr;`来使用空指针。 138 | 139 | - > 指针和引用的区别:引用本身并非一个对象,引用定义后就不能绑定到其他的对象了;指针并没有此限制,相当于变量一样使用。 140 | 141 | - > 赋值语句永远改变的是**左侧**的对象。 142 | 143 | - `void*`指针可以存放**任意**对象的地址。因无类型,仅操作内存空间,对所存对象无法访问。 144 | 145 | - 其他指针类型必须要与所指对象**严格匹配**。 146 | 147 | - 两个指针相减的类型是`ptrdiff_t`。 148 | 149 | - 建议:初始化所有指针。 150 | 151 | - `int* p1, p2;//*是对p1的修饰,所以p2还是int型` 152 | 153 | ## const限定符 154 | 155 | - 动机:希望定义一些不能被改变值的变量。 156 | 157 | ### 初始化和const 158 | - const对象**必须初始化**,且**不能被改变**。 159 | - const变量默认不能被其他文件访问,非要访问,必须在指定const定义之前加extern。要想在多个文件中使用const变量共享,定义和声明都加extern关键字即可。 160 | 161 | ### const的引用 162 | 163 | - **reference to const**(对常量的引用):指向const对象的引用,如 `const int ival=1; const int &refVal = ival;`,可以读取但不能修改`refVal`。 164 | - **临时量**(temporary)对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建的一个未命名的对象。 165 | - 对临时量的引用是非法行为。 166 | 167 | ### 指针和const 168 | 169 | - **pointer to const**(指向常量的指针):不能用于改变其所指对象的值, 如 `const double pi = 3.14; const double *cptr = π`。 170 | - **const pointer**:指针本身是常量,也就是说指针固定指向该对象,(存放在指针中的地址不变,地址所对应的那个对象值可以修改)如 `int i = 0; int *const ptr = &i;` 171 | 172 | ### 顶层const 173 | 174 | - `顶层const`:指针本身是个常量。 175 | - `底层const`:指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。 176 | 177 | ### `constexpr`和常量表达式(▲可选) 178 | 179 | - 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。 180 | - `C++11`新标准规定,允许将变量声明为`constexpr`类型以便由编译器来验证变量的值是否是一个常量的表达式。 181 | 182 | ## 处理类型 183 | 184 | ### 类型别名 185 | 186 | - 传统别名:使用**typedef**来定义类型的同义词。 `typedef double wages;` 187 | - 新标准别名:别名声明(alias declaration): `using SI = Sales_item;`(C++11) 188 | 189 | ```c++ 190 | // 对于复合类型(指针等)不能代回原式来进行理解 191 | typedef char *pstring; // pstring是char*的别名 192 | const pstring cstr = 0; // 指向char的常量指针 193 | // 如改写为const char *cstr = 0;不正确,为指向const char的指针 194 | 195 | // 辅助理解(可代回后加括号) 196 | // const pstring cstr = 0;代回后const (char *) cstr = 0; 197 | // const char *cstr = 0;即为(const char *) cstr = 0; 198 | ``` 199 | 200 | ### auto类型说明符 c++11 201 | 202 | - **auto**类型说明符:让编译器**自动推断类型**。 203 | - 一条声明语句只能有一个数据类型,所以一个auto声明多个变量时只能相同的变量类型(包括复杂类型&和*)。`auto sz = 0, pi =3.14//错误` 204 | - `int i = 0, &r = i; auto a = r;` 推断`a`的类型是`int`。 205 | - 会忽略`顶层const`。 206 | - `const int ci = 1; const auto f = ci;`推断类型是`int`,如果希望是顶层const需要自己加`const` 207 | 208 | ### decltype类型指示符 209 | 210 | - 从表达式的类型推断出要定义的变量的类型。 211 | - **decltype**:选择并返回操作数的**数据类型**。 212 | - `decltype(f()) sum = x;` 推断`sum`的类型是函数`f`的返回类型。 213 | - 不会忽略`顶层const`。 214 | - 如果对变量加括号,编译器会将其认为是一个表达式,如int i-->(i),则decltype((i))得到结果为int&引用。 215 | - 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。 216 | - `C++11` 217 | 218 | ## 自定义数据结构 219 | 220 | ### struct 221 | 222 | > 尽量不要吧类定义和对象定义放在一起。如`struct Student{} xiaoming,xiaofang;` 223 | - 类可以以关键字`struct`开始,紧跟类名和类体。 224 | - 类数据成员:类体定义类的成员。 225 | - `C++11`:可以为类数据成员提供一个**类内初始值**(in-class initializer)。 226 | 227 | ### 编写自己的头文件 228 | 229 | - 头文件通常包含哪些只能被定义一次的实体:类、`const`和`constexpr`变量。 230 | 231 | 预处理器概述: 232 | 233 | - **预处理器**(preprocessor):确保头文件多次包含仍能安全工作。 234 | - 当预处理器看到`#include`标记时,会用指定的头文件内容代替`#include` 235 | - **头文件保护符**(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。 236 | - `#indef`已定义时为真 237 | - `#inndef`未定义时为真 238 | - 头文件保护符的名称需要唯一,且保持全部大写。养成良好习惯,不论是否该头文件被包含,要加保护符。 239 | 240 | ```c++ 241 | #ifndef SALES_DATA_H //SALES_DATA_H未定义时为真 242 | #define SALES_DATA_H 243 | strct Sale_data{ 244 | ... 245 | } 246 | #endif 247 | ``` 248 | -------------------------------------------------------------------------------- /notes/ch03.md: -------------------------------------------------------------------------------- 1 | # 第三章 字符串、向量和数组 2 | 3 | ## using声明 4 | - 使用某个命名空间:例如 `using std::cin`表示使用命名空间`std`中的名字`cin`。 5 | - 头文件中不应该包含`using`声明。这样使用了该头文件的源码也会使用这个声明,会带来风险。 6 | 7 | ## string 8 | - 标准库类型`string`表示可变长的字符序列。 9 | - `#include `,然后 `using std::string;` 10 | - **string对象**:注意,不同于字符串字面值。 11 | 12 | ### 定义和初始化string对象 13 | 14 | 初始化`string`对象的方式: 15 | 16 | | 方式 | 解释 | 17 | | -- | -- | 18 | | `string s1` | 默认初始化,`s1`是个空字符串 | 19 | | `string s2(s1)` | `s2`是`s1`的副本 | 20 | | `string s2 = s1` | 等价于`s2(s1)`,`s2`是`s1`的副本 | 21 | | `string s3("value")` | `s3`是字面值“value”的副本,除了字面值最后的那个空字符外 | 22 | | `string s3 = "value"` | 等价于`s3("value")`,`s3`是字面值"value"的副本 | 23 | | `string s4(n, 'c')` | 把`s4`初始化为由连续`n`个字符`c`组成的串 | 24 | 25 | - 拷贝初始化(copy initialization):使用等号`=`将一个已有的对象拷贝到正在创建的对象。 26 | - 直接初始化(direct initialization):通过括号给对象赋值。 27 | 28 | ### string对象上的操作 29 | 30 | `string`的操作: 31 | 32 | | 操作 | 解释 | 33 | |-----|-----| 34 | | `os << s` | 将`s`写到输出流`os`当中,返回`os` | 35 | | `is >> s` | 从`is`中读取字符串赋给`s`,字符串以空白分割,返回`is` | 36 | | `getline(is, s)` | 从`is`中读取一行赋给`s`,返回`is` | 37 | | `s.empty()` | `s`为空返回`true`,否则返回`false` | 38 | | `s.size()` | 返回`s`中字符的个数 | 39 | | `s[n]` | 返回`s`中第`n`个字符的引用,位置`n`从0计起 | 40 | | `s1+s2` | 返回`s1`和`s2`连接后的结果 | 41 | | `s1=s2` | 用`s2`的副本代替`s1`中原来的字符 | 42 | | `s1==s2` | 如果`s1`和`s2`中所含的字符完全一样,则它们相等;`string`对象的相等性判断对字母的大小写敏感 | 43 | | `s1!=s2` | 同上 | 44 | | `<`, `<=`, `>`, `>=` | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较) | 45 | 46 | - string io: 47 | - 执行读操作`>>`:忽略掉开头的空白(包括空格、换行符和制表符),直到遇到下一处空白为止。 48 | - `getline`:读取一整行,**包括空白符**。 49 | - `s.size()`返回的时`string::size_type`类型,记住是一个**无符号**类型的值,不要和`int`混用 50 | - `s1+s2`使用时,保证至少一侧是string类型。`string s1 = "hello" + "world" // 错误,两侧均为字符串字面值` 51 | - **字符串字面值和string是不同的类型。** 52 | 53 | ### 处理string对象中的字符 54 | 55 | - **ctype.h vs. cctype**:C++修改了c的标准库,名称为去掉`.h`,前面加`c`。 56 | > 如c++版本为`cctype`,c版本为`ctype.h` 57 | - **尽量使用c++版本的头文件**,即`cctype` 58 | 59 | `cctype`头文件中定义了一组标准函数: 60 | 61 | | 函数 | 解释 | 62 | |-----|-----| 63 | | `isalnum(c)` | 当`c`是字母或数字时为真 | 64 | | `isalpha(c)` | 当`c`是字母时为真 | 65 | | `iscntrl(c)` | 当`c`是控制字符时为真 | 66 | | `isdigit(c)` | 当`c`是数字时为真 | 67 | | `isgraph(c)` | 当`c`不是空格但可以打印时为真 | 68 | | `islower(c)` | 当`c`是小写字母时为真 | 69 | | `isprint(c)` | 当`c`是可打印字符时为真 | 70 | | `ispunct(c)` | 当`c`是标点符号时为真 | 71 | | `isspace(c)` | 当`c`是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符) | 72 | | `isupper(c)` | 当`c`是大写字母时为真 | 73 | | `isxdigit(c)` | 当`c`是十六进制数字时为真 | 74 | | `tolower(c)` | 当`c`是大写字母,输出对应的小写字母;否则原样输出`c` | 75 | | `toupper(c)` | 当`c`是小写字母,输出对应的大写字母;否则原样输出`c` | 76 | 77 | - 遍历字符串:使用**范围for**(range for)语句: `for (auto c: str)`,或者 `for (auto &c: str)`使用引用直接改变字符串中的字符。 (C++11) 78 | - `str[x]`,[]输入参数为`string::size_type`类型,给出`int`整型也会自动转化为该类型 79 | 80 | ## vector 81 | - vector是一个**容器**,也是一个类模板; 82 | - `#include ` 然后 `using std::vector;` 83 | - 容器:包含其他对象。 84 | - 类模板:本身不是类,但可以**实例化instantiation**出一个类。 `vector`是一个模板, `vector`是一个类型。 85 | - 通过将类型放在类模板名称后面的**尖括号**中来指定**类型**,如`vector ivec`。 86 | 87 | ### 定义和初始化vector对象 88 | 89 | 初始化`vector`对象的方法 90 | 91 | | 方法 | 解释 | 92 | |-----|-----| 93 | | `vector v1` | `v1`是一个空`vector`,它潜在的元素是`T`类型的,执行默认初始化 | 94 | | `vector v2(v1)` | `v2`中包含有`v1`所有元素的副本 | 95 | | `vector v2 = v1` | 等价于`v2(v1)`,`v2`中包含`v1`所有元素的副本 | 96 | | `vector v3(n, val)` | `v3`包含了n个重复的元素,每个元素的值都是`val` | 97 | | `vector v4(n)` | `v4`包含了n个重复地执行了值初始化的对象 | 98 | | `vector v5{a, b, c...}` | `v5`包含了初始值个数的元素,每个元素被赋予相应的初始值 | 99 | | `vector v5={a, b, c...}` | 等价于`v5{a, b, c...}` | 100 | 101 | - 列表初始化: `vector v{"a", "an", "the"};` (C++11) 102 | 103 | ### 向vector对象中添加元素 104 | 105 | - `v.push_back(e)` 在尾部增加元素。 106 | 107 | ### 其他vector操作 108 | 109 | `vector`支持的操作: 110 | 111 | | 操作 | 解释 | 112 | |-----|-----| 113 | | `v.emtpy()` | 如果`v`不含有任何元素,返回真;否则返回假 | 114 | | `v.size()` | 返回`v`中元素的个数| 115 | | `v.push_back(t)` | 向`v`的尾端添加一个值为`t`的元素 | 116 | | `v[n]` | 返回`v`中第`n`个位置上元素的**引用** | 117 | | `v1 = v2` | 用`v2`中的元素拷贝替换`v1`中的元素 | 118 | | `v1 = {a,b,c...}` | 用列表中元素的拷贝替换`v1`中的元素 | 119 | | `v1 == v2` | `v1`和`v2`相等当且仅当它们的元素数量相同且对应位置的元素值都相同 | 120 | | `v1 != v2` | 同上 | 121 | | `<`,`<=`,`>`, `>=` | 以字典顺序进行比较 | 122 | 123 | - 范围`for`语句内不应该改变其遍历序列的大小。 124 | - `vector`对象(以及`string`对象)的下标运算符,只能对确知已存在的元素执行下标操作,不能用于添加元素。 125 | 126 | ## 迭代器iterator 127 | 128 | - 所有标准库容器都可以使用迭代器。 129 | - 类似于指针类型,迭代器也提供了对对象的间接访问。 130 | 131 | ### 使用迭代器 132 | 133 | - `vector::iterator iter`。 134 | - `auto b = v.begin();`返回指向第一个元素的迭代器。 135 | - `auto e = v.end();`返回指向最后一个元素的下一个(哨兵,尾后,one past the end)的迭代器(off the end)。 136 | - 如果容器为空, `begin()`和 `end()`返回的是同一个迭代器,都是尾后迭代器。 137 | - 使用解引用符`*`访问迭代器指向的元素。 138 | - 养成使用迭代器和`!=`的习惯(泛型编程)。 139 | - **容器**:可以包含其他对象;但所有的对象必须类型相同。 140 | - **迭代器(iterator)**:每种标准容器都有自己的迭代器。`C++`倾向于用迭代器而不是下标遍历元素。 141 | - **const_iterator**:只能读取容器内元素不能改变。 142 | - **箭头运算符**: 解引用 + 成员访问,`it->mem`等价于 `(*it).mem` 143 | - **谨记**:但凡是使用了**迭代器**的循环体,都**不要**向迭代器所属的容器**添加元素**。 144 | 145 | 标准容器迭代器的运算符: 146 | 147 | | 运算符 | 解释 | 148 | |-----|-----| 149 | | `*iter` | 返回迭代器`iter`所指向的**元素的引用** | 150 | | `iter->mem` | 等价于`(*iter).mem` | 151 | | `++iter` | 令`iter`指示容器中的下一个元素 | 152 | | `--iter` | 令`iter`指示容器中的上一个元素 | 153 | | `iter1 == iter2` | 判断两个迭代器是否相等 | 154 | 155 | ### 迭代器运算 156 | 157 | `vector`和`string`迭代器支持的运算: 158 | 159 | | 运算符 | 解释 | 160 | |-----|-----| 161 | | `iter + n` | 迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。 | 162 | | `iter - n` | 迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。 | 163 | | `iter1 += n` | 迭代器加法的复合赋值语句,将`iter1`加n的结果赋给`iter1` | 164 | | `iter1 -= n` | 迭代器减法的复合赋值语句,将`iter2`减n的结果赋给`iter1` | 165 | | `iter1 - iter2` | 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。 | 166 | | `>`、`>=`、`<`、`<=` | 迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说明前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或尾元素的下一位置。 | 167 | 168 | - **difference_type**:保证足够大以存储任何两个迭代器对象间的距离,可正可负。 169 | 170 | ## 数组 171 | 172 | - 相当于vector的低级版,**长度固定**。 173 | 174 | ### 定义和初始化内置数组 175 | 176 | - 初始化:`char input_buffer[buffer_size];`,长度必须是const表达式,或者不写,让编译器自己推断。 177 | - 数组不允许直接赋值给另一个数组。 178 | 179 | ### 访问数组元素 180 | 181 | - 数组下标的类型:`size_t` 。 182 | - 字符数组的特殊性:结尾处有一个空字符,如 `char a[] = "hello";` 。 183 | - 用数组初始化 `vector`: `int a[] = {1,2,3,4,5}; vector v(begin(a), end(a));` 。 184 | 185 | ### 数组和指针 186 | 187 | - 使用数组时,编译器一般会把它转换成指针。 188 | - 标准库类型限定使用的下标必须是无符号类型,而内置的下标可以处理负值。 189 | - **指针访问数组**:在表达式中使用数组名时,名字会自动转换成指向数组的第一个元素的指针。 190 | 191 | ## C风格字符串 192 | 193 | - 从C继承来的字符串。 194 | - 用空字符结束(`\0`)。 195 | - 对大多数应用来说,使用标准库 `string`比使用C风格字符串更安全、更高效。 196 | - 获取 `string` 中的 `cstring` : `const char *str = s.c_str();` 。 197 | 198 | C标准库String函数,定义在`` 中: 199 | 200 | | 函数 | 介绍 | 201 | |-----|-----| 202 | | `strlen(p)` | 返回`p`的长度,空字符不计算在内 | 203 | | `strcmp(p1, p2)` | 比较`p1`和`p2`的相等性。如果`p1==p2`,返回0;如果`p1>p2`,返回一个正值;如果`p1 text; 33 | for(const auto &s: text){ 34 | cout<=0) 61 | cout<<*iter++< **简介是一种美德**,追求简洁能降低程序出错可能性 65 | 66 | ## 成员访问运算符 67 | 68 | `ptr->mem`等价于`(*ptr).mem` 69 | 70 | 注意`.`运算符优先级大于`*`,所以记得加括号 71 | 72 | ## 条件运算符 73 | 74 | - 条件运算符(`?:`)允许我们把简单的`if-else`逻辑嵌入到单个表达式中去,按照如下形式:`cond? expr1: expr2` 75 | 76 | - 可以嵌套使用,**右结合律**,从右向左顺序组合 77 | 78 | - ```c++ 79 | finalgrade = (grade > 90) ? "high pass" 80 | : (grade < 60) ? "fail" : "pass"; 81 | //等价于 82 | finalgrade = (grade > 90) ? "high pass" 83 | : ((grade < 60) ? "fail" : "pass"); 84 | ``` 85 | 86 | - 输出表达式使用条件运算符记得加括号,条件运算符优先级太低。 87 | 88 | ## 位运算符 89 | 90 | 用于检查和设置二进制位的功能。 91 | 92 | - 位运算符是作用于**整数类型**的运算对象。 93 | - 二进制位向左移(`<<`)或者向右移(`>>`),移出边界外的位就被舍弃掉了。 94 | - 位取反(`~`)(逐位求反)、与(`&`)、或(`|`)、异或(`^`) 95 | 96 | 有符号数负值可能移位后变号,所以强烈建议**位运算符仅用于无符号数**。 97 | 98 | 应用: 99 | 100 | ```c++ 101 | unsigned long quiz1 = 0; // 每一位代表一个学生是否通过考试 102 | 1UL << 12; // 代表第12个学生通过 103 | quiz1 |= (1UL << 12); // 将第12个学生置为已通过 104 | quiz1 &= ~(1UL << 12); // 将第12个学生修改为未通过 105 | bool stu12 = quiz1 & (1UL << 12); // 判断第12个学生是否通过 106 | ``` 107 | 108 | > 位运算符使用较少,但是重载cout、cin大家都用过 109 | 110 | 位运算符满足左结合律,优先级介于中间,使用时尽量加括号。 111 | 112 | ## sizeof运算符 113 | 114 | - 返回一条表达式或一个类型名字所占的**字节数**。 115 | - 返回的类型是 `size_t`的常量表达式。 116 | - `sizeof`并不实际计算其运算对象的值。 117 | - 两种形式: 118 | 1. `sizeof (type)`,给出类型名 119 | 2. `sizeof expr`,给出表达式 120 | - 可用sizeof返回数组的大小 121 | 122 | ```c++ 123 | int ia[10]; 124 | // sizeof(ia)返回整个数组所占空间的大小 125 | // sizeof(ia)/sizeof(*ia)返回数组的大小 126 | constexpr size_t sz = sizeof(ia)/sizeof(*ia); 127 | int arr[sz]; 128 | ``` 129 | 130 | ## 逗号运算符 131 | 132 | 从左向右依次求值。 133 | 134 | 左侧求值结果丢弃,逗号运算符**结果是右侧表达式**的值。 135 | 136 | ## 类型转换 137 | 138 | ### 隐式类型转换 139 | 140 | > 设计为尽可能避免损失精度,即转换为更精细类型。 141 | 142 | - 比 `int`类型小的整数值先提升为较大的整数类型。 143 | - 条件中,非布尔转换成布尔。 144 | - 初始化中,初始值转换成变量的类型。 145 | - 算术运算或者关系运算的运算对象有多种类型,要转换成同一种类型。 146 | - 函数调用时也会有转换。 147 | 148 | #### 算术转换 149 | 150 | ##### 整型提升 151 | 152 | * 常见的char、bool、short能存在int就会转换成int,否则提升为`unsigned int` 153 | * `wchar_t,char16_t,char32_t`提升为整型中`int,long,long long ……`最小的,且能容纳原类型所有可能值的类型。 154 | 155 | #### 其他转换 156 | 157 | > p143 158 | 159 | ### 显式类型转换(尽量避免) 160 | 161 | - **static_cast**:任何明确定义的类型转换,只要不包含底层const,都可以使用。 `double slope = static_cast(j);` 162 | 163 | - **dynamic_cast**:支持运行时类型识别。 164 | 165 | - **const_cast**:只能改变运算对象的底层const,一般可用于去除const性质。 `const char *pc; char *p = const_cast(pc)` 166 | 167 | > 只有其可以改变常量属性 168 | 169 | - **reinterpret_cast**:通常为运算对象的位模式提供低层次上的重新解释。 170 | 171 | #### 旧式强制类型转换 172 | 173 | `type expr` 174 | 175 | ## 运算符优先级表 176 | 177 | > p147 -------------------------------------------------------------------------------- /notes/ch05.md: -------------------------------------------------------------------------------- 1 | # 第五章 语句 2 | 3 | ## 简单语句 4 | 5 | - **表达式语句**:一个表达式末尾加上分号,就变成了表达式语句。 6 | - **空语句**:只有一个单独的分号。 7 | - **复合语句(块)**:用花括号 `{}`包裹起来的语句和声明的序列。一个块就是一个作用域。 8 | 9 | ## 条件语句 10 | 11 | - **悬垂else**(dangling else):用来描述在嵌套的`if else`语句中,如果`if`比`else`多时如何处理的问题。C++使用的方法是`else`匹配最近没有配对的`if`。 12 | 13 | ## 迭代语句 14 | 15 | - **while**:当不确定到底要迭代多少次时,使用 `while`循环比较合适,比如读取输入的内容。 16 | - **for**: `for`语句可以省略掉 `init-statement`, `condition`和 `expression`的任何一个;**甚至全部**。 17 | - **范围for**: `for (declaration: expression) statement` 18 | 19 | ## 跳转语句 20 | 21 | - **break**:`break`语句负责终止离它最近的`while`、`do while`、`for`或者`switch`语句,并从这些语句之后的第一条语句开始继续执行。 22 | - **continue**:终止最近的循环中的当前迭代并立即开始下一次迭代。只能在`while`、`do while`、`for`循环的内部。 23 | 24 | ## try语句块和异常处理 25 | 26 | - **throw表达式**:异常检测部分使用 `throw`表达式来表示它遇到了无法处理的问题。我们说 `throw`引发 `raise`了异常。 27 | - **try语句块**:以 `try`关键词开始,以一个或多个 `catch`字句结束。 `try`语句块中的代码抛出的异常通常会被某个 `catch`捕获并处理。 `catch`子句也被称为**异常处理代码**。 28 | - **异常类**:用于在 `throw`表达式和相关的 `catch`子句之间传递异常的具体信息。 29 | 30 | -------------------------------------------------------------------------------- /notes/ch06.md: -------------------------------------------------------------------------------- 1 | # 第六章 函数 2 | 3 | ## 函数基础 4 | 5 | - **函数定义**:包括返回类型、函数名字和0个或者多个**形参**(parameter)组成的列表和函数体。 6 | - **调用运算符**:调用运算符的形式是一对圆括号 `()`,作用于一个表达式,该表达式是函数或者指向函数的指针。 7 | - 圆括号内是用逗号隔开的**实参**(argument)列表。 8 | - 函数调用过程: 9 | - 1.主调函数(calling function)的执行被中断。 10 | - 2.被调函数(called function)开始执行。 11 | - **形参和实参**:形参和实参的**个数**和**类型**必须匹配上。 12 | - **返回类型**: `void`表示函数不返回任何值。函数的返回类型不能是数组类型或者函数类型,但可以是指向数组或者函数的指针。 13 | - **名字**:名字的作用于是程序文本的一部分,名字在其中可见。 14 | 15 | ### 局部对象 16 | 17 | - **生命周期**:对象的生命周期是程序执行过程中该对象存在的一段时间。 18 | - **局部变量**(local variable):形参和函数体内部定义的变量统称为局部变量。它对函数而言是局部的,对函数外部而言是**隐藏**的。 19 | - **自动对象**:只存在于块执行期间的对象。当块的执行结束后,它的值就变成**未定义**的了。 20 | - **局部静态对象**: `static`类型的局部变量,生命周期贯穿函数调用前后。 21 | 22 | ### 函数声明 23 | 24 | - **函数声明**:函数的声明和定义唯一的区别是声明无需函数体,用一个分号替代。函数声明主要用于描述函数的接口,也称**函数原型**。 25 | - **在头文件中进行函数声明**:建议变量在头文件中声明;在源文件中定义。 26 | - **分离编译**: `CC a.cc b.cc`直接编译生成可执行文件;`CC -c a.cc b.cc`编译生成对象代码`a.o b.o`; `CC a.o b.o`编译生成可执行文件。 27 | 28 | ## 参数传递 29 | 30 | - 形参初始化的机理和变量初始化一样。 31 | - **引用传递**(passed by reference):又称传引用调用(called by reference),指**形参是引用类型**,引用形参是它对应的实参的别名。 32 | - **值传递**(passed by value):又称传值调用(called by value),指实参的值是通过**拷贝**传递给形参。 33 | 34 | ### 传值参数 35 | 36 | - 当初始化一个非引用类型的变量时,初始值被拷贝给变量。 37 | - 函数对形参做的所有操作都不会影响实参。 38 | - **指针形参**:常用在C中,`C++`建议使用引用类型的形参代替指针。 39 | 40 | ### 传引用参数 41 | 42 | - 通过使用引用形参,允许函数改变一个或多个实参的值。 43 | - 引用形参直接关联到绑定的对象,而非对象的副本。 44 | - 使用引用形参可以用于**返回额外的信息**。 45 | - 经常用引用形参来避免不必要的复制。 46 | - `void swap(int &v1, int &v2)` 47 | - 如果无需改变引用形参的值,最好将其声明为常量引用。 48 | 49 | ### const形参和实参 50 | 51 | - 形参的顶层`const`被忽略。`void func(const int i);`调用时既可以传入`const int`也可以传入`int`。 52 | - 我们可以使用非常量初始化一个底层`const`对象,但是反过来不行。 53 | - 在函数中,不能改变实参的**局部副本**。 54 | - 尽量使用常量引用。 55 | 56 | ### 数组形参 57 | 58 | - 当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。 59 | - 要注意数组的实际长度,不能越界。 60 | 61 | ### main处理命令行选项 62 | 63 | - `int main(int argc, char *argv[]){...}` 64 | - 第一个形参代表参数的个数;第二个形参是参数C风格字符串数组。 65 | 66 | ### 可变形参 67 | 68 | `initializer_list`提供的操作(`C++11`): 69 | 70 | | 操作 | 解释 | 71 | |-----|-----| 72 | | `initializer_list lst;` | 默认初始化;`T`类型元素的空列表 | 73 | | `initializer_list lst{a,b,c...};` | `lst`的元素数量和初始值一样多;`lst`的元素是对应初始值的副本;列表中的元素是`const`。 | 74 | | `lst2(lst)` | 拷贝或赋值一个`initializer_list`对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。 | 75 | | `lst2 = lst` | 同上 | 76 | | `lst.size()` | 列表中的元素数量 | 77 | | `lst.begin()` | 返回指向`lst`中首元素的指针 | 78 | | `lst.end()` | 返回指向`lst`中微元素下一位置的指针 | 79 | 80 | `initializer_list`使用demo: 81 | 82 | ```cpp 83 | void err_msg(ErrCode e, initializer_list il){ 84 | cout << e.msg << endl; 85 | for (auto bed = il.begin(); beg != il.end(); ++ beg) 86 | cout << *beg << " "; 87 | cout << endl; 88 | } 89 | 90 | err_msg(ErrCode(0), {"functionX", "okay}); 91 | ``` 92 | 93 | - 所有实参类型相同,可以使用 `initializer_list`的标准库类型。 94 | - 实参类型不同,可以使用`可变参数模板`。 95 | - 省略形参符: `...`,便于`C++`访问某些C代码,这些C代码使用了 `varargs`的C标准功能。 96 | 97 | ## 返回类型和return语句 98 | 99 | ### 无返回值函数 100 | 101 | 没有返回值的 `return`语句只能用在返回类型是 `void`的函数中,返回 `void`的函数不要求非得有 `return`语句。 102 | 103 | ### 有返回值函数 104 | 105 | - `return`语句的返回值的类型必须和函数的返回类型相同,或者能够**隐式地**转换成函数的返回类型。 106 | - 值的返回:返回的值用于初始化调用点的一个**临时量**,该临时量就是函数调用的结果。 107 | - **不要返回局部对象的引用或指针**。 108 | - **引用返回左值**:函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值;其他返回类型得到右值。 109 | - **列表初始化返回值**:函数可以返回花括号包围的值的列表。(`C++11`) 110 | - **主函数main的返回值**:如果结尾没有`return`,编译器将隐式地插入一条返回0的`return`语句。返回0代表执行成功。 111 | 112 | ### 返回数组指针 113 | 114 | - `Type (*function (parameter_list))[dimension]` 115 | - 使用类型别名: `typedef int arrT[10];` 或者 `using arrT = int[10;]`,然后 `arrT* func() {...}` 116 | - 使用 `decltype`: `decltype(odd) *arrPtr(int i) {...}` 117 | - **尾置返回类型**: 在形参列表后面以一个`->`开始:`auto func(int i) -> int(*)[10]`(`C++11`) 118 | 119 | ## 函数重载 120 | 121 | - **重载**:如果同一作用域内几个函数名字相同但形参列表不同,我们称之为重载(overload)函数。 122 | - `main`函数不能重载。 123 | - **重载和const形参**: 124 | - 一个有顶层const的形参和没有它的函数无法区分。 `Record lookup(Phone* const)`和 `Record lookup(Phone*)`无法区分。 125 | - 相反,是否有某个底层const形参可以区分。 `Record lookup(Account*)`和 `Record lookup(const Account*)`可以区分。 126 | - **重载和作用域**:若在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体,在不同的作用域中无法重载函数名。 127 | 128 | ## 特殊用途语言特性 129 | 130 | ### 默认实参 131 | 132 | - `string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');` 133 | - 一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。 134 | 135 | ### 内联(inline)函数 136 | 137 | - 普通函数的缺点:调用函数比求解等价表达式要慢得多。 138 | - `inline`函数可以避免函数调用的开销,可以让编译器在编译时**内联地展开**该函数。 139 | - `inline`函数应该在头文件中定义。 140 | 141 | ### constexpr函数 142 | 143 | - 指能用于常量表达式的函数。 144 | - `constexpr int new_sz() {return 42;}` 145 | - 函数的返回类型及所有形参类型都要是字面值类型。 146 | - `constexpr`函数应该在头文件中定义。 147 | 148 | ### 调试帮助 149 | 150 | - `assert`预处理宏(preprocessor macro):`assert(expr);` 151 | 152 | 开关调试状态: 153 | 154 | `CC -D NDEBUG main.c`可以定义这个变量`NDEBUG`。 155 | 156 | ```cpp 157 | void print(){ 158 | #ifndef NDEBUG 159 | cerr << __func__ << "..." << endl; 160 | #endif 161 | } 162 | ``` 163 | 164 | ## 函数匹配 165 | 166 | - 重载函数匹配的**三个步骤**:1.候选函数;2.可行函数;3.寻找最佳匹配。 167 | - **候选函数**:选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。 168 | - **可行函数**:考察本次调用提供的实参,选出可以被这组实参调用的函数,新选出的函数称为可行函数(viable function)。 169 | - **寻找最佳匹配**:基本思想:实参类型和形参类型越接近,它们匹配地越好。 170 | 171 | ## 函数指针 172 | 173 | - **函数指针**:是指向函数的指针。 174 | - `bool (*pf)(const string &, const string &);` 注:两端的括号不可少。 175 | - **函数指针形参**: 176 | - 形参中使用函数定义或者函数指针定义效果一样。 177 | - 使用类型别名或者`decltype`。 178 | - **返回指向函数的指针**:1.类型别名;2.尾置返回类型。 179 | -------------------------------------------------------------------------------- /notes/ch07.md: -------------------------------------------------------------------------------- 1 | # 第七章 类 (Class) 2 | 3 | ## 定义抽象数据类型 4 | 5 | - **类背后的基本思想**:**数据抽象**(data abstraction)和**封装**(encapsulation)。 6 | - 数据抽象是一种依赖于**接口**(interface)和**实现**(implementation)分离的编程技术。 7 | 8 | ### 类成员 (Member) 9 | 10 | - 必须在类的内部声明,不能在其他地方增加成员。 11 | - 成员可以是数据,函数,类型别名。 12 | 13 | ### 类的成员函数 14 | 15 | - 成员函数的**声明**必须在类的内部。 16 | - 成员函数的**定义**既可以在类的内部也可以在外部。 17 | - 使用点运算符 `.` 调用成员函数。 18 | - 必须对任何`const`或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式。 19 | - `ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }` 20 | - 默认实参: `Sales_item(const std::string &book): isbn(book), units_sold(0), revenue(0.0) { }` 21 | - `*this`: 22 | - 每个成员函数都有一个额外的,隐含的形参`this`。 23 | - `this`总是指向当前对象,因此`this`是一个常量指针。 24 | - 形参表后面的`const`,改变了隐含的`this`形参的类型,如 `bool same_isbn(const Sales_item &rhs) const`,这种函数称为“常量成员函数”(`this`指向的当前对象是常量)。 25 | - `return *this;`可以让成员函数连续调用。 26 | - 普通的非`const`成员函数:`this`是指向类类型的`const`指针(可以改变`this`所指向的值,不能改变`this`保存的地址)。 27 | - `const`成员函数:`this`是指向const类类型的`const`指针(既不能改变`this`所指向的值,也不能改变`this`保存的地址)。 28 | 29 | ### 非成员函数 30 | 31 | - 和类相关的非成员函数,定义和声明都应该在类的外部。 32 | 33 | ### 类的构造函数 34 | 35 | - 类通过一个或者几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做**构造函数**。 36 | - 构造函数是特殊的成员函数。 37 | - 构造函数放在类的`public`部分。 38 | - 与类同名的成员函数。 39 | - `Sales_item(): units_sold(0), revenue(0.0) { }` 40 | - `=default`要求编译器合成默认的构造函数。(`C++11`) 41 | - 初始化列表:冒号和花括号之间的代码: `Sales_item(): units_sold(0), revenue(0.0) { }` 42 | 43 | ## 访问控制与封装 44 | 45 | - **访问说明符**(access specifiers): 46 | - `public`:定义在 `public`后面的成员在整个程序内可以被访问; `public`成员定义类的接口。 47 | - `private`:定义在 `private`后面的成员可以被类的成员函数访问,但不能被使用该类的代码访问; `private`隐藏了类的实现细节。 48 | - 使用 `class`或者 `struct`:都可以被用于定义一个类。唯一的却别在于访问权限。 49 | - 使用 `class`:在第一个访问说明符之前的成员是 `priavte`的。 50 | - 使用 `struct`:在第一个访问说明符之前的成员是 `public`的。 51 | 52 | ### 友元 53 | 54 | - 允许特定的**非成员函数**访问一个类的**私有成员**. 55 | - 友元的声明以关键字 `friend`开始。 `friend Sales_data add(const Sales_data&, const Sales_data&);`表示非成员函数`add`可以访问类的非公有成员。 56 | - 通常将友元声明成组地放在**类定义的开始或者结尾**。 57 | - 类之间的友元: 58 | - 如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。 59 | 60 | ### 封装的益处 61 | 62 | - 确保用户的代码不会无意间破坏封装对象的状态。 63 | - 被封装的类的具体实现细节可以随时改变,而无需调整用户级别的代码。 64 | 65 | ## 类的其他特性 66 | 67 | - 成员函数作为内联函数 `inline`: 68 | - 在类的内部,常有一些规模较小的函数适合于被声明成内联函数。 69 | - **定义**在类内部的函数是**自动内联**的。 70 | - 在类外部定义的成员函数,也可以在声明时显式地加上 `inline`。 71 | - **可变数据成员** (mutable data member): 72 | - `mutable size_t access_ctr;` 73 | - 永远不会是`const`,即使它是`const`对象的成员。 74 | - **类类型**: 75 | - 每个类定义了唯一的类型。 76 | 77 | ## 类的作用域 78 | 79 | - 每个类都会定义它自己的作用域。在类的作用域之外,普通的数据和函数成员只能由引用、对象、指针使用成员访问运算符来访问。 80 | - 函数的**返回类型**通常在函数名前面,因此当成员函数定义在类的外部时,返回类型中使用的名字都位于类的作用域之外。 81 | - 如果成员使用了外层作用域中的某个名字,而该名字代表一种**类型**,则类不能在之后重新定义该名字。 82 | - 类中的**类型名定义**都要放在一开始。 83 | 84 | ## 构造函数再探 85 | 86 | - 构造函数初始值列表: 87 | - 类似`python`使用赋值的方式有时候不行,比如`const`或者引用类型的数据,只能初始化,不能赋值。(注意初始化和赋值的区别) 88 | - 最好让构造函数初始值的顺序和成员声明的顺序保持一致。 89 | - 如果一个构造函数为所有参数都提供了默认参数,那么它实际上也定义了默认的构造函数。 90 | 91 | ### 委托构造函数 (delegating constructor, `C++11`) 92 | 93 | - 委托构造函数将自己的职责委托给了其他构造函数。 94 | - `Sale_data(): Sale_data("", 0, 0) {}` 95 | 96 | ### 隐式的类型转换 97 | 98 | - 如果构造函数**只接受一个实参**,则它实际上定义了转换为此类类型的**隐式转换机制**。这种构造函数又叫**转换构造函数**(converting constructor)。 99 | - 编译器只会自动地执行`仅一步`类型转换。 100 | - 抑制构造函数定义的隐式转换: 101 | - 将构造函数声明为`explicit`加以阻止。 102 | - `explicit`构造函数只能用于直接初始化,不能用于拷贝形式的初始化。 103 | 104 | ### 聚合类 (aggregate class) 105 | 106 | - 满足以下所有条件: 107 | - 所有成员都是`public`的。 108 | - 没有定义任何构造函数。 109 | - 没有类内初始值。 110 | - 没有基类,也没有`virtual`函数。 111 | - 可以使用一个花括号括起来的成员初始值列表,初始值的顺序必须和声明的顺序一致。 112 | 113 | ### 字面值常量类 114 | 115 | - `constexpr`函数的参数和返回值必须是字面值。 116 | - **字面值类型**:除了算术类型、引用和指针外,某些类也是字面值类型。 117 | - 数据成员都是字面值类型的聚合类是字面值常量类。 118 | - 如果不是聚合类,则必须满足下面所有条件: 119 | - 数据成员都必须是字面值类型。 120 | - 类必须至少含有一个`constexpr`构造函数。 121 | - 如果一个数据成员含有类内部初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的`constexpr`构造函数。 122 | - 类必须使用析构函数的默认定义,该成员负责销毁类的对象。 123 | 124 | ## 类的静态成员 125 | 126 | - 非`static`数据成员存在于类类型的每个对象中。 127 | - `static`数据成员独立于该类的任意对象而存在。 128 | - 每个`static`数据成员是与类关联的对象,并不与该类的对象相关联。 129 | - 声明: 130 | - 声明之前加上关键词`static`。 131 | - 使用: 132 | - 使用**作用域运算符**`::`直接访问静态成员:`r = Account::rate();` 133 | - 也可以使用对象访问:`r = ac.rate();` 134 | - 定义: 135 | - 在类外部定义时不用加`static`。 136 | - 初始化: 137 | - 通常不在类的内部初始化,而是在定义时进行初始化,如 `double Account::interestRate = initRate();` 138 | - 如果一定要在类内部定义,则要求必须是字面值常量类型的`constexpr`。 139 | -------------------------------------------------------------------------------- /notes/ch08.md: -------------------------------------------------------------------------------- 1 | # 第八章 IO库 2 | 3 | ## 前面章节已经在用的IO库设施 4 | 5 | - **istream**:输入流类型,提供输入操作。 6 | - **ostream**:输出流类型,提供输出操作 7 | - **cin**:一个`istream`对象,从标准输入读取数据。 8 | - **cout**:一个`ostream`对象,向标准输出写入数据。 9 | - **cerr**:一个`ostream`对象,向标准错误写入消息。 10 | - **>>运算符**:用来从一个`istream`对象中读取输入数据。 11 | - **<<运算符**:用来向一个`ostream`对象中写入输出数据。 12 | - **getline函数**:从一个给定的`istream`对象中读取一行数据,存入到一个给定的`string`对象中。 13 | 14 | ## IO类 15 | 16 | ### 标准库定义的IO类型 17 | 18 | - `iostream`头文件:从标准流中读写数据,`istream`、`ostream`等。 19 | - `fstream`头文件:从文件中读写数据,`ifstream`、`ofstream`等。 20 | - `sstream`头文件:从字符串中读写数据,`istringstream`、`ostringstream` 21 | 22 | ### IO对象不可复制或赋值 23 | 24 | - 1.IO对象不能存在容器里. 25 | - 2.形参和返回类型也不能是流类型。 26 | - 3.形参和返回类型一般是流的**引用**。 27 | - 4.读写一个IO对象会改变其状态,因此传递和返回的引用不能是`const`的。 28 | 29 | ### 条件状态 30 | 31 | | 状态 | 解释 | 32 | | ----------- | ----------- | 33 | | `strm:iostate` | 是一种机器无关的**类型**,提供了表达条件状态的完整功能 | 34 | | `strm:badbit` | 用来指出流已经崩溃 | 35 | | `strm:failbit` | 用来指出一个IO操作失败了 | 36 | | `strm:eofbit` | 用来指出流到达了文件结束 | 37 | | `strm:goodbit` | 用来指出流未处于错误状态,此值保证为零 | 38 | | `s.eof()` | 若流`s`的`eofbit`置位,则返回`true` | 39 | | `s.fail()` | 若流`s`的`failbit`置位,则返回`true` | 40 | | `s.bad()` | 若流`s`的`badbit`置位,则返回`true` | 41 | | `s.good()` | 若流`s`处于有效状态,则返回`true` | 42 | | `s.clear()` | 将流`s`中所有条件状态位复位,将流的状态设置成有效,返回`void` | 43 | | `s.clear(flags)` | 将流`s`中指定的条件状态位复位,返回`void` | 44 | | `s.setstate(flags)` | 根据给定的标志位,将流`s`中对应的条件状态位置位,返回`void` | 45 | | `s.rdstate()` | 返回流`s`的当前条件状态,返回值类型为`strm::iostate` | 46 | 47 | 上表中,`strm`是一种IO类型,(如`istream`), `s`是一个流对象。 48 | 49 | ### 管理输出缓冲 50 | 51 | - 每个输出流都管理一个缓冲区,执行输出的代码,文本串可能立即打印出来,也可能被操作系统保存在缓冲区内,随后再打印。 52 | - 刷新缓冲区,可以使用如下IO操纵符: 53 | - `endl`:输出一个换行符并刷新缓冲区。 54 | - `flush`:刷新流,单不添加任何字符。 55 | - `ends`:在缓冲区插入空字符`null`,然后刷新。 56 | - `unitbuf`:告诉流接下来每次操作之后都要进行一次`flush`操作。 57 | - `nounitbuf`:回到正常的缓冲方式。 58 | 59 | ## 文件输入输出 60 | 61 | - 头文件`fstream`定义了三个类型来支持文件IO: 62 | - `ifstream`从一个给定文件读取数据。 63 | - `ofstream`向一个给定文件写入数据。 64 | - `fstream`可以读写给定文件。 65 | - **文件流**:需要读写文件时,必须定义自己的文件流对象,并绑定在需要的文件上。 66 | 67 | ### fstream特有的操作 68 | 69 | | 操作 | 解释 | 70 | | ----------- | ----------- | 71 | | `fstream fstrm;` | 创建一个未绑定的文件流。 | 72 | | `fstream fstrm(s);` | 创建一个文件流,并打开名为`s`的文件,`s`可以是`string`也可以是`char`指针 | 73 | | `fstream fstrm(s, mode);` | 与前一个构造函数类似,但按指定`mode`打开文件 | 74 | | `fstrm.open(s)` | 打开名为`s`的文件,并和`fstrm`绑定 | 75 | | `fstrm.close()` | 关闭和`fstrm`绑定的文件 | 76 | | `fstrm.is_open()` | 返回一个`bool`值,指出与`fstrm`关联的文件是否成功打开且尚未关闭 | 77 | 78 | 上表中,`fstream`是头文件`fstream`中定义的一个类型,`fstrm`是一个文件流对象。 79 | 80 | ### 文件模式 81 | 82 | | 文件模式 | 解释 | 83 | | ----------- | ----------- | 84 | |`in` | 以读的方式打开 | 85 | | `out` | 以写的方式打开 | 86 | | `app` | 每次写操作前均定位到文件末尾 | 87 | | `ate` | 打开文件后立即定位到文件末尾 | 88 | | `trunc` | 截断文件 | 89 | | `binary` | 以二进制方式进行IO操作。 | 90 | 91 | ## string流 92 | 93 | - 头文件`sstream`定义了三个类型来支持内存IO: 94 | - `istringstream`从`string`读取数据。 95 | - `ostringstream`向`string`写入数据。 96 | - `stringstream`可以读写给定`string`。 97 | 98 | ### stringstream特有的操作 99 | 100 | | 操作 | 解释 | 101 | | ----------- | ----------- | 102 | |`sstream strm` | 定义一个未绑定的`stringstream`对象 | 103 | | `sstream strm(s)` | 用`s`初始化对象 | 104 | | `strm.str()` | 返回`strm`所保存的`string`的拷贝 | 105 | | `strm.str(s)` | 将`s`拷贝到`strm`中,返回`void` | 106 | 107 | 上表中`sstream`是头文件`sstream`中任意一个类型。`s`是一个`string`。 108 | -------------------------------------------------------------------------------- /notes/ch10.md: -------------------------------------------------------------------------------- 1 | # 第十章 泛型算法 2 | 3 | ## 泛型算法 4 | 5 | - 因为它们实现共同的操作,所以称之为“**算法**”;而“**泛型**”、指的是它们可以操作在多种容器类型上。 6 | - 泛型算法本身不执行容器操作,只是单独依赖迭代器和迭代器操作实现。 7 | - 头文件: `#include `或者 `#include `(算数相关) 8 | - 大多数算法是通过遍历两个迭代器标记的一段元素来实现其功能。 9 | - 必要的编程假定:算法永远不会改变底层容器的大小。算法可能改变容器中保存的元素的值,也可能在容器内移动元素,但不能直接添加或者删除元素。 10 | 11 | ### find 12 | 13 | - `vector::const_iterator result = find(vec.begin(), vec.end(), search_value);` 14 | - 输入:两个标记范围的迭代器和目标查找值。返回:如果找到,返回对应的迭代器,否则返回第二个参数,即标记结尾的迭代器。 15 | 16 | ## 初识泛型算法 17 | 18 | - 标准库提供了超过100个算法,但这些算法有一致的结构。 19 | - 理解算法的最基本的方法是了解它们是否读取元素、改变元素、重排元素顺序。 20 | 21 | ### 只读算法 22 | 23 | - 只读取范围中的元素,不改变元素。 24 | - 如 `find`和 `accumulate`(在`numeric`中定义,求和)。 25 | - `find_first_of`,输入:两对迭代器标记两段范围,在第一段中找第二段中任意元素,返回第一个匹配的元素,找不到返回第一段的`end`迭代器。 26 | - 通常最好使用`cbegin`和`cend`。 27 | - `equal`:确定两个序列是否保存相同的值。 28 | 29 | ### 写容器元素的算法 30 | 31 | - 一些算法将新值赋予序列中的元素。 32 | - 算法不检查写操作。 33 | - `fill`: `fill(vec.begin(), vec.end(), 0);` 将每个元素重置为0 34 | - `fill_n`: `fill_n(vec.begin(), 10, 0);` 35 | - 插入迭代器`back_inserter`: 36 | - 用来确保算法有足够的空间存储数据。 37 | - `#include ` 38 | - `back_inserter(vec)` 39 | - 拷贝算法`copy`: 40 | - 输入:前两个参数指定输入范围,第三个指向目标序列。 41 | - `copy (ilst.begin(), ilst.end(), back_inserter(ivec));` 42 | - `copy`时必须保证目标目的序列至少要包含与输入序列一样多的元素。 43 | 44 | ### 重排容器元素的算法 45 | 46 | - 这些算法会重排容器中元素的顺序。 47 | - 排序算法`sort`: 48 | - 接受两个迭代器,表示要排序的元素范围。 49 | - 消除重复`unique`: 50 | - 之前要先调用`sort` 51 | - 返回的迭代器指向最后一个不重复元素之后的位置。 52 | - 顺序会变,重复的元素被“删除”。 53 | - 并没有真正删除,真正删除必须使用容器操作。 54 | 55 | ## 定制操作 56 | 57 | ### 向算法传递函数: 58 | 59 | - 谓词(`predicate`): 60 | - 是一个**可调用的表达式**,返回结果是一个能用作条件的值 61 | - 一元谓词:接受一个参数 62 | - 二元谓词:接受两个参数 63 | 64 | - 例子: 65 | - `stable_sort`: 66 | - 保留相等元素的原始相对位置。 67 | - `stable_sort(words.begin(), words.end(), isShorter);` 68 | 69 | ### lambda表达式 70 | 71 | - 有时可能希望操作可以接受更多的参数。 72 | - `lambda`表达式表示一个可调用的代码单元,可以理解成是一个未命名的内联函数。 73 | - 形式:`[capture list](parameter list) -> return type {function body}`。 74 | - 其中`capture list`捕获列表是一个`lambda`所在函数定义的局部变量的列表(通常为空)。不可忽略。 75 | - `return type`是返回类型。可忽略。 76 | - `parameter`是参数列表。可忽略。 77 | - `function body`是函数体。不可忽略。 78 | - `auto f = [] {return 42;}` 79 | 80 | - 例子: 81 | - `find_if`: 82 | - 接受一对表示范围的迭代器和一个谓词,用来查找第一个满足特定要求的元素。返回第一个使谓词返回非0值的元素。 83 | - `auto wc = find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz;});` 84 | - `for_each`: 85 | - 接受一个可调用对象,并对序列中每个元素调用此对象。 86 | - `for_each(wc, words.end(), [](const string &s){cout << s << " ";})` 87 | 88 | ### lambda捕获和返回 89 | 90 | - 定义`lambda`时会生成一个新的类类型和该类型的一个对象。 91 | - 默认情况下,从`lambda`生成的类都包含一个对应该`lambda`所捕获的变量的数据成员,在`lambda`对象创建时被初始化。 92 | - **值捕获**:前提是变量可以拷贝,`size_t v1 = 42; auto f = [v1] {return v1;};`。 93 | - **引用捕获**:必须保证在`lambda`执行时,变量是存在的,`auto f2 = [&v1] {return v1;};` 94 | - 尽量减少捕获的数据量,尽可能避免捕获指针或引用。 95 | - **隐式捕获**:让编译器推断捕获列表,在捕获列表中写一个`&`(引用方式)或`=`(值方式)。`auto f3 = [=] {return v1;}` 96 | 97 | **lambda捕获列表**: 98 | 99 | | 捕获列表 | 解释 | 100 | |-----|-----| 101 | | `[]` | 空捕获列表。`lambda`不能使用所在函数中的变量。一个`lambda`只有在捕获变量后才能使用它们。 | 102 | | `[names]` | `names`是一个逗号分隔的名字列表,这些名字都是在`lambda`所在函数的局部变量,捕获列表中的变量都被拷贝,名字前如果使用了`&`,则采用引用捕获方式。 | 103 | | `[&]` | 隐式捕获列表,采用引用捕获方式。`lambda`体中所使用的来自所在函数的实体都采用引用方式使用。 | 104 | | `[=]` | 隐式捕获列表,采用值捕获方式。 | 105 | | `[&, identifier_list]` | `identifier_list`是一个逗号分隔的列表,包含0个或多个来自所在函数的变量。这些变量采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获。`identifier_list`中的名字前面不能使用`&` | 106 | | `[=, identifier_list]` | `identifier_list`中的变量采用引用方式捕获,而任何隐式捕获的变量都采用值方式捕获。`identifier_list`中的名字不能包括`this`,且前面必须使用`&` | 107 | 108 | ### 参数绑定 109 | 110 | - `lambda`表达式更适合在一两个地方使用的简单操作。 111 | - 如果是很多地方使用相同的操作,还是需要定义函数。 112 | - 函数如何包装成一元谓词?使用参数绑定。 113 | - 标准库`bind`函数: 114 | - 定义在头文件`functional`中,可以看做为一个通用的函数适配器。 115 | - `auto newCallable = bind(callable, arg_list);` 116 | - 我们再调用`newCallable`的时候,`newCallable`会调用`callable`并传递给它`arg_list`中的参数。 117 | - `_n`代表第n个位置的参数。定义在`placeholders`的命名空间中。`using std::placeholder::_1;` 118 | - `auto g = bind(f, a, b, _2, c, _1);`,调用`g(_1, _2)`实际上调用`f(a, b, _2, c, _1)` 119 | - 非占位符的参数要使用引用传参,必须使用标准库`ref`函数或者`cref`函数。 120 | 121 | ## 再探迭代器 122 | 123 | ### 插入迭代器 124 | 125 | - 插入器是一种迭代器适配器,接受一个容器,生成一个迭代器,能实现向给定容器添加元素。 126 | - 三种类型: 127 | - `back_inserter`:创建一个使用`push_back`的迭代器。 128 | - `front_inserter`创建一个使用`push_front`的迭代器。 129 | - `inserter`创建一个使用`insert`的迭代器。接受第二个参数,即一个指向给定容器的迭代器,元素会被查到迭代器所指向的元素之前。 130 | 131 | **插入迭代器操作**: 132 | 133 | | 操作 | 解释 | 134 | |-----|-----| 135 | | `it=t` | 在`it`指定的当前位置插入值`t`。假定`c`是`it`绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用`c.push_back(t)`、`c.push_front(t)`、`c.insert(t, p)`,其中`p`是传递给`inserter`的迭代器位置 | 136 | | `*it, ++it, it++` | 这些操作虽然存在,但不会对`it`做任何事情,每个操作都返回`it` | 137 | 138 | ### iostream迭代器 139 | 140 | - 迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。 141 | - 通过使用流迭代器,我们可以用泛型算法从流对象中读取数据以及向其写入数据。 142 | 143 | **istream_iterator的操作**: 144 | 145 | | 操作 | 解释 | 146 | |-----|-----| 147 | | `istream_iterator in(is);` | `in`从输入流`is`读取类型为`T`的值 | 148 | |`istream_iterator end;` | 读取类型是`T`的值的`istream_iterator`迭代器,表示尾后位置 | 149 | | `in1 == in2` | `in1`和`in2`必须读取相同类型。如果他们都是尾后迭代器,或绑定到相同的输入,则两者相等。 | 150 | | `in1 != in2` | 类似上条 | 151 | | `*in` | 返回从流中读取的值 | 152 | | `in->mem` | 与`*(in).mem`含义相同 | 153 | | `++in, in++` | 使用元素类型所定义的`>>`运算符从流中读取下一个值。前置版本返回一个指向递增后迭代器的引用,后置版本返回旧值。 | 154 | 155 | **ostream_iterator的操作**: 156 | 157 | | 操作 | 解释 | 158 | |-----|-----| 159 | | `ostream_iterator out(os);` | `out`将类型为`T`的值写到输出流`os`中 | 160 | | `ostream_iterator out(os, d);` | `out`将类型为`T`的值写到输出流`os`中,每个值后面都输出一个`d`。`d`指向一个空字符结尾的字符数组。 | 161 | | `out = val` | 用`<<`运算符将`val`写入到`out`所绑定的`ostream`中。`val`的类型必须和`out`可写的类型兼容。 | 162 | | `*out, ++out, out++` | 这些运算符是存在的,但不对`out`做任何事情。每个运算符都返回`out`。 | 163 | 164 | ### 反向迭代器 165 | 166 | - 反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。 167 | - 对于反向迭代器,递增和递减的操作含义会颠倒。 168 | - 实现向后遍历,配合`rbegin`和`rend`。 169 | 170 | ## 泛型算法结构 171 | 172 | ### 5类迭代器 173 | 174 | | 迭代器类别 | 解释 | 支持的操作| 175 | |-----|-----|-----| 176 | | 输入迭代器 | 只读,不写;单遍扫描,只能递增 | `==`,`!=`,`++`,`*`,`->` | 177 | | 输出迭代器 | 只写,不读;单遍扫描,只能递增 | `++`,`*` | 178 | | 前向迭代器 | 可读写;多遍扫描,只能递增 | `==`,`!=`,`++`,`*`,`->` | 179 | | 双向迭代器 | 可读写;多遍扫描,可递增递减 | `==`,`!=`,`++`,`--`,`*`,`->` | 180 | | 随机访问迭代器 | 可读写,多遍扫描,支持全部迭代器运算 | `==`,`!=`,`<`,`<=`,`>`,`>=`,`++`,`--`,`+`,`+=`,`-`,`-=`,`*`,`->`,`iter[n]`==`*(iter[n])` | 181 | 182 | ### 算法的形参模式 183 | 184 | - `alg(beg, end, other args);` 185 | - `alg(beg, end, dest, other args);` 186 | - `alg(beg, end, beg2, other args);` 187 | - `alg(beg, end, beg2, end2, other args);` 188 | 189 | 其中,`alg`是算法名称,`beg`和`end`表示算法所操作的输入范围。`dest`、`beg2`、`end2`都是迭代器参数,是否使用要依赖于执行的操作。 190 | 191 | ### 算法命名规范 192 | 193 | - 一些算法使用重载形式传递一个谓词。 194 | - 接受一个元素值的算法通常有一个**不同名**的版本:加`_if`,接受一个谓词代替元素值。 195 | - 区分拷贝元素的版本和不拷贝的版本:拷贝版本通常加`_copy`。 196 | 197 | ## 特定容器算法 198 | 199 | - 对于`list`和`forward_list`,优先使用成员函数版本的算法而不是通用算法。 200 | 201 | **list和forward_list成员函数版本的算法**: 202 | 203 | | 操作 | 解释 | 204 | |-----|-----| 205 | | `lst.merge(lst2)` | 将来自`lst2`的元素合并入`lst`,二者都必须是有序的,元素将从`lst2`中删除。 | 206 | | `lst.merge(lst2, comp)` | 同上,给定比较操作。 | 207 | | `lst.remove(val)` | 调用`erase`删除掉与给定值相等(==)的每个元素 | 208 | | `lst.remove_if(pred)` | 调用`erase`删除掉令一元谓词为真的每个元素 | 209 | | `lst.reverse()` | 反转`lst`中元素的顺序 | 210 | | `lst.sort()` | 使用`<`排序元素 | 211 | | `lst.sort(comp)` | 使用给定比较操作排序元素 | 212 | | `lst.unique()` | 调用`erase`删除同一个值的连续拷贝。使用`==`。 | 213 | | `lst.unique(pred)` | 调用`erase`删除同一个值的连续拷贝。使用给定的二元谓词。 | 214 | 215 | - 上面的操作都返回`void` 216 | 217 | **list和forward_list的splice成员函数版本的参数**: 218 | 219 | | 参数 | 解释 | 220 | |-----|-----| 221 | | `(p, lst2)` | `p`是一个指向`lst`中元素的迭代器,或者一个指向`flst`首前位置的迭代器。函数将`lst2`中的所有元素移动到`lst`中`p`之前的位置或是`flst`中`p`之后的位置。将元素从`lst2`中删除。`lst2`的类型必须和`lst`相同,而且不能是同一个链表。 | 222 | | `(p, lst2, p2)` | 同上,`p2`是一个指向`lst2`中位置的有效的迭代器,将`p2`指向的元素移动到`lst`中,或将`p2`之后的元素移动到`flst`中。`lst2`可以是于`lst`或`flst`相同的链表。 | 223 | | `(p, lst2, b, e)` | `b`和`e`表示`lst2`中的合法范围。将给定范围中的元素从`lst2`移动到`lst`或`first`中。`lst2`与`lst`可以使相同的链表,但`p`不能指向给定范围中的元素。 | 224 | 225 | - 使用`lst.splice(args)`或`flst.splice_after(args)` 226 | -------------------------------------------------------------------------------- /notes/ch11.md: -------------------------------------------------------------------------------- 1 | # 第十一章 关联容器 2 | 3 | - 关联容器和顺序容器的不同:关联容器中的元素时按照**关键字**来保存和访问的。 4 | - 关联容器支持通过关键字来高效地查找和读取元素,基本的关联容器类型是 `map`和 `set`。 5 | 6 | **关联容器类型**: 7 | 8 | | 容器类型 | 解释 | 9 | |-----|-----| 10 | | 按顺序存储 | | 11 | | `map` | 关键数组:保存`关键字-值`对 | 12 | | `set` | 关键字即值,即只保存关键字的容器 | 13 | | `multimap` | 支持同一个键多次出现的`map` | 14 | | `multiset` | 支持同一个键多次出现的`set` | 15 | | 无序集合 | | 16 | | `unordered_map` | 用哈希函数组织的`map` | 17 | | `unordered_set` | 用哈希函数组织的`set` | 18 | | `unordered_multimap` | 哈希组织的`map`,关键字可以重复出现 | 19 | | `unordered_multiset` | 哈希组织的`set`,关键字可以重复出现 | 20 | 21 | ## 关联容器概述 22 | 23 | ### 定义关联容器 24 | 25 | - 需要指定元素类型。 26 | - 列表初始化: 27 | - `map`:`map word_count = {{"a", 1}, {"b", 2}};` 28 | - `set`:`set exclude = {"the", "a"};` 29 | 30 | ### 关键字类型的要求 31 | 32 | - 对于有序容器,关键字类型必须定义元素比较的方法。默认是`<`。 33 | - 如果想传递一个比较的函数,可以这样定义:`multiset bookstore(compareIsbn);` 34 | 35 | ### pair 36 | 37 | - 在`utility`头文件中定义。 38 | - 一个`pair`保存两个数据成员,两个类型不要求一样。 39 | 40 | **pair的操作**: 41 | 42 | | 操作 | 解释 | 43 | |-----|-----| 44 | | `pair p;` | `p`是一个`pair`,两个类型分别是`T1`和`T2`的成员都进行了值初始化。 | 45 | | `pair p(v1, v2);` | `first`和`second`分别用`v1`和`v2`进行初始化。 | 46 | | `pairp = {v1, v2};` | 等价于`p(v1, v2) | 47 | | `make_pair(v1, v2);` | `pair`的类型从`v1`和`v2`的类型推断出来。 | 48 | | `p.first` | 返回`p`的名为`first`的数据成员。 | 49 | | `p.second` | 返回`p`的名为`second`的数据成员。 | 50 | | `p1 relop p2` | 运算关系符按字典序定义。 | 51 | | `p1 == p2` | 必须两对元素两两相等 | 52 | | `p1 != p2` | 同上 | 53 | 54 | ## 关联容器操作 55 | 56 | **关联容器额外的类型别名**: 57 | 58 | | 类型别名 | 解释 | 59 | |-----|-----| 60 | | `key_type` | 此容器类型的关键字类型 | 61 | | `mapped_type` | 每个关键字关联的类型,只适用于`map` | 62 | | `value_type` | 对于`map`,是`pair`; 对于`set`,和`key_type`相同。 | 63 | 64 | ### 关联容器迭代器 65 | 66 | - 解引用一个关联容器迭代器时,会得到一个类型为容器的`value_type`的值的引用。 67 | - `set`的迭代器是`const`的。 68 | - 遍历关联容器:使用`begin`和`end`,遍历`map`、`multimap`、`set`、`multiset`时,迭代器按**关键字升序**遍历元素。 69 | 70 | ### 添加元素 71 | 72 | **关联容器`insert`操作**: 73 | 74 | | `insert`操作 | 关联容器 | 75 | |-----|-----| 76 | | `c.insert(v)` `c.emplace(args)` | `v`是`value_type`类型的对象;`args`用来构造一个元素。 对于`map`和`set`,只有元素的关键字不存在`c`中才插入或构造元素。函数返回一个`pair`,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的`bool`值。对于`multimap`和`multiset`则会插入范围中的每个元素。| 77 | | `c.insert(b, e)` `c.insert(il)` | `b`和`e`是迭代器,表示一个`c::value_type`类型值的范围;`il`是这种值的花括号列表。函数返回`void`。对于 `map`和`set`,只插入关键字不在`c`中的元素。 | 78 | | `c.insert(p, v)` `c.emplace(p, args)` | 类似`insert(v)`,但将迭代器`p`作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。 | 79 | 80 | 向`map`添加元素: 81 | - `word_count.insert({word, 1});` 82 | - `word_count.insert(make_pair(word, 1));` 83 | - `word_count.insert(pair(word, 1));` 84 | - `word_count.insert(map::value_type (word, 1));` 85 | 86 | ### 删除元素 87 | 88 | **从关联容器中删除元素**: 89 | 90 | | 操作 | 解释 | 91 | |-----|-----| 92 | | `c.erase(k)` | 从`c`中删除每个关键字为`k`的元素。返回一个`size_type`值,指出删除的元素的数量。 | 93 | | `c.erase(p)` | 从`c`中删除迭代器`p`指定的元素。`p`必须指向`c`中一个真实元素,不能等于`c.end()`。返回一个指向`p`之后元素的迭代器,若`p`指向`c`中的尾元素,则返回`c.end()` | 94 | | `c.erase(b, e)` | 删除迭代器对`b`和`e`所表示范围中的元素。返回`e`。 | 95 | 96 | ### 下标操作 97 | 98 | **`map`和`unordered_map`的下标操作**: 99 | 100 | | 操作 | 解释 | 101 | |-----|-----| 102 | | `c[k]` | 返回关键字为`k`的元素;如果`k`不在`c`中,添加一个关键字为`k`的元素,对其值初始化。 | 103 | | `c.at(k)` | 访问关键字为`k`的元素,带参数检查;若`k`不存在在`c`中,抛出一个`out_of_range`异常。 | 104 | 105 | ### 查找元素 106 | 107 | **在一个关联容器中查找元素**: 108 | 109 | | 操作 | 解释 | 110 | |-----|-----| 111 | | `c.find(k)` | 返回一个迭代器,指向第一个关键字为`k`的元素,若`k`不在容器中,则返回尾后迭代器 | 112 | | `c.count(k)` | 返回关键字等于`k`的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1。 | 113 | | `c.lower_bound(k)` | 返回一个迭代器,指向第一个关键字**不小于**`k`的元素。 | 114 | | `c.upper_bound(k)` | 返回一个迭代器,指向第一个关键字**大于**`k`的元素。 | 115 | | `c.equal_range(k)` | 返回一个迭代器`pair`,表示关键字等于`k`的元素的范围。若`k`不存在,`pair`的两个成员均等于`c.end()`。 | 116 | 117 | - `lower_bound`和`upper_bound`不适用于无序容器。 118 | - 下标和`at`操作只适用于非`const`的`map`和`unordered_map`。 119 | 120 | ## 无序容器 121 | 122 | - 有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的`==`运算符。 123 | - 理论上哈希技术可以获得更好的性能。 124 | - 无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。 125 | 126 | **无序容器管理操作**: 127 | 128 | | 操作 | 解释 | 129 | |-----|-----| 130 | | **桶接口** | | 131 | | `c.bucket_count()` | 正在使用的桶的数目 | 132 | | `c.max_bucket_count()` | 容器能容纳的最多的桶的数目 | 133 | | `c.bucket_size(n)` | 第`n`个桶中有多少个元素 | 134 | | `c.bucket(k)` | 关键字为`k`的元素在哪个桶中 | 135 | | **桶迭代** | | 136 | | `local_iterator` | 可以用来访问桶中元素的迭代器类型 | 137 | | `const_local_iterator` | 桶迭代器的`const`版本 | 138 | | `c.begin(n)`,`c.end(n)` | 桶`n`的首元素迭代器 | 139 | | `c.cbegin(n)`,`c.cend(n)` | 与前两个函数类似,但返回`const_local_iterator`。 | 140 | | **哈希策略** | | 141 | | `c.load_factor()` | 每个桶的平均元素数量,返回`float`值。 | 142 | | `c.max_load_factor()` | `c`试图维护的平均比桶大小,返回`float`值。`c`会在需要时添加新的桶,以使得`load_factor<=max_load_factor` | 143 | | `c.rehash(n)` | 重组存储,使得`bucket_count>=n`,且`bucket_count>size/max_load_factor` | 144 | | `c.reverse(n)` | 重组存储,使得`c`可以保存`n`个元素且不必`rehash`。 | 145 | -------------------------------------------------------------------------------- /notes/ch12.md: -------------------------------------------------------------------------------- 1 | # 第十二章 动态内存 2 | 3 | - 对象的生命周期: 4 | - 全局对象在程序启动时分配,结束时销毁。 5 | - 局部对象在进入程序块时创建,离开块时销毁。 6 | - 局部`static`对象在第一次使用前分配,在程序结束时销毁。 7 | - 动态分配对象:只能显式地被释放。 8 | 9 | - 对象的内存位置: 10 | - **静态内存**用来保存局部`static`对象、类`static`对象、定义在任何函数之外的变量。 11 | - **栈内存**用来保存定义在函数内的非`static`对象。 12 | - **堆内存**,又称自由空间,用来存储**动态分配**的对象。 13 | 14 | ## 动态内存与智能指针 15 | 16 | - 动态内存管理: 17 | - `new`:在动态内存中为对象分配空间并返回一个指向该对象的指针。 18 | - `delete`:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。 19 | - 智能指针: 20 | - 管理动态对象。 21 | - 行为类似常规指针。 22 | - 负责自动释放所指向的对象。 23 | - 智能指针也是模板。 24 | 25 | ### shared_ptr类 26 | 27 | **shared_ptr和unique_ptr都支持的操作**: 28 | 29 | | 操作 | 解释 | 30 | |-----|-----| 31 | | `shared_ptr sp` `unique_ptr up` | 空智能指针,可以指向类型是`T`的对象 | 32 | | `p` | 将`p`用作一个条件判断,若`p`指向一个对象,则为`true` | 33 | | `*p` | 解引用`p`,获得它指向的对象。 | 34 | | `p->mem` | 等价于`(*p).mem` | 35 | | `p.get()` | 返回`p`中保存的指针,要小心使用,若智能指针释放了对象,返回的指针所指向的对象也就消失了。 | 36 | | `swap(p, q)` `p.swap(q)` | 交换`p`和`q`中的指针 | 37 | 38 | **shared_ptr独有的操作**: 39 | 40 | | 操作 | 解释 | 41 | |-----|-----| 42 | | `make_shared(args)` | 返回一个`shared_ptr`,指向一个动态分配的类型为`T`的对象。使用`args`初始化此对象。 | 43 | | `shared_ptrp(q)` | `p`是`shared_ptr q`的拷贝;此操作会**递增**`q`中的计数器。`q`中的指针必须能转换为`T*` | 44 | | `p = q` | `p`和`q`都是`shared_ptr`,所保存的指针必须能互相转换。此操作会**递减**`p`的引用计数,**递增**`q`的引用计数;若`p`的引用计数变为0,则将其管理的原内存释放。 | 45 | | `p.unique()` | 若`p.use_count()`是1,返回`true`;否则返回`false` | 46 | | `p.use_count()` | 返回与`p`共享对象的智能指针数量;可能很慢,主要用于调试。 | 47 | 48 | - **使用动态内存的三种原因**: 49 | - 程序不知道自己需要使用多少对象(比如容器类)。 50 | - 程序不知道所需要对象的准确类型。 51 | - 程序需要在多个对象间共享数据。 52 | 53 | ### 直接管理内存 54 | 55 | - 用`new`动态分配和初始化对象。 56 | - `new`无法为分配的对象命名(因为自由空间分配的内存是无名的),因此是返回一个指向该对象的指针。 57 | - `int *pi = new int(123);` 58 | - 一旦内存耗尽,会抛出类型是`bad_alloc`的异常。 59 | - 用`delete`将动态内存归还给系统。 60 | - 接受一个指针,指向要释放的对象。 61 | - `delete`后的指针称为空悬指针(dangling pointer)。 62 | - 使用`new`和`delete`管理动态内存存在三个常见问题: 63 | - 1.忘记`delete`内存。 64 | - 2.使用已经释放掉的对象。 65 | - 3.同一块内存释放两次。 66 | - 坚持只使用智能指针可以避免上述所有问题。 67 | 68 | ### shared_ptr和new结合使用 69 | 70 | **定义和改变shared_ptr的其他方法**: 71 | 72 | | 操作 | 解释 | 73 | |-----|-----| 74 | | `shared_ptr p(q)` | `p`管理内置指针`q`所指向的对象;`q`必须指向`new`分配的内存,且能够转换为`T*`类型 | 75 | | `shared_ptr p(u)` | `p`从`unique_ptr u`那里接管了对象的所有权;将`u`置为空 | 76 | | `shared_ptr p(q, d)` | `p`接管了内置指针`q`所指向的对象的所有权。`q`必须能转换为`T*`类型。`p`将使用可调用对象`d`来代替`delete`。 | 77 | | `shared_ptr p(p2, d)` | `p`是`shared_ptr p2`的拷贝,唯一的区别是`p`将可调用对象`d`来代替`delete`。 | 78 | | `p.reset()` | 若`p`是唯一指向其对象的`shared_ptr`,`reset`会释放此对象。若传递了可选的参数内置指针`q`,会令`p`指向`q`,否则会将`p`置空。若还传递了参数`d`,则会调用`d`而不是`delete`来释放`q`。 | 79 | | `p.reset(q)` | 同上 | 80 | | `p.reset(q, d)` | 同上 | 81 | 82 | ### 智能指针和异常 83 | 84 | - 如果使用智能指针,即使程序块由于异常过早结束,智能指针类也能确保在内存不需要的时候将其释放。 85 | - **智能指针陷阱**: 86 | - 不用相同的内置指针初始化(或`reset`)多个智能指针 87 | - 不`delete get()`返回的指针。 88 | - 如果你使用`get()`返回的指针,记得当最后一个对应的智能指针销毁后,你的指针就无效了。 89 | - 如果你使用智能指针管理的资源不是`new`分配的内存,记住传递给它一个删除器。 90 | 91 | ### unique_ptr 92 | 93 | - 某一个时刻只能有一个`unique_ptr`指向一个给定的对象。 94 | - 不支持拷贝或者赋值操作。 95 | - 向后兼容:`auto_ptr`:老版本,具有`unique_ptr`的部分特性。特别是,不能在容器中保存`auto_ptr`,也不能从函数返回`auto_ptr`。 96 | 97 | **unique_ptr操作**: 98 | 99 | | 操作 | 解释 | 100 | |-----|-----| 101 | | `unique_ptr u1` | 空`unique_ptr`,可以指向类型是`T`的对象。`u1`会使用`delete`来是释放它的指针。 | 102 | | `unique_ptr u2` | `u2`会使用一个类型为`D`的可调用对象来释放它的指针。 | 103 | | `unique_ptr u(d)` | 空`unique_ptr`,指向类型为`T`的对象,用类型为`D`的对象`d`代替`delete` | 104 | | `u = nullptr` | 释放`u`指向的对象,将`u`置为空。 | 105 | | `u.release()` | `u`放弃对指针的控制权,返回指针,并将`u`置空。 | 106 | | `u.reset()` | 释放`u`指向的对象 | 107 | | `u.reset(q)` | 令`u`指向`q`指向的对象 | 108 | | `u.reset(nullptr)` | 将`u`置空 | 109 | 110 | ### weak_ptr 111 | 112 | - `weak_ptr`是一种不控制所指向对象生存期的智能指针。 113 | - 指向一个由`shared_ptr`管理的对象,不改变`shared_ptr`的引用计数。 114 | - 一旦最后一个指向对象的`shared_ptr`被销毁,对象就会被释放,不管有没有`weak_ptr`指向该对象。 115 | 116 | **weak_ptr操作**: 117 | 118 | | 操作 | 解释 | 119 | |-----|-----| 120 | | `weak_ptr w` | 空`weak_ptr`可以指向类型为`T`的对象 | 121 | | `weak_ptr w(sp)` | 与`shared_ptr`指向相同对象的`weak_ptr`。`T`必须能转换为`sp`指向的类型。 | 122 | | `w = p` | `p`可以是`shared_ptr`或一个`weak_ptr`。赋值后`w`和`p`共享对象。 | 123 | | `w.reset()` | 将`w`置为空。 | 124 | | `w.use_count()` | 与`w`共享对象的`shared_ptr`的数量。 | 125 | | `w.expired()` | 若`w.use_count()`为0,返回`true`,否则返回`false` | 126 | | `w.lock()` | 如果`expired`为`true`,则返回一个空`shared_ptr`;否则返回一个指向`w`的对象的`shared_ptr`。 | 127 | 128 | ## 动态数组 129 | 130 | ### new和数组 131 | 132 | - `new`一个动态数组: 133 | - 类型名之后加一对方括号,指明分配的对象数目(必须是整型,不必是常量)。 134 | - 返回**指向第一个对象的指针**。 135 | - `int *p = new int[size];` 136 | 137 | - `delete`一个动态数组: 138 | - `delete [] p;` 139 | 140 | - `unique_ptr`和数组: 141 | - 指向数组的`unique_ptr`不支持成员访问运算符(点和箭头)。 142 | 143 | | 操作 | 解释 | 144 | |-----|-----| 145 | | `unique_ptr u` | `u`可以指向一个动态分配的数组,整数元素类型为`T` | 146 | | `unique_ptr u(p)` | `u`指向内置指针`p`所指向的动态分配的数组。`p`必须能转换为类型`T*`。 | 147 | | `u[i]` | 返回`u`拥有的数组中位置`i`处的对象。`u`必须指向一个数组。 | 148 | 149 | ### allocator类 150 | 151 | - 标准库`allocator`类定义在头文件`memory`中,帮助我们将内存分配和对象构造分离开。 152 | - 分配的是原始的、未构造的内存。 153 | - `allocator`是一个模板。 154 | - `allocator alloc;` 155 | 156 | **标准库allocator类及其算法**: 157 | 158 | | 操作 | 解释 | 159 | |-----|-----| 160 | | `allocator a` | 定义了一个名为`a`的`allocator`对象,它可以为类型为`T`的对象分配内存 | 161 | | `a.allocate(n)` | 分配一段原始的、未构造的内存,保存`n`个类型为`T`的对象。 | 162 | | `a.deallocate(p, n)` | 释放从`T*`指针`p`中地址开始的内存,这块内存保存了`n`个类型为`T`的对象;`p`必须是一个先前由`allocate`返回的指针。且`n`必须是`p`创建时所要求的大小。在调用`deallocate`之前,用户必须对每个在这块内存中创建的对象调用`destroy`。 | 163 | | `a.construct(p, args)` | `p`必须是一个类型是`T*`的指针,指向一块原始内存;`args`被传递给类型为`T`的构造函数,用来在`p`指向的内存中构造一个对象。 | 164 | | `a.destroy(p)` | `p`为`T*`类型的指针,此算法对`p`指向的对象执行析构函数。 | 165 | 166 | **allocator伴随算法**: 167 | 168 | | 操作 | 解释 | 169 | |-----|-----| 170 | | `uninitialized_copy(b, e, b2)` | 从迭代器`b`和`e`给定的输入范围中拷贝元素到迭代器`b2`指定的未构造的原始内存中。`b2`指向的内存必须足够大,能够容纳输入序列中元素的拷贝。 | 171 | | `uninitialized_copy_n(b, n, b2)` | 从迭代器`b`指向的元素开始,拷贝`n`个元素到`b2`开始的内存中。 | 172 | | `uninitialized_fill(b, e, t)` | 在迭代器`b`和`e`执行的原始内存范围中创建对象,对象的值均为`t`的拷贝。 | 173 | | `uninitialized_fill_n(b, n, t)` | 从迭代器`b`指向的内存地址开始创建`n`个对象。`b`必须指向足够大的未构造的原始内存,能够容纳给定数量的对象。 | 174 | 175 | - 定义在头文件`memory`中。 176 | - 在给定目的位置创建元素,而不是由系统分配内存给他们。 177 | -------------------------------------------------------------------------------- /notes/ch13.md: -------------------------------------------------------------------------------- 1 | # 第十三章 拷贝控制 2 | 3 | **拷贝控制操作**(copy control): 4 | 5 | - 拷贝构造函数(copy constructor) 6 | - 拷贝赋值运算符(copy-assignment operator) 7 | - 移动构造函数(move constructor) 8 | - 移动赋值函数(move-assignement operator) 9 | - 析构函数(destructor) 10 | 11 | ## 拷贝、赋值和销毁 12 | 13 | ### 拷贝构造函数 14 | 15 | - 如果一个构造函数的第一个参数是**自身类类型的引用**,且任何额外参数都有默认值,则此构造函数是**拷贝构造函数**。 16 | - `class Foo{ public: Foo(const Foo&); }` 17 | - **合成的拷贝构造函数**(synthesized copy constructor):会将参数的成员逐个拷贝到正在创建的对象中。 18 | - **拷贝初始化**: 19 | - 将右侧运算对象拷贝到正在创建的对象中,如果需要,还需进行类型转换。 20 | - 通常使用拷贝构造函数完成。 21 | - `string book = "9-99";` 22 | - 出现场景: 23 | - 用`=`定义变量时。 24 | - 将一个对象作为实参传递给一个非引用类型的形参。 25 | - 从一个返回类型为非引用类型的函数返回一个对象。 26 | - 用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。 27 | 28 | ### 拷贝赋值运算符 29 | 30 | - **重载赋值运算符**: 31 | - 重写一个名为`operator=`的函数. 32 | - 通常返回一个指向其左侧运算对象的引用。 33 | - `Foo& operator=(const Foo&);` 34 | - **合成拷贝赋值运算符**: 35 | - 将右侧运算对象的每个非`static`成员赋予左侧运算对象的对应成员。 36 | 37 | ### 析构函数 38 | 39 | - 释放对象所使用的资源,并销毁对象的非`static`数据成员。 40 | - 名字由波浪号接类名构成。没有返回值,也不接受参数。 41 | - `~Foo();` 42 | - 调用时机: 43 | - 变量在离开其作用域时。 44 | - 当一个对象被销毁时,其成员被销毁。 45 | - 容器被销毁时,其元素被销毁。 46 | - 动态分配的对象,当对指向它的指针应用`delete`运算符时。 47 | - 对于临时对象,当创建它的完整表达式结束时。 48 | - **合成析构函数**: 49 | - 空函数体执行完后,**成员会被自动销毁。** 50 | - 注意:析构函数体本身并不直接销毁成员。 51 | 52 | ### 三/五法则 53 | 54 | - 需要析构函数的类也需要拷贝和赋值操作。 55 | - 需要拷贝操作的类也需要赋值操作,反之亦然。 56 | 57 | ### 使用=default 58 | 59 | - 可以通过将拷贝控制成员定义为`=default`来显式地要求编译器生成合成的版本。 60 | - 合成的函数将隐式地声明为内联的。 61 | 62 | ### 阻止拷贝 63 | 64 | - 大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符,无论是隐式地还是显式地。 65 | - 定义删除的函数:`=delete`。 66 | - 虽然声明了它们,但是不能以任何方式使用它们。 67 | - 析构函数不能是删除的成员。 68 | - 如果一个类有数据成员不能默认构造、拷贝、复制或者销毁,则对应的成员函数将被定义为删除的。 69 | - 老版本使用`private`声明来阻止拷贝。 70 | 71 | ## 拷贝控制和资源管理 72 | 73 | - 类的行为可以像一个值,也可以像一个指针。 74 | - 行为像值:对象有自己的状态,副本和原对象是完全独立的。 75 | - 行为像指针:共享状态,拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。 76 | 77 | ## 交换操作 78 | 79 | - 管理资源的类通常还定义一个名为`swap`的函数。 80 | - 经常用于重排元素顺序的算法。 81 | - 用`swap`而不是`std::swap`。 82 | 83 | ## 对象移动 84 | 85 | - 很多拷贝操作后,原对象会被销毁,因此引入移动操作可以大幅度提升性能。 86 | - 在新标准中,我们可以用容器保存不可拷贝的类型,只要它们可以被移动即可。 87 | - 标准库容器、`string`和`shared_ptr`类既可以支持移动也支持拷贝。`IO`类和`unique_ptr`类可以移动但不能拷贝。 88 | 89 | ### 右值引用 90 | 91 | - 新标准引入右值引用以支持移动操作。 92 | - 通过`&&`获得右值引用。 93 | - 只能绑定到一个将要销毁的对象。 94 | - 常规引用可以称之为左值引用。 95 | - 左值持久,右值短暂。 96 | 97 | **move函数**: 98 | 99 | - `int &&rr2 = std::move(rr1);` 100 | - `move`告诉编译器,我们有一个左值,但我希望像右值一样处理它。 101 | - 调用`move`意味着:除了对`rr1`赋值或者销毁它外,我们将不再使用它。 102 | 103 | ### 移动构造函数和移动赋值运算符 104 | 105 | - **移动构造函数**: 106 | - 第一个参数是该类类型的一个引用,关键是,这个引用参数是一个**右值引用**。 107 | - `StrVec::StrVec(StrVec &&s) noexcept{}` 108 | - 不分配任何新内存,只是接管给定的内存。 109 | - **移动赋值运算符**: 110 | - `StrVec& StrVec::operator=(StrVec && rhs) noexcept{}` 111 | - 移动右值,拷贝左值。 112 | - 如果没有移动构造函数,右值也被拷贝。 113 | - 更新三/五法则:如果一个类定义了任何一个拷贝操作,它就应该定义所有五个操作。 114 | - 移动迭代器: 115 | - `make_move_iterator`函数讲一个普通迭代器转换为一个移动迭代器。 116 | - 建议:小心地使用移动操作,以获得性能提升。 117 | 118 | ### 右值引用和成员函数 119 | 120 | - 区分移动和拷贝的重载函数通常有一个版本接受一个`const T&`,而另一个版本接受一个`T&&`。 121 | - 引用限定符: 122 | - 在参数列表后面防止一个`&`,限定只能向可修改的左值赋值而不能向右值赋值。 123 | 124 | -------------------------------------------------------------------------------- /notes/ch14.md: -------------------------------------------------------------------------------- 1 | # 第十四章 重载运算与类型转换 2 | 3 | ## 基本概念 4 | 5 | - 重载运算符是具有特殊名字的函数:由关键字`operator`和其后要定义的运算符号共同组成。 6 | - 当一个重载的运算符是成员函数时,`this`绑定到左侧运算对象。动态运算符符函数的参数数量比运算对象的数量**少一个**。 7 | - 只能重载大多数的运算符,而不能发明新的运算符号。 8 | - 重载运算符的优先级和结合律跟对应的内置运算符保持一致。 9 | - 调用方式: 10 | - `data1 + data2;` 11 | - `operator+(data1, data2);` 12 | - 是否是成员函数: 13 | - 赋值(`=`)、下标(`[]`)、调用(`()`)和成员访问箭头(`->`)运算符必须是成员。 14 | - 复合赋值运算符一般来说是成员。 15 | - 改变对象状态的运算符或者和给定类型密切相关的运算符通常是成员,如递增、解引用。 16 | - 具有对称性的运算符如算术、相等性、关系和位运算符等,通常是非成员函数。 17 | 18 | **运算符**: 19 | 20 | | 可以被重载 | 不可以被重载 | 21 | |-----|-----| 22 | | `+`, `-`, `*`, `/`, `%`, `^` | `::`, `.*`, `.`, `? :`, | 23 | | `&`, `|`, `~`, `!`, `,`, `=` | | 24 | | `<`, `>`, `<=`, `>=`, `++`, `--` | | 25 | | `<<`, `>>`, `==`, `!=`, `&&`, `||` | | 26 | | `+=`, `-=`, `/=`, `%=`, `^=`, `&=` | | 27 | | |=, `*=`, `<<=`, `>>=`, `[]`, `()` | | 28 | | `->`, `->*`, `new`, `new[]`, `delete`, `delete[]` | | 29 | 30 | ## 输入和输出运算符 31 | 32 | ### 重载输出运算符<< 33 | 34 | - 第一个形参通常是一个非常量的`ostream`对象的引用。非常量是因为向流中写入会改变其状态;而引用是因为我们无法复制一个`ostream`对象。 35 | - 输入输出运算符必须是非成员函数。 36 | 37 | ### 重载输入运算符>> 38 | 39 | - 第一个形参通常是运算符将要读取的流的引用,第二个形参是将要读取到的(非常量)对象的引用。 40 | - 输入运算符必须处理输入可能失败的情况,而输出运算符不需要。 41 | 42 | ## 算数和关系运算符(+、-、*、/) 43 | 44 | - 如果类同时定义了算数运算符和相关的复合赋值运算符,则通常情况下应该使用复合赋值来实现算数运算符。 45 | 46 | ### 相等运算符== 47 | 48 | - 如果定义了`operator==`,则这个类也应该定义`operator!=`。 49 | - 相等运算符和不等运算符的一个应该把工作委托给另一个。 50 | - 相等运算符应该具有传递性。 51 | - 如果某个类在逻辑上有相等性的含义,则该类应该定义`operator==`,这样做可以使用户更容易使用标准库算法来处理这个类。 52 | 53 | ### 关系运算符 54 | 55 | - 如果存在唯一一种逻辑可靠的`<`定义,则应该考虑为这个类定义`<`运算符。如果同时还包含`==`,则当且晋档`<`的定义和`++`产生的结果一直时才定义`<`运算符。 56 | 57 | ## 赋值运算符= 58 | 59 | - 我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。 60 | - 赋值运算符必须定义成类的成员,复合赋值运算符通常情况下也应该这么做。这两类运算符都应该返回左侧运算对象的引用。 61 | 62 | ## 下标运算符[] 63 | 64 | - 下标运算符必须是成员函数。 65 | - 一般会定义两个版本: 66 | - 1.返回普通引用。 67 | - 2.类的常量成员,并返回常量引用。 68 | 69 | ## 递增和递减运算符(++、--) 70 | 71 | - 定义递增和递减运算符的类应该同时定义前置版本和后置版本。 72 | - 通常应该被定义成类的成员。 73 | - 为了和内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。 74 | - 同样为了和内置版本保持一致,后置运算符应该返回递增或递减前对象的值,而不是引用。 75 | - 后置版本接受一个额外的,不被使用的`int`类型的形参。因为不会用到,所以无需命名。 76 | 77 | ## 成员访问运算符(*、->) 78 | 79 | - 箭头运算符必须是类的成员。解引用运算符通常也是类的成员,尽管并非必须如此。 80 | - 重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。 81 | - 解引用和乘法的区别是一个是一元运算符,一个是二元运算符。 82 | 83 | ## 函数调用运算符 84 | 85 | - 可以像使用函数一样,调用该类的对象。因为这样对待类同时也能存储状态,所以与普通函数相比更加灵活。 86 | - 函数调用运算符必须是成员函数。 87 | - 一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。 88 | - 如果累定义了调用运算符,则该类的对象称作**函数对象**。 89 | 90 | ### `lambda`是函数对象 91 | 92 | - `lambda`捕获变量:`lambda`产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数。 93 | 94 | ### 标准库定义的函数对象 95 | 96 | **标准库函数对象**: 97 | 98 | | 算术 | 关系 | 逻辑 | 99 | |-----|-----|-----| 100 | | `plus` | `equal_to` | `logical_and` | 101 | | `minus` | `not_equal_to` | `logical_or` | 102 | | `multiplies` | `greater` | `logical_not` | 103 | | `divides` | `greater_equal` | | 104 | | `modulus` | `less` | | 105 | | `negate` | `less_equal` | | 106 | 107 | - 可以在算法中使用标准库函数对象。 108 | 109 | ### 可调用对象与function 110 | 111 | **标准库function类型**: 112 | 113 | | 操作 | 解释 | 114 | |-----|-----| 115 | | `function f;` | `f`是一个用来存储可调用对象的空`function`,这些可调用对象的调用形式应该与类型`T`相同。 | 116 | | `function f(nullptr);` | 显式地构造一个空`function` | 117 | | `function f(obj)` | 在`f`中存储可调用对象`obj`的副本 | 118 | | `f` | 将`f`作为条件:当`f`含有一个可调用对象时为真;否则为假。 | 119 | | 定义为`function`的成员的类型 | | 120 | | `result_type` | 该`function`类型的可调用对象返回的类型 | 121 | | `argument_type` | 当`T`有一个或两个实参时定义的类型。如果`T`只有一个实参,则`argument_type` | 122 | | `first_argument_type` | 第一个实参的类型 | 123 | | `second_argument_type` | 第二个实参的类型 | 124 | 125 | - 例如:声明一个`function`类型,它可以表示接受两个`int`,返回一个`int`的可调用对象。`function` 126 | 127 | ## 重载、类型转换、运算符 128 | 129 | ### 类型转换运算符 130 | 131 | - 类型转换运算符是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。类型转换函数的一般形式如下:`operator type() const;` 132 | - 一个类型转换函数必须是类的成员函数;它不能声明返回类型,形参列表也必须为空。类型转换函数通常应该是`const`。 133 | - 避免过度使用类型转换函数。 134 | - C++11引入了显式的类型转换运算符。 135 | - 向`bool`的类型转换通常用在条件部分,因此`operator bool`一般定义成`explicit`的。 136 | 137 | ### 避免有二义性的类型转换 138 | 139 | - 通常,不要为类第几个亿相同的类型转换,也不要在类中定义两个及以上转换源或转换目标是算术类型的转换。 140 | - 在调用重载函数时,如果需要额外的标准类型转换,则该转换的级别只有当所有可行函数都请求同一个用户定义的类型转换时才有用。如果所需的用户定义的类型转换不止一个,则该调用具有二义性。 141 | 142 | ### 函数匹配与重载运算符 143 | 144 | - 如果`a`是一种类型,则表达式`a sym b`可能是: 145 | - `a.operatorsym(b);` 146 | - `operatorsym(a,b);` 147 | - 如果我们队同一个类既提供了转换目标是算术类型的类型转换,也提供了重载的运算符,则将会遇到重载运算符与内置运算符的二义性问题。 148 | -------------------------------------------------------------------------------- /notes/ch15.md: -------------------------------------------------------------------------------- 1 | # 第十五章 面向对象程序设计 2 | 3 | ## OOP:概述 4 | 5 | - 面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承和动态绑定。 6 | - **继承**(inheritance): 7 | - 通过继承联系在一起的类构成一种层次关系。 8 | - 通常在层次关系的根部有一个**基类**(base class)。 9 | - 其他类直接或者简介从基类继承而来,这些继承得到的类成为**派生类**(derived class)。 10 | - 基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。 11 | - 对于某些函数,基类希望它的派生类个自定义适合自己的版本,此时基类就将这些函数声明成**虚函数**(virtual function)。 12 | - 派生类必须通过使用**类派生列表**(class derivation list)明确指出它是从哪个基类继承而来。形式:一个冒号,后面紧跟以逗号分隔的基类列表,每个基类前都可以有访问说明符。`class Bulk_quote : public Quote{};` 13 | - 派生类必须在其内部对所有重新定义的虚函数进行声明。可以在函数之前加上`virtual`关键字,也可以不加。C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数,即在函数的形参列表之后加一个`override`关键字。 14 | - **动态绑定**(dynamic binding,又称运行时绑定): 15 | - 使用同一段代码可以分别处理基类和派生类的对象。 16 | - 函数的运行版本由实参决定,即在运行时选择函数的版本。 17 | 18 | ## 定义基类和派生类 19 | 20 | ### 定义基类 21 | 22 | - 基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。 23 | - 基类通过在其成员函数的声明语句前加上关键字`virtual`使得该函数执行**动态绑定**。 24 | - 如果成员函数没有被声明为虚函数,则解析过程发生在编译时而非运行时。 25 | - 访问控制: 26 | - `protected` : 基类和和其派生类还有友元可以访问。 27 | - `private` : 只有基类本身和友元可以访问。 28 | 29 | ### 定义派生类 30 | 31 | - 派生类必须通过类派生列表(class derivation list)明确指出它是从哪个基类继承而来。形式:冒号,后面紧跟以逗号分隔的基类列表,每个基类前面可以有一下三种访问说明符的一个:`public`、`protected`、`private`。 32 | - C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数,即在函数的形参列表之后加一个`override`关键字。 33 | - 派生类构造函数:派生类必须使用基类的构造函数去初始化它的基类部分。 34 | - 静态成员:如果基类定义了一个基类成员,则在整个继承体系中只存在该成员的唯一定义。 35 | - 派生类的声明:声明中不包含它的派生列表。 36 | - C++11新标准提供了一种防止继承的方法,在类名后面跟一个关键字`final`。 37 | 38 | ### 类型转换与继承 39 | 40 | - 理解基类和派生类之间的类型抓换是理解C++语言面向对象编程的关键所在。 41 | - 可以将基类的指针或引用绑定到派生类对象上。 42 | - 不存在从基类向派生类的隐式类型转换。 43 | - 派生类向基类的自动类型转换只对指针或引用类型有效,对象之间不存在类型转换。 44 | 45 | ## 虚函数 46 | 47 | - 使用虚函数可以执行动态绑定。 48 | - OOP的核心思想是多态性(polymorphism)。 49 | - 当且仅当对通过指针或引用调用虚函数时,才会在运行时解析该调用,也只有在这种情况下对象的动态类型才有可能与静态类型不同。 50 | - 派生类必须在其内部对所有重新定义的虚函数进行声明。可以在函数之前加上`virtual`关键字,也可以不加。 51 | - C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数,即在函数的形参列表之后加一个`override`关键字。 52 | - 如果我们想覆盖某个虚函数,但不小心把形参列表弄错了,这个时候就不会覆盖基类中的虚函数。加上`override`可以明确程序员的意图,让编译器帮忙确认参数列表是否出错。 53 | - 如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致。 54 | - 通常,只有成员函数(或友元)中的代码才需要使用**作用域运算符**(`::`)来回避虚函数的机制。 55 | 56 | ## 抽象基类 57 | 58 | - **纯虚函数**(pure virtual):清晰地告诉用户当前的函数是没有实际意义的。纯虚函数无需定义,只用在函数体的位置前书写`=0`就可以将一个虚函数说明为纯虚函数。 59 | - 含有纯虚函数的类是**抽象基类**(abstract base class)。不能创建抽象基类的对象。 60 | 61 | ## 访问控制与继承 62 | 63 | - 受保护的成员: 64 | - `protected`说明符可以看做是`public`和`private`中的产物。 65 | - 类似于私有成员,受保护的成员对类的用户来说是不可访问的。 66 | - 类似于公有成员,受保护的成员对于派生类的成员和友元来说是可访问的。 67 | - 派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对于一个基类对象中的受保护成员没有任何访问特权。 68 | - 派生访问说明符: 69 | - 对于派生类的成员(及友元)能否访问其直接积累的成员没什么影响。 70 | - 派生访问说明符的目的是:控制派生类用户对于基类成员的访问权限。比如`struct Priv_Drev: private Base{}`意味着在派生类`Priv_Drev`中,从`Base`继承而来的部分都是`private`的。 71 | - 友元关系不能继承。 72 | - 改变个别成员的可访问性:使用`using`。 73 | - 默认情况下,使用`class`关键字定义的派生类是私有继承的;使用`struct`关键字定义的派生类是公有继承的。 74 | 75 | ## 继承中的类作用域 76 | 77 | - 每个类定义自己的作用域,在这个作用域内我们定义类的成员。当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内。 78 | - 派生类的成员将隐藏同名的基类成员。 79 | - 除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字。 80 | 81 | ## 构造函数与拷贝控制 82 | 83 | ### 虚析构函数 84 | 85 | - 基类通常应该定义一个虚析构函数,这样我们就能动态分配继承体系中的对象了。 86 | - 如果基类的析构函数不是虚函数,则`delete`一个指向派生类对象的基类指针将产生未定义的行为。 87 | - 虚析构函数将阻止合成移动操作。 88 | 89 | ### 合成拷贝控制与继承 90 | 91 | - 基类或派生类的合成拷贝控制成员的行为和其他合成的构造函数、赋值运算符或析构函数类似:他们对类本身的成员依次进行初始化、赋值或销毁的操作。 92 | 93 | ### 派生类的拷贝控制成员 94 | 95 | - 当派生类定义了拷贝或移动操作时,该操作负责拷贝或移动包括基类部分成员在内的整个对象。 96 | - 派生类析构函数:派生类析构函数先执行,然后执行基类的析构函数。 97 | 98 | ### 继承的构造函数 99 | 100 | - C++11新标准中,派生类可以重用其直接基类定义的构造函数。 101 | - 如`using Disc_quote::Disc_quote;`,注明了要继承`Disc_quote`的构造函数。 102 | 103 | ## 容器与继承 104 | 105 | - 当我们使用容器存放继承体系中的对象时,通常必须采用间接存储的方式。 106 | - 派生类对象直接赋值给积累对象,其中的派生类部分会被切掉。 107 | - 在容器中放置(智能)指针而非对象。 108 | - 对于C++面向对象的编程来说,一个悖论是我们无法直接使用对象进行面向对象编程。相反,我们必须使用指针和引用。因为指针会增加程序的复杂性,所以经常定义一些辅助的类来处理这些复杂的情况。 109 | 110 | 111 | ## 文本查询程序再探 112 | 113 | - 使系统支持:单词查询、逻辑非查询、逻辑或查询、逻辑与查询。 114 | 115 | ### 面向对象的解决方案 116 | 117 | - 将几种不同的查询建模成相互独立的类,这些类共享一个公共基类: 118 | - `WordQuery` 119 | - `NotQuery` 120 | - `OrQuery` 121 | - `AndQuery` 122 | - 这些类包含两个操作: 123 | - `eval`:接受一个`TextQuery`对象并返回一个`QueryResult`。 124 | - `rep`:返回基础查询的`string`表示形式。 125 | - 继承和组合: 126 | - 当我们令一个类公有地继承另一个类时,派生类应当反映与基类的“是一种(Is A)”的关系。 127 | - 类型之间另一种常见的关系是“有一个(Has A)”的关系。 128 | - 对于面向对象编程的新手来说,想要理解一个程序,最困难的部分往往是理解程序的设计思路。一旦掌握了设计思路,接下来的实现也就水到渠成了。 129 | 130 | **Query程序设计**: 131 | 132 | | 操作 | 解释 | 133 | |-----|-----| 134 | | `Query`程序接口类和操作 | | 135 | | `TextQuery` | 该类读入给定的文件并构建一个查找图。包含一个`query`操作,它接受一个`string`实参,返回一个`QueryResult`对象;该`QueryResult`对象表示`string`出现的行。 | 136 | | `QueryResult` | 该类保存一个`query`操作的结果。 | 137 | | `Query` | 是一个接口类,指向`Query_base`派生类的对象。 | 138 | | `Query q(s)` | 将`Query`对象`q`绑定到一个存放着`string s`的新`WordQuery`对象上。 | 139 | | `q1 & q2` | 返回一个`Query`对象,该`Query`绑定到一个存放`q1`和`q2`的新`AndQuery`对象上。 | 140 | | `q1 | q2` | 返回一个`Query`对象,该`Query`绑定到一个存放`q1`和`q2`的新`OrQuery`对象上。 | 141 | | `~q` | 返回一个`Query`对象,该`Query`绑定到一个存放`q`的新`NotQuery`对象上。 | 142 | | `Query`程序实现类 | | 143 | | `Query_base` | 查询类的抽象基类 | 144 | | `WordQuery` | `Query_base`的派生类,用于查找一个给定的单词 | 145 | | `NotQuery` | `Query_base`的派生类,用于查找一个给定的单词 | 146 | | `BinaryQuery` | `Query_base`的派生类,查询结果是`Query`运算对象没有出现的行的集合 | 147 | | `OrQuery` | `Query_base`的派生类,返回它的两个运算对象分别出现的行的并集 | 148 | | `AndQuery` | `Query_base`的派生类,返回它的两个运算对象分别出现的行的交集 | 149 | -------------------------------------------------------------------------------- /notes/ch16.md: -------------------------------------------------------------------------------- 1 | # 第十六章 模板和泛型编程 2 | 3 | - 面向对象编程和泛型编程都能处理在编写程序时不知道类型的情况。 4 | - OOP能处理类型在程序运行之前都未知的情况; 5 | - 泛型编程中,在编译时就可以获知类型。 6 | 7 | ## 定义模板 8 | 9 | - **模板**:模板是泛型编程的基础。一个模板就是一个创建类或函数的蓝图或者说公式。 10 | 11 | ### 函数模板 12 | 13 | - `template int compare(const T &v1, const T &v2){}` 14 | - 模板定义以关键字 `template`开始,后接**模板形参表**,模板形参表是用**尖括号**`<>`括住的一个或多个**模板形参**的列表,用逗号分隔,**不能为空**。 15 | - 使用模板时,我们显式或隐式地指定模板实参,将其绑定到模板参数上。 16 | - 模板类型参数:类型参数前必须使用关键字`class`或者`typename`,这两个关键字含义相同,可以互换使用。旧的程序只能使用`class`。 17 | - 非类型模板参数:表示一个值而非一个类型。实参必须是常量表达式。`template void array_init(T (&parm)[N]){}` 18 | - 内联函数模板: `template inline T min(const T&, const T&);` 19 | - 模板程序应该尽量减少对实参类型的要求。 20 | - 函数模板和类模板成员函数的定义通常放在头文件中。 21 | 22 | ### 类模板 23 | 24 | - 类模板用于生成类的蓝图。 25 | - 不同于函数模板,编译器不能推断模板参数类型。 26 | - **定义类模板**: 27 | - `template class Queue {};` 28 | - 实例化类模板:提供显式模板实参列表,来实例化出特定的类。 29 | - 一个类模板中所有的实例都形成一个独立的类。 30 | - **模板形参作用域**:模板形参的名字可以在声明为模板形参之后直到模板声明或定义的末尾处使用。 31 | - 类模板的成员函数: 32 | - `template ret-type Blob::member-name(parm-list)` 33 | - 默认情况下,对于一个实例化了的类模板,其成员只有在使用时才被实例化。 34 | - 新标准允许模板将自己的类型参数成为友元。`template class Bar{friend T;};`。 35 | - 模板类型别名:因为模板不是一个类型,因此无法定义一个`typedef`引用一个模板,但是新标准允许我们为类模板定义一个类型别名:`template using twin = pair;` 36 | 37 | ### 模板参数 38 | 39 | - 模板参数与作用域:一个模板参数名的可用范围是在声明之后,至模板声明或定义结束前。 40 | - 一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置。 41 | - 当我们希望通知编译器一个名字表示类型时,必须使用关键字`typename`,而不能使用`class`。 42 | - 默认模板实参:`template class Numbers{}` 43 | 44 | ### 成员模板 45 | 46 | - 成员模板(member template):本身是模板的函数成员。 47 | - 普通(非模板)类的成员模板。 48 | - 类模板的成员模板。 49 | 50 | ### 控制实例化 51 | 52 | - 动机:在多个文件中实例化相同模板的额外开销可能非常严重。 53 | - 显式实例化: 54 | - `extern template declaration; // 实例化声明` 55 | - `template declaration; // 实例化定义` 56 | 57 | ### 效率与灵活性 58 | 59 | 60 | ## 模板实参推断 61 | 62 | - 对函数模板,编译器利用调用中的函数实参来确定其模板参数,这个过程叫**模板实参推断**。 63 | 64 | ### 类型转换与模板类型参数 65 | 66 | - 能够自动转换类型的只有: 67 | - 和其他函数一样,顶层`const`会被忽略。 68 | - 数组实参或函数实参转换为指针。 69 | 70 | ### 函数模板显式实参 71 | 72 | - 某些情况下,编译器无法推断出模板实参的类型。 73 | - 定义:`template T1 sum(T2, T3);` 74 | - 使用函数显式实参调用:`auto val3 = sum(i, lng); // T1是显式指定,T2和T3都是从函数实参类型推断而来` 75 | - **注意**:正常类型转换可以应用于显式指定的实参。 76 | 77 | ### 尾置返回类型与类型转换 78 | 79 | - 使用场景:并不清楚返回结果的准确类型,但知道所需类型是和参数相关的。 80 | - `template auto fcn(It beg, It end) -> decltype(*beg)` 81 | - 尾置返回允许我们在参数列表之后声明返回类型。 82 | 83 | 标准库的**类型转换**模板: 84 | 85 | - 定义在头文件`type_traits`中。 86 | 87 | | 对`Mod`,其中`Mod`是: | 若`T`是: | 则`Mod::type`是: | 88 | |-----|-----|-----| 89 | | `remove_reference` | `X&`或`X&&` | `X` | 90 | | | 否则 | `T` | 91 | | `add_const` | `X&`或`const X`或函数 | `T` | 92 | | | 否则 | `const T` | 93 | | `add_lvalue_reference` | `X&` | `T` | 94 | | | `X&&` | `X&` | 95 | | | 否则 | `T&` | 96 | | `add_rvalue_reference` | `X&`或`X&&` | `T` | 97 | | | 否则 | `T&&` | 98 | | `remove_pointer` | `X*` | `X` | 99 | | | 否则 | `T`| 100 | | `add_pointer` | `X&`或`X&&` | `X*` | 101 | | | 否则 | `T*` | 102 | | `make_signed` | `unsigned X` | `X` | 103 | | | 否则 | `T` | 104 | | `make_unsigned` | 带符号类型 | `unsigned X` | 105 | | | 否则 | `T` | 106 | | `remove_extent` | `X[n]` | `X` | 107 | | | 否则 | `T` | 108 | | `remove_all_extents` | `X[n1][n2]...` | `X` | 109 | | | 否则 | `T` | 110 | 111 | ### 函数指针和实参推断 112 | 113 | - 当使用一个函数模板初始化一个函数指针或为一个函数指针赋值时,编译器使用指针的类型来推断模板实参。 114 | 115 | ### 模板实参推断和引用 116 | 117 | - 从左值引用函数推断类型:若形如`T&`,则只能传递给它一个左值。但如果是`const T&`,则可以接受一个右值。 118 | - 从右值引用函数推断类型:若形如`T&&`,则只能传递给它一个右值。 119 | - 引用折叠和右值引用参数: 120 | - 规则1:当我们将一个左值传递给函数的右值引用参数,且右值引用指向模板类型参数时(如`T&&`),编译器会推断模板类型参数为实参的左值引用类型。 121 | - 规则2:如果我们间接创造一个引用的引用,则这些引用形成了**折叠**。折叠引用只能应用在间接创造的引用的引用,如类型别名或模板参数。对于一个给定类型`X`: 122 | - `X& &`、`X& &&`和`X&& &`都折叠成类型`X&`。 123 | - 类型`X&& &&`折叠成`X&&`。 124 | - 上面两个例外规则导致两个重要结果: 125 | - 1.如果一个函数参数是一个指向模板类型参数的右值引用(如`T&&`),则它可以被绑定到一个左值上; 126 | - 2.如果实参是一个左值,则推断出的模板实参类型将是一个左值引用,且函数参数将被实例化为一个左值引用参数(`T&`)。 127 | 128 | ### 理解std::move 129 | 130 | - 标准库`move`函数是使用右值引用的模板的一个很好的例子。 131 | - 从一个左值`static_cast`到一个右值引用是允许的。 132 | 133 | ```cpp 134 | template 135 | typename remove_reference::type&& move(T&& t) 136 | { 137 | return static_cast::type&&>(t); 138 | } 139 | ``` 140 | 141 | ### 转发 142 | 143 | - 使用一个名为`forward`的新标准库设施来传递参数,它能够保持原始实参的类型。 144 | - 定义在头文件`utility`中。 145 | - 必须通过显式模板实参来调用。 146 | - `forward`返回显式实参类型的右值引用。即,`forward`的返回类型是`T&&`。 147 | 148 | ## 重载与模板 149 | 150 | - 多个可行模板:当有多个重载模板对一个调用提供同样好的匹配时,会选择最特例化的版本。 151 | - 非模板和模板重载:对于一个调用,如果一个非函数模板与一个函数模板提供同样好的匹配,则选择非模板版本。 152 | 153 | ## 可变参数模板 154 | 155 | **可变参数模板**就是一个接受可变数目参数的模板函数或模板类。 156 | - 可变数目的参数被称为参数包。 157 | - 模板参数包:标识另个或多个模板参数。 158 | - 函数参数包:标识另个或者多个函数参数。 159 | - 用一个省略号来指出一个模板参数或函数参数,表示一个包。 160 | - `template `,`Args`第一个模板参数包。 161 | - `void foo(const T &t, const Args& ... rest);`,`rest`是一个函数参数包。 162 | - `sizeof...`运算符,返回参数的数目。 163 | 164 | ### 编写可变参数函数模板 165 | 166 | - 可变参数函数通常是递归的:第一步调用处理包中的第一个实参,然后用剩余实参调用自身。 167 | 168 | ### 包扩展 169 | 170 | - 对于一个参数包,除了获取它的大小,唯一能做的事情就是**扩展**(expand)。 171 | - 扩展一个包时,还要提供用于每个扩展元素的**模式**(pattern)。 172 | 173 | ### 转发参数包 174 | 175 | - 新标准下可以组合使用可变参数模板和`forward`机制,实现将实参不变地传递给其他函数。 176 | 177 | ## 模板特例化(Specializations) 178 | 179 | - 定义函数模板特例化:关键字`template`后面跟一个空尖括号对(`<>`)。 180 | - 特例化的本质是实例化一个模板,而不是重载它。特例化不影响函数匹配。 181 | - 模板及其特例化版本应该声明在同一个头文件中。所有同名模板的声明应该放在前面,然后是特例化版本。 182 | - 我们可以部分特例化类模板,但不能部分特例化函数模板。 183 | -------------------------------------------------------------------------------- /notes/ch18.md: -------------------------------------------------------------------------------- 1 | # 第十八章 用于大型程序的工具 2 | 3 | 大规模应用程序的特殊要求包括: 4 | 5 | - 在独立开发的子系统之间协同处理错误的能力。 6 | - 使用各种库进行协同开发的能力。 7 | - 对比较复杂的应用概念建模的能力。 8 | 9 | ## 异常处理 10 | 11 | **异常处理**(exception handling)机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并作出相应的处理。 12 | 13 | ### 抛出异常 14 | 15 | 在C++语言中,我们通过**抛出**(throwing)一条表达式来**引发**(raised)一个异常。异常类型和当前的调用链决定了哪段**处理代码**(handler)将用来处理该异常。 16 | 17 | 程序的控制权从`throw`转移到`catch`模块。 18 | 19 | **栈展开**:当`throw`出现在一个`try语句块`时,检查该`try语句块`相关的`catch`字句,若有匹配则处理;若无匹配,则继续检查外层的`try`匹配的`catch`。 20 | 21 | 若一个异常没有被捕获,则它将终止当前的程序。 22 | 23 | 对象销毁: 24 | 25 | - 块退出后,它的局部对象将被销毁。 26 | - 若异常发生在构造函数中,即使某个对象只构造了一部分,也要确保已构造的成员正确地被销毁。 27 | - 将资源释放放在类的析构函数中,以保证资源能被正确释放。析构函数本身不会引发异常。 28 | 29 | ### 捕获异常 30 | 31 | 若无需访问抛出的异常对象,则可以忽略捕获形参的名字。 32 | 33 | 通常,若`catch`接受的异常与某个继承体系有关,则最好将该`catch`的参数定义成引用类型。 34 | 35 | 搜索`catch`未必是最佳匹配,而是第一个匹配,因此,越细化的`catch`越应该放在`catch`列表前段。 36 | 37 | 重新抛出:`catch`代码执行一条`throw;`将异常传递给另一个`catch`语句。 38 | 39 | 捕获所有异常:`catch(...)` 40 | 41 | ### 构造函数 42 | 43 | 处理构造函数初始值异常的唯一方法是将构造函数协程函数`try`语句块。 44 | 45 | 示例: 46 | 47 | ```cpp 48 | template 49 | Blob::Blob(std::initializer_list il) try: 50 | data(std::make_shared >(il){ 51 | /*函数体*/ 52 | } catch(const std::bad_alloc &e){ handle_out_of_memory(e); } 53 | ``` 54 | 55 | ### noexcept异常说明 56 | 57 | 使用`noexcept`说明指定某个函数不会抛出异常。 58 | 59 | 示例: 60 | 61 | ```cpp 62 | void recoup(int) noexcept; //C++11 63 | coid recoup(int) throw(); //老版本 64 | ``` 65 | 66 | ### 异常类层次 67 | 68 | 标准exception层次: 69 | 70 | - exception 71 | - bad_cast 72 | - bad_alloc 73 | - runtime_error 74 | - overflow_error 75 | - underflow_error 76 | - range_error 77 | - logic_error 78 | - domain_error 79 | - invalid_argument 80 | - out_of_range 81 | - length_error 82 | 83 | 自定义异常类: 84 | 85 | 示例: 86 | 87 | ```cpp 88 | class out_of_stock: public std::runtime_error { 89 | explicit out_of_stock(const std::string &s): 90 | std::runtime_error(s){ } 91 | }; 92 | ``` 93 | 94 | ## 命名空间 95 | 96 | 多个库将名字放置在全局命名空间中将引发**命名空间污染**(namespace pollution)。**命名空间**(namespace)分割了全局命名空间,其中每个命名空间是一个作用域。 97 | 98 | ### 命名空间定义 99 | 100 | 命名空间的定义包含两部分:1.关键字`namespace`;2.命名空间名称。后面是一系列由花括号括起来的声明和定义。命名空间作用域后面无需分号。 101 | 102 | 示例: 103 | 104 | ```cpp 105 | namespace cplusplus_primer{ 106 | 107 | } 108 | ``` 109 | 110 | 每个命名空间都是一个**作用域**。定义在某个命名空间内的名字可以被该命名空间内的其他成员直接访问,也可以被这些成员内嵌套作用域中的任何单位访问。位于该命名空间之外的代码必须明确指出所用的名字是属于哪个命名空间的。 111 | 112 | 命名空间可以是**不连续**的。这点不同于其他作用域,意味着同一命名空间可以在多处出现。 113 | 114 | **内联命名空间**(C++11): 115 | 116 | 无需使用该命名空间的前缀,通过外层命名空间就可以直接访问。 117 | 118 | 示例: 119 | 120 | ```cpp 121 | namespace cplusplus_primer{ 122 | inline namespace FifthEd{ 123 | // 表示本书第5版代码 124 | class Query_base {}; 125 | } 126 | } 127 | 128 | cplusplus_primer::Query_base qb; 129 | ``` 130 | 131 | **未命名的命名空间**: 132 | 133 | 指关键字`namespace`后面紧跟花括号的用法。未命名的命名空间中定义的变量拥有静态的声明周期:在第一次使用前创建,直到程序结束才销毁。不能跨越多个文件。 134 | 135 | ### 使用命名空间成员 136 | 137 | 像`namespace_name::member_name`这样使用命名空间的成员非常繁琐。 138 | 139 | **命名空间的别名**: 140 | 141 | ```cpp 142 | namespace primer = cplusplus_primer; 143 | ``` 144 | 145 | **using声明**(using declaration): 146 | 147 | 一条`using`声明语句一次只引入命名空间的一个成员。 148 | 149 | ```cpp 150 | using std::string; 151 | 152 | string s = "hello"; 153 | ``` 154 | 155 | **using指示**(using directive): 156 | 157 | 使得某个特定的命名空间中所有的名字都可见。 158 | 159 | ```cpp 160 | using namespace std; 161 | 162 | string s = "hello"; 163 | ``` 164 | 165 | ### 类、命名空间与作用域 166 | 167 | ```cpp 168 | namespace A{ 169 | class C1{ 170 | public: 171 | int f3(); 172 | } 173 | } 174 | 175 | A::C1::f3 176 | ``` 177 | 178 | ### 重载与命名空间 179 | 180 | `using`声明语句声明的是一个名字,而非特定的函数,也就是包括该函数的所有版本,都被引入到当前作用域中。 181 | 182 | ## 多重继承与虚继承 183 | 184 | ### 多重继承 185 | 186 | ### 类型转换与多个基类 187 | 188 | ### 多重继承下的类作用域 189 | 190 | * 当一个类拥有多个基类时,有可能出现派生类从两个或更多基类中继承了同名成员的情况。此时,不加前缀限定符直接使用该名字将引发二义性。 191 | 192 | ### 虚继承 193 | 194 | * 虚继承的目的是令某个类做出声明,承诺愿意共享它的基类。其中,共享的基类子对象成为**虚基类**。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含唯一一个共享的虚基类子对象。 195 | * 虚派生只影响从指定了虚基类的派生类中进一步派生出的类,它不会影响派生类本身。 196 | 197 | ### 构造函数与虚继承 198 | 199 | * h含有虚基类的对象的构造顺序与一般的顺序稍有**区别**:首先使用提供给最底层派生类构造函数的初始值初始化该对象的虚基类子部分,接下来按照直接基类在派生列表中出现的次序对其进行初始化。 200 | * 虚基类总是先于非虚基类构造,与它们在继承体系中的次序和位置无关。 -------------------------------------------------------------------------------- /notes/ch19.md: -------------------------------------------------------------------------------- 1 | # 第十九章 特殊工具与技术 2 | 3 | ## 控制内存分配 4 | 5 | ### 重载new和delete 6 | 7 | * **`new`表达式的工作机理**: 8 | 9 | ```c++ 10 | string *sp = new string("a value"); //分配并初始化一个string对象 11 | string *arr = new string[10]; // 分配10个默认初始化的string对象 12 | ``` 13 | 14 | * 上述代码实际执行了**三步操作**: 15 | * `new`表达式调用一个名为`operator new`(或`operator new []`)的标准库函数,它分配一块**足够大的**、**原始的**、**未命名的**内存空间以便存储特定类型的对象(或对象的数组)。 16 | * 编译器运行相应的构造函数以构造这些对象,并为其传入初始值。 17 | * 对象被分配了空间并构造完成,返回一个指向该对象的指针。 18 | 19 | * **`delete`表达式的工作机理**: 20 | 21 | ```c++ 22 | delete sp; // 销毁*sp,然后释放sp指向的内存空间 23 | delete [] arr; // 销毁数组中的元素,然后释放对应的内存空间 24 | ``` 25 | 26 | * 上述代码实际执行了**两步操作**: 27 | * 对`sp`所指向的对象或者`arr`所指的数组中的元素执行对应的析构函数。 28 | * 编译器调用名为`operator delete`(或`operator delete[]`)的标准库函数释放内存空间。 29 | * 当自定义了全局的`operator new`函数和`operator delete`函数后,我们就担负起了控制动态内存分配的职责。这两个函数**必须是正确的**。因为它们是程序整个处理过程中至关重要的一部分。 30 | * 标准库定义了`operator new`函数和`operator delete`函数的8个重载版本: 31 | 32 | ```c++ 33 | // 这些版本可能抛出异常 34 | void *operator new(size_t); // 分配一个对象 35 | void *operator new[](size_t); // 分配一个数组 36 | void *operator delete(void*) noexcept; // 释放一个对象 37 | void *operator delete[](void*) noexcept; // 释放一个数组 38 | 39 | // 这些版本承诺不会抛出异常 40 | void *operator new(size_t, nothrow_t&) noexcept; 41 | void *operator new[](size_t, nothrow_t&) noexcept; 42 | void *operator delete(void*, nothrow_t&) noexcept; 43 | void *operator delete[](void*, nothrow_t&) noexcept; 44 | ``` 45 | 46 | * 应用程序可以自定义上面函数版本中的任意一个,前提是自定义的版本必须位于**全局作用域**或者**类作用域**中。 47 | * **注意:** 提供新的`operator new`函数和`operator delete`函数的目的在于改变内存分配的方式,但是不管怎样,都不能改变`new`运算符和`delete`运算符的基本含义。 48 | * 使用从C语言继承的函数`malloc`和`free`函数能实现以某种方式执行分配内存和释放内存的操作: 49 | 50 | ```c++ 51 | #include 52 | 53 | void *operator new(size_t size) { 54 | if(void *mem = malloc(size)) 55 | return mme; 56 | else 57 | throw bad_alloc(); 58 | } 59 | 60 | void operator delete(void *mem) noexcept { 61 | free(mem); 62 | } 63 | ``` 64 | 65 | ### 定位new表达式 66 | 67 | * 应该使用new的定位`new(placement new)`形式传递一个地址,定位`new`的形式如下: 68 | 69 | ```c++ 70 | new (place_address) type 71 | new (place_address) type (initializers) 72 | new (place_address) type [size] 73 | new (place_address) type [size] {braced initializer list} 74 | // place_address必须是一个指针,同时在initializers中提供一个(可能为空的)以逗号分隔的初始值列表,该初始值列表将用于构造新分配的对象。 75 | ``` 76 | 77 | * 当只传入一个指针类型的实参时,定位`new`表达式构造对象但是不分配内存。 78 | * 调用析构函数会销毁对象,但是不会释放内存。 79 | 80 | ```c++ 81 | string *sp = new string("a value"); // 分配并初始化一个string对象 82 | sp->~string(); 83 | ``` 84 | 85 | ## 运行时类型识别 86 | 87 | * 运行时类型识别`(run-time type identification, RTTI)`的功能由两个运算符实现: 88 | * `typeid`运算符, 用于返回表达式的类型。 89 | * `dynamic_cast`运算符,用于将基类的指针或引用安全地转换曾派生类的指针或引用。 90 | * 使用`RTTI`必须要加倍小心。在可能的情况下,最好定义虚函数而非直接接管类型管理的重任。 91 | 92 | ### dynamic_cast运算符 93 | 94 | * dynamic_cast运算符的使用形式如下: 95 | 96 | ```c++ 97 | dynamic_cast(e) // e必须是一个有效的指针 98 | dynamic_cast(e) // e必须是一个左值 99 | dynamic_cast(e) // e不能是左值 100 | // 以上,type类型必须时一个类类型,并且通常情况下该类型应该含有虚函数。 101 | // e的类型必须符合三个条件中的任意一个,它们是: 102 | // 1. e的类型是目标type的公有派生类; 103 | // 2. e的类型是目标type的共有基类; 104 | // 3. e的类型就是目标type的类型; 105 | 106 | // 指针类型的dynamic_cast 107 | // 假设Base类至少含有一个虚函数,Derived是Base的共有派生类。 108 | if (Derived *dp = dynamic_cast(bp)) { 109 | // 使用dp指向的Derived对象 110 | } else { // bp指向一个Base对象 111 | // 使用dp指向的Base对象 112 | } 113 | 114 | // 引用类型的dynamic_cast 115 | void f(const Base &b) { 116 | try { 117 | const Derived &d = dynamic_cast(b); 118 | // 使用b引用的Derived对象 119 | } catch (bad_cast) { 120 | // 处理类型转换失败的情况 121 | } 122 | } 123 | ``` 124 | 125 | * 可以对一个空指针执行`dynamic_cast`,结果是所需类型的空指针。 126 | 127 | ### typeid运算符 128 | 129 | * `typeid运算符(typeid operator)`,它允许程序向表达式提问:**你的对象是什么类型?** 130 | * `typeid`表达式的形式是`typeid(e)`,其中`e`可以是任意表达式或类型的名字,它操作的结果是一个常量对象的引用。它可以作用于任意类型的表达式。 131 | * 通常情况下,使用typeid比较两条表达式的类型是否相同,或者比较一条表达式的类型是否与指定类型相同: 132 | 133 | ```c++ 134 | Derived *dp = new Derived; 135 | Base *bp = dp; 136 | 137 | if (typeid(*bp) == typeid(*dp)) { 138 | // bp和dp指向同一类型的对象 139 | } 140 | 141 | if (typeid(*bp) == typeid(Derived)) { 142 | // bp实际指向Derived对象 143 | } 144 | ``` 145 | 146 | * 当typeid作用于指针时(而非指针所指向的对象),返回的结果是该指针的静态编译时类型。 147 | 148 | ```c++ 149 | // 下面的检查永远是失败的:bp的类型是指向Base的指针 150 | if (typeid(bp) == typeid(Derived)) { 151 | // 永远不会执行 152 | } 153 | ``` 154 | 155 | ### 使用RTTI 156 | 157 | * 用途:为具有继承关系的类实现相等运算符时。对于两个对象来说,如果它们的类型相同并且对应的数据成员取值相同,则说这两个对象是相等的。 158 | 159 | ```c++ 160 | // 类的层次关系 161 | class Base { 162 | friend bool operator==(const Base&, const Base&); 163 | public: 164 | // Base的接口成员 165 | protected: 166 | virtual bool equal(const Base&) const; 167 | // Base的数据成员和其他用于实现的成员 168 | }; 169 | 170 | class Derived: public Base { 171 | public: 172 | // Derived的其他接口成员 173 | protected: 174 | bool equal(const Base&) const; 175 | // Derived的数据成员和其他用于实现的成员 176 | }; 177 | 178 | // 类型敏感的相等运算符 179 | bool operator==(const Base &lhs, const Base &rhs) { 180 | // 如果typeid不相同,返回false;否则虚调用equal 181 | return typeid(lhs) == typeid(rhs) && lhs.equal(rhs); 182 | } 183 | 184 | // 虚equal函数 185 | bool Derived::equal(const Base &rhs) const { 186 | auto r = dynamic_cast(rhs); 187 | // 执行比较两个Derived对象的操作并返回结果 188 | } 189 | 190 | // 基类equal函数 191 | bool Base::equal(const Base &rhs) const { 192 | // 执行比较Base对象的操作 193 | } 194 | ``` 195 | 196 | ### type_info类 197 | 198 | ## 枚举类型 199 | 200 | * 枚举类型`(enumeration)`使我们可以将一组整型常量组织在一起。枚举属于字面值常量类型。 201 | * **限定作用域的枚举类型(scoped enumeration)**:首先是关键字`enum class(或enum struct)`,随后是枚举类型名字以及用花括号括起来的以逗号分隔的枚举成员列表,最后是一个分号。 202 | 203 | ```c++ 204 | enum class open_modes {input, output, append}; 205 | ``` 206 | 207 | * 不限定作用域的枚举类型`(unscoped enumeration)`:省略关键字`class(或struct)`,枚举类型的名字是可选的。 208 | 209 | ```c++ 210 | enum color {red, yellow, green}; 211 | 212 | enum {floatPrec = 6, doublePrec = 10, double_doublePrec = 10}; 213 | 214 | ``` 215 | 216 | ## 类成员指针 217 | 218 | **成员指针**:指可以指向类的非静态成员的指针。 219 | 220 | ### 数据成员指针 221 | 222 | * 和其他指针一样,在声明成员指针时也使用*来表示当前声明的名字是一个指针。与普通指针不同的时,成员指针还必须包含成员所属的类。 223 | 224 | ```c++ 225 | // pdata可以指向一个常量(非常量)Screen对象的string成员 226 | const string Screen::*pdata; 227 | 228 | // C++11 229 | auto pdata = &Screen::contents; 230 | ``` 231 | 232 | * 当我们初始化一个成员指针或为成员指针赋值时,该指针没有指向任何数据。成员指针指定了成员而非该成员所属的对象,只有当解引用成员指针时才提供对象的信息。 233 | 234 | ```c++ 235 | Screen myScreen, *pScreen = &myScreen; 236 | 237 | auto s = myScreen.*pdata; 238 | 239 | s = pScreen->*pdata; 240 | ``` 241 | 242 | ### 成员函数指针 243 | 244 | * 因为函数调用运算符的优先级较高,所以在声明指向成员函数的指针并使用这些的指针进行函数调用时,括号必不可少:`(C::*p)(parms)`和`(obj.*p)(args)`。 245 | 246 | ### 将成员函数用作可调用对象 247 | 248 | ## 嵌套类 249 | 250 | * 一个类可以定义在另一个类的内部,前者称为嵌套类(nested class)或嵌套类型(nested type)。**嵌套类常用于定义作为实现部分的类**。 251 | * 嵌套类是一个独立的类,与外层类基本没有什么关系。特别是,外层类的对象和嵌套类的对象是相互独立的。 252 | * 嵌套类的名字在外层类作用域中是可见的,在外层类作用域之外不可见。 253 | 254 | ## union:一种节省空间的类 255 | 256 | * `联合(union)`是一种特殊的类。一个`union`可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。**它不能含有引用类型的成员和虚函数**。 257 | 258 | ```c++ 259 | // Token类型的对象只有一个成员,该成员的类型可能是下列类型中的任意一种 260 | union Token { 261 | // 默认情况下成员是共有的 262 | char cval; 263 | int ival; 264 | double dval; 265 | }; 266 | 267 | ``` 268 | 269 | * `匿名union(anonymous union)`是一个未命名的`union`,并且在右花括号和分号之间没有任何声明。 270 | 271 | ```c++ 272 | union { 273 | char cval; 274 | int ival; 275 | double dval; 276 | }; 277 | 278 | // 可以直接访问它的成员 279 | cal = 'c'; 280 | ival = 42; 281 | ``` 282 | 283 | * **注意:** `匿名union`不能包含受保护的成员或私有成员,也不能定义成员函数。 284 | 285 | ## 局部类 286 | 287 | * `局部类(local class)`:可以定义在某个函数的内部的类。它的类型只在定义它的作用域内可见。和嵌套类不同,局部类的成员受到严格限制。 288 | * 局部类的所有成员(包括函数在内)都必须完整定义在类的内部。因此,局部类的作用与嵌套类相比相差很远。 289 | * **局部类不能使用函数作用域中的变量。** 290 | 291 | ```c++ 292 | int a, val; 293 | void foo(int val) { 294 | static inti si; 295 | enum loc { a = 1024, b}; 296 | 297 | // Bar是foo的局部类 298 | struct Bar { 299 | Loc locVal; // 正确:使用一个局部类型名 300 | int barVal; 301 | 302 | void fooBar(Loc l = a) { // 正确:默认实参是Loc::a 303 | barVal = val; // 错误:val是foo的局部变量 304 | barVal == ::val; // 正确:使用一个全局对象 305 | barVal = si; // 正确:使用一个静态局部对象 306 | locVal = b; // 正确:使用一个枚举成员 307 | } 308 | }; 309 | } 310 | 311 | ``` 312 | 313 | ## 固有的不可移植的特性 314 | 315 | 所谓不可移植的特性是指**因机器而异的特性**,当将含有不可移植特性的程序从一台机器转移到另一台机器上时,通常需要重新编写该程序。 316 | 317 | ### 位域 318 | 319 | * 类可以将其(非静态)数据成员定义成**位域(bit-field)**,在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。 320 | * 位域在内存中的布局是与机器相关的。 321 | * 位域的类型必须是整型或枚举类型。因为带符号位域的行为是由具体实现确定的,通常情况下我们使用无符号类型保存一个位域。 322 | 323 | ```c++ 324 | typedef unsigned int Bit; 325 | class File { 326 | Bit mode: 2; 327 | Bit modified: 1; 328 | Bit prot_owner: 3; 329 | Bit prot_group: 3; 330 | Bit prot_world: 3; 331 | public: 332 | enum modes {READ = 01, WRITE = 02, EXECUTE = 03}; 333 | File &open(modes); 334 | void close(); 335 | void write(); 336 | bool isRead() const; 337 | void setWrite(); 338 | } 339 | 340 | // 使用位域 341 | void File::write() { 342 | modified = 1; 343 | // ... 344 | } 345 | 346 | void File::close() { 347 | if( modified) 348 | // ...保存内容 349 | } 350 | 351 | File &File::open(File::modes m) { 352 | mode |= READ; // 按默认方式设置READ 353 | // 其他处理 354 | if(m & WRITE) // 如果打开了READ和WRITE 355 | // 按照读/写方式打开文件 356 | return *this; 357 | } 358 | ``` 359 | 360 | ### volatile限定符 361 | 362 | * 当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为`volatile`。关键字`volatile`告诉编译器不应对这样的对象进行优化。 363 | * `const`和`volatile`的一个重要区别是不能使用合成的拷贝/移动构造函数及赋值运算符初始化`volatile`对象或者从`volatile`对象赋值。 364 | 365 | ### 链接指示:extern "C" 366 | 367 | * `C++`使用`链接指示(linkage directive)`指出任意非`C++`函数所用的语言。 368 | * 要想把`C++`代码和其他语言(包括`C`语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的`C++`编译器是兼容的。 369 | * `C++`从C语言继承的标准库函数可以定义为`C`函数,但并非必须:决定使用`C`还是`C++`实现的`C`标准库,是每个`C++`实现的事情。 370 | * 有时需要在C和C++中编译同一个源文件,为了实现这一目的,在编译C++版本的程序时预处理器定义`__cplusplus`。 371 | 372 | ```c++ 373 | #ifdef __cplusplus 374 | extern "C" 375 | #endif 376 | int strcmp(const char*, const char*); 377 | ``` 378 | --------------------------------------------------------------------------------