C++ Primer 学习笔记
第一周
2022年8月8日
函数的定义
P2
一个函数的定义包含四部分:返回类型、函数名、形参列表、函数体
main函数的返回类型必须为’int’返回值
P2-3
return
。结束函数的执行
main的返回值被用来指示状态。返回值0表明成功,非0的返回值的含义由系统定义,通常用来指出错误类型标准输入输出对象
P5
iostream库定义了四个IO对象。标准输入cin、标准输出cout、标准错误cerr、clog输出日志操纵符
P6
endl
。结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流命名空间
P7
std
初始化
P7
初始化一个变量,就是在变量创建的同时为它赋予一个值文件结束符
P14
Windows: Ctrl+Z,Enter/Return
UNIX: Ctrl+D
2022年8月9日
- 成员函数
P20
item1.isbn()
。item1
对象名;.
点运算符;isbn
成员函数/方法;()
调用运算符
调用名为’item1’的对象的成员函数’isbn’,此函数返回item1中保存的ISBN书号
本周tips
修改代码时,不要忘记同时更新注释(P8)
本周评语
main函数是一种约定,当你编译完程序就会生成一个可执行程序exe,双击的时候操作系统会开一条进程并加载你这个exe文件,然后寻找main函数并执行,当执行结束进程退出时候,这个返回值就是告诉操作系统进程是否是正常结束,正常结束就是返回0,不正常结束就是返回其他值
第二周
2022年8月16日
算术类型
P30-31
算术类型(字符、整型数、布尔值、浮点数)和空类型
整型(整型数、字符、布尔值)和浮点型
比特(0和1)、字节(可寻址的最小内存块)、字(存储的基本单元)带符号类型和无符号类型
P31-32
除去布尔型和扩展的字符型之外,其他整型可以划分为’带符号的’和’无符号的’
带符号类型可以表示正数、负数或0;无符号类型仅能表示大于等于0的值
字符型分为三种:char
、signed char
、unsigned char
2022年8月17日
类型转换
P33-35
布尔值赋给非布尔值,若初始值为true,结果为1
当表达式里既有带符号类型又有无符号类型,带符号数会自动地转换成无符号数字符和字符串字面值
P36
单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符则构成字符串型字面值
编译器在每个字符串的结尾处添加一个空字符\0
,因此,字符串字面值的实际长度要比它的内容多1
两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体转义序列
P36
我们也可以用泛化的转义序列,其形式是\x
后紧跟1个或多个十六进制数字,或者\
后紧跟1个、2个或3个八进制数字\
后跟着的八进制数字超过3个,只有前3个数字与\
构成转义序列
2022年8月19日
对象
P39
通常情况下,对象是指一块能存储数据并具有某种类型的内存空间初始化
P39
初始化≠赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代
2022年8月20日
列表初始化
P39
C++11新标准,初始化对象或赋值都可以使用”列表初始化”默认初始化
P40
内置类型,定义于任何函数体之外的变量被初始化为0,定义在函数体内部的内置类型变量将不被初始化
类的对象如果没有显式地初始化,则其值由类确定
2022年8月21日
变量声明和定义的关系
P41
声明,使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明
定义,负责创建与名字关联的实体
变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值标识符
P42
C++为标准库保留了一些名字
用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头
定义在函数体外的标识符不能以下划线开头作用域
P44
因为全局作用域并没有名字,所以当作用域运算符::
的左侧为空时,向全局作用域发出请求获取作用域操作符右侧名字对应的变量
本周tips
如何选择类型
P32
当明确知晓数值不可能为负时,选用无符号类型
整数运算,选用int,int不足直接用’long long’
算术表达式,不要用char或bool,存放字符和布尔值使用
浮点数运算,选用double变量命名规范
P42
标识符要能体现实际意义
变量名一般用小写字母,如index
用户自定义的类名一般以大写字母开头,如Sales_item
如果标识符用多个单词组成,则单词间应有明显区分,如student_loan或studentLoan
本周评语
了解一点代码到exe的基本过程吧:每个代码cpp文件,会把include头文件展开,然后一个cpp编译生成一个obj文件,这个过程成为编译,最后link会把所有依赖到的obj里面的代码链接到一块,生成可执行程序exe,这个过程成为链接
那么link怎么找到哪些代码是最终需要的呢,答案就是从main函数出发,main用到的就需要放到最终的exe里面,然后是main用到的再用到的,是一颗树的结构。main用到函数A,B,函数A,B用到函数C,D,E,F等等
所以这也是为什么你写两个cpp文件,里面都写一个同样的函数比如funcA,如果返回值,参数列表都一样的话,最终会报一个redefination,重复定义的错误,因为link的时候不知道去链接哪一个
第三周
2022年8月24日
引用
P46
引用即别名,引用并非对象,引用必须初始化
定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用指针
P47-48
指针本身就是一个对象,允许对指针赋值和拷贝,在指针的生命周期内它可以先后指向几个不同的对象
指针无须在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值
指针存放的是某个对象的地址,除0、nullptr、其他指针、NULL外无法直接赋值&
取地址符,获取对象的地址*
解引用符,访问指针的对象空指针用
nullptr
初始化(C++11新标准引入)void*指针
P50
“void*“是一种特殊的指针类型,可用于存放任意对象的地址
“void*“指针指向的对象不能直接操作。void*可以用来和别的指针比较、作为函数的输入或输出、或者赋给另外一个void*指针指向指针的引用
P52
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用int i=42; int *p; int *&r=p;
本周评语
所有变量,包括指针无非都是内存条上的几个字节,不同类型只是编译器用不同的方式去解读这几个字节
int,那么这个4个字节对应一个整数去理解
int*,那么这4个字节对应的是一个内存条地址(32位系统地址是4个字节,64位系统地址是8个字节)
第四周
2022年9月3日
const限定符
P54
如果想在多个文件之间共享const对象,必须在变量的定义或者声明之前添加extern关键字
常量引用=指向常量的引用,常量指针≠指向常量的指针(见下5.顶层const笔记)const的引用
P55
引用的类型必须与其所引用对象的类型一致,但是有两个例外
第一个例外是,在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可
第二个例外在P534指针和const
P56
指针的类型必须与其所指对象的类型一致,但有两个例外
第一个例外是,允许令一个指向常量的指针指向一个非常量对象
第二个例外在P534const指针
P56
const int *p
指向常量的指针,可以不初始化,不可以通过指针改变所指对象的值int *const p=&i
常量指针,必须被初始化,指针本身的值不可以改变,修改所指对象的值取决与所指对象的类型顶层const
P57
顶层表示指针本身是个常量,底层表示指针所指的对象是一个常量
顶层const可以表示任意的对象是常量,如算术类型、类、指针等;底层const则与指针和引用等复合类型的基本类型部分有关
指针类型既可以是顶层const也可以是底层const当执行对象拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说非常量可以转换成常量,反之则不行
constexpr常量表达式
P58-59
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式
C++11新标准规定,允许将变量声明为constexpr
以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化字面值类型
P59
到目前为止接触过的数据类型中,算术类型、引用、指针都属于字面值类型
自定义类、IO库、string类型则不属于字面值类型,不能被定义成constexpr
其他字面值类型将在P267、P736介绍一个constexpr指针的初始值必须是nullptr、0、存储于某个固定地址中的对象
函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量
定义于函数体之外的对象其地址固定不变,能用来初始化constexpr指针(P184提及)
允许函数定义一类有效范围超出函数本身的变量,这类变量和定义在函数体之外的变量一样也有固定地址,因此,constexpr引用能绑定到这样的变量上,constexpr指针也能指向这样的变量
2022年9月4日
类型别名
P60-61
传统定义类型别名用typedef
,例:typedef double wages;
C++11新标准定义使用别名声明,例:using SI=Sales_item;
typedef char * pstring;
pstring是char*的别名,const pstring cstr=0;
c是指向char的常量指针(这点容易搞混P61)auto类型说明符
P61
C++11新标准引入auto
类型说明符,用它能让编译器替我们去分析表达式所属的类型
auto定义的变量必须有初始值,auto能在一条语句中声明多个变量,该语句中所有变量的初始基本数据类型都必须一样
auto一般会忽略掉顶层const,保留底层const。用const auto
变成顶层const。auto的引用会保留顶层constdecltype类型指示符
P62-63
C++11新标准引入decltype
类型指示符,它的作用是选择并返回操作数的数据类型(包括顶层const和引用)decltype((variable))
的结果永远是引用,decltype((variable=variable))
的结果也是引用自定义数据结构
P65
C++11新标准规定,可以为数据成员提供一个类内初始值预处理器
P68
头文件保护符,#define
指令把一个名字设定为预处理量,ifdef
当且仅当变量已定义时为真,ifndef
当且仅当变量未定义时为真,一旦结果为真,则执行后续操作直至遇到#endif
指令为止
本周tips
赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果i是int,则表达式i=x的类型是int&,所以
decltype((variable=variable))
的结果也是引用
第五周-第六周
复习·本周评语
类的实例叫对象(比如class A; A a;)这里a就是对象,类型为class A。int,char这些是内置变量类型,不叫对象,因为不是类的实例。c++是c语言的扩展,c语言是没有类跟对象的概念的,但依然有int char 这些内置变量类型。指针变量也是变量,即内存上的一个位置,里面存放的是一个内存地址,仅此而已,至于编译器怎么理解这个地址,就看指针的类型了,如果是int,那就表示这个地址往后4个字节存放的是一个int变量,如果是double,那么就是这个地址往后8个字节是一个double变量,等等
const int i;是一个常量变量,咬文嚼字没必要的,他的意思就是告诉编译器这个i变量运行时不可改变,运行时只读(但实际上还是有无数种方法可以改掉),但是编译时不一定能确定它的值,如果编译时能100%确定应该用constexpr int i = 100;这样就是编译时确认的常量。关于这个你跑一下下面这段代码,你会非常惊讶运算的结果
1 |
|
const在c++里面有常量跟只读两层含义,容易产生混淆,c++新标准推荐常量用constexpr,只读用const,这样可以清晰明了很多
“自定义类、IO库、string类型则不属于字面值类型,不能被定义成constexpr”这句话可以归纳成一句,类(class)不属于字面值类型,io库,string只不过是c++库帮你写好的类(class)而已,类的对象有特殊的逻辑,后面你会学到,所以不能用constexpr
第七周
2022年9月25日
命名空间using声明
P74
头文件不应包含using声明直接初始化和拷贝初始化
P76
如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去
如果不使用等号,则执行的是直接初始化string对象上的操作
P77-81
string的操作(P77)
使用>>执行读取操作时,string对象会自动忽略开头的空白(空格符、换行符、制表符),并从第一个真正的字符开始读起,直到遇见下一处空白为止
“getline函数”的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)
“size函数”返回的是一个string::size_type类型的值,这是一种无符号类型,不能与带符号类型混用
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧运算对象至少一个是string。string s = s1 + "," + "asd"
合法(原理和连续输入连续输出一样)
第八周
2022年10月1日
处理String对象中的字符
P82
cctype头文件中的函数(P82)“范围for语句”处理String对象中的字符
P82-83
如果想要改变string对象中字符的值,必须把循环变量定义成引用类型“下标运算符”处理String对象中的字符
P84-85
下标运算符[]
接受的输入参数是string::size_type
类型的值
2022年10月2日
标准库类型vector
P86-89
“实例化”,编译器根据模板创建类或者函数的过程。当使用模板时,需要指出编译器应把类或函数实例化成何种类型
初始化vector对象的方法(P87)
各种初始化的区别(P88)
vector对象支持列表初始化,使用圆括号提供的值可以说是用来构造vector对象的,使用花括号则是列表初始化,但当花括号里的值无法初始化vector对象则会变成构造vector对象向vector对象中添加元素
P90-91
vector对象能高效增长(P91)
如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环,具体原因见(P168)
本周tips
cout
不被认为是C++关键字,如果定义一个名为cout
的变量可能引发难以排查的错误C++标准库兼容了C语言的标准库,C++将这些标准库命名为”c’name’”,在cname的头文件中定义的名字从属于命名空间std,而(将cname定义成).h头文件中的则不然(P82)
&&
逻辑与运算符,C++语言规定只有当左侧运算对象为真时才会检测右侧运算对象的情况(P85)
第九周
2022年10月4日
- 其他vector操作
P91-94
其他vector操作(P91)
vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素
通过下标访问不存在的元素不会被编译器发现,在运行时会产生缓冲区溢出的错误,解决的方法是尽可能使用”范围for”语句
2022年10月6日
迭代器
P95
对迭代器而言,其对象是容器中的元素或者string对象中的字符
迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置,其他所有情况都属于无效
“begin成员”负责返回指向第一个元素(或第一个字符)的迭代器
“end成员”负责返回指向容器(或string对象)尾元素的下一位置的迭代器,该迭代器指示的是一个本不存在的尾后元素
end成员指向的迭代器没有实际含义,仅是个标记,常被称为”尾后迭代器”或”尾迭代器”。如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器迭代器运算符
P96
标准容器迭代器的运算符(P96)
2022年10月7日
将迭代器从一个元素移动到另一个元素
P96-97
泛型编程(P97)
for循环中用!=、==而非<,因为所有标准库容器的迭代器都定义了==和!=,但是它们中的大多数都没有定义<运算符
因此,只要我们养成使用迭代器和!=的习惯,就不用太在意用的到底是哪种容器类型for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
迭代器类型
P97
拥有迭代器的标准库类型(严格来说,string对象不属于容器类型)使用iterator
和const_iterator
来表示迭代器类型
iterator能读写对象中的元素,const_iterator只能读取对象中的对象。如果vector对象或string对象是一个常量,只能使用const_iterator,如果不是常量,两者都能用
术语:迭代器和迭代器类型(P97)begin和end运算符
P98
C++11新标准引入了两个新函数,分别是cbegin和cend。不论vector对象或string对象本身是否是常量,返回值都是const_iterator
如果对象只需读操作而无须写操作的话最好使用常量类型(const_iterator)(P191)结合解引用和成员访问操作
P98
解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员(*it).empty()
,该表达式的含义是先对it解引用,然后解引用的结果再执行点运算符
(*it).empty()的圆括号必不可少,具体原因见(P121)
“箭头运算符->
“,把解引用和成员访问两个操作结合在一起,it->mem等于(*it).mem