【C++】深蓝学院课程:C++基础与深度解析 - 课程知识点目录

第5章: 表达式基础与详述(类型转换)

第3节: 表达式基础:类型转换

28:【视频】类型转换

  • 整型提升和浮点提升(无损转换)
  • reinterpret_cast:主要用于指针相关的类型转换
  • 为什么不建议使用C风格转换:因为可能会调用 reinterpret_cast 从而使得转换的结果并非预期

第9章: 动态内存管理

第2节: 智能指针

64-3:【视频】智能指针—unique_ptr

  • unique_ptr不支持复制,但可以移动

第11章: 泛型算法:值捕获,[=]

Keywords: lambda lambda表达式

11.1 第1节: 泛算法型

73:【视频】泛型算法 IV:流迭代器

  • 流迭代器: istream_iterator / ostream_iterator
  • istream_iterator构造函数:constructs a new istream_iterator
  • 使用istream构造istream_iterator:istream_iterator( istream_type& stream );

74-1:【视频】bind

  • std::copy_if:在复制容器时可限定加入的条件,类似于python中的filter()
  • std::bind:实现高阶函数绑定,类似于python中的functools.partial

74-2:【视频】Lambda表达式 I

  • 推荐教材:《C++ Lambda Story》
  • Lambda表达式的捕获特性
  • 值捕获:auto lamb = [=] {...} - [28:12]
  • 可以使用 C++ Insights 来查看lambda表达式的等价类实现
  • *this捕获:用来避免悬挂指针带来的问题

74-3:【视频】Lambda表达式 II

  • 说明符:mutable / constexpr / consteval(C++20)
  • 使用const auto&避免因为类型定义不同导致的对象构造操作

75:【视频】泛型算法改进——ranges (C++20)

  • std::ranges可以使用容器而非迭代器作为输入

第12章: 类与面向对象编程

第1节: 结构体与对象聚合

78-1:【视频】part I

  • 结构体的定义要加分号表示结束
  • 当结构体只有声明而没有定义时,实例化对象会报错:error: aggregate ‘Str1 xxx’ has incomplete type
  • 结构体的数据成员不能用auto声明,即使在声明时给定了初始值(编译器会报错,[Cpp_struct_member_auto])
  • 结构体的聚合初始化(Aggregate initialization)
  • 成员限定符mutable:使const结构体对象的成员可以进行修改

78-2:【视频】part II

  • 使用符号定义数组长度需要编译期常量,在结构体中也是同样的规则 [Cpp_array_size_error]

79-1:【视频】part I

  • 成员函数的定义:(1)类内定义(隐式内联)(2)类内声明 + 类外定义

80:【视频】访问限定符与友元

  • 友元friend的声明可以放到public | protected | private的任意一个区域,效果是相同的
  • hidden friend函数的应用

第4节: 构造、析构与复制成员函数

81-1:【视频】part I

  • 构造函数的初始化列表:初始化即赋值(避免拷贝构造过程)
  • 使用初始化列表时,可以使用已经初始化的成员变量作为实参初始化下一个成员变量
  • 成员变量的初始化顺序由其声明顺序决定,跟初始化列表中的顺序无关
  • 基本类型的非静态数据成员会被随机初始化,例如:int
  • 调用缺省构造函数时避免 most vexing parse:调用缺省构造函数不要加括号 [Cpp_default_constructor_with_parentheses]
  • 使用default关键字定义缺省构造函数

81-2:【视频】part II

  • 拷贝构造函数也属于构造函数
  • 当移动构造函数不存在时,编译器会调用拷贝构造函数

81-3:【视频】part III

  • 拷贝赋值:Copy assignment
  • Copy assignment operator function 返回值的结果对应的是 x = y 表达式的值

81-4:【视频】part IV

  • delete关键字:禁用函数接口
  • Delete拷贝构造是一种函数声明,会使编译器停止产生默认构造函数
  • C++17引入函数的传参优化(请参见 Mandatory elision of copy/move operations):当传入临时对象作为实参时,会直接作为形参而无需拷贝构造的步骤
  • 为什么大多数情况下,不要为移动构造(移动赋值)函数引入delete限定符
  • 注意:从C++14起 constexpr | consteval 成员函数不具有const属性

第5节: 字面值类、成员指针与bind交互

82-2:【视频】part II

  • Pointer To Member:(类内)成员指针
  • (类内)成员指针的使用:obj.*mem_ptr | ptr->*mem_ptr
  • 使用std::bind可以基于数据成员指针生成可调用对象

第13章: 类的细节

第1节: 运算符重载

84-2:【视频】运算符重载 II

  • operator[]通常返回引用
  • 由于const的情况,需要对读写的情况重载两种operator[]函数

84-3:【视频】运算符重载 III

  • 解引用运算符(*)重载可以模拟智能指针的行为
  • 成员访问运算符(->):视作单目运算符重载
  • 成员访问运算符(->):必须返回类指针或者带有operator->重载类的对象

84-4:【视频】运算符重载 IV

  • 类型转换运算符重载
  • explicit bool的特殊性:条件表达式会自动调用bool类型转换而跳过explicit限定
  • C++20对<=>重载:可以实现之前六种比较运算符的重载
  • 重载==运算符编译器可以自动推导!=运算符的操作
  • ==运算符对其它类型重载支持交换律,例如:重载operator == (Str, int),则也会支持 str == int 表达式的比较 [Cpp_operator_equal]
  • ==运算符重载推荐定义为成员函数:bool operator == (type x);因为其重载支持交换律

85-1:【视频】类的继承

  • 通常采用public继承
  • struct在缺省情况下使用public继承

85-3:【视频】虚函数 II

  • 由虚函数所引入的动态绑定属于运行期行为,与编译期行为有所区别
  • 虚函数在运行时的默认形参来自于当前静态类型对应的虚函数定义,于是会出现虚函数的默认形参被基类定义覆盖的现象 [Cpprun_virtual_func]
  • 通过CppInsights查看,发现编译器会在编译时将静态类型(基类)的虚函数的默认形参添加到调用处
  • 虚函数的调用成本高于非虚函数,使用可以final关键字可以中止虚函数的继承,编译器会根据final优化vtable来提高性能
  • 可以在类名右边加上final,表示此类不会再进行派生
  • 引用本质上也是用指针实现的

85-4:【视频】继承与成员函数

  • 派生类显式定义的构造函数(包括派生类的拷贝构造函数)将隐式调用基类的默认构造函数
  • 派生类显式定义的构造函数如果想调用其它构造函数,需要使用类初始化列表

86-1:【视频】补充知识 I

  • using可以用来改变基类成员的访问权限
  • using可以利用基类构造函数自动生成派生类构造函数 [Cpp_using_base_ctor]
  • friend友元声明的本质是:赋予函数与类方法同样的访问权限

86-2:【视频】补充知识 II

  • 使用虚函数基类实现多态时,推荐加上virtual ~Base() = default;以自动调用派生类的析沟函数
  • [[no_unique_address]]属性:进行空基类优化

第14章: 模板

第1节: 函数模板

89-1:【视频】函数模板

  • typename关键字可以替换为class,含义相同
  • 函数模板的显式实例化:显式用<>给定模板参数
  • 还有隐式实例化,不用给出模板参数

89-3:【视频】函数模板实参推导的若干讨论

  • 模板类型可以指定默认值

89-4:【视频】函数模板的实例化控制

  • 模板函数的显式实例化(Explicit Instantiation)

90-1:【视频】函数模板的特化

  • 特化函数对多个函数模板的匹配遵循类型细化优先原则,(与函数实例化对多个函数模板的匹配相同);
  • 类型细化优先原则:当特化函数可以与多个函数模板的匹配时,优先匹配类型最具体的类型;例如,对于可匹配的类型范围 T < T*,int *可以匹配T* || T,而int能匹配T而无法匹配T*;因此编译器会让int *选择匹配范围更小的T*模板,[Cpp_fine_type];

第2节: 类模板与成员函数模板

91-1:【视频】类模板与成员函数模板

类模板的声明与定义

遵循翻译单元的一处定义原则

在类模板外定义成员函数

【C++】深蓝学院课程:C++基础与深度解析 - 课程知识点目录_第1张图片

在Google中搜索gcc的vector实现

在谷歌中搜索:gcc github vector

类模板的友元函数仅仅是当前实例类的友元

而不是所有实例类的友元,gcc的实现可能有问题, [Cpp_template _friend_issue]:

template<typename T>
class B {
    int x;
    friend auto operator+(B input1, B input2) {
        B res;
        res.x = input1.x + input2.x;
        B<float> tmp;
        tmp.x;

        return res;
    }
};

Gcc实现认为函数也是B的友元,无论BB是否是同一个类型;

91:【视频】类模板的实例化、特化与实参推导

  • 自定义推导指引:User-defined deduction guides
  • C++ 17之前类模板推导的解决方案:引入辅助模板函数

第3节: Concepts

92-1:【视频】C++ 20新概念 Concepts

模板的问题:没有对模板参数引入相应的限制

  • 参数是否可以正常工作,通常需要阅读代码进行理解
  • 编译报错友好性较差(std::vector报错举例)

Concept 的定义与使用

  • 可以将typename替换为concept谓词
  • 包含多个模板参数concept,用做类型constraint时,省略第一个参数,将推导类型将作为首个参数 —— [19:37]

92-2:【视频】requires表达式

  • {x + 1} -> std::same as;可以直接写成{x + 1} -> int;,两者是等价的 —— [7:16]
  • requires从句会影响重载解析与特化版本的选取,可以用来实现模板函数重载 —— [10:46]
  • requires从句所引入的限定具有偏序特性,系统会选择限制最严格的版本 —— [14:09]
  • 如果只定义了类模板的特化版本,而没有给出默认实例化的定义,可能编译器会报错:“has incomplete type and cannot ba defined” —— [16:42]

第4节: 模板相关内容

94:【视频】完美转发:T&& + std::forward

示例:Cpp_perfect_forwarding

第15章: 元编程

第2节: 顺序、分支、循环代码的编写方式

97-1:【视频】顺序代码的编写方式

  • 代码示例:实现对类型去除引用添加const限定 —— [3:36]
  • 元函数是通过类定义(一般是struct)来实现的

97-2:【视频】分支代码的编写方式

基于(偏)特化引入分支

示例:“对常量100实现编译期的减法计算” —— [10:49]

using实现合法的类型赋值表达式

例如:using x= Imp<100>::type;

基于SFINAE引入分支

  • 基于std::enable_if引入分支
  • 模板形参的默认值只有在模板实例化时才会被考虑,所以使用 “typename+enable_if” 进行元程序分支会提示重载错误,[Error_typename_enable_if]

97-3:【视频】循环代码的编写方式

使用模板实现类型容器

示例代码:

template <typename...> class Cont;
using Input = Cont<int, char, double, bool, void>;

使用循环处理数组:获取数组中 id=0,2,4,6… 的元素

示例代码:Cpp_get_even_types

获取m种数据类型的最后n个类型

示例代码:Cpp_m_n

作业

Project4 - 第13章: 类的细节

文字版地牢游戏;

知识点应用

  • 类的抽象 & 虚函数动态绑定
  • Public,protected,private访问权限
  • 继承中的权限和构造(public继承,子类调用基类构造函数)
  • 智能指针 shared_ptr(reset()make_shared()

Project5 - 第15章: 元编程

使用元编程实现大数加法;

知识点应用

  • 使用struct实现元函数
  • 模板的使用
  • 可变参数的模板
  • 使用递归的思想, 在编译期实现加减法
  • 使用递归的思想, 在编译期实现长除法

测试用例

在这里插入图片描述

Final Project - 第16章: 其他的工具与技术

作业题目

本次大作业的目标为设计一个泛型矩阵运算标头库(header only),此库内部含有一个矩阵类(为表述方便,假定名称为Matrix)可以完成矩阵的加法、减法和乘法,Matrix具有一个模板参数表示其元素类型,它应该支持下述的全部用法和特性:

// Matrix应该支持无参构造,此时它是一个1*1的矩阵,内部元素值为模板类型的默认值
Matrix<int> mat1;

// 通过矩阵尺寸构造,内部元素值为模板类型的默认值
Matrix<int> mat2(2, 2);
mat2[1][0] = 42; // 可以通过[][]访问内部元素

//mat1[0][1]; // 访问越界时程序行为是未定义的

assert(mat2.at(2) == 42); // 可以使用at访问矩阵按行展开后对应的元素
//mat2.at(5); // 使用at访问越界时应该抛出异常
mat2.at(1) = 1;

// 可以使用push_back()向Matrix添加元素,新添加的元素可以通过at访问
mat2.push_back(13);
mat2.push_back(14);
assert(mat2.at(4) == 13); // pass
assert(mat2.at(5) == 14); // pass
// 可以使用reshape()修改矩阵尺寸,多余的元素会被删除,缺少的元素会使用默认值填充
mat2.reshape(3, 3);
/* 此时mat2表示的矩阵为
0 1 42
0 13 14
0 0 0
*/

// 使用initializer_list构造Matrix,此时Matrix为一个行向量
Matrix<int> mat3{1, 2, 3, 4};
Matrix<int> mat4{5, 6, 7, 8};
mat3.reshape(2, 3);
mat4.reshape(2, 3);
mat3 + mat4; // 矩阵加法,如果矩阵尺寸不同需抛出异常
mat3 - mat4; // 矩阵减法,如果矩阵尺寸不同需抛出异常
mat3 * mat4; // 矩阵乘法,此处为两个2*3的矩阵相乘,不满足矩阵乘法定义,需要抛出异常

知识点应用

  • 模板的使用
  • 编译器检查模板参数
  • 异常处理
  • 矩阵拼接的返回参数推导

你可能感兴趣的:(C++,lambda,lambda表达式)