写在前面
本系列所剖析的caffe源码来自于master分支, commit: 9b891540。
本系列不会特别着重于深度学习的网络层构造的实现方式,而是从工程角度看C++的语言特性及设置。
首先介绍一下caffe的代码布局:
头文件放在: include
而源文件则在: src/caffe/
这种设置的原因很简单,src文件的源码会被编译成库去被调用,而头文件的存在则作为api提供。
Blob类
Blob.hpp
特点1:尽量用const, enum, inline, 替换#define
const int kMaxBlobAxes = 32;
上来 Line12 就是const定义, 注意哦,别小看这一句哦。
<Effective C++> Scoott Meyers著,侯捷老师译的条款02:尽量以Const, enum, inline, 替换#define。
原因很简单,如果把代码写成如下:
#define kMaxBlobAxes 32
那么编译器可能从来都没有见过kMaxBlobAxes记号是什么,在程序进入预编译期的时候,记号名称就被32替换了,如果后续出现了使用这个变量导致的代码错误,那么编译器报错只会提示32 xxxxx, 而不是提示kMaxBlobAxes记号名称,这会在后期加重debug的难度。
特点2. 构造列表初始化及explict 关键字在构造函数的意义
// L23 - 32
template <typename Dtype>
class Blob {
public:
Blob()
: data_(), diff_(), count_(0), capacity_(0) {
}
/// @brief Deprecated; use <code>Blob(const vector<int>& shape)</code>.
explicit Blob(const int num, const int channels, const int height,
const int width);
explicit Blob(const vector<int>& shape);
Blob是一个模板类,他有三个构造函数。
-
a) 第一个构造函数,也是默认构造函数
Blob() : data_(), diff_(), count_(0), capacity_(0)
这里使用了C++类构造函数初始化列表
知识点:“类构造函数初始化列表”:这个的形式就是一个冒号:然后跟一串数据成员,每个数据成员有个小括号对传入参数进行初始化复制。类构造函数初始化列表的特性: 与一般显示的初始化并没有什么明显不同,但又如下两种情况是一定要用初始化列表的构造函数
- case1: 若数据成员存在没有默认构造函数的类。
- case2: const 成员或引用类型的成员
//一个简单的例子 class T1{ public: T1(const string& str) std::cout << str << std::endl; }; class T2{ public: //只能进行类成员初始化列表初始化 T2() : t("123"), cnt(3) { } public: //数据成员T1不存在默认构造函数, //cnt为const数据 T1 t; const