第一章 开始以及预备工作
注:本文所有代码全部在 Linux 系统下用 g++ 编译器进行编译运行。
我以前有学过 C++,但是总觉得一团乱麻,看这个视频学会一点,那个视频学一点,学的感觉不成系统。一直听说《C++primer》这本书是学习 C++的系统书籍,可以当做工具书来看,那么意味着这本书很全面。而且也有好多博主推荐,所以我决定开始学这本书,并且对我的学习过程进行一个记录。通过这个记录,我希望能与其他学习这本书的朋友一起进步,共同交流学习 C++的心得。
章节概述
第一章主要介绍 C++的编译和运行、输入输出、注释、while 循环和 for 循环、if 条件判断语句、类简介,最后将这些内容用一个书店程序来进行整理。
学习目标
- 掌握在 Linux 系统上通过命令行对 C++程序进行编译和运行
- 掌握 cin、cout 进行基本的输入输出
- 基本掌握循环语句和条件判断语句
- 完成书店程序的编写
重要概念:
-
每个 C++程序都是从 main 函数开始运行的,函数是程序设计中的基本模块,而 main 函数就是整个程序的起点。
-
Linux 系统命令行中使用 g++ text.cpp -o text 来编译 C++程序, 如果想对有问题的程序结构发出警告,可以使用 g++ -Wall text. cpp -o text 对代码进行编译。
-
使用标准输入输出库 iostream,标准输入:cin 标准输出:cout,标准错误:cerr,输出一般性信息:clog。目前来说就 cin 和 cout 就够了。
-
使用 cin 输入数据:std:: cin >> v1>> v2;像水流一样输入数据,所以叫流式输入。水流从键盘流入程序,从键盘输入一个数,这个数就跟着水流的方向>>流到了 v 1 中,再输入一个数,就继续跟着>>流到 v2 中。
-
使用 cout 输出数据:std:: cout << v1 << v2 << std:: endl; endl 是操作符,作用是将目前程序中的所有输出全部显示到屏幕上。cout 就像是出口,水流从右边往出口流,v1 先跟着水流的方向<<流出,接着 v2 再跟着流出,最后才是 endl 从 cout 流出去。
建议一直刷新流,防止程序崩溃时输出还留在缓冲区,从而误判崩溃位置的错误。下面是是否使用操作符对程序输出的影响。
#include <iostream>
#include <cstdio> // for setvbuf function
#include <unistd.h> // for sleep function
using namespace std;
int main() {
// 设置标准输出为完全缓冲模式
setvbuf(stdout, NULL, _IOFBF, 1024);
cout << "刷新缓冲区" << endl; // 刷新缓冲区
sleep(2); // 休眠2秒
cout << "不刷新缓冲区"<<'\n'; // 不刷新缓冲区
sleep(2); // 休眠2秒
cout << "再次刷新缓冲区" << endl; // 刷新缓冲区
sleep(2); // 休眠2秒
cout << "不刷新缓冲区" << '\n'; // 不刷新缓冲区
sleep(2); // 休眠2秒
return 0;
}
-
通过##include来调用头文件,标准库的头文件用<>,其他头文件用“”。
比如标准输入输出库iostream就是使用<\iostream>来进行调用。书店程序中的头文件 Salse_items 就是使用"Salse_items" 。头文件,顾名思义,一般都是放在源文件的开头地方。 -
注释分为单行注释//或者多行注释/* */
-
while 语句就是反复执行循环语句直到循环条件不符合为止,适合处理需要重复执行次数不确定的任务,比如:用户输入、读取文件内容、等待条件满足。
例:读取数量不定的输入数据。
while (std:: cin >> value) 当遇到文件结束符或一个无效输入时,会使得条件为假,从而结束 while 循环。在 Linux 系统中,文件结束符为 Ctrl+D。 -
for 用于执行确定次数的任务,比如:遍历数组或集合:依次访问数组或集合中的每个元素。计数循环和复杂迭代。使用前置递增运算符来进行计数循环,++i 的执行效率更高。
-
if 条件判断语句:条件为真,执行 if 后面的语句,条件为假,执行 else 后面的语句(如有)。
-
类:类机制是 C++最重要的特性之一。类定义了类型极其相关联的操作。类名就是类的名称,比如 string 就是一个类,string 就是类名,和 int、double 一样。int a; 和 Sales_item item; 所表达的意思是相通的,int a 是定义了一个 int 类型的变量 a,Sales_item item 是定义了一个 Sales_item 类型的变量 item; 并且还可以自定义类的行为,比如 int 类型就可以相加,那么 Sales_item 这个类型也可以有类似的行为,比如书籍数量的相加。
-
文件重定向:允许我们将标准输入和输出与命名文件关联起来:
./addItems <infile.txt> outfile.txt
这句代码可以让程序读取infile.txt中的内容,运行之后将结果输出到outfile.txt中。这样我们就不必在测试代码的时候一直从命令行输入。
- 成员函数(方法):在类内定义的函数,是类的一部分。通过点运算符.+调用运算符()来调用这个函数。
课后练习
1.1
编译:g++ 1-1.cpp -o 1-1
运行:./1-1
1.2
编译g++ 1-2.cpp -o 1-2
运行./1-2之后,在命令行输入 echo $? 来查看上一个命令的退出状态码,结果为255,因为Linux系统上的退出状态码是一个8位的无符号整数,会被转化为255。
1.3
#include<iostream>
int main()
{
std::cout << "Hello, World。" << std::endl;
return 0;
}
1.4
#include<iostream>
int main()
{
int num1, num2;
std::cin >> num1 >> num2;
std::cout << num1 * num2 << std::endl;
return 0;
}
1.5
#include<iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The sum of ";
std::cout << v1;
std::cout << " and ";
std::cout << v2;
std::cout << " is ";
std::cout << v1 + v2;
std::cout << std::endl;
return 0;
}
1.6
#include<iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1 = 0, v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The sum of " << v1;
std::cout<< " and " << v2;
std::cout<< " is " << v1 + v2 << std::endl;
return 0;
}
//不合法是因为第7行的分号将第7行和第8行这两行代码分开了,导致第8行代码没有流式输出,第8行和第9行是同样的问题。
//可以将第7行和第8行的分号删去,或者在第8行和第9行的前面加 std::cout
1.7
/*
*注释对/**/不能嵌套。 */
*“不能嵌套”几个字会被认为是源码,
*像剩余程序一样处理
*/
int main()
{
return 0;
}
错误信息:此声明没有存储类或类型说明符C/C++(77)
无法识别的标记C/C++(7)
<\error-type> 不能嵌套
/不能嵌套。
1.8
#include<iostream>
int main()
{
std::cout << "/*";//合法
//std::cout << "*/"; //合法
//std::cout <</* "/*" */; //错误,将输出运算符到分号之间的内容全部注释了
//std::cout << /* "/*" /* "/*" */;错误,将输出运算符到分号之间的内容全部注释了
return 0;
}
1.9
#include<iostream>
int main()
{
int val = 50, sum = 0;
while (val <= 100){
std::cout << val << " ";
sum += val;
++val;
}
std::cout << std::endl << "50到100的整数相加结果为: " << sum << std::endl;
return 0;
}
1.10
#include<iostream>
int main()
{
int val = 10;
while (val >=0){
std::cout << val << " ";
--val;
}
std::cout << std::endl;
return 0;
}
1.11
#include<iostream>
int main()
{
int num1 = 0, num2 = 0;
std::cout << "请输入两个整数:";
std::cin >> num1 >> num2;
while (num1 +1 < num2)
{
++num1;
std::cout << num1 << " ";
}
while (num2 + 1< num1)
{
++num2;
std::cout << num2 << " ";
}
std::cout << std::endl;
return 0;
}
1.12
完成的功能是从-100一直累加到100,结果为0
#include<iostream>
int main()
{
int sum = 0;
for (int i = -100; i <= 100; ++i){
sum += i;
}
std::cout << sum << std::endl;
return 0;
}
1.13
1.13-1
#include<iostream>
int main()
{
int sum = 0;
for (int i = 50; i <= 100; ++i)
sum += i;
std::cout << sum << std::endl;
return 0;
}
1.13-2
#include<iostream>
int main()
{
for (int i = 10; i >= 0; --i)
std::cout << i << " ";
return 0;
}
1.13-3
#include<iostream>
int main()
{
int num1 = 0, num2 = 0;
std::cout << "请输入两个整数" << std::endl;
std::cin >> num1 >> num2;
for (int i = num1+1; i < num2; ++i)
std::cout << i << " ";
std::cout << std::endl;
}
1.14
for循环
- 优点:
- 1、结构清晰:将初始化、条件检查和更新操作集中在一行,使得循环的结构清晰明了,特别适合用于已知循环次数的情况。
- 2、简洁:适合用于计数循环,减少了初始化和更新变量的冗余代码。
- 3、对于固定次数的循环,
for循环的语法一目了然。
- 缺点:
- 1、灵活性差。无法用于复杂条件的情况下。
- 2、不适合不确定次数的循环
while循环
- 优点:
- 1、灵活性强:可以在循环体中包含复杂的条件逻辑。
- 2、适用于不确定次数的循环。
- 3、简洁的条件控制:只要条件为真,就一直执行。
- 缺点:
- 1、容易陷入死循环。
- 2、代码可读性较差:初始化、条件检查和更新操作都分散在不同的位置。
1.15
略
1.16
#include<iostream>
int main()
{
int val = 0;
int sum = 0;
while (std::cin >> val)
{
sum += val;
}
std::cout << std::endl << sum << std::endl;
return 0;
}
1.17
所有值都相等,输出结果就是这个值的次数。
输入:42 42 42 42 42 42
输出: 42 出现 5 次
没有重复值,输出结果就是把这些值全部输出一次。
输入: 1 2 3 4 5 6
输出: 1 出现 1次
2 出现 1次
3 出现 1次
4 出现 1次
5 出现 1次
6 出现 1 次
1.18
略
1.19
题目说错了,应该说的是1.11,我1.11的程序已经处理了两个数的大小情况。
1.20
头文件
#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
源文件
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item book;
std::cout << "书籍销售记录为: " << std::endl;
while (std::cin >> book)
{
std::cout << book << std::endl;
}
return 0;
}
1.21
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item book1, book2;
std::cin >> book1 >> book2;
if (book1.isbn() == book2.isbn())
{
std::cout << "书籍销售记录为: "<< book1 + book2 << std::endl;
}
else
{
std::cerr << "数据不一致!" << std::endl;
return -1;
}
return 0;
}
1.22
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item book1, book2, sum;//初始化三个图书对象
//从标准输入读取数据,直到输入结束
if (std::cin >> book1)
{ //循环处理每个图书的销售记录
while (std::cin >> book2)
{
//如果当前记录的isbn与上次相同,则进行加法运算
if(book1.isbn() == book2.isbn())
{
//如果是同一本书,则加法运算
sum = book1 + book2;
}
//将运算后的作为第一本书与后面的书进行累加
book1 = sum;
}
std::cout << sum << std::endl;
}
return 0;
}
1.23 和 1.24
默认每个 ISBN 是存储在一起的,不然当前的知识没办法做这道题。(和书中 if 语句处的算法一致)
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item book1, book2;//初始化三个图书对象
int cnt = 1;
// 从标准输入读取数据,直到输入结束
if (std::cin >> book1)
{ //循环处理每个图书的销售记录
while (std::cin >> book2)
{
//如果当前记录的isbn与上次相同,则进行加法运算
if(book1.isbn() == book2.isbn())
{
//如果是同一本书,则加法运算
++cnt;
book1 += book2;
}
else
{
//如果isbn不同,则输出上次计算的结果
std::cout << book1 << " 销售次数 " << cnt << std::endl;
//将当前记录作为下一次计算的书
book1 = book2;
//将计数器清零
cnt = 1;
}
}
std::cout << book1 << " 销售次数 " << cnt << std::endl;
}
return 0;
}
1.25
略,程序就是书上的,之前已经提供了头文件源码。

浙公网安备 33010602011771号