文章目录
课程网站:https://web.stanford.edu/class/archive/cs/cs106b/cs106b.1208/schedule
说明:可以在下面的网站找到一些doc文件 http://cs106b.stanford.edu 2020
第一部分 c++基础
课件是官网上的,但是看的视频是bilibili上的18年的
同步测试
Lecture1 基础
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RmVPyhxp-1610276594369)(https://i.loli.net/2020/07/04/uwa2vRqcVrdWnTZ.png)]
介绍部分
c++是编译型语言(compiled vs interpreted):这意味着运行c++程序之前必须编译成机器码
介绍:安装 Qt Creator
[Qt 安装]https://web.stanford.edu/class/cs106b/qt/
在每次作业都会给出 Qt Creator starter project files
介绍:课程安排
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AaNbIcug-1610276594371)(https://i.loli.net/2020/06/28/xRoh82NvTSHAkYX.png)]
第一个代码及语法基础
打开第一课的
HelloWorld.pro
/*
* hello.cpp
* This program prints a welcome message to the user.
*/
#include <iostream>
using namespace std;
int main(){
cout << "Hello, world!" << endl;
return 0
}
学习:
- comment 的方法
- #include 导入一些库
- 将这个文件保存为后缀为 .cpp 的文件
1 C++的注释
- 单行注释
- 多行注释
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4HtPvZNA-1610276594372)(https://i.loli.net/2021/01/04/LiTvloVScGtZdNF.png)]
- c++ 的源代码(source code) 放在
.cpp
文件夹, 额外的声明文件可以放在.h
文件中 - 源代码被编译成
binary object .o
文件 - 与Java不同,c++可执行文件是 platform-dependent
我们也可以简单比较以下C++和Python程序的区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvNY8NjG-1610276594375)(https://i.loli.net/2020/07/04/YXncOxA57VKjpst.png)]
2 C++中的 main() 函数
main()
函数一般来说是一个程序的起点,main()
函数返回一个正数给操作系统:0 代表成功,非0代表失败
3 C++ 的基本 syntax
c++ 中的变量和类型:
使用一个变量之前必须声明它的类型
int x = 42 + 7 * -5;
double po = 3.14159;
char c = 'Q';
bool b = true;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjwfaMtt-1610276594377)(https://i.loli.net/2021/01/04/BCJWbXuirFkgYwf.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ukozqYvW-1610276594378)(https://i.loli.net/2021/01/04/lumQ72dRsJpvqLH.png)]
c++ 中的计算优先顺序 Operator Precedence
https://en.cppreference.com/w/cpp/language/operator_precedence
c++ 中的 布尔表达式
c++ 中的 control flow
1 条件语句 if
2 for 循环
initializationStatement
: 初始化一个变量。egint i = 0
testExpression
: 首先对testExpression
求值,然后在每次运行循环后求此表达式,如果为 true,则循环继续进行下一次迭代, e.gi < 3
updateStatement
:updateStatement
发生在每个循环之后,``testExpression计算之前 e.g
i++`
for (int i = 0; i < 10; i++){
if (i % 2 == 0){
x += i;
}
}
补充:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WnJlLxf9-1610276594383)(https://i.loli.net/2020/07/05/rXYeLbounCHtQcq.png)]
第三点 如果没有写 increment 则 i 会一直为0(如果初始化
int i =0
的话)
for 循环的第二种写法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C0a8SPT1-1610276594383)(https://i.loli.net/2020/07/05/g8cqlBWhwxsHNI2.png)]
- 注变量的类型根据范围内的类型来定或者使用 auto
- 注意如果对list中的元素进行改变,变量使用reference的类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GW0w3i6d-1610276594384)(https://i.loli.net/2020/07/05/LMYyONRqE321nHx.png)]
3 while loops
while (x > 0 && c == 'Q' || b){
x = x / 2;
if (x == 42){
return 0;
}
}
小练习:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQLo1YMe-1610276594385)(https://i.loli.net/2021/01/04/wQrhZz2odgil1PS.png)]
#inclued <iostream>
using namespace std;
int main(){
/* TODO */
for (int i = 10; i > 0 ; i--){
cout << i << endl;
}
cout << "Liftoff" << endl;
}
4 C++ include 头文件初步
<>
来表示包含C++系统自带的头文件“.h”
表示包含本地头文件(注意使用双引号)
5 namespace
和 using
- 命名空间(
namespace
)是指范围标识符 (scope identifiers ):- 帮助避免使用相同名字的项的冲突
- c++控制台的 I/O对象的命名空间是
std
- 在代码中使用例如:
using namespace std
– 在全局范围内使用 namespace::identifier
:若没有using
声明,你通过 precede 这个 symbol with 它的 namespace 名即::
符号,可以 access 一个namespace
的 symbols
6 用户输入和输出 User Input and Output
1 在终端输出 console output: cout << << endl;
-
endl
表示end of line
-
类似
\n
: 问题是如果我想换行应该怎么做?写了
endl
不写分号继续使用 << 即可
注意没有
endl
的情况。
2 从终端输入: cin >>
不鼓励这种方式读取输入
cin <<
从终端读取用户输入存在的问题,如图所示:
-
只能一次读取一个word
-
如果输入的不是一个
int
而是一串字符串会怎么样呢? -
如果输入的不是
int
类型而是浮点数类型会怎样?
3 解决 cin <<
方法的局限性:使用 stanford 标准库
我们建议使用另一种方法 reading user input:使用 Standford library 的一个函数:getLine()
getLine()
函数接收一个参数,该参数是显示给用户的提示. 用户按下 “ Enter / Return” 键提交答案后,该函数将返回用户在控制台中键入的值,其他读取用户输入的函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oed62n4M-1610276594386)(https://i.loli.net/2020/06/28/ASBHReD5WKp1nri.png)]
使用表格解释下:
请在程序前包含头文件(第三方库) | #include “simpio.h” |
---|---|
函数名 | 描述 |
getInteger("prompt") | 不断从终端读取直到读取一个整数 |
getReal("prompt") | 不断从终端读取直到读取到一个实数(double) |
getLine("prompt") | 读取返回一行(回车)文本 |
getYesOrNo("prompt") getYesOrNo("prompt","y","n") | 通过输入“yes”or“no”函数返回布尔值 (或者指定输入值例如 “y” or “n” ) |
应用的例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-keYoxbxt-1610276594387)(https://i.loli.net/2020/06/28/StZX7p9kzqC1QRB.png)]
如果你 #include “simpio.h”
将可以使用上述读取函数
注意一点,如果你没有 #include <iostream>
包含系统的这个库,你就不能使用 cout<<
输出
下一节: how to write a funtion?
函数写在调用之后将会报错
Lecture 2 函数
函数模板(function prototype)
returnType functionName(varType parameter1, varType parameter2, ...);
函数参数:看起来非常像变量声明语句,你可以认为 参数 是特殊的属于函数的* 局部变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2TqdcbKh-1610276594388)(https://i.loli.net/2021/01/04/fB26wUPxeKb43Yt.png)]
关于 parameters 和 arguments 的定义区别:
- parameters: one or more variables that your function expects as inputs
- arguments: The values passed into your function and assigned to its parameter variables
关于函数的返回:
- 函数名前面是函数返回的类型,如果想要定义一个没有返回值的函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xn9GXgKN-1610276594389)(https://i.loli.net/2021/01/04/oLDAIC4XW7cU6fm.png)]
举例:
double average(double a, double b){
double sum = a + b;
return sum / 2;
}
int main(){
double mid = average(10.6, 7.3);
cout << mid << endl;
return 0;
}
关于函数的顺序问题(declaration order matters):
- 我首先写了一个函数,名为
void song()
,然后我在后面的程序里调用了两次
但是如果把 void song()
剪切到 main()
函数下方,程序编译的时候将会报错,我们可以总是在 main()
之前定义函数,但是通常我们会先写 main()
函数,再写其中需要的函数,解决方法是:
-
把被调用的函数写在主函数前面
-
使用 function prototypes
在主函数之前,在程序顶部声明一个(不包含主体)的函数,相当于告诉程序我后面一定会写这个函数。这称为:
function prototype
例如
函数:c++内置数学函数
有一些已经存在的的函数是可以直接调用的,比如 数学函数(注意头文件 include <cmath>
)
函数名 | 描述(返回) |
---|---|
abs(value) | 绝对值 |
ceil(value) | 向上圆整 |
floor(value) | 向下圆整 |
log10(value) | 取以10为底的对数 |
max(value1,value2) min(value1,value2) | 两个值中较大的值 两个值中较小的值 |
pow(base, exp) | 取指数 |
round(value) | 最近的圆整数 |
sqrt(value) | 取2次根号 |
sin(value) cos(value) tan(value) | 正弦 余弦 正切 |
- 使用Stanfor的
“gmath.h”
获得更多数学函数
函数:默认参数
关于函数定义时的默认参数:在定义函数是给定默认参数可以使这个参数变成可选参数—— 在用户没有给参数时,调用函数将会使用默认参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ntheqziG-1610276594390)(https://i.loli.net/2021/01/04/AWRlZC43jxO2izS.png)]
函数:参数 value semantics
学习概念:
- value semantics
- reference semantic
value semantics: 当一个 parameter (by value)被传入函数时(意思就是调用函数时),用新的变量存储了这个传入值的副本 —— 意味着被调用的函数使用什么参数表示都不影响函数的结果,也不影响被传递变量本身
例如
// c++:
#include<iostream>
using namespace std;
int doubleValue(int x){
x *= 2;
return x;
}
int main(){
int myValue = 5;
int result = doubleValue(myValue); //1
cout << "myValue: " << myValue << " ";
cout << "result: " << resule << endl;
}
myValue: 5 resultL 10
这样的结果输出的原因是:
- parameter
x
by value=5 被传递给 函数doubleValue
—— 1 - 意味着 variable
x
是 a copy of the variable(``myValue=5`)
在函数内改变这个变量是不会改变 calling function 的值的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDMpe6vL-1610276594391)(https://i.loli.net/2021/01/04/Otlz7Cqg2xphRi4.png)]
再看一个例子 —— swap(int a, int b)
两个整数的值的交换
void swap(int a, int b){
int temp = a;
a = b;
b = temp
}
int main(){
int x = 17;
int y = 35;
sway(x, y);
cout << x << "," << y << endl;
return 0
}
17,35
-
但在下面的主函数中,我们调用了这个函数并输出 x 和 y 结果值没有变,为什么?
-
因为交换的的时变量的值的复制,而**变量(指
x
和y
)**本身的值并没有改变
函数:reference semantics
reference semantics/reference parameters:当你在声明一个变量时,在它的类型后面加&
,那么它将指向变量存储的同一位置。那么当一个函数调用这个 参数/值 时,变量不是这个值的复制,而是这个值的reference,这意味着对这个变量的改变也是对原始值的改变
-
Regular variables – 常规变量: 看做是 a named container storing a value.
-
References (例如
double& weight_ref
)-- : 只是看做一个 box 指向已经存在的变量 a box that just refers to an existing variable.References 可以有 names and types, 就像常规变量一样(但不改变它的本质)
理解什么叫指向变量本质(-内存)
还是上面提到的相同的例子:
void swap(int& a, int& b){
int temp = a;
a = b;
b = temp
}
int main(){
int x = 17;
int y = 35;
sway(x, y);
cout << x << "," << y << endl;
return 0
}
//值交换了
35, 17
在 c++ 中每一种 data type 都可以被 passed 作为 value semantics 或者 reference semantics
- 相较于调用一个函数然后 return something,c++ 使用 reference的方法 也可以使函数 “return something”
- 当我们使用任何 data type 的时候,我们都要决定是要 copy it 还是 share it
讨论:
我们什么时候使用 reference 声明变量
- To allow
helper
functions to edit data structures in other functions – 当我们允许helper
函数在其他函数中编辑数据结构时- But why don’t we just return a copy of the data structure? – 为什么不在其他函数中返回一个副本呢?
- 避免在内存中生成大型数据结构的新副本
- Passing data structures by reference makes your code more efficient!
- reference 还提供了针对多个返回值(multiple return values)的解决方法
- 在你的函数中可以有一个返回值,同时也可以在这个函数中直接编辑作为
ref
参数传入的a Vector object 。这样就让你的函数实现了多个返回值:return both the vector and the actual return value! - 你的函数可以 take in multiple pieces of information by reference 并修改它们。通过这种方式,你可以同时 “return” 修改后的Vector和一些关于结构如何修改的辅助信息。这就好像你的函数正在 向调用它的函数 返回了两块更新过的信息
- 在你的函数中可以有一个返回值,同时也可以在这个函数中直接编辑作为
我们什么时候不使用 reference 声明变量
-
如果我们始终使用reference,则函数都将能够编辑彼此的变量,并且作用域scoping会造成混淆!
- 这也将使bugs的可能性更大。 变量的意外更改可能会在各个函数之间存在。
-
如果数据本身很小(即按值复制的成本很低),那么我们就不需要使用reference。
-
注意:如果要通过 reference 传递参数,则不能提供literal作为参数。
没懂这个意思 – 见代码
void tripleWeight(double& weight_ref){ weight_ref *= 3; } int main(){ double weight = 1.06; tripleWeight(weight); // tripleWeight(1.06) 是错误的因为没有变量-没有share内存地址? cout << weight << endl; //3.18 }
实例
案例1:这个函数想实现你可以约会的人的年龄范围,这个函数必须要返回:
- minimum age;maximum age
输出为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2TovZNP-1610276594395)(https://i.loli.net/2020/06/29/QSZN9wqDxF1p4X7.png)]
则你可以使用两个 reference parameters – int& min
int& max
,这样在我们调用函数 datingRange()
将修改这两个参数。
你可以看到上面
main
函数 中datingRange(48, young,old)
中的参数young
h和old
没有指定任何值,他们实际上存储了 计算出的min
和max
的值
当你只需要返回一个输出, return 语句是不错的选择,但如果有多个 return 的话 reference parameters 是不错的选择
案例2 写一个c++函数,求二次函数的根
- 我们的函数应该accept什么参数?
- 他应该 return 什么?
- 什么 parameters 应该以 value 传递?
- 什么 parameters 应该以 reference 传递?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WebrfxNs-1610276594396)(https://i.loli.net/2020/06/28/hZ9z5WvLRDi1c3w.png)]
如果我不需要 return anything
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twe77Q5Z-1610276594396)(https://i.loli.net/2020/06/28/UXCd79uR1Qkl6Ly.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYjkJSzR-1610276594397)(https://i.loli.net/2020/06/28/oEjAMBbQ8xVnpz3.png)]
这里可能有问题:为什么不实用
cout<<
打印出输出
- 依个人习惯,如果print,你在 console 可以看到,但是他们随后会 gone,但是当这些参与其它的calculate的时候不能简单的打印出来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjsDZrsI-1610276594398)(https://i.loli.net/2020/06/28/UW84JgzymF2CvqD.png)]
输出:
The roots are 4 and -1
在主函数中:我想要 声明一些变量来存储 roots(22行)我不需要给它任何的值,然后我们调用这个函数 pass in a b c
的值。
问题,为啥不把 a b c 也作为 reference parameters ?
- 我们不准备改变这几个参数的值,我们不一定想要和别人 share anything。
一些错误的尝试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPwCe1yH-1610276594398)(https://i.loli.net/2020/06/29/hEmr6N73xVyKvAI.png)]
这就是上面提到的对于reference 参数不能使用literal传入参数 你必须pass a place 存放 这个double value
如果你想 pass in 一个几百行的数组一般使用 reference parameters 因为 使用 copy 将会占用很大的 memory空间
附录:关于函数的组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6LhST1i-1610276594399)(https://i.loli.net/2020/06/29/UBFyuvHVa4JM71w.png)]
查看手册看一些函数编写的例子,总结 compose 和 decompose 的问题
的calculate的时候不能简单的打印出来。
[外链图片转存中…(img-CjsDZrsI-1610276594398)]
输出:
The roots are 4 and -1
在主函数中:我想要 声明一些变量来存储 roots(22行)我不需要给它任何的值,然后我们调用这个函数 pass in a b c
的值。
问题,为啥不把 a b c 也作为 reference parameters ?
- 我们不准备改变这几个参数的值,我们不一定想要和别人 share anything。
一些错误的尝试
[外链图片转存中…(img-qPwCe1yH-1610276594398)]
这就是上面提到的对于reference 参数不能使用literal传入参数 你必须pass a place 存放 这个double value
如果你想 pass in 一个几百行的数组一般使用 reference parameters 因为 使用 copy 将会占用很大的 memory空间
附录:关于函数的组成
[外链图片转存中…(img-K6LhST1i-1610276594399)]
查看手册看一些函数编写的例子,总结 compose 和 decompose 的问题