Showing preview only (529K chars total). Download the full file or copy to clipboard to get everything.
Repository: applenob/Cpp_Primer_Practice
Branch: master
Commit: 1faaf5f0c693
Files: 120
Total size: 499.3 KB
Directory structure:
gitextract_hhvs5jci/
├── .gitignore
├── README.md
├── cpp_source/
│ ├── ch01/
│ │ ├── Sales_item.h
│ │ ├── ch01.cpp
│ │ └── data/
│ │ ├── add
│ │ ├── add_item
│ │ ├── book_sales
│ │ ├── mysum
│ │ └── occurs
│ ├── ch02/
│ │ ├── Sales_item.h
│ │ └── ch02.cpp
│ ├── ch03/
│ │ └── ch3.cpp
│ ├── ch04/
│ │ └── ch04.cpp
│ ├── ch05/
│ │ └── ch5.cpp
│ ├── ch06/
│ │ ├── Chapter6.h
│ │ ├── ch6.cpp
│ │ ├── fact.cpp
│ │ └── factMain.cpp
│ ├── ch07/
│ │ ├── ex_7_41
│ │ ├── ex_7_41.cpp
│ │ ├── ex_7_41.h
│ │ ├── ex_7_41_main.cpp
│ │ ├── screen
│ │ ├── screen.cpp
│ │ └── screen.h
│ ├── ch08/
│ │ └── ch8.cpp
│ ├── ch09/
│ │ ├── date
│ │ └── date.cpp
│ ├── ch10/
│ │ ├── biggies
│ │ ├── biggies.cpp
│ │ ├── inserter
│ │ └── inserter.cpp
│ ├── ch11/
│ │ ├── ex_11_3
│ │ ├── ex_11_3.cpp
│ │ ├── ex_11_4
│ │ └── ex_11_4.cpp
│ ├── ch12/
│ │ ├── ex_12_27
│ │ ├── ex_12_27.cpp
│ │ ├── ex_12_27.h
│ │ ├── ex_12_27_main.cpp
│ │ └── storyDataFile.txt
│ ├── ch13/
│ │ ├── ex_13_13
│ │ ├── ex_13_13.cpp
│ │ ├── ex_13_34_36_37.cpp
│ │ └── ex_13_34_36_37.h
│ ├── ch14/
│ │ ├── ex_14_44
│ │ └── ex_14_44.cpp
│ ├── ch15/
│ │ ├── ex_15_26/
│ │ │ ├── bulk_quote.cpp
│ │ │ ├── bulk_quote.h
│ │ │ ├── disc_quote.cpp
│ │ │ ├── disc_quote.h
│ │ │ ├── limit_quote.cpp
│ │ │ ├── limit_quote.h
│ │ │ ├── main.cpp
│ │ │ ├── quote.cpp
│ │ │ └── quote.h
│ │ └── text_query/
│ │ ├── StrBlob.h
│ │ ├── andquery.cpp
│ │ ├── andquery.h
│ │ ├── binaryquery.cpp
│ │ ├── binaryquery.h
│ │ ├── main.cpp
│ │ ├── notquery.cpp
│ │ ├── notquery.h
│ │ ├── orquery.cpp
│ │ ├── orquery.h
│ │ ├── query.cpp
│ │ ├── query.h
│ │ ├── query_base.cpp
│ │ ├── query_base.h
│ │ ├── queryresult.cpp
│ │ ├── queryresult.h
│ │ ├── storyDataFile.txt
│ │ ├── textquery.cpp
│ │ ├── textquery.h
│ │ ├── wordquery.cpp
│ │ └── wordquery.h
│ ├── ch16/
│ │ ├── ex_16_51
│ │ └── ex_16_51.cpp
│ └── ch17/
│ ├── ex_17_4.cpp
│ ├── ex_17_4_SalesData.cpp
│ └── ex_17_4_SalesData.h
├── excersize/
│ ├── ch01.md
│ ├── ch02.md
│ ├── ch03.md
│ ├── ch04.md
│ ├── ch05.md
│ ├── ch06.md
│ ├── ch07.md
│ ├── ch08.md
│ ├── ch09.md
│ ├── ch10.md
│ ├── ch11.md
│ ├── ch12.md
│ ├── ch13.md
│ ├── ch14.md
│ ├── ch15.md
│ ├── ch16.md
│ ├── ch17.md
│ ├── ch18.md
│ └── ch19.md
└── notes/
├── ch01.md
├── ch02.md
├── ch03.md
├── ch04.md
├── ch05.md
├── ch06.md
├── ch07.md
├── ch08.md
├── ch09.md
├── ch10.md
├── ch11.md
├── ch12.md
├── ch13.md
├── ch14.md
├── ch15.md
├── ch16.md
├── ch17.md
├── ch18.md
└── ch19.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea
cmake-build-debug
test_in_file
test_out_file
CMakeLists.txt
main
.DS_Store
.vscode
================================================
FILE: README.md
================================================
# Cpp Primer 笔记 & 答案
## 简介
《C++ Primer 中文版(第 5 版)》学习仓库,包括**笔记**和**课后练习答案**。
## 环境
- system: ubuntu 16.04
- IDE: VS Code
- compiler: g++
[豆瓣链接](https://book.douban.com/subject/25708312/)
## 目录
- 第1章 : 开始 [笔记](./notes/ch01.md) [练习](./excersize/ch01.md)
- 第 I 部分 : C++基础
- 第2章 : 变量和基本类型 [笔记](./notes/ch02.md) [练习](./excersize/ch02.md)
- 第3章 : 字符串、向量和数组 [笔记](./notes/ch03.md) [练习](./excersize/ch03.md)
- 第4章 : 表达式 [笔记](./notes/ch04.md) [练习](./excersize/ch04.md)
- 第5章 : 语句 [笔记](./notes/ch05.md) [练习](./excersize/ch05.md)
- 第6章 : 函数 [笔记](./notes/ch06.md) [练习](./excersize/ch06.md)
- 第7章 : 类 [笔记](./notes/ch07.md) [练习](./excersize/ch07.md)
- 第 II 部分 : C++标准库
- 第8章 : IO库 [笔记](./notes/ch08.md) [练习](./excersize/ch08.md)
- 第9章 : 顺序容器 [笔记](./notes/ch09.md) [练习](./excersize/ch09.md)
- 第10章 : 泛型算法 [笔记](./notes/ch10.md) [练习](./excersize/ch10.md)
- 第11章 : 关联容器 [笔记](./notes/ch11.md) [练习](./excersize/ch11.md)
- 第12章 : 动态内存 [笔记](./notes/ch12.md) [练习](./excersize/ch12.md)
- 第 III 部分 : 类设计者的工具
- 第13章 : 拷贝控制 [笔记](./notes/ch13.md) [练习](./excersize/ch13.md)
- 第14章 : 重载与类型转换 [笔记](./notes/ch14.md) [练习](./excersize/ch14.md)
- 第15章 : 面向对象程序设计 [笔记](./notes/ch15.md) [练习](./excersize/ch15.md)
- 第16章 : 模版与泛型编程 [笔记](./notes/ch16.md) [练习](./excersize/ch16.md)
- 第 IV 部分 : 高级主题
- 第17章 : 标准库与特殊设施 [笔记](./notes/ch17.md) [练习](./excersize/ch17.md)
- 第18章 : 用于大型程序的工具 [笔记](./notes/ch18.md) [练习](./excersize/ch18.md)
- 第19章 : 特殊工具与技术 [笔记](./notes/ch19.md) [练习](./excersize/ch19.md)
## 参考
- [C++ Primer 5 Answers(C++11/14)](https://github.com/Mooophy/Cpp-Primer)
- [《C++ Primer》第五版中文版习题答案](https://github.com/huangmingchuan/Cpp_Primer_Answers)
## 参与贡献
本仓库由多位小伙伴一起参与编写,欢迎大家对本仓库进行补充,一起帮大家更好地理解这本“大部头”。

================================================
FILE: cpp_source/ch01/Sales_item.h
================================================
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
* copyright and warranty notices given in that book:
*
* "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced.Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
/* This file defines the Sales_item class used in chapter 1.
* The code used in this file will be explained in
* Chapter 7 (Classes) and Chapter 14 (Overloaded Operators)
* Readers shouldn't try to understand the code in this file
* until they have read those chapters.
*/
#ifndef SALESITEM_H
// we're here only if SALESITEM_H has not yet been defined
#define SALESITEM_H
// Definition of Sales_item class and related functions goes here
#include <iostream>
#include <string>
class Sales_item {
// these declarations are explained section 7.2.1, p. 270
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator>>(std::istream&, Sales_item&);
friend std::ostream& operator<<(std::ostream&, const Sales_item&);
friend bool operator<(const Sales_item&, const Sales_item&);
friend bool
operator==(const Sales_item&, const Sales_item&);
public:
// constructors are explained in section 7.1.4, pages 262 - 265
// default constructor needed to initialize members of built-in type
Sales_item() = default;
Sales_item(const std::string &book): bookNo(book) { }
Sales_item(std::istream &is) { is >> *this; }
public:
// operations on Sales_item objects
// member binary operator: left-hand operand bound to implicit this pointer
Sales_item& operator+=(const Sales_item&);
// operations on Sales_item objects
std::string isbn() const { return bookNo; }
double avg_price() const;
// private members as before
private:
std::string bookNo; // implicitly initialized to the empty string
unsigned units_sold = 0; // explicitly initialized
double revenue = 0.0;
};
// used in chapter 10
inline
bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs)
{ return lhs.isbn() == rhs.isbn(); }
// nonmember binary operator: must declare a parameter for each operand
Sales_item operator+(const Sales_item&, const Sales_item&);
inline bool
operator==(const Sales_item &lhs, const Sales_item &rhs)
{
// must be made a friend of Sales_item
return lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue &&
lhs.isbn() == rhs.isbn();
}
inline bool
operator!=(const Sales_item &lhs, const Sales_item &rhs)
{
return !(lhs == rhs); // != defined in terms of operator==
}
// assumes that both objects refer to the same ISBN
Sales_item& Sales_item::operator+=(const Sales_item& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return
ret += rhs; // add in the contents of (|rhs|)
return ret; // return (|ret|) by value
}
std::istream&
operator>>(std::istream& in, Sales_item& s)
{
double price;
in >> s.bookNo >> s.units_sold >> price;
// check that the inputs succeeded
if (in)
s.revenue = s.units_sold * price;
else
s = Sales_item(); // input failed: reset object to default state
return in;
}
std::ostream&
operator<<(std::ostream& out, const Sales_item& s)
{
out << s.isbn() << " " << s.units_sold << " "
<< s.revenue << " " << s.avg_price();
return out;
}
double Sales_item::avg_price() const
{
if (units_sold)
return revenue/units_sold;
else
return 0;
}
#endif
================================================
FILE: cpp_source/ch01/ch01.cpp
================================================
//
// Created by cer
// chapter 1
// 开始
#include <iostream>
#include "Sales_item.h"
void basic_io(){
std::cout << "Enter two numbers:" << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
}
void basic_while(){
int sum = 0, val = 1;
while (val <= 10){
sum += val;
++val;
}
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
}
void basic_for(){
int sum = 0;
for (int val = 1; val <= 10; ++val){
sum += val;
}
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
}
void basic_if(){
std::cout << "Enter two numbers:" << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
int lower, upper;
if (v1<=v2){
lower = v1;
upper = v2;
} else {
lower = v2;
upper = v1;
}
int sum = 0;
for (int val = lower; val <= upper; ++val)
sum += val;
std::cout << "Sum of " << lower
<< " to " << upper
<< " inclusive is "
<< sum << std::endl;
}
void basic_cin(){
int sum = 0, value;
while (std::cin >> value)
sum += value;
std::cout << "Sum is: " << sum << std::endl;
}
void q_1_3(){
std::cout << "Hello, World" << std::endl;
}
void q_1_4(){
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The product of " << v1 << " and " << v2
<< " is " << v1 * v2 << std::endl;
}
void q_1_5(){
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The product of ";
std::cout << v1;
std::cout << " and ";
std::cout << v2;
std::cout << " is ";
std::cout << v1 * v2;
std::cout << std::endl;
}
void q_1_9(){
int sum = 0, val = 50;
while (val <= 100){
sum += val;
val += 1;
}
std::cout << "Sum of 50 to 100 inclusive is "
<< sum << std::endl;
}
void q_1_10(){
int val = 10;
while (val >= 0){
std::cout << val << " ";
val -= 1;
}
std::cout << std::endl;
}
void q_1_11(){
int start = 0, end = 0;
std::cout << "Please input two num: ";
std::cin >> start >> end;
if (start <= end) {
while (start <= end){
std::cout << start << " ";
++start;
}
std::cout << std::endl;
}
else{
std::cout << "start should be smaller than end !!!";
}
}
void q_1_16(){
int sum = 0;
for (int value = 0; std::cin >> value; )
sum += value;
std::cout << sum << std::endl;
}
void count_num(){
// 统计输入中每个值连续出现了多少次
int currVal = 0, val = 0;
if (std::cin >> currVal){
int cnt = 1;
while (std::cin >> val){
if (val == currVal)
++cnt;
else {
std::cout << currVal << " occurs "
<< cnt << " times " << std::endl;
currVal = val;
cnt = 1;
}
}
std::cout << currVal << " occurs "
<< cnt << " times " << std::endl;
}
}
void q_1_20(){
for (Sales_item item; std::cin >> item; std::cout << item << std::endl);
}
void q_1_21(){
Sales_item item_1;
Sales_item item_2;
std::cin >> item_1;
std::cout << item_1 << std::endl;
std::cin >> item_2;
std::cout << item_2 << std::endl;
std::cout << "sum of sale items: " << item_1 + item_2 << std::endl;
}
void q_1_22(){
Sales_item sum_item;
std::cin >> sum_item;
std::cout << sum_item << std::endl;
for (Sales_item item; std::cin >> item; std::cout << item << std::endl){
sum_item += item;
}
std::cout << "sum of sale items: " << sum_item << std::endl;
}
void q_1_23(){
Sales_item total;
if (std::cin >> total){
Sales_item trans;
while (std::cin >> trans){
if (total.isbn() == trans.isbn()) {
total += trans;
}
else {
std::cout << total << std::endl;
total = trans;
}
}
std::cout << total << std::endl;
}
else {
std::cerr << "No data?!" << std::endl;
return;
}
}
int main() {
// basic_io();
// basic_while();
// basic_for();
// basic_if();
// basic_cin();
// q_1_3();
// q_1_4();
// q_1_5();
// /* 正常注释 /* 嵌套注释 */ 正常注释*/
// std::cout << /* "*/" /* "/*" */;
// q_1_9();
// q_1_10();
// q_1_11();
// q_1_16();
// count_num();
// q_1_20();
// q_1_21();
// q_1_22();
q_1_23();
return 0;
}
================================================
FILE: cpp_source/ch01/data/add
================================================
3 7
================================================
FILE: cpp_source/ch01/data/add_item
================================================
0-201-78345-X 3 20.00
0-201-78345-X 2 25.00
================================================
FILE: cpp_source/ch01/data/book_sales
================================================
0-201-70353-X 4 24.99
0-201-82470-1 4 45.39
0-201-88954-4 2 15.00
0-201-88954-4 5 12.00
0-201-88954-4 7 12.00
0-201-88954-4 2 12.00
0-399-82477-1 2 45.39
0-399-82477-1 3 45.39
0-201-78345-X 3 20.00
0-201-78345-X 2 25.00
================================================
FILE: cpp_source/ch01/data/mysum
================================================
3 4 5 6
================================================
FILE: cpp_source/ch01/data/occurs
================================================
42 42 42 42 42 55 55 62 100 100 100
================================================
FILE: cpp_source/ch02/Sales_item.h
================================================
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
* copyright and warranty notices given in that book:
*
* "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced.Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
/* This file defines the Sales_item class used in chapter 1.
* The code used in this file will be explained in
* Chapter 7 (Classes) and Chapter 14 (Overloaded Operators)
* Readers shouldn't try to understand the code in this file
* until they have read those chapters.
*/
#ifndef SALESITEM_H
// we're here only if SALESITEM_H has not yet been defined
#define SALESITEM_H
// Definition of Sales_item class and related functions goes here
#include <iostream>
#include <string>
class Sales_item {
// these declarations are explained section 7.2.1, p. 270
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator>>(std::istream&, Sales_item&);
friend std::ostream& operator<<(std::ostream&, const Sales_item&);
friend bool operator<(const Sales_item&, const Sales_item&);
friend bool
operator==(const Sales_item&, const Sales_item&);
public:
// constructors are explained in section 7.1.4, pages 262 - 265
// default constructor needed to initialize members of built-in type
Sales_item() = default;
Sales_item(const std::string &book): bookNo(book) { }
Sales_item(std::istream &is) { is >> *this; }
public:
// operations on Sales_item objects
// member binary operator: left-hand operand bound to implicit this pointer
Sales_item& operator+=(const Sales_item&);
// operations on Sales_item objects
std::string isbn() const { return bookNo; }
double avg_price() const;
// private members as before
private:
std::string bookNo; // implicitly initialized to the empty string
unsigned units_sold = 0; // explicitly initialized
double revenue = 0.0;
};
// used in chapter 10
inline
bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs)
{ return lhs.isbn() == rhs.isbn(); }
// nonmember binary operator: must declare a parameter for each operand
Sales_item operator+(const Sales_item&, const Sales_item&);
inline bool
operator==(const Sales_item &lhs, const Sales_item &rhs)
{
// must be made a friend of Sales_item
return lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue &&
lhs.isbn() == rhs.isbn();
}
inline bool
operator!=(const Sales_item &lhs, const Sales_item &rhs)
{
return !(lhs == rhs); // != defined in terms of operator==
}
// assumes that both objects refer to the same ISBN
Sales_item& Sales_item::operator+=(const Sales_item& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs); // copy (|lhs|) into a local object that we'll return
ret += rhs; // add in the contents of (|rhs|)
return ret; // return (|ret|) by value
}
std::istream&
operator>>(std::istream& in, Sales_item& s)
{
double price;
in >> s.bookNo >> s.units_sold >> price;
// check that the inputs succeeded
if (in)
s.revenue = s.units_sold * price;
else
s = Sales_item(); // input failed: reset object to default state
return in;
}
std::ostream&
operator<<(std::ostream& out, const Sales_item& s)
{
out << s.isbn() << " " << s.units_sold << " "
<< s.revenue << " " << s.avg_price();
return out;
}
double Sales_item::avg_price() const
{
if (units_sold)
return revenue/units_sold;
else
return 0;
}
#endif
================================================
FILE: cpp_source/ch02/ch02.cpp
================================================
//
// Created by cer
// chapter 2
// 变量和基本类型
#include <iostream>
#include "Sales_item.h"
void basic_class(){
Sales_item book;
std::cin >> book;
std::cout << book << std::endl;
}
void q_2_3(){
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;
std::cout << u - u2 << std::endl;
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;
std::cout << i - i2 << std::endl;
std::cout << i - u << std::endl;
std::cout << u - i << std::endl;
}
// 重写 1.5.1 1.5.2 1.6
// 1.5.1
#include <iostream>
#include <string>
struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int q_1_5_1()
{
Sale_data book;
double price;
std::cin >> book.bookNo >> book.units_sold >> price;
book.revenue = book.units_sold * price;
std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;
return 0;
}
// 1.5.2
int q_1_5_2()
{
Sale_data book1, book2;
double price1, price2;
std::cin >> book1.bookNo >> book1.units_sold >> price1;
std::cin >> book2.bookNo >> book2.units_sold >> price2;
book1.revenue = book1.units_sold * price1;
book2.revenue = book2.units_sold * price2;
if (book1.bookNo == book2.bookNo)
{
unsigned totalCnt = book1.units_sold + book2.units_sold;
double totalRevenue = book1.revenue + book2.revenue;
std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
if (totalCnt != 0)
std::cout << totalRevenue / totalCnt << std::endl;
else
std::cout << "(no sales)" << std::endl;
return 0;
}
else
{
std::cerr << "Data must refer to same ISBN" << std::endl;
return -1; // indicate failure
}
}
// 1.6
int q_1_6()
{
Sale_data total;
double totalPrice;
if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
{
total.revenue = total.units_sold * totalPrice;
Sale_data trans;
double transPrice;
while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
{
trans.revenue = trans.units_sold * transPrice;
if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
if (total.units_sold != 0)
std::cout << total.revenue / total.units_sold << std::endl;
else
std::cout << "(no sales)" << std::endl;
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;
}
}
std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
if (total.units_sold != 0)
std::cout << total.revenue / total.units_sold << std::endl;
else
std::cout << "(no sales)" << std::endl;
return 0;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1; // indicate failure
}
}
int main(){
// q_2_3();
// q_1_5_1();
// q_1_5_2();
q_1_6();
return 0;
}
================================================
FILE: cpp_source/ch03/ch3.cpp
================================================
//
// Created by cer
// chapter 3
// 字符串、向量和数组
#include <iostream>
#include <vector>
#include <string>
#include <bitset>
#include <cctype>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::bitset;
void basic_getline(){
string line;
while (getline(cin, line))
cout << line << endl;
}
void basic_string(){
string s = "Test String.";
cout << "Size: " << s.size() << endl
<< "Empty: " << s.empty() << endl
<< "char at index 0:" << s[0] << endl;
for(string::size_type i=0; i<s.size(); i++){
cout << s[i];
}
cout << endl;
}
void basic_vector(){
vector<int> v;
for (int i = 0; i != 10; i++){
v.push_back(i);
}
for (vector<int>::iterator iter = v.begin();
iter != v.end(); ++iter){
*iter = 0;
}
// 不能直接输出vector
}
void basic_bieset(){
bitset<16> bitvec1(0xffff);
string bit_str = "1100";
bitset<32> bitvec2(bit_str); //从右到左读取字符串,只能是string对象,不能是字面值
bitset<16> bitvec3(bit_str, 0, 3); // 只要前三位
cout << bitvec1 << endl
<< bitvec2 << endl
<< bitvec3 << endl
<< bitvec2.test(3) <<" " <<bitvec2.test(3) << endl
<< bitvec2.to_ulong() << endl;
}
void q_3_14(){
vector<int> v;
int i;
while (cin >> i){
v.push_back(i);
}
}
void q_3_17(){
vector<string> v;
string s;
while (cin >> s){
v.push_back(s);
}
for (auto &str : v)
{
for (auto &c : str)
{
c = toupper(c);
}
}
for (auto str : v){
cout << str << endl;
}
}
void q_3_23(){
vector<int> v(10, 1);
for (auto it=v.begin(); it!=v.end(); it++){
*it *= 2;
}
for (auto one : v){
cout << one <<endl;
}
}
void q_3_31(){
int a[10];
for (size_t i=0; i != 10; i++){
a[i] = i;
}
}
int main(){
// basic_getline();
// basic_string();
// basic_bieset();
// q_3_14();
// q_3_17();
q_3_23();
}
================================================
FILE: cpp_source/ch04/ch04.cpp
================================================
//
// chapter 4
// 表达式
#include <iostream>
using namespace std;
int q_4_28()
{
cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;
cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;
cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;
cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;
return 0;
}
int main(){
q_4_28();
}
================================================
FILE: cpp_source/ch05/ch5.cpp
================================================
//
// Created by cer on 17-9-18.
// chapter 5
// 语句
#include <iostream>
#include <vector>
using namespace std;
int divide(int a, int b){
if (b == 0){
// 抛出异常
throw runtime_error("b cannot be 0!");
}
else{
return a / b;
}
}
int main(){
int a = 1, b = 0, res;
try{
res = divide(a, b);
cout << res << endl;
}catch(runtime_error err){
cout << err.what() << endl;
}
return 0;
}
================================================
FILE: cpp_source/ch06/Chapter6.h
================================================
//
// Created by cer on 19-1-20.
//
#ifndef CPP_PRIMER_PRACTICE_CHAPTER6_H
#define CPP_PRIMER_PRACTICE_CHAPTER6_H
int fact(int val);
int func();
template <typename T>
T abs(T i)
{
return i >= 0 ? i : -i;
}
#endif //CPP_PRIMER_PRACTICE_CHAPTER6_H
================================================
FILE: cpp_source/ch06/ch6.cpp
================================================
//
// Created by cer on 17-9-19.
// chapter 06
// 函数
#include <iostream>
#include <string>
using namespace std
int fact(int i)
{
return i > 1 ? i * fact(i - 1) : 1;
}
void interactive_fact()
{
string const prompt = "Enter a number within [1, 13) :\n";
string const out_of_range = "Out of range, please try again.\n";
for (int i; cout << prompt, cin >> i; )
{
if (i < 1 || i > 12)
{
cout << out_of_range;
continue;
}
cout << fact(i) << endl;
}
}
bool str_subrange(const string &str1, const string &str2){
if(str1.size()==str2.size())
return str1==str2;
string::size_type size={min(str1.size(),str2.size())};
string::size_type i=0;
while(i!=size){
if(str1[i]!=str2[i])
return ; //error! no return value!
}
}
int main()
{
// interactive_fact();
str_subrange();
return 0;
}
================================================
FILE: cpp_source/ch06/fact.cpp
================================================
//
// Created by cer on 19-1-20.
//
#include "Chapter6.h"
#include <iostream>
int fact(int val)
{
if (val == 0 || val == 1) return 1;
else return val * fact(val-1);
}
int func()
{
int n, ret = 1;
std::cout << "input a number: ";
std::cin >> n;
while (n > 1) ret *= n--;
return ret;
}
================================================
FILE: cpp_source/ch06/factMain.cpp
================================================
//
// Created by cer on 19-1-20.
//
#include "Chapter6.h"
#include <iostream>
int main()
{
std::cout << "5! is " << fact(5) << std::endl;
std::cout << func() << std::endl;
std::cout << abs(-9.78) << std::endl;
}
================================================
FILE: cpp_source/ch07/ex_7_41.cpp
================================================
#include "ex_7_41.h"
// constructor
Sales_data::Sales_data(std::istream &is) : Sales_data()
{
std::cout << "Sales_data(istream &is)" << std::endl;
read(is, *this);
}
// member functions.
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// friend functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
================================================
FILE: cpp_source/ch07/ex_7_41.h
================================================
#ifndef CP5_ex7_41_h
#define CP5_ex7_41_h
#include <string>
#include <iostream>
class Sales_data {
friend std::istream &read(std::istream &is, Sales_data &item);
friend std::ostream &print(std::ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p)
{ std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl; }
Sales_data() : Sales_data("", 0, 0.0f)
{ std::cout << "Sales_data()" << std::endl; }
Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f)
{ std::cout << "Sales_data(const std::string&)" << std::endl; }
Sales_data(std::istream &is);
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
private:
inline double avg_price() const;
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline
double Sales_data::avg_price() const
{
return units_sold ? revenue/units_sold : 0;
}
// declarations for nonmember parts of the Sales_data interface.
std::istream &read(std::istream &is, Sales_data &item);
std::ostream &print(std::ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
#endif
================================================
FILE: cpp_source/ch07/ex_7_41_main.cpp
================================================
#include "ex_7_41.h"
using std::cout; using std::endl;
int main()
{
cout << "1. default way: " << endl;
cout << "----------------" << endl;
Sales_data s1;
cout << "\n2. use std::string as parameter: " << endl;
cout << "----------------" << endl;
Sales_data s2("CPP-Primer-5th");
cout << "\n3. complete parameters: " << endl;
cout << "----------------" << endl;
Sales_data s3("CPP-Primer-5th", 3, 25.8);
cout << "\n4. use istream as parameter: " << endl;
cout << "----------------" << endl;
Sales_data s4(std::cin);
return 0;
}
================================================
FILE: cpp_source/ch07/screen.cpp
================================================
# include "screen.h"
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(std::cout);
std::cout << "\n";
myScreen.display(std::cout);
std::cout << "\n";
return 0;
}
================================================
FILE: cpp_source/ch07/screen.h
================================================
#ifndef CH07_screen
#define CH07_screen
#include <string>
#include <iostream>
class Screen {
public:
using pos = std::string::size_type;
Screen() = default; // 1
Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width+c]; }
inline Screen& move(pos r, pos c);
inline Screen& set(char c);
inline Screen& set(pos r, pos c, char ch);
const Screen& display(std::ostream &os) const { do_display(os); return *this; }
Screen& display(std::ostream &os) { do_display(os); return *this; }
private:
void do_display(std::ostream &os) const { os << contents; }
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
inline Screen& Screen::move(pos r, pos c)
{
cursor = r*width + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r*width+c] = ch;
return *this;
}
#endif
================================================
FILE: cpp_source/ch08/ch8.cpp
================================================
// chapter 08
// 标准IO库
#include <iostream>
#include <unistd.h>
#include <fstream>
using namespace std;
void loading(){
for (int i = 0; i != 11; i++){
cout << "loading " << (i * 10) << "%\r" << flush;
sleep(1);
}
cout << "loaded! " << endl;
}
void file_io(){
string in_file_name = "test_in_file";
string out_file_name = "test_out_file";
ifstream infile(in_file_name.c_str());
ofstream outfile(out_file_name.c_str());
if (!infile){
cerr << "err: unable to open input file: "
<< in_file_name << endl;
}
else{
// 按词读取
// string word;
// while(infile >> word) {
// cout << word << endl;
// }
// 按行读取
string line;
while(getline(infile,line)){
cout << line << endl;
// 按行写入
outfile << line << endl;
}
}
infile.close();
outfile.close();
}
int main(){
file_io();
return 0;
}
================================================
FILE: cpp_source/ch09/date.cpp
================================================
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class My_date{
private:
unsigned year, month, day;
public:
My_date(const string &s){
unsigned tag;
unsigned format;
format = tag = 0;
// 1/1/1900
if(s.find_first_of("/")!= string :: npos)
{
format = 0x01;
}
// January 1, 1900 or Jan 1, 1900
if((s.find_first_of(',') >= 4) && s.find_first_of(',')!= string :: npos){
format = 0x10;
}
else{ // Jan 1 1900
if(s.find_first_of(' ') >= 3
&& s.find_first_of(' ')!= string :: npos){
format = 0x10;
tag = 1;
}
}
switch(format){
case 0x01:
day = stoi(s.substr(0, s.find_first_of("/")));
month = stoi(s.substr(s.find_first_of("/") + 1, s.find_last_of("/")- s.find_first_of("/")));
year = stoi(s.substr(s.find_last_of("/") + 1, 4));
break;
case 0x10:
if( s.find("Jan") < s.size() ) month = 1;
if( s.find("Feb") < s.size() ) month = 2;
if( s.find("Mar") < s.size() ) month = 3;
if( s.find("Apr") < s.size() ) month = 4;
if( s.find("May") < s.size() ) month = 5;
if( s.find("Jun") < s.size() ) month = 6;
if( s.find("Jul") < s.size() ) month = 7;
if( s.find("Aug") < s.size() ) month = 8;
if( s.find("Sep") < s.size() ) month = 9;
if( s.find("Oct") < s.size() ) month =10;
if( s.find("Nov") < s.size() ) month =11;
if( s.find("Dec") < s.size() ) month =12;
char chr = ',';
if(tag == 1){
chr = ' ';
}
day = stoi(s.substr(s.find_first_of("123456789"), s.find_first_of(chr) - s.find_first_of("123456789")));
year = stoi(s.substr(s.find_last_of(' ') + 1, 4));
break;
}
}
void print(){
cout << "day:" << day << " " << "month: " << month << " " << "year: " << year;
}
};
int main()
{
My_date d("Jan 1 1900");
d.print();
return 0;
}
================================================
FILE: cpp_source/ch10/biggies.cpp
================================================
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// from ex 10.9
void elimdups(std::vector<std::string> &vs)
{
std::sort(vs.begin(), vs.end());
auto new_end = std::unique(vs.begin(), vs.end());
vs.erase(new_end, vs.end());
}
void biggies(std::vector<std::string> &vs, std::size_t sz)
{
using std::string;
elimdups(vs);
// sort by size, but maintain alphabetical order for same size.
std::stable_sort(vs.begin(), vs.end(), [](string const& lhs, string const& rhs){
return lhs.size() < rhs.size();
});
// get an iterator to the first one whose size() is >= sz
auto wc = std::find_if(vs.begin(), vs.end(), [sz](string const& s){
return s.size() >= sz;
});
// print the biggies
std::for_each(wc, vs.end(), [](const string &s){
std::cout << s << " ";
});
}
int main()
{
// ex10.16
std::vector<std::string> v
{
"1234","1234","1234","hi~", "alan", "alan", "cp"
};
std::cout << "ex10.16: ";
biggies(v, 3);
std::cout << std::endl;
return 0;
}
================================================
FILE: cpp_source/ch10/inserter.cpp
================================================
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
#include <iterator>
using std::list; using std::copy; using std::cout; using std::endl;
template<typename Sequence>
void print(Sequence const& seq)
{
for (const auto& i: seq)
std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
std::vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// uses inserter
list<int> lst1;
copy(vec.cbegin(), vec.cend(), inserter(lst1, lst1.begin()));
print(lst1);
// uses back_inserter
list<int> lit2;
copy(vec.cbegin(), vec.cend(), back_inserter(lit2));
print(lit2);
// uses front_inserter
list<int> lst3;
copy(vec.cbegin(), vec.cend(), front_inserter(lst3));
print(lst3);
}
================================================
FILE: cpp_source/ch11/ex_11_3.cpp
================================================
#include <string>
#include <map>
#include <iostream>
using namespace std;
int main(){
map<string, int> word_count;
string tmp;
while (cin >> tmp){
word_count[tmp] += 1;
}
for (const auto& elem : word_count)
std::cout << elem.first << " : " << elem.second << endl;
return 0;
}
================================================
FILE: cpp_source/ch11/ex_11_4.cpp
================================================
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
void word_count_pro(std::map<std::string, int>& m)
{
std::string word;
while (std::cin >> word)
{
for (auto& ch : word)
ch = tolower(ch);
word.erase(std::remove_if(word.begin(), word.end(), ispunct),
word.end());
++m[word];
}
for (const auto& e : m) std::cout << e.first << " : " << e.second << "\n";
}
int main()
{
std::map<std::string, int> m;
word_count_pro(m);
return 0;
}
================================================
FILE: cpp_source/ch12/ex_12_27.cpp
================================================
#include "ex_12_27.h"
#include <sstream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
TextQuery::TextQuery(ifstream& ifs) : file(new vector<string>)
{
string text;
while (getline(ifs, text))
{
file->push_back(text); // 保存文本
int n = file->size() - 1; // 当前行号
istringstream line(text);
string word;
while (line >> word)
{
auto &lines = wm[word];
if (!lines)
lines.reset(new set<line_no>); // 如果第一次遇到这个单词,则新建一个set
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const string& s) const
{
static shared_ptr<set<line_no> > nodata(new set<line_no>);
// 使用find而不是下标运算符来查找单词,避免将单词添加到wm中
auto loc = wm.find(s);
if (loc == wm.end())
return QueryResult(s, nodata, file); // 未找到
else
return QueryResult(s, loc->second, file);
}
std::ostream& print(std::ostream& os, const QueryResult& qr)
{
// 如果找到了单词,打印出现次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << " "
<< "time" << (qr.lines->size() > 1 ? "s" : "") << endl;
// 打印单词出现的每一行
for (auto num : *qr.lines)
os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
return os;
}
================================================
FILE: cpp_source/ch12/ex_12_27.h
================================================
#ifndef EX12_27_H
#define EX12_27_H
#include <fstream>
#include <memory>
#include <vector>
#include <string>
#include <map>
#include <set>
class QueryResult; //为了定义函数query的返回类型,这个定义是必须的
class TextQuery
{
public:
// 行号的类型
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string& s) const;
private:
std::shared_ptr<std::vector<std::string> > file; // 输入文件
// 每个单词到它所在的行号的集合的映射
std::map<std::string, std::shared_ptr<std::set<line_no> > > wm;
};
class QueryResult
{
public:
friend std::ostream& print(std::ostream&, const QueryResult&);
QueryResult(std::string s,
std::shared_ptr<std::set<TextQuery::line_no> > p,
std::shared_ptr<std::vector<std::string> > f) :
sought(s), lines(p), file(f)
{}
private:
std::string sought; // 查询的单词
std::shared_ptr<std::set<TextQuery::line_no> > lines; // 出现的行号
std::shared_ptr<std::vector<std::string> > file; // 输入文件
};
std::ostream& print(std::ostream&, const QueryResult&);
#endif
================================================
FILE: cpp_source/ch12/ex_12_27_main.cpp
================================================
#include <iostream>
#include <string>
#include <fstream>
#include "ex_12_27.h"
using namespace std;
void runQueries(ifstream& infile)
{
// infile是一个ifstream,指向我们要处理的文件
TextQuery tq(infile);
// 与用户交互:提示用户输入要查询的单词,完成查询并打印结果
while (true)
{
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到了文件尾或者用户输入了‘q’时循环终止
if (!(cin >> s) || s == "q") break;
// 指向查询并打印结果
print(cout, tq.query(s)) << endl;
}
}
int main()
{
ifstream ifs("storyDataFile.txt");
runQueries(ifs);
return 0;
}
================================================
FILE: cpp_source/ch12/storyDataFile.txt
================================================
Hello world!
This is a funny trip for learning c++.
I am Javen Chen.
Nice to see you.
================================================
FILE: cpp_source/ch13/ex_13_13.cpp
================================================
#include <iostream>
#include <vector>
#include <initializer_list>
struct X {
X() { std::cout << "X()" << std::endl; }
X(const X&) { std::cout << "X(const X&)" << std::endl; }
X& operator=(const X&) { std::cout << "X& operator=(const X&)" << std::endl; return *this; }
~X() { std::cout << "~X()" << std::endl; }
};
void f(const X &rx, X x)
{
std::cout << "code: std::vector<X> vec;" << std::endl;
std::vector<X> vec;
std::cout << "code: vec.reserve(2);" << std::endl;
vec.reserve(2);
std::cout << "code: vec.push_back(rx);" << std::endl;
vec.push_back(rx);
std::cout << "code: vec.push_back(x);" << std::endl;
vec.push_back(x);
}
int main()
{
std::cout << "code: X *px = new X;" << std::endl;
X *px = new X;
std::cout << "code: f(*px, *px);" << std::endl;
f(*px, *px);
std::cout << "code: delete px;" << std::endl;
delete px;
return 0;
}
================================================
FILE: cpp_source/ch13/ex_13_34_36_37.cpp
================================================
#include "ex_13_34_36_37.h"
#include <iostream>
void swap(Message &lhs, Message &rhs)
{
using std::swap;
lhs.remove_from_Folders(); // Use existing member function to avoid duplicate code.
rhs.remove_from_Folders(); // Use existing member function to avoid duplicate code.
swap(lhs.folders, rhs.folders);
swap(lhs.contents, rhs.contents);
lhs.add_to_Folders(lhs); // Use existing member function to avoid duplicate code.
rhs.add_to_Folders(rhs); // Use existing member function to avoid duplicate code.
}
// Message Implementation
void Message::save(Folder &f)
{
addFldr(&f); // Use existing member function to avoid duplicate code.
f.addMsg(this);
}
void Message::remove(Folder &f)
{
remFldr(&f); // Use existing member function to avoid duplicate code.
f.remMsg(this);
}
void Message::add_to_Folders(const Message &m)
{
for (auto f : m.folders)
f->addMsg(this);
}
Message::Message(const Message &m)
: contents(m.contents), folders(m.folders)
{
add_to_Folders(m);
}
void Message::remove_from_Folders()
{
for (auto f : folders)
f->remMsg(this);
// The book added one line here: folders.clear(); but I think it is redundant and more importantly, it will cause a bug:
// - In Message::operator=, in the case of self-assignment, it first calls remove_from_Folders() and its folders.clear()
// clears the data member of lhs(rhs), and there is no way we can assign it back to lhs.
// Refer to: http://stackoverflow.com/questions/29308115/protection-again-self-assignment
// - Why is it redundant? As its analogous function Message::add_to_Folders(), Message::remove_from_Folders() should ONLY
// take care of the bookkeeping in Folders but not touch the Message's own data members - makes it much clearer and easier
// to use. As you can see in the 2 places where we call Message::remove_from_Folders(): in Message::operator=, folders.clear()
// introduces a bug as illustrated above; in the destructor ~Message(), the member "folders" will be destroyed anyways, why do
// we need to clear it first?
}
Message::~Message()
{
remove_from_Folders();
}
Message &Message::operator=(const Message &rhs)
{
remove_from_Folders();
contents = rhs.contents;
folders = rhs.folders;
add_to_Folders(rhs);
return *this;
}
void Message::print_debug()
{
std::cout << contents << std::endl;
}
// Folder Implementation
void swap(Folder &lhs, Folder &rhs)
{
using std::swap;
lhs.remove_from_Message();
rhs.remove_from_Message();
swap(lhs.msgs, rhs.msgs);
lhs.add_to_Message(lhs);
rhs.add_to_Message(rhs);
}
void Folder::add_to_Message(const Folder &f)
{
for (auto m : f.msgs)
m->addFldr(this);
}
Folder::Folder(const Folder &f)
: msgs(f.msgs)
{
add_to_Message(f);
}
void Folder::remove_from_Message()
{
for (auto m : msgs)
m->remFldr(this);
}
Folder::~Folder()
{
remove_from_Message();
}
Folder &Folder::operator=(const Folder &rhs)
{
remove_from_Message();
msgs = rhs.msgs;
add_to_Message(rhs);
return *this;
}
void Folder::print_debug()
{
for (auto m : msgs)
std::cout << m->contents << " ";
std::cout << std::endl;
}
int main()
{
return 0;
}
================================================
FILE: cpp_source/ch13/ex_13_34_36_37.h
================================================
#ifndef CP5_ex13_34_36_37_h
#define CP5_ex13_34_36_37_h
#include <string>
#include <set>
class Folder;
class Message {
friend void swap(Message &, Message &);
friend class Folder;
public:
explicit Message(const std::string &str = ""):contents(str) { }
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
void print_debug();
private:
std::string contents;
std::set<Folder*> folders;
void add_to_Folders(const Message&);
void remove_from_Folders();
void addFldr(Folder *f) { folders.insert(f); }
void remFldr(Folder *f) { folders.erase(f); }
};
void swap(Message&, Message&);
class Folder {
friend void swap(Folder &, Folder &);
friend class Message;
public:
Folder() = default;
Folder(const Folder &);
Folder& operator=(const Folder &);
~Folder();
void print_debug();
private:
std::set<Message*> msgs;
void add_to_Message(const Folder&);
void remove_from_Message();
void addMsg(Message *m) { msgs.insert(m); }
void remMsg(Message *m) { msgs.erase(m); }
};
void swap(Folder &, Folder &);
#endif // MESSAGE
================================================
FILE: cpp_source/ch14/ex_14_44.cpp
================================================
#include <iostream>
#include <string>
#include <map>
#include <functional>
int add(int i, int j) { return i + j; }
auto mod = [](int i, int j) { return i % j; };
struct Div { int operator ()(int i, int j) const { return i / j; } };
auto binops = std::map<std::string, std::function<int(int, int)>>
{
{ "+", add }, // function pointer
{ "-", std::minus<int>() }, // library functor
{ "/", Div() }, // user-defined functor
{ "*", [](int i, int j) { return i*j; } }, // unnamed lambda
{ "%", mod } // named lambda object
};
int main()
{
while (std::cout << "Pls enter as: num operator num :\n", true)
{
int lhs, rhs; std::string op;
std::cin >> lhs >> op >> rhs;
std::cout << binops[op](lhs, rhs) << std::endl;
}
return 0;
}
================================================
FILE: cpp_source/ch15/ex_15_26/bulk_quote.cpp
================================================
#include "bulk_quote.h"
double Bulk_quote::net_price(std::size_t n) const
{
return n * price * ( n >= quantity ? 1 - discount : 1);
}
void Bulk_quote::debug() const
{
std::cout //<< "data members of this class:\n"
<< "min_qty= " << quantity << " "
<< "discount= " << this->discount<< " \n";
}
================================================
FILE: cpp_source/ch15/ex_15_26/bulk_quote.h
================================================
#ifndef BULK_QUOTE_H
#define BULK_QUOTE_H
#include "disc_quote.h"
class Bulk_quote : public Disc_quote
{
public:
Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; }
Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :
Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; }
// copy constructor
Bulk_quote(const Bulk_quote& bq) : Disc_quote(bq)
{ std::cout << "Bulk_quote : copy constructor\n"; }
// move constructor
//page 535, " In a constructor, noexcept appears between the parameter list and the : that begins the constructor initializer list"
Bulk_quote(Bulk_quote&& bq) noexcept : Disc_quote(std::move(bq))
{
std::cout << "Bulk_quote : move constructor\n";
}
// copy =()
Bulk_quote& operator =(const Bulk_quote& rhs)
{
Disc_quote::operator =(rhs);
std::cout << "Bulk_quote : copy =()\n";
return *this;
}
// move =()
Bulk_quote& operator =(Bulk_quote&& rhs) noexcept
{
Disc_quote::operator =(std::move(rhs));
std::cout << "Bulk_quote : move =()\n";
return *this;
}
double net_price(std::size_t n) const override;
void debug() const override;
~Bulk_quote() override
{
std::cout << "destructing Bulk_quote\n";
}
};
#endif // BULK_QUOTE_H
================================================
FILE: cpp_source/ch15/ex_15_26/disc_quote.cpp
================================================
#include "disc_quote.h"
================================================
FILE: cpp_source/ch15/ex_15_26/disc_quote.h
================================================
#ifndef DISC_QUOTE_H
#define DISC_QUOTE_H
#include "quote.h"
class Disc_quote : public Quote
{
friend bool operator !=(const Disc_quote& lhs, const Disc_quote& rhs);
public:
Disc_quote() { std::cout << "default constructing Disc_quote\n"; }
Disc_quote(const std::string& b, double p, std::size_t q, double d) :
Quote(b, p), quantity(q), discount(d)
{
std::cout << "Disc_quote : constructor taking 4 parameters.\n";
}
// copy constructor
Disc_quote(const Disc_quote& dq) :
Quote(dq), quantity(dq.quantity), discount(dq.discount)
{
std::cout << "Disc_quote : copy constructor.\n";
}
// move constructor
Disc_quote(Disc_quote&& dq) noexcept :
Quote(std::move(dq)), quantity(std::move(dq.quantity)), discount(std::move(dq.discount))
{
std::cout << "Disc_quote : move constructor.\n";
}
// copy =()
Disc_quote& operator =(const Disc_quote& rhs)
{
Quote::operator =(rhs);
this->quantity = rhs.quantity;
this->discount = rhs.discount;
std::cout << "Disc_quote : copy =()\n";
return *this;
}
// move =()
Disc_quote& operator =(Disc_quote&& rhs) noexcept
{
if (*this != rhs)
{
Quote::operator =(std::move(rhs));
this->quantity = std::move(rhs.quantity);
this->discount = std::move(rhs.discount);
}
std::cout << "Disc_quote : move =()\n";
return *this;
}
virtual double net_price(std::size_t n) const override = 0;
~Disc_quote()
{
std::cout << "destructing Dis_quote\n";
}
protected:
std::size_t quantity = 3;
double discount = 0.0;
};
bool inline
operator !=(const Disc_quote& lhs, const Disc_quote& rhs)
{
return Quote(lhs) != Quote(rhs)
&&
lhs.quantity != rhs.quantity
&&
lhs.discount != rhs.discount;
}
#endif // DISC_QUOTE_H
================================================
FILE: cpp_source/ch15/ex_15_26/limit_quote.cpp
================================================
#include "limit_quote.h"
void Limit_quote::debug() const
{
std::cout //<< "data members of this class:\n"
<< "max_qty= " << this->quantity << " "
<< "discount= " << this->discount<< " \n";
}
================================================
FILE: cpp_source/ch15/ex_15_26/limit_quote.h
================================================
#ifndef LIMIT_QUOTE_H
#define LIMIT_QUOTE_H
#include "disc_quote.h"
class Limit_quote : public Disc_quote
{
public:
Limit_quote() = default;
Limit_quote(const std::string& b, double p, std::size_t max, double disc):
Disc_quote(b, p, max, disc) { }
double net_price(std::size_t n) const override
{ return n * price * (n < quantity ? 1 - discount : 1 ); }
void debug() const override;
};
#endif // LIMIT_QUOTE_H
================================================
FILE: cpp_source/ch15/ex_15_26/main.cpp
================================================
#include <iostream>
#include <string>
#include "quote.h"
#include "bulk_quote.h"
#include "limit_quote.h"
#include "disc_quote.h"
int main()
{
Bulk_quote bq1;
Bulk_quote bq2("ss", 2.05, 12, 0.3);
bq2 = std::move(bq2);
return 0;
}
================================================
FILE: cpp_source/ch15/ex_15_26/quote.cpp
================================================
#include "quote.h"
void Quote::debug() const
{
std::cout //<< "data members of this class:\n"
<< "bookNo= " <<this->bookNo << " "
<< "price= " <<this->price<< " \n";
}
================================================
FILE: cpp_source/ch15/ex_15_26/quote.h
================================================
#ifndef QUOTE_H
#define QUOTE_H
#include <string>
#include <iostream>
class Quote
{
friend bool operator !=(const Quote& lhs, const Quote& rhs);
public:
Quote() { std::cout << "default constructing Quote\n"; }
Quote(const std::string &b, double p) :
bookNo(b), price(p) { std::cout << "Quote : constructor taking 2 parameters\n"; }
// copy constructor
Quote(const Quote& q) : bookNo(q.bookNo), price(q.price)
{ std::cout << "Quote: copy constructing\n"; }
// move constructor
Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price))
{ std::cout << "Quote: move constructing\n"; }
// copy =
Quote& operator =(const Quote& rhs)
{
if(*this != rhs)
{
bookNo = rhs.bookNo;
price = rhs.price;
}
std::cout << "Quote: copy =() \n";
return *this;
}
// move =
Quote& operator =(Quote&& rhs) noexcept
{
if(*this != rhs)
{
bookNo = std::move(rhs.bookNo);
price = std::move(rhs.price);
}
std::cout << "Quote: move =!!!!!!!!! \n";
return *this;
}
std::string isbn() const { return bookNo; }
virtual double net_price(std::size_t n) const { return n * price; }
virtual void debug() const;
virtual ~Quote()
{
std::cout << "destructing Quote\n";
}
private:
std::string bookNo;
protected:
double price = 10.0;
};
bool inline
operator !=(const Quote& lhs, const Quote& rhs)
{
return lhs.bookNo != rhs.bookNo
&&
lhs.price != rhs.price;
}
#endif // QUOTE_H
================================================
FILE: cpp_source/ch15/text_query/StrBlob.h
================================================
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
*
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced. Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
#ifndef STRBLOB_H
#define STRBLOB_H
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <stdexcept>
// forward declaration needed for friend declaration in StrBlob
class StrBlobPtr;
class StrBlob
{
friend class StrBlobPtr;
public:
typedef std::vector<std::string>::size_type size_type;
// constructors
StrBlob() : data(std::make_shared<std::vector<std::string>>()) { }
StrBlob(std::initializer_list<std::string> il);
// size operations
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
// add and remove elements
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
// element access
std::string& front();
std::string& back();
// interface to StrBlobPtr
StrBlobPtr begin(); // can't be defined until StrBlobPtr is
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
// throws msg if data[i] isn't valid
void check(size_type i, const std::string &msg) const;
};
// constructor
inline
StrBlob::StrBlob(std::initializer_list<std::string> il):
data(std::make_shared<std::vector<std::string>>(il)) { }
// StrBlobPtr throws an exception on attempts to access a nonexistent element
class StrBlobPtr
{
friend bool eq(const StrBlobPtr&, const StrBlobPtr&);
public:
StrBlobPtr(): curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
// newly overloaded why?
StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { }
std::string& deref() const;
StrBlobPtr& incr(); // prefix version
StrBlobPtr& decr(); // prefix version
private:
// check returns a shared_ptr to the vector if the check succeeds
std::shared_ptr<std::vector<std::string>>
check(std::size_t, const std::string&) const;
// store a weak_ptr, which means the underlying vector might be destroyed
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr; // current position within the array
};
inline
std::string& StrBlobPtr::deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
inline
std::shared_ptr<std::vector<std::string>>
StrBlobPtr::check(std::size_t i, const std::string &msg) const
{
auto ret = wptr.lock(); // is the vector still around?
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret; // otherwise, return a shared_ptr to the vector
}
// prefix: return a reference to the incremented object
inline
StrBlobPtr& StrBlobPtr::incr()
{
// if curr already points past the end of the container, can't increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
inline
StrBlobPtr& StrBlobPtr::decr()
{
// if curr is zero, decrementing it will yield an invalid subscript
--curr; // move the current state back one element}
check(-1, "decrement past begin of StrBlobPtr");
return *this;
}
// begin and end members for StrBlob
inline
StrBlobPtr
StrBlob::begin()
{
return StrBlobPtr(*this);
}
inline
StrBlobPtr
StrBlob::end()
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
// named equality operators for StrBlobPtr
inline
bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
// if the underlying vector is the same
if (l == r)
// then they're equal if they're both null or
// if they point to the same element
return (!r || lhs.curr == rhs.curr);
else
return false; // if they point to difference vectors, they're not equal
}
inline
bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
return !eq(lhs, rhs);
}
#endif
================================================
FILE: cpp_source/ch15/text_query/andquery.cpp
================================================
#include "andquery.h"
================================================
FILE: cpp_source/ch15/text_query/andquery.h
================================================
#ifndef ANDQUERY_H
#define ANDQUERY_H
#include "binaryquery.h"
class AndQuery : public BinaryQuery
{
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& left, const Query& right):
BinaryQuery(left, right, "&")
{
std::cout << "AndQuery::AndQuery()\n";
}
// @note: inherits rep and define eval
QueryResult eval(const TextQuery &) const override
{
// this is just a placeholder rather than the real definition
}
};
inline Query operator& (const Query& lhs, const Query& rhs)
{
return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
#endif // ANDQUERY_H
================================================
FILE: cpp_source/ch15/text_query/binaryquery.cpp
================================================
#include "binaryquery.h"
================================================
FILE: cpp_source/ch15/text_query/binaryquery.h
================================================
#ifndef BINARYQUERY_H
#define BINARYQUERY_H
#include "query_base.h"
#include "query.h"
/**
* @brief The BinaryQuery class
*An abstract class holds data needed by the query types that operate on two operands
*/
class BinaryQuery : public Query_base
{
protected:
BinaryQuery(const Query&l, const Query& r, std::string s):
lhs(l), rhs(r), opSym(s)
{
std::cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n";
}
// @note: abstract class: BinaryQuery doesn't define eval
std::string rep() const override
{
std::cout << "BinaryQuery::rep()\n";
return "(" + lhs.rep() + " "
+ opSym + " "
+ rhs.rep() + ")";
}
Query lhs, rhs;
std::string opSym;
};
#endif // BINARYQUERY_H
================================================
FILE: cpp_source/ch15/text_query/main.cpp
================================================
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include "queryresult.h"
#include "textquery.h"
#include "query_base.h"
#include "query.h"
#include "andquery.h"
#include "orquery.h"
int main()
{
std::ifstream file("storyDataFile.txt");
TextQuery tQuery(file);
Query q = Query("hello") | Query("am");
return 0;
}
================================================
FILE: cpp_source/ch15/text_query/notquery.cpp
================================================
#include "notquery.h"
================================================
FILE: cpp_source/ch15/text_query/notquery.h
================================================
#ifndef NOTQUERY_H
#define NOTQUERY_H
#include "query_base.h"
#include "query.h"
/**
* @brief The NotQuery class
*
*The ~ operator generates a NotQuery, which holds a Query,
*which it negates.
*/
class NotQuery : public Query_base
{
friend Query operator~(const Query& operand);
NotQuery(const Query& q): query(q)
{
std::cout << "NotQuery::NotQuery()\n";
}
// virtuals:
std::string rep() const override
{
std::cout << "NotQuery::rep()\n";
return "~(" + query.rep() + ")";
}
QueryResult eval(const TextQuery &) const override;
Query query;
};
inline Query operator~(const Query& operand)
{
return std::shared_ptr<Query_base>(new NotQuery(operand));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// note : There is an imlplicit conversion here.
// The Query constructor that takes shared_ptr is not
// "explicit", thus the compiler allows this conversion.
}
#endif // NOTQUERY_H
================================================
FILE: cpp_source/ch15/text_query/orquery.cpp
================================================
#include "orquery.h"
================================================
FILE: cpp_source/ch15/text_query/orquery.h
================================================
#ifndef ORQUERY_H
#define ORQUERY_H
#include "binaryquery.h"
class OrQuery :public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& left, const Query& right):
BinaryQuery(left, right, "|")
{
std::cout << "OrQuery::OrQuery\n";
}
QueryResult eval(const TextQuery& )const override
{
//place holder
}
};
inline Query operator|(const Query &lhs, const Query& rhs)
{
return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
#endif // ORQUERY_H
================================================
FILE: cpp_source/ch15/text_query/query.cpp
================================================
#include "query.h"
================================================
FILE: cpp_source/ch15/text_query/query.h
================================================
#ifndef QUERY_H
#define QUERY_H
#include <iostream>
#include <string>
#include <memory>
#include "query_base.h"
#include "queryresult.h"
#include "textquery.h"
#include "wordquery.h"
/**
* @brief interface class to manage the Query_base inheritance hierachy
* Query类对外提供接口,同时隐藏了Quer_base的继承体系。
* 每个Query对象都有一个指向Query_base对象的shared_ptr。
*/
class Query
{
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
// build a new WordQuery
Query(const std::string& s) : q(new WordQuery(s))
{
std::cout << "Query::Query(const std::string& s) where s="+s+"\n";
}
// interface functions: call the corresponding Query_base operatopns
QueryResult eval(const TextQuery& t) const
{ return q->eval(t); }
std::string rep() const
{
std::cout << "Query::rep() \n";
return q->rep();
}
private:
// constructor only for friends
Query(std::shared_ptr<Query_base> query) :
q(query)
{
std::cout << "Query::Query(std::shared_ptr<Query_base> query)\n";
}
std::shared_ptr<Query_base> q;
};
inline std::ostream&
operator << (std::ostream& os, const Query& query)
{
// make a virtual call through its Query_base pointer to rep();
return os << query.rep();
}
#endif // QUERY_H
================================================
FILE: cpp_source/ch15/text_query/query_base.cpp
================================================
#include "query_base.h"
================================================
FILE: cpp_source/ch15/text_query/query_base.h
================================================
#ifndef QUERY_BASE_H
#define QUERY_BASE_H
#include "textquery.h"
#include "queryresult.h"
/**
* @brief abstract class acts as a base class for all concrete query types
* all members are private.
*/
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no; // used in the eval function
virtual ~Query_base() = default;
private:
// returns QueryResult that matches this query
virtual QueryResult eval(const TextQuery&) const = 0;
// a string representation of this query
virtual std::string rep() const = 0;
};
#endif // QUERY_BASE_H
================================================
FILE: cpp_source/ch15/text_query/queryresult.cpp
================================================
/***************************************************************************
* @file queryresult.cpp
* @author Alan.W
* @date 30 DEC 2013
* @remark using class StrBlob
***************************************************************************/
#include "queryresult.h"
/**
* @brief print the result to the output stream specified.
* @note class QueryResult's friend
*/
std::ostream
&print(std::ostream &os, const QueryResult &qr)
{
os << qr.sought << " occurs " << qr.sp_lines->size() << " "
<< "times" << "\n";
// print each line in which the word appears
for ( auto &index : *qr.sp_lines)
{
os << "\t(line " << index + 1 << ") ";
const StrBlobPtr wp(qr.file, index);
os << wp.deref() << "\n";
}
return os;
}
================================================
FILE: cpp_source/ch15/text_query/queryresult.h
================================================
/***************************************************************************
* @file queryresult.h
* @author Alan.W
* @date 30 DEC 2013
* @remark using class StrBlob
***************************************************************************/
//
// Exercise 12.33:
// In Chapter 15 we’ll extend our query system and will need some additional
// members in the QueryResult class.
//
// Add members named begin and end that
// return iterators into the set of line numbers returned by a given query,
// and a member named get_file that returns a shared_ptr to the file in the
// QueryResult object.
//
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include <iostream>
#include <memory>
#include <set>
#include <vector>
#include <string>
#include "textquery.h"
/**
* @brief Query Result
*/
class QueryResult
{
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
// constructor
QueryResult(std::string s,
std::shared_ptr<std::set<TextQuery::line_no>> sp_l,
StrBlob f) :
sought(s), sp_lines(sp_l), file(f) { }
// added for ex12.33
// ? Think about wether the "const"s here are expected.
const StrBlob& get_file() const{ return file; }
std::set<TextQuery::line_no>::iterator
begin() { return sp_lines->begin(); }
std::set<TextQuery::line_no>::iterator
end() { return sp_lines->end(); }
private:
// three data members
std::string sought;
std::shared_ptr<std::set<TextQuery::line_no>> sp_lines;
StrBlob file;
};
/**
* @brief print the result to the output stream specified.
*/
std::ostream&
print(std::ostream&, const QueryResult &);
#endif // QUERYRESULT_H
================================================
FILE: cpp_source/ch15/text_query/storyDataFile.txt
================================================
Hello world!
This is a funny trip for learning c++.
I am Javen Chen.
Nice to see you.
================================================
FILE: cpp_source/ch15/text_query/textquery.cpp
================================================
/***************************************************************************
* @file textquery.cpp
* @author Alan.W
* @date 30 DEC 2013
* @remark The TextQuery class using StrBlob
***************************************************************************/
//
// Exercise 12.32:
// Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a
// vector<string> to hold the input file.
// Relevant post on Stack Overflow:
// http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m
//
#include "textquery.h"
#include "queryresult.h"
#include <iostream>
#include <sstream>
#include <iterator>
/**
* @brief constructor using StrBlob.
*/
TextQuery::TextQuery(std::ifstream &fin) :
file(StrBlob()),
wordMap(std::map<std::string, std::shared_ptr<std::set<line_no>>>())
{
std::string line;
// each line
while(std::getline(fin, line))
{
file.push_back(line);
int n = file.size() - 1; // the current line number
// each word
std::stringstream lineSteam(line);
std::string word;
while(lineSteam >> word)
{
std::shared_ptr<std::set<line_no>>&
sp_lines = wordMap[word];
// if null
if(!sp_lines)
{
sp_lines.reset(new std::set<line_no>);
}
sp_lines->insert(n);
}
}
}
/**
* @brief do a query opertion and return QueryResult object.
*/
QueryResult
TextQuery::query(const std::string &sought) const
{
// dynamicaly allocated set used for the word does not appear.
static std::shared_ptr<std::set<line_no>> noData(new std::set<line_no>);
// fetch the iterator to the matching element in the map<word, lines>.
//std::map<std::string, std::shared_ptr<std::set<index_Tp>>>::const_iterator
auto iter = wordMap.find(sought);
if(iter == wordMap.end())
return QueryResult(sought, noData, file);
else
return QueryResult(sought, iter->second, file);
}
================================================
FILE: cpp_source/ch15/text_query/textquery.h
================================================
/***************************************************************************
* @file textquery.h
* @author Alan.W
* @date 30 DEC 2013
* @remark The TextQuery class using StrBlob
***************************************************************************/
//
// Exercise 12.32:
// Rewrite the TextQuery and QueryResult classes to use a StrBlob instead of a
// vector<string> to hold the input file.
// Relevant post on Stack Overflow:
// http://stackoverflow.com/questions/20823225/what-will-happen-if-a-user-defined-constructor-omits-ininitialization-for-data-m
//
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include "StrBlob.h"
#include <map>
#include <set>
#include <string>
#include <memory>
#include <fstream>
class QueryResult;
/**
* @brief The TextQuery class using StrBlob
*/
class TextQuery
{
public:
typedef StrBlob::size_type line_no;
// constructor
TextQuery(std::ifstream& fin);
// query operation
QueryResult query(const std::string&) const;
private:
// data members
StrBlob file;
std::map<std::string,
std::shared_ptr<std::set<line_no>>> wordMap;
};
#endif // TEXTQUERY_H
================================================
FILE: cpp_source/ch15/text_query/wordquery.cpp
================================================
#include "wordquery.h"
================================================
FILE: cpp_source/ch15/text_query/wordquery.h
================================================
#ifndef WORDQUERY_H
#define WORDQUERY_H
#include "query_base.h"
/**
* @brief The WordQuery class
*The only class that actually performs a query on the given TextQuery object.
*No public members defined in this class. All operation are through the friend
*class Query.
*/
class WordQuery : public Query_base
{
// class Query uses the WordQuery constructor
friend class Query;
WordQuery(const std::string& s):
query_word(s)
{
std::cout << "WordQuery::WordQuery(" + s + ")\n";
}
// virtuals:
QueryResult eval(const TextQuery& t) const override
{ return t.query(query_word); }
std::string rep() const override
{
std::cout << "WodQuery::rep()\n";
return query_word;
}
std::string query_word;
};
#endif // WORDQUERY_H
================================================
FILE: cpp_source/ch16/ex_16_51.cpp
================================================
#include <iostream>
using namespace std;
template <typename T, typename ... Args>
void foo(const T &t, const Args& ... rest){
cout << "sizeof...(Args): " << sizeof...(Args) << endl;
cout << "sizeof...(rest): " << sizeof...(rest) << endl;
};
void test_param_packet(){
int i = 0;
double d = 3.14;
string s = "how now brown cow";
foo(i, s, 42, d);
foo(s, 42, "hi");
foo(d, s);
foo("hi");
}
int main(){
test_param_packet();
return 0;
}
================================================
FILE: cpp_source/ch17/ex_17_4.cpp
================================================
#include <iostream>
#include <tuple>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <numeric>
#include "ex_17_4_SalesData.h"
using namespace std;
// matches有三个成员:1.一个书店的索引。2.指向书店中元素的迭代器。3.指向书店中元素的迭代器。
typedef tuple<vector<Sales_data>::size_type,
vector<Sales_data>::const_iterator,
vector<Sales_data>::const_iterator>
matches;
// files保存每家书店的销售记录
// findBook返回一个vector,每家销售了给定书籍的书店在其中都有一项
vector<matches> findBook(const vector<vector<Sales_data>> &files,
const string &book)
{
vector<matches> ret; //初始化为空vector
// 对每家书店,查找给定书籍匹配的记录范围
for (auto it = files.cbegin; it != files.cend(); ++it)
{
// 查找具有相同ISBN的Sales_data范围,found是一个迭代器pair
auto found = equal_range(it->cbegin(), it->cend(), book, compareIsbn);
if (found.first != found.second) // 此书店销售了给定书籍
// 记住此书店的索引及匹配的范围
ret.push_back(make_tuple(it - files.cbegin(), found.first, found.second));
}
return ret; //如果未找到匹配记录,ret为空
}
void reportResults(istream &in, ostream &os,
const vector<vector<Sales_data> > &files){
string s; //要查找的书
while (in >> s){
auto trans = findBook(files, s);
if (trans.empty()){
cout << s << " not found in any stores" << endl;
continue; // 获得下一本要查找的书
}
for (const auto &store : trans) // 对每家销售了给定书籍的书店
// get<n>返回store中tuple的指定的成员
os << "store " << get<0>(store) << " sales: "
<< accumulate(get<1>(store), get<2>(store), Sales_data(s))
<< endl;
}
}
int main(){
return 0;
}
================================================
FILE: cpp_source/ch17/ex_17_4_SalesData.cpp
================================================
#include <iostream>
using std::istream; using std::ostream;
#include "ex_17_4_SalesData.h"
Sales_data::Sales_data(std::istream &is)
{
// read will read a transaction from is into this object
read(is, *this);
}
double
Sales_data::avg_price() const {
if (units_sold)
return revenue/units_sold;
else
return 0;
}
// add the value of the given Sales_data into this object
Sales_data&
Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold; // add the members of rhs into
revenue += rhs.revenue; // the members of ``this'' object
return *this; // return the object on which the function was called
}
// = Sales_data
Sales_data &Sales_data::operator =(const Sales_data &rhs)
{
this->bookNo = rhs.bookNo;
this->revenue = rhs.revenue;
this->units_sold = rhs.units_sold;
return *this;
}
// =string
Sales_data &Sales_data::operator =(const std::string &rhs)
{
*this= Sales_data(rhs);
return *this;
}
// +=
Sales_data &Sales_data::operator +=(const Sales_data &rhs)
{
this->revenue += rhs.revenue;
this->units_sold += rhs.units_sold;
return *this;
}
Sales_data
add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs; // copy data members from lhs into sum
sum.combine(rhs); // add data members from rhs into sum
return sum;
}
// transactions contain ISBN, number of copies sold, and sales price
istream&
read(istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
ostream&
print(ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
// added 10.Jan 2014
std::ostream &
operator <<(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
// added 12.Jan 2014
std::istream&
operator >>(std::istream &is, Sales_data &s)
{
double price;
// read input
is >> s.bookNo >> s.units_sold >> price;
// if successful, write into the object, give the object default state otherwise.
if(is)
s.revenue = s.units_sold * price;
else
s = Sales_data();
return is;
}
================================================
FILE: cpp_source/ch17/ex_17_4_SalesData.h
================================================
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
#include <iostream>
class Sales_data
{
// friends
friend Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs);
friend std::ostream&
operator << (std::ostream& os, const Sales_data& s);
friend std::istream&
operator >> (std::istream& is, Sales_data& s);
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
public:
// constructors
Sales_data() = default;
Sales_data(const std::string &s): bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(p*n) { }
Sales_data(const Sales_data &s ):
bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue)
{ }
Sales_data(Sales_data&& s):
bookNo(s.bookNo), units_sold(s.units_sold), revenue(s.revenue)
{ }
~Sales_data(){ }
Sales_data(std::istream &);
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
// assignments
Sales_data& operator =(const Sales_data& rhs);
Sales_data& operator =(const std::string& rhs);
Sales_data& operator +=(const Sales_data& rhs);
// conversion
explicit operator std::string () const { return bookNo; }
explicit operator double () const { return revenue; }
double avg_price() const;
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// overloaded operators added 10.Jan.2014 for ex14.2
inline Sales_data
operator+(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum += rhs;
return sum;
}
std::ostream&
operator << (std::ostream& os, const Sales_data& item);
std::istream&
operator >> (std::istream& is, Sales_data& s);
// nonmember Sales_data interface functions
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
// used in future chapters
inline
bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() < rhs.isbn();
}
#endif
================================================
FILE: excersize/ch01.md
================================================
# 第一章 开始
## 练习1.1
查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第2页的main程序。
解:
- ``g++ --std=c++11 ch1.cpp -o main``
- ``./main``
## 练习1.2
改写程序,让它返回-1。返回值-1通常被当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main返回的错误标识。
解:
- 在ubuntu下,使用g++,返回-1,``./main``没有发现任何异常。
- ``echo $?``,返回255。
## 练习1.3
编写程序,在标准输出上打印Hello, World。
解:
```cpp
#include <iostream>
int main()
{
std::cout << "Hello, World" << std::endl;
return 0;
}
```
## 练习1.4
我们的程序使用加法运算符`+`来将两个数相加。编写程序使用乘法运算符`*`,来打印两个数的积。
解:
```cpp
#include <iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The product of " << v1 << " and " << v2
<< " is " << v1 * v2 << std::endl;
}
```
## 练习1.5
我们将所有的输出操作放在一条很长的语句中,重写程序,将每个运算对象的打印操作放在一条独立的语句中。
解:
```cpp
#include <iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The product of ";
std::cout << v1;
std::cout << " and ";
std::cout << v2;
std::cout << " is ";
std::cout << v1 * v2;
std::cout << std::endl;
}
```
## 练习1.6
解释下面程序片段是否合法。
```cpp
std::cout << "The sum of " << v1;
<< " and " << v2;
<< " is " << v1 + v2 << std::endl;
```
如果程序是合法的,它的输出是什么?如果程序不合法,原因何在?应该如何修正?
解:
程序不合法,有多余的分号,修改如下:
```cpp
std::cout << "The sum of " << v1
<< " and " << v2
<< " is " << v1 + v2 << std::endl;
```
## 练习1.7
编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。
解:
```cpp
/* 正常注释 /* 嵌套注释 */ 正常注释*/
```
错误信息:
```
/* 正常注释 /* 嵌套注释 */ 正常注释*/
^
ch1.cpp:97:37: error: stray ‘\255’ in program
ch1.cpp:97:37: error: stray ‘\243’ in program
ch1.cpp:97:37: error: stray ‘\345’ in program
ch1.cpp:97:37: error: stray ‘\270’ in program
ch1.cpp:97:37: error: stray ‘\270’ in program
ch1.cpp:97:37: error: stray ‘\346’ in program
ch1.cpp:97:37: error: stray ‘\263’ in program
ch1.cpp:97:37: error: stray ‘\250’ in program
ch1.cpp:97:37: error: stray ‘\351’ in program
ch1.cpp:97:37: error: stray ‘\207’ in program
ch1.cpp:97:37: error: stray ‘\212’ in program
ch1.cpp: In function ‘int main()’:
ch1.cpp:97:50: error: expected primary-expression before ‘/’ token
/* 正常注释 /* 嵌套注释 */ 正常注释*/
^
ch1.cpp:98:5: error: expected primary-expression before ‘return’
return 0;
^
```
## 练习1.8
指出下列哪些输出语句是合法的(如果有的话):
```cpp
std::cout << "/*";
std::cout << "*/";
std::cout << /* "*/" */;
std::cout << /* "*/" /* "/*" */;
```
预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案(编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。
解:
只有第三句编译出错,改成如下即可:
```cpp
std::cout << /* "*/" */";
```
第四句等价于输出 `" /* "`。
## 练习1.9
编写程序,使用`while`循环将50到100整数相加。
解:
```cpp
#include <iostream>
int main()
{
int sum = 0, val = 50;
while (val <= 100){
sum += val;
val += 1;
}
std::cout << "Sum of 50 to 100 inclusive is "
<< sum << std::endl;
}
```
## 练习1.10
除了`++`运算符将运算对象的值增加1之外,还有一个递减运算符`--`实现将值减少1.编写程序与,使用递减运算符在循环中按递减顺序打印出10到0之间的整数。
解:
```cpp
#include <iostream>
int main()
{
int val = 10;
while (val >= 0){
std::cout << val << " ";
val -= 1;
}
std::cout << std::endl;
}
```
## 练习1.11
编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数。
解:
```cpp
#include <iostream>
int main()
{
int start = 0, end = 0;
std::cout << "Please input two num: ";
std::cin >> start >> end;
if (start <= end) {
while (start <= end){
std::cout << start << " ";
++start;
}
std::cout << std::endl;
}
else{
std::cout << "start should be smaller than end !!!";
}
}
```
## 练习1.12
下面的for循环完成了什么功能?sum的终值是多少?
```cpp
int sum = 0;
for (int i = -100; i <= 100; ++i)
sum += i;
```
解:
从-100加到100,sum的终值是0。
## 练习1.13
使用for循环重做1.4.1节中的所有练习(练习1.9到1.11)。
解:
### 练习1.9
```cpp
#include <iostream>
int main()
{
int sum = 0;
for (int val = 50; val <= 100; ++val){
sum += val;
}
std::cout << "Sum of 50 to 100 inclusive is "
<< sum << std::endl;
}
```
### 练习1.10
```cpp
#include <iostream>
int main()
{
for (int val = 10; val >=0; --val){
std::cout << val << " ";
}
std::cout << std::endl;
}
```
### 练习1.11
```cpp
#include <iostream>
int main()
{
int start = 0, end = 0;
std::cout << "Please input two num: ";
std::cin >> start >> end;
if (start <= end) {
for (; start <= end; ++start){
std::cout << start << " ";
}
std::cout << std::endl;
}
else{
std::cout << "start should be smaller than end !!!";
}
}
```
## 练习1.14
对比for循环和while循环,两种形式的优缺点各是什么?
解:
```
The main difference between the `for`'s and the `while`'s is a matter of pragmatics:
we usually use `for` when there is a known number of iterations,
and use `while` constructs when the number of iterations in not known in advance.
The `while` vs `do ... while` issue is also of pragmatics,
the second executes the instructions once at start,
and afterwards it behaves just like the simple `while`.
```
## 练习1.15
编写程序,包含第14页“再探编译”中讨论的常见错误。熟悉编译器生成的错误信息。
解:
编译器可以检查出的错误有:
- 语法错误
- 类型错误
- 声明错误
## 练习1.16
编写程序,从cin读取一组数,输出其和。
解:
```cpp
#include <iostream>
int main()
{
int sum = 0;
for (int value = 0; std::cin >> value; )
sum += value;
std::cout << sum << std::endl;
return 0;
}
```
## 练习1.17
如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?
## 练习1.18
编译并运行本节的程序,给它输入全都相等的值。再次运行程序,输入没有重复的值。
解:
全部重复:
```
1 1 1 1 1
1 occurs 5 times
```
没有重复:
```
1 2 3 4 5
1 occurs 1 times
2 occurs 1 times
3 occurs 1 times
4 occurs 1 times
5 occurs 1 times
```
## 练习1.19
修改你为1.4.1节练习1.11(第11页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况。
解:
```cpp
#include <iostream>
int main()
{
int start = 0, end = 0;
std::cout << "Please input two num: ";
std::cin >> start >> end;
if (start <= end) {
while (start <= end){
std::cout << start << " ";
++start;
}
std::cout << std::endl;
}
else{
std::cout << "start should be smaller than end !!!";
}
}
```
## 练习1.20
在网站http://www.informit.com/title/032174113 上,第1章的代码目录包含了头文件 Sales_item.h。将它拷贝到你自己的工作目录中。用它编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上。
解:
```cpp
#include <iostream>
#include "Sales_item.h"
int main()
{
for (Sales_item item; std::cin >> item; std::cout << item << std::endl);
return 0;
}
```
命令:
```
./main < data/add_item
```
输出:
```
0-201-78345-X 3 60 20
0-201-78345-X 2 50 25
```
## 练习1.21
编写程序,读取两个 ISBN 相同的 Sales_item 对象,输出他们的和。
解:
```cpp
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item item_1;
Sales_item item_2;
std::cin >> item_1;
std::cout << item_1 << std::endl;
std::cin >> item_2;
std::cout << item_2 << std::endl;
std::cout << "sum of sale items: " << item_1 + item_2 << std::endl;
return 0;
}
```
命令:
```
./main < data/add_item
```
输出:
```
0-201-78345-X 3 60 20
0-201-78345-X 2 50 25
sum of sale items: 0-201-78345-X 5 110 22
```
## 练习1.22
编写程序,读取多个具有相同 ISBN 的销售记录,输出所有记录的和。
解:
```cpp
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item sum_item;
std::cin >> sum_item;
std::cout << sum_item << std::endl;
for (Sales_item item; std::cin >> item; std::cout << item << std::endl){
sum_item += item;
}
std::cout << "sum of sale items: " << sum_item << std::endl;
return 0;
}
```
命令:
```
./main < data/add_item
```
输出:
```
0-201-78345-X 3 60 20
0-201-78345-X 2 50 25
sum of sale items: 0-201-78345-X 5 110 22
```
## 练习1.23
编写程序,读取多条销售记录,并统计每个 ISBN(每本书)有几条销售记录。
## 练习1.24
输入表示多个 ISBN 的多条销售记录来测试上一个程序,每个 ISBN 的记录应该聚在一起。
解:
```cpp
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item total;
if (std::cin >> total){
Sales_item trans;
while (std::cin >> trans){
if (total.isbn() == trans.isbn()) {
total += trans;
}
else {
std::cout << total << std::endl;
total = trans;
}
}
std::cout << total << std::endl;
}
else {
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
```
命令:
```
./main < data/book_sales
```
输出:
```
0-201-70353-X 4 99.96 24.99
0-201-82470-1 4 181.56 45.39
0-201-88954-4 16 198 12.375
0-399-82477-1 5 226.95 45.39
0-201-78345-X 5 110 22
```
## 练习1.25
借助网站上的`Sales_item.h`头文件,编译并运行本节给出的书店程序。
================================================
FILE: excersize/ch02.md
================================================
# 第二章 变量和基本类型
## 练习2.1
类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?
解:
C++ 规定 short 和 int 至少16位,long 至少32位,long long 至少64位。 带符号类型能够表示正数、负数和 0 ,而无符号类型只能够表示 0 和正整数。浮点数的取值范围和精度不同,计算效率也有差异。
## 练习2.2
计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。
解:
使用`double`。需要进行浮点计算。
## 练习2.3
读程序写结果。
```cpp
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;
std::cout << u - u2 << std::endl;
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;
std::cout << i - i2 << std::endl;
std::cout << i - u << std::endl;
std::cout << u - i << std::endl;
```
解:
输出:
```
32
4294967264
32
-32
0
0
```
## 练习2.4
编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。
## 练习2.5
指出下述字面值的数据类型并说明每一组内几种字面值的区别:
```
(a) 'a', L'a', "a", L"a"
(b) 10, 10u, 10L, 10uL, 012, 0xC
(c) 3.14, 3.14f, 3.14L
(d) 10, 10u, 10., 10e-2
```
解:
- (a): 字符字面值,宽字符字面值,字符串字面值,宽字符串字面值。
- (b): 十进制整型,十进制无符号整型,十进制长整型,十进制无符号长整型, 八进制整型,十六进制整型。
- (c): double, float, long double
- (d): 十进制整型,十进制无符号整型,double, double
## 练习2.6
下面两组定义是否有区别,如果有,请叙述之:
```cpp
int month = 9, day = 7;
int month = 09, day = 07;
```
解:
第一行定义的是十进制的整型,第二行定义的是八进制的整型。但是month变量有误,八进制不能直接写9。
## 练习2.7
下述字面值表示何种含义?它们各自的数据类型是什么?
```cpp
(a) "Who goes with F\145rgus?\012"
(b) 3.14e1L
(c) 1024f
(d) 3.14L
```
解:
- (a) Who goes with Fergus?(换行),string 类型
- (b) long double
- (c) 无效,因为后缀`f`只能用于浮点字面量,而1024是整型。
- (d) long double
## 练习2.8
请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。
解:
```cpp
#include <iostream>
int main()
{
std::cout << 2 << "\115\012";
std::cout << 2 << "\t\115\012";
return 0;
}
```
## 练习2.9
解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。
- (a) std::cin >> int input_value;
- (b) int i = { 3.14 };
- (c) double salary = wage = 9999.99;
- (d) int i = 3.14;
解:
(a): 应该先定义再使用。
```cpp
int input_value = 0;
std::cin >> input_value;
```
(b): 用列表初始化内置类型的变量时,如果存在丢失信息的风险,则编译器将报错。
```cpp
double i = { 3.14 };
```
(c): 在这里`wage`是未定义的,应该在此之前将其定义。
```cpp
double wage;
double salary = wage = 9999.99;
```
(d): 不报错,但是小数部分会被截断。
```cpp
double i = 3.14;
```
## 练习2.10
下列变量的初值分别是什么?
```cpp
std::string global_str;
int global_int;
int main()
{
int local_int;
std::string local_str;
}
```
解:
`global_str`和`global_int`是全局变量,所以初值分别为空字符串和0。
`local_int`是局部变量并且没有初始化,它的初值是未定义的。
`local_str` 是 `string` 类的对象,它的值由类确定,为空字符串。
## 练习2.11
指出下面的语句是声明还是定义:
- (a) extern int ix = 1024;
- (b) int iy;
- (c) extern int iz;
解:
(a): 定义
(b): 定义
(c): 声明
## 练习2.12
请指出下面的名字中哪些是非法的?
- (a) int double = 3.14;
- (b) int _;
- (c) int catch-22;
- (d) int 1_or_2 = 1;
- (e) double Double = 3.14;
解:
(a), (c), (d) 非法。
## 练习2.13
下面程序中`j`的值是多少?
```cpp
int i = 42;
int main()
{
int i = 100;
int j = i;
}
```
解:
`j`的值是100,局部变量`i`覆盖了全局变量`i`。
## 练习2.14
下面的程序合法吗?如果合法,它将输出什么?
```cpp
int i = 100, sum = 0;
for (int i = 0; i != 10; ++i)
sum += i;
std::cout << i << " " << sum << std::endl;
```
解:
合法。输出是 100 45 。
## 练习2.15
下面的哪个定义是不合法的?为什么?
- (a) int ival = 1.01;
- (b) int &rval1 = 1.01;
- (c) int &rval2 = ival;
- (d) int &rval3;
解:
(b)和(d)不合法,(b)引用必须绑定在对象上,(d)引用必须初始化。
## 练习2.16
考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作?
```cpp
int i = 0, &r1 = i;
double d = 0, &r2 = d;
```
- (a) r2 = 3.14159;
- (b) r2 = r1;
- (c) i = r2;
- (d) r1 = d;
解:
- (a): 合法。给 d 赋值为 3.14159。
- (b): 合法。会执行自动转换(int->double)。
- (c): 合法。会发生小数截取。
- (d): 合法。会发生小数截取。
## 练习2.17
执行下面的代码段将输出什么结果?
```cpp
int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl;
```
解:
输出:10 10
## 练习2.18
编写代码分别改变指针的值以及指针所指对象的值。
解:
```cpp
int a = 0, b = 1;
int *p1 = &a, *p2 = p1;
// change the value of a pointer.
p1 = &b;
// change the value to which the pointer points
*p2 = b;
```
## 练习2.19
说明指针和引用的主要区别
解:
引用是另一个对象的别名,而指针本身就是一个对象。
引用必须初始化,并且一旦定义了引用就无法再绑定到其他对象。而指针无须在定义时赋初值,也可以重新赋值让其指向其他对象。
## 练习2.20
请叙述下面这段代码的作用。
```cpp
int i = 42;
int *p1 = &i;
*p1 = *p1 * *p1;
```
解:
让指针 pi 指向 i,然后将 i 的值重新赋值为 42 * 42 (1764)。
## 练习2.21
请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
`int i = 0;`
- (a) double* dp = &i;
- (b) int *ip = i;
- (c) int *p = &i;
解:
- (a): 非法。不能将一个指向 `double` 的指针指向 `int` 。
- (b): 非法。不能将 `int` 变量赋给指针。
- (c): 合法。
## 练习2.22
假设 p 是一个 int 型指针,请说明下述代码的含义。
```cpp
if (p) // ...
if (*p) // ...
```
解:
第一句判断 p 是不是一个空指针,
第二句判断 p 所指向的对象的值是不是为0
## 练习2.23
给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。
解:
能,可以使用try catch的异常处理来分辨指针p是否指向一个合法的对象,但通过普通控制结构无法实现。
## 练习2.24
在下面这段代码中为什么 p 合法而 lp 非法?
```cpp
int i = 42;
void *p = &i;
long *lp = &i;
```
解:
`void *`是从C语言那里继承过来的,可以指向任何类型的对象。
而其他指针类型必须要与所指对象严格匹配。
## 练习2.25
说明下列变量的类型和值。
```cpp
(a) int* ip, i, &r = i;
(b) int i, *ip = 0;
(c) int* ip, ip2;
```
解:
- (a): ip 是一个指向 int 的指针, i 是一个 int, r 是 i 的引用。
- (b): i 是 int , ip 是一个空指针。
- (c): ip 是一个指向 int 的指针, ip2 是一个 int。
## 练习2.26
下面哪些语句是合法的?如果不合法,请说明为什么?
解:
```cpp
const int buf; // 不合法, const 对象必须初始化
int cnt = 0; // 合法
const int sz = cnt; // 合法
++cnt; ++sz; // 不合法, const 对象不能被改变
```
## 练习2.27
下面的哪些初始化是合法的?请说明原因。
解:
```cpp
int i = -1, &r = 0; // 不合法, r 必须引用一个对象
int *const p2 = &i2; // 合法,常量指针
const int i = -1, &r = 0; // 合法
const int *const p3 = &i2; // 合法
const int *p1 = &i2; // 合法
const int &const r2; // 不合法, r2 是引用, 引用自带顶层 const, 第二个const写法多余但合法, 但引用需要初始化.
const int i2 = i, &r = i; // 合法
```
## 练习2.28
说明下面的这些定义是什么意思,挑出其中不合法的。
解:
```cpp
int i, *const cp; // 不合法, const 指针必须初始化
int *p1, *const p2; // 不合法, const 指针必须初始化
const int ic, &r = ic; // 不合法, const int 必须初始化
const int *const p3; // 不合法, const 指针必须初始化
const int *p; // 合法. 一个指针,指向 const int
```
## 练习2.29
假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。
解:
```cpp
i = ic; // 合法, 常量赋值给普通变量
p1 = p3; // 不合法, p3 是const指针不能赋值给普通指针
p1 = ⁣ // 不合法, 普通指针不能指向常量
p3 = ⁣ // 不合法, p3 是常量指针且指向常量, 故p3 不能被修改, 本句赋值语句正在修改
p2 = p1; // 不合法, p2是常量指针, 有顶层const, 不能被修改
ic = *p3; // 不合法, 对 p3 取值后是一个 int 然后赋值给 ic, 但ic是常量不能被修改
```
## 练习2.30
对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?
```cpp
const int v2 = 0; int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
```
解:
v2 是顶层const,p2 是底层const,p3 既是顶层const又是底层const,r2 是底层const。
## 练习2.31
假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。
解:
```cpp
r1 = v2; // 合法, 顶层const在拷贝时不受影响
p1 = p2; // 不合法, p2 是底层const,如果要拷贝必须要求 p1 也是底层const
p2 = p1; // 合法, int* 可以转换成const int*
p1 = p3; // 不合法, p3 是一个底层const,p1 不是
p2 = p3; // 合法, p2 和 p3 都是底层const,拷贝时忽略掉顶层const
```
## 练习2.32
下面的代码是否合法?如果非法,请设法将其修改正确。
```cpp
int null = 0, *p = null;
```
解:
非法,即使int的值恰好是0,也不能直接给指针赋值int变量。应改为
```cpp
int null = 0, *p = &null;
```
而且应该注意到,null都是小写,并不是关键字或者预处理变量。
## 练习2.33
利用本节定义的变量,判断下列语句的运行结果。
解:
```cpp
a=42; // a 是 int
b=42; // b 是一个 int,(ci的顶层const在拷贝时被忽略掉了)
c=42; // c 也是一个int
d=42; // d 是一个 int *,所以语句非法
e=42; // e 是一个 const int *, 所以语句非法
g=42; // g 是一个 const int 的引用,引用都是底层const,所以不能被赋值
```
## 练习2.34
基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?如果不对,请反复研读本节的示例直到你明白错在何处为止。
## 练习2.35
判断下列定义推断出的类型是什么,然后编写程序进行验证。
```cpp
const int i = 42;
auto j = i; const auto &k = i; auto *p = &i;
const auto j2 = i, &k2 = i;
```
解:
j 是 int,k 是 const int的引用,p 是const int *,j2 是const int,k2 是 const int 的引用。
## 练习2.36
关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。
```cpp
int a = 3, b = 4;
decltype(a) c = a;
decltype((b)) d = a;
++c;
++d;
```
解:
c 是 int 类型,值为 4。d 是 int & 类型,绑定到 a,a 的值为 4 。
## 练习2.37
赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。
```cpp
int a = 3, b = 4;
decltype(a) c = a;
decltype(a = b) d = a;
```
解:
c 是 int 类型,值为 3。d 是 int& 类型,绑定到 a。
## 练习2.38
说明由decltype 指定类型和由auto指定类型有何区别。请举一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。
解:
decltype 处理顶层const和引用的方式与 auto不同,decltype会将顶层const和引用保留起来。
```cpp
int i = 0, &r = i;
//相同
auto a = i;
decltype(i) b = i;
//不同 d 是一个 int&
auto c = r;
decltype(r) d = r;
```
## 练习2.39
编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。
```cpp
struct Foo { /* 此处为空 */ } // 注意:没有分号
int main()
{
return 0;
}。
```
解:
提示应输入分号。
## 练习2.40
根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。
```cpp
struct Sale_data
{
std::string bookNo;
std::string bookName;
unsigned units_sold = 0;
double revenue = 0.0;
double price = 0.0;
//...
}
```
## 练习2.41
使用你自己的Sale_data类重写1.5.1节(第20页)、1.5.2节(第21页)和1.6节(第22页)的练习。眼下先把Sales_data类的定义和main函数放在一个文件里。
```cpp
// 1.5.1
#include <iostream>
#include <string>
struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
Sale_data book;
double price;
std::cin >> book.bookNo >> book.units_sold >> price;
book.revenue = book.units_sold * price;
std::cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;
return 0;
}
```
```cpp
// 1.5.2
#include <iostream>
#include <string>
struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
Sale_data book1, book2;
double price1, price2;
std::cin >> book1.bookNo >> book1.units_sold >> price1;
std::cin >> book2.bookNo >> book2.units_sold >> price2;
book1.revenue = book1.units_sold * price1;
book2.revenue = book2.units_sold * price2;
if (book1.bookNo == book2.bookNo)
{
unsigned totalCnt = book1.units_sold + book2.units_sold;
double totalRevenue = book1.revenue + book2.revenue;
std::cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
if (totalCnt != 0)
std::cout << totalRevenue / totalCnt << std::endl;
else
std::cout << "(no sales)" << std::endl;
return 0;
}
else
{
std::cerr << "Data must refer to same ISBN" << std::endl;
return -1; // indicate failure
}
}
```
```cpp
// 1.6
#include <iostream>
#include <string>
struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
Sale_data total;
double totalPrice;
if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
{
total.revenue = total.units_sold * totalPrice;
Sale_data trans;
double transPrice;
while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
{
trans.revenue = trans.units_sold * transPrice;
if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
if (total.units_sold != 0)
std::cout << total.revenue / total.units_sold << std::endl;
else
std::cout << "(no sales)" << std::endl;
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;
}
}
std::cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
if (total.units_sold != 0)
std::cout << total.revenue / total.units_sold << std::endl;
else
std::cout << "(no sales)" << std::endl;
return 0;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1; // indicate failure
}
}
```
## 练习2.42
根据你自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页)的练习。
================================================
FILE: excersize/ch03.md
================================================
# 第三章 字符串、向量和数组
## 练习3.1
使用恰当的using 声明重做 1.4.1节和2.6.2节的练习。
解:
1.4.1
```cpp
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
int sum = 0;
for (int val = 1; val <= 10; ++val) sum += val;
cout << "Sum of 1 to 10 inclusive is " << sum << endl;
return 0;
}
```
2.6.2 类似
## 练习3.2
编写一段程序从标准输入中一次读入一行,然后修改该程序使其一次读入一个词。
解:
一次读入一行:
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::getline;
int main()
{
string s;
while (getline(cin,s))
{
cout << s << endl;
}
return 0;
}
```
一次读入一个词
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::getline;
int main()
{
string s;
while (cin >> s)
{
cout << s << endl;
}
return 0;
}
```
## 练习3.3
请说明string类的输入运算符和getline函数分别是如何处理空白字符的。
解:
- 类似`is >> s`的读取:string对象会忽略开头的空白并从第一个真正的字符开始,直到遇见下一**空白**为止。
- 类似`getline(is, s)`的读取:string对象会从输入流中读取字符,直到遇见**换行符**为止。
## 练习3.4
编写一段程序读取两个字符串,比较其是否相等并输出结果。如果不相等,输出比较大的那个字符串。改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串。
解:
比较大的
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string str1, str2;
while (cin >> str1 >> str2)
{
if (str1 == str2)
cout << "The two strings are equal." << endl;
else
cout << "The larger string is " << ((str1 > str2) ? str1 : str2);
}
return 0;
}
```
长度大的
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string str1, str2;
while (cin >> str1 >> str2)
{
if (str1.size() == str2.size())
cout << "The two strings have the same length." << endl;
else
cout << "The longer string is " << ((str1.size() > str2.size()) ? str1 : str2) << endl;
}
return 0;
}
```
## 练习3.5
编写一段程序从标准输入中读入多个字符串并将他们连接起来,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分割开来。
解:
未隔开的:
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string result, s;
while (cin >> s)
{
result += s;
}
cout << result << endl;
return 0;
}
```
隔开的:
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string result, s;
while (cin >> s)
{
result += s + " ";
}
cout << result << endl;
return 0;
}
```
## 练习3.6
编写一段程序,使用范围for语句将字符串内所有字符用X代替。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string s = "this is a string";
for (auto &x : s)
{
x = 'X';
}
cout << s << endl;
return 0;
}
```
## 练习3.7
就上一题完成的程序而言,如果将循环控制的变量设置为char将发生什么?先估计一下结果,然后实际编程进行验证。
解:
如果设置为char,那么原来的字符串不会发生改变。
## 练习3.8
分别用while循环和传统for循环重写第一题的程序,你觉得哪种形式更好呢?为什么?
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string s = "this is a string";
decltype(s.size()) i = 0;
while (i != s.size())
{
s[i] = 'X';
++i;
}
cout << s << endl;
for (i = 0; i != s.size(); ++i)
{
s[i] = 'Y';
}
cout << s << endl;
return 0;
}
```
范围for语句更好,不直接操作索引,更简洁。
## 练习3.9
下面的程序有何作用?它合法吗?如果不合法?为什么?
```cpp
string s;
cout << s[0] << endl;
```
解:
不合法。使用下标访问空字符串是非法的行为。
## 练习3.10
编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩余的部分。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string s = "this, is. a :string!";
string result;
for (auto x : s)
{
if (!ispunct(x))
{
result += x;
}
}
cout << result << endl;
return 0;
}
```
## 练习3.11
下面的范围for语句合法吗?如果合法,c的类型是什么?
```cpp
const string s = "Keep out!";
for(auto &c : s){ /* ... */ }
```
解:
要根据for循环中的代码来看是否合法,c是string 对象中字符的引用,s是常量。因此如果for循环中的代码重新给c赋值就会非法,如果不改变c的值,那么合法。
## 练习3.12
下列vector对象的定义有不正确的吗?如果有,请指出来。对于正确的,描述其执行结果;对于不正确的,说明其错误的原因。
```cpp
vector<vector<int>> ivec; // 在C++11当中合法
vector<string> svec = ivec; // 不合法,类型不一样
vector<string> svec(10, "null"); // 合法
```
## 练习3.13
下列的vector对象各包含多少个元素?这些元素的值分别是多少?
```cpp
vector<int> v1; // size:0, no values.
vector<int> v2(10); // size:10, value:0
vector<int> v3(10, 42); // size:10, value:42
vector<int> v4{ 10 }; // size:1, value:10
vector<int> v5{ 10, 42 }; // size:2, value:10, 42
vector<string> v6{ 10 }; // size:10, value:""
vector<string> v7{ 10, "hi" }; // size:10, value:"hi"
```
## 练习3.14
编写一段程序,用cin读入一组整数并把它们存入一个vector对象。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> v;
int i;
while (cin >> i)
{
v.push_back(i);
}
return 0;
}
```
## 练习3.15
改写上题程序,不过这次读入的是字符串。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;
int main()
{
vector<string> v;
string i;
while (cin >> i)
{
v.push_back(i);
}
return 0;
}
```
## 练习3.16
编写一段程序,把练习3.13中vector对象的容量和具体内容输出出来
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;
int main()
{
vector<int> v1; // size:0, no values.
vector<int> v2(10); // size:10, value:0
vector<int> v3(10, 42); // size:10, value:42
vector<int> v4{ 10 }; // size:1, value:10
vector<int> v5{ 10, 42 }; // size:2, value:10, 42
vector<string> v6{ 10 }; // size:10, value:""
vector<string> v7{ 10, "hi" }; // size:10, value:"hi"
cout << "v1 size :" << v1.size() << endl;
cout << "v2 size :" << v2.size() << endl;
cout << "v3 size :" << v3.size() << endl;
cout << "v4 size :" << v4.size() << endl;
cout << "v5 size :" << v5.size() << endl;
cout << "v6 size :" << v6.size() << endl;
cout << "v7 size :" << v7.size() << endl;
cout << "v1 content: ";
for (auto i : v1)
{
cout << i << " , ";
}
cout << endl;
cout << "v2 content: ";
for (auto i : v2)
{
cout << i << " , ";
}
cout << endl;
cout << "v3 content: ";
for (auto i : v3)
{
cout << i << " , ";
}
cout << endl;
cout << "v4 content: ";
for (auto i : v4)
{
cout << i << " , ";
}
cout << endl;
cout << "v5 content: ";
for (auto i : v5)
{
cout << i << " , ";
}
cout << endl;
cout << "v6 content: ";
for (auto i : v6)
{
cout << i << " , ";
}
cout << endl;
cout << "v7 content: ";
for (auto i : v7)
{
cout << i << " , ";
}
cout << endl;
return 0;
}
```
## 练习3.17
从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改为大写形式。输出改变后的结果,每个词占一行。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;
int main()
{
vector<string> v;
string s;
while (cin >> s)
{
v.push_back(s);
}
for (auto &str : v)
{
for (auto &c : str)
{
c = toupper(c);
}
}
for (auto i : v)
{
cout << i << endl;
}
return 0;
}
```
## 练习3.18
下面的程序合法吗?如果不合法,你准备如何修改?
```cpp
vector<int> ivec;
ivec[0] = 42;
```
解:
不合法。应改为:
```cpp
ivec.push_back(42);
```
## 练习3.19
如果想定义一个含有10个元素的vector对象,所有元素的值都是42,请例举三种不同的实现方法,哪种方式更好呢?
如下三种:
```cpp
vector<int> ivec1(10, 42);
vector<int> ivec2{ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
vector<int> ivec3;
for (int i = 0; i < 10; ++i)
ivec3.push_back(42);
```
第一种方式最好。
## 练习3.20
读入一组整数并把他们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求先输出第一个和最后一个元素的和,接着输出第二个和倒数第二个元素的和,以此类推。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;
int main()
{
vector<int> ivec;
int i;
while (cin >> i)
{
ivec.push_back(i);
}
for (int i = 0; i < ivec.size() - 1; ++i)
{
cout << ivec[i] + ivec[i + 1] << endl;
}
//---------------------------------
cout << "---------------------------------" << endl;
int m = 0;
int n = ivec.size() - 1;
while (m < n)
{
cout << ivec[m] + ivec[n] << endl;
++m;
--n;
}
return 0;
}
```
## 练习3.21
请使用迭代器重做3.3.3节的第一个练习。
解:
```cpp
#include <vector>
#include <iterator>
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
using std::endl;
void check_and_print(const vector<int>& vec)
{
cout << "size: " << vec.size() << " content: [";
for (auto it = vec.begin(); it != vec.end(); ++it)
cout << *it << (it != vec.end() - 1 ? "," : "");
cout << "]\n" << endl;
}
void check_and_print(const vector<string>& vec)
{
cout << "size: " << vec.size() << " content: [";
for (auto it = vec.begin(); it != vec.end(); ++it)
cout << *it << (it != vec.end() - 1 ? "," : "");
cout << "]\n" << endl;
}
int main()
{
vector<int> v1;
vector<int> v2(10);
vector<int> v3(10, 42);
vector<int> v4{ 10 };
vector<int> v5{ 10, 42 };
vector<string> v6{ 10 };
vector<string> v7{ 10, "hi" };
check_and_print(v1);
check_and_print(v2);
check_and_print(v3);
check_and_print(v4);
check_and_print(v5);
check_and_print(v6);
check_and_print(v7);
return 0;
}
```
## 练习3.22
修改之前那个输出text第一段的程序,首先把text的第一段全部改成大写形式,然后输出它。
解: 略
## 练习3.23
编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。
解:
```cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10, 1);
for (auto it=v.begin(); it!=v.end(); it++){
*it *= 2;
}
for (auto one : v){
cout << one <<endl;
}
return 0;
}
```
## 练习3.24
请使用迭代器重做3.3.3节的最后一个练习。
解:
```cpp
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;
int main()
{
vector<int> ivec;
int i;
while (cin >> i)
{
ivec.push_back(i);
}
for (auto it = ivec.begin(); it != ivec.end() - 1; ++it)
{
cout << *it + *(it + 1) << endl;
}
//---------------------------------
cout << "---------------------------------" << endl;
auto it1 = ivec.begin();
auto it2 = ivec.end() - 1;
while (it1 < it2)
{
cout << *it1 + *it2 << endl;
++it1;
--it2;
}
return 0;
}
```
## 练习3.25
3.3.3节划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序实现完全相同的功能。
解:
```cpp
#include <vector>
#include <iostream>
using std::vector; using std::cout; using std::cin; using std::endl;
int main()
{
vector<unsigned> scores(11, 0);
unsigned grade;
while (cin >> grade)
{
if (grade <= 100)
++*(scores.begin() + grade / 10);
}
for (auto s : scores)
cout << s << " ";
cout << endl;
return 0;
}
```
## 练习3.26
在100页的二分搜索程序中,为什么用的是 `mid = beg + (end - beg) / 2`, 而非 `mid = (beg + end) / 2 ;` ?
解:
因为两个迭代器相互之间支持的运算只有 `-` ,而没有 `+` 。
但是迭代器和迭代器差值(整数值)之间支持 `+`。
## 练习3.27
假设`txt_size`是一个无参函数,它的返回值是`int`。请回答下列哪个定义是非法的,为什么?
```cpp
unsigned buf_size = 1024;
(a) int ia[buf_size];
(b) int ia[4 * 7 - 14];
(c) int ia[txt_size()];
(d) char st[11] = "fundamental";
```
解:
- (a) 非法。维度必须是一个常量表达式。
- (b) 合法。
- (c) 非法。txt_size() 的值必须要到运行时才能得到。
- (d) 非法。数组的大小应该是12。
## 练习3.28
下列数组中元素的值是什么?
```cpp
string sa[10];
int ia[10];
int main() {
string sa2[10];
int ia2[10];
}
```
解:
数组的元素会被默认初始化。
`sa`的元素值全部为空字符串,`ia` 的元素值全部为0。
`sa2`的元素值全部为空字符串,`ia2`的元素值全部未定义。
## 练习3.29
相比于vector 来说,数组有哪些缺点,请例举一些。
解:
- 数组的大小是确定的。
- 不能随意增加元素。
- 不允许拷贝和赋值。
## 练习3.30
指出下面代码中的索引错误。
```cpp
constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix)
ia[ix] = ix;
```
解:
当`ix`增长到 10 的时候,`ia[ix]`的下标越界。
## 练习3.31
编写一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值。
```cpp
#include <iostream>
using std::cout; using std::endl;
int main()
{
int arr[10];
for (auto i = 0; i < 10; ++i) arr[i] = i;
for (auto i : arr) cout << i << " ";
cout << endl;
return 0;
}
```
## 练习3.32
将上一题刚刚创建的数组拷贝给另一数组。利用vector重写程序,实现类似的功能。
```cpp
#include <iostream>
#include <vector>
using std::cout; using std::endl; using std::vector;
int main()
{
// array
int arr[10];
for (int i = 0; i < 10; ++i) arr[i] = i;
int arr2[10];
for (int i = 0; i < 10; ++i) arr2[i] = arr[i];
// vector
vector<int> v(10);
for (int i = 0; i != 10; ++i) v[i] = arr[i];
vector<int> v2(v);
for (auto i : v2) cout << i << " ";
cout << endl;
return 0;
}
```
## 练习3.33
对于104页的程序来说,如果不初始化scores将会发生什么?
解:
数组中所有元素的值将会未定义。
## 练习3.34
假定`p1` 和 `p2` 都指向同一个数组中的元素,则下面程序的功能是什么?什么情况下该程序是非法的?
```cpp
p1 += p2 - p1;
```
解:
将 `p1` 移动到 `p2` 的位置。任何情况下都合法。
## 练习3.35
编写一段程序,利用指针将数组中的元素置为0。
解:
```cpp
#include <iostream>
using std::cout; using std::endl;
int main()
{
const int size = 10;
int arr[size];
for (auto ptr = arr; ptr != arr + size; ++ptr) *ptr = 0;
for (auto i : arr) cout << i << " ";
cout << endl;
return 0;
}
```
## 练习3.36
编写一段程序,比较两个数组是否相等。再写一段程序,比较两个vector对象是否相等。
解:
```cpp
#include <iostream>
#include <vector>
#include <iterator>
using std::begin; using std::end; using std::cout; using std::endl; using std::vector;
// pb point to begin of the array, pe point to end of the array.
bool compare(int* const pb1, int* const pe1, int* const pb2, int* const pe2)
{
if ((pe1 - pb1) != (pe2 - pb2)) // have different size.
return false;
else
{
for (int* i = pb1, *j = pb2; (i != pe1) && (j != pe2); ++i, ++j)
if (*i != *j) return false;
}
return true;
}
int main()
{
int arr1[3] = { 0, 1, 2 };
int arr2[3] = { 0, 2, 4 };
if (compare(begin(arr1), end(arr1), begin(arr2), end(arr2)))
cout << "The two arrays are equal." << endl;
else
cout << "The two arrays are not equal." << endl;
cout << "==========" << endl;
vector<int> vec1 = { 0, 1, 2 };
vector<int> vec2 = { 0, 1, 2 };
if (vec1 == vec2)
cout << "The two vectors are equal." << endl;
else
cout << "The two vectors are not equal." << endl;
return 0;
}
```
## 练习3.37
下面的程序是何含义,程序的输出结果是什么?
```cpp
const char ca[] = { 'h', 'e', 'l', 'l', 'o' };
const char *cp = ca;
while (*cp) {
cout << *cp << endl;
++cp;
}
```
解:
会将ca 字符数组中的元素打印出来。但是因为没有空字符的存在,程序不会退出循环。
## 练习3.38
在本节中我们提到,将两个指针相加不但是非法的,而且也没有什么意义。请问为什么两个指针相加没有意义?
解:
将两个指针相减可以表示两个指针(在同一数组中)相距的距离,将指针加上一个整数也可以表示移动这个指针到某一位置。但是两个指针相加并没有逻辑上的意义,因此两个指针不能相加。
## 练习3.39
编写一段程序,比较两个 `string` 对象。再编写一段程序,比较两个C风格字符串的内容。
解:
```cpp
#include <iostream>
#include <string>
#include <cstring>
using std::cout; using std::endl; using std::string;
int main()
{
// use string.
string s1("Mooophy"), s2("Pezy");
if (s1 == s2)
cout << "same string." << endl;
else if (s1 > s2)
cout << "Mooophy > Pezy" << endl;
else
cout << "Mooophy < Pezy" << endl;
cout << "=========" << endl;
// use C-Style character strings.
const char* cs1 = "Wangyue";
const char* cs2 = "Pezy";
auto result = strcmp(cs1, cs2);
if (result == 0)
cout << "same string." << endl;
else if (result < 0)
cout << "Wangyue < Pezy" << endl;
else
cout << "Wangyue > Pezy" << endl;
return 0;
}
```
## 练习3.40
编写一段程序,定义两个字符数组并用字符串字面值初始化它们;接着再定义一个字符数组存放前面两个数组连接后的结果。使用`strcpy`和`strcat`把前两个数组的内容拷贝到第三个数组当中。
解:
```cpp
#include <iostream>
#include <cstring>
const char cstr1[]="Hello";
const char cstr2[]="world!";
int main()
{
constexpr size_t new_size = strlen(cstr1) + strlen(" ") + strlen(cstr2) +1;
char cstr3[new_size];
strcpy(cstr3, cstr1);
strcat(cstr3, " ");
strcat(cstr3, cstr2);
std::cout << cstr3 << std::endl;
}
```
## 练习3.41
编写一段程序,用整型数组初始化一个vector对象。
```cpp
#include <iostream>
#include <vector>
using std::vector; using std::cout; using std::endl; using std::begin; using std::end;
int main()
{
int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<int> v(begin(arr), end(arr));
for (auto i : v) cout << i << " ";
cout << endl;
return 0;
}
```
## 练习3.42
编写一段程序,将含有整数元素的 `vector` 对象拷贝给一个整型数组。
解:
```cpp
#include <iostream>
#include <vector>
using std::vector; using std::cout; using std::endl; using std::begin; using std::end;
int main()
{
vector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int arr[10];
for (int i = 0; i != v.size(); ++i) arr[i] = v[i];
for (auto i : arr) cout << i << " ";
cout << endl;
return 0;
}
```
## 练习3.43
编写3个不同版本的程序,令其均能输出`ia`的元素。
版本1使用范围`for`语句管理迭代过程;版本2和版本3都使用普通`for`语句,其中版本2要求使用下标运算符,版本3要求使用指针。
此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、`auto`关键字和`decltype`关键字。
解:
```cpp
#include <iostream>
using std::cout; using std::endl;
int main()
{
int arr[3][4] =
{
{ 0, 1, 2, 3 },
{ 4, 5, 6, 7 },
{ 8, 9, 10, 11 }
};
// range for
for (const int(&row)[4] : arr)
for (int col : row) cout << col << " ";
cout << endl;
// for loop
for (size_t i = 0; i != 3; ++i)
for (size_t j = 0; j != 4; ++j) cout << arr[i][j] << " ";
cout << endl;
// using pointers.
for (int(*row)[4] = arr; row != arr + 3; ++row)
for (int *col = *row; col != *row + 4; ++col) cout << *col << " ";
cout << endl;
return 0;
}
```
## 练习3.44
改写上一个练习中的程序,使用类型别名来代替循环控制变量的类型。
解:
```cpp
#include <iostream>
using std::cout; using std::endl;
int main()
{
int ia[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
// a range for to manage the iteration
// use type alias
using int_array = int[4];
for (int_array& p : ia)
for (int q : p)
cout << q << " ";
cout << endl;
// ordinary for loop using subscripts
for (size_t i = 0; i != 3; ++i)
for (size_t j = 0; j != 4; ++j)
cout << ia[i][j] << " ";
cout << endl;
// using pointers.
// use type alias
for (int_array* p = ia; p != ia + 3; ++p)
for (int *q = *p; q != *p + 4; ++q)
cout << *q << " ";
cout << endl;
return 0;
}
```
## 练习3.45
再一次改写程序,这次使用 `auto` 关键字。
解:
```cpp
#include <iostream>
using std::cout; using std::endl;
int main()
{
int ia[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
// a range for to manage the iteration
for (auto& p : ia)
for (int q : p)
cout << q << " ";
cout << endl;
// ordinary for loop using subscripts
for (size_t i = 0; i != 3; ++i)
for (size_t j = 0; j != 4; ++j)
cout << ia[i][j] << " ";
cout << endl;
// using pointers.
for (auto p = ia; p != ia + 3; ++p)
for (int *q = *p; q != *p + 4; ++q)
cout << *q << " ";
cout << endl;
return 0;
}
```
================================================
FILE: excersize/ch04.md
================================================
# 第四章 表达式
## 练习4.1
表达式`5 + 10 * 20 / 2`的求值结果是多少?
解:
等价于`5 + ((10 * 20) / 2) = 105`
## 练习4.2
根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
(a) `*vec.begin()`
(b) `*vec.begin() + 1`
解:
```cpp
*(vec.begin())
(*(vec.begin())) + 1
```
## 练习4.3
C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
解:
可以接受。C++的设计思想是尽可能地“相信”程序员,将效率最大化。然而这种思想却有着潜在的危害,就是无法控制程序员自身引发的错误。因此 Java 的诞生也是必然,Java的思想就是尽可能地“不相信”程序员。
## 练习4.4
在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
`12 / 3 * 4 + 5 * 15 + 24 % 4 / 2`
解:
`((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2) = 16 + 75 + 0 = 91`
## 练习4.5
写出下列表达式的求值结果。
```cpp
-30 * 3 + 21 / 5 // -90+4 = -86
-30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18
30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0
-30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2
```
## 练习4.6
写出一条表达式用于确定一个整数是奇数还是偶数。
解:
```cpp
if (i % 2 == 0) /* ... */
```
或者
```cpp
if (i & 0x1) /* ... */
```
## 练习4.7
溢出是何含义?写出三条将导致溢出的表达式。
解:
当计算的结果超出该类型所能表示的范围时就会产生溢出。
```cpp
short svalue = 32767; ++svalue; // -32768
unsigned uivalue = 0; --uivalue; // 4294967295
unsigned short usvalue = 65535; ++usvalue; // 0
```
## 练习4.8
说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。
解:
- 逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种策略称为 **短路求值**。
- 相等性运算符未定义求值顺序。
## 练习4.9
解释在下面的`if`语句中条件部分的判断过程。
```cpp
const char *cp = "Hello World";
if (cp && *cp)
```
解:
首先判断`cp`,`cp` 不是一个空指针,因此`cp`为真。然后判断`*cp`,`*cp` 的值是字符`'H'`,非0。因此最后的结果为真。
## 练习4.10
为`while`循环写一个条件,使其从标准输入中读取整数,遇到`42`时停止。
解:
```cpp
int i;
while(cin >> i && i != 42)
```
## 练习4.11
书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。
解:
```cpp
a>b && b>c && c>d
```
## 练习4.12
假设`i`、`j`和`k`是三个整数,说明表达式`i != j < k`的含义。
解:
这个表达式等于`i != (j < k)`。首先得到`j < k`的结果为`true`或`false`,转换为整数值是`1`或`0`,然后判断`i`不等于`1`或`0` ,最终的结果为`bool`值。
## 练习4.13
在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
```cpp
int i; double d;
d = i = 3.5; // i = 3, d = 3.0
i = d = 3.5; // d = 3.5, i = 3
```
## 练习4.14
执行下述 if 语句后将发生什么情况?
```cpp
if (42 = i) // 编译错误。赋值运算符左侧必须是一个可修改的左值。而字面值是右值。
if (i = 42) // true.
```
## 练习4.15
下面的赋值是非法的,为什么?应该如何修改?
```cpp
double dval; int ival; int *pi;
dval = ival = pi = 0;
```
解:
`p`是指针,不能赋值给`int`,应该改为:
```cpp
dval = ival = 0;
pi = 0;
```
## 练习4.16
尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
```cpp
if (p = getPtr() != 0)
if (i = 1024)
```
解:
```cpp
if ((p=getPtr()) != 0)
if (i == 1024)
```
## 练习4.17
说明前置递增运算符和后置递增运算符的区别。
解:
前置递增运算符将对象本身作为左值返回,而后置递增运算符将对象原始值的副本作为右值返回。
## 练习4.18
如果132页那个输出`vector`对象元素的`while`循环使用前置递增运算符,将得到什么结果?
解:
将会从第二个元素开始取值,并且最后对`v.end()`进行取值,结果是未定义的。
## 练习4.19
假设`ptr`的类型是指向`int`的指针、`vec`的类型是`vector`、`ival`的类型是`int`,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
```cpp
(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]
```
解:
- (a) 判断`ptr`不是一个空指针,并且`ptr`当前指向的元素的值也为真,然后将`ptr`指向下一个元素
- (b) 判断`ival`的值为真,并且`(ival + 1)`的值也为真
- (c) 表达式有误。C++并没有规定`<=`运算符两边的求值顺序,应该改为`vec[ival] <= vec[ival+1]`
## 练习4.20
假设`iter`的类型是`vector<string>::iterator`, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
```cpp
(a) *iter++;
(b) (*iter)++;
(c) *iter.empty();
(d) iter->empty();
(e) ++*iter;
(f) iter++->empty();
```
解:
- (a)合法。返回迭代器所指向的元素,然后迭代器递增。
- (b)不合法。因为`vector`元素类型是`string`,没有`++`操作。
- (c)不合法。这里应该加括号。
- (d)合法。判断迭代器当前的元素是否为空。
- (e)不合法。`string`类型没有`++`操作。
- (f)合法。判断迭代器当前元素是否为空,然后迭代器递增。
## 练习4.21
编写一段程序,使用条件运算符从`vector`中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
解:
```cpp
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> ivec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto &i : ivec)
{
cout << ((i & 0x1) ? i * 2 : i) << " ";
}
cout << endl;
return 0;
}
```
## 练习4.22
本节的示例程序将成绩划分为`high pass`、`pass` 和 `fail` 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为`low pass`。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个`if`语句。哪个版本的程序更容易理解呢?为什么?
解:
```cpp
#include <iostream>
using std::cout; using std::cin; using std::endl;
int main()
{
for (unsigned g; cin >> g;)
{
auto result = g > 90 ? "high pass" : g < 60 ? "fail" : g < 75 ? "low pass" : "pass";
cout << result << endl;
// -------------------------
if (g > 90) cout << "high pass";
else if (g < 60) cout << "fail";
else if (g < 75) cout << "low pass";
else cout << "pass";
cout << endl;
}
return 0;
}
```
第二个版本容易理解。当条件运算符嵌套层数变多之后,代码的可读性急剧下降。而`if else`的逻辑很清晰。
## 练习4.23
因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?
```cpp
string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;
```
解:
加法运算符的优先级高于条件运算符。因此要改为:
```cpp
string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;
```
## 练习4.24
本节的示例程序将成绩划分为`high pass`、`pass`、和`fail`三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?
解:
如果条件运算符满足的是左结合律。那么
`finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";`
等同于
`finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";`
假如此时 `grade > 90` ,第一个条件表达式的结果是 `"high pass"` ,而字符串字面值的类型是 `const char *`,非空所以为真。因此第二个条件表达式的结果是 `"fail"`。这样就出现了自相矛盾的逻辑。
## 练习4.25
如果一台机器上`int`占32位、`char`占8位,用的是`Latin-1`字符集,其中字符`'q'` 的二进制形式是`01110001`,那么表达式`~'q' << 6`的值是什么?
解:
首先将`char`类型提升为`int`类型,即`00000000 00000000 00000000 01110001`,然后取反,再左移6位,结果是-7296。
## 练习4.26
在本节关于测验成绩的例子中,如果使用`unsigned int` 作为`quiz1` 的类型会发生什么情况?
解:
在有的机器上,`unsigned int` 类型可能只有 16 位,因此结果是未定义的。
## 练习4.27
下列表达式的结果是什么?
```cpp
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2
(b) ul1 | ul2
(c) ul1 && ul2
(d) ul1 || ul2
```
解:
- (a) 3
- (b) 7
- (c) true
- (d) ture
## 练习4.28
编写一段程序,输出每一种内置类型所占空间的大小。
解:
```cpp
#include <iostream>
using namespace std;
int main()
{
cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;
cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;
cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;
cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;
return 0;
}
```
输出:
```
bool: 1 bytes
char: 1 bytes
wchar_t: 4 bytes
char16_t: 2 bytes
char32_t: 4 bytes
short: 2 bytes
int: 4 bytes
long: 8 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
long double: 16 bytes
```
## 练习4.29
推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
```cpp
int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(p)/sizeof(*p) << endl;
```
解:
第一个输出结果是 10。
第二个结果取决于机器。sizeof(p)即int\*的内存空间大小, 32位机4B, 64位机8B; sizeof(\*p)即sizeof(int), 通常为4B, C++标准规定其不得小于2B。
## 练习4.30
根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。
```cpp
(a) sizeof x + y
(b) sizeof p->mem[i]
(c) sizeof a < b
(d) sizeof f()
```
解:
```cpp
(a) (sizeof x) + y
(b) sizeof(p->mem[i])
(c) sizeof(a) < b
(d) sizeof(f())
```
## 练习4.31
本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
解:
在4.5节(132页)已经说过了,除非必须,否则不用递增递减运算符的后置版本。在这里要使用后者版本的递增递减运算符不需要任何改动。
## 练习4.32
解释下面这个循环的含义。
```cpp
constexpr int size = 5;
int ia[size] = { 1, 2, 3, 4, 5 };
for (int *ptr = ia, ix = 0;
ix != size && ptr != ia+size;
++ix, ++ptr) { /* ... */ }
```
解:
这个循环在遍历数组`ia`,指针`ptr`和整型`ix`都是起到一个循环计数的功能。
## 练习4.33
根据4.12节中的表说明下面这条表达式的含义。
```cpp
someValue ? ++x, ++y : --x, --y
```
解:
逗号表达式的优先级是最低的。因此这条表达式也等于:
```cpp
(someValue ? ++x, ++y : --x), --y
```
如果`someValue`的值为真,`x` 和 `y` 的值都自增并返回 `y` 值,然后丢弃`y`值,`y`递减并返回`y`值。如果`someValue`的值为假,`x` 递减并返回`x` 值,然后丢弃`x`值,`y`递减并返回`y`值。
## 练习4.34
根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换:
```cpp
(a) if (fval)
(b) dval = fval + ival;
(c) dval + ival * cval;
```
需要注意每种运算符遵循的是左结合律还是右结合律。
解:
```cpp
(a) fval 转换为 bool 类型
(b) ival 转换为 float ,相加的结果转换为 double
(c) cval 转换为 int,然后相乘的结果转换为 double
```
## 练习4.35
假设有如下的定义:
```cpp
char cval;
int ival;
unsigned int ui;
float fval;
double dval;
```
请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。
```cpp
(a) cval = 'a' + 3;
(b) fval = ui - ival * 1.0;
(c) dval = ui * fval;
(d) cval = ival + fval + dval;
```
解:
- (a) `'a'` 转换为 `int` ,然后与 `3` 相加的结果转换为 `char`
- (b) `ival` 转换为 `double`,`ui` 转换为 `double`,结果转换为 `float`
- (c) `ui` 转换为 `float`,结果转换为 `double`
- (d) `ival` 转换为 `float`,与`fval`相加后的结果转换为 `double`,最后的结果转换为`char`
## 练习4.36
假设 `i` 是`int`类型,`d` 是`double`类型,书写表达式 `i*=d` 使其执行整数类型的乘法而非浮点类型的乘法。
解:
```cpp
i *= static_cast<int>(d);
```
## 练习4.37
练习4.37
用命名的强制类型转换改写下列旧式的转换语句。
```cpp
int i; double d; const string *ps; char *pc; void *pv;
(a) pv = (void*)ps;
(b) i = int(*pc);
(c) pv = &d;
(d) pc = (char*)pv;
```
解:
```cpp
(a) pv = static_cast<void*>(const_cast<string*>(ps));
(b) i = static_cast<int>(*pc);
(c) pv = static_cast<void*>(&d);
(d) pc = static_cast<char*>(pv);
```
## 练习4.38
说明下面这条表达式的含义。
```cpp
double slope = static_cast<double>(j/i);
```
解:
将`j/i`的结果值转换为`double`,然后赋值给`slope`。
================================================
FILE: excersize/ch05.md
================================================
# 第五章 语句
## 练习5.1
什么是空语句?什么时候会用到空语句?
解:
只含义一个单独的分号的语句是空语句。如:`;`。
如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句。
```cpp
while (cin >> s && s != sought)
;
```
## 练习5.2
什么是块?什么时候会用到块?
解:
用花括号括起来的语句和声明的序列就是块。
```cpp
{
// ...
}
```
如果在程序的某个地方,语法上需要一条语句,而逻辑上需要多条语句,此时应该使用块
```cpp
while (val <= 10) {
sum += val;
++val;
}
```
## 练习5.3
使用逗号运算符重写1.4.1节的`while`循环,使它不再需要块,观察改写之后的代码可读性提高了还是降低了。
```cpp
while (val <= 10)
sum += val, ++val;
```
代码的可读性反而降低了。
## 练习5.4
说明下列例子的含义,如果存在问题,试着修改它。
```cpp
(a) while (string::iterator iter != s.end()) { /* . . . */ }
(b) while (bool status = find(word)) { /* . . . */ }
if (!status) { /* . . . */ }
```
解:
- (a) 这个循环试图用迭代器遍历`string`,但是变量的定义应该放在循环的外面,目前每次循环都会重新定义一个变量,明显是错误的。
- (b) 这个循环的`while`和`if`是两个独立的语句,`if`语句中无法访问`status`变量,正确的做法是应该将`if`语句包含在`while`里面。
## 练习5.5
写一段自己的程序,使用`if else`语句实现把数字转换为字母成绩的要求。
```cpp
#include <iostream>
#include <vector>
#include <string>
using std::vector; using std::string; using std::cout; using std::endl; using std::cin;
int main()
{
vector<string> scores = { "F", "D", "C", "B", "A", "A++" };
for (int g; cin >> g;)
{
string letter;
if (g < 60)
{
letter = scores[0];
}
else
{
letter = scores[(g - 50) / 10];
if (g != 100)
letter += g % 10 > 7 ? "+" : g % 10 < 3 ? "-" : "";
}
cout << letter << endl;
}
return 0;
}
```
## 练习5.6
改写上一题的程序,使用条件运算符代替`if else`语句。
```cpp
#include <iostream>
#include <vector>
#include <string>
using std::vector; using std::string; using std::cout; using std::endl; using std::cin;
int main()
{
vector<string> scores = { "F", "D", "C", "B", "A", "A++" };
int grade = 0;
while (cin >> grade)
{
string lettergrade = grade < 60 ? scores[0] : scores[(grade - 50) / 10];
lettergrade += (grade == 100 || grade < 60) ? "" : (grade % 10 > 7) ? "+" : (grade % 10 < 3) ? "-" : "";
cout << lettergrade << endl;
}
return 0;
}
```
## 练习5.7
改写下列代码段中的错误。
```cpp
(a) if (ival1 != ival2)
ival1 = ival2
else
ival1 = ival2 = 0;
(b) if (ival < minval)
minval = ival;
occurs = 1;
(c) if (int ival = get_value())
cout << "ival = " << ival << endl;
if (!ival)
cout << "ival = 0\n";
(d) if (ival = 0)
ival = get_value();
```
解:
- (a) `ival1 = ival2` 后面少了分号。
- (b) 应该用花括号括起来。
- (c) `if (!ival)` 应该改为 `else`。
- (d) `if (ival = 0)` 应该改为 `if (ival == 0)`。
## 练习5.8
什么是“悬垂else”?C++语言是如何处理else子句的?
解:
用来描述在嵌套的`if else`语句中,如果`if`比`else`多时如何处理的问题。C++使用的方法是`else`匹配最近没有配对的`if`。
## 练习5.9
编写一段程序,使用一系列`if`语句统计从`cin`读入的文本中有多少元音字母。
解:
```cpp
#include <iostream>
using std::cout; using std::endl; using std::cin;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
while (cin >> ch)
{
if (ch == 'a') ++aCnt;
else if (ch == 'e') ++eCnt;
else if (ch == 'i') ++iCnt;
else if (ch == 'o') ++oCnt;
else if (ch == 'u') ++uCnt;
}
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u: \t" << uCnt << endl;
return 0;
}
```
## 练习5.10
我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计元音字母的大写形式,也就是说,新程序遇到'a'和'A'都应该递增`aCnt`的值,以此类推。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
while (cin >> ch)
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
}
cout << "Number of vowel a(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << endl;
return 0;
}
```
## 练习5.11
修改统计元音字母的程序,使其也能统计空格、制表符、和换行符的数量。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0;
char ch;
while (cin >> std::noskipws >> ch) //noskipws(no skip whitespce)
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case ' ':
++spaceCnt;
break;
case '\t':
++tabCnt;
break;
case '\n':
++newLineCnt;
break;
}
cout << "Number of vowel a(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << '\n'
<< "Number of space: \t" << spaceCnt << '\n'
<< "Number of tab char: \t" << tabCnt << '\n'
<< "Number of new line: \t" << newLineCnt << endl;
return 0;
}
```
其中,使用 `std::noskipws`可以保留默认跳过的空格。
## 练习5.12
修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量:`ff`、`fl`和`fi`。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0, ffCnt = 0, flCnt = 0, fiCnt = 0;
char ch, prech = '\0';
while (cin >> std::noskipws >> ch)
{
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
if (prech == 'f') ++fiCnt;
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case ' ':
++spaceCnt;
break;
case '\t':
++tabCnt;
break;
case '\n':
++newLineCnt;
break;
case 'f':
if (prech == 'f') ++ffCnt;
break;
case 'l':
if (prech == 'f') ++flCnt;
break;
}
prech = ch;
}
cout << "Number of vowel a(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << '\n'
<< "Number of space: \t" << spaceCnt << '\n'
<< "Number of tab char: \t" << tabCnt << '\n'
<< "Number of new line: \t" << newLineCnt << '\n'
<< "Number of ff: \t" << ffCnt << '\n'
<< "Number of fl: \t" << flCnt << '\n'
<< "Number of fi: \t" << fiCnt << endl;
return 0;
}
```
## 练习5.13
下面显示的每个程序都含有一个常见的编码错误,指出错误在哪里,然后修改它们。
```cpp
(a) unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a': aCnt++;
case 'e': eCnt++;
default: iouCnt++;
}
(b) unsigned index = some_value();
switch (index) {
case 1:
int ix = get_value();
ivec[ ix ] = index;
break;
default:
ix = ivec.size()-1;
ivec[ ix ] = index;
}
(c) unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1, 3, 5, 7, 9:
oddcnt++;
break;
case 2, 4, 6, 8, 10:
evencnt++;
break;
}
(d) unsigned ival=512, jval=1024, kval=4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch(swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
```
解:
(a) 少了`break`语句。应该为:
```cpp
unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a': aCnt++; break;
case 'e': eCnt++; break;
default: iouCnt++; break;
}
```
(b) 在`default`分支当中,`ix`未定义。应该在外部定义`ix`。
```cpp
unsigned index = some_value();
int ix;
switch (index) {
case 1:
ix = get_value();
ivec[ ix ] = index;
break;
default:
ix = static_cast<int>(ivec.size())-1;
ivec[ ix ] = index;
}
```
(c) `case`后面应该用冒号而不是逗号。
```cpp
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1: case 3: case 5: case 7: case 9:
oddcnt++;
break;
case 2: case 4: case 6: case 8: case 0:
evencnt++;
break;
}
```
(d) `case`标签必须是整型常量表达式。
```cpp
const unsigned ival=512, jval=1024, kval=4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch(swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
```
## 练习5.14
编写一段程序,从标准输入中读取若干`string`对象并查找连续重复出现的单词,所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。
例如:如果输入是:
```
how now now now brown cow cow
```
那么输出应该表明单词now连续出现了3次。
解:
```cpp
#include <iostream>
#include <string>
using std::cout; using std::cin; using std::endl; using std::string; using std::pair;
int main()
{
pair<string, int> max_duplicated;
int count = 0;
for (string str, prestr; cin >> str; prestr = str)
{
if (str == prestr) ++count;
else count = 0;
if (count > max_duplicated.second) max_duplicated = { prestr, count };
}
if (max_duplicated.first.empty()) cout << "There's no duplicated string." << endl;
else cout << "the word " << max_duplicated.first << " occurred " << max_duplicated.second + 1 << " times. " << endl;
return 0;
}
```
## 练习5.15
说明下列循环的含义并改正其中的错误。
```cpp
(a) for (int ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .
(b) int ix;
for (ix != sz; ++ix) { /* ... */ }
(c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
```
解:
应该改为下面这样:
```cpp
(a) int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .
(b) int ix;
for (; ix != sz; ++ix) { /* ... */ }
(c) for (int ix = 0; ix != sz; ++ix) { /*...*/ }
```
## 练习5.16
`while`循环特别适用于那种条件不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。
`for`循环更像是在按步骤迭代,它的索引值在某个范围内一次变化。根据每种循环的习惯各自编写一段程序,然后分别用另一种循环改写。
如果只能使用一种循环,你倾向于哪种?为什么?
解:
```cpp
int i;
while ( cin >> i )
// ...
for (int i = 0; cin >> i;)
// ...
for (int i = 0; i != size; ++i)
// ...
int i = 0;
while (i != size)
{
// ...
++i;
}
```
如果只能用一种循环,我会更倾向使用`while`,因为`while`显得简洁,代码可读性强。
## 练习5.17
假设有两个包含整数的`vector`对象,编写一段程序,检验其中一个`vector`对象是否是另一个的前缀。
为了实现这一目标,对于两个不等长的`vector`对象,只需挑出长度较短的那个,把它的所有元素和另一个`vector`对象比较即可。
例如,如果两个`vector`对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8,则程序的返回结果为真。
解:
```cpp
#include <iostream>
#include <vector>
using std::cout; using std::vector;
bool is_prefix(vector<int> const& lhs, vector<int> const& rhs)
{
if(lhs.size() > rhs.size())
return is_prefix(rhs, lhs);
for(unsigned i = 0; i != lhs.size(); ++i)
if(lhs[i] != rhs[i]) return false;
return true;
}
int main()
{
vector<int> l{ 0, 1, 1, 2 };
vector<int> r{ 0, 1, 1, 2, 3, 5, 8 };
cout << (is_prefix(r, l) ? "yes\n" : "no\n");
return 0;
}
```
## 练习5.18
说明下列循环的含义并改正其中的错误。
```cpp
(a) do { // 应该添加花括号
int v1, v2;
cout << "Please enter two numbers to sum:" ;
if (cin >> v1 >> v2)
cout << "Sum is: " << v1 + v2 << endl;
}while (cin);
(b) int ival;
do {
// . . .
} while (ival = get_response()); // 应该将ival 定义在循环外
(c) int ival = get_response();
do {
ival = get_response();
} while (ival); // 应该将ival 定义在循环外
```
## 练习5.19
编写一段程序,使用`do while`循环重复地执行下述任务:
首先提示用户输入两个`string`对象,然后挑出较短的那个并输出它。
解:
```cpp
#include <iostream>
#include <string>
using std::cout; using std::cin; using std::endl; using std::string;
int main()
{
string rsp;
do {
cout << "Input two strings: ";
string str1, str2;
cin >> str1 >> str2;
cout << (str1 <= str2 ? str1 : str2)
<< " is less than the other. " << "\n\n"
<< "More? Enter yes or no: ";
cin >> rsp;
} while (!rsp.empty() && tolower(rsp[0]) == 'y');
return 0;
}
```
## 练习5.20
编写一段程序,从标准输入中读取`string`对象的序列直到连续出现两个相同的单词或者所有的单词都读完为止。
使用`while`循环一次读取一个单词,当一个单词连续出现两次时使用`break`语句终止循环。
输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。
解:
```cpp
#include <iostream>
#include <string>
using std::cout; using std::cin; using std::endl; using std::string;
int main()
{
string read, tmp;
while (cin >> read)
if (read == tmp) break; else tmp = read;
if (cin.eof()) cout << "no word was repeated." << endl; //eof(end of file)判断输入是否结束,或者文件结束符,等同于 CTRL+Z
else cout << read << " occurs twice in succession." << endl;
return 0;
}
```
## 练习5.21
修改5.5.1节练习题的程序,使其找到的重复单词必须以大写字母开头。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
#include <string>
using std::string;
int main()
{
string curr, prev;
bool no_twice = true;
while (cin >> curr)
{
if (isupper(curr[0]) && prev == curr)
{
cout << curr << ": occurs twice in succession." << endl;
no_twice = false;
break;
}
prev = curr;
}
if (no_twice)
cout << "no word was repeated." << endl;
return 0;
}
```
## 练习5.22
本节的最后一个例子跳回到`begin`,其实使用循环能更好的完成该任务,重写这段代码,注意不再使用`goto`语句。
```cpp
// 向后跳过一个带初始化的变量定义是合法的
begin:
int sz = get_size();
if (sz <= 0) {
goto begin;
}
```
解:
用 for 循环修改的话就是这样
```cpp
for (int sz = get_size(); sz <=0; sz = get_size())
;
```
## 练习5.23
编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。
解:
```cpp
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
int i, j;
cin >> i >> j;
cout << i / j << endl;
return 0;
}
```
## 练习5.24
修改你的程序,使得当第二个数是0时抛出异常。先不要设定`catch`子句,运行程序并真的为除数输入0,看看会发生什么?
解:
```cpp
#include <iostream>
#include <stdexcept>
int main(void)
{
int i, j;
std::cin >> i >> j;
if (j == 0)
throw std::runtime_error("divisor is 0");
std::cout << i / j << std::endl;
return 0;
}
```
## 练习5.25
修改上一题的程序,使用`try`语句块去捕获异常。`catch`子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行`try`语句块的内容。
解:
```cpp
#include <iostream>
#include <stdexcept>
using std::cin; using std::cout; using std::endl; using std::runtime_error;
int main(void)
{
for (int i, j; cout << "Input two integers:\n", cin >> i >> j; )
{
try
{
if (j == 0)
throw runtime_error("divisor is 0");
cout << i / j << endl;
}
catch (runtime_error err)
{
cout << err.what() << "\nTry again? Enter y or n" << endl;
char c;
cin >> c;
if (!cin || c == 'n')
break;
}
}
return 0;
}
```
================================================
FILE: excersize/ch06.md
================================================
# 第六章 函数
## 练习6.1
实参和形参的区别的什么?
解:
实参是函数调用的实际值,是形参的初始值。
## 练习6.2
请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?
```cpp
(a) int f() {
string s;
// ...
return s;
}
(b) f2(int i) { /* ... */ }
(c) int calc(int v1, int v1) { /* ... */ }
(d) double square (double x) return x * x;
```
解:
应该改为下面这样:
```cpp
(a) string f() {
string s;
// ...
return s;
}
(b) void f2(int i) { /* ... */ }
(c) int calc(int v1, int v2) { /* ... */ return ; }
(d) double square (double x) { return x * x; }
```
## 练习6.3
编写你自己的`fact`函数,上机检查是否正确。注:阶乘。
解:
```cpp
#include <iostream>
int fact(int i)
{
if(i<0)
{
std::runtime_error err("Input cannot be a negative number");
std::cout << err.what() << std::endl;
}
return i > 1 ? i * fact( i - 1 ) : 1;
}
int main()
{
std::cout << std::boolalpha << (120 == fact(5)) << std::endl;
return 0;
}
```
启用`std::boolalpha`,可以输出 `"true"`或者 `"false"`。
## 练习6.4
编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。
```cpp
#include <iostream>
#include <string>
int fact(int i)
{
return i > 1 ? i * fact(i - 1) : 1;
}
void interactive_fact()
{
std::string const prompt = "Enter a number within [1, 13) :\n";
std::string const out_of_range = "Out of range, please try again.\n";
for (int i; std::cout << prompt, std::cin >> i; )
{
if (i < 1 || i > 12)
{
std::cout << out_of_range;
continue;
}
std::cout << fact(i) << std::endl;
}
}
int main()
{
interactive_fact();
return 0;
}
```
## 练习6.5
编写一个函数输出其实参的绝对值。
```cpp
#include <iostream>
int abs(int i)
{
return i > 0 ? i : -i;
}
int main()
{
std::cout << abs(-5) << std::endl;
return 0;
}
```
## 练习6.6
说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时达到这三种形式。
解:
形参定义在函数形参列表里面;局部变量定义在代码块里面;
局部静态变量在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止时才被销毁。
```cpp
// 例子
int count_add(int n) // n是形参
{
static int ctr = 0; // ctr 是局部静态变量
ctr += n;
return ctr;
}
int main()
{
for (int i = 0; i != 10; ++i) // i 是局部变量
cout << count_add(i) << endl;
return 0;
}
```
## 练习6.7
编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。
解:
```cpp
int generate()
{
static int ctr = 0;
return ctr++;
}
```
## 练习6.8
编写一个名为Chapter6.h 的头文件,令其包含6.1节练习中的函数声明。
解:
```cpp
int fact(int val);
int func();
template <typename T> //参考:https://blog.csdn.net/fightingforcv/article/details/51472586
T abs(T i)
{
return i >= 0 ? i : -i;
}
```
## 练习6.9 : fact.cc | factMain.cc
编写你自己的fact.cc 和factMain.cc ,这两个文件都应该包含上一小节的练习中编写的 Chapter6.h 头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。
解:
fact.cc:
```cpp
#include "Chapter6.h"
#include <iostream>
int fact(int val)
{
if (val == 0 || val == 1) return 1;
else return val * fact(val-1);
}
int func()
{
int n, ret = 1;
std::cout << "input a number: ";
std::cin >> n;
while (n > 1) ret *= n--;
return ret;
}
```
factMain.cc:
```cpp
#include "Chapter6.h"
#include <iostream>
int main()
{
std::cout << "5! is " << fact(5) << std::endl;
std::cout << func() << std::endl;
std::cout << abs(-9.78) << std::endl;
}
```
编译: `g++ factMain.cpp fact.cpp -o main`
## 练习6.10
编写一个函数,使用指针形参交换两个整数的值。
在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。
解:
```cpp
#include <iostream>
#include <string>
void swap(int* lhs, int* rhs)
{
int tmp;
tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
int main()
{
for (int lft, rht; std::cout << "Please Enter:\n", std::cin >> lft >> rht;)
{
swap(&lft, &rht);
std::cout << lft << " " << rht << std::endl;
}
return 0;
}
```
## 练习6.11
编写并验证你自己的reset函数,使其作用于引用类型的参数。注:reset即置0。
解:
```cpp
#include <iostream>
void reset(int &i)
{
i = 0;
}
int main()
{
int i = 42;
reset(i);
std::cout << i << std::endl;
return 0;
}
```
## 练习6.12
改写6.2.1节练习中的程序,使其引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?
```cpp
#include <iostream>
#include <string>
void swap(int& lhs, int& rhs)
{
int temp = lhs;
lhs = rhs;
rhs = temp;
}
int main()
{
for (int left, right; std::cout << "Please Enter:\n", std::cin >> left >> right; )
{
swap(left, right);
std::cout << left << " " << right << std::endl;
}
return 0;
}
```
很明显引用更好用。
## 练习6.13
假设`T`是某种类型的名字,说明以下两个函数声明的区别:
一个是`void f(T)`, 另一个是`void f(&T)`。
解:
`void f(T)`的参数通过值传递,在函数中`T`是实参的副本,改变`T`不会影响到原来的实参。
`void f(&T)`的参数通过引用传递,在函数中的`T`是实参的引用,`T`的改变也就是实参的改变。
## 练习6.14
举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
解:
例如交换两个整数的函数,形参应该是引用
```cpp
void swap(int& lhs, int& rhs)
{
int temp = lhs;
lhs = rhs;
rhs = temp;
}
```
当实参的值是右值时,形参不能为引用类型
```cpp
int add(int a, int b)
{
return a + b;
}
int main()
{
int i = add(1,2);
return 0;
}
```
## 练习6.15
说明`find_char`函数中的三个形参为什么是现在的类型,特别说明为什么`s`是常量引用而`occurs`是普通引用?
为什么`s`和`occurs`是引用类型而`c`不是?
如果令`s`是普通引用会发生什么情况?
如果令`occurs`是常量引用会发生什么情况?
解:
- 因为字符串可能很长,因此使用引用避免拷贝;
- 而在函数中我们不希望改变`s`的内容,所以令`s`为常量。
- `occurs`是要传到函数外部的变量,所以使用引用,`occurs`的值会改变,所以是普通引用。
- 因为我们只需要`c`的值,这个实参可能是右值(右值实参无法用于引用形参),所以`c`不能用引用类型。
- 如果`s`是普通引用,也可能会意外改变原来字符串的内容。
- `occurs`如果是常量引用,那么意味着不能改变它的值,那也就失去意义了。
## 练习6.16
下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。
```cpp
bool is_empty(string& s) { return s.empty(); }
```
解:
局限性在于常量字符串和字符串字面值无法作为该函数的实参,如果下面这样调用是非法的:
```cpp
const string str;
bool flag = is_empty(str); //非法
bool flag = is_empty("hello"); //非法
```
所以要将这个函数的形参定义为常量引用:
```cpp
bool is_empty(const string& s) { return s.empty(); }
```
## 练习6.17
编写一个函数,判断`string`对象中是否含有大写字母。
编写另一个函数,把`string`对象全部改写成小写形式。
在这两个函数中你使用的形参类型相同吗?为什么?
解:
两个函数的形参不一样。第一个函数使用常量引用,第二个函数使用普通引用。
## 练习6.18
为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
- (a) 名为`compare`的函数,返回布尔值,两个参数都是`matrix`类的引用。
- (b) 名为`change_val`的函数,返回`vector`的迭代器,有两个参数:一个是`int`,另一个是`vector`的迭代器。
解:
```cpp
(a) bool compare(matrix &m1, matrix &m2);
(b) vector<int>::iterator change_val(int, vector<int>::iterator);
```
## 练习6.19
假定有如下声明,判断哪个调用合法、哪个调用不合法。对于不合法的函数调用,说明原因。
```cpp
double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
vector<int> vec(10);
(a) calc(23.4, 55.1);
(b) count("abcda",'a');
(c) calc(66);
(d) sum(vec.begin(), vec.end(), 3.8);
```
解:
- (a) 不合法。`calc`只有一个参数。
- (b) 合法。
- (c) 合法。
- (d) 合法。
## 练习6.20
引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?
解:
应该尽量将引用形参设为常量引用,除非有明确的目的是为了改变这个引用变量。
如果形参应该是常量引用,而我们将其设为了普通引用,那么常量实参将无法作用于普通引用形参。
## 练习6.21
编写一个函数,令其接受两个参数:一个是`int`型的数,另一个是`int`指针。
函数比较`int`的值和指针所指的值,返回较大的那个。
在该函数中指针的类型应该是什么?
解:
```cpp
#include <iostream>
using std::cout;
int larger_one(const int i, const int *const p)
{
return (i > *p) ? i : *p;
}
int main()
{
int i = 6;
cout << larger_one(7, &i);
return 0;
}
```
应该是`const int *`类型。
## 练习6.22
编写一个函数,令其交换两个`int`指针。
解:
```cpp
#include <iostream>
#include <string>
void swap(int*& lft, int*& rht)
{
auto tmp = lft;
lft = rht;
rht = tmp;
}
int main()
{
int i = 42, j = 99;
auto lft = &i;
auto rht = &j;
swap(lft, rht);
std::cout << *lft << " " << *rht << std::endl;
return 0;
}
```
## 练习6.23
参考本节介绍的几个`print`函数,根据理解编写你自己的版本。
依次调用每个函数使其输入下面定义的`i`和`j`:
```cpp
int i = 0, j[2] = { 0, 1 };
```
解:
```cpp
#include <iostream>
using std::cout; using std::endl; using std::begin; using std::end;
void print(const int *pi)
{
if(pi)
cout << *pi << endl;
}
void print(const char *p)
{
if (p)
while (*p) cout << *p++;
cout << endl;
}
void print(const int *beg, const int *end)
{
while (beg != end)
cout << *beg++ << endl;
}
void print(const int ia[], size_t size)
{
for (size_t i = 0; i != size; ++i) {
cout << ia[i] << endl;
}
}
void print(int (&arr)[2])
{
for (auto i : arr)
cout << i << endl;
}
int main()
{
int i = 0, j[2] = { 0, 1 };
char ch[5] = "pezy";
print(ch);
print(begin(j), end(j));
print(&i);
print(j, end(j)-begin(j));
print(j);
return 0;
}
```
## 练习6.24
描述下面这个函数的行为。如果代码中存在问题,请指出并改正。
```cpp
void print(const int ia[10])
{
for (size_t i = 0; i != 10; ++i)
cout << ia[i] << endl;
}
```
解:
当数组作为实参的时候,会被自动转换为指向首元素的指针。
因此函数形参接受的是一个指针。
如果要让这个代码成功运行(不更改也可以运行),可以将形参改为数组的引用。
```cpp
void print(const int (&ia)[10])
{
for (size_t i = 0; i != 10; ++i)
cout << ia[i] << endl;
}
```
## 练习6.25
编写一个`main`函数,令其接受两个实参。把实参的内容连接成一个`string`对象并输出出来。
## 练习6.26
编写一个程序,使其接受本节所示的选项;输出传递给`main`函数的实参内容。
解:
包括6.25
```cpp
#include <iostream>
#include <string>
int main(int argc, char **argv)
{
std::string str;
for (int i = 1; i != argc; ++i)
str += std::string(argv[i]) + " ";
std::cout << str << std::endl;
return 0;
}
```
## 练习6.27
编写一个函数,它的参数是`initializer_list`类型的对象,函数的功能是计算列表中所有元素的和。
解:
```cpp
#include <iostream>
#include <initializer_list>
int sum(std::initializer_list<int> const& il)
{
int sum = 0;
for (auto i : il) sum += i;
return sum;
}
int main(void)
{
auto il = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::cout << sum(il) << std::endl;
return 0;
}
```
## 练习6.28
在`error_msg`函数的第二个版本中包含`ErrCode`类型的参数,其中循环内的`elem`是什么类型?
解:
`elem`是`const string &`类型。
## 练习6.29
在范围`for`循环中使用`initializer_list`对象时,应该将循环控制变量声明成引用类型吗?为什么?
解:
应该使用常量引用类型。`initializer_list`对象中的元素都是常量,我们无法修改`initializer_list`对象中的元素的值。
## 练习6.30
编译第200页的`str_subrange`函数,看看你的编译器是如何处理函数中的错误的。
解:
编译器信息:
```
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
```
编译错误信息:
```
ch6.cpp:38:9: error: return-statement with no value, in function returning ‘bool’ [-fpermissive]
```
## 练习6.31
什么情况下返回的引用无效?什么情况下返回常量的引用无效?
解:
当返回的引用的对象是局部变量时,返回的引用无效;当我们希望返回的对象被修改时,返回常量的引用无效。
## 练习6.32
下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。
```cpp
int &get(int *array, int index) { return array[index]; }
int main()
{
int ia[10];
for (int i = 0; i != 10; ++i)
get(ia, i) = i;
}
```
解:
合法。`get`函数根据索引取得数组中的元素的引用。
## 练习6.33
编写一个递归函数,输出`vector`对象的内容。
解:
```cpp
#include <iostream>
#include <vector>
using std::vector; using std::cout;
using Iter = vector<int>::const_iterator;
void print(Iter first, Iter last)
{
if (first != last)
{
cout << *first << " ";
print(++first, last);
}
}
int main()
{
vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
print(vec.cbegin(), vec.cend());
return 0;
}
```
## 练习6.34
如果`factorial`函数的停止条件如下所示,将发生什么?
```cpp
if (val != 0)
```
解:
如果`val`为正数,从结果上来说没有区别(多乘了个1);
如果`val`为负数,那么递归永远不会结束。
## 练习6.35
在调用`factorial`函数时,为什么我们传入的值是`val-1`而非`val--`?
解:
如果传入的值是`val--`,那么将会永远传入相同的值来调用该函数,递归将永远不会结束。
## 练习6.36
编写一个函数声明,使其返回数组的引用并且该数组包含10个`string`对象。
不用使用尾置返回类型、`decltype`或者类型别名。
解:
```cpp
string (&fun())[10];
```
## 练习6.37
为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用`decltype`关键字。
你觉得哪种形式最好?为什么?
解:
```cpp
typedef string str_arr[10];
str_arr& fun();
auto fun()->string(&)[10];
string s[10];
decltype(s)& fun();
```
我觉得尾置返回类型最好,就一行代码。
## 练习6.38
修改`arrPtr`函数,使其返回数组的引用。
解:
```cpp
decltype(odd)& arrPtr(int i)
{
return (i % 2) ? odd : even;
}
```
## 练习6.39
说明在下面的每组声明中第二条语句是何含义。
如果有非法的声明,请指出来。
```cpp
(a) int calc(int, int);
int calc(const int, const int);
(b) int get();
double get();
(c) int *reset(int *);
double *reset(double *);
```
解:
- (a) 非法。因为顶层const不影响传入函数的对象,所以第二个声明无法与第一个声明区分开来。
- (b) 非法。对于重载的函数来说,它们应该只有形参的数量和形参的类型不同。返回值与重载无关。
- (c) 合法。
## 练习6.40
下面的哪个声明是错误的?为什么?
```cpp
(a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char bckgrnd);
```
解:
(a) 正确。
(b) 错误。因为一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。
## 练习6.41
下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?
```cpp
char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a) init();
(b) init(24,10);
(c) init(14,'*');
```
解:
- (a) 非法。第一个参数不是默认参数,最少需要一个实参。
- (b) 合法。
- (c) 合法,但与初衷不符。字符`*`被解释成`int`传入到了第二个参数。而初衷是要传给第三个参数。
## 练习6.42
给`make_plural`函数的第二个形参赋予默认实参's', 利用新版本的函数输出单词success和failure的单数和复数形式。
解:
```cpp
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
string make_plural(size_t ctr, const string& word, const string& ending = "s")
{
return (ctr > 1) ? word + ending : word;
}
int main()
{
cout << "single: " << make_plural(1, "success", "es") << " "
<< make_plural(1, "failure") << endl;
cout << "plural : " << make_plural(2, "success", "es") << " "
<< make_plural(2, "failure") << endl;
return 0;
}
```
## 练习6.43
你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
```cpp
(a) inline bool eq(const BigInt&, const BigInt&) {...}
(b) void putValues(int *arr, int size);
```
解:
全部都放进头文件。(a) 是内联函数,(b) 是声明。
## 练习6.44
将6.2.2节的`isShorter`函数改写成内联函数。
解:
```cpp
inline bool is_shorter(const string &lft, const string &rht)
{
return lft.size() < rht.size();
}
```
## 练习6.45
回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?
如果是,将它们改写成内联函数;如果不是,说明原因。
解:
一般来说,内联机制用于优化规模小、流程直接、频繁调用的函数。
## 练习6.46
能把`isShorter`函数定义成`constexpr`函数吗?
如果能,将它改写成`constxpre`函数;如果不能,说明原因。
解:
不能。`constexpr`函数的返回值类型及所有形参都得是字面值类型。
## 练习6.47
改写6.3.2节练习中使用递归输出`vector`内容的程序,使其有条件地输出与执行过程有关的信息。
例如,每次调用时输出`vector`对象的大小。
分别在打开和关闭调试器的情况下编译并执行这个程序。
解:
```cpp
#include <iostream>
#include <vector>
using std::vector; using std::cout; using std::endl;
void printVec(vector<int> &vec)
{
#ifndef NDEBUG
cout << "vector size: " << vec.size() << endl;
#endif
if (!vec.empty())
{
auto tmp = vec.back();
vec.pop_back();
printVec(vec);
cout << tmp << " ";
}
}
int main()
{
vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
printVec(vec);
cout << endl;
return 0;
}
```
## 练习6.48
说明下面这个循环的含义,它对assert的使用合理吗?
```cpp
string s;
while (cin >> s && s != sought) { } //空函数体
assert(cin);
```
解:
不合理。从这个程序的意图来看,应该用
```cpp
assert(s == sought);
```
## 练习6.49
什么是候选函数?什么是可行函数?
解:
候选函数:与被调用函数同名,并且其声明在调用点可见。
可行函数:形参与实参的数量相等,并且每个实参类型与对应的形参类型相同或者能转换成形参的类型。
## 练习6.50
已知有第217页对函数`f`的声明,对于下面的每一个调用列出可行函数。
其中哪个函数是最佳匹配?
如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?
```cpp
(a) f(2.56, 42)
(b) f(42)
(c) f(42, 0)
(d) f(2.56, 3.14)
```
解:
- (a) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
该调用具有二义性而不合法。
- (b) `void f(int);` 是可行函数。调用合法。
- (c) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
`void f(int, int);`是最佳匹配。
- (d) `void f(int, int);`和`void f(double, double = 3.14);`是可行函数。
`void f(double, double = 3.14);`是最佳匹配。
## 练习6.51
编写函数`f`的4版本,令其各输出一条可以区分的消息。
验证上一个练习的答案,如果你的回答错了,反复研究本节内容直到你弄清自己错在何处。
解:
```cpp
#include <iostream>
using std::cout; using std::endl;
void f()
{
cout << "f()" << endl;
}
void f(int)
{
cout << "f(int)" << endl;
}
void f(int, int)
{
cout << "f(int, int)" << endl;
}
void f(double, double)
{
cout << "f(double, double)" << endl;
}
int main()
{
//f(2.56, 42); // error: 'f' is ambiguous.
f(42);
f(42, 0);
f(2.56, 3.14);
return 0;
}
```
## 练习6.52
已知有如下声明:
```cpp
void manip(int ,int);
double dobj;
```
请指出下列调用中每个类型转换的等级。
```cpp
(a) manip('a', 'z');
(b) manip(55.4, dobj);
```
解:
- (a) 第3级。类型提升实现的匹配。
- (b) 第4级。算术类型转换实现的匹配。
## 练习6.53
说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。
```cpp
(a) int calc(int&, int&);
int calc(const int&, const int&);
(b) int calc(char*, char*);
int calc(const char*, const char*);
(c) int calc(char*, char*);
int calc(char* const, char* const);
```
解:
(c) 不合法。顶层const不影响传入函数的对象。
## 练习6.54
编写函数的声明,令其接受两个`int`形参并返回类型也是`int`;然后声明一个`vector`对象,令其元素是指向该函数的指针。
解:
```cpp
int func(int, int);
vector<decltype(func)*> v;
```
## 练习6.55
编写4个函数,分别对两个`int`值执行加、减、乘、除运算;在上一题创建的`vector`对象中保存指向这些函数的指针。
解:
```cpp
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
v.push_back(add);
v.push_back(subtract);
v.push_back(multiply);
v.push_back(divide);
```
## 练习6.56
调用上述`vector`对象中的每个元素并输出结果。
解:
```cpp
std::vector<decltype(func) *> vec{ add, subtract, multiply, divide };
for (auto f : vec)
std::cout << f(2, 2) << std::endl;
```
================================================
FILE: excersize/ch07.md
================================================
# 第七章 类
## 练习7.1
使用2.6.1节定义的`Sales_data`类为1.6节的交易处理程序编写一个新版本。
解:
```cpp
#include <iostream>
#include <string>
using std::cin; using std::cout; using std::endl; using std::string;
struct Sales_data
{
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
Sales_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
total = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
```
## 练习7.2
曾在2.6.2节的练习中编写了一个`Sales_data`类,请向这个类添加`combine`函数和`isbn`成员。
解:
```cpp
#include <string>
struct Sales_data {
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
```
## 练习7.3
修改7.1.1节的交易处理程序,令其使用这些成员。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
int main()
{
Sales_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
total = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
```
## 练习7.4
编写一个名为`Person`的类,使其表示人员的姓名和地址。使用`string`对象存放这些元素,接下来的练习将不断充实这个类的其他特征。
解:
```cpp
#include <string>
class Person {
std::string name;
std::string address;
};
```
## 练习7.5
在你的`Person`类中提供一些操作使其能够返回姓名和地址。
这些函数是否应该是`const`的呢?解释原因。
解:
```cpp
#include <string>
class Person
{
std::string name;
std::string address;
public:
auto get_name() const -> std::string const& { return name; }
auto get_addr() const -> std::string const& { return address; }
};
```
应该是`const`的。因为常量的`Person`对象也需要使用这些函数操作。
## 练习7.6
对于函数`add`、`read`和`print`,定义你自己的版本。
解:
```cpp
#include <string>
#include <iostream>
struct Sales_data {
std::string const& isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// member functions.
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// nonmember functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
```
## 练习7.7
使用这些新函数重写7.1.2节练习中的程序。
```cpp
int main()
{
Sales_data total;
if (read(std::cin, total))
{
Sales_data trans;
while (read(std::cin, trans)) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(std::cout, total) << std::endl;
total = trans;
}
}
print(std::cout, total) << std::endl;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
```
## 练习7.8
为什么`read`函数将其`Sales_data`参数定义成普通的引用,而`print`函数将其参数定义成常量引用?
解:
因为`read`函数会改变对象的内容,而`print`函数不会。
## 练习7.9
对于7.1.2节练习中代码,添加读取和打印`Person`对象的操作。
解:
```cpp
#include <string>
#include <iostream>
struct Person
{
std::string const& getName() const { return name; }
std::string const& getAddress() const { return address; }
std::string name;
std::string address;
};
std::istream &read(std::istream &is, Person &person)
{
return is >> person.name >> person.address;
}
std::ostream &print(std::ostream &os, const Person &person)
{
return os << person.name << " " << person.address;
}
```
## 练习7.10
在下面这条`if`语句中,条件部分的作用是什么?
```cpp
if (read(read(cin, data1), data2)) //等价read(std::cin, data1);read(std::cin, data2);
```
解:
`read`函数的返回值是`istream`对象,
`if`语句中条件部分的作用是从输入流中读取数据给两个`data`对象。
## 练习7.11 :
在你的`Sales_data`类中添加构造函数,
然后编写一段程序令其用到每个构造函数。
解:
头文件:
```cpp
#include <string>
#include <iostream>
struct Sales_data {
Sales_data() = default;
Sales_data(const std::string &s):bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
Sales_data(std::istream &is);
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// nonmember functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
// member functions.
Sales_data::Sales_data(std::istream &is)
{
read(is, *this);
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
```
主函数:
```cpp
int main()
{
Sales_data item1;
print(std::cout, item1) << std::endl;
Sales_data item2("0-201-78345-X");
print(std::cout, item2) << std::endl;
Sales_data item3("0-201-78345-X", 3, 20.00);
print(std::cout, item3) << std::endl;
Sales_data item4(std::cin);
print(std::cout, item4) << std::endl;
return 0;
}
```
## 练习7.12
把只接受一个`istream`作为参数的构造函数移到类的内部。
解:
```cpp
#include <string>
#include <iostream>
struct Sales_data;
std::istream &read(std::istream&, Sales_data&);
struct Sales_data {
Sales_data() = default;
Sales_data(const std::string &s):bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
Sales_data(std::istream &is) { read(is, *this); }
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// member functions.
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// nonmember functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
```
## 练习7.13
使用`istream`构造函数重写第229页的程序。
解:
```cpp
int main()
{
Sales_data total(std::cin);
if (!total.isbn().empty())
{
std::istream &is = std::cin;
while (is) {
Sales_data trans(is);
if (!is) break;
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(std::cout, total) << std::endl;
total = trans;
}
}
print(std::cout, total) << std::endl;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
```
## 练习7.14
编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员。
```cpp
Sales_data() : units_sold(0) , revenue(0) { }
```
## 练习7.15
为你的`Person`类添加正确的构造函数。
解:
```cpp
#include <string>
#include <iostream>
struct Person;
std::istream &read(std::istream&, Person&);
struct Person
{
Person() = default;
Person(const std::string& sname, const std::string& saddr) :name(sname), address(saddr) {}
Person(std::istream &is) { read(is, *this); }
std::string getName() const { return name; }
std::string getAddress() const { return address; }
std::string name;
std::string address;
};
std::istream &read(std::istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
std::ostream &print(std::ostream &os, const Person &person)
{
os << person.name << " " << person.address;
return os;
}
```
## 练习7.16
在类的定义中对于访问说明符出现的位置和次数有限定吗?
如果有,是什么?什么样的成员应该定义在`public`说明符之后?
什么样的成员应该定义在`private`说明符之后?
解:
在类的定义中对于访问说明符出现的位置和次数**没有限定**。
每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者达到类的结尾处为止。
如果某个成员能够在整个程序内都被访问,那么它应该定义为`public`;
如果某个成员只能在类内部访问,那么它应该定义为`private`。
## 练习7.17
使用`class`和`struct`时有区别吗?如果有,是什么?
解:
`class`和`struct`的唯一区别是默认的访问级别不同。
## 练习7.18
封装是何含义?它有什么用处?
解:
将类内部分成员设置为外部不可见,而提供部分接口给外面,这样的行为叫做封装。
用处:
- 1.确保用户的代码不会无意间破坏封装对象的状态。
- 2.被封装的类的具体实现细节可以随时改变,而无需调整用户级别的代码。
## 练习7.19
在你的`Person`类中,你将把哪些成员声明成`public`的?
哪些声明成`private`的?
解释你这样做的原因。
构造函数、`getName()`、`getAddress()`函数将设为`public`。
`name`和 `address` 将设为`private`。
函数是暴露给外部的接口,因此要设为`public`;
而数据则应该隐藏让外部不可见。
## 练习7.20
友元在什么时候有用?请分别举出使用友元的利弊。
解:
当其他类或者函数想要访问当前类的私有变量时,这个时候应该用友元。
利:
与当前类有关的接口函数能直接访问类的私有变量。
弊:
牺牲了封装性与可维护性。
## 练习7.21
修改你的`Sales_data`类使其隐藏实现的细节。
你之前编写的关于`Sales_data`操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。
解:
```cpp
#include <string>
#include <iostream>
class Sales_data {
friend std::istream &read(std::istream &is, Sales_data &item);
friend std::ostream &print(std::ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data() = default;
Sales_data(const std::string &s):bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
Sales_data(std::istream &is) { read(is, *this); }
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// member functions.
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// friend functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
```
## 练习7.22
修改你的`Person`类使其隐藏实现的细节。
解:
```cpp
#include <string>
#include <iostream>
class Person {
friend std::istream &read(std::istream &is, Person &person);
friend std::ostream &print(std::ostream &os, const Person &person);
public:
Person() = default;
Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ }
Person(std::istream &is){ read(is, *this); }
std::string getName() const { return name; }
std::string getAddress() const { return address; }
private:
std::string name;
std::string address;
};
std::istream &read(std::istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
std::ostream &print(std::ostream &os, const Person &person)
{
os << person.name << " " << person.address;
return os;
}
```
## 练习7.23
编写你自己的`Screen`类型。
解:
```cpp
#include <string>
class Screen {
public:
using pos = std::string::size_type;
Screen() = default;
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ }
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width+c]; }
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
```
# 练习7.24
给你的`Screen`类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将`contents`初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。
解:
```cpp
#include <string>
class Screen {
public:
using pos = std::string::size_type;
Screen() = default; // 1
Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width+c]; }
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
```
## 练习7.25
`Screen`能安全地依赖于拷贝和赋值操作的默认版本吗?
如果能,为什么?如果不能?为什么?
解:
能。 `Screen`的成员只有内置类型和`string`,因此能安全地依赖于拷贝和赋值操作的默认版本。
管理动态内存的类则不能依赖于拷贝和赋值操作的默认版本,而且也应该尽量使用`string`和`vector`来避免动态管理内存的复杂性。
## 练习7.26
将`Sales_data::avg_price`定义成内联函数。
解:
在头文件中加入:
```cpp
inline double Sales_data::avg_price() const
{
return units_sold ? revenue/units_sold : 0;
}
```
## 练习7.27
给你自己的`Screen`类添加`move`、`set` 和`display`函数,通过执行下面的代码检验你的类是否正确。
```cpp
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
```
解:
增加代码:
```cpp
#include <string>
#include <iostream>
class Screen {
public:
... ...
inline Screen& move(pos r, pos c);
inline Screen& set(char c);
inline Screen& set(pos r, pos c, char ch);
const Screen& display(std::ostream &os) const { do_display(os); return *this; }
Screen& display(std::ostream &os) { do_display(os); return *this; }
private:
void do_display(std::ostream &os) const { os << contents; }
... ...
};
inline Screen& Screen::move(pos r, pos c)
{
cursor = r*width + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r*width+c] = ch;
return *this;
}
```
测试代码:
```cpp
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(std::cout);
std::cout << "\n";
myScreen.display(std::cout);
std::cout << "\n";
return 0;
}
```
## 练习7.28
如果`move`、`set`和`display`函数的返回类型不是`Screen&` 而是`Screen`,则在上一个练习中将会发生什么?
解:
如果返回类型是`Screen`,那么`move`返回的是`*this`的一个副本,因此`set`函数只能改变临时副本而不能改变`myScreen`的值。
## 练习7.29
修改你的`Screen`类,令`move`、`set`和`display`函数返回`Screen`并检查程序的运行结果,在上一个练习中你的推测正确吗?
解:
推测正确。
```
#with '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
^
# without '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
^
```
## 练习7.30
通过`this`指针使用成员的做法虽然合法,但是有点多余。讨论显示使用指针访问成员的优缺点。
解:
优点:
程序的意图更明确
函数的参数可以与成员同名,如
```cpp
void setAddr(const std::string &addr) { this->addr = addr; }
```
缺点:
有时候显得有点多余,如
```cpp
std::string getAddr() const { return this->addr; }
```
## 练习7.31
定义一对类`X`和`Y`,其中`X`包含一个指向`Y`的指针,而`Y`包含一个类型为`X`的对象。
解:
```cpp
class Y;
class X{
Y* y = nullptr;
};
class Y{
X x;
};
```
## 练习7.32
定义你自己的`Screen`和`Window_mgr`,其中`clear`是`Window_mgr`的成员,是`Screen`的友元。
解:
```cpp
#include <vector>
#include <iostream>
#include <string>
class Screen;
class Window_mgr
{
public:
using ScreenIndex = std::vector<Screen>::size_type;
inline void clear(ScreenIndex);
private:
std::vector<Screen> screens;
};
class Screen
{
friend void Window_mgr::clear(ScreenIndex);
public:
using pos = std::string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht*wd,' ') {}
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c) {}
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width + c]; }
inline Screen& move(pos r, pos c);
inline Screen& set(char c);
inline Screen& set(pos r, pos c, char ch);
const Screen& display(std::ostream& os) const { do_display(os); return *this; }
Screen& display(std::ostream& os) { do_display(os); return *this; }
private:
void do_display(std::ostream &os) const { os << contents; }
private:
pos cursor = 0;
pos width = 0, height = 0;
std::string contents;
};
inline void Window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = std::string(s.height*s.width,' ');
}
inline Screen& Screen::move(pos r, pos c)
{
cursor = r*width + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r*width + c] = ch;
return *this;
}
```
## 练习7.33
如果我们给`Screen`添加一个如下所示的`size`成员将发生什么情况?如果出现了问题,请尝试修改它。
```cpp
pos Screen::size() const
{
return height * width;
}
```
解:
纠正:错误为 error: extra qualification 'Screen::' on member 'size' [-fpermissive]
则应该去掉Screen::,改为
```cpp
pos size() const{
return height * width;
}
```
## 练习7.34
如果我们把第256页`Screen`类的`pos`的`typedef`放在类的最后一行会发生什么情况?
解:
在 dummy_fcn(pos height) 函数中会出现 未定义的标识符pos。
类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。
## 练习7.35
解释下面代码的含义,说明其中的`Type`和`initVal`分别使用了哪个定义。如果代码存在错误,尝试修改它。
```cpp
typedef string Type;
Type initVal();
class Exercise {
public:
typedef double Type;
Type setVal(Type);
Type initVal();
private:
int val;
};
Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
```
解:
书上255页中说:
```
然而在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。
```
因此重复定义`Type`是错误的行为。
虽然重复定义类型名字是错误的行为,但是编译器并不为此负责。所以我们要人为地遵守一些原则,在这里有一些讨论。
## 练习7.36
下面的初始值是错误的,请找出问题所在并尝试修改它。
```cpp
struct X {
X (int i, int j): base(i), rem(base % j) {}
int rem, base;
};
```
解:
应该改为:
```cpp
struct X {
X (int i, int j): base(i), rem(base % j) {}
int base, rem;
};
```
## 练习7.37
使用本节提供的`Sales_data`类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有的数据成员的值。
解:
```cpp
Sales_data first_item(cin); // 使用 Sales_data(std::istream &is) ; 各成员值从输入流中读取
int main() {
// 使用默认构造函数 bookNo = "", cnt = 0, revenue = 0.0
Sales_data next;
// 使用 Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
Sales_data last("9-999-99999-9");
}
```
## 练习7.38
有些情况下我们希望提供`cin`作为接受`istream&`参数的构造函数的默认实参,请声明这样的构造函数。
解:
```cpp
Sales_data(std::istream &is = std::cin) { read(is, *this); }
```
## 练习7.39
如果接受`string`的构造函数和接受`istream&`的构造函数都使用默认实参,这种行为合法吗?如果不,为什么?
解:
不合法。当你调用`Sales_data()`构造函数时,无法区分是哪个重载。
## 练习7.40
从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。
```
(a) Book
(b) Data
(c) Employee
(d) Vehicle
(e) Object
(f) Tree
```
解:
(a) Book.
```cpp
class Book
{
public:
Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
explicit Book(std::istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
std::string name_;
std::string author_;
std::string pubdate_;
};
```
## 练习7.41
使用委托构造函数重新编写你的`Sales_data`类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建`Sales_data`对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。
解:
- [头文件](https://github.com/applenob/Cpp_Primer_Practice/tree/master/cpp_source/ch07/ex_7_41.h)
- [源文件](https://github.com/applenob/Cpp_Primer_Practice/tree/master/cpp_source/ch07/ex_7_41.cpp)
- [主函数](https://github.com/applenob/Cpp_Primer_Practice/tree/master/cpp_source/ch07/ex_7_41_main.cpp)
总结:使用委托构造函数,调用顺序是:
- 1.实际的构造函数的函数体。
- 2.委托构造函数的函数体。
## 练习7.42
对于你在练习7.40中编写的类,确定哪些构造函数可以使用委托。如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。
解:
```cpp
class Book
{
public:
Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
Book(unsigned isbn) : Book(isbn, "", "", "") {}
explicit Book(std::istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
std::string name_;
std::string author_;
std::string pubdate_;
};
```
## 练习7.43
假定有一个名为`NoDefault`的类,它有一个接受`int`的构造函数,但是没有默认构造函数。定义类`C`,`C`有一个 `NoDefault`类型的成员,定义`C`的默认构造函数。
```cpp
class NoDefault {
public:
NoDefault(int i) { }
};
class C {
public:
C() : def(0) { }
private:
NoDefault def;
};
```
## 练习7.44
下面这条声明合法吗?如果不,为什么?
```cpp
vector<NoDefault> vec(10);//vec初始化有10个元素
```
解:
不合法。因为`NoDefault`没有默认构造函数。
## 练习7.45
如果在上一个练习中定义的vector的元素类型是C,则声明合法吗?为什么?
合法。因为`C`有默认构造函数。
## 练习7.46
下面哪些论断是不正确的?为什么?
- (a) 一个类必须至少提供一个构造函数。
- (b) 默认构造函数是参数列表为空的构造函数。
- (c) 如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数。
- (d) 如果类没有定义默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。
解:
- (a) 不正确。如果我们的类没有显式地定义构造函数,那么编译器就会为我们隐式地定义一个默认构造函数,并称之为合成的默认构造函数。
- (b) 不完全正确。为每个参数都提供了默认值的构造函数也是默认构造函数。
- (c) 不正确。哪怕没有意义的值也需要初始化。
- (d) 不正确。只有当一个类没有定义**任何构造函数**的时候,编译器才会生成一个默认构造函数。
## 练习7.47
说明接受一个`string`参数的`Sales_data`构造函数是否应该是`explicit`的,并解释这样做的优缺点。
解:
是否需要从`string`到`Sales_data`的转换依赖于我们对用户使用该转换的看法。在此例中,这种转换可能是对的。`null_book`中的`string`可能表示了一个不存在的`ISBN`编号。
优点:
可以抑制构造函数定义的隐式转换
缺点:
为了转换要显式地使用构造函数
## 练习7.48
假定`Sales_data`的构造函数不是`explicit`的,则下述定义将执行什么样的操作?
解:
```cpp
string null_isbn("9-999-9999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
```
这些定义和是不是`explicit`的无关。
## 练习7.49
对于`combine`函数的三种不同声明,当我们调用`i.combine(s)`时分别发生什么情况?其中`i`是一个`Sales_data`,而` s`是一个`string`对象。
解:
```cpp
(a) Sales_data &combine(Sales_data); // ok
(b) Sales_data &combine(Sales_data&); // error C2664: 无法将参数 1 从“std::string”转换为“Sales_data &” 因为隐式转换只有一次
(c) Sales_data &combine(const Sales_data&) const; // 该成员函数是const 的,意味着不能改变对象。而 combine函数的本意就是要改变对象
```
## 练习7.50
确定在你的`Person`类中是否有一些构造函数应该是`explicit` 的。
解:
```cpp
explicit Person(std::istream &is){ read(is, *this); }
```
## 练习7.51
`vector`将其单参数的构造函数定义成`explicit`的,而`string`则不是,你觉得原因何在?
假如我们有一个这样的函数:
```cpp
int getSize(const std::vector<int>&);
```
如果`vector`没有将单参数构造函数定义成`explicit`的,我们就可以这样调用:
```cpp
getSize(34);
```
很明显这样调用会让人困惑,函数实际上会初始化一个拥有34个元素的`vecto`r的临时量,然后返回34。但是这样没有任何意义。而`string`则不同,`string`的单参数构造函数的参数是`const char *`,因此凡是在需要用到`string`的地方都可以用` const char *`来代替(字面值就是`const char *`)。如:
```cpp
void print(std::string);
print("hello world");
```
## 练习7.52
使用2.6.1节的 `Sales_data` 类,解释下面的初始化过程。如果存在问题,尝试修改它。
```cpp
Sales_data item = {"987-0590353403", 25, 15.99};
```
解:
`Sales_data` 类不是聚合类,应该修改成如下:
```cpp
struct Sales_data {
std::string bookNo;
unsigned units_sold;
double revenue;
};
```
## 练习7.53
定义你自己的`Debug`。
解:
```cpp
class Debug {
public:
constexpr Debug(bool b = true) : hw(b), io(b), other(b) { }
constexpr Debug(bool h, bool i, bool o) : hw(r), io(i), other(0) { }
constexpr bool any() { return hw || io || other; }
void set_hw(bool b) { hw = b; }
void set_io(bool b) { io = b; }
void set_other(bool b) { other = b; }
private:
bool hw; // runtime error
bool io; // I/O error
bool other; // the others
};
```
## 练习7.54
`Debug`中以 `set_` 开头的成员应该被声明成`constexpr` 吗?如果不,为什么?
解:
不能。`constexpr`函数必须包含一个返回语句。
## 练习7.55
7.5.5节的`Data`类是字面值常量类吗?请解释原因。
解:
不是。因为`std::string`不是字面值类型。
## 练习7.56
什么是类的静态成员?它有何优点?静态成员与普通成员有何区别?
解:
与类本身相关,而不是与类的各个对象相关的成员是静态成员。静态成员能用于某些场景,而普通成员不能。
## 练习7.57
编写你自己的`Account`类。
解:
```cpp
class Account {
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double newRate) { interestRate = newRate; }
private:
std::string owner;
double amount;
static double interestRate;
static constexpr double todayRate = 42.42;
static double initRate() { return todayRate; }
};
double Account::interestRate = initRate();
```
## 练习7.58
下面的静态数据成员的声明和定义有错误吗?请解释原因。
```cpp
//example.h
class Example {
public:
static double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec(vecSize);
};
//example.c
#include "example.h"
double Example::rate;
vector<double> Example::vec;
```
解:
`rate`应该是一个**常量表达式**。而类内只能初始化整型类型的静态常量,所以不能在类内初始化`vec`。修改后如下:
```cpp
// example.h
class Example {
public:
static constexpr double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec;
};
// example.C
#include "example.h"
constexpr double Example::rate;
vector<double> Example::vec(Example::vecSize);
```
================================================
FILE: excersize/ch08.md
================================================
# 第八章 IO库
## 练习8.1
> 编写函数,接受一个`istream&`参数,返回值类型也是`istream&`。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。
解:
```cpp
std::istream& func(std::istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
```
## 练习8.2
> 测试函数,调用参数为`cin`。
解:
```cpp
#include <iostream>
using std::istream;
istream& func(istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
int main()
{
istream& is = func(std::cin);
std::cout << is.rdstate() << std::endl;
return 0;
}
```
## 练习8.3
> 什么情况下,下面的`while`循环会终止?
```cpp
while (cin >> i) /* ... */
```
解:
如`badbit`、`failbit`、`eofbit` 的任一个被置位,那么检测流状态的条件会失败。
## 练习8.4
> 编写函数,以读模式打开一个文件,将其内容读入到一个`string`的`vector`中,将每一行作为一个独立的元素存于`vector`中。
解:
```cpp
void ReadFileToVec(const string& fileName, vector<string>& vec)
{
ifstream ifs(fileName);
if (ifs)
{
string buf;
while (getline(ifs, buf))
vec.push_back(buf);
}
}
```
## 练习8.5
> 重写上面的程序,将每个单词作为一个独立的元素进行存储。
解:
```cpp
void ReadFileToVec(const string& fileName, vector<string>& vec)
{
ifstream ifs(fileName);
if (ifs)
{
string buf;
while (ifs >> buf)
vec.push_back(buf);
}
}
```
## 练习8.6
> 重写7.1.1节的书店程序,从一个文件中读取交易记录。将文件名作为一个参数传递给`main`。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.h"
using std::ifstream; using std::cout; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
```
## 练习8.7
> 修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给`main`函数。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.h"
using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
ofstream output(argv[2]);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
```
## 练习8.8
> 修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.h"
using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
ofstream output(argv[2], ofstream::app);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
```
## 练习8.9
> 使用你为8.1.2节第一个练习所编写的函数打印一个`istringstream`对象的内容。
解:
```cpp
#include <iostream>
#include <sstream>
using std::istream;
istream& func(istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
int main()
{
std::istringstream iss("hello");
func(iss);
return 0;
}
```
## 练习8.10
> 编写程序,将来自一个文件中的行保存在一个`vector`中。然后使用一个`istringstream`从`vector`读取数据元素,每次读取一个单词。
解:
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
using std::vector; using std::string; using std::ifstream; using std::istringstream; using std::cout; using std::endl; using std::cerr;
int main()
{
ifstream ifs("../data/book.txt");
if (!ifs)
{
cerr << "No data?" << endl;
return -1;
}
vector<string> vecLine;
string line;
while (getline(ifs, line))
vecLine.push_back(line);
for (auto &s : vecLine)
{
istringstream iss(s);
string word;
while (iss >> word)
cout << word << endl;
}
return 0;
}
```
## 练习8.11
> 本节的程序在外层`while`循环中定义了`istringstream`对象。如果`record`对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将`record`的定义移到`while`循环之外,验证你设想的修改方法是否正确。
解:
```cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using std::vector; using std::string; using std::cin; using std::istringstream;
struct PersonInfo {
string name;
vector<string> phones;
};
int main()
{
string line, word;
vector<PersonInfo> people;
istringstream record;
while (getline(cin, line))
{
PersonInfo info;
record.clear();
record.str(line);
record >> info.name;
while (record >> word)
info.phones.push_back(word);
people.push_back(info);
}
for (auto &p : people)
{
std::cout << p.name << " ";
for (auto &s : p.phones)
std::cout << s << " ";
std::cout << std::endl;
}
return 0;
}
```
## 练习8.12
> 我们为什么没有在`PersonInfo`中使用类内初始化?
解:
因为这里只需要聚合类就够了,所以没有必要在`PersionInfo`中使用类内初始化。
## 练习8.13
> 重写本节的电话号码程序,从一个命名文件而非`cin`读取数据。
解:
```cpp
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
using std::vector; using std::string; using std::cin; using std::istringstream;
using std::ostringstream; using std::ifstream; using std::cerr; using std::cout; using std::endl;
using std::isdigit;
struct PersonInfo {
string name;
vector<string> phones;
};
bool valid(const string& str)
{
return isdigit(str[0]);
}
string format(const string& str)
{
return str.substr(0,3) + "-" + str.substr(3,3) + "-" + str.substr(6);
}
int main()
{
ifstream ifs("../data/phonenumbers.txt");
if (!ifs)
{
cerr << "no phone numbers?" << endl;
return -1;
}
string line, word;
vector<PersonInfo> people;
istringstream record;
while (getline(ifs, line))
{
PersonInfo info;
record.clear();
record.str(line);
record >> info.name;
while (record >> word)
info.phones.push_back(word);
people.push_back(info);
}
for (const auto &entry : people)
{
ostringstream formatted, badNums;
for (const auto &nums : entry.phones)
if (!valid(nums)) badNums << " " << nums;
else formatted << " " << format(nums);
if (badNums.str().empty())
cout << entry.name << " " << formatted.str() << endl;
else
cerr << "input error: " << entry.name
<< " invalid number(s) " << badNums.str() << endl;
}
return 0;
}
```
## 练习8.14
> 我们为什么将`entry`和`nums`定义为`const auto&`?
解:
它们都是类类型,因此使用引用避免拷贝。
在循环当中不会改变它们的值,因此用`const`。
================================================
FILE: excersize/ch09.md
================================================
## 练习9.1
> 对于下面的程序任务,`vector`、`deque`和`list`哪种容器最为适合?解释你的选择的理由。如果没有哪一种容器优于其他容器,也请解释理由。
* (a) 读取固定数量的单词,将它们按字典序插入到容器中。我们将在下一章中看到,关联容器更适合这个问题。
* (b) 读取未知数量的单词,总是将单词插入到末尾。删除操作在头部进行。
* (c) 从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出。
解:
* (a) `list` ,因为需要频繁的插入操作。
* (b) `deque` ,总是在头尾进行插入、删除操作。
* (c) `vector` ,不需要进行插入删除操作。
## 练习9.2
> 定义一个`list`对象,其元素类型是`int`的`deque`。
解:
```cpp
std::list<std::deque<int>> l;
```
## 练习9.3
> 构成迭代器范围的迭代器有何限制?
解:
两个迭代器 `begin` 和 `end`需满足以下条件:
* 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置。
* 我们可以通过反复递增`begin`来到达`end`。换句话说,`end` 不在`begin`之前。
## 练习9.4
> 编写函数,接受一对指向`vector<int>`的迭代器和一个`int`值。在两个迭代器指定的范围中查找给定的值,返回一个布尔值来指出是否找到。
解:
```cpp
bool find(vector<int>::const_iterator begin, vector<int>::const_iterator end, int i)
{
while (begin++ != end)
{
if (*begin == i)
return true;
}
return false;
}
```
## 练习9.5
> 重写上一题的函数,返回一个迭代器指向找到的元素。注意,程序必须处理未找到给定值的情况。
解:
```cpp
vector<int>::const_iterator find(vector<int>::const_iterator begin, vector<int>::const_iterator end, int i)
{
while (begin != end)
{
if (*begin == i)
return begin;
++begin;
}
return end;
}
```
## 练习9.6
> 下面的程序有何错误?你应该如何修改它?
```cpp
list<int> lst1;
list<int>::iterator iter1 = lst1.begin(),
iter2 = lst1.end();
while (iter1 < iter2) /* ... */
```
解:
修改成如下:
```cpp
while (iter1 != iter2)
```
## 练习9.7
> 为了索引`int`的`vector`中的元素,应该使用什么类型?
解:
```cpp
vector<int>::size_type
```
## 练习9.8
> 为了读取`string`的`list`中的元素,应该使用什么类型?如果写入`list`,又应该使用什么类型?
解:
```cpp
list<string>::const_iterator // 读
list<string>::iterator // 写
```
## 练习9.9
> `begin`和`cbegin`两个函数有什么不同?
解:
`begin` 返回的是普通迭代器,`cbegin` 返回的是常量迭代器。
## 练习9.10
> 下面4个对象分别是什么类型?
```cpp
vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
```
解:
`it1` 是 `vector<int>::iterator`
`it2`,`it3` 和 `it4` 是 `vector<int>::const_iterator`
## 练习9.11
> 对6种创建和初始化`vector`对象的方法,每一种都给出一个实例。解释每个`vector`包含什么值。
解:
```cpp
vector<int> vec; // 0
vector<int> vec(10); // 10个0
vector<int> vec(10, 1); // 10个1
vector<int> vec{ 1, 2, 3, 4, 5 }; // 1, 2, 3, 4, 5
vector<int> vec(other_vec); // 拷贝 other_vec 的元素
vector<int> vec(other_vec.begin(), other_vec.end()); // 拷贝 other_vec 的元素
```
## 练习9.12
> 对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数,解释它们的不同。
解:
* 接受一个容器创建其拷贝的构造函数,必须容器类型和元素类型都相同。
* 接受两个迭代器创建拷贝的构造函数,只需要元素的类型能够相互转换,容器类型和元素类型可以不同。
## 练习9.13
> 如何从一个`list<int>`初始化一个`vector<double>`?从一个`vector<int>`又该如何创建?编写代码验证你的答案。
解:
```cpp
list<int> ilst(5, 4);
vector<int> ivc(5, 5);
vector<double> dvc(ilst.begin(), ilst.end());
vector<double> dvc2(ivc.begin(), ivc.end());
```
## 练习9.14
> 编写程序,将一个`list`中的`char *`指针元素赋值给一个`vector`中的`string`。
解:
```cpp
std::list<const char*> l{ "hello", "world" };
std::vector<std::string> v;
v.assign(l.cbegin(), l.cend());
```
## 练习9.15
> 编写程序,判定两个`vector<int>`是否相等。
解:
```cpp
std::vector<int> vec1{ 1, 2, 3, 4, 5 };
std::vector<int> vec2{ 1, 2, 3, 4, 5 };
std::vector<int> vec3{ 1, 2, 3, 4 };
std::cout << (vec1 == vec2 ? "true" : "false") << std::endl;
std::cout << (vec1 == vec3 ? "true" : "false") << std::endl;
```
## 练习9.16
> 重写上一题的程序,比较一个list<int>中的元素和一个vector<int>中的元素。
解:
```cpp
std::list<int> li{ 1, 2, 3, 4, 5 };
std::vector<int> vec2{ 1, 2, 3, 4, 5 };
std::vector<int> vec3{ 1, 2, 3, 4 };
std::cout << (std::vector
gitextract_hhvs5jci/
├── .gitignore
├── README.md
├── cpp_source/
│ ├── ch01/
│ │ ├── Sales_item.h
│ │ ├── ch01.cpp
│ │ └── data/
│ │ ├── add
│ │ ├── add_item
│ │ ├── book_sales
│ │ ├── mysum
│ │ └── occurs
│ ├── ch02/
│ │ ├── Sales_item.h
│ │ └── ch02.cpp
│ ├── ch03/
│ │ └── ch3.cpp
│ ├── ch04/
│ │ └── ch04.cpp
│ ├── ch05/
│ │ └── ch5.cpp
│ ├── ch06/
│ │ ├── Chapter6.h
│ │ ├── ch6.cpp
│ │ ├── fact.cpp
│ │ └── factMain.cpp
│ ├── ch07/
│ │ ├── ex_7_41
│ │ ├── ex_7_41.cpp
│ │ ├── ex_7_41.h
│ │ ├── ex_7_41_main.cpp
│ │ ├── screen
│ │ ├── screen.cpp
│ │ └── screen.h
│ ├── ch08/
│ │ └── ch8.cpp
│ ├── ch09/
│ │ ├── date
│ │ └── date.cpp
│ ├── ch10/
│ │ ├── biggies
│ │ ├── biggies.cpp
│ │ ├── inserter
│ │ └── inserter.cpp
│ ├── ch11/
│ │ ├── ex_11_3
│ │ ├── ex_11_3.cpp
│ │ ├── ex_11_4
│ │ └── ex_11_4.cpp
│ ├── ch12/
│ │ ├── ex_12_27
│ │ ├── ex_12_27.cpp
│ │ ├── ex_12_27.h
│ │ ├── ex_12_27_main.cpp
│ │ └── storyDataFile.txt
│ ├── ch13/
│ │ ├── ex_13_13
│ │ ├── ex_13_13.cpp
│ │ ├── ex_13_34_36_37.cpp
│ │ └── ex_13_34_36_37.h
│ ├── ch14/
│ │ ├── ex_14_44
│ │ └── ex_14_44.cpp
│ ├── ch15/
│ │ ├── ex_15_26/
│ │ │ ├── bulk_quote.cpp
│ │ │ ├── bulk_quote.h
│ │ │ ├── disc_quote.cpp
│ │ │ ├── disc_quote.h
│ │ │ ├── limit_quote.cpp
│ │ │ ├── limit_quote.h
│ │ │ ├── main.cpp
│ │ │ ├── quote.cpp
│ │ │ └── quote.h
│ │ └── text_query/
│ │ ├── StrBlob.h
│ │ ├── andquery.cpp
│ │ ├── andquery.h
│ │ ├── binaryquery.cpp
│ │ ├── binaryquery.h
│ │ ├── main.cpp
│ │ ├── notquery.cpp
│ │ ├── notquery.h
│ │ ├── orquery.cpp
│ │ ├── orquery.h
│ │ ├── query.cpp
│ │ ├── query.h
│ │ ├── query_base.cpp
│ │ ├── query_base.h
│ │ ├── queryresult.cpp
│ │ ├── queryresult.h
│ │ ├── storyDataFile.txt
│ │ ├── textquery.cpp
│ │ ├── textquery.h
│ │ ├── wordquery.cpp
│ │ └── wordquery.h
│ ├── ch16/
│ │ ├── ex_16_51
│ │ └── ex_16_51.cpp
│ └── ch17/
│ ├── ex_17_4.cpp
│ ├── ex_17_4_SalesData.cpp
│ └── ex_17_4_SalesData.h
├── excersize/
│ ├── ch01.md
│ ├── ch02.md
│ ├── ch03.md
│ ├── ch04.md
│ ├── ch05.md
│ ├── ch06.md
│ ├── ch07.md
│ ├── ch08.md
│ ├── ch09.md
│ ├── ch10.md
│ ├── ch11.md
│ ├── ch12.md
│ ├── ch13.md
│ ├── ch14.md
│ ├── ch15.md
│ ├── ch16.md
│ ├── ch17.md
│ ├── ch18.md
│ └── ch19.md
└── notes/
├── ch01.md
├── ch02.md
├── ch03.md
├── ch04.md
├── ch05.md
├── ch06.md
├── ch07.md
├── ch08.md
├── ch09.md
├── ch10.md
├── ch11.md
├── ch12.md
├── ch13.md
├── ch14.md
├── ch15.md
├── ch16.md
├── ch17.md
├── ch18.md
└── ch19.md
SYMBOL INDEX (142 symbols across 49 files)
FILE: cpp_source/ch01/Sales_item.h
function class (line 45) | class Sales_item {
function compareIsbn (line 75) | inline
FILE: cpp_source/ch01/ch01.cpp
function basic_io (line 9) | void basic_io(){
function basic_while (line 17) | void basic_while(){
function basic_for (line 27) | void basic_for(){
function basic_if (line 36) | void basic_if(){
function basic_cin (line 57) | void basic_cin(){
function q_1_3 (line 64) | void q_1_3(){
function q_1_4 (line 68) | void q_1_4(){
function q_1_5 (line 76) | void q_1_5(){
function q_1_9 (line 89) | void q_1_9(){
function q_1_10 (line 99) | void q_1_10(){
function q_1_11 (line 108) | void q_1_11(){
function q_1_16 (line 124) | void q_1_16(){
function count_num (line 131) | void count_num(){
function q_1_20 (line 151) | void q_1_20(){
function q_1_21 (line 155) | void q_1_21(){
function q_1_22 (line 165) | void q_1_22(){
function q_1_23 (line 175) | void q_1_23(){
function main (line 196) | int main() {
FILE: cpp_source/ch02/Sales_item.h
function class (line 45) | class Sales_item {
function compareIsbn (line 75) | inline
FILE: cpp_source/ch02/ch02.cpp
function basic_class (line 9) | void basic_class(){
function q_2_3 (line 15) | void q_2_3(){
type Sale_data (line 33) | struct Sale_data
function q_1_5_1 (line 40) | int q_1_5_1()
function q_1_5_2 (line 52) | int q_1_5_2()
function q_1_6 (line 80) | int q_1_6()
function main (line 129) | int main(){
FILE: cpp_source/ch03/ch3.cpp
function basic_getline (line 19) | void basic_getline(){
function basic_string (line 25) | void basic_string(){
function basic_vector (line 36) | void basic_vector(){
function basic_bieset (line 48) | void basic_bieset(){
function q_3_14 (line 60) | void q_3_14(){
function q_3_17 (line 68) | void q_3_17(){
function q_3_23 (line 87) | void q_3_23(){
function q_3_31 (line 97) | void q_3_31(){
function main (line 104) | int main(){
FILE: cpp_source/ch04/ch04.cpp
function q_4_28 (line 9) | int q_4_28()
function main (line 30) | int main(){
FILE: cpp_source/ch05/ch5.cpp
function divide (line 10) | int divide(int a, int b){
function main (line 20) | int main(){
FILE: cpp_source/ch06/ch6.cpp
function fact (line 11) | int fact(int i)
function interactive_fact (line 16) | void interactive_fact()
function str_subrange (line 31) | bool str_subrange(const string &str1, const string &str2){
function main (line 42) | int main()
FILE: cpp_source/ch06/fact.cpp
function fact (line 8) | int fact(int val)
function func (line 14) | int func()
FILE: cpp_source/ch06/factMain.cpp
function main (line 9) | int main()
FILE: cpp_source/ch07/ex_7_41.cpp
function Sales_data (line 11) | Sales_data& Sales_data::combine(const Sales_data& rhs)
function Sales_data (line 33) | Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
FILE: cpp_source/ch07/ex_7_41.h
function class (line 7) | class Sales_data {
FILE: cpp_source/ch07/ex_7_41_main.cpp
function main (line 4) | int main()
FILE: cpp_source/ch07/screen.cpp
function main (line 3) | int main()
FILE: cpp_source/ch07/screen.h
function class (line 7) | class Screen {
function get (line 16) | char get(pos r, pos c) const { return contents[r*width+c]; }
function Screen (line 21) | const Screen& display(std::ostream &os) const { do_display(os); return *...
function Screen (line 33) | inline Screen& Screen::move(pos r, pos c)
function Screen (line 39) | inline Screen& Screen::set(char c)
function Screen (line 45) | inline Screen& Screen::set(pos r, pos c, char ch)
FILE: cpp_source/ch08/ch8.cpp
function loading (line 10) | void loading(){
function file_io (line 18) | void file_io(){
function main (line 46) | int main(){
FILE: cpp_source/ch09/date.cpp
class My_date (line 6) | class My_date{
method My_date (line 10) | My_date(const string &s){
method print (line 68) | void print(){
function main (line 72) | int main()
FILE: cpp_source/ch10/biggies.cpp
function elimdups (line 7) | void elimdups(std::vector<std::string> &vs)
function biggies (line 14) | void biggies(std::vector<std::string> &vs, std::size_t sz)
function main (line 36) | int main()
FILE: cpp_source/ch10/inserter.cpp
function print (line 9) | void print(Sequence const& seq)
function main (line 16) | int main()
FILE: cpp_source/ch11/ex_11_3.cpp
function main (line 7) | int main(){
FILE: cpp_source/ch11/ex_11_4.cpp
function word_count_pro (line 7) | void word_count_pro(std::map<std::string, int>& m)
function main (line 22) | int main()
FILE: cpp_source/ch12/ex_12_27.cpp
function QueryResult (line 28) | QueryResult TextQuery::query(const string& s) const
FILE: cpp_source/ch12/ex_12_27.h
function class (line 13) | class TextQuery
function class (line 27) | class QueryResult
FILE: cpp_source/ch12/ex_12_27_main.cpp
function runQueries (line 8) | void runQueries(ifstream& infile)
function main (line 24) | int main()
FILE: cpp_source/ch13/ex_13_13.cpp
type X (line 5) | struct X {
method X (line 6) | X() { std::cout << "X()" << std::endl; }
method X (line 7) | X(const X&) { std::cout << "X(const X&)" << std::endl; }
method X (line 8) | X& operator=(const X&) { std::cout << "X& operator=(const X&)" << std:...
function f (line 12) | void f(const X &rx, X x)
function main (line 24) | int main()
FILE: cpp_source/ch13/ex_13_34_36_37.cpp
function swap (line 4) | void swap(Message &lhs, Message &rhs)
function Message (line 63) | Message &Message::operator=(const Message &rhs)
function swap (line 79) | void swap(Folder &lhs, Folder &rhs)
function Folder (line 114) | Folder &Folder::operator=(const Folder &rhs)
function main (line 129) | int main()
FILE: cpp_source/ch13/ex_13_34_36_37.h
function class (line 9) | class Message {
function class (line 35) | class Folder {
FILE: cpp_source/ch14/ex_14_44.cpp
function add (line 6) | int add(int i, int j) { return i + j; }
type Div (line 8) | struct Div { int operator ()(int i, int j) const { return i / j; } }
function main (line 20) | int main()
FILE: cpp_source/ch15/ex_15_26/bulk_quote.h
function class (line 5) | class Bulk_quote : public Disc_quote
FILE: cpp_source/ch15/ex_15_26/disc_quote.h
function class (line 5) | class Disc_quote : public Quote
FILE: cpp_source/ch15/ex_15_26/limit_quote.h
function class (line 6) | class Limit_quote : public Disc_quote
FILE: cpp_source/ch15/ex_15_26/main.cpp
function main (line 10) | int main()
FILE: cpp_source/ch15/ex_15_26/quote.h
function class (line 7) | class Quote
function virtual (line 50) | virtual double net_price(std::size_t n) const { return n * price; }
function virtual (line 51) | virtual void debug() const;
FILE: cpp_source/ch15/text_query/StrBlob.h
function class (line 40) | class StrBlob
function StrBlob (line 72) | inline
function class (line 77) | class StrBlobPtr
function std (line 100) | inline
function StrBlobPtr (line 121) | inline
function StrBlobPtr (line 130) | inline
function StrBlobPtr (line 140) | inline
function StrBlobPtr (line 147) | inline
function eq (line 156) | inline
function neq (line 169) | inline
FILE: cpp_source/ch15/text_query/andquery.h
function class (line 7) | class AndQuery : public BinaryQuery
function QueryResult (line 18) | QueryResult eval(const TextQuery &) const override
function Query (line 24) | inline Query operator& (const Query& lhs, const Query& rhs)
FILE: cpp_source/ch15/text_query/binaryquery.h
function class (line 13) | class BinaryQuery : public Query_base
FILE: cpp_source/ch15/text_query/main.cpp
function main (line 17) | int main()
FILE: cpp_source/ch15/text_query/notquery.h
function class (line 13) | class NotQuery : public Query_base
FILE: cpp_source/ch15/text_query/orquery.h
function class (line 6) | class OrQuery :public BinaryQuery
function QueryResult (line 15) | QueryResult eval(const TextQuery& )const override
FILE: cpp_source/ch15/text_query/query.h
function class (line 19) | class Query
FILE: cpp_source/ch15/text_query/query_base.h
function class (line 10) | class Query_base
FILE: cpp_source/ch15/text_query/queryresult.h
function class (line 32) | class QueryResult
FILE: cpp_source/ch15/text_query/textquery.cpp
function QueryResult (line 57) | QueryResult
FILE: cpp_source/ch15/text_query/textquery.h
function class (line 32) | class TextQuery
FILE: cpp_source/ch15/text_query/wordquery.h
function class (line 12) | class WordQuery : public Query_base
FILE: cpp_source/ch16/ex_16_51.cpp
function foo (line 6) | void foo(const T &t, const Args& ... rest){
function test_param_packet (line 11) | void test_param_packet(){
function main (line 22) | int main(){
FILE: cpp_source/ch17/ex_17_4.cpp
function findBook (line 21) | vector<matches> findBook(const vector<vector<Sales_data>> &files,
function reportResults (line 37) | void reportResults(istream &in, ostream &os,
function main (line 54) | int main(){
FILE: cpp_source/ch17/ex_17_4_SalesData.cpp
function Sales_data (line 21) | Sales_data&
function Sales_data (line 29) | Sales_data &Sales_data::operator =(const Sales_data &rhs)
function Sales_data (line 39) | Sales_data &Sales_data::operator =(const std::string &rhs)
function Sales_data (line 46) | Sales_data &Sales_data::operator +=(const Sales_data &rhs)
function Sales_data (line 54) | Sales_data
function istream (line 63) | istream&
function ostream (line 72) | ostream&
FILE: cpp_source/ch17/ex_17_4_SalesData.h
function class (line 8) | class Sales_data
function compareIsbn (line 82) | inline
Condensed preview — 120 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (550K chars).
[
{
"path": ".gitignore",
"chars": 88,
"preview": ".idea\ncmake-build-debug\ntest_in_file\ntest_out_file\nCMakeLists.txt\nmain\n.DS_Store\n.vscode"
},
{
"path": "README.md",
"chars": 1827,
"preview": "# Cpp Primer 笔记 & 答案\n\n## 简介\n\n《C++ Primer 中文版(第 5 版)》学习仓库,包括**笔记**和**课后练习答案**。\n\n## 环境\n\n- system: ubuntu 16.04\n- IDE: VS C"
},
{
"path": "cpp_source/ch01/Sales_item.h",
"chars": 4732,
"preview": "/*\n * This file contains code from \"C++ Primer, Fifth Edition\", by Stanley B.\n * Lippman, Josee Lajoie, and Barbara E. M"
},
{
"path": "cpp_source/ch01/ch01.cpp",
"chars": 4749,
"preview": "//\n// Created by cer\n// chapter 1\n// 开始\n\n#include <iostream>\n#include \"Sales_item.h\"\n\nvoid basic_io(){\n std::cout << "
},
{
"path": "cpp_source/ch01/data/add",
"chars": 5,
"preview": "3 7 \n"
},
{
"path": "cpp_source/ch01/data/add_item",
"chars": 44,
"preview": "0-201-78345-X 3 20.00\n0-201-78345-X 2 25.00\n"
},
{
"path": "cpp_source/ch01/data/book_sales",
"chars": 224,
"preview": "0-201-70353-X 4 24.99\n0-201-82470-1 4 45.39\n0-201-88954-4 2 15.00 \n0-201-88954-4 5 12.00 \n0-201-88954-4 7 12.00 \n0-201-8"
},
{
"path": "cpp_source/ch01/data/mysum",
"chars": 8,
"preview": "3 4 5 6\n"
},
{
"path": "cpp_source/ch01/data/occurs",
"chars": 36,
"preview": "42 42 42 42 42 55 55 62 100 100 100\n"
},
{
"path": "cpp_source/ch02/Sales_item.h",
"chars": 4732,
"preview": "/*\n * This file contains code from \"C++ Primer, Fifth Edition\", by Stanley B.\n * Lippman, Josee Lajoie, and Barbara E. M"
},
{
"path": "cpp_source/ch02/ch02.cpp",
"chars": 3359,
"preview": "//\n// Created by cer\n// chapter 2\n// 变量和基本类型\n\n#include <iostream>\n#include \"Sales_item.h\"\n\nvoid basic_class(){\n Sales"
},
{
"path": "cpp_source/ch03/ch3.cpp",
"chars": 2023,
"preview": "//\n// Created by cer\n// chapter 3\n// 字符串、向量和数组\n\n#include <iostream>\n#include <vector>\n#include <string>\n#include <bitset"
},
{
"path": "cpp_source/ch04/ch04.cpp",
"chars": 940,
"preview": "//\n// chapter 4\n// 表达式\n\n#include <iostream>\n\nusing namespace std;\n\nint q_4_28()\n{\n cout << \"bool:\\t\\t\" << sizeof(bool"
},
{
"path": "cpp_source/ch05/ch5.cpp",
"chars": 462,
"preview": "//\n// Created by cer on 17-9-18.\n// chapter 5\n// 语句\n\n#include <iostream>\n#include <vector>\nusing namespace std;\n\nint div"
},
{
"path": "cpp_source/ch06/Chapter6.h",
"chars": 254,
"preview": "//\n// Created by cer on 19-1-20.\n//\n\n#ifndef CPP_PRIMER_PRACTICE_CHAPTER6_H\n#define CPP_PRIMER_PRACTICE_CHAPTER6_H\n\nint "
},
{
"path": "cpp_source/ch06/ch6.cpp",
"chars": 908,
"preview": "//\n// Created by cer on 17-9-19.\n// chapter 06\n// 函数\n\n#include <iostream>\n#include <string>\n\nusing namespace std\n\nint fa"
},
{
"path": "cpp_source/ch06/fact.cpp",
"chars": 315,
"preview": "//\n// Created by cer on 19-1-20.\n//\n\n#include \"Chapter6.h\"\n#include <iostream>\n\nint fact(int val)\n{\n if (val == 0 || "
},
{
"path": "cpp_source/ch06/factMain.cpp",
"chars": 227,
"preview": "//\n// Created by cer on 19-1-20.\n//\n\n\n#include \"Chapter6.h\"\n#include <iostream>\n\nint main()\n{\n std::cout << \"5! is \" "
},
{
"path": "cpp_source/ch07/ex_7_41.cpp",
"chars": 832,
"preview": "#include \"ex_7_41.h\"\n\n// constructor\nSales_data::Sales_data(std::istream &is) : Sales_data()\n{\n std::cout << \"Sales_d"
},
{
"path": "cpp_source/ch07/ex_7_41.h",
"chars": 1376,
"preview": "#ifndef CP5_ex7_41_h\n#define CP5_ex7_41_h\n\n#include <string>\n#include <iostream>\n\nclass Sales_data {\n friend std::ist"
},
{
"path": "cpp_source/ch07/ex_7_41_main.cpp",
"chars": 602,
"preview": "#include \"ex_7_41.h\"\nusing std::cout; using std::endl;\n\nint main()\n{\n cout << \"1. default way: \" << endl;\n cout <<"
},
{
"path": "cpp_source/ch07/screen.cpp",
"chars": 215,
"preview": "# include \"screen.h\"\n\nint main()\n{\n Screen myScreen(5, 5, 'X');\n myScreen.move(4, 0).set('#').display(std::cout);\n"
},
{
"path": "cpp_source/ch07/screen.h",
"chars": 1178,
"preview": "#ifndef CH07_screen\n#define CH07_screen\n\n#include <string>\n#include <iostream>\n\nclass Screen {\npublic:\n using pos = s"
},
{
"path": "cpp_source/ch08/ch8.cpp",
"chars": 981,
"preview": "// chapter 08\n// 标准IO库\n\n#include <iostream>\n#include <unistd.h>\n#include <fstream>\n\nusing namespace std;\n\nvoid loading()"
},
{
"path": "cpp_source/ch09/date.cpp",
"chars": 2200,
"preview": "#include <iostream>\n#include <string>\n#include <vector>\n\nusing namespace std;\nclass My_date{\nprivate:\n unsigned year,"
},
{
"path": "cpp_source/ch10/biggies.cpp",
"chars": 1102,
"preview": "#include <iostream>\n#include <string>\n#include <vector>\n#include <algorithm>\n\n// from ex 10.9\nvoid elimdups(std::vector<"
},
{
"path": "cpp_source/ch10/inserter.cpp",
"chars": 767,
"preview": "#include <iostream>\n#include <algorithm>\n#include <vector>\n#include <list>\n#include <iterator>\nusing std::list; using st"
},
{
"path": "cpp_source/ch11/ex_11_3.cpp",
"chars": 308,
"preview": "#include <string>\n#include <map>\n#include <iostream>\n\nusing namespace std;\n\nint main(){\n map<string, int> word_count;"
},
{
"path": "cpp_source/ch11/ex_11_4.cpp",
"chars": 495,
"preview": "#include <iostream>\n#include <map>\n#include <string>\n#include <algorithm>\n#include <cctype>\n\nvoid word_count_pro(std::ma"
},
{
"path": "cpp_source/ch12/ex_12_27.cpp",
"chars": 1148,
"preview": "#include \"ex_12_27.h\"\n#include <sstream>\n#include <fstream>\n#include <vector>\n#include <string>\n\nusing namespace std;\n\nT"
},
{
"path": "cpp_source/ch12/ex_12_27.h",
"chars": 1014,
"preview": "#ifndef EX12_27_H\n#define EX12_27_H\n\n#include <fstream>\n#include <memory>\n#include <vector>\n#include <string>\n#include <"
},
{
"path": "cpp_source/ch12/ex_12_27_main.cpp",
"chars": 528,
"preview": "#include <iostream>\n#include <string>\n#include <fstream>\n#include \"ex_12_27.h\"\n\nusing namespace std;\n\nvoid runQueries(if"
},
{
"path": "cpp_source/ch12/storyDataFile.txt",
"chars": 85,
"preview": "Hello world!\nThis is a funny trip for learning c++.\nI am Javen Chen.\nNice to see you."
},
{
"path": "cpp_source/ch13/ex_13_13.cpp",
"chars": 920,
"preview": "#include <iostream>\n#include <vector>\n#include <initializer_list>\n\nstruct X {\n X() { std::cout << \"X()\" << std::endl;"
},
{
"path": "cpp_source/ch13/ex_13_34_36_37.cpp",
"chars": 3331,
"preview": "#include \"ex_13_34_36_37.h\"\n#include <iostream>\n\nvoid swap(Message &lhs, Message &rhs) \n{\n using std::swap;\n lhs.r"
},
{
"path": "cpp_source/ch13/ex_13_34_36_37.h",
"chars": 1189,
"preview": "#ifndef CP5_ex13_34_36_37_h\n#define CP5_ex13_34_36_37_h\n\n#include <string>\n#include <set>\n\nclass Folder;\n\nclass Message "
},
{
"path": "cpp_source/ch14/ex_14_44.cpp",
"chars": 854,
"preview": "#include <iostream>\n#include <string>\n#include <map> \n#include <functional> \n\nint add(int i, int j) { return i + j; }\nau"
},
{
"path": "cpp_source/ch15/ex_15_26/bulk_quote.cpp",
"chars": 330,
"preview": "#include \"bulk_quote.h\"\n\ndouble Bulk_quote::net_price(std::size_t n) const\n{\n return n * price * ( n >= quantity ? 1 "
},
{
"path": "cpp_source/ch15/ex_15_26/bulk_quote.h",
"chars": 1391,
"preview": "#ifndef BULK_QUOTE_H\n#define BULK_QUOTE_H\n#include \"disc_quote.h\"\n\nclass Bulk_quote : public Disc_quote\n{\n\npublic:\n B"
},
{
"path": "cpp_source/ch15/ex_15_26/disc_quote.cpp",
"chars": 23,
"preview": "#include \"disc_quote.h\""
},
{
"path": "cpp_source/ch15/ex_15_26/disc_quote.h",
"chars": 1970,
"preview": "#ifndef DISC_QUOTE_H\n#define DISC_QUOTE_H\n\n#include \"quote.h\"\nclass Disc_quote : public Quote\n{\n friend bool operator"
},
{
"path": "cpp_source/ch15/ex_15_26/limit_quote.cpp",
"chars": 224,
"preview": "#include \"limit_quote.h\"\n\n\nvoid Limit_quote::debug() const\n{\n std::cout //<< \"data members of this class:\\n\"\n "
},
{
"path": "cpp_source/ch15/ex_15_26/limit_quote.h",
"chars": 445,
"preview": "#ifndef LIMIT_QUOTE_H\n#define LIMIT_QUOTE_H\n\n#include \"disc_quote.h\"\n\nclass Limit_quote : public Disc_quote\n{\npublic:\n "
},
{
"path": "cpp_source/ch15/ex_15_26/main.cpp",
"chars": 250,
"preview": "#include <iostream>\n#include <string>\n\n#include \"quote.h\"\n#include \"bulk_quote.h\"\n#include \"limit_quote.h\"\n#include \"dis"
},
{
"path": "cpp_source/ch15/ex_15_26/quote.cpp",
"chars": 200,
"preview": "#include \"quote.h\"\n\nvoid Quote::debug() const\n{\n std::cout //<< \"data members of this class:\\n\"\n << \"boo"
},
{
"path": "cpp_source/ch15/ex_15_26/quote.h",
"chars": 1657,
"preview": "#ifndef QUOTE_H\n#define QUOTE_H\n\n#include <string>\n#include <iostream>\n\nclass Quote\n{\n friend bool operator !=(const "
},
{
"path": "cpp_source/ch15/text_query/StrBlob.h",
"chars": 5073,
"preview": "/*\n * This file contains code from \"C++ Primer, Fifth Edition\", by Stanley B.\n * Lippman, Josee Lajoie, and Barbara E. M"
},
{
"path": "cpp_source/ch15/text_query/andquery.cpp",
"chars": 23,
"preview": "#include \"andquery.h\"\n\n"
},
{
"path": "cpp_source/ch15/text_query/andquery.h",
"chars": 646,
"preview": "#ifndef ANDQUERY_H\n#define ANDQUERY_H\n\n\n#include \"binaryquery.h\"\n\nclass AndQuery : public BinaryQuery\n{\n friend Query"
},
{
"path": "cpp_source/ch15/text_query/binaryquery.cpp",
"chars": 26,
"preview": "#include \"binaryquery.h\"\n\n"
},
{
"path": "cpp_source/ch15/text_query/binaryquery.h",
"chars": 784,
"preview": "#ifndef BINARYQUERY_H\n#define BINARYQUERY_H\n\n#include \"query_base.h\"\n#include \"query.h\"\n\n\n\n/**\n * @brief The BinaryQuery"
},
{
"path": "cpp_source/ch15/text_query/main.cpp",
"chars": 382,
"preview": "\n#include <iostream>\n#include <string>\n#include <vector>\n#include <memory>\n#include <fstream>\n\n#include \"queryresult.h\"\n"
},
{
"path": "cpp_source/ch15/text_query/notquery.cpp",
"chars": 22,
"preview": "#include \"notquery.h\"\n"
},
{
"path": "cpp_source/ch15/text_query/notquery.h",
"chars": 998,
"preview": "#ifndef NOTQUERY_H\n#define NOTQUERY_H\n#include \"query_base.h\"\n#include \"query.h\"\n\n\n/**\n * @brief The NotQuery class\n *\n "
},
{
"path": "cpp_source/ch15/text_query/orquery.cpp",
"chars": 22,
"preview": "#include \"orquery.h\"\n\n"
},
{
"path": "cpp_source/ch15/text_query/orquery.h",
"chars": 541,
"preview": "#ifndef ORQUERY_H\n#define ORQUERY_H\n\n#include \"binaryquery.h\"\n\nclass OrQuery :public BinaryQuery\n{\n friend Query oper"
},
{
"path": "cpp_source/ch15/text_query/query.cpp",
"chars": 21,
"preview": "#include \"query.h\"\n\n\n"
},
{
"path": "cpp_source/ch15/text_query/query.h",
"chars": 1374,
"preview": "#ifndef QUERY_H\n#define QUERY_H\n\n#include <iostream>\n#include <string>\n#include <memory>\n#include \"query_base.h\"\n#includ"
},
{
"path": "cpp_source/ch15/text_query/query_base.cpp",
"chars": 25,
"preview": "#include \"query_base.h\"\n\n"
},
{
"path": "cpp_source/ch15/text_query/query_base.h",
"chars": 603,
"preview": "#ifndef QUERY_BASE_H\n#define QUERY_BASE_H\n#include \"textquery.h\"\n#include \"queryresult.h\"\n\n/**\n * @brief abstract class "
},
{
"path": "cpp_source/ch15/text_query/queryresult.cpp",
"chars": 813,
"preview": "/***************************************************************************\n * @file queryresult.cpp\n * @author"
},
{
"path": "cpp_source/ch15/text_query/queryresult.h",
"chars": 1714,
"preview": "/***************************************************************************\n * @file queryresult.h\n * @author "
},
{
"path": "cpp_source/ch15/text_query/storyDataFile.txt",
"chars": 85,
"preview": "Hello world!\nThis is a funny trip for learning c++.\nI am Javen Chen.\nNice to see you."
},
{
"path": "cpp_source/ch15/text_query/textquery.cpp",
"chars": 2131,
"preview": "/***************************************************************************\n * @file textquery.cpp\n * @author "
},
{
"path": "cpp_source/ch15/text_query/textquery.h",
"chars": 1174,
"preview": "/***************************************************************************\n * @file textquery.h\n * @author "
},
{
"path": "cpp_source/ch15/text_query/wordquery.cpp",
"chars": 24,
"preview": "#include \"wordquery.h\"\n\n"
},
{
"path": "cpp_source/ch15/text_query/wordquery.h",
"chars": 806,
"preview": "#ifndef WORDQUERY_H\n#define WORDQUERY_H\n\n#include \"query_base.h\"\n\n/**\n * @brief The WordQuery class\n *The only class tha"
},
{
"path": "cpp_source/ch16/ex_16_51.cpp",
"chars": 480,
"preview": "#include <iostream>\n\nusing namespace std;\n\ntemplate <typename T, typename ... Args>\nvoid foo(const T &t, const Args& ..."
},
{
"path": "cpp_source/ch17/ex_17_4.cpp",
"chars": 1676,
"preview": "#include <iostream>\n#include <tuple>\n#include <string>\n#include <vector>\n#include <algorithm>\n#include <utility>\n#includ"
},
{
"path": "cpp_source/ch17/ex_17_4_SalesData.cpp",
"chars": 2329,
"preview": "#include <iostream>\nusing std::istream; using std::ostream;\n\n#include \"ex_17_4_SalesData.h\"\n\nSales_data::Sales_data(std:"
},
{
"path": "cpp_source/ch17/ex_17_4_SalesData.h",
"chars": 2227,
"preview": "#ifndef SALES_DATA_H\n#define SALES_DATA_H\n\n#include <string>\n#include <iostream>\n\n\nclass Sales_data\n{\n // friends\n "
},
{
"path": "excersize/ch01.md",
"chars": 8507,
"preview": "# 第一章 开始\n\n## 练习1.1\n查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第2页的main程序。\n\n解:\n- ``g++ --std=c++11 ch1.cpp -o main``\n- ``./main``\n\n##"
},
{
"path": "excersize/ch02.md",
"chars": 11383,
"preview": "# 第二章 变量和基本类型 \n\n## 练习2.1\n类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?\n\n解:\n\nC++ 规定 short "
},
{
"path": "excersize/ch03.md",
"chars": 18560,
"preview": "# 第三章 字符串、向量和数组\n\n## 练习3.1\n\n使用恰当的using 声明重做 1.4.1节和2.6.2节的练习。\n\n解:\n\n1.4.1\n\n```cpp\n#include <iostream>\n\nusing std::cin;\nusi"
},
{
"path": "excersize/ch04.md",
"chars": 9192,
"preview": "# 第四章 表达式\n\n## 练习4.1\n\n表达式`5 + 10 * 20 / 2`的求值结果是多少?\n\n解:\n\n等价于`5 + ((10 * 20) / 2) = 105`\n\n## 练习4.2\n\n根据4.12节中的表,在下述表达式的合理位置"
},
{
"path": "excersize/ch05.md",
"chars": 14985,
"preview": "# 第五章 语句\n\n## 练习5.1\n\n什么是空语句?什么时候会用到空语句?\n\n解:\n\n只含义一个单独的分号的语句是空语句。如:`;`。\n\n如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句。\n```cpp\nwhi"
},
{
"path": "excersize/ch06.md",
"chars": 15723,
"preview": "# 第六章 函数\n\n## 练习6.1\n实参和形参的区别的什么?\n\n解:\n\n实参是函数调用的实际值,是形参的初始值。\n\n## 练习6.2\n请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?\n\n```cpp\n(a) int f() {\n"
},
{
"path": "excersize/ch07.md",
"chars": 25292,
"preview": "# 第七章 类\n\n## 练习7.1\n\n使用2.6.1节定义的`Sales_data`类为1.6节的交易处理程序编写一个新版本。\n\n解:\n\n```cpp\n#include <iostream>\n#include <string>\nusing "
},
{
"path": "excersize/ch08.md",
"chars": 7487,
"preview": "# 第八章 IO库\n\n## 练习8.1\n> 编写函数,接受一个`istream&`参数,返回值类型也是`istream&`。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前"
},
{
"path": "excersize/ch09.md",
"chars": 20570,
"preview": "## 练习9.1\n\n> 对于下面的程序任务,`vector`、`deque`和`list`哪种容器最为适合?解释你的选择的理由。如果没有哪一种容器优于其他容器,也请解释理由。\n* (a) 读取固定数量的单词,将它们按字典序插入到容器中。我们"
},
{
"path": "excersize/ch10.md",
"chars": 20417,
"preview": "# 第十章 泛型算法\n\n## 练习10.1\n\n> 头文件`algorithm`中定义了一个名为`count`的函数,它类似`find`, 接受一对迭代器和一个值作为参数。`count`返回给定值在序列中出现的次数。编写程序,读取`int`序"
},
{
"path": "excersize/ch11.md",
"chars": 13123,
"preview": "# 第十一章 关联容器\n\n## 练习11.1\n\n> 描述`map`和`vector`的不同。\n\n解:\n\n`map` 是关联容器, `vector` 是顺序容器。\n\n## 练习11.2\n\n> 分别给出最适合使用`list`、`vector`、"
},
{
"path": "excersize/ch12.md",
"chars": 18541,
"preview": "# 第十二章 动态内存\n\n## 练习12.1\n\n> 在此代码的结尾,`b1` 和 `b2` 各包含多少个元素?\n\n```cpp\nStrBlob b1;\n{\n\tStrBlob b2 = {\"a\", \"an\", \"the\"};\n\tb1 = b2"
},
{
"path": "excersize/ch13.md",
"chars": 31354,
"preview": "# 第十三章 拷贝控制\n\n## 练习13.1\n\n> 拷贝构造函数是什么?什么时候使用它?\n\n解:\n\n如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。当使用**拷贝初始化**时,我们会用到"
},
{
"path": "excersize/ch14.md",
"chars": 38915,
"preview": "# 第十四章 重载运算与类型转换\n\n## 练习14.1\n\n> 在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?\n\n解:\n\n我们可以直接调用重载运算符函数。重置运算符与内置运算符有一样的优先级与结合性"
},
{
"path": "excersize/ch15.md",
"chars": 23714,
"preview": "# 第十五章 面向对象程序设计\n\n## 练习15.1\n\n> 什么是虚成员?\n\n解:\n\n对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。\n\n## 练习15.2\n\n> `protected` 访问说明符"
},
{
"path": "excersize/ch16.md",
"chars": 31174,
"preview": "## 练习16.1\n\n> 给出实例化的定义。\n\n解:\n\n当编译器实例化一个模版时,它使用实际的模版参数代替对应的模版参数来创建出模版的一个新“实例”。\n\n## 练习16.2\n\n> 编写并测试你自己版本的 `compare` 函数。\n\n解:\n"
},
{
"path": "excersize/ch17.md",
"chars": 22000,
"preview": "## 练习17.1\n\n> 定义一个保存三个`int`值的 `tuple`,并将其成员分别初始化为10、20和30。\n\n解:\n\n```cpp\nauto t = tuple<int, int, int>{10, 20, 30};\n```\n\n##"
},
{
"path": "excersize/ch18.md",
"chars": 7173,
"preview": "## 练习18.1\n\n> 在下列 `throw` 语句中异常对象的类型是什么?\n```cpp\n(a) range_error r(\"error\");\n\tthrow r;\n(b) exception *p = &r;\n\tthrow *p;\n`"
},
{
"path": "excersize/ch19.md",
"chars": 2487,
"preview": "## 练习19.1\n\n> 使用 malloc 编写你自己的 operator new(sizt_t)函数,使用 free 编写operator delete(void *)函数。\n\n## 练习19.2\n\n> 默认情况下,allocator "
},
{
"path": "notes/ch01.md",
"chars": 5012,
"preview": "# 第一章 开始\n\n## 熟悉编译器\n\n**g++**:\n\n- 编译:`g++ --std=c++11 ch01.cpp -o main`\n- 运行:`./prog1`\n- 查看运行状态:`echo $?`\n- 编译多个文件:`g++ ch"
},
{
"path": "notes/ch02.md",
"chars": 5714,
"preview": "# 第二章 变量和基本类型\n\n任何常用的编程语言都具备一组公共的语法特征,最基本的特征包括:\n\n- 整型、字符型等内置类型\n- 变量,用来为对象命名\n- 表达式和语句,用于操作上述数据类型的具体值\n- if 或 while 等控制结构,有选"
},
{
"path": "notes/ch03.md",
"chars": 6281,
"preview": "# 第三章 字符串、向量和数组\n\n## using声明\n- 使用某个命名空间:例如 `using std::cin`表示使用命名空间`std`中的名字`cin`。\n- 头文件中不应该包含`using`声明。这样使用了该头文件的源码也会使用这"
},
{
"path": "notes/ch04.md",
"chars": 3273,
"preview": "# 第四章 表达式\n\n## 表达式基础\n\n- **运算对象转换**:小整数类型会被提升为较大的整数类型\n- **重载运算符**:当运算符作用在类类型的运算对象时,用户可以自行定义其含义。\n- **左值和右值**:\n - C中原意:左值"
},
{
"path": "notes/ch05.md",
"chars": 863,
"preview": "# 第五章 语句\n\n## 简单语句\n\n- **表达式语句**:一个表达式末尾加上分号,就变成了表达式语句。\n- **空语句**:只有一个单独的分号。\n- **复合语句(块)**:用花括号 `{}`包裹起来的语句和声明的序列。一个块就是一个作"
},
{
"path": "notes/ch06.md",
"chars": 4426,
"preview": "# 第六章 函数\n\n## 函数基础\n\n- **函数定义**:包括返回类型、函数名字和0个或者多个**形参**(parameter)组成的列表和函数体。\n- **调用运算符**:调用运算符的形式是一对圆括号 `()`,作用于一个表达式,该表达"
},
{
"path": "notes/ch07.md",
"chars": 3636,
"preview": "# 第七章 类 (Class)\n\n## 定义抽象数据类型\n\n- **类背后的基本思想**:**数据抽象**(data abstraction)和**封装**(encapsulation)。\n- 数据抽象是一种依赖于**接口**(interf"
},
{
"path": "notes/ch08.md",
"chars": 2775,
"preview": "# 第八章 IO库\n\n## 前面章节已经在用的IO库设施\n\n- **istream**:输入流类型,提供输入操作。\n- **ostream**:输出流类型,提供输出操作\n- **cin**:一个`istream`对象,从标准输入读取数据。\n"
},
{
"path": "notes/ch09.md",
"chars": 10252,
"preview": "# 第九章 顺序容器\n\n## 顺序容器概述\n\n- **顺序容器**(sequential container):为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。\n\n### 顺序容器类型"
},
{
"path": "notes/ch10.md",
"chars": 6775,
"preview": "# 第十章 泛型算法\n\n## 泛型算法\n\n- 因为它们实现共同的操作,所以称之为“**算法**”;而“**泛型**”、指的是它们可以操作在多种容器类型上。\n- 泛型算法本身不执行容器操作,只是单独依赖迭代器和迭代器操作实现。\n- 头文件: "
},
{
"path": "notes/ch11.md",
"chars": 4185,
"preview": "# 第十一章 关联容器\n\n- 关联容器和顺序容器的不同:关联容器中的元素时按照**关键字**来保存和访问的。\n- 关联容器支持通过关键字来高效地查找和读取元素,基本的关联容器类型是 `map`和 `set`。\n\n**关联容器类型**:\n\n|"
},
{
"path": "notes/ch12.md",
"chars": 5184,
"preview": "# 第十二章 动态内存\n\n- 对象的生命周期:\n - 全局对象在程序启动时分配,结束时销毁。\n - 局部对象在进入程序块时创建,离开块时销毁。\n - 局部`static`对象在第一次使用前分配,在程序结束时销毁。\n - 动态分配对象"
},
{
"path": "notes/ch13.md",
"chars": 2445,
"preview": "# 第十三章 拷贝控制\n\n**拷贝控制操作**(copy control):\n\n- 拷贝构造函数(copy constructor)\n- 拷贝赋值运算符(copy-assignment operator)\n- 移动构造函数(move con"
},
{
"path": "notes/ch14.md",
"chars": 3663,
"preview": "# 第十四章 重载运算与类型转换\n\n## 基本概念\n\n- 重载运算符是具有特殊名字的函数:由关键字`operator`和其后要定义的运算符号共同组成。\n- 当一个重载的运算符是成员函数时,`this`绑定到左侧运算对象。动态运算符符函数的参"
},
{
"path": "notes/ch15.md",
"chars": 4452,
"preview": "# 第十五章 面向对象程序设计\n\n## OOP:概述\n\n- 面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承和动态绑定。\n- **继承**(inheritance):\n - 通过继承联系"
},
{
"path": "notes/ch16.md",
"chars": 4551,
"preview": "# 第十六章 模板和泛型编程\n\n- 面向对象编程和泛型编程都能处理在编写程序时不知道类型的情况。\n - OOP能处理类型在程序运行之前都未知的情况;\n - 泛型编程中,在编译时就可以获知类型。\n\n## 定义模板\n\n- **模板**:模板"
},
{
"path": "notes/ch17.md",
"chars": 11469,
"preview": "# 第十七章 标准库特殊设施\n\n## tuple类型\n\n- `tuple`是类似`pair`的模板,每个成员类型都可以不同,但`tuple`可以有任意数量的成员。\n- 但每个确定的`tuple`类型的成员数目是固定的。\n- 我们可以将`tu"
},
{
"path": "notes/ch18.md",
"chars": 3210,
"preview": "# 第十八章 用于大型程序的工具\n\n大规模应用程序的特殊要求包括:\n\n- 在独立开发的子系统之间协同处理错误的能力。\n- 使用各种库进行协同开发的能力。\n- 对比较复杂的应用概念建模的能力。\n\n## 异常处理\n\n**异常处理**(excep"
},
{
"path": "notes/ch19.md",
"chars": 8018,
"preview": "# 第十九章 特殊工具与技术\n\n## 控制内存分配\n\n### 重载new和delete\n\n* **`new`表达式的工作机理**:\n\n```c++\nstring *sp = new string(\"a value\"); //分配并初始化一个"
}
]
// ... and 11 more files (download for full content)
About this extraction
This page contains the full source code of the applenob/Cpp_Primer_Practice GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 120 files (499.3 KB), approximately 204.3k tokens, and a symbol index with 142 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.