├── .travis.yml ├── LICENSE ├── 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 └── run.sh /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | script: true 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 一步一个脚印 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Cpp logo

2 |

cpp-primer-learning

3 |

4 | LICENSE 5 | languange 6 | chat 7 |

8 | 9 | 10 | Solutions for C++ *Primer* 5th exercises. 11 | ## Introduction 12 | This project is used to help people who want to learn C++ language by themselves. 13 | 14 | I'm a C + + beginner and the code can be problematic. If you find a mistake, you can write an issue [here](https://github.com/codingboylj/cpp-primer-learning/issues/new) and I'll fix it as soon as I can. 15 | ## Download e-books or Source Code 16 | 17 | You can get it from the relase:https://github.com/ybygjylj/cpp-primer-learning/releases/tag/1.0 18 | 19 | ## My system environment 20 | | My system environment | | 21 | | --------------------- | ----------------------------- | 22 | | Kernel | x86_64 Linux 5.6.19-2-MANJARO | 23 | | g++ version | 5.5.0 | 24 | | editor | neovim 0.4.3 | 25 | 26 | ## Run c++ code quickly on linux 27 | In Linux, compiling a CPP file from the command line and running it requires several commands to execute, so I created a shell to quickly compile and run the CPP code. You can execute the CPP file with the run statement by adding the alias directive definition.(in ~/.zshrc or ~/.bashrc) 28 | 29 | ``` 30 | alias run="~/Documents/c++/run.sh" 31 | ``` 32 | 33 | When you want to perform the test.cpp, you can execute the command: 34 | 35 | ``` 36 | run test.cpp 37 | ``` 38 | 39 | If your code is correct, it will run directly, otherwise an error will be thrown. 40 | 41 | -------------------------------------------------------------------------------- /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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/stepbystepcode/cpp-primer-learning/a07f5c3f9cb1faa2a489bdd0f2067fe098b1abcf/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/ch02.md: -------------------------------------------------------------------------------- 1 | # 第二章 变量和基本类型 2 | 3 | ## 练习2.1 4 | 类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么? 5 | 6 | 解: 7 | 8 | C++ 规定 short 和 int 至少16位,long 至少32位,long long 至少64位。 带符号类型能够表示正数、负数和 0 ,而无符号类型只能够表示 0 和正整数。 9 | 10 | ## 练习2.2 11 | 计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。 12 | 13 | 解: 14 | 15 | 使用`double`。需要进行浮点计算。 16 | 17 | ## 练习2.3 18 | 读程序写结果。 19 | ```cpp 20 | unsigned u = 10, u2 = 42; 21 | std::cout << u2 - u << std::endl; 22 | std::cout << u - u2 << std::endl; 23 | int i = 10, i2 = 42; 24 | std::cout << i2 - i << std::endl; 25 | std::cout << i - i2 << std::endl; 26 | std::cout << i - u << std::endl; 27 | std::cout << u - i << std::endl; 28 | ``` 29 | 30 | 解: 31 | 32 | 输出: 33 | ``` 34 | 32 35 | 4294967264 36 | 32 37 | -32 38 | 0 39 | 0 40 | ``` 41 | 42 | ## 练习2.4 43 | 编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。 44 | 45 | ## 练习2.5 46 | 指出下述字面值的数据类型并说明每一组内几种字面值的区别: 47 | ``` 48 | (a) 'a', L'a', "a", L"a" 49 | (b) 10, 10u, 10L, 10uL, 012, 0xC 50 | (c) 3.14, 3.14f, 3.14L 51 | (d) 10, 10u, 10., 10e-2 52 | ``` 53 | 54 | 解: 55 | - (a): 字符字面值,宽字符字面值,字符串字面值,宽字符串字面值。 56 | - (b): 十进制整型,十进制无符号整型,十进制长整型,八进制整型,十六进制整型。 57 | - (c): double, float, long double 58 | - (d): 十进制整型,十进制无符号整型,double, double 59 | 60 | ## 练习2.6 61 | 下面两组定义是否有区别,如果有,请叙述之: 62 | ```cpp 63 | int month = 9, day = 7; 64 | int month = 09, day = 07; 65 | ``` 66 | 解: 67 | 68 | 第一行定义的是十进制的整型,第二行定义的是八进制的整型。但是month变量有误,八进制不能直接写9。 69 | 70 | ## 练习2.7 71 | 下述字面值表示何种含义?它们各自的数据类型是什么? 72 | ```cpp 73 | (a) "Who goes with F\145rgus?\012" 74 | (b) 3.14e1L 75 | (c) 1024f 76 | (d) 3.14L 77 | ``` 78 | 79 | 解: 80 | - (a) Who goes with Fergus?(换行),string 类型 81 | - (b) long double 82 | - (c) 无效,因为后缀`f`只能用于浮点字面量,而1024是整型。 83 | - (d) long double 84 | 85 | ## 练习2.8 86 | 请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。 87 | 88 | 解: 89 | ```cpp 90 | #include 91 | int main() 92 | { 93 | std::cout << 2 << "\115\012"; 94 | std::cout << 2 << "\t\115\012"; 95 | return 0; 96 | } 97 | ``` 98 | 99 | ## 练习2.9 100 | 解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。 101 | 102 | - (a) std::cin >> int input_value; 103 | - (b) int i = { 3.14 }; 104 | - (c) double salary = wage = 9999.99; 105 | - (d) int i = 3.14; 106 | 107 | 解: 108 | 109 | (a): 应该先定义再使用。 110 | ```cpp 111 | int input_value = 0; 112 | std::cin >> input_value; 113 | ``` 114 | 115 | (b): 用列表初始化内置类型的变量时,如果存在丢失信息的风险,则编译器将报错。 116 | ```cpp 117 | double i = { 3.14 }; 118 | ``` 119 | 120 | (c): 在这里`wage`是未定义的,应该在此之前将其定义。 121 | ```cpp 122 | double wage; 123 | double salary = wage = 9999.99; 124 | ``` 125 | 126 | (d): 不报错,但是小数部分会被截断。 127 | ```cpp 128 | double i = 3.14; 129 | ``` 130 | 131 | ## 练习2.10 132 | 下列变量的初值分别是什么? 133 | ```cpp 134 | std::string global_str; 135 | int global_int; 136 | int main() 137 | { 138 | int local_int; 139 | std::string local_str; 140 | } 141 | ``` 142 | 143 | 解: 144 | 145 | `global_str`和`global_int`是全局变量,所以初值分别为空字符串和0。 146 | `local_int`是局部变量并且没有初始化,它的初值是未定义的。 147 | `local_str` 是 `string` 类的对象,它的值由类确定,为空字符串。 148 | 149 | ## 练习2.11 150 | 指出下面的语句是声明还是定义: 151 | 152 | - (a) extern int ix = 1024; 153 | - (b) int iy; 154 | - (c) extern int iz; 155 | 156 | 解: 157 | 158 | (a): 定义 159 | (b): 定义 160 | (c): 声明 161 | 162 | ## 练习2.12 163 | 请指出下面的名字中哪些是非法的? 164 | 165 | - (a) int double = 3.14; 166 | - (b) int _; 167 | - (c) int catch-22; 168 | - (d) int 1_or_2 = 1; 169 | - (e) double Double = 3.14; 170 | 171 | 解: 172 | 173 | (a), (c), (d) 非法。 174 | 175 | ## 练习2.13 176 | 下面程序中`j`的值是多少? 177 | 178 | ```cpp 179 | int i = 42; 180 | int main() 181 | { 182 | int i = 100; 183 | int j = i; 184 | } 185 | ``` 186 | 187 | 解: 188 | 189 | `j`的值是100,局部变量`i`覆盖了全局变量`i`。 190 | 191 | ## 练习2.14 192 | 下面的程序合法吗?如果合法,它将输出什么? 193 | ```cpp 194 | int i = 100, sum = 0; 195 | for (int i = 0; i != 10; ++i) 196 | sum += i; 197 | std::cout << i << " " << sum << std::endl; 198 | ``` 199 | 200 | 解: 201 | 202 | 合法。输出是 100 45 。 203 | 204 | 205 | ## 练习2.15 206 | 下面的哪个定义是不合法的?为什么? 207 | 208 | - (a) int ival = 1.01; 209 | - (b) int &rval1 = 1.01; 210 | - (c) int &rval2 = ival; 211 | - (d) int &rval3; 212 | 213 | 解: 214 | 215 | (b)和(d)不合法,(b)引用必须绑定在对象上,(d)引用必须初始化。 216 | 217 | ## 练习2.16 218 | 考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作? 219 | 220 | ```cpp 221 | int i = 0, &r1 = i; 222 | double d = 0, &r2 = d; 223 | ``` 224 | - (a) r2 = 3.14159; 225 | - (b) r2 = r1; 226 | - (c) i = r2; 227 | - (d) r1 = d; 228 | 229 | 解: 230 | 231 | - (a): 合法。给 d 赋值为 3.14159。 232 | - (b): 合法。会执行自动转换(int->double)。 233 | - (c): 合法。会发生小数截取。 234 | - (d): 合法。会发生小数截取。 235 | 236 | ## 练习2.17 237 | 执行下面的代码段将输出什么结果? 238 | ```cpp 239 | int i, &ri = i; 240 | i = 5; ri = 10; 241 | std::cout << i << " " << ri << std::endl; 242 | ``` 243 | 244 | 解: 245 | 246 | 输出:10, 10 247 | 248 | ## 练习2.18 249 | 编写代码分别改变指针的值以及指针所指对象的值。 250 | 251 | 解: 252 | ```cpp 253 | int a = 0, b = 1; 254 | int *p1 = &a, *p2 = p1; 255 | 256 | // change the value of a pointer. 257 | p1 = &b; 258 | // change the value to which the pointer points 259 | *p2 = b; 260 | ``` 261 | 262 | ## 练习2.19 263 | 说明指针和引用的主要区别 264 | 265 | 解: 266 | 267 | 引用是另一个对象的别名,而指针本身就是一个对象。 268 | 引用必须初始化,并且一旦定义了引用就无法再绑定到其他对象。而指针无须在定义时赋初值,也可以重新赋值让其指向其他对象。 269 | 270 | ## 练习2.20 271 | 请叙述下面这段代码的作用。 272 | 273 | ```cpp 274 | int i = 42; 275 | int *p1 = &i; 276 | *p1 = *p1 * *p1; 277 | ``` 278 | 279 | 解: 280 | 281 | 让指针 pi 指向 i,然后将 i 的值重新赋值为 42 * 42 (1764)。 282 | 283 | ## 练习2.21 284 | 请解释下述定义。在这些定义中有非法的吗?如果有,为什么? 285 | 286 | `int i = 0;` 287 | - (a) double* dp = &i; 288 | - (b) int *ip = i; 289 | - (c) int *p = &i; 290 | 291 | 解: 292 | 293 | - (a): 非法。不能将一个指向 `double` 的指针指向 `int` 。 294 | - (b): 非法。不能将 `int` 变量赋给指针。 295 | - (c): 合法。 296 | 297 | ## 练习2.22 298 | 假设 p 是一个 int 型指针,请说明下述代码的含义。 299 | 300 | ```cpp 301 | if (p) // ... 302 | if (*p) // ... 303 | ``` 304 | 305 | 解: 306 | 307 | 第一句判断 p 是不是一个空指针, 308 | 第二句判断 p 所指向的对象的值是不是为0 309 | 310 | 311 | ## 练习2.23 312 | 给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。 313 | 314 | 解: 315 | 316 | 不能,因为首先要确定这个指针是不是合法的,才能判断它所指向的对象是不是合法的。 317 | 318 | ## 练习2.24 319 | 在下面这段代码中为什么 p 合法而 lp 非法? 320 | ```cpp 321 | int i = 42; 322 | void *p = &i; 323 | long *lp = &i; 324 | ``` 325 | 326 | 解: 327 | 328 | `void *`是从C语言那里继承过来的,可以指向任何类型的对象。 329 | 而其他指针类型必须要与所指对象严格匹配。 330 | 331 | ## 练习2.25 332 | 说明下列变量的类型和值。 333 | ```cpp 334 | (a) int* ip, i, &r = i; 335 | (b) int i, *ip = 0; 336 | (c) int* ip, ip2; 337 | ``` 338 | 339 | 解: 340 | - (a): ip 是一个指向 int 的指针, i 是一个 int, r 是 i 的引用。 341 | - (b): i 是 int , ip 是一个空指针。 342 | - (c): ip 是一个指向 int 的指针, ip2 是一个 int。 343 | 344 | ## 练习2.26 345 | 下面哪些语句是合法的?如果不合法,请说明为什么? 346 | 347 | 解: 348 | ```cpp 349 | const int buf; // 不合法, const 对象必须初始化 350 | int cnt = 0; // 合法 351 | const int sz = cnt; // 合法 352 | ++cnt; ++sz; // 不合法, const 对象不能被改变 353 | ``` 354 | 355 | ## 练习2.27 356 | 下面的哪些初始化是合法的?请说明原因。 357 | 358 | 解: 359 | 360 | ```cpp 361 | int i = -1, &r = 0; // 不合法, r 必须引用一个对象 362 | int *const p2 = &i2; // 合法,常量指针 363 | const int i = -1, &r = 0; // 合法 364 | const int *const p3 = &i2; // 合法 365 | const int *p1 = &i2; // 合法 366 | const int &const r2; // 不合法, r2 是引用,引用没有顶层 const 367 | const int i2 = i, &r = i; // 合法 368 | ``` 369 | 370 | ## 练习2.28 371 | 说明下面的这些定义是什么意思,挑出其中不合法的。 372 | 373 | 解: 374 | ```cpp 375 | int i, *const cp; // 不合法, const 指针必须初始化 376 | int *p1, *const p2; // 不合法, const 指针必须初始化 377 | const int ic, &r = ic; // 不合法, const int 必须初始化 378 | const int *const p3; // 不合法, const 指针必须初始化 379 | const int *p; // 合法. 一个指针,指向 const int 380 | ``` 381 | 382 | ## 练习2.29 383 | 假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。 384 | 385 | 解: 386 | ```cpp 387 | i = ic; // 合法, 常量赋值给普通变量 388 | p1 = p3; // 不合法, p3 是const指针不能赋值给普通指针 389 | p1 = ⁣ // 不合法, 普通指针不能指向常量 390 | p3 = ⁣ // 合法, p3 是常量指针且指向常量 391 | p2 = p1; // 合法, 可以将普通指针赋值给常量指针 392 | ic = *p3; // 合法, 对 p3 取值后是一个 int 然后赋值给 ic 393 | ``` 394 | 395 | ## 练习2.30 396 | 对于下面的这些语句,请说明对象被声明成了顶层const还是底层const? 397 | 398 | ```cpp 399 | const int v2 = 0; int v1 = v2; 400 | int *p1 = &v1, &r1 = v1; 401 | const int *p2 = &v2, *const p3 = &i, &r2 = v2; 402 | ``` 403 | 404 | 解: 405 | 406 | v2 是顶层const,p2 是底层const,p3 既是顶层const又是底层const,r2 是底层const。 407 | 408 | ## 练习2.31 409 | 假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。 410 | 411 | 解: 412 | 413 | ```cpp 414 | r1 = v2; // 合法, 顶层const在拷贝时不受影响 415 | p1 = p2; // 不合法, p2 是底层const,如果要拷贝必须要求 p1 也是底层const 416 | p2 = p1; // 合法, int* 可以转换成const int* 417 | p1 = p3; // 不合法, p3 是一个底层const,p1 不是 418 | p2 = p3; // 合法, p2 和 p3 都是底层const,拷贝时忽略掉顶层const 419 | ``` 420 | 421 | ## 练习2.32 422 | 下面的代码是否合法?如果非法,请设法将其修改正确。 423 | ```cpp 424 | int null = 0, *p = null; 425 | ``` 426 | 解: 427 | 428 | 合法。指针可以初始化为 0 表示为空指针。 429 | 430 | ## 练习2.33 431 | 利用本节定义的变量,判断下列语句的运行结果。 432 | 433 | 解: 434 | 435 | ```cpp 436 | a=42; // a 是 int 437 | b=42; // b 是一个 int,(ci的顶层const在拷贝时被忽略掉了) 438 | c=42; // c 也是一个int 439 | d=42; // d 是一个 int *,所以语句非法 440 | e=42; // e 是一个 const int *, 所以语句非法 441 | g=42; // g 是一个 const int 的引用,引用都是底层const,所以不能被赋值 442 | ``` 443 | 444 | ## 练习2.34 445 | 基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?如果不对,请反复研读本节的示例直到你明白错在何处为止。 446 | 447 | ## 练习2.35 448 | 判断下列定义推断出的类型是什么,然后编写程序进行验证。 449 | 450 | ```cpp 451 | const int i = 42; 452 | auto j = i; const auto &k = i; auto *p = &i; 453 | const auto j2 = i, &k2 = i; 454 | ``` 455 | 456 | 解: 457 | 458 | j 是 int,k 是 const int的引用,p 是const int *,j2 是const int,k2 是 const int 的引用。 459 | 460 | ## 练习2.36 461 | 关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。 462 | 463 | ```cpp 464 | int a = 3, b = 4; 465 | decltype(a) c = a; 466 | decltype((b)) d = a; 467 | ++c; 468 | ++d; 469 | ``` 470 | 471 | 解: 472 | 473 | c 是 int 类型,值为 4。d 是 int & 类型,绑定到 a,a 的值为 4 。 474 | 475 | ## 练习2.37 476 | 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。 477 | 478 | ```cpp 479 | int a = 3, b = 4; 480 | decltype(a) c = a; 481 | decltype(a = b) d = a; 482 | ``` 483 | 484 | 解: 485 | 486 | c 是 int 类型,值为 3。d 是 int& 类型,绑定到 a。 487 | 488 | ## 练习2.38 489 | 说明由decltype 指定类型和由auto指定类型有何区别。请举一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。 490 | 491 | 解: 492 | 493 | decltype 处理顶层const和引用的方式与 auto不同,decltype会将顶层const和引用保留起来。 494 | ```cpp 495 | int i = 0, &r = i; 496 | //相同 497 | auto a = i; 498 | decltype(i) b = i; 499 | 500 | //不同 d 是一个 int& 501 | auto c = r; 502 | decltype(r) d = r; 503 | ``` 504 | 505 | ## 练习2.39 506 | 编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。 507 | ```cpp 508 | struct Foo { /* 此处为空 */ } // 注意:没有分号 509 | int main() 510 | { 511 | return 0; 512 | }。 513 | ``` 514 | 解: 515 | 516 | 提示应输入分号。 517 | 518 | ## 练习2.40 519 | 根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。 520 | 521 | ```cpp 522 | struct Sale_data 523 | { 524 | std::string bookNo; 525 | std::string bookName; 526 | unsigned units_sold = 0; 527 | double revenue = 0.0; 528 | double price = 0.0; 529 | //... 530 | } 531 | ``` 532 | 533 | ## 练习2.41 534 | 使用你自己的Sale_data类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把Sales_data类的定义和main函数放在一个文件里。 535 | 536 | ```cpp 537 | // 1.5.1 538 | 539 | #include 540 | #include 541 | 542 | struct Sale_data 543 | { 544 | std::string bookNo; 545 | unsigned units_sold = 0; 546 | double revenue = 0.0; 547 | }; 548 | 549 | int main() 550 | { 551 | Sale_data book; 552 | double price; 553 | std::cin >> book.bookNo >> book.units_sold >> price; 554 | book.revenue = book.units_sold * price; 555 | std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price; 556 | 557 | return 0; 558 | } 559 | 560 | ``` 561 | 562 | ```cpp 563 | // 1.5.2 564 | 565 | #include 566 | #include 567 | 568 | struct Sale_data 569 | { 570 | std::string bookNo; 571 | unsigned units_sold = 0; 572 | double revenue = 0.0; 573 | }; 574 | 575 | int main() 576 | { 577 | Sale_data book1, book2; 578 | double price1, price2; 579 | std::cin >> book1.bookNo >> book1.units_sold >> price1; 580 | std::cin >> book2.bookNo >> book2.units_sold >> price2; 581 | book1.revenue = book1.units_sold * price1; 582 | book2.revenue = book2.units_sold * price2; 583 | 584 | if (book1.bookNo == book2.bookNo) 585 | { 586 | unsigned totalCnt = book1.units_sold + book2.units_sold; 587 | double totalRevenue = book1.revenue + book2.revenue; 588 | std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " "; 589 | if (totalCnt != 0) 590 | std::cout << totalRevenue / totalCnt << std::endl; 591 | else 592 | std::cout << "(no sales)" << std::endl; 593 | return 0; 594 | } 595 | else 596 | { 597 | std::cerr << "Data must refer to same ISBN" << std::endl; 598 | return -1; // indicate failure 599 | } 600 | } 601 | 602 | ``` 603 | 604 | ```cpp 605 | // 1.6 606 | 607 | #include 608 | #include 609 | 610 | struct Sale_data 611 | { 612 | std::string bookNo; 613 | unsigned units_sold = 0; 614 | double revenue = 0.0; 615 | }; 616 | 617 | int main() 618 | { 619 | Sale_data total; 620 | double totalPrice; 621 | if (std::cin >> total.bookNo >> total.units_sold >> totalPrice) 622 | { 623 | total.revenue = total.units_sold * totalPrice; 624 | 625 | Sale_data trans; 626 | double transPrice; 627 | while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice) 628 | { 629 | trans.revenue = trans.units_sold * transPrice; 630 | 631 | if (total.bookNo == trans.bookNo) 632 | { 633 | total.units_sold += trans.units_sold; 634 | total.revenue += trans.revenue; 635 | } 636 | else 637 | { 638 | std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; 639 | if (total.units_sold != 0) 640 | std::cout << total.revenue / total.units_sold << std::endl; 641 | else 642 | std::cout << "(no sales)" << std::endl; 643 | 644 | total.bookNo = trans.bookNo; 645 | total.units_sold = trans.units_sold; 646 | total.revenue = trans.revenue; 647 | } 648 | } 649 | 650 | std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " "; 651 | if (total.units_sold != 0) 652 | std::cout << total.revenue / total.units_sold << std::endl; 653 | else 654 | std::cout << "(no sales)" << std::endl; 655 | 656 | return 0; 657 | } 658 | else 659 | { 660 | std::cerr << "No data?!" << std::endl; 661 | return -1; // indicate failure 662 | } 663 | } 664 | ``` 665 | 666 | ## 练习2.42 667 | 根据你自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页)的练习。 668 | 669 | 670 | 671 | -------------------------------------------------------------------------------- /excersize/ch04.md: -------------------------------------------------------------------------------- 1 | # 第四章 表达式 2 | 3 | ## 练习4.1 4 | 5 | 表达式`5 + 10 * 20 / 2`的求值结果是多少? 6 | 7 | 解: 8 | 9 | 等价于`5 + ((10 * 20) / 2) = 105` 10 | 11 | ## 练习4.2 12 | 13 | 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。 14 | (a) `*vec.begin()` 15 | (b) `*vec.begin() + 1` 16 | 17 | 解: 18 | 19 | ```cpp 20 | *(vec.begin()) 21 | (*(vec.begin())) + 1 22 | ``` 23 | 24 | ## 练习4.3 25 | 26 | C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。 27 | 28 | 解: 29 | 30 | 可以接受。C++的设计思想是尽可能地“相信”程序员,将效率最大化。然而这种思想却有着潜在的危害,就是无法控制程序员自身引发的错误。因此 Java 的诞生也是必然,Java的思想就是尽可能地“不相信”程序员。 31 | 32 | ## 练习4.4 33 | 在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。 34 | 35 | `12 / 3 * 4 + 5 * 15 + 24 % 4 / 2` 36 | 37 | 解: 38 | 39 | `((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2) = 16 + 75 + 0 = 91` 40 | 41 | ## 练习4.5 42 | 43 | 写出下列表达式的求值结果。 44 | ```cpp 45 | -30 * 3 + 21 / 5 // -90+4 = -86 46 | -30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18 47 | 30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0 48 | -30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2 49 | ``` 50 | 51 | ## 练习4.6 52 | 53 | 写出一条表达式用于确定一个整数是奇数还是偶数。 54 | 55 | 解: 56 | 57 | ```cpp 58 | if (i % 2 == 0) /* ... */ 59 | ``` 60 | 或者 61 | ```cpp 62 | if (i & 0x1) /* ... */ 63 | ``` 64 | 65 | ## 练习4.7 66 | 67 | 溢出是何含义?写出三条将导致溢出的表达式。 68 | 69 | 解: 70 | 71 | 当计算的结果超出该类型所能表示的范围时就会产生溢出。 72 | 73 | ```cpp 74 | short svalue = 32767; ++svalue; // -32768 75 | unsigned uivalue = 0; --uivalue; // 4294967295 76 | unsigned short usvalue = 65535; ++usvalue; // 0 77 | ``` 78 | 79 | ## 练习4.8 80 | 81 | 说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。 82 | 83 | 解: 84 | 85 | - 逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种策略称为 **短路求值**。 86 | - 相等性运算符未定义求值顺序。 87 | 88 | ## 练习4.9 89 | 90 | 解释在下面的`if`语句中条件部分的判断过程。 91 | ```cpp 92 | const char *cp = "Hello World"; 93 | if (cp && *cp) 94 | ``` 95 | 96 | 解: 97 | 98 | 首先判断`cp`,`cp` 不是一个空指针,因此`cp`为真。然后判断`*cp`,`*cp` 的值是字符`'H'`,非0。因此最后的结果为真。 99 | 100 | ## 练习4.10 101 | 102 | 为`while`循环写一个条件,使其从标准输入中读取整数,遇到`42`时停止。 103 | 104 | 解: 105 | 106 | ```cpp 107 | int i; 108 | while(cin >> i && i != 42) 109 | ``` 110 | 111 | ## 练习4.11 112 | 113 | 书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。 114 | 115 | 解: 116 | 117 | ```cpp 118 | a>b && b>c && c>d 119 | ``` 120 | 121 | ## 练习4.12 122 | 123 | 假设`i`、`j`和`k`是三个整数,说明表达式`i != j < k`的含义。 124 | 125 | 解: 126 | 127 | 这个表达式等于`i != (j < k)`。首先得到`j < k`的结果为`true`或`false`,转换为整数值是`1`或`0`,然后判断`i`不等于`1`或`0` ,最终的结果为`bool`值。 128 | 129 | ## 练习4.13 130 | 131 | 在下述语句中,当赋值完成后 i 和 d 的值分别是多少? 132 | 133 | ```cpp 134 | int i; double d; 135 | d = i = 3.5; // i = 3, d = 3.0 136 | i = d = 3.5; // d = 3.5, i = 3 137 | ``` 138 | 139 | ## 练习4.14 140 | 141 | 执行下述 if 语句后将发生什么情况? 142 | 143 | ```cpp 144 | if (42 = i) // 编译错误。赋值运算符左侧必须是一个可修改的左值。而字面值是右值。 145 | if (i = 42) // true. 146 | ``` 147 | 148 | ## 练习4.15 149 | 150 | 下面的赋值是非法的,为什么?应该如何修改? 151 | 152 | ```cpp 153 | double dval; int ival; int *pi; 154 | dval = ival = pi = 0; 155 | ``` 156 | 157 | 解: 158 | `p`是指针,不能赋值给`int`,应该改为: 159 | 160 | ```cpp 161 | dval = ival = 0; 162 | pi = 0; 163 | ``` 164 | 165 | ## 练习4.16 166 | 167 | 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改? 168 | 169 | ```cpp 170 | if (p = getPtr() != 0) 171 | if (i = 1024) 172 | ``` 173 | 174 | 解: 175 | 176 | ```cpp 177 | if ((p=getPtr()) != 0) 178 | if (i == 1024) 179 | ``` 180 | 181 | ## 练习4.17 182 | 183 | 说明前置递增运算符和后置递增运算符的区别。 184 | 185 | 解: 186 | 187 | 前置递增运算符将对象本身作为左值返回,而后置递增运算符将对象原始值的副本作为右值返回。 188 | 189 | ## 练习4.18 190 | 191 | 如果132页那个输出`vector`对象元素的`while`循环使用前置递增运算符,将得到什么结果? 192 | 193 | 解: 194 | 195 | 将会从第二个元素开始取值,并且最后对`v.end()`进行取值,结果是未定义的。 196 | 197 | ## 练习4.19 198 | 199 | 假设`ptr`的类型是指向`int`的指针、`vec`的类型是`vector`、`ival`的类型是`int`,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改? 200 | 201 | ```cpp 202 | (a) ptr != 0 && *ptr++ 203 | (b) ival++ && ival 204 | (c) vec[ival++] <= vec[ival] 205 | ``` 206 | 207 | 解: 208 | 209 | - (a) 判断`ptr`不是一个空指针,并且`ptr`当前指向的元素的值也为真,然后将`ptr`指向下一个元素 210 | - (b) 判断`ival`的值为真,并且`(ival + 1)`的值也为真 211 | - (c) 表达式有误。C++并没有规定`<=`运算符两边的求值顺序,应该改为`vec[ival] <= vec[ival+1]` 212 | 213 | ## 练习4.20 214 | 215 | 假设`iter`的类型是`vector::iterator`, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处? 216 | 217 | ```cpp 218 | (a) *iter++; 219 | (b) (*iter)++; 220 | (c) *iter.empty(); 221 | (d) iter->empty(); 222 | (e) ++*iter; 223 | (f) iter++->empty(); 224 | ``` 225 | 226 | 解: 227 | 228 | - (a)合法。返回迭代器所指向的元素,然后迭代器递增。 229 | - (b)不合法。因为`vector`元素类型是`string`,没有`++`操作。 230 | - (c)不合法。这里应该加括号。 231 | - (d)合法。判断迭代器当前的元素是否为空。 232 | - (e)不合法。`string`类型没有`++`操作。 233 | - (f)合法。判断迭代器当前元素是否为空,然后迭代器递增。 234 | 235 | ## 练习4.21 236 | 237 | 编写一段程序,使用条件运算符从`vector`中找到哪些元素的值是奇数,然后将这些奇数值翻倍。 238 | 239 | 解: 240 | 241 | ```cpp 242 | #include 243 | #include 244 | 245 | using std::cout; 246 | using std::endl; 247 | using std::vector; 248 | 249 | int main() 250 | { 251 | vector ivec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 252 | 253 | for (auto i : ivec) 254 | { 255 | cout << ((i & 0x1) ? i * 2 : i) << " "; 256 | } 257 | cout << endl; 258 | 259 | return 0; 260 | } 261 | ``` 262 | 263 | ## 练习4.22 264 | 265 | 本节的示例程序将成绩划分为`high pass`、`pass` 和 `fail` 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为`low pass`。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个`if`语句。哪个版本的程序更容易理解呢?为什么? 266 | 267 | 解: 268 | 269 | ```cpp 270 | #include 271 | using std::cout; using std::cin; using std::endl; 272 | 273 | int main() 274 | { 275 | for (unsigned g; cin >> g;) 276 | { 277 | auto result = g > 90 ? "high pass" : g < 60 ? "fail" : g < 75 ? "low pass" : "pass"; 278 | cout << result << endl; 279 | 280 | // ------------------------- 281 | if (g > 90) cout << "high pass"; 282 | else if (g < 60) cout << "fail"; 283 | else if (g < 75) cout << "low pass"; 284 | else cout << "pass"; 285 | cout << endl; 286 | } 287 | 288 | return 0; 289 | } 290 | ``` 291 | 第二个版本容易理解。当条件运算符嵌套层数变多之后,代码的可读性急剧下降。而`if else`的逻辑很清晰。 292 | 293 | ## 练习4.23 294 | 295 | 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改? 296 | 297 | ```cpp 298 | string s = "word"; 299 | string pl = s + s[s.size() - 1] == 's' ? "" : "s" ; 300 | ``` 301 | 302 | 解: 303 | 304 | 加法运算符的优先级高于条件运算符。因此要改为: 305 | 306 | ```cpp 307 | string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ; 308 | ``` 309 | 310 | ## 练习4.24 311 | 312 | 本节的示例程序将成绩划分为`high pass`、`pass`、和`fail`三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的? 313 | 314 | 解: 315 | 316 | 如果条件运算符满足的是左结合律。那么 317 | 318 | `finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";` 319 | 等同于 320 | `finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";` 321 | 假如此时 `grade > 90` ,第一个条件表达式的结果是 `"high pass"` ,而字符串字面值的类型是 `const char *`,非空所以为真。因此第二个条件表达式的结果是 `"fail"`。这样就出现了自相矛盾的逻辑。 322 | 323 | ## 练习4.25 324 | 325 | 如果一台机器上`int`占32位、`char`占8位,用的是`Latin-1`字符集,其中字符`'q'` 的二进制形式是`01110001`,那么表达式`~'q' << 6`的值是什么? 326 | 327 | 解: 328 | 329 | 首先将`char`类型提升为`int`类型,即`00000000 00000000 00000000 01110001`,然后取反,再左移6位,结果是-7296。 330 | 331 | ## 练习4.26 332 | 333 | 在本节关于测验成绩的例子中,如果使用`unsigned int` 作为`quiz1` 的类型会发生什么情况? 334 | 335 | 解: 336 | 337 | 在有的机器上,`unsigned int` 类型可能只有 16 位,因此结果是未定义的。 338 | 339 | ## 练习4.27 340 | 341 | 下列表达式的结果是什么? 342 | ```cpp 343 | unsigned long ul1 = 3, ul2 = 7; 344 | (a) ul1 & ul2 345 | (b) ul1 | ul2 346 | (c) ul1 && ul2 347 | (d) ul1 || ul2 348 | ``` 349 | 350 | 解: 351 | 352 | - (a) 3 353 | - (b) 7 354 | - (c) true 355 | - (d) ture 356 | 357 | ## 练习4.28 358 | 359 | 编写一段程序,输出每一种内置类型所占空间的大小。 360 | 361 | 解: 362 | 363 | ```cpp 364 | #include 365 | 366 | using namespace std; 367 | 368 | int main() 369 | { 370 | cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl; 371 | 372 | cout << "char:\t\t" << sizeof(char) << " bytes" << endl; 373 | cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl; 374 | cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl; 375 | cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl; 376 | 377 | cout << "short:\t\t" << sizeof(short) << " bytes" << endl; 378 | cout << "int:\t\t" << sizeof(int) << " bytes" << endl; 379 | cout << "long:\t\t" << sizeof(long) << " bytes" << endl; 380 | cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl; 381 | 382 | cout << "float:\t\t" << sizeof(float) << " bytes" << endl; 383 | cout << "double:\t\t" << sizeof(double) << " bytes" << endl; 384 | cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl; 385 | 386 | return 0; 387 | } 388 | ``` 389 | 390 | 输出: 391 | 392 | ``` 393 | bool: 1 bytes 394 | 395 | char: 1 bytes 396 | wchar_t: 4 bytes 397 | char16_t: 2 bytes 398 | char32_t: 4 bytes 399 | 400 | short: 2 bytes 401 | int: 4 bytes 402 | long: 8 bytes 403 | long long: 8 bytes 404 | 405 | float: 4 bytes 406 | double: 8 bytes 407 | long double: 16 bytes 408 | ``` 409 | 410 | ## 练习4.29 411 | 412 | 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么? 413 | 414 | ```cpp 415 | int x[10]; int *p = x; 416 | cout << sizeof(x)/sizeof(*x) << endl; 417 | cout << sizeof(p)/sizeof(*p) << endl; 418 | ``` 419 | 420 | 解: 421 | 422 | 第一个输出结果是 10。第二个结果1,此处用法不合理不是未定义,参考https://www.geeksforgeeks.org/using-sizof-operator-with-array-paratmeters/。 423 | 424 | ## 练习4.30 425 | 426 | 根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。 427 | 428 | ```cpp 429 | (a) sizeof x + y 430 | (b) sizeof p->mem[i] 431 | (c) sizeof a < b 432 | (d) sizeof f() 433 | ``` 434 | 435 | 解: 436 | 437 | ```cpp 438 | (a) (sizeof x) + y 439 | (b) sizeof(p->mem[i]) 440 | (c) sizeof(a) < b 441 | (d) sizeof(f()) 442 | ``` 443 | 444 | ## 练习4.31 445 | 446 | 本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。 447 | 448 | 解: 449 | 450 | 在4.5节(132页)已经说过了,除非必须,否则不用递增递减运算符的后置版本。在这里要使用后者版本的递增递减运算符不需要任何改动。 451 | 452 | ## 练习4.32 453 | 解释下面这个循环的含义。 454 | 455 | ```cpp 456 | constexpr int size = 5; 457 | int ia[size] = { 1, 2, 3, 4, 5 }; 458 | for (int *ptr = ia, ix = 0; 459 | ix != size && ptr != ia+size; 460 | ++ix, ++ptr) { /* ... */ } 461 | ``` 462 | 463 | 解: 464 | 465 | 这个循环在遍历数组`ia`,指针`ptr`和整型`ix`都是起到一个循环计数的功能。 466 | 467 | ## 练习4.33 468 | 469 | 根据4.12节中的表说明下面这条表达式的含义。 470 | 471 | ```cpp 472 | someValue ? ++x, ++y : --x, --y 473 | ``` 474 | 475 | 解: 476 | 477 | 逗号表达式的优先级是最低的。因此这条表达式也等于: 478 | ```cpp 479 | (someValue ? ++x, ++y : --x), --y 480 | ``` 481 | 如果`someValue`的值为真,`x` 和 `y` 的值都自增并返回 `y` 值,然后丢弃`y`值,`y`递减并返回`y`值。如果`someValue`的值为假,`x` 递减并返回`x` 值,然后丢弃`x`值,`y`递减并返回`y`值。 482 | 483 | 484 | 485 | ## 练习4.34 486 | 487 | 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换: 488 | 489 | ```cpp 490 | (a) if (fval) 491 | (b) dval = fval + ival; 492 | (c) dval + ival * cval; 493 | ``` 494 | 495 | 需要注意每种运算符遵循的是左结合律还是右结合律。 496 | 497 | 解: 498 | 499 | ```cpp 500 | (a) fval 转换为 bool 类型 501 | (b) ival 转换为 float ,相加的结果转换为 double 502 | (c) cval 转换为 int,然后相乘的结果转换为 double 503 | ``` 504 | 505 | ## 练习4.35 506 | 507 | 假设有如下的定义: 508 | 509 | ```cpp 510 | char cval; 511 | int ival; 512 | unsigned int ui; 513 | float fval; 514 | double dval; 515 | ``` 516 | 517 | 请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。 518 | 519 | ```cpp 520 | (a) cval = 'a' + 3; 521 | (b) fval = ui - ival * 1.0; 522 | (c) dval = ui * fval; 523 | (d) cval = ival + fval + dval; 524 | ``` 525 | 526 | 解: 527 | 528 | - (a) `'a'` 转换为 `int` ,然后与 `3` 相加的结果转换为 `char` 529 | - (b) `ival` 转换为 `double`,`ui` 转换为 `double`,结果转换为 `float` 530 | - (c) `ui` 转换为 `float`,结果转换为 `double` 531 | - (d) `ival` 转换为 `float`,与`fval`相加后的结果转换为 `double`,最后的结果转换为`char` 532 | 533 | ## 练习4.36 534 | 535 | 假设 `i` 是`int`类型,`d` 是`double`类型,书写表达式 `i*=d` 使其执行整数类型的乘法而非浮点类型的乘法。 536 | 537 | 解: 538 | 539 | ```cpp 540 | i *= static_cast(d); 541 | ``` 542 | 543 | ## 练习4.37 544 | 545 | 练习4.37 546 | 用命名的强制类型转换改写下列旧式的转换语句。 547 | 548 | ```cpp 549 | int i; double d; const string *ps; char *pc; void *pv; 550 | (a) pv = (void*)ps; 551 | (b) i = int(*pc); 552 | (c) pv = &d; 553 | (d) pc = (char*)pv; 554 | ``` 555 | 556 | 解: 557 | 558 | ```cpp 559 | (a) pv = static_cast(const_cast(ps)); 560 | (b) i = static_cast(*pc); 561 | (c) pv = static_cast(&d); 562 | (d) pc = static_cast(pv); 563 | ``` 564 | 565 | ## 练习4.38 566 | 说明下面这条表达式的含义。 567 | 568 | ```cpp 569 | double slope = static_cast(j/i); 570 | ``` 571 | 572 | 解: 573 | 574 | 将`j/i`的结果值转换为`double`,然后赋值给`slope`。 575 | 576 | 577 | -------------------------------------------------------------------------------- /excersize/ch05.md: -------------------------------------------------------------------------------- 1 | # 第五章 语句 2 | 3 | ## 练习5.1 4 | 5 | 什么是空语句?什么时候会用到空语句? 6 | 7 | 解: 8 | 9 | 只含义一个单独的分号的语句是空语句。如:`;`。 10 | 11 | 如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句。 12 | ```cpp 13 | while (cin >> s && s != sought) 14 | ; 15 | ``` 16 | 17 | ## 练习5.2 18 | 19 | 什么是块?什么时候会用到块? 20 | 21 | 解: 22 | 23 | 用花括号括起来的语句和声明的序列就是块。 24 | ```cpp 25 | { 26 | // ... 27 | } 28 | ``` 29 | 如果在程序的某个地方,语法上需要一条语句,而逻辑上需要多条语句,此时应该使用块 30 | ```cpp 31 | while (val <= 10) { 32 | sum += val; 33 | ++val; 34 | } 35 | ``` 36 | 37 | ## 练习5.3 38 | 使用逗号运算符重写1.4.1节的`while`循环,使它不再需要块,观察改写之后的代码可读性提高了还是降低了。 39 | 40 | ```cpp 41 | while (val <= 10) 42 | sum += val, ++val; 43 | ``` 44 | 代码的可读性反而降低了。 45 | 46 | ## 练习5.4 47 | 48 | 说明下列例子的含义,如果存在问题,试着修改它。 49 | 50 | ```cpp 51 | (a) while (string::iterator iter != s.end()) { /* . . . */ } 52 | 53 | (b) while (bool status = find(word)) { /* . . . */ } 54 | if (!status) { /* . . . */ } 55 | ``` 56 | 57 | 解: 58 | 59 | - (a) 这个循环试图用迭代器遍历`string`,但是变量的定义应该放在循环的外面,目前每次循环都会重新定义一个变量,明显是错误的。 60 | - (b) 这个循环的`while`和`if`是两个独立的语句,`if`语句中无法访问`status`变量,正确的做法是应该将`if`语句包含在`while`里面。 61 | 62 | ## 练习5.5 63 | 写一段自己的程序,使用`if else`语句实现把数字转换为字母成绩的要求。 64 | 65 | ```cpp 66 | #include 67 | #include 68 | #include 69 | using std::vector; using std::string; using std::cout; using std::endl; using std::cin; 70 | 71 | int main() 72 | { 73 | vector scores = { "F", "D", "C", "B", "A", "A++" }; 74 | for (int g; cin >> g;) 75 | { 76 | string letter; 77 | if (g < 60) 78 | { 79 | letter = scores[0]; 80 | } 81 | else 82 | { 83 | letter = scores[(g - 50) / 10]; 84 | if (g != 100) 85 | letter += g % 10 > 7 ? "+" : g % 10 < 3 ? "-" : ""; 86 | } 87 | cout << letter << endl; 88 | } 89 | 90 | return 0; 91 | } 92 | ``` 93 | 94 | ## 练习5.6 95 | 96 | 改写上一题的程序,使用条件运算符代替`if else`语句。 97 | 98 | ```cpp 99 | #include 100 | #include 101 | #include 102 | using std::vector; using std::string; using std::cout; using std::endl; using std::cin; 103 | 104 | int main() 105 | { 106 | vector scores = { "F", "D", "C", "B", "A", "A++" }; 107 | 108 | int grade = 0; 109 | while (cin >> grade) 110 | { 111 | string lettergrade = grade < 60 ? scores[0] : scores[(grade - 50) / 10]; 112 | lettergrade += (grade == 100 || grade < 60) ? "" : (grade % 10 > 7) ? "+" : (grade % 10 < 3) ? "-" : ""; 113 | cout << lettergrade << endl; 114 | } 115 | 116 | return 0; 117 | } 118 | ``` 119 | 120 | ## 练习5.7 121 | 改写下列代码段中的错误。 122 | 123 | ```cpp 124 | (a) if (ival1 != ival2) 125 | ival1 = ival2 126 | else 127 | ival1 = ival2 = 0; 128 | (b) if (ival < minval) 129 | minval = ival; 130 | occurs = 1; 131 | (c) if (int ival = get_value()) 132 | cout << "ival = " << ival << endl; 133 | if (!ival) 134 | cout << "ival = 0\n"; 135 | (d) if (ival = 0) 136 | ival = get_value(); 137 | ``` 138 | 139 | 解: 140 | 141 | - (a) `ival1 = ival2` 后面少了分号。 142 | - (b) 应该用花括号括起来。 143 | - (c) `if (!ival)` 应该改为 `else`。 144 | - (d) `if (ival = 0)` 应该改为 `if (ival == 0)`。 145 | 146 | ## 练习5.8 147 | 什么是“悬垂else”?C++语言是如何处理else子句的? 148 | 149 | 解: 150 | 151 | 用来描述在嵌套的`if else`语句中,如果`if`比`else`多时如何处理的问题。C++使用的方法是`else`匹配最近没有配对的`if`。 152 | 153 | ## 练习5.9 154 | 编写一段程序,使用一系列`if`语句统计从`cin`读入的文本中有多少元音字母。 155 | 156 | 解: 157 | 158 | ```cpp 159 | #include 160 | 161 | using std::cout; using std::endl; using std::cin; 162 | 163 | int main() 164 | { 165 | unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0; 166 | char ch; 167 | while (cin >> ch) 168 | { 169 | if (ch == 'a') ++aCnt; 170 | else if (ch == 'e') ++eCnt; 171 | else if (ch == 'i') ++iCnt; 172 | else if (ch == 'o') ++oCnt; 173 | else if (ch == 'u') ++uCnt; 174 | } 175 | cout << "Number of vowel a: \t" << aCnt << '\n' 176 | << "Number of vowel e: \t" << eCnt << '\n' 177 | << "Number of vowel i: \t" << iCnt << '\n' 178 | << "Number of vowel o: \t" << oCnt << '\n' 179 | << "Number of vowel u: \t" << uCnt << endl; 180 | 181 | return 0; 182 | } 183 | ``` 184 | 185 | ## 练习5.10 186 | 我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计元音字母的大写形式,也就是说,新程序遇到'a'和'A'都应该递增`aCnt`的值,以此类推。 187 | 188 | 解: 189 | 190 | ```cpp 191 | #include 192 | using std::cin; using std::cout; using std::endl; 193 | 194 | int main() 195 | { 196 | unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0; 197 | char ch; 198 | while (cin >> ch) 199 | switch (ch) 200 | { 201 | case 'a': 202 | case 'A': 203 | ++aCnt; 204 | break; 205 | case 'e': 206 | case 'E': 207 | ++eCnt; 208 | break; 209 | case 'i': 210 | case 'I': 211 | ++iCnt; 212 | break; 213 | case 'o': 214 | case 'O': 215 | ++oCnt; 216 | break; 217 | case 'u': 218 | case 'U': 219 | ++uCnt; 220 | break; 221 | } 222 | 223 | cout << "Number of vowel a(A): \t" << aCnt << '\n' 224 | << "Number of vowel e(E): \t" << eCnt << '\n' 225 | << "Number of vowel i(I): \t" << iCnt << '\n' 226 | << "Number of vowel o(O): \t" << oCnt << '\n' 227 | << "Number of vowel u(U): \t" << uCnt << endl; 228 | 229 | return 0; 230 | } 231 | ``` 232 | 233 | ## 练习5.11 234 | 修改统计元音字母的程序,使其也能统计空格、制表符、和换行符的数量。 235 | 236 | 解: 237 | 238 | ```cpp 239 | #include 240 | 241 | using std::cin; using std::cout; using std::endl; 242 | 243 | int main() 244 | { 245 | unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0; 246 | char ch; 247 | while (cin >> std::noskipws >> ch) //noskipws(no skip whitespce) 248 | switch (ch) 249 | { 250 | case 'a': 251 | case 'A': 252 | ++aCnt; 253 | break; 254 | case 'e': 255 | case 'E': 256 | ++eCnt; 257 | break; 258 | case 'i': 259 | case 'I': 260 | ++iCnt; 261 | break; 262 | case 'o': 263 | case 'O': 264 | ++oCnt; 265 | break; 266 | case 'u': 267 | case 'U': 268 | ++uCnt; 269 | break; 270 | case ' ': 271 | ++spaceCnt; 272 | break; 273 | case '\t': 274 | ++tabCnt; 275 | break; 276 | case '\n': 277 | ++newLineCnt; 278 | break; 279 | } 280 | 281 | cout << "Number of vowel a(A): \t" << aCnt << '\n' 282 | << "Number of vowel e(E): \t" << eCnt << '\n' 283 | << "Number of vowel i(I): \t" << iCnt << '\n' 284 | << "Number of vowel o(O): \t" << oCnt << '\n' 285 | << "Number of vowel u(U): \t" << uCnt << '\n' 286 | << "Number of space: \t" << spaceCnt << '\n' 287 | << "Number of tab char: \t" << tabCnt << '\n' 288 | << "Number of new line: \t" << newLineCnt << endl; 289 | 290 | return 0; 291 | } 292 | ``` 293 | 294 | 其中,使用 `std::noskipws`可以保留默认跳过的空格。 295 | 296 | ## 练习5.12 297 | 修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量:`ff`、`fl`和`fi`。 298 | 299 | 解: 300 | 301 | ```cpp 302 | #include 303 | 304 | using std::cin; using std::cout; using std::endl; 305 | 306 | int main() 307 | { 308 | unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0, ffCnt = 0, flCnt = 0, fiCnt = 0; 309 | char ch, prech = '\0'; 310 | while (cin >> std::noskipws >> ch) 311 | { 312 | switch (ch) 313 | { 314 | case 'a': 315 | case 'A': 316 | ++aCnt; 317 | break; 318 | case 'e': 319 | case 'E': 320 | ++eCnt; 321 | break; 322 | case 'i': 323 | if (prech == 'f') ++fiCnt; 324 | case 'I': 325 | ++iCnt; 326 | break; 327 | case 'o': 328 | case 'O': 329 | ++oCnt; 330 | break; 331 | case 'u': 332 | case 'U': 333 | ++uCnt; 334 | break; 335 | case ' ': 336 | ++spaceCnt; 337 | break; 338 | case '\t': 339 | ++tabCnt; 340 | break; 341 | case '\n': 342 | ++newLineCnt; 343 | break; 344 | case 'f': 345 | if (prech == 'f') ++ffCnt; 346 | break; 347 | case 'l': 348 | if (prech == 'f') ++flCnt; 349 | break; 350 | } 351 | prech = ch; 352 | } 353 | 354 | cout << "Number of vowel a(A): \t" << aCnt << '\n' 355 | << "Number of vowel e(E): \t" << eCnt << '\n' 356 | << "Number of vowel i(I): \t" << iCnt << '\n' 357 | << "Number of vowel o(O): \t" << oCnt << '\n' 358 | << "Number of vowel u(U): \t" << uCnt << '\n' 359 | << "Number of space: \t" << spaceCnt << '\n' 360 | << "Number of tab char: \t" << tabCnt << '\n' 361 | << "Number of new line: \t" << newLineCnt << '\n' 362 | << "Number of ff: \t" << ffCnt << '\n' 363 | << "Number of fl: \t" << flCnt << '\n' 364 | << "Number of fi: \t" << fiCnt << endl; 365 | 366 | return 0; 367 | } 368 | 369 | ``` 370 | 371 | ## 练习5.13 372 | 下面显示的每个程序都含有一个常见的编码错误,指出错误在哪里,然后修改它们。 373 | ```cpp 374 | (a) unsigned aCnt = 0, eCnt = 0, iouCnt = 0; 375 | char ch = next_text(); 376 | switch (ch) { 377 | case 'a': aCnt++; 378 | case 'e': eCnt++; 379 | default: iouCnt++; 380 | } 381 | (b) unsigned index = some_value(); 382 | switch (index) { 383 | case 1: 384 | int ix = get_value(); 385 | ivec[ ix ] = index; 386 | break; 387 | default: 388 | ix = ivec.size()-1; 389 | ivec[ ix ] = index; 390 | } 391 | (c) unsigned evenCnt = 0, oddCnt = 0; 392 | int digit = get_num() % 10; 393 | switch (digit) { 394 | case 1, 3, 5, 7, 9: 395 | oddcnt++; 396 | break; 397 | case 2, 4, 6, 8, 10: 398 | evencnt++; 399 | break; 400 | } 401 | (d) unsigned ival=512, jval=1024, kval=4096; 402 | unsigned bufsize; 403 | unsigned swt = get_bufCnt(); 404 | switch(swt) { 405 | case ival: 406 | bufsize = ival * sizeof(int); 407 | break; 408 | case jval: 409 | bufsize = jval * sizeof(int); 410 | break; 411 | case kval: 412 | bufsize = kval * sizeof(int); 413 | break; 414 | } 415 | ``` 416 | 417 | 解: 418 | 419 | (a) 少了`break`语句。应该为: 420 | ```cpp 421 | unsigned aCnt = 0, eCnt = 0, iouCnt = 0; 422 | char ch = next_text(); 423 | switch (ch) { 424 | case 'a': aCnt++; break; 425 | case 'e': eCnt++; break; 426 | default: iouCnt++; break; 427 | } 428 | ``` 429 | 430 | (b) 在`default`分支当中,`ix`未定义。应该在外部定义`ix`。 431 | ```cpp 432 | unsigned index = some_value(); 433 | int ix; 434 | switch (index) { 435 | case 1: 436 | ix = get_value(); 437 | ivec[ ix ] = index; 438 | break; 439 | default: 440 | ix = static_cast(ivec.size())-1; 441 | ivec[ ix ] = index; 442 | } 443 | ``` 444 | 445 | (c) `case`后面应该用冒号而不是逗号。 446 | ```cpp 447 | unsigned evenCnt = 0, oddCnt = 0; 448 | int digit = get_num() % 10; 449 | switch (digit) { 450 | case 1: case 3: case 5: case 7: case 9: 451 | oddcnt++; 452 | break; 453 | case 2: case 4: case 6: case 8: case 0: 454 | evencnt++; 455 | break; 456 | } 457 | ``` 458 | 459 | (d) `case`标签必须是整型常量表达式。 460 | ```cpp 461 | const unsigned ival=512, jval=1024, kval=4096; 462 | unsigned bufsize; 463 | unsigned swt = get_bufCnt(); 464 | switch(swt) { 465 | case ival: 466 | bufsize = ival * sizeof(int); 467 | break; 468 | case jval: 469 | bufsize = jval * sizeof(int); 470 | break; 471 | case kval: 472 | bufsize = kval * sizeof(int); 473 | break; 474 | } 475 | ``` 476 | 477 | ## 练习5.14 478 | 编写一段程序,从标准输入中读取若干`string`对象并查找连续重复出现的单词,所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。 479 | 例如:如果输入是: 480 | ``` 481 | how now now now brown cow cow 482 | ``` 483 | 那么输出应该表明单词now连续出现了3次。 484 | 485 | 解: 486 | 487 | ```cpp 488 | #include 489 | #include 490 | 491 | using std::cout; using std::cin; using std::endl; using std::string; using std::pair; 492 | 493 | int main() 494 | { 495 | pair max_duplicated; 496 | int count = 0; 497 | for (string str, prestr; cin >> str; prestr = str) 498 | { 499 | if (str == prestr) ++count; 500 | else count = 0; 501 | if (count > max_duplicated.second) max_duplicated = { prestr, count }; 502 | } 503 | 504 | if (max_duplicated.first.empty()) cout << "There's no duplicated string." << endl; 505 | else cout << "the word " << max_duplicated.first << " occurred " << max_duplicated.second + 1 << " times. " << endl; 506 | 507 | return 0; 508 | } 509 | ``` 510 | 511 | ## 练习5.15 512 | 说明下列循环的含义并改正其中的错误。 513 | 514 | ```cpp 515 | (a) for (int ix = 0; ix != sz; ++ix) { /* ... */ } 516 | if (ix != sz) 517 | // . . . 518 | (b) int ix; 519 | for (ix != sz; ++ix) { /* ... */ } 520 | (c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ } 521 | ``` 522 | 523 | 解: 524 | 525 | 应该改为下面这样: 526 | 527 | ```cpp 528 | (a) int ix; 529 | for (ix = 0; ix != sz; ++ix) { /* ... */ } 530 | if (ix != sz) 531 | // . . . 532 | (b) int ix; 533 | for (; ix != sz; ++ix) { /* ... */ } 534 | (c) for (int ix = 0; ix != sz; ++ix) { /*...*/ } 535 | ``` 536 | 537 | ## 练习5.16 538 | 539 | `while`循环特别适用于那种条件不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。 540 | `for`循环更像是在按步骤迭代,它的索引值在某个范围内一次变化。根据每种循环的习惯各自编写一段程序,然后分别用另一种循环改写。 541 | 如果只能使用一种循环,你倾向于哪种?为什么? 542 | 543 | 解: 544 | 545 | ```cpp 546 | int i; 547 | while ( cin >> i ) 548 | // ... 549 | 550 | 551 | for (int i = 0; cin >> i;) 552 | // ... 553 | 554 | 555 | for (int i = 0; i != size; ++i) 556 | // ... 557 | 558 | 559 | int i = 0; 560 | while (i != size) 561 | { 562 | // ... 563 | ++i; 564 | } 565 | ``` 566 | 如果只能用一种循环,我会更倾向使用`while`,因为`while`显得简洁,代码可读性强。 567 | 568 | ## 练习5.17 569 | 570 | 假设有两个包含整数的`vector`对象,编写一段程序,检验其中一个`vector`对象是否是另一个的前缀。 571 | 为了实现这一目标,对于两个不等长的`vector`对象,只需挑出长度较短的那个,把它的所有元素和另一个`vector`对象比较即可。 572 | 例如,如果两个`vector`对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8,则程序的返回结果为真。 573 | 574 | 解: 575 | 576 | ```cpp 577 | #include 578 | #include 579 | 580 | using std::cout; using std::vector; 581 | 582 | bool is_prefix(vector const& lhs, vector const& rhs) 583 | { 584 | if(lhs.size() > rhs.size()) 585 | return is_prefix(rhs, lhs); 586 | for(unsigned i = 0; i != lhs.size(); ++i) 587 | if(lhs[i] != rhs[i]) return false; 588 | return true; 589 | } 590 | 591 | int main() 592 | { 593 | vector l{ 0, 1, 1, 2 }; 594 | vector r{ 0, 1, 1, 2, 3, 5, 8 }; 595 | cout << (is_prefix(r, l) ? "yes\n" : "no\n"); 596 | 597 | return 0; 598 | } 599 | ``` 600 | 601 | ## 练习5.18 602 | 说明下列循环的含义并改正其中的错误。 603 | 604 | ```cpp 605 | (a) do { // 应该添加花括号 606 | int v1, v2; 607 | cout << "Please enter two numbers to sum:" ; 608 | if (cin >> v1 >> v2) 609 | cout << "Sum is: " << v1 + v2 << endl; 610 | }while (cin); 611 | (b) int ival; 612 | do { 613 | // . . . 614 | } while (ival = get_response()); // 应该将ival 定义在循环外 615 | (c) int ival = get_response(); 616 | do { 617 | ival = get_response(); 618 | } while (ival); // 应该将ival 定义在循环外 619 | ``` 620 | 621 | ## 练习5.19 622 | 编写一段程序,使用`do while`循环重复地执行下述任务: 623 | 首先提示用户输入两个`string`对象,然后挑出较短的那个并输出它。 624 | 625 | 解: 626 | 627 | ```cpp 628 | #include 629 | #include 630 | 631 | using std::cout; using std::cin; using std::endl; using std::string; 632 | 633 | int main() 634 | { 635 | string rsp; 636 | do { 637 | cout << "Input two strings: "; 638 | string str1, str2; 639 | cin >> str1 >> str2; 640 | cout << (str1 <= str2 ? str1 : str2) 641 | << " is less than the other. " << "\n\n" 642 | << "More? Enter yes or no: "; 643 | cin >> rsp; 644 | } while (!rsp.empty() && tolower(rsp[0]) == 'y'); 645 | return 0; 646 | } 647 | ``` 648 | 649 | ## 练习5.20 650 | 编写一段程序,从标准输入中读取`string`对象的序列直到连续出现两个相同的单词或者所有的单词都读完为止。 651 | 使用`while`循环一次读取一个单词,当一个单词连续出现两次时使用`break`语句终止循环。 652 | 输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。 653 | 654 | 解: 655 | 656 | ```cpp 657 | #include 658 | #include 659 | using std::cout; using std::cin; using std::endl; using std::string; 660 | 661 | int main() 662 | { 663 | string read, tmp; 664 | while (cin >> read) 665 | if (read == tmp) break; else tmp = read; 666 | 667 | if (cin.eof()) cout << "no word was repeated." << endl; //eof(end of file)判断输入是否结束,或者文件结束符,等同于 CTRL+Z 668 | else cout << read << " occurs twice in succession." << endl; 669 | 670 | return 0; 671 | } 672 | ``` 673 | 674 | ## 练习5.21 675 | 修改5.5.1节练习题的程序,使其找到的重复单词必须以大写字母开头。 676 | 677 | 解: 678 | 679 | ```cpp 680 | #include 681 | using std::cin; using std::cout; using std::endl; 682 | #include 683 | using std::string; 684 | 685 | int main() 686 | { 687 | string curr, prev; 688 | bool no_twice = true; 689 | while (cin >> curr) 690 | { 691 | if (isupper(curr[0]) && prev == curr) 692 | { 693 | cout << curr << ": occurs twice in succession." << endl; 694 | no_twice = false; 695 | break; 696 | } 697 | prev = curr; 698 | } 699 | 700 | if (no_twice) 701 | cout << "no word was repeated." << endl; 702 | 703 | return 0; 704 | } 705 | ``` 706 | 707 | ## 练习5.22 708 | 本节的最后一个例子跳回到`begin`,其实使用循环能更好的完成该任务,重写这段代码,注意不再使用`goto`语句。 709 | 710 | ```cpp 711 | // 向后跳过一个带初始化的变量定义是合法的 712 | begin: 713 | int sz = get_size(); 714 | if (sz <= 0) { 715 | goto begin; 716 | } 717 | ``` 718 | 719 | 解: 720 | 721 | 用 for 循环修改的话就是这样 722 | ```cpp 723 | for (int sz = get_size(); sz <=0; sz = get_size()) 724 | ; 725 | ``` 726 | 727 | ## 练习5.23 728 | 编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。 729 | 730 | 解: 731 | 732 | ```cpp 733 | #include 734 | using std::cin; 735 | using std::cout; 736 | using std::endl; 737 | 738 | int main() 739 | { 740 | int i, j; 741 | cin >> i >> j; 742 | cout << i / j << endl; 743 | 744 | return 0; 745 | } 746 | ``` 747 | 748 | ## 练习5.24 749 | 修改你的程序,使得当第二个数是0时抛出异常。先不要设定`catch`子句,运行程序并真的为除数输入0,看看会发生什么? 750 | 751 | 解: 752 | 753 | ```cpp 754 | #include 755 | #include 756 | 757 | int main(void) 758 | { 759 | int i, j; 760 | std::cin >> i >> j; 761 | if (j == 0) 762 | throw std::runtime_error("divisor is 0"); 763 | std::cout << i / j << std::endl; 764 | 765 | return 0; 766 | } 767 | ``` 768 | 769 | ## 练习5.25 770 | 修改上一题的程序,使用`try`语句块去捕获异常。`catch`子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行`try`语句块的内容。 771 | 772 | 解: 773 | 774 | ```cpp 775 | #include 776 | #include 777 | using std::cin; using std::cout; using std::endl; using std::runtime_error; 778 | 779 | int main(void) 780 | { 781 | for (int i, j; cout << "Input two integers:\n", cin >> i >> j; ) 782 | { 783 | try 784 | { 785 | if (j == 0) 786 | throw runtime_error("divisor is 0"); 787 | cout << i / j << endl; 788 | } 789 | catch (runtime_error err) 790 | { 791 | cout << err.what() << "\nTry again? Enter y or n" << endl; 792 | char c; 793 | cin >> c; 794 | if (!cin || c == 'n') 795 | break; 796 | } 797 | } 798 | 799 | return 0; 800 | } 801 | ``` -------------------------------------------------------------------------------- /excersize/ch06.md: -------------------------------------------------------------------------------- 1 | # 第六章 函数 2 | 3 | ## 练习6.1 4 | 实参和形参的区别的什么? 5 | 6 | 解: 7 | 8 | 实参是函数调用的实际值,是形参的初始值。 9 | 10 | ## 练习6.2 11 | 请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢? 12 | 13 | ```cpp 14 | (a) int f() { 15 | string s; 16 | // ... 17 | return s; 18 | } 19 | (b) f2(int i) { /* ... */ } 20 | (c) int calc(int v1, int v1) { /* ... */ } 21 | (d) double square (double x) return x * x; 22 | ``` 23 | 24 | 解: 25 | 26 | 应该改为下面这样: 27 | 28 | ```cpp 29 | (a) string f() { 30 | string s; 31 | // ... 32 | return s; 33 | } 34 | (b) void f2(int i) { /* ... */ } 35 | (c) int calc(int v1, int v2) { /* ... */ return ; } 36 | (d) double square (double x) { return x * x; } 37 | ``` 38 | 39 | ## 练习6.3 40 | 编写你自己的`fact`函数,上机检查是否正确。注:阶乘。 41 | 42 | 解: 43 | 44 | ```cpp 45 | #include 46 | 47 | int fact(int i) 48 | { 49 | if(i<0) 50 | { 51 | std::runtime_error err("Input cannot be a negative number"); 52 | std::cout << err.what() << std::endl; 53 | } 54 | return i > 1 ? i * fact( i - 1 ) : 1; 55 | } 56 | 57 | int main() 58 | { 59 | std::cout << std::boolalpha << (120 == fact(5)) << std::endl; 60 | return 0; 61 | } 62 | ``` 63 | 64 | 启用`std::boolalpha`,可以输出 `"true"`或者 `"false"`。 65 | 66 | ## 练习6.4 67 | 编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。 68 | 69 | ```cpp 70 | #include 71 | #include 72 | 73 | int fact(int i) 74 | { 75 | return i > 1 ? i * fact(i - 1) : 1; 76 | } 77 | 78 | void interactive_fact() 79 | { 80 | std::string const prompt = "Enter a number within [1, 13) :\n"; 81 | std::string const out_of_range = "Out of range, please try again.\n"; 82 | for (int i; std::cout << prompt, std::cin >> i; ) 83 | { 84 | if (i < 1 || i > 12) 85 | { 86 | std::cout << out_of_range; 87 | continue; 88 | } 89 | std::cout << fact(i) << std::endl; 90 | } 91 | } 92 | 93 | int main() 94 | { 95 | interactive_fact(); 96 | return 0; 97 | } 98 | ``` 99 | 100 | ## 练习6.5 101 | 编写一个函数输出其实参的绝对值。 102 | 103 | ```cpp 104 | #include 105 | 106 | int abs(int i) 107 | { 108 | return i > 0 ? i : -i; 109 | } 110 | 111 | int main() 112 | { 113 | std::cout << abs(-5) << std::endl; 114 | return 0; 115 | } 116 | ``` 117 | 118 | ## 练习6.6 119 | 说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时达到这三种形式。 120 | 121 | 解: 122 | 123 | 形参定义在函数形参列表里面;局部变量定义在代码块里面; 124 | 局部静态变量在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止时才被销毁。 125 | 126 | ```cpp 127 | // 例子 128 | int count_add(int n) // n是形参 129 | { 130 | static int ctr = 0; // ctr 是局部静态变量 131 | ctr += n; 132 | return ctr; 133 | } 134 | 135 | int main() 136 | { 137 | for (int i = 0; i != 10; ++i) // i 是局部变量 138 | cout << count_add(i) << endl; 139 | 140 | return 0; 141 | } 142 | ``` 143 | 144 | ## 练习6.7 145 | 编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。 146 | 147 | 解: 148 | 149 | ```cpp 150 | int generate() 151 | { 152 | static int ctr = 0; 153 | return ctr++; 154 | } 155 | ``` 156 | 157 | ## 练习6.8 158 | 编写一个名为Chapter6.h 的头文件,令其包含6.1节练习中的函数声明。 159 | 160 | 解: 161 | 162 | ```cpp 163 | 164 | int fact(int val); 165 | int func(); 166 | 167 | template //参考:https://blog.csdn.net/fightingforcv/article/details/51472586 168 | 169 | T abs(T i) 170 | { 171 | return i >= 0 ? i : -i; 172 | } 173 | ``` 174 | 175 | ## 练习6.9 : fact.cc | factMain.cc 176 | 编写你自己的fact.cc 和factMain.cc ,这两个文件都应该包含上一小节的练习中编写的 Chapter6.h 头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。 177 | 178 | 解: 179 | 180 | fact.cc: 181 | 182 | ```cpp 183 | #include "Chapter6.h" 184 | #include 185 | 186 | int fact(int val) 187 | { 188 | if (val == 0 || val == 1) return 1; 189 | else return val * fact(val-1); 190 | } 191 | 192 | int func() 193 | { 194 | int n, ret = 1; 195 | std::cout << "input a number: "; 196 | std::cin >> n; 197 | while (n > 1) ret *= n--; 198 | return ret; 199 | } 200 | 201 | ``` 202 | 203 | factMain.cc: 204 | 205 | ```cpp 206 | #include "Chapter6.h" 207 | #include 208 | 209 | int main() 210 | { 211 | std::cout << "5! is " << fact(5) << std::endl; 212 | std::cout << func() << std::endl; 213 | std::cout << abs(-9.78) << std::endl; 214 | } 215 | ``` 216 | 217 | 编译: `g++ factMain.cpp fact.cpp -o main` 218 | 219 | ## 练习6.10 220 | 编写一个函数,使用指针形参交换两个整数的值。 221 | 在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。 222 | 223 | 解: 224 | 225 | ```cpp 226 | #include 227 | #include 228 | 229 | void swap(int* lhs, int* rhs) 230 | { 231 | int tmp; 232 | tmp = *lhs; 233 | *lhs = *rhs; 234 | *rhs = tmp; 235 | } 236 | 237 | int main() 238 | { 239 | for (int lft, rht; std::cout << "Please Enter:\n", std::cin >> lft >> rht;) 240 | { 241 | swap(&lft, &rht); 242 | std::cout << lft << " " << rht << std::endl; 243 | } 244 | 245 | return 0; 246 | } 247 | ``` 248 | 249 | ## 练习6.11 250 | 编写并验证你自己的reset函数,使其作用于引用类型的参数。注:reset即置0。 251 | 252 | 解: 253 | 254 | ```cpp 255 | #include 256 | 257 | void reset(int &i) 258 | { 259 | i = 0; 260 | } 261 | 262 | int main() 263 | { 264 | int i = 42; 265 | reset(i); 266 | std::cout << i << std::endl; 267 | return 0; 268 | } 269 | ``` 270 | 271 | ## 练习6.12 272 | 改写6.2.1节练习中的程序,使其引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么? 273 | 274 | ```cpp 275 | #include 276 | #include 277 | 278 | 279 | void swap(int& lhs, int& rhs) 280 | { 281 | int temp = lhs; 282 | lhs = rhs; 283 | rhs = temp; 284 | } 285 | 286 | int main() 287 | { 288 | for (int left, right; std::cout << "Please Enter:\n", std::cin >> left >> right; ) 289 | { 290 | swap(left, right); 291 | std::cout << left << " " << right << std::endl; 292 | } 293 | 294 | return 0; 295 | } 296 | ``` 297 | 很明显引用更好用。 298 | 299 | ## 练习6.13 300 | 假设`T`是某种类型的名字,说明以下两个函数声明的区别: 301 | 一个是`void f(T)`, 另一个是`void f(&T)`。 302 | 303 | 解: 304 | 305 | `void f(T)`的参数通过值传递,在函数中`T`是实参的副本,改变`T`不会影响到原来的实参。 306 | `void f(&T)`的参数通过引用传递,在函数中的`T`是实参的引用,`T`的改变也就是实参的改变。 307 | 308 | ## 练习6.14 309 | 举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。 310 | 311 | 解: 312 | 313 | 例如交换两个整数的函数,形参应该是引用 314 | 315 | ```cpp 316 | void swap(int& lhs, int& rhs) 317 | { 318 | int temp = lhs; 319 | lhs = rhs; 320 | rhs = temp; 321 | } 322 | ``` 323 | 324 | 当实参的值是右值时,形参不能为引用类型 325 | 326 | ```cpp 327 | int add(int a, int b) 328 | { 329 | return a + b; 330 | } 331 | 332 | int main() 333 | { 334 | int i = add(1,2); 335 | return 0; 336 | } 337 | ``` 338 | 339 | ## 练习6.15 340 | 说明`find_char`函数中的三个形参为什么是现在的类型,特别说明为什么`s`是常量引用而`occurs`是普通引用? 341 | 为什么`s`和`occurs`是引用类型而`c`不是? 342 | 如果令`s`是普通引用会发生什么情况? 343 | 如果令`occurs`是常量引用会发生什么情况? 344 | 345 | 解: 346 | 347 | - 因为字符串可能很长,因此使用引用避免拷贝; 348 | - 而在函数中我们不希望改变`s`的内容,所以令`s`为常量。 349 | - `occurs`是要传到函数外部的变量,所以使用引用,`occurs`的值会改变,所以是普通引用。 350 | - 因为我们只需要`c`的值,这个实参可能是右值(右值实参无法用于引用形参),所以`c`不能用引用类型。 351 | - 如果`s`是普通引用,也可能会意外改变原来字符串的内容。 352 | - `occurs`如果是常量引用,那么意味着不能改变它的值,那也就失去意义了。 353 | 354 | ## 练习6.16 355 | 下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。 356 | ```cpp 357 | bool is_empty(string& s) { return s.empty(); } 358 | ``` 359 | 360 | 解: 361 | 362 | 局限性在于常量字符串和字符串字面值无法作为该函数的实参,如果下面这样调用是非法的: 363 | 364 | ```cpp 365 | const string str; 366 | bool flag = is_empty(str); //非法 367 | bool flag = is_empty("hello"); //非法 368 | ``` 369 | 370 | 所以要将这个函数的形参定义为常量引用: 371 | 372 | ```cpp 373 | bool is_empty(const string& s) { return s.empty(); } 374 | ``` 375 | 376 | ## 练习6.17 377 | 编写一个函数,判断`string`对象中是否含有大写字母。 378 | 编写另一个函数,把`string`对象全部改写成小写形式。 379 | 在这两个函数中你使用的形参类型相同吗?为什么? 380 | 381 | 解: 382 | 383 | 两个函数的形参不一样。第一个函数使用常量引用,第二个函数使用普通引用。 384 | 385 | ## 练习6.18 386 | 为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。 387 | 388 | - (a) 名为`compare`的函数,返回布尔值,两个参数都是`matrix`类的引用。 389 | - (b) 名为`change_val`的函数,返回`vector`的迭代器,有两个参数:一个是`int`,另一个是`vector`的迭代器。 390 | 391 | 解: 392 | 393 | ```cpp 394 | (a) bool compare(matrix &m1, matrix &m2); 395 | (b) vector::iterator change_val(int, vector::iterator); 396 | ``` 397 | 398 | ## 练习6.19 399 | 假定有如下声明,判断哪个调用合法、哪个调用不合法。对于不合法的函数调用,说明原因。 400 | 401 | ```cpp 402 | double calc(double); 403 | int count(const string &, char); 404 | int sum(vector::iterator, vector::iterator, int); 405 | vector vec(10); 406 | (a) calc(23.4, 55.1); 407 | (b) count("abcda",'a'); 408 | (c) calc(66); 409 | (d) sum(vec.begin(), vec.end(), 3.8); 410 | ``` 411 | 412 | 解: 413 | 414 | - (a) 不合法。`calc`只有一个参数。 415 | - (b) 合法。 416 | - (c) 合法。 417 | - (d) 合法。 418 | 419 | ## 练习6.20 420 | 引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况? 421 | 422 | 解: 423 | 424 | 应该尽量将引用形参设为常量引用,除非有明确的目的是为了改变这个引用变量。 425 | 如果形参应该是常量引用,而我们将其设为了普通引用,那么常量实参将无法作用于普通引用形参。 426 | 427 | ## 练习6.21 428 | 编写一个函数,令其接受两个参数:一个是`int`型的数,另一个是`int`指针。 429 | 函数比较`int`的值和指针所指的值,返回较大的那个。 430 | 在该函数中指针的类型应该是什么? 431 | 432 | 解: 433 | 434 | ```cpp 435 | #include 436 | using std::cout; 437 | 438 | int larger_one(const int i, const int *const p) 439 | { 440 | return (i > *p) ? i : *p; 441 | } 442 | 443 | int main() 444 | { 445 | int i = 6; 446 | cout << larger_one(7, &i); 447 | 448 | return 0; 449 | } 450 | ``` 451 | 452 | 应该是`const int *`类型。 453 | 454 | ## 练习6.22 455 | 编写一个函数,令其交换两个`int`指针。 456 | 457 | 解: 458 | 459 | ```cpp 460 | #include 461 | #include 462 | 463 | void swap(int*& lft, int*& rht) 464 | { 465 | auto tmp = lft; 466 | lft = rht; 467 | rht = tmp; 468 | } 469 | 470 | int main() 471 | { 472 | int i = 42, j = 99; 473 | auto lft = &i; 474 | auto rht = &j; 475 | swap(lft, rht); 476 | std::cout << *lft << " " << *rht << std::endl; 477 | 478 | return 0; 479 | } 480 | ``` 481 | 482 | ## 练习6.23 483 | 参考本节介绍的几个`print`函数,根据理解编写你自己的版本。 484 | 依次调用每个函数使其输入下面定义的`i`和`j`: 485 | 486 | ```cpp 487 | int i = 0, j[2] = { 0, 1 }; 488 | ``` 489 | 490 | 解: 491 | 492 | ```cpp 493 | #include 494 | using std::cout; using std::endl; using std::begin; using std::end; 495 | 496 | void print(const int *pi) 497 | { 498 | if(pi) 499 | cout << *pi << endl; 500 | } 501 | 502 | void print(const char *p) 503 | { 504 | if (p) 505 | while (*p) cout << *p++; 506 | cout << endl; 507 | } 508 | 509 | void print(const int *beg, const int *end) 510 | { 511 | while (beg != end) 512 | cout << *beg++ << endl; 513 | } 514 | 515 | void print(const int ia[], size_t size) 516 | { 517 | for (size_t i = 0; i != size; ++i) { 518 | cout << ia[i] << endl; 519 | } 520 | } 521 | 522 | void print(int (&arr)[2]) 523 | { 524 | for (auto i : arr) 525 | cout << i << endl; 526 | } 527 | 528 | int main() 529 | { 530 | int i = 0, j[2] = { 0, 1 }; 531 | char ch[5] = "pezy"; 532 | 533 | print(ch); 534 | print(begin(j), end(j)); 535 | print(&i); 536 | print(j, end(j)-begin(j)); 537 | print(j); 538 | 539 | return 0; 540 | } 541 | ``` 542 | 543 | ## 练习6.24 544 | 描述下面这个函数的行为。如果代码中存在问题,请指出并改正。 545 | 546 | ```cpp 547 | void print(const int ia[10]) 548 | { 549 | for (size_t i = 0; i != 10; ++i) 550 | cout << ia[i] << endl; 551 | } 552 | ``` 553 | 554 | 解: 555 | 556 | 当数组作为实参的时候,会被自动转换为指向首元素的指针。 557 | 因此函数形参接受的是一个指针。 558 | 如果要让这个代码成功运行(不更改也可以运行),可以将形参改为数组的引用。 559 | 560 | ```cpp 561 | void print(const int (&ia)[10]) 562 | { 563 | for (size_t i = 0; i != 10; ++i) 564 | cout << ia[i] << endl; 565 | } 566 | ``` 567 | 568 | ## 练习6.25 569 | 编写一个`main`函数,令其接受两个实参。把实参的内容连接成一个`string`对象并输出出来。 570 | 571 | ## 练习6.26 572 | 编写一个程序,使其接受本节所示的选项;输出传递给`main`函数的实参内容。 573 | 574 | 解: 575 | 576 | 包括6.25 577 | 578 | ```cpp 579 | #include 580 | #include 581 | 582 | int main(int argc, char **argv) 583 | { 584 | std::string str; 585 | for (int i = 1; i != argc; ++i) 586 | str += std::string(argv[i]) + " "; 587 | 588 | std::cout << str << std::endl; 589 | return 0; 590 | } 591 | ``` 592 | 593 | ## 练习6.27 594 | 编写一个函数,它的参数是`initializer_list`类型的对象,函数的功能是计算列表中所有元素的和。 595 | 596 | 解: 597 | 598 | ```cpp 599 | #include 600 | #include 601 | 602 | int sum(std::initializer_list const& il) 603 | { 604 | int sum = 0; 605 | for (auto i : il) sum += i; 606 | return sum; 607 | } 608 | 609 | int main(void) 610 | { 611 | auto il = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 612 | std::cout << sum(il) << std::endl; 613 | 614 | return 0; 615 | } 616 | ``` 617 | 618 | ## 练习6.28 619 | 在`error_msg`函数的第二个版本中包含`ErrCode`类型的参数,其中循环内的`elem`是什么类型? 620 | 621 | 解: 622 | 623 | `elem`是`const string &`类型。 624 | 625 | ## 练习6.29 626 | 在范围`for`循环中使用`initializer_list`对象时,应该将循环控制变量声明成引用类型吗?为什么? 627 | 628 | 解: 629 | 630 | 应该使用常量引用类型。`initializer_list`对象中的元素都是常量,我们无法修改`initializer_list`对象中的元素的值。 631 | 632 | ## 练习6.30 633 | 编译第200页的`str_subrange`函数,看看你的编译器是如何处理函数中的错误的。 634 | 635 | 解: 636 | 637 | 编译器信息: 638 | ``` 639 | g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609 640 | ``` 641 | 642 | 编译错误信息: 643 | 644 | ``` 645 | ch6.cpp:38:9: error: return-statement with no value, in function returning ‘bool’ [-fpermissive] 646 | ``` 647 | 648 | ## 练习6.31 649 | 什么情况下返回的引用无效?什么情况下返回常量的引用无效? 650 | 651 | 解: 652 | 653 | 当返回的引用的对象是局部变量时,返回的引用无效;当我们希望返回的对象被修改时,返回常量的引用无效。 654 | 655 | ## 练习6.32 656 | 下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。 657 | 658 | ```cpp 659 | int &get(int *array, int index) { return array[index]; } 660 | int main() 661 | { 662 | int ia[10]; 663 | for (int i = 0; i != 10; ++i) 664 | get(ia, i) = i; 665 | } 666 | ``` 667 | 668 | 解: 669 | 670 | 合法。`get`函数根据索引取得数组中的元素的引用。 671 | 672 | ## 练习6.33 673 | 编写一个递归函数,输出`vector`对象的内容。 674 | 675 | 解: 676 | 677 | ```cpp 678 | #include 679 | #include 680 | using std::vector; using std::cout; 681 | using Iter = vector::const_iterator; 682 | 683 | void print(Iter first, Iter last) 684 | { 685 | if (first != last) 686 | { 687 | cout << *first << " "; 688 | print(++first, last); 689 | } 690 | } 691 | 692 | int main() 693 | { 694 | vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 695 | print(vec.cbegin(), vec.cend()); 696 | 697 | return 0; 698 | } 699 | ``` 700 | 701 | ## 练习6.34 702 | 如果`factorial`函数的停止条件如下所示,将发生什么? 703 | 704 | ```cpp 705 | if (val != 0) 706 | ``` 707 | 708 | 解: 709 | 如果`val`为正数,从结果上来说没有区别(多乘了个1); 710 | 如果`val`为负数,那么递归永远不会结束。 711 | 712 | ## 练习6.35 713 | 在调用`factorial`函数时,为什么我们传入的值是`val-1`而非`val--`? 714 | 715 | 解: 716 | 717 | 如果传入的值是`val--`,那么将会永远传入相同的值来调用该函数,递归将永远不会结束。 718 | 719 | ## 练习6.36 720 | 编写一个函数声明,使其返回数组的引用并且该数组包含10个`string`对象。 721 | 不用使用尾置返回类型、`decltype`或者类型别名。 722 | 723 | 解: 724 | 725 | ```cpp 726 | string (&fun())[10]; 727 | ``` 728 | 729 | ## 练习6.37 730 | 为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用`decltype`关键字。 731 | 你觉得哪种形式最好?为什么? 732 | 733 | 解: 734 | 735 | ```cpp 736 | typedef string str_arr[10]; 737 | str_arr& fun(); 738 | 739 | auto fun()->string(&)[10]; 740 | 741 | string s[10]; 742 | decltype(s)& fun(); 743 | ``` 744 | 745 | 我觉得尾置返回类型最好,就一行代码。 746 | 747 | ## 练习6.38 748 | 修改`arrPtr`函数,使其返回数组的引用。 749 | 750 | 解: 751 | 752 | ```cpp 753 | decltype(odd)& arrPtr(int i) 754 | { 755 | return (i % 2) ? odd : even; 756 | } 757 | ``` 758 | 759 | ## 练习6.39 760 | 说明在下面的每组声明中第二条语句是何含义。 761 | 如果有非法的声明,请指出来。 762 | 763 | ```cpp 764 | (a) int calc(int, int); 765 | int calc(const int, const int); 766 | (b) int get(); 767 | double get(); 768 | (c) int *reset(int *); 769 | double *reset(double *); 770 | ``` 771 | 772 | 解: 773 | 774 | - (a) 非法。因为顶层const不影响传入函数的对象,所以第二个声明无法与第一个声明区分开来。 775 | - (b) 非法。对于重载的函数来说,它们应该只有形参的数量和形参的类型不同。返回值与重载无关。 776 | - (c) 合法。 777 | 778 | ## 练习6.40 779 | 下面的哪个声明是错误的?为什么? 780 | 781 | ```cpp 782 | (a) int ff(int a, int b = 0, int c = 0); 783 | (b) char *init(int ht = 24, int wd, char bckgrnd); 784 | ``` 785 | 786 | 解: 787 | 788 | (a) 正确。 789 | (b) 错误。因为一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。 790 | 791 | ## 练习6.41 792 | 下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么? 793 | 794 | ```cpp 795 | char *init(int ht, int wd = 80, char bckgrnd = ' '); 796 | (a) init(); 797 | (b) init(24,10); 798 | (c) init(14,'*'); 799 | ``` 800 | 801 | 解: 802 | 803 | - (a) 非法。第一个参数不是默认参数,最少需要一个实参。 804 | - (b) 合法。 805 | - (c) 合法,但与初衷不符。字符`*`被解释成`int`传入到了第二个参数。而初衷是要传给第三个参数。 806 | 807 | ## 练习6.42 808 | 给`make_plural`函数的第二个形参赋予默认实参's', 利用新版本的函数输出单词success和failure的单数和复数形式。 809 | 810 | 解: 811 | 812 | ```cpp 813 | #include 814 | #include 815 | 816 | using std::string; 817 | using std::cout; 818 | using std::endl; 819 | 820 | string make_plural(size_t ctr, const string& word, const string& ending = "s") 821 | { 822 | return (ctr > 1) ? word + ending : word; 823 | } 824 | 825 | int main() 826 | { 827 | cout << "single: " << make_plural(1, "success", "es") << " " 828 | << make_plural(1, "failure") << endl; 829 | cout << "plural : " << make_plural(2, "success", "es") << " " 830 | << make_plural(2, "failure") << endl; 831 | 832 | return 0; 833 | } 834 | ``` 835 | 836 | ## 练习6.43 837 | 你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么? 838 | 839 | ```cpp 840 | (a) inline bool eq(const BigInt&, const BigInt&) {...} 841 | (b) void putValues(int *arr, int size); 842 | ``` 843 | 844 | 解: 845 | 846 | 全部都放进头文件。(a) 是内联函数,(b) 是声明。 847 | 848 | ## 练习6.44 849 | 将6.2.2节的`isShorter`函数改写成内联函数。 850 | 851 | 解: 852 | 853 | ```cpp 854 | inline bool is_shorter(const string &lft, const string &rht) 855 | { 856 | return lft.size() < rht.size(); 857 | } 858 | ``` 859 | 860 | ## 练习6.45 861 | 回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗? 862 | 如果是,将它们改写成内联函数;如果不是,说明原因。 863 | 864 | 解: 865 | 866 | 一般来说,内联机制用于优化规模小、流程直接、频繁调用的函数。 867 | 868 | ## 练习6.46 869 | 能把`isShorter`函数定义成`constexpr`函数吗? 870 | 如果能,将它改写成`constxpre`函数;如果不能,说明原因。 871 | 872 | 解: 873 | 874 | 不能。`constexpr`函数的返回值类型及所有形参都得是字面值类型。 875 | 876 | ## 练习6.47 877 | 改写6.3.2节练习中使用递归输出`vector`内容的程序,使其有条件地输出与执行过程有关的信息。 878 | 例如,每次调用时输出`vector`对象的大小。 879 | 分别在打开和关闭调试器的情况下编译并执行这个程序。 880 | 881 | 解: 882 | 883 | ```cpp 884 | #include 885 | #include 886 | using std::vector; using std::cout; using std::endl; 887 | 888 | void printVec(vector &vec) 889 | { 890 | #ifndef NDEBUG 891 | cout << "vector size: " << vec.size() << endl; 892 | #endif 893 | if (!vec.empty()) 894 | { 895 | auto tmp = vec.back(); 896 | vec.pop_back(); 897 | printVec(vec); 898 | cout << tmp << " "; 899 | } 900 | } 901 | 902 | int main() 903 | { 904 | vector vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 905 | printVec(vec); 906 | cout << endl; 907 | 908 | return 0; 909 | } 910 | ``` 911 | 912 | ## 练习6.48 913 | 说明下面这个循环的含义,它对assert的使用合理吗? 914 | 915 | ```cpp 916 | string s; 917 | while (cin >> s && s != sought) { } //空函数体 918 | assert(cin); 919 | ``` 920 | 921 | 解: 922 | 923 | 不合理。从这个程序的意图来看,应该用 924 | 925 | ```cpp 926 | assert(s == sought); 927 | ``` 928 | 929 | ## 练习6.49 930 | 什么是候选函数?什么是可行函数? 931 | 932 | 解: 933 | 934 | 候选函数:与被调用函数同名,并且其声明在调用点可见。 935 | 可行函数:形参与实参的数量相等,并且每个实参类型与对应的形参类型相同或者能转换成形参的类型。 936 | 937 | ## 练习6.50 938 | 已知有第217页对函数`f`的声明,对于下面的每一个调用列出可行函数。 939 | 其中哪个函数是最佳匹配? 940 | 如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性? 941 | 942 | ```cpp 943 | (a) f(2.56, 42) 944 | (b) f(42) 945 | (c) f(42, 0) 946 | (d) f(2.56, 3.14) 947 | ``` 948 | 949 | 解: 950 | 951 | - (a) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。 952 | 该调用具有二义性而不合法。 953 | - (b) `void f(int);` 是可行函数。调用合法。 954 | - (c) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。 955 | `void f(int, int);`是最佳匹配。 956 | - (d) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。 957 | `void f(double, double = 3.14);`是最佳匹配。 958 | 959 | ## 练习6.51 960 | 编写函数`f`的4版本,令其各输出一条可以区分的消息。 961 | 验证上一个练习的答案,如果你的回答错了,反复研究本节内容直到你弄清自己错在何处。 962 | 963 | 解: 964 | 965 | ```cpp 966 | #include 967 | using std::cout; using std::endl; 968 | 969 | void f() 970 | { 971 | cout << "f()" << endl; 972 | } 973 | 974 | void f(int) 975 | { 976 | cout << "f(int)" << endl; 977 | } 978 | 979 | void f(int, int) 980 | { 981 | cout << "f(int, int)" << endl; 982 | } 983 | 984 | void f(double, double) 985 | { 986 | cout << "f(double, double)" << endl; 987 | } 988 | 989 | int main() 990 | { 991 | //f(2.56, 42); // error: 'f' is ambiguous. 992 | f(42); 993 | f(42, 0); 994 | f(2.56, 3.14); 995 | 996 | return 0; 997 | } 998 | 999 | ``` 1000 | 1001 | ## 练习6.52 1002 | 已知有如下声明: 1003 | ```cpp 1004 | void manip(int ,int); 1005 | double dobj; 1006 | ``` 1007 | 请指出下列调用中每个类型转换的等级。 1008 | 1009 | ```cpp 1010 | (a) manip('a', 'z'); 1011 | (b) manip(55.4, dobj); 1012 | ``` 1013 | 1014 | 解: 1015 | 1016 | - (a) 第3级。类型提升实现的匹配。 1017 | - (b) 第4级。算术类型转换实现的匹配。 1018 | 1019 | ## 练习6.53 1020 | 说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。 1021 | 1022 | 1023 | ```cpp 1024 | (a) int calc(int&, int&); 1025 | int calc(const int&, const int&); 1026 | (b) int calc(char*, char*); 1027 | int calc(const char*, const char*); 1028 | (c) int calc(char*, char*); 1029 | int calc(char* const, char* const); 1030 | ``` 1031 | 1032 | 解: 1033 | 1034 | (c) 不合法。顶层const不影响传入函数的对象。 1035 | 1036 | ## 练习6.54 1037 | 编写函数的声明,令其接受两个`int`形参并返回类型也是`int`;然后声明一个`vector`对象,令其元素是指向该函数的指针。 1038 | 1039 | 解: 1040 | 1041 | ```cpp 1042 | int func(int, int); 1043 | vector v; 1044 | ``` 1045 | 1046 | ## 练习6.55 1047 | 编写4个函数,分别对两个`int`值执行加、减、乘、除运算;在上一题创建的`vector`对象中保存指向这些函数的指针。 1048 | 1049 | 解: 1050 | 1051 | ```cpp 1052 | int add(int a, int b) { return a + b; } 1053 | int subtract(int a, int b) { return a - b; } 1054 | int multiply(int a, int b) { return a * b; } 1055 | int divide(int a, int b) { return b != 0 ? a / b : 0; } 1056 | 1057 | v.push_back(add); 1058 | v.push_back(subtract); 1059 | v.push_back(multiply); 1060 | v.push_back(divide); 1061 | ``` 1062 | 1063 | ## 练习6.56 1064 | 调用上述`vector`对象中的每个元素并输出结果。 1065 | 1066 | 解: 1067 | 1068 | ```cpp 1069 | std::vector vec{ add, subtract, multiply, divide }; 1070 | for (auto f : vec) 1071 | std::cout << f(2, 2) << std::endl; 1072 | ``` -------------------------------------------------------------------------------- /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/ch11.md: -------------------------------------------------------------------------------- 1 | # 第十一章 关联容器 2 | 3 | ## 练习11.1 4 | 5 | > 描述`map`和`vector`的不同。 6 | 7 | 解: 8 | 9 | `map` 是关联容器, `vector` 是顺序容器。 10 | 11 | ## 练习11.2 12 | 13 | > 分别给出最适合使用`list`、`vector`、`deque`、`map`以及`set`的例子。 14 | 15 | 解: 16 | 17 | * `list`:双向链表,适合频繁插入删除元素的场景。 18 | * `vector`:适合频繁访问元素的场景。 19 | * `deque`:双端队列,适合频繁在头尾插入删除元素的场景。 20 | * `map`:字典。 21 | * `set`:适合有序不重复的元素的场景。 22 | 23 | ## 练习11.3 24 | 25 | > 编写你自己的单词计数程序。 26 | 27 | 解: 28 | 29 | ```cpp 30 | #include 31 | #include 32 | #include 33 | 34 | using namespace std; 35 | 36 | int main(){ 37 | map word_count; 38 | string tmp; 39 | while (cin >> tmp){ 40 | word_count[tmp] += 1; 41 | } 42 | for (const auto& elem : word_count) 43 | std::cout << elem.first << " : " << elem.second << endl; 44 | return 0; 45 | } 46 | ``` 47 | 48 | ## 练习11.4 49 | 50 | > 扩展你的程序,忽略大小写和标点。例如,"example."、"example,"和"Example"应该递增相同的计数器。 51 | 52 | 解: 53 | 54 | ```cpp 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | void word_count_pro(std::map& m) 62 | { 63 | std::string word; 64 | while (std::cin >> word) 65 | { 66 | for (auto& ch : word) 67 | ch = tolower(ch); 68 | 69 | word.erase(std::remove_if(word.begin(), word.end(), ispunct), 70 | word.end()); 71 | ++m[word]; 72 | } 73 | for (const auto& e : m) std::cout << e.first << " : " << e.second << "\n"; 74 | } 75 | 76 | int main() 77 | { 78 | std::map m; 79 | word_count_pro(m); 80 | 81 | return 0; 82 | } 83 | ``` 84 | 85 | ## 练习11.5 86 | 87 | > 解释`map`和`set`的区别。你如何选择使用哪个? 88 | 89 | 解: 90 | 91 | `map` 是键值对,而 `set` 只有键没有值。当我需要存储键值对的时候使用 `map`,而只需要键的时候使用 `set`。 92 | 93 | ## 练习11.6 94 | 95 | > 解释`set`和`list`的区别。你如何选择使用哪个? 96 | 97 | `set` 是有序不重复集合,底层实现是红黑树,而 `list` 是无序可重复集合,底层实现是链表。 98 | 99 | ## 练习11.7 100 | 101 | > 定义一个`map`,关键字是家庭的姓,值是一个`vector`,保存家中孩子(们)的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子。 102 | 103 | 解: 104 | 105 | ```cpp 106 | map> m; 107 | for (string ln; cout << "Last name:\n", cin >> ln && ln != "@q";) 108 | for (string cn; cout << "|-Children's names:\n", cin >> cn && cn != "@q";) 109 | m[ln].push_back(cn); 110 | ``` 111 | 112 | ## 练习11.8 113 | 114 | > 编写一个程序,在一个`vector`而不是一个`set`中保存不重复的单词。使用`set`的优点是什么? 115 | 116 | 解: 117 | 118 | ```cpp 119 | #include 120 | #include 121 | #include 122 | #include 123 | 124 | int main() 125 | { 126 | std::vector exclude = { "aa", "bb", "cc", "dd", "ee", "ff" }; 127 | for (std::string word; std::cout << "Enter plz:\n", std::cin >> word;) 128 | { 129 | auto is_excluded = std::binary_search(exclude.cbegin(), exclude.cend(), word); 130 | auto reply = is_excluded ? "excluded" : "not excluded"; 131 | std::cout << reply << std::endl; 132 | } 133 | return 0; 134 | } 135 | ``` 136 | 137 | `set`的优点是集合本身的元素就是不重复。 138 | 139 | ## 练习11.9 140 | 141 | > 定义一个`map`,将单词与一个行号的`list`关联,`list`中保存的是单词所出现的行号。 142 | 143 | 解: 144 | 145 | ```cpp 146 | std::map> m; 147 | ``` 148 | 149 | ## 练习11.10 150 | 151 | > 可以定义一个`vector::iterator` 到 `int` 的`map`吗?`list::iterator` 到 `int` 的`map`呢?对于两种情况,如果不能,解释为什么。 152 | 153 | 解: 154 | 155 | 可以定义 `vector::iterator` 到 `int` 的`map`,但是不能定义 `list::iterator` 到 `int` 的`map`。因为`map`的键必须实现 `<` 操作,`list` 的迭代器不支持比较运算。 156 | 157 | ## 练习11.11 158 | 159 | > 不使用`decltype` 重新定义 `bookstore`。 160 | 161 | 解: 162 | 163 | ```cpp 164 | using Less = bool (*)(Sales_data const&, Sales_data const&); 165 | std::multiset bookstore(less); 166 | ``` 167 | 168 | ## 练习11.12 169 | 170 | > 编写程序,读入`string`和`int`的序列,将每个`string`和`int`存入一个`pair` 中,`pair`保存在一个`vector`中。 171 | 172 | 解: 173 | 174 | ```cpp 175 | #include 176 | #include 177 | #include 178 | #include 179 | 180 | int main() 181 | { 182 | std::vector> vec; 183 | std::string str; 184 | int i; 185 | while (std::cin >> str >> i) 186 | vec.push_back(std::pair(str, i)); 187 | 188 | for (const auto &p : vec) 189 | std::cout << p.first << ":" << p.second << std::endl; 190 | } 191 | ``` 192 | 193 | ## 练习11.13 194 | 195 | > 在上一题的程序中,至少有三种创建`pair`的方法。编写此程序的三个版本,分别采用不同的方法创建`pair`。解释你认为哪种形式最易于编写和理解,为什么? 196 | 197 | 解: 198 | 199 | ```cpp 200 | vec.push_back(std::make_pair(str, i)); 201 | vec.push_back({ str, i }); 202 | vec.push_back(std::pair(str, i)); 203 | ``` 204 | 205 | 使用花括号的初始化器最易于理解和编写。 206 | 207 | ## 练习11.14 208 | 209 | > 扩展你在11.2.1节练习中编写的孩子姓达到名的`map`,添加一个`pair`的`vector`,保存孩子的名和生日。 210 | 211 | 解: 212 | 213 | ```cpp 214 | #include 215 | #include 216 | #include 217 | #include 218 | 219 | using std::ostream; 220 | using std::cout; 221 | using std::cin; 222 | using std::endl; 223 | using std::string; 224 | using std::make_pair; 225 | using std::pair; 226 | using std::vector; 227 | using std::map; 228 | 229 | class Families 230 | { 231 | public: 232 | using Child = pair; 233 | using Children = vector; 234 | using Data = map; 235 | 236 | void add(string const& last_name, string const& first_name, string birthday) 237 | { 238 | auto child = make_pair(first_name, birthday); 239 | _data[last_name].push_back(child); 240 | } 241 | 242 | void print() const 243 | { 244 | for (auto const& pair : _data) 245 | { 246 | cout << pair.first << ":\n"; 247 | for (auto const& child : pair.second) 248 | cout << child.first << " " << child.second << endl; 249 | cout << endl; 250 | } 251 | } 252 | 253 | private: 254 | Data _data; 255 | }; 256 | 257 | int main() 258 | { 259 | Families families; 260 | auto msg = "Please enter last name, first name and birthday:\n"; 261 | for (string l, f, b; cout << msg, cin >> l >> f >> b; families.add(l, f, b)); 262 | families.print(); 263 | 264 | return 0; 265 | } 266 | ``` 267 | 268 | ## 练习11.15 269 | 270 | > 对一个`int`到`vector的map`,其`mapped_type`、`key_type`和 `value_type`分别是什么? 271 | 272 | 解: 273 | 274 | * `mapped_type` : `vector` 275 | * `key_type` : `int` 276 | * `value_type` : `std::pair` 277 | 278 | ## 练习11.16 279 | 280 | > 使用一个`map`迭代器编写一个表达式,将一个值赋予一个元素。 281 | 282 | 解: 283 | 284 | ```cpp 285 | std::map::iterator it = m.begin(); 286 | it->second = "hello"; 287 | ``` 288 | 289 | ## 练习11.17 290 | 291 | > 假定`c`是一个`string`的`multiset`,`v` 是一个`string` 的`vector`,解释下面的调用。指出每个调用是否合法: 292 | 293 | ```cpp 294 | copy(v.begin(), v.end(), inserter(c, c.end())); 295 | copy(v.begin(), v.end(), back_inserter(c)); 296 | copy(c.begin(), c.end(), inserter(v, v.end())); 297 | copy(c.begin(), c.end(), back_inserter(v)); 298 | ``` 299 | 300 | 解: 301 | 302 | 第二个调用不合法,因为 `multiset` 没有 `push_back` 方法。其他调用都合法。 303 | 304 | ## 练习11.18 305 | 306 | > 写出第382页循环中`map_it` 的类型,不要使用`auto` 或 `decltype`。 307 | 308 | 解: 309 | 310 | ```cpp 311 | map::const_iterator map_it = word_count.cbegin(); 312 | ``` 313 | 314 | ## 练习11.19 315 | 316 | > 定义一个变量,通过对11.2.2节中的名为 `bookstore` 的`multiset` 调用`begin()`来初始化这个变量。写出变量的类型,不要使用`auto` 或 `decltype`。 317 | 318 | 解: 319 | 320 | ```cpp 321 | using compareType = bool (*)(const Sales_data &lhs, const Sales_data &rhs); 322 | std::multiset bookstore(compareIsbn); 323 | std::multiset::iterator c_it = bookstore.begin(); 324 | ``` 325 | 326 | ## 练习11.20 327 | 328 | > 重写11.1节练习的单词计数程序,使用`insert`代替下标操作。你认为哪个程序更容易编写和阅读?解释原因。 329 | 330 | 解: 331 | 332 | ```cpp 333 | #include 334 | #include 335 | #include 336 | 337 | using std::string; 338 | using std::map; 339 | using std::cin; 340 | using std::cout; 341 | 342 | int main() 343 | { 344 | map counts; 345 | for (string word; cin >> word;) 346 | { 347 | auto result = counts.insert({ word, 1 }); 348 | if (!result.second) 349 | ++result.first->second; 350 | } 351 | for (auto const& count : counts) 352 | cout << count.first << " " << count.second << ((count.second > 1) ? " times\n" : " time\n"); 353 | 354 | return 0; 355 | } 356 | ``` 357 | 358 | 使用`insert`更容易阅读和编写。`insert`有返回值,可以明确的体现出插入操作的结果。 359 | 360 | ## 练习11.21 361 | 362 | > 假定`word_count`是一个`string`到`size_t`的`map`,`word`是一个`string`,解释下面循环的作用: 363 | 364 | ```cpp 365 | while (cin >> word) 366 | ++word_count.insert({word, 0}).first->second; 367 | ``` 368 | 369 | 解: 370 | 371 | 这条语句等价于: 372 | 373 | ```cpp 374 | while (cin >> word) 375 | { 376 | auto result = word_count.insert({word, 0}); 377 | ++(result.first->second); 378 | } 379 | ``` 380 | 381 | 若`insert`成功:先添加一个元素,然后返回一个 `pair`,`pair` 的 `first`元素是一个迭代器。这个迭代器指向刚刚添加的元素,这个元素是`pair`,然后递增`pair`的`second`成员。 382 | 若`insert`失败:递增已有指定关键字的元素的 `second`成员。 383 | 384 | ## 练习11.22 385 | 386 | > 给定一个`map>`,对此容器的插入一个元素的`insert`版本,写出其参数类型和返回类型。 387 | 388 | ```cpp 389 | std::pair> // 参数类型 390 | std::pair>::iterator, bool> // 返回类型 391 | ``` 392 | 393 | ## 练习11.23 394 | 395 | > 11.2.1节练习中的`map` 以孩子的姓为关键字,保存他们的名的`vector`,用`multimap` 重写此`map`。 396 | 397 | 解: 398 | 399 | ```cpp 400 | #include 401 | #include 402 | #include 403 | 404 | using std::string; 405 | using std::multimap; 406 | using std::cin; 407 | using std::endl; 408 | 409 | int main() 410 | { 411 | multimap families; 412 | for (string lname, cname; cin >> cname >> lname; families.emplace(lname, cname)); 413 | for (auto const& family : families) 414 | std::cout << family.second << " " << family.first << endl; 415 | } 416 | ``` 417 | 418 | ## 练习11.24 419 | 420 | > 下面的程序完成什么功能? 421 | > 422 | ```cpp 423 | map m; 424 | m[0] = 1; 425 | ``` 426 | 427 | 解: 428 | 429 | 添加一个元素到`map`中,如果该键存在,则重新赋值。 430 | 431 | ## 练习11.25 432 | 433 | > 对比下面的程序与上一题程序 434 | 435 | ```cpp 436 | vector v; 437 | v[0] = 1; 438 | ``` 439 | 440 | 解: 441 | 442 | 未定义行为,`vector` 的下标越界访问。 443 | 444 | ## 练习11.26 445 | 446 | > 可以用什么类型来对一个`map`进行下标操作?下标运算符返回的类型是什么?请给出一个具体例子——即,定义一个`map`,然后写出一个可以用来对`map`进行下标操作的类型以及下标运算符将会返会的类型。 447 | 448 | 解: 449 | 450 | ```cpp 451 | std::map m = { { 1,"ss" },{ 2,"sz" } }; 452 | using KeyType = std::map::key_type; 453 | using ReturnType = std::map::mapped_type; 454 | ``` 455 | 456 | ## 练习11.27 457 | 458 | > 对于什么问题你会使用`count`来解决?什么时候你又会选择`find`呢? 459 | 460 | 解: 461 | 462 | 对于允许重复关键字的容器,应该用 `count` ; 对于不允许重复关键字的容器,应该用 `find` 。 463 | 464 | ## 练习11.28 465 | 466 | > 对一个`string`到`int`的`vector`的`map`,定义并初始化一个变量来保存在其上调用`find`所返回的结果。 467 | 468 | 解: 469 | 470 | ```cpp 471 | map> m; 472 | map>::iterator it = m.find("key"); 473 | ``` 474 | 475 | ## 练习11.29 476 | 477 | > 如果给定的关键字不在容器中,`upper_bound`、`lower_bound` 和 `equal_range` 分别会返回什么? 478 | 479 | 解: 480 | 481 | 如果给定的关键字不在容器中,则 `lower_bound`和 `upper_bound` 会返回相等的迭代器,指向一个不影响排序的关键字插入位置。而`equal_range` 会返回一个 `pair`,`pair` 中的两个迭代器都指向关键字可以插入的位置。 482 | 483 | ## 练习11.30 484 | 485 | > 对于本节最后一个程序中的输出表达式,解释运算对象`pos.first->second`的含义。 486 | 487 | 解: 488 | 489 | `pos` 是一个`pair`,`pos.first` 是一个迭代器,指向匹配关键字的元素,该元素是一个 `pair`,访问该元素的第二个成员。 490 | 491 | ## 练习11.31 492 | 493 | > 编写程序,定义一个作者及其作品的`multimap`。使用`find`在`multimap`中查找一个元素并用`erase`删除它。确保你的程序在元素不在`map` 中时也能正常运行。 494 | 495 | 解: 496 | 497 | ```cpp 498 | #include 499 | #include 500 | #include 501 | 502 | using std::string; 503 | 504 | int main() 505 | { 506 | std::multimap authors{ 507 | { "alan", "DMA" }, 508 | { "pezy", "LeetCode" }, 509 | { "alan", "CLRS" }, 510 | { "wang", "FTP" }, 511 | { "pezy", "CP5" }, 512 | { "wang", "CPP-Concurrency" } }; 513 | 514 | string author = "pezy"; 515 | string work = "CP5"; 516 | 517 | auto found = authors.find(author); 518 | auto count = authors.count(author); 519 | while (count) 520 | { 521 | if (found->second == work) 522 | { 523 | authors.erase(found); 524 | break; 525 | } 526 | ++found; 527 | --count; 528 | } 529 | 530 | for (const auto &author : authors) 531 | std::cout << author.first << " " << author.second << std::endl; 532 | 533 | return 0; 534 | } 535 | ``` 536 | 537 | ## 练习11.32 538 | 539 | > 使用上一题定义的`multimap`编写一个程序,按字典序打印作者列表和他们的作品。 540 | 541 | 解: 542 | 543 | ```cpp 544 | #include 545 | #include 546 | #include 547 | #include 548 | 549 | using std::string; 550 | 551 | int main() 552 | { 553 | std::multimap authors{ 554 | { "alan", "DMA" }, 555 | { "pezy", "LeetCode" }, 556 | { "alan", "CLRS" }, 557 | { "wang", "FTP" }, 558 | { "pezy", "CP5" }, 559 | { "wang", "CPP-Concurrency" } }; 560 | std::map> order_authors; 561 | 562 | for (const auto &author : authors) 563 | order_authors[author.first].insert(author.second); 564 | 565 | for (const auto &author : order_authors) 566 | { 567 | std::cout << author.first << ": "; 568 | for (const auto &work : author.second) 569 | std::cout << work << " "; 570 | std::cout << std::endl; 571 | } 572 | 573 | return 0; 574 | } 575 | ``` 576 | 577 | ## 练习11.33 578 | 579 | > 实现你自己版本的单词转换程序。 580 | 581 | 解: 582 | 583 | ```cpp 584 | #include 585 | #include 586 | #include 587 | #include 588 | #include 589 | 590 | using std::string; using std::ifstream; 591 | 592 | std::map buildMap(ifstream &map_file) 593 | { 594 | std::map trans_map; 595 | for (string key, value; map_file >> key && getline(map_file, value); ) 596 | if (value.size() > 1) trans_map[key] = value.substr(1).substr(0, value.find_last_not_of(' ')); 597 | return trans_map; 598 | } 599 | 600 | const string & transform(const string &s, const std::map &m) 601 | { 602 | auto map_it = m.find(s); 603 | return map_it == m.cend() ? s : map_it->second; 604 | } 605 | 606 | void word_transform(ifstream &map, ifstream &input) 607 | { 608 | auto trans_map = buildMap(map); 609 | for (string text; getline(input, text); ) { 610 | std::istringstream iss(text); 611 | for (string word; iss >> word; ) 612 | std::cout << transform(word, trans_map) << " "; 613 | std::cout << std::endl; 614 | } 615 | } 616 | 617 | int main() 618 | { 619 | ifstream ifs_map("../data/word_transformation_bad.txt"), ifs_content("../data/given_to_transform.txt"); 620 | if (ifs_map && ifs_content) word_transform(ifs_map, ifs_content); 621 | else std::cerr << "can't find the documents." << std::endl; 622 | } 623 | ``` 624 | 625 | ## 练习11.34 626 | 627 | > 如果你将`transform` 函数中的`find`替换为下标运算符,会发生什么情况? 628 | 629 | 解: 630 | 631 | 如果使用下标运算符,当关键字未在容器中时,会往容器中添加一个新元素。 632 | 633 | ## 练习11.35 634 | 635 | > 在`buildMap`中,如果进行如下改写,会有什么效果? 636 | 637 | ```cpp 638 | trans_map[key] = value.substr(1); 639 | //改为 640 | trans_map.insert({key, value.substr(1)}); 641 | ``` 642 | 643 | 解: 644 | 645 | 当一个转换规则的关键字多次出现的时候,使用下标运算符会保留最后一次添加的规则,而用insert则保留第一次添加的规则。 646 | 647 | ## 练习11.36 648 | 649 | > 我们的程序并没检查输入文件的合法性。特别是,它假定转换规则文件中的规则都是有意义的。如果文件中的某一行包含一个关键字、一个空格,然后就结束了,会发生什么?预测程序的行为并进行验证,再与你的程序进行比较。 650 | 651 | 解: 652 | 653 | 如果关键字没有对应的规则,那么程序会抛出一个 `runtime_error`。 654 | 655 | ## 练习11.37 656 | 657 | > 一个无序容器与其有序版本相比有何优势?有序版本有何优势? 658 | 659 | 无序容器拥有更好的性能,有序容器使得元素始终有序。 660 | 661 | ## 练习11.38 662 | 663 | > 用 `unordered_map` 重写单词计数程序和单词转换程序。 664 | 665 | 解: 666 | 667 | ```cpp 668 | #include 669 | #include 670 | #include 671 | #include 672 | #include 673 | #include 674 | 675 | using std::string; 676 | 677 | void wordCounting() 678 | { 679 | std::unordered_map word_count; 680 | for (string word; std::cin >> word; ++word_count[word]); 681 | for (const auto &w : word_count) 682 | std::cout << w.first << " occurs " << w.second << (w.second > 1 ? "times" : "time") << std::endl; 683 | } 684 | 685 | void wordTransformation() 686 | { 687 | std::ifstream ifs_map("../data/word_transformation.txt"), ifs_content("../data/given_to_transform.txt"); 688 | if (!ifs_map || !ifs_content) { 689 | std::cerr << "can't find the documents." << std::endl; 690 | return; 691 | } 692 | 693 | std::unordered_map trans_map; 694 | for (string key, value; ifs_map >> key && getline(ifs_map, value); ) 695 | if (value.size() > 1) trans_map[key] = value.substr(1).substr(0, value.find_last_not_of(' ')); 696 | 697 | for (string text, word; getline(ifs_content, text); std::cout << std::endl) 698 | for (std::istringstream iss(text); iss >> word; ) { 699 | auto map_it = trans_map.find(word); 700 | std::cout << (map_it == trans_map.cend() ? word : map_it->second) << " "; 701 | } 702 | } 703 | 704 | int main() 705 | { 706 | //wordCounting(); 707 | wordTransformation(); 708 | } 709 | ``` 710 | -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | cppname=$1 2 | outname=${cppname%.*} 3 | outname=$outname".out" 4 | g++ $cppname -o $outname -Wall 5 | ./$outname 6 | --------------------------------------------------------------------------------