Effective C++ 读书笔记05
前言
本文是阅读《Effective C++ 改善程序与设计的55个具体做法(第三版)》的心得笔记第五部分,文章也会按照原书的顺序依次记录各个条款。
第一部分的阅读笔记参见effective C++ 读书笔记01。
第二部分的阅读笔记参见effective C++ 读书笔记02。
第三部分的阅读笔记参见effective C++ 读书笔记03。
第四部分的阅读笔记参见effective C++ 读书笔记04。
继承与面向对象设计
条款32:确定你的public 继承塑模出 is-a 关系
以 C++ 进行面向对象编程时,最重要的一条规则是:public inheritance(公开继承)意味着 is-a(是一种)的关系。也就是说,每一个公开继承的派生类对象同时也是一个基类对象,反之不成立,基类是更一般化的概念,派生类是更特殊化的概念。即里氏替换原则(Liskov Substitution Principle):任何基类可以出现的地方,子类一定可以出现。比如下面这个例子:
class Person { ... };class Student: public Perso ...
现代C++ [3]: 右值引用、移动语义和完美转发
前言
在本文中将依次讨论左值和右值、左值引用和右值引用、移动语义和完美转发。
值类别
每个表达式只属于三种基本值类别中的一种:纯右值 (prvalue)、亡值 (xvalue)、左值 (lvalue)。
三种基本值类型又组成了两种混合类型:泛左值(glvalue)(包括左值和亡值)和右值(rvalue)(包括纯右值和亡值),即我们普遍意义上的左值和右值。具体的定义可以参见[1],相当之具体且繁复。除非考据党,一般而言不需要这么严谨,只需要知道,左值是指表达式结束后依然存在的持久化对象(可做取地址操作),右值是指表达式结束时就不再存在的临时对象(不可取地址),左值通常是具名变量或对象,而右值通常不具名。
左值引用和右值引用
左值引用
左值引用就是我们通常所指的引用&,给变量(左值)取一个别名的操作。
int a = 1; // a 为左值int& lref_a = a; // 左值引用lref_a是a的别名,修改lref_a即修改aint& lref_b = 1; // 编译错误!不能对右值取左值引用
注意,(左值)引用不是对象,所以不存在引用的数组,不存在指 ...
非线性最小二乘 [1]: GetStart
前言
非线性最小二乘法(NLS)是一种优化技术,可用于为包含非线性特征的数据集建立回归(回归即最佳拟合)模型。此类数据集的模型系数是非线性的。
NLS回归理论
在本文中将遵守如下的符号表示的惯例:
“帽子”符号用来表示在数据上拟合回归模型过程中生成的值,如 β^\pmb{\hat{\beta}}β^β^ 代表拟合系数的向量;
yobs\pmb{y_{obs}}yobsyobs 代表因变量y的观察值向量;
未加粗表示标量,加粗表示向量或矩阵。如 yobsiy_{obs_i}yobsi 表示向量 yobs\pmb{y_{obs}}yobsyobs 中第 iii 个标量;
我们假设回归矩阵 XXX 的尺寸为 m×nm \times nm×n,即它有 m 行 n 列 回归变量。yyy 矩阵的尺寸为 m×1m \times 1m×1,系数矩阵的尺寸为n×1n \times 1n×1 或者 1×n1 \times n1×n(转置形式)。
下面将看到3个可用MLS训练的非线性模型的例子:
例1
在下面这个模型中,回归系数 β1\beta_1β1 和 β2\beta_2β2 分 ...
Eigen[1]: GetStart
前言
Eigen是一个高层次开源C++库,有效支持线性代数、矩阵和矢量运算、数值分析及其相关算法。
编译安装
下载Eigen源码,解压缩后,用Cmake-GUI打开,选择MSVC编译:
Configure后修改install目录:
点击Generate生成工程,然后点击open Project打开VS .sln工程文件,编译INSTALL项目
编译结束后,便可以在上面指定的install文件夹下找到Eigen库。
测试用例
新建CMakeLists.txt和eigen_test.cpp文件,内容分别如下:
cmake_minimum_required(VERSION 3.6)project(eigen_test)find_package(Eigen3 REQUIRED)include_directories(${EIGEN3_INCLUDE_DIRS})message(-----${EIGEN3_INCLUDE_DIRS}-----)add_executable(eigen_test eigen_test.cpp)target_link_l ...
Effective C++ 读书笔记04
前言
本文是阅读《Effective C++ 改善程序与设计的55个具体做法(第三版)》的心得笔记第四部分,文章也会按照原书的顺序依次记录各个条款。
第一部分的阅读笔记参见effective C++ 读书笔记01。
第二部分的阅读笔记参见effective C++ 读书笔记02。
第三部分的阅读笔记参见effective C++ 读书笔记03。
实现
条款26:尽可能延后变量定义式的出现时间
尽可能延后变量定义式的出现时间,理由如下:
可以避免不必要的构造和析构成本:比如在使用某对象前,因某些原因函数返回或抛出异常,若已定义了该对象,便仍需要承担构造和析构成本,即便你并未真正的使用它;
延后变量定义式的真正意义并不只是延后变量定义式的位置,甚至应当延后定义直到你能为其提供初值实参为止。这样可以避免无意义的default构造成本,而且用具有明显意义的初值来初始化变量,还可以附带说明变量的目的;
std::string encryptPassword(const std::string& password){ if(password.length() & ...
SIMD[3]: NEON 内联汇编
前言
汇编作为代码的最底层实现,大概的流程就是加载数据到寄存器,计算,把寄存器的值写回内存。
一般运行瓶颈就在于数据的加载和写出还有指令之间的数据依赖等等,所以怎么更高效的读写数据还有使相邻指令之间的数据依赖最小等等,是做优化经常都会遇到的问题,当然这个很吃经验,但是也总是会有一些套路可寻。
ARMv7与ARMv8对比
寄存器
通用寄存器
ARMv7(或 ARMv8 的AArch32执行状态) 有 16 个 32-bit 通用寄存器,用 R0-R15 表示。
ARMv8(默认指AArch64执行状态) 有 31 个 64-bit 通用寄存器,用 X0-X30 表示。还有一个不同名称(取决于使用它的上下文)的特殊寄存器。和ARMv7不一样的是,这31个寄存器也可以作为 32-bit 寄存器来用,用 W0-W30 表示,其中 Wn 是 Xn 的低32位。
向量寄存器
ARMv7 包含 16 个 128-bit 向量寄存器,用 Q0-Q15 表示,其中每个Q寄存器又可以拆分成两个 64-bit 向量寄存器来用,用 D0-D31 来表示,对应关系如下图:
ARMv8(AArch64)具有32 ...
SIMD[2]: NEON Intrinsics
前言
Neon Intrinsics 是对ARM架构下的高级SIMD指令的拓展实现,本质是编译器知道其精确实现的函数。Neon Intrinsics是在arm_neon.h中定义的一组C/C++函数,由Arm编译器和GCC支持。这些函数让你不用直接编写汇编代码就可以使用Neon,因为这些函数本身包含内联到调用代码处的短汇编内核。此外,寄存器分配和流水线优化由编译器处理,从而避免了汇编程序员面临的许多困难。
Neon Intrinsics的优势:
强大:内在函数使程序员无需手写汇编代码即可直接访问 Neon 指令集。
移植性强:对于不同的目标处理器,手写的 Neon 汇编指令可能需要重写。但包含 Neon 内在函数的 C 和 C++ 代码 在新目标或新执行状态(例如,从 AArch32 迁移到 AArch64)下编译时,代码更改最少或无需更改。
灵活:程序员可以按需切换使用 Neon 或者 C/C++,同时避免许多底层的工程问题。
Neon Intrinsics的劣势:
跟直接使用库或通过编译器优化比,学习成本更高;
跟手写汇编比,可能性能改善不能最大化;
函数检索
Neon I ...
SIMD[1]: NEON入门
简介
ARM CPU 最初只有普通的寄存器,可以进行基本数据类型的基础运算。从 ARMv5 架构开始引入 VFP(vector-floating-point) 指令扩展,可以通过使用短向量指令来加速浮点计算。从 ARMv7 架构开始引入 NEON 技术,NEON 技术同样是依靠向量指令来加速计算。鉴于 NEON 技术提供的向量技术加速效果体验更优秀,从 ARMv7 架构开始 VFP 向量指令加速的模式被弃用,因此 VFP 单元有时也称之为 FPU(Floating Point Unit)单元。
ARM NEON 技术本质上是一种高级的**单指令多数据(SIMD)**架构扩展,这种扩展仅在一些 ARMv7-A 和 ARMv7-R 架构以及 ARMv8 架构上支持。
这里将先以ARMv8中的NEON展开介绍,后续文章再讨论ARMv7和ARMv8的差异。
ARMv8 NEON
ARMv8-A包括32位和64位执行状态,每一种都有自己的指令集:
AArch64是用来描述ARMv8-A架构64位执行状态的名称。在AArch64状态下,处理器执行A64指令集,其中包含NEON指令(也称为SIMD ...
现代C++ [2]: 模板特化
前言
模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。
模板
类模板
适用于描述通用但类型安全的数据结构,如下:
#include <iostream>// class templatetemplate <typename T>class Compare {public: bool equal(T a, T b);};template <typename T>bool Compare<T>::equal(T a, T b) { return a == b;}int main() { Compare<int> C; std::cout << C.equal(1, 2) << std::endl; return 0;}
函数模板
#include <iostream>// function templatetemplate <typenam ...
Effective C++ 读书笔记03
前言
本文是阅读《Effective C++ 改善程序与设计的55个具体做法(第三版)》的心得笔记第三部分,文章也会按照原书的顺序依次记录各个条款。
第一部分的阅读笔记参见effective C++ 读书笔记01。
第二部分的阅读笔记参见effective C++ 读书笔记02。
设计与声明
条款18:让接口容易被正确使用,不易被误用
想要开发一个容易被正确使用,不易被误用的接口,首先要考虑使用者会犯什么错误,并设法阻止。
导入新(自定义)类型,预防接口被误用
考虑如下一个Date类:
class Date {public: Date(int month, int day, int year); // 1. int数值范围太大,有可能传入非法值 ... // 2. 都是int类型,没有区分,可能传参次序错误};
可以通过封装Month,Day和Year的struct/class,构建新的数据类型,并限制其数值范围。如下:
// 封装结构体类型struct Day {explicit Day(int d) : val(d) & ...