写在前面
我一开始搞错了,以为C++PrimerPlus是C++Primer的进阶版,不对,完全反过来的,第一本书更适合入门,第二本书更像是词典,这两本书都是好书,但是花时间去读完就没必要了,因为我自己读着觉得这本书的章节设计很不合理,显示词典一样按而不是按科技树点的,比如没交类型先教分离式编程,没教数组先教vector等等,但是其中有很多细化的概念值得我们了解,本篇就是我把这本书中的细化概念提炼的过程。
这份教程严格意义上来说是我学习C++Peimer这本书的时候的感悟和经验,其中章节排布以及示例会尽量贴近这本书,因此,在观看本教程时建议边看这本书以加深理解,如果需要电子版可以点击这里自取电子版C++Primer及课后题答案,希望各位同学看完本教程能对c++有更深层次的了解。
1.1 编写一个简单的C++程序
这是一个最最最简单的程序叫做,它能输出一个“helloworld”在终端窗口具体包含三个东西:头文件,主函数,以及返回值。
可能由于很多学过一点c++的小伙伴就会有疑问了,说阿伦,你这个代码不对,我们老师在讲c++的时候说在头文件下要加上一行代码
using namespace std;
还会有人好奇,标准输入输出不是cin
和cout
吗,怎么多了个std::
是什么意思,这些都不用着急,每个学校教的流程不同,接触到这些知识的时间也不同,此时我们根据C++Primer这本书的流程走,我们只要知道无论是using namespace std;
还是std::
都基于一个概念,那就是命名空间就行了,相关的详细解释会在第三章进行详解。而且在一些有多个命名空间的代码中,用命名空间::函数
这种形式的写法更加保险。
下面就让我们复制这个代码去ide里运行吧!
#include <iostream>
int main()
{
std::cout << "Hello, World!" << std::endl;
return 0;
}
对了,关于IDE,我知道很多人在学校用的是codeblocks甚至时DevC++之类的代码编译器,这里我推荐大家在写代码的时候使用vscode,因为这个代码编辑器跨平台,无论你是windows、macOS甚至Linux都有对应的版本,而且配置流程相对简单,对代码兼容性很高,支持市面上绝大多数的语言。
第二个要讲的是在终端对代码进行编译,如果你一开始用的是代码编译器而不是代码编辑器,那你可能对这个概念有点陌生,因为大多数代码编辑器在编译的时候不是显式的给你看编译代码,而是直接输出结果,但如果你使用的是代码编辑器则完全不同,以vscode的codeRuner插件在macOS的编译过程为例,他编译的时候会生成这么一行东西
学过Linux的小伙伴可能看出来了,这里面就是我们在命令行执行的命令
第一句cd "/Users/alun/code/c-primer/1/"
说的是定位到指定路径,然后&&
表示紧接着执行,然后是g++ -std=c++17 1-3.cpp -o 1-3
这一行值得重点讲解,c++在编译的过程中会使用一些编译器,其中c++用得最多的是g++, std=c++17
这一行是我的习惯,指定版本c++17编译,这样可以支持更多c11之后的新语法,我知道很多学校用的标准是c98、c99甚至c97,但是现在主流的公司都在用c11、c14甚至c20,因此我们用新一点的版本是有好处的。
最后这句"/Users/alun/code/c-primer/1/"1-3
是在macOS下的程序执行指令,在windows下可能略有不同。
至此,我们就在控制台打印出了我们人生中的第一个c++程序:Hello,World!
1.2 输入输出
前面我们的例子中讲过一个头文件#include <iostream>
这个是什么呢,很多学过一点c++的同学会认识这头文件,但是不知道这是什么意思。
如果你们学过c语言,并且知道c语言头文件#include<stdio.h>
就能理解这是个什么东西,这俩都是标准的输入输出库,其中iostream包含了两个基础类型 istream和ostream,分别表示输入流和输出流。 一个流就是一个字符序列,是从IO设备读出或写入IO设备的。
其中,标准库还定义了四个IO对象,分别是cin
、cout
、cerr
和clog
,可能大家只是听过cin、cout,那这个c是什么意思呢,有部分同学觉得是c语言的c,其实这种理解也没错,书中说到c字的发音是’see’,也就是说,cin
应该表达的意思是我们’see’它输入’in’了,cout
表达的意思是’see-out’,cerr
就更通俗易懂了’see-error’,clog
是’see-log’,后两个哪怕是科班大一的同学认识的都比较少,因为其并不是我们常用的IO类型,做一定了解就足够,我们通常用cerr
来输出警告和错误消息,因此它也被称为标准错误 (standarderror)。 而 clog
用来输出日志等一般信息。
下面我们来看一个标准的输入输出的程序:
#include <iostream>
int main()
{
int v1 = 0,v2 = 0;
std::cin >> v1 >> v2;
std::cout << "The product of " << v1 << " and " << v2 << " is "<< v1 * v2 << std::endl;
return 0;
}
在这个代码中,执行时会等待你输入两个数字,然后输出他们的和,此时细心的小伙伴就会留意到,在标准输入输出函数和变量之间夹杂着一个运算符>>
和<<
他们分别是输入运算符>>
和输出运算符<<
,有个极致好用的辨别方法,就如同你在学习箭头➡️一样,箭头指向什么方向就是什么运算符,比如输入运算符>>
方向是从std::cin
指向变量的,就是输入运算符。还有一个std::endl
的意思和c语言中的\n
是一致的,表示换行。
1.3 注释
注释是每种语言不可或缺的一部分,在c++种,注释和c语言是一致的,有两种形式分别是//
和/* */
,第一种适合逐行注释,而第二种适合逐段注释。
int main()
{
//这是一个逐行注释
/*
这是一个逐段注释
在这两个标志中的所有数据都会被注释
*/
}
在书中我们可以看到逐段注释的形式类似
int main()
{
/*
*这是一个逐段注释
*在这两个标志中的所有数据都会被注释
*/
}
但是是事实上,中间的*
号可加可不加,在大多数编辑器/编译器中只需要有首尾标志即可触发注释。
但是有一种情况要非常非常注意!那就是在逐段注释中千万不能包含其他逐段注释,否则当注释遇到第一个*/
时就会结束注释
1.4 控制流
这一章只能说是本章节两个重点之一,一般的,程序执行顺序是从上到下的,但是使用控制流语句可以支持我们写出更为复杂的执行路径。
1.4.1 while
while 会是程序反复执行一段代码直至给定判断为假。
下面这个示例代码标识输出从50累加到100的累加值。
#include <iostream>
int main()
{
int var = 50 , sum = 0;
while(var <= 100)
{
sum += var;
++var;
}
std::cout << sum << std::endl;
}
而且,在c++中,标准输入语句也有真假,当输入的类型正常则为真,异常则为假,因此我们可以由此实现不定长度的数据输入。
比如这样
#include <iostream>
int main()
{
int sum = 0,var = 0;
while(std::cin >> var)
{
sum+=var;
}
std::cout << sum << std::endl;
}
当我们输入字符串时其为假,则输出值
顺带的,我发现在我这一版本的c++Primer中没有提及do while
语句,这里顺嘴提一下
do while
语句和while
的唯一区别是,do while
语句无论条件是否为假都会先执行一次,而while
当初始条件为假时就不会继续执行了。
1.4.2 for
for
循环和while
的最大区别就是,for
循环会更加适合定长/知道数量的数据的输入它的使用示例如下
#include <iostream>
int main()
{
int sum = 0;
for(int i = -100;i <= 100; ++i)//sum从0开始从-100加到100结果仍然为0
sum +=i;
return 0;
}
这个代码用于输出i从-100加到100的值,for的括号接受三个参数(执行语句),第一个定义循环初始值,第二个定义循环结束条件,第三个定义循环自增或自减条件。
从这里拓展出两个知识点
1.4.2.1 增强for
一个是增强for
(又称范围for
),我们在后面c++11的新特性章节会讲到。
用法
#include <iostream>
int main()
{
int arr[] = {1, 2, 3, 4, 5};
for(int &a:arr)
{
std::cout << a << std::endl;
}
return 0;
}
是不是看起来和python的for很像
1.4.2.2 自增自减符号
这个在第四章会讲,一般的,常用的自增自减符号又 ++、--、+=、-=
,拓展出来的还有*=、/=
等,其代表的意思都是变量 = 变量 + 值
比如++/--
的值为1,等价于+=1/-=1
,然后,还有一个非常重要的点是变量++/--
和++/--变量
的效果有一定不同,如果不在实时变化的语句中,这两种写法输出都代表着函数本身自增或自减,但是如果在一些及时变化的语句中,比如输入输出或者条件判断中,自增自减符号在前表示先进行自增自减再做其他事,在后面则先进行其他操作再进行自增或自建操作。
#include <iostream>
int main()
{
int a = 10,int b = 10;
std::cout << a++ << ++b << std::endl;
std::cout << a << b << std::endl;
return 0;
}
在这种情况下,虽然最后输出都是11,但是第一次输出a = 10,而b = 11。
1.4.3 if-else
这个应该没有什么可说的,本质上说成中文就是:如果xxx 就 xxx 否则 xxx
,下面是一个最简单的示例
#include <iostream>
int main()
{
int sun = 0;
std::cin >> sun;
if(sun > 0)
{
std::cout << "当前是白天" << std::endl;
}
else
{
std::cout << "当前是黑天" << std::endl;
}
return 0;
}
翻译成人话就是,如果
太阳不止一个,就是
白天,否则
是黑天。
当然,条件判断语句也可以相互嵌套,比如:
#include <iostream>
int main()
{
int sun = 0;
std::cin >> sun;
if(sun > 0)
{
std::cout << "当前是白天" << std::endl;
if( sun > 1)
{
std::cout << "后羿还没射日" << std::endl;
}
else if(sun < 9)
{
std::cout << "后羿已经射了"<< 9 - sun <<"个日" << std::endl;
}
else
{
std::cout << "哪来的那么多日" << std::endl;
}
}
else
{
std::cout << "当前是黑天" << std::endl;
}
return 0;
}
在这个代码中,我们可以看到,控制流语句是可以层层嵌套的,并且if-else
后面还可以继续接上if-else
1.4.4 switch
很奇怪的是,不知道为什么switch
语句在书中此章也没提及,这里简单提及一下,switch
语句写法如下
#include <iostream>
int main()
{
int score = 0;
std::cin >> score;
switch(score)
{
case 100:
std::cout << "你考了满分" << std::endl;
break;
case 99:
std::cout << "你考了99分" << std::endl;
break;
//省略98-1分的过程
//...
case 0:
std::cout << "你咋没分数" << std::endl;
break;
default:
std::cout << "你考了个啥玩意儿" << std::endl;
break;
}
retur 0;
}
几个要注意的点
- 一定要写
break
,否则会一直执行下去 - 写上
default
,表示出现意料之外的情况需要怎么处理 case
后面的类型要和switch
里的类型相匹配
switch
适合一长串已知值的条件判断,比一堆if-else
看起来直观多了
1.5 类简介
这一章很奇怪,如果是我的话应该会放到后面和类一起讲,但是为了和书中讲述的知识点相匹配,我们还是根据书中的知识讲。
首先,当我们需要了解类是个什么玩意儿,在C++中,我们通过定义一个类(class)
来定义自己的数据结构。一个类定义了一个类型,以及与其关联的一组操作。类机制是C++最重要的特性之一。实际上,C++最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型(classtype)
,同时,这也是唯一一个C++特性。
这一章其实没讲什么东西,在教材中定义了一个类叫Sales _item
,我们需要通过调用其头文件以实现调用这个类,并且需要定义一个类对象,对他进行写入读出,并且使用.
来访问其中的类成员变量。
没什么好说的,给个实例程序吧
#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;
}
其中这个类的头文件在这里C++Primer配套资料,访问不了的可以在这个教程的关联资源下载。
至此,我们的第一章就讲完啦
结尾
学完了这一章,你应该可以独立开发一些控制台程序,试一试独立开发一个可以实现整型数(int)加减乘的代码
要求:
- 首先输入一个数字代表进行的操作(1加、2减、3乘)
- 然后输入两个整数
- 输出结果
- 支持不定项输入(也就是进行一轮之后可以进行第二轮的输入)
- 支持判断是否为负数
祝同学们在学习c++的路上一路狂飙一帆风顺!