1. c++ primer 第一章习题
1.1 练习1.3
#include<iostream>int main(){std::cout << "Hello World!\n";return 0;
}
下图来自:Hello World
注意:别为了方便cout前不写std::而直接用 using namespace std; 把所有symbols都引入,而是写为 std::cout
写完后编译源码形成可执行文件
编译时可以加入一些参数
编译和执行过程展示
1.2 练习1.4
#include<iostream>int main(){//初始化两个整数int n1,n2;std::cout << "Input two integer:";//输入两个整数std::cin >> n1 >> n2;//两个整数相乘std::cout << "product of two integer:" << n1*n2 << '\n';return 0;
}
下图来自:Input & Output (Basics)
注意:别每次输出都用std::endl结尾,每次调用 std::endl 都会刷新输出缓冲区并立即写入输出,这会造成性能退化
注意:
仅对运算符 << 进行一次调用(每次额外的调用都会产生少量开销)
仅当您绝对需要确保某些输出需要立即实现时才使用 std::endl
1.3 练习1.6
1.4 练习1.7
注意:嵌套注释是非法的
1.5 练习1.9
#include<iostream>int main(){int i=50,result=0;while (i<=100){result+=i;i++;}std::cout << "sum of 50 to 100:" << result << std::endl;return 0;
}
1.6 练习1.10
#include<iostream>int main(){int i=10;while (i>=0){std::cout << i << '\n';i--;}return 0;
}
1.7 练习 1.11
#include<iostream>int main(){int a=0,b=0;std::cout << "please input two integer:";std::cin >> a >> b;std::cout << "integer from " << a << " to " << b << ": ";for (size_t i = a; i <= b; i++){std::cout << i << " ";}std::cout << std::endl;return 0;
}
注意:为避免bugs,当我们在声明变量时要进行初始化
注意:要充分考虑用户输入的各种情况
1.8 练习 1.13
1.4.1节中的练习包括:1.9、1.10、1.11
用for重写练习1.9
#include<iostream>int main(){int sum = 0;for (size_t i = 50; i <= 100; i++){sum += i;}std::cout << "The sum of 50 to 100 is " << sum << std::endl;return 0;
}
什么时候使用size_t?
数组下标、循环计数、指定分配内存大小等等
用for重写练习1.10
#include<iostream>int main(){for (size_t i = 10; i >= 0; i--){std::cout << i << '\n';}std::cout << std::endl;return 0;
}
打印出来的不是10到0,而是一堆大整数
原因:当前的代码会导致无限循环,因为 size_t 是无符号整数,当 i 减到 0 时再减 1 会导致它变成一个非常大的正数。
解决:去掉等于0
#include<iostream>int main(){for (size_t i = 10; i > 0; i--) //去掉=0{std::cout << i << '\n';}std::cout << std::endl;return 0;
}
参考答案:
用for重写练习1.11
#include<iostream>int main(){int a=0,b=0;std::cout<<"Enter two numbers:";std::cin>>a>>b;if (a<=b){for (size_t i = a; i <= b; i++){std::cout<<i<<'\n';}}else{for (size_t i = b; i <= a; i++){std::cout<<i<<'\n';}}std::cout<<std::endl;return 0;
}
什么时候使用std::endl?
参考答案:
1.9 练习 1.14
1.10 练习 1.15
1.11 练习 1.16
#include<iostream>
int main(){int number=0,sum=0;std::cout<<"Enter numbers (Add new line in the end):"<<std::endl;while (std::cin>>number) //当输入流成功读取一个整数时,循环继续{sum+=number;}std::cout<<"the sum of these numbers:"<< sum <<std::endl;return 0;
}
参考答案:
1.12 练习 1.20
cpp-primer/sales_item.h
Sales_item.h
/*
#ifndef 和 #endif 是为了防止多次包含相同的头文件。这种技术称为“包含保护”或“头文件保护”。它的目的是避免重复定义和编译错误。
第一次包含:
当头文件第一次被包含时,SALESITEM_H 未定义,因此 #ifndef SALESITEM_H 条件为真,编译器会处理接下来的代码。
#define SALESITEM_H 定义了 SALESITEM_H,以便下次包含该文件时,#ifndef SALESITEM_H 条件为假。
后续包含:
当头文件再次被包含时,SALESITEM_H 已定义,因此 #ifndef SALESITEM_H 条件为假,编译器会跳过 #ifndef 和 #endif 之间的所有代码。
*/
#ifndef SALESITEM_H // 如果 SALESITEM_H 未定义
#define SALESITEM_H // 定义 SALESITEM_H// 包含输入输出流库
#include <iostream>
// 包含字符串库
#include <string>// 定义 Sales_item 类
class Sales_item {// 声明友元函数,允许这些函数访问类的私有成员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:// 默认构造函数,用于初始化内置类型的成员Sales_item() = default;// 带参数的构造函数,用于初始化 bookNoSales_item(const std::string &book): bookNo(book) { }// 从输入流中读取数据的构造函数Sales_item(std::istream &is) { is >> *this; }
public:// 成员函数,重载 += 运算符Sales_item& operator+=(const Sales_item&);// 成员函数,返回书籍编号std::string isbn() const { return bookNo; }// 成员函数,计算平均价格double avg_price() const;
private:// 私有成员变量,书籍编号std::string bookNo; // 隐式初始化为空字符串// 私有成员变量,售出数量unsigned units_sold = 0; // 显式初始化// 私有成员变量,收入double revenue = 0.0;
};// 内联函数,比较两个 Sales_item 对象的 ISBN
inline
bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs)
{ return lhs.isbn() == rhs.isbn(); }// 非成员函数,重载 + 运算符
Sales_item operator+(const Sales_item&, const Sales_item&);// 内联函数,重载 == 运算符
inline bool
operator==(const Sales_item &lhs, const Sales_item &rhs)
{// 比较两个 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);
}// 成员函数,重载 += 运算符
// 假设两个对象都引用相同的 ISBN
Sales_item& Sales_item::operator+=(const Sales_item& rhs)
{// 累加售出数量和收入units_sold += rhs.units_sold; revenue += rhs.revenue; return *this;
}// 非成员函数,重载 + 运算符
// 假设两个对象都引用相同的 ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{// 创建一个临时对象 ret,初始化为 lhsSales_item ret(lhs); // 将 rhs 的内容添加到 retret += rhs; // 返回 retreturn ret;
}// 非成员函数,重载 >> 运算符
std::istream&
operator>>(std::istream& in, Sales_item& s)
{// 临时变量,用于存储价格double price;// 从输入流中读取书籍编号、售出数量和价格in >> s.bookNo >> s.units_sold >> price;// 检查输入是否成功if (in)// 计算收入s.revenue = s.units_sold * price;else // 输入失败,将对象重置为默认状态s = Sales_item(); 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 // 否则返回 0return 0;
}#endif // 结束 #ifndef 块
#include<iostream>
#include"Sales_item.h"
/*
book私有成员变量private:// 私有成员变量,书籍编号std::string bookNo; // 隐式初始化为空字符串// 私有成员变量,售出数量unsigned units_sold = 0; // 显式初始化// 私有成员变量,收入double revenue = 0.0;重载输入
// 非成员函数,重载 >> 运算符
std::istream&
operator>>(std::istream& in, Sales_item& s)
{// 临时变量,用于存储价格double price;// 从输入流中读取书籍编号、售出数量和价格in >> s.bookNo >> s.units_sold >> price;// 检查输入是否成功if (in)// 计算收入s.revenue = s.units_sold * price;else // 输入失败,将对象重置为默认状态s = Sales_item(); 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;
}
*/
int main(){//实例化类对象Sales_item book;//读取ISBN编号、售出数量和总价std::cout << "请输入书籍编号、售出数量和总价:" << std::endl;//读取多本书籍销售记录while(std::cin >> book){//将每条记录打印到标准输出上std::cout << "ISBN、售出数量、总价、单价" << '\n' << book << std::endl;}return 0;
}
参考答案:
1.13 练习 1.21
#include<iostream>
#include"Sales_item.h"
/*
book私有成员变量private:// 私有成员变量,书籍编号std::string bookNo; // 隐式初始化为空字符串// 私有成员变量,售出数量unsigned units_sold = 0; // 显式初始化// 私有成员变量,收入double revenue = 0.0;重载输入
// 非成员函数,重载 >> 运算符
std::istream&
operator>>(std::istream& in, Sales_item& s)
{// 临时变量,用于存储价格double price;// 从输入流中读取书籍编号、售出数量和价格in >> s.bookNo >> s.units_sold >> price;// 检查输入是否成功if (in)// 计算收入s.revenue = s.units_sold * price;else // 输入失败,将对象重置为默认状态s = Sales_item(); 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;
}重载+
// 非成员函数,重载 + 运算符
// 假设两个对象都引用相同的 ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{// 创建一个临时对象 ret,初始化为 lhsSales_item ret(lhs); // 将 rhs 的内容添加到 retret += rhs; // 返回 retreturn ret;
}
*/
int main(){//实例化类对象Sales_item book;Sales_item book2;//输出提示信息std::cout << "Please input two books' sales information:" << std::endl;//读取两本书的销售信息std::cin >> book >> book2;//检查两本书是否是同一本if(book.isbn() == book2.isbn()){//将每条记录打印到标准输出上std::cout << "ISBN、售出数量、总价、单价" << std::endl;//如果是同一本书,输出两本书的销售信息std::cout << book + book2 << std::endl;}else{//如果不是同一本书,输出错误信息std::cerr << "Data must refer to same ISBN" << std::endl;return -1;}return 0;
}
参考答案:
头文件中提供了比较两本书isbn的函数
// 内联函数,比较两个 Sales_item 对象的 ISBN
inline
bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs)
{ return lhs.isbn() == rhs.isbn(); }
头文件中重载了加号操作符
// 非成员函数,重载 + 运算符
// 假设两个对象都引用相同的 ISBN
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{// 创建一个临时对象 ret,初始化为 lhsSales_item ret(lhs); // 将 rhs 的内容添加到 retret += rhs; // 返回 retreturn ret;
}
1.14 练习 1.22
#include<iostream>
#include"Sales_item.h"int main() {Sales_item total; // 保存当前 ISBN 的总和std::cout << "Please input the sales record: " << std::endl;if (std::cin >> total) { // 读取第一条销售记录Sales_item trans; // 保存每一条销售记录while (std::cin >> trans) { // 读取剩余的销售记录if (compareIsbn(total,trans)) { // 检查 ISBN 是否相同total += trans; // 更新总和,相同书的销售记录合并} else {std::cout << "ISBN is different" << std::endl;}}std::cout << "Total for ISBN " << total.isbn() << ": " << total << std::endl; // 输出最后一组 ISBN 的总和} else {std::cerr << "No data?!" << std::endl; // 没有输入任何数据return -1; // 表示失败}return 0; // 表示成功
}
参考答案:
1.15 练习 1.23
#include <iostream>
#include "Sales_item.h"int main() {Sales_item currentBook, newBook;int count = 0;int cnt[100] = {0}; // 假设最多有 100 种不同的 ISBN// 读取 ISBN、售出数量和价格std::cout << "请输入书籍信息(Ctrl+D 结束输入):" << std::endl;if (std::cin >> currentBook) {cnt[count]++; // 统计第一条记录while (std::cin >> newBook) {if (compareIsbn(currentBook, newBook)) { // ISBN 相同cnt[count]++;} else { // ISBN 不同currentBook = newBook; // 更新当前书籍cnt[++count]++; // 统计新 ISBN 的记录}}} else {std::cerr << "No data?!" << std::endl;return -1; // 表示失败}// 打印每个 ISBN 的销售记录数量for (int i = 0; i <= count; i++) {std::cout << "ISBN " << i + 1 << " has " << cnt[i] << " records." << std::endl;}return 0; // 表示成功
}
参考答案:
#include <iostream>
#include "Sales_item.h"int main() {Sales_item trans1, trans2;int num = 1;std::cout << "输入多条销售记录"<< std::endl;if (std::cin>>trans1) {while (std::cin>>trans2) //读入后续交易记录{//检查两条交易记录是否有相同的ISBNif (compareIsbn(trans1, trans2)){num++;}else{ //ISBN不同std::cout << "ISBN:" << trans1.isbn() << "有" << num << "条销售记录" << std::endl; //打印相同书籍的销售记录trans1 = trans2; //将新书籍销售记录赋给trans1num = 1; //重置num}}std::cout << "ISBN:" << trans1.isbn() << "有" << num << "条销售记录" << std::endl;}else{ //没有读入数据std::cerr << "No data?!" << std::endl;return -1;}return 0;
}
每次比较两个书籍的ISBN,如果一样就num加一,再读入一条记录,比较两条记录,如果ISBN不一样,先打印之前的trans1的记录,然后将这条不一样的记录覆盖trans1,用于下一次比较,对应num置1