[0.x] 前言 🎓

对C++了解的越多,你就会越讨厌这门语言……

[0.1.x] 课程相关


- 课程信息
- 教师:翁恺
- 课时:16节
- 智云课堂链接
- 阅读材料
- Bruce Eckel’s Programming Blog
- Thinking in C++
- Thinking in C++, 2nd Edition, Volume 1
- Thinking in C++, 2nd Edition, Volume 2
- Bjarne Stroustrup’s Homepage
- The Design and Evolution of C++
- cppreference
- C++ Primer
- 菜鸟教程(可以用于中文术语对照)
- XXJJ的笔记1 C++面向对象

[0.2.x] 学习日志

12345
123
123
12345
【2022】
[1.13] L1
[1.14] L2
[1.15] L3 L4
[1.16] L5
[1.17] L6
[1.18] 整理
[1.19] L7
[1.21] L8 T1
[1.22] L9
[1.23] L10
[1.24] L11 T2 整理
[1.25] L12 整理
[1.26] L13 T5
[1.27] L14
[1.28] L15 L16
[1.29] T6

[0.3.x] 笔记结构

| > 内容上,笔记中大多数情况下只记录我认为有必要记录下来的事情,加上智云自己的问题,所以可能和课程内容相比有些缺失。

  • 主要按照课程进度进行追踪记录
  • 对应的知识笔记会在对应知识点附近以🔗&超链接的形式出现

| | —- |

[0.4.x] 练习&作业


[1.x] 课程笔记 📖

[1.1.x] L1


- cin``cout本质上都是变量,而对应的<<``>>是经过重载的属于这些变量的位移运算符,表示的含义是将之后的 tocken 塞入输出流 / 从输入流读取,并且返回一个 cin / cout 变量。
- 🔗**string**区别于int等,是一个类,形如name.length()的语句体现了面向对象语言的思想。
- 🔗动态内存分配new``delete[01:23:00]

[1.2.x] L2

|
- 🔗引用reference [00:05:00]-[00:32:00]
- “如果你要传一个(大)结构体进函数,那么最好的办法是传一个指针而不是结构体。”——《C圣经》
- 然而在所有传入指针的函数中,都存在一个安全问题——即我不希望这个函数会对我传入的这个指针指向的变量有影响,所以有这样一种操作:
- void function(**const** typename *p);可以让函数虽然能得到地址,但无法修改变量
- 声明定义的区别:
- 对于编译器来说,声明(declaration)不产生实际代码,仅仅表示“存在这么一个东西”;而定义(defination)产生实际代码,它直接决定了“这个东西是什么”这件事
- eg1.在定义全局变量x之前(代码文本上的“之前”)的位置想使用该全局变量x,可以使用extern int x;声明它(也就是说不能在这里给它赋初值)
- eg2.struct node{ int x,y; };本身和其成员变量在此处都算声明

  • 🔗引入类的概念
    - this指针[00:55:00]
    - “所有普通的成员函数都有一个隐藏的参数,是它参数表里的第一项,是Cpp编译器做的语法糖,而这个参数就叫this,是这个结构体的指针。”
    - “成员函数在使用的时候会默认在引用成员变量的地方前加上this指针,以便我们更方便地写代码。”即我们可以直接在成员函数中使用成员变量。
    - “C++98所有的代码都是可以被翻译成C的。”
    - “在C++中,classstruct基本上一样,区别在于访问权限。”
    - 在class中,你可以用public:private:来标识哪些是公开的,哪些是私有的
    - 一般来说,数据成员应该是私有的,方法是对外公开的
    - 🔗**::** 范围解析运算符resolver
    - container一般指存放对象的容器,常见操作为get()put()
    - stash一般指可以扩大的container,且stash可以放任何同类型的东西,常见操作为add()fetch()
    - 对象的关系
    - 对象抽象对象实例
    | | —- |

[1.3.x] L3

|

- OOP Characteristics [00:17:00]
- Everything is an object.
- A program is a bunch of objects telling each other what to do by sending messages.
- “这里的关键词是”what”,为什么,因为我只需要一个结果,这是一个请求,我不需要告诉它如何做,不是”how”。”
- “对象和对象之间的交互方式是要求对方做什么,而不是指点对方做什么。”
- Each object has its own memory made up of other objects.
- Every object has a type.
- All objects of a particular type can receive the same messages.
- “这句话也需要反过来理解,所有能接收相同消息的我们当做同一种类型的对象。”
- “其实oop的核心就是封装、继承、多态。”
- 🔗构造函数 C’tor **(constructor) [00:22:00]
- 🔗
析构函数 D’tor (destructor) [00:55:00]
- “通常我们在析构函数里做的事情是处理(消除)内存以外的其他资源。”
-
definition of a class**
- 我们希望一个类的被分离放在两个文件里
- 在xxx.h文件中,我们希望存放类的声明及其函数的原型
- 在xxx.cpp文件中,我们希望存放这些函数的body
- 而这两个文件的前缀应该相同
- 也希望一个头文件只声明一个类
- 并且希望它符合 “standard header file structure” (下方)
- Compile Unit [01:15:00]
- header = interface [01:20:00]
- standard header file structure
```cpp

ifndef HEADER_FLAG

define HEADER_FLAG

// …

endif // HEADER_FLAG

  1. <br />- 抽象思维 [01:24:00]<br />- TDD测试驱动开发<br /> |
  2. | --- |
  3. <a name="CY4Zb"></a>
  4. ## [1.4.x] L4
  5. | <br />- 构造函数的初始化列表 [00:23:00] (没仔细讲,提了一嘴)<br />```cpp
  6. Clock::Clock():
  7. hour(24), minute(60) // hour和minute作为Clock类的两个成员对象做初始化
  8. {
  9. // ...
  10. }


- 容器Container(Collection) [00:33:00]
- 🔗STL 标准模板库
- 泛型
| | —- |

[1.5.x] L5


- 🔗函数 [00:04:00]
- 🔗初始化
- 初始化列表 [00:26:00]
- 构造函数支持🔗重载(Overload), 类似Go的interface,使用满足参数条件的那个
- 默认构造器的必要性 [00:50:00]
- 🔗函数默认参数Default Argument
- 🔗C++ access control [01:14:00]
- 🔗内联函数inline

[1.6.x] L6


- 🔗**const**[00:04:00]
- 指针&const相关 [00:16:00]
- 指针与对象相关 [00:30:00]
- 对象&const相关 [00:34:00]
- 🔗static [00:45:00]
- namespace [01:15:00]
- using [01:18:00]
- 继承(Inheritance)
- 嵌入对象(Embedded Objects),需要被初始化或者存在默认构造器,我们希望他们是private的

[1.7.x] L7

因为智云课堂的关系,我实在听不清内容,所以这节课我以文本学习的形式为主
主要参考资料为Thinking in C++(参见[0.1.阅读材料])
🔗继承Inheritance
🔗继承中的权限控制

[1.8.x] L8


- 🔗多态Polymorphism [00:09:00]
- 两个技术基础:
- 🔗向上造型Upcast[00:20:00]
- 🔗动态绑定Dynamic Binding
- 多态变量Polymophic Variable [00:52:00]
- 🔗切片Slice [00:15:00]
- 🔗向上造型Upcast
- 🔗虚函数virtual [00:38:00]
- 虚函数表 [01:00:00]
- 🔗动态绑定与静态绑定
- 🔗Override关系
- 子类override了父类的函数可以返回父类的函数的返回值的子类(这个情况只适用于指针和引用)
- 父类的函数返回一个水果,子类override以后可以返回一个苹果(但是你不能直接返回一个苹果对象)
- 本质和上面的向上造型类似


- 我们不希望出现重新定义父类中的非virtual函数的行为(出于效率考虑,我们更希望静态绑定)
- 重定义default value是没有效果的,因为这件事是在编译时刻进行的,但多态特性在运行时刻才会体现,所以不会起作用(参考视频中的说法,就是default value是由指针的“类模板”决定的,而非对象的“实际情况”决定的)


- 在Shape``Ellipse``Circle这个例子中,Shape的概念非常抽象(我可以让你绘制一个圆,但我无法让你绘制一个形状),所以Shape::render();并没有实际内容,我们可以通过定义Shape::render() = 0;来指定它是一个纯虚函数,此时Shape是一个抽象类,它不能用来制造对象实例
- 在习惯中,如果一个类没有成员变量,且所有函数都是虚函数,则它成为一个interface

[1.9.x] L9

| cpp void f(){ Stash Students(); // 这是一个函数声明而非对象声明,是为了向古老版本兼容 } xxjj详细讨论该问题的文章:无参构造对象时为什么不加括号
🔗拷贝构造函数Copy C’tor [00:30:00]
🔗重载运算符Overloaded Operators [01.19.00] | | —- |

[1.10.x] L10

| 一个操作在结果副作用的区分 [00:22:00]
🔗成员/全局 运算符重载选择策略
🔗运算符重载规范 [00:08:00]
🔗输入输出流与重载
- 🔗创造一个控制符 [00:42:00]
🔗赋值与拷贝构造 [00:43:00]
🔗重载类型转换
🔗初始化 [01:20:00]
函数传入传出 [01:23:00]
- 一般传入时我们更希望使用指针/引用而非直接传对象,有时更希望有const修饰\
- 具体来说:
- 如果你希望存储一个对象,那么就传入一个对象
- 如果你只希望得到它的值,那么传入一个常指针或常引用
- 如果你想对该对象做一些事,那么传入它的指针或者引用
- 如果你在函数内新建了一个对象,那么将整个对象传出
- 如果你希望传出的是一个指针,那最好只传出传入的指针
- 永远不要在函数内新建某些东西并传出它的指针
🔗左值与右值 [01:33:00]
- 右值引用的作用 [01:38:00]
| | —- |

[1.11.x] L11

| 🔗模板Template [00:05:00]
- 利用模板,你可以进行泛型编程
- 这意味着你可以将函数的类型当做参数
- 函数模板
- 类模板
weak关键字 [01:35:00] | | —- |

[1.12.x] L12

| 异常Exceptions [00:05:00]
image.png
assert[00:39:00]
- 常用于集成测试检查
image.png
image.png
image.png
- catch后的括号中可以使用...来捕捉任何异常;或者<typeName> &<refName>来捕捉异常抛出的对象,然后在后面使用<refName>。其中,前者(即catch(...))必须放在最后
- 在catch语句下再次throw;(之后不带参数),会将已经捕捉到的异常再次向上抛出
- 程序会根据catch书写的顺序检查异常,并且只处理catch到的第一个异常


继承与异常
image.png
image.png
- 在顺序方面,将某个异常类的子类放在该异常类之后会被认为是不合理的,因为无论如何这个异常类的子类都不会被捕捉到
image.png
- 可以用下图方式来声明一个函数可能抛出什么异常
image.png
- 如果函数中抛出了没有在一开始声明的异常,会被认为是unexpected exception
image.png
- 如果catch()直接捕捉对象而非引用,容易造成内存泄漏
| | —- |

[1.13.x] L13

| 引用计数reference counting:记录指向该对象的指针数量

智能指针Smart Pointer [00:06:00]
- 模板
- 遗传
- 引用计数
image.png
image.png

主要是智能指针实现的解析,但是感觉看课比如看代码,所以就先这样吧x
| | —- |

[1.14.x] L14

| 类的设计方法
- 目标:
- 易于理解
- 易于维护
- 具有可重用性
- 责任驱动设计Responsibility-driven design
- 耦合Coupling
- 内聚Cohesion
- 重构Refactoring


耦合关系
- 耦合指两个分开的单元之间的联系
- 如果两个单元联系很紧密,我们称之为紧耦合
<解耦>
- 我们更希望是松耦合,这样理解代码会简单很多
- 实现松耦合的技术:
- 回调call-back [00:36:00]
- 中央消息机制message mech [00:37:00]
内聚关系
- 单个的单元负责的事情需要有所限制(这个单元可以是函数、对象甚至是变量)
- 如果一个单元只有一个单一的任务,则认为它具有高内聚
- 我们更希望是高内聚,即尽可能让一个单元仅负责一个任务


一种Bad Design的标志——代码复制Code duplication

handler

enum | | —- |

[1.15.x] L15

| #include <functional>
lambda函数

流Stream
- 特征:[一维] [单向]
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

继承构造函数
image.png
image.png
image.png
image.png
image.png
**using**派生类无法获得默认参数值!
image.png

委派构造函数
image.png
image.png
image.png
但是我们要避免出现循环链接 | | —- |

[1.16.x] L16

| 浅拷贝 & 深拷贝
浅拷贝
- 编译器自动执行的拷贝
- 在有指针的情况下,这种拷贝是有害的
移动构造
image.png

image.png

- “引用”的初始化一般被称为“绑定”
image.png
image.png

完美转发
image.png
image.png
image.png | | —- |