C++ Primer(第五版) 学习笔记

来源:互联网 发布:杭州淘宝拍照基地 编辑:程序博客网 时间:2024/05/16 14:50

如有不正确的地方,请读者及时指正,如有需要欢迎转载,谢谢!

C++语言基础:
1. C++11增加了long long 类型,表示最小尺寸为64的整数。
2. 浮点数运算使用double,通常运算速度更快。
3. 超过int容量时使用long long类型,无符号数使用unsigned。
4. 切勿混用带符号类型和无符号类型,会隐式向无符号类型转换。
5. 指定字面值的类型前缀L表示宽字符,后置ULL表示无符号长整数。
6. C++中变量和对象通常意义下是一样的。
7. 对象是具有某种数据类型的内存空间。
8. 对象的初始化与赋值是两回事。
9. 引用不是对象,它只是为一个已经存在的对象所起的另一个名字。
10. 指针是指向另一种类型的复合类型,指针对象本身是一个对象,通常所说的指针指的就是指针对象。
11. 默认情况下,const对象只在文件内部有效,如果想在多个文件内共享const对象,必须在定义和声明之前添加extern关键字。
12. const对象一般是在.cpp中extern定义并初始化,在头文件中extern声明使用。
13. 常量引用是对const的引用,不能使用引用修改对象的值。
14. 不允许将非常量的引用绑定到常量对象上,但允许将常量引用绑定到非常量对象上。
15. 指向常量的指针不能用于改变其所指对象的值。
16. typedef和using两种方法可以为类型创建别名。
17. C++11引入auto关键字,让编译器替我们去分析表达式所属的类型。
18. C++11新标准规定可以为类内部成员提供一个类内初始值。
19. 头文件保护符依赖于预处理命令,与#pragma once相比,#ifndef易于跨平台。
20. 头文件不应该包含using声明。
21. C++语言规定&&操作符只有当左侧运算对象为真时才检查右侧对象,||操作符只有当左侧运算符为假时才检查右侧对象。
22. 运算符操作数之间存在算数转换和整形提升。
23. 尽量避免类型强转,必须时使用static_cast<>()来进行类型强转。
24. 函数无须改变形参引用的值,最好将其声明为常量引用。
25. 当以数组为形参时,通常显示的传递一个表示数组大小的形参。
26. main函数int main(int argc, char *argv[]){...}, argc为数组元素个数,argv[0]一定保持的是程序的名字,可选实参从argv[1]开始,argv[argc]一定为0。
27. C++11提供了可变数量的形参initializer_list<T> 来表示形参列表,类似于标准库的vector使用方法。
28. 在含有return语句循环后面也应该有一条return语句,因为循环可能不会被执行就退出,很多编译器无法发现此类错误。
29. 不要返回局部对象的引用或指针。
30. C++语言中名字查找先于类型检查。
31. 可以使用预处理命令来编写自己的条件调试代码,如#ifndef NDEBUG ... #endif。
32. C++编译器,预处理器提供了5个对于调试很有用的名字__func__,__FILE__,__LINE__,__TIME__,__DATE__。

类:
1. 类的基本思想是数据抽象和封装,数据抽象是一种依赖于接口和实现分离的编程(及设计)技术。
2. 类的成员函数通过一个名为this的隐藏形参来访问调用它的那个对象。
3. 默认情况下this是一个隐式的指向类类型的常量指针,保存的是调用对象的地址,不允许改变this的指向,但可以改变this指向的对象。
4. 类允许将const关键字置于成员函数参数列表之后,则该函数为常量成员函数,不能在其中改变调用它的对象的内容,成员函数的定义必须与声明匹配。
5. 与类相关的非成员函数可以声明为类的友元函数,类内部前置friend声明,友元函数可以访问类的私有成员,类外函数声明和定义不加friend前缀。
6. 类的构造函数和类同名,没有返回值,支持重载,任务是初始化类对象的数据成员,不能被声明为const的。
7. 类包含内置类型和复合类型的成员,只有当这些成员全都被赋予了类内的初始值时,这个类才适合使用合成的默认构造函数。
8. C++11允许使用后置关键字 = default指定默认构造函数和析构函数,带有形参的构造函数通常使用构造函数初始值列表来初始化数据成员。
9. 访问说明符加强类的封装性,public成员可以被整个程序访问,private成员仅可被类的成员函数访问。
10. class和struct关键字定义类的唯一区别是默认的访问权限不一样。
11. 类内部可以使用inline关键字显示的声明内联函数,类外定义,内联函数一般与类定义在同一个头文件中。
12. 类的前向声明,可以定义类的指针和引用,也可以作为参数或者返回值类型。
13. 类成员初始化的顺序与它们在类定义中出现的顺序一致,构造函数初始化列表不影响这个顺序。
14. 具有单个形参的构造函数声明可以前置explicit关键字来阻止隐式转换,定义前不加explicit关键字,只能用于直接初始化。
15. 前置static表明该成员为类的静态成员,归整个类共有,类的静态成员需要在.cpp中单独定义,定义不加static关键字,具有全局作用域。
16. 静态成员函数不与任何对象绑定,不能使用this指针,只能使用类的静态数据成员。

拷贝控制:
1. 拷贝构造函数,拷贝赋值运算符,析构函数统称为类的拷贝控制。
2. 拷贝构造函数与类名相同,无返回值,参数几乎总是一个自身类类型的const引用,通常不应该是explicit的。
3. 合成的拷贝构造函数会将其参数的非static成员依次拷贝到正在创建的对象中,指针只是浅拷贝。
4. 拷贝初始化将在下述情况中发生:使用=赋值运算符赋值新的对象;将一个对象作为实参传递给一个非引用类的形参;返回一个非引用类型的对象;用花括号列表初始化4一个数组的元素或聚合类的成员;标准库的insert和push等操作。
5. 使用关键字“operator运算符”作为函数名来重载运算符,常见的重载有=,==,!=,<<,+,<等。
6. 当重载运算符是一个成员函数时,this绑定到左侧运算对象,参数数量比运算对象少一个。
7. 重载赋值运算符=通常应该返回一个指向其左侧运算对象的引用,参数一般是自身类类型的const引用。
8. 析构函数释放对象使用的资源,并销毁对象的非static数据成员,当一个对象被销毁,就会调用析构函数。
9. 析构函数由~加类名构成,没有返回值,不接受参数,不能被重载,动态分配内存的类几乎总是需要析构函数。
10. 隐式销毁一个内置类型的指针,不会delete掉它所指向的对象。
11. 需要析构函数的类肯定也需要拷贝构造函数和重载赋值操作,拷贝和赋值几乎是等同的存在。
12. C++11允许后置=delete来阻止初始化,拷贝和赋值操作,旧标准通常声明其函数为private的来阻止这些操作。
13. 标准库IO类属于不能拷贝的类型,只能通过引用参数或引用返回值传递。

面向对象:
1. 面向对象的程序设计的核心思想是数据抽象、继承和动态绑定。
2. 基类需要将两种函数区分开来,希望派生类进行覆盖的函数和希望派生类直接继承的函数。
3. 基类希望派生类覆盖实现的函数声明为虚函数virtual,C++11允许后置override显示表明派生类改写的虚函数。
4. 基类通常应该定义一个虚析构函数,即时该函数不执行任何操作也是如此。
5. 当且仅当使用一个基类的引用或指针调用一个虚函数时将发生动态绑定。
6. 通常建议在设计时只使用单继承和公有继承public。
7. 基类希望派生类可以访问,但禁止其他用户访问的成员定义为protected。
8. 派生类不能继承基类的构造函数,拷贝构造函数,但能够继承析构函数。
9. 派生类对象含有与其基类对应的组成部分,允许将基类的指针或引用绑定到派生类对象的基类部分上,此时编译器会隐式的执行派生类到基类的类型转换。
10. 派生类向基类的隐式转换只对指针或引用类型有效,不存在基类向派生类的隐式转换。
11. 如果不特别指明,派生类对象的基类部分会像数据成员一样默认初始化,特别指明的情况下需要通过基类构造函数进行初始化。
12. 派生类对象的初始化顺序为首选初始化基类部分,然后按照声明的顺序依次初始化派生类成员。
13. 遵循基类的接口,应该只通过调用基类的构造函数来初始化从基类继承来的成员,不应该由派生类直接赋值,派生类构造函数应该只初始化它的直接基类。
14. 基类的静态成员在派生类中也只存在唯一实例。
15. C++11提供了防止继承发生的方法,在类的定义时后置关键字final。
16. 当使用基类的引用和指针时,实际上我们并不清楚该引用或指针所绑定的对象的真实类型。
17. 对非虚函数的调用和通过对象进行的虚函数调用都是在编译时绑定的。
18. 基类的一个函数被声明为virtual虚函数,在派生类中仍然是虚函数,派生类可以不使用virtual关键字。
19. 派送类覆盖了某个虚函数时,该函数的名称、形参、返回值必须基类中的完全匹配。
20. 派生类的虚函数调用它覆盖的基类的虚函数版本时需要使用作用域运算符::来回避虚函数机制。
21. 基类可以将虚函数后置=0声明为纯虚函数,纯虚函数无须定义。
22. 含有纯虚函数的类是抽象基类,抽象基类不能实例化对象,抽象基类负责定义接口,派生类覆盖实现接口。
23. 派生类的成员将隐藏同名的基类成员,除继承的虚函数外,派生类最好不要重用其基类的名字。
24. 理解派生类函数的调用过程中的名字查找和类型检查规则对于理解C++继承至关重要,名字查找先于类型检查。
25. 如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为。
26. 容器和存在继承关系的类型无法兼容,当我们希望在容器中存放具有继承关系的对象时,我们通常的做法是基类的指针(更好的选择是智能指针)而非对象。
27. 继承为“是一种Is a”的关系,组合为“有一个has a”的关系,尽量多使用组合而非继承。
28. 友元关系不能被传递,也不能被继承。
29. 派生类的合成默认构造函数会先调用基类的合成默认构造函数。
30. 如果在派生类中显式调用基类的构造函数,只能在构造函数的初始化列表中调用,并且只能调用其直接父类的。
31. 当使用struct包含另外一个struct时,实际为C语言方式的继承,通常将内部的struct置于最前,此时可以用于类型强转。
32. 析构函数的执行顺序与构造函数完全相反。

异常处理:
1. 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。
2. 异常处理使用try{}catch(){ }语句块捕获,使用throw语句抛出异常。
3. 异常抛出后,捕获异常的过程为栈展开过程,沿着嵌套函数的调用链不断向外层查找,如没有被捕获,则终止当前程序。
4. 栈展开过程中对象按照作用域被自动销毁,调用的析构函数不应该抛出异常。
5. 编译器使用异常抛出表达式throw对异常对象进行拷贝初始化,意味着该对象类必须存在可访问的析构函数和拷贝构造函数,异常处理完毕后异常对象被销毁。
6. 当抛出一条表达式时,该表达式的静态编译时类型决定了异常对象的类型,如抛出一个基类指针,则实际指向的派生类对象被切断,只有基类部分被抛出。
7. catch子句的异常声明类型决定了处理代码所能捕获的异常类型,支持引用和非引用类型,通常情况下,最好使用引用类型。
8. catch语句的匹配过程按照其出现的顺序逐一匹配,所以越是专门的catch越应该置于前端,基类则放在最后。
9. 捕获所有异常的catch语句为 catch(...){ },可以匹配任意的异常类型。
10. C++11中提供了noexcept说明某个函数不会抛出异常,方便编译器优化,语法是在声明和定义语句后置,如void func(int a) noexcept;。
11. 标准库异常类继承关系,exception-->(bas_cast,runtime_error,logic_error,bad_alloc)-->(invalid_argument,out_of_range,overflow_error,...)。
12. 标准库异常对象存在一个what虚函数成员,返回类型const char*,负责返回用于初始化异常对象的信息。
13. 我们可以编写自定义的异常类,一般都是继承标准库的异常类扩展。
14. Google的C++编程规范表明程序要根据适当情况慎重选择使用异常捕获和处理。
15. 异常发生期间正确执行了清理工作的程序被称作“异常安全”的代码。

命名空间:
1. 命名空间是为防止命名冲突提供了可控机制,每个命名空间有自己独立的作用域。
2. 定义命名空间 namespace NS{  },命名空间可以嵌套定义,C++11引入内联命名空间inline namespace。
3. 命名空间可以是不连续的,可以跨越多个文件,多处定义同一个命名空间则为打开。
4. 命名空间的定义和使用都应该在#include后面,否则即是将整个.h文件名字都重新定义在命名空间内,容易造成错误。
5. 一般情况下,在.h文件内部定义或打开命名空间,.h文件一般包含属于该命名下的类型、变量和函数等成员。
6. 命名空间的使用包括using声明using NS::name;导入一条名字。 using指示using namespace NS;导入命名空间所有名字。
7. 一般在.h对应的实现cpp文件中使用using指示,通常在文件顶部#include下面;在用到该命名空间成员的其他.cpp中使用using声明,头文件不应该使用using声明。
8. 可以为命名空间创建别名简化命名空间的使用,如namespace primer = cplusplus_primer;
9. 全局命名空间以隐式的方式声明,并且在所有程序中存在, 如::member_name。
10. 未命名的命名空间namespace { },该空间内的变量拥有静态生命周期,一般用来取代文件内的static静态声明,该空间的名字不需要前缀,直接使用。
11. 与其他命名空间不同,未命名的命名空间仅在特定文件内部有效,不能跨越多个文件,多个文件内定义的未命名空间相互独立,毫无关联。




0 0
原创粉丝点击