本文参考书籍《C++Primer 5th》,这个专栏主要分享我的学习笔记与总结,以及课后题答案.
开坑!这几天在疯狂补课.
1.1 编写一个简单的C++程序
- 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main.操作系统通过调用main来运行C++程序
//一个函数的定义包含四部分:返回类型、函数名、
//一个括号包围的形参列表{...(可以为空)}、结构体
int main () {
//()中可以为空 main函数的返回类型必须为int
return 0; // 在C++中,大多数C++语句以分号表示结束。
}
//函数定义的最后一部分是函数体,它是一个以左花括号{(curly brace)开始,
//以右花括号 } 结束的语句块(block of statements):
1.1.1、编译、运行程序
编写好程序后,我们就需要编译它。很多
PC
机上的编译器都具备集成开发环境(Integrated DevelopedEnvironment,IDE)
,将编译器与其他程序创建和分析工具包装在一起。
①程序源文件命名约定
程序文件通常被称为源文件(source file
),在大多数系统中,源文件的名字以一个后缀为结尾,后缀是由一个句点后接一个或多个字符组成的。后缀告诉系统这个文件是一个C++
程序。不同编译器使用不同的后缀命名约定,最常见的包括.cc 、 .cxx、.cpp、.cp及.C
1.1节练习
练习1.1:
查阅你使用的编译器的文档,确定它所使用的文件命名约定。编译并运行第2页的main程序。
#include <iostream>
int main () {
return 0;
}
练习1.2:
改写程序,让它返回
-1
。返回值-1
通常被当作程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main
返回的错误标识。
对比发现与上面完全相同,但是仔细对比,它与返回0
的值完全不同,他会返回一个很大的数4294967295
。
1.2 初识输入输出
iostream
库包含两个基础类型istream
和ostream
,分别表示输入流和输出流。一个流就是一个字符序列,是从IO
设备读出或写入IO
设备的。术语“流”(stream
)想要表达的是,随着时间的推移,字符 是顺序生成或消耗的。
①标准输入输出对象
标准库定义了4
个IO
对象。
- istream 输入类型:
cin
:标准输入(standard input)。 - ostream 输出类型:
cout
:标准输出(standard output)。
cerr
:用来输出警告和错误信息,称为 标准错误(standard error)。
clog
: 用来输出程序运行时的一般性信息。(用得少)
总结:四个IO对象,除了cerr没有缓冲,其他都是有缓冲(即待缓冲区满或者遇到endl时才会输出),因此,如果程序遇到调用栈用完了的威胁(无限,没有出口的递归)时使用cerr,cerr不经过缓冲而直接输出,一般用于迅速输出出错信息,是标准错误
参考cerr详解文章链接
②一个使用 I0 库的程序
通过使用 IO 库,我们可以扩展 main 程序,使之能提
示用户输入两个数,然后输出它们的和:
#include <iostream>//头文件iostream
int main () {
std::cout << "Enter two numbers: " << std::endl;//输出字符串字面值常量 "..."
int vl = 0, v2 = 0;
std::cin >> vl >> v2;
std::cout << "The sum of " << vl << " and " << v2
<< " is " << vl + v2 << std::endl;
return 0;
}
输出结果↓
其中<<
为输出运算符,且符号左侧对象必须是ostream
对象.
>>
为输入运算符,它接受一个 istream
作为其左侧运算对象,接受一个对象作为其右侧运算对象。返回其左侧运算对象为其计算的结果。
//下面这三个语句等价
std::cin >> v1;
std::cin >> v2;
(std::cin >> v1) >> v2;
std::cin >> v1 >> v2;
//由于此运算符返回其左侧运算对象,因此我们可以将一系列输入请求合并到单一语句中。
endl
是一个被称为操纵符(manipulator)的特殊值。写入 endl 的效果是结束当前行,并将与设备关联的缓冲区(buffer)中的内容刷到设备中。缓冲刷 新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写
入流。
1.2 节练习
练习 1.3
编写程序,在标准输出上打印
Hello,World。
#include <iostream>
int main () {
std::cout << "Hello,World。" << std::endl;
return 0;
}
练习 1.4
编写程序使用乘法运算符*, 来打印两个数的积。
#include <iostream>
int main () {
int a,b;
std::cout << "请输入俩个数"<< std::endl;
std::cin >> a >> b;
std::cout << a << "和" << b << "的积为" << a * b << std::endl;
return 0;
}
练习 1.5
我们将所有输出操作放在一条很长的语句中。重写程序,将每个运算对象的打印操作放在一条独立的语句中。[思路:对输出语句进行不同形式的练习,同时让读者体会良好的程序格式]
#include <iostream>
int main () {
int a,b;
std::cout << "请输入俩个数";
std::cout << std::endl;
std::cin >> a >> b;
std::cout << a << "和" << b << "的积为"
<< a * b << std::endl;
return 0;
}
练习 1.6
解释下面程序片段是否合法,如果程序是合法的,它输出什么?如果程序不合法,原因何在?应该如何修正?
std::cout << "The sum of "<< vl;
<< " and ” << v2;
<< "is " << vl + v2 << std::endl;
答案
这段代码不合法。前两行的末尾有分号,表示语句结束,第 2、3
两行为两条新的语句。而这两条语句在“<<”
之前缺少了输出流,应在“<<”
之前加上“std: :cout"
或者去除前俩行的分号,即得到正确的程序。
1.3 注释简介
注释可以帮助人类读者理解程序。注释通常用于概述算法,确定变量的用途,或者解释晦涩难懂的代码段。编译器会忽略注释,因此注释对程序的行为或性能不会有任何影响。还有,当你修改代码时,不要忘记同时更新注释!
C++中注释的种类
①单行注释以双斜线(//)开始,以换行符结束。当前行双斜线右侧的所有内容都会被编译器忽略,这种注释可以包含任何文本,包括额外的双斜线。使用如下:
cin >> 1;//输出一个数字1
②另一种注释使用继承自 C 语言的两个界定符(/*和*/
)。这种注释以/*
开始,以*/
结束,可以包含除*/
外的任意内容,包括换行符。编译器将落在/*和*/
之间的所有内容 都当作注释。使用如下:
#include <iostream>
int main () {
int a,b;
/*
std::cout << "请输入俩个数";
std::cout << std::endl;
std::cin >> a >> b;
std::cout << a << "和" << b << "的积为"
<< a * b << std::endl;
*/
return 0;
}
小结:注释界定符对通常用于多行解释,而双斜线注释常用于半行和单行附注。
③注释界定符不能嵌套
/*
* 注释对/* */不能嵌套。
* “不能嵌套”几个字会被认为是源码,
* 像剩余程序一样处理
*/
int main() {
return 0;
}
我们通常需要在调试期间注释掉一些代码。由于这些代码可能包含界定符对形式的注释,因此可能导致注释嵌套错误,因此最好的方式是用单行注释方式注释掉代码段的每一行
。
// /*
// *单行注释中的任何内容都会被忽略
// *包括嵌套的注释对也一样会被忽略
// */
1.3 节练习
练习 1.7
编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。
对不正确的嵌套注释,不同编译器给出的错误信息可能是不同的,而且通常很难理解。以dev c++5.11 为例 :
报错信息特别多,而且这些错误信息都很难直接与注释错误嵌套挂上钩,程序员需要有一定的经验才能快速定位错误,来确定错误原因。
练习 1.8
指出下列哪些输出语句是合法的(如果有的话):
std::cout << "/*";
std::cout << "*/";
std::cout << /* "*/" */;
std::cout << /* "*/" /* "/*" */;
答案
第一条和第二条语句显然是合法的。
第三条语句中,第一个双引号被注释掉了,因此<<
运算符后真正被编译的内容是" */
,编译器认为这是一个不完整的字符串,所以会报告:6 行 error: missing terminating " character
即,缺少结束的双引号。在分号前补上一个双引号,这条语句就变为正确的了。
第四条看起来很混乱,但它是正确的。第一个双引号被注释掉了,第四个双引号也被注释掉了,所以会输出 第二个双引号和第三个双引号之间的"/*"
字符串的文字内容。但是,这样的程序风格显然是不好的。
修改之后↓
1.4 控制流
1.4.1 while 语句
while
语句反复执行一段代码,直至给定条件为假为止。形式如下:
while {
condition) {
statement
}
while
语句的执行过程是交替地检测condition
条件和执行关联的语句statement
,直至condition
为假时停止,简单来说就是先判断条件,再根据条件的正确来执行,如果条件一直为真,就会一直做下去。
求 1 到 10 这 10 个数之和
#include <iostream>
int main() {
int sum = 0, val = 1;
//只要 val 的值小于等于 10, while 循环就会持续执行
while (val <= 10) {
sum += val; //这个语句等价与sum = sum + val
++val; //这个语句等价与 val = val + 1
std::cout << "Sum of 1 to 10 inclusive is" << sum << std::endl;
}
return 0;
}
前缀递增运算符++ val;
是先赋值val
给执行所在条件语句
后缀递增运算符val ++;
是先执行所在条件语句再赋值给val
1.4.1 节练习
练习 1.9
编写程序,使用 while 循环将 50 到 100 的整数相加。
不难把上面的语句稍微改改就行。
#include <iostream>
int main() {
int sum = 0, val = 50;
//只要 val 的值小于等于 10, while 循环就会持续执行
while (val <= 100) {
sum += val; //这个语句等价与sum = sum + val
++val; //这个语句等价与 val = val + 1
}
std::cout << "Sum of 50 to 100 inclusive is" << sum << std::endl;
return 0;
}
练习 1.10
除了++运算符将运算对象的值增加 1 之外,还有一个递减运算符(
--
)实现将值减少 1。编写程序,使用递减运算符在循环中按递减顺序打印出 10 到 0 之间的整数。
#include <iostream>
int main() {
int val = 10;
//只要 val 的值小于等于 10, while 循环就会持续执行
while (val > 0) {
std::cout <<"val ="<< val << std::endl;
-- val; //这个语句等价与 val = val - 1
}
return 0;
}
练习 1.11
编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数
#include <iostream>
int main() {
std::cout << "请输入两个数";
std::cout << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
if (v1 > v2) //由大至小打印
while (v1 >= v2) {
std::cout << v1 << ' ';//先打印v1 然后打印空格
v1 --;
}
else //由小至大打印
while (v1 <= v2) {
std::cout << v1<< ' ';
v1++;
}
std::cout << std::endl;
return 0;
}
1.4.2 for 语句
可以用
for
语句来重写从1
加到10
的程序:
#include <iostream>
int main() {
int sum = 0; //从 1 加到 10
for (int val = 1; val <= 10; ++val) {
//我的习惯会加一个花括号
sum += val; // 等价于 sum = sum + val
}
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
return 0;
}
每个 for 语句都包含两部分:循环头和循环体。循环头控制循环体的执行次数,它由三部分组成 : 一 个初始 化语句 (init-statement) 、 一 个循环 条件 (condition) 以 及 一 个 表 达 式(expression )。
for
循环的总体执行流程:
1 .创建变量val
,将其初始化为1
。
2 .检测val
是否小于等于10
。若检测成功,执行for
循环体。若失败,退出循环, 继续执行for
循环体之后的第一条语句。
3 .将val
的值增加1
.
4 .重复第2
步中的条件检测,只要条件为真就继续执行剩余步骤。
练习1.12
下面的for循环完成了什么功能?sum的终值是多少?
int sum = 0;
for (int i = -100; i <= 100; ++i) sum += i;
由于是对称的,所以答案为 0
练习 1.13
使用 for 循环重做 141 节中的所有练习(第 11 页)。只做一个 其他类似
#include <iostream>
int main() {
int sum = 0 ;
for(int val = 1; val <= 10; val ++) {
sum += val; //这个语句等价与sum = sum + val
std::cout << "Sum of 1 to 10 inclusive is" << sum << std::endl;
}
return 0;
}
练习 1.14
对比
for
循环和while
循环,两种形式的优缺点各是什么?
在循环次数已知的情况下,for 循环的形式显然更为简洁。而循环次数无法预知时,用 while 循环实现更适合。用特定条件控制循环是否执行,循环体中执行的语句可能导致循环判定条件发生变化。
练习 1.15
编写程序,包含第 14 页“再探编译”中讨论的常见错误。熟悉编译器生成
的错误信息。(自行编译)
一般就是语法错误、类型错误、声明错误。错误信息通常包含一个行号和一条简短描述,描述了编译器认为的我们所犯的错误。按照报告的顺序来逐个修正错误,是一种好习惯。因为一个单个错误常常会具有传递效应,导致编译器在其后报告比实际数量多得多的错误信息。另一个好习惯是在每修正一个错误后就立即重新编译代码,或者最多是修正了一小部分明显的错误后就重新编译。这就是所谓的“编辑-编译-调试”(edit-compile-debug)周期
1.4.3 读取数量不定的输入数据
在前一节中,我们编写程序实现了 1 到 10 这 10 个整数求和。扩展此程序一个很自然的方向是实现对用户输入的一组数求和。在这种情况下,我们预先不知道要对多少个数求和,这就需要不断读取数据直至没有新的输入为止:
#include <iostream>
int main() {
int sum = 0, value = 0;
while (std::cin >> value)//返回其左侧运算对象,直到遇到文件结束符
//(end-of-file),或遇到一个无效 输入时(例如读入的值不是一个整数)
sum += value; // 等价于 sum = sum + value
std::cout << "Sum is: " << sum << std::endl;
return 0;
}
此表达式从标准输入读取下一个数,保存在 value 中。输入运算符(参见 1.2 节,第 7 页)返回其左侧运算对象,这样就可以做到读取多个数进行求和,另外还需要一个知识
练习 1.16:
编写程序,从 cin 读取一组数,输出其和。(就是上面的例子)
1.4.4 if 语旬
C++也提供了if
语句来支持条件执行。
if(判断条件) {
...
}
else {
...
}
C++用=进行赋值,用==
作为相等运算符;两个运算符都可以出现在条件中、 一个常见的错误是想在条件中使==
(相等判断),却误用了="
我们要牢记一件重要的事情:其他可能的程序格式总是存在的。当你要选择一种格式风格时,思考一下它会对程序的可读性和易理解性有什么影响,而一旦选择了一种风格,就要坚持使用。
1.5 类简介
在解决书店程序之前,我们还需要了解的唯一一个 C++特性,就是如何定义一个数据 结构(data structure)来表示销售数据。在 C++中,我们通过定义一个类(class)来定义 自己的数据结构。一个类定义了一个类型,以及与其关联的一组操作。类机制是 C++最重 要的特性之一。实际上,C++最初的一个设计焦点就是能定义使用上像内置类型一样自然 的类类型(class type)
为了使用类,我们需要了解三件事情:
• 类名是什么?
• 它是在哪里定义的?
• 它支持什么操作?
对于书店程序来说,我们假定类名为 Sales_item
,头文件Sales_item.h
中已经定义了这个类。[ 类名 ]
我们通常使用
.h
作为头文件的后缀,但也有一些程序员习惯.H、.hpp 或.hxx
。标 准库头文件通常不带后缀。编译器一般不关心头文件名的形式,但有的IDE
对此有特定要求。
1.5.1 Sales item 类
Sales_item
类的作用是表示一本书的总销售额、售出册数和平均售价。我们现在不关心这些数词如何存储、如何计算。为了使用一个类,我们不必关心它是如何实现的,只 需知道类对象可以执行什么操作。
//类型名就是类名
Sales_item item;//定义了一个名为 item 的Sales_item类型对象
除了可以定义 Sales_item 类型的变量之外,我们还可以:
• 调用一个名为isbn
的函数从一个 Sales_item
对象中提取ISBN
书号。
• 用输入运算符(>>
)和输出运算符(<<
)读写 Sales_item
类型的对象。
,用运算符(= / + / - / += / -=...
)将一个Sales_item
对象进行操作。
读写 Sales item
#include <iostream>
#include "Sales_item.h"
//对于不属于标准库的头文件,则用双引号("")包围。
int main()
{
Sales_item book;//定义了一个名为 book 的对象
//读人工 SBN 号、售出的册数以及销售价格
std::cin >> book;
//写入工 SBN、售出的册数、总销售额和平均价格
std::cout << book << std::endl;
return 0;
}
Sales_item对象的加法
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item item1,item2;
std::cin>>item1>>item2;
std::cout<<item1+item2<<std::endl;
return 0;
}
如果输入如下内容:
0-201-78345-x 3 20.00
0-201-78345-x 2 25.00
则输出为:
0-201-78345-x 5 110 22
初始成员函数
//将两个Sales_item的对象相加并检查是否有相同的ISBN码
#include<iostream>
#include"Sales_item.h"
int main()
{
Sales_item item1,item2;
std::cin>>item1>>item2;
//首先检查两个Sales_item对象是否有相同的ISBN值
if(item1.ISBN()==item2.ISBN())
{
std::cout<<item1+item2<<std::endl;
return 0;//表示输入成功
}
else
{
std::cout<<"Data must refer to same ISBN"
<<std::endl;
return -1;//表示输入失败
}
}
什么是成员函数?
调用名叫ISBN
的成员函数(member function)。成员函数是定义为类的一部分函数,有时也被称为“方法(method)”。
点运算符(.
) 只能用与类类型的对象,其左侧运算对象只能是一个类类型的对象,右侧运算对象必须是该类型的一个成员名,其运算结果是右侧运算对象所指定的成员。
我们使用调用运算符( ()
) 来调用一个函数。调用运算符是一对圆括号,里面用来存放实参(argument)列表(可能为空)。
练习1.20
在网站http://www.informit.com/title/032174113 上,第1章的代码目录包含了头文件 Sales_item.h。将它拷贝到你自己的工作目录中,并编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上。
Sales_item.h
#ifndef SALESITEM_H
#define SALESITEM_H
#include <iostream>
#include <string>
class Sales_item {
public:
Sales_item(const std::string &book) :isbn(book), units_sold(0), revenue(0.0) {
}
Sales_item(std::istream &is) {
is >> *this; }
friend std::istream& operator>>(std::istream &, Sales_item &);
friend std::ostream& operator<<(std::ostream &, const Sales_item &);
public:
Sales_item & operator+=(const Sales_item&);
public:
double avg_price() const;
bool same_isbn(const Sales_item &rhs)const {
return isbn == rhs.isbn;
}
Sales_item() :units_sold(0), revenue(0.0) {
}
public:
std::string isbn;
unsigned units_sold;
double revenue;
};
using std::istream;
using std::ostream;
Sales_item operator+(const Sales_item &, const Sales_item &);
inline bool operator==(const Sales_item &lhs, const Sales_item &rhs) {
return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.same_isbn(rhs);
}
inline bool operator!=(const Sales_item &lhs, const Sales_item &rhs) {
return !(lhs == rhs);
}
inline Sales_item & Sales_item::operator +=(const Sales_item &rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline Sales_item operator+(const Sales_item &lhs, const Sales_item &rhs) {
Sales_item ret(lhs);
ret += rhs;
return ret;
}
inline istream& operator>>(istream &in, Sales_item &s) {
double price;
in >> s.isbn >> s.units_sold >> price;
if (in)
s.revenue = s.units_sold * price;
else
s = Sales_item();
return in;
}
inline ostream& operator<<(ostream &out, const Sales_item &s) {
out << s.isbn << "\t" << s.units_sold << "\t" << s.revenue << "\t" << s.avg_price();
return out;
}
inline double Sales_item::avg_price() const {
if (units_sold)
return revenue / units_sold;
else
return 0;
}
#endif
main.cpp
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item book;
std::cout << "请输入销售记录:"
<< std::endl;
while (std::cin >> book) {
std::cout << "ISBN、售出本数和平均售价为 "
<< book << std::endl;
}
return 0;
}
如果输入:0-201-70353-X 4 24.99
则输出为:0-201-70353-X 4 99.96 24.99
练习 1.21
编写程序,读取两个工ISBN
相同的 Sales_item
对象,输出它们的和。
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item book1, book2;
std::cin >> book1 >> book2;
std::cout << book1 + book2 << std::endl;
return 0;
}
练习 1.22
编写程序,读取多个具有相同 ISBN 的销售记录,输出所有记录的和。
#include <iostream>
#include "Sales_item.h"
int main(void)
{
Sales_item total, trans;
std::cout << "请输入ISBN相同的销售记录:" << std::endl;
if (std::cin >> total)
{
while (std::cin >> trans)
if (compareIsbn(total, trans))//ISBN相同
total += trans;
else//ISBN不同
{
std::cout << "ISBN不同" << std::endl;
return -1;
}
std::cout << "汇总信息:ISBN、售出本数、销售额和平均售价为 "
<< total << std::endl;
}
else
{
std::cout << "没有数据" << std::endl;
return -1;
}
return 0;
}
1.6书店程序
现在我们已经准备好完成书店程序了。我们需要从一个文件中读取销售记录,生成每本书的销售报告,显示售出册数、总销售额和平均售价。我们假定每个 ISBN 书号的所有销售记录在文件中是聚在一起保存的。
#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; // total 现在表示下一本书的销售额
}
}
std::cout<<total<<std::endl;// 打印最后一本书的结果
}
else
{
//没有输入!警告读者
std::cerr<<"No Data!"<<std::endl;
return -1; //表示失败
}
return 0;
}
1.6 节练习
练习 1.25:借助网站上的 Sales item.h 头文件,编译并运行本节给出的书店程序。(已完成)
小结
本章介绍了足够多的 C++语言的知识,以使你能够编译、运行简单的 C++ 程序。我们看到了如何定义一个 main 函数,它是操作系统执行你的程序的调用入口。我们还看到了 如何定义变量,如何进行输入输出,以及如何编写 if、for 和 while 语句。本章最后介 绍了 C++中最基本的特性—类。在本章中,我们看到了,对于其他人定义的一个类,我 们应该如何创建、使用其对象。在后续章节中,我们将介绍如何定义自己的类。
最后:
感谢阅读,有错误&不懂&想法欢迎评论,看到回复~~~