一、目录分析
拿到一套源代码,首先别急着看源码,第一步要做的应该是分析目录,举个例子:
这套源代码的总目录下分为5个子目录,总目录下除了含有一个顶层CmakeLists.txt文件外。其中还包括:
- cmake_modules/目录保存的是一些cmake文件,
- config/目录下存放一个配置文件,
- include/目录又包含一个子目录myslam/,该子目录存放应用程序相关的头文件,而include/下也可以存放一些库的头文件。
- src/目录下主要存放cpp的源文件。
- 该程序在编译运行前,需要在总目录下新建一个目录build/,然后在build/里make,编译生成的中间文件会全部存放到build/中,避免污染源代码。
二、命名风格
根据上图所示的cpp原文件名,可以看到文件名直白易懂,基本上概括了该文件所实现的任务。如frontend.cpp应该是实现了视觉里程计前端的功能,而backend.cpp就是实现了后端优化功能,camera.cpp文件是相机相关功能,viewer.cpp应该是主要负责可视化部分。可以看到,不同文件分别负责并实现了一部分功能,具有相似属性的部分尽可能放到同一个文件中,使得源代码层次分明,便于阅读。
1 #ifndef MYSLAM_BACKEND_H 2 #define MYSLAM_BACKEND_H 3 4 #include "myslam/common_include.h" 5 #include "myslam/frame.h" 6 #include "myslam/map.h" 7 8 namespace myslam { 9 class Map; 10 11 /** 12 * 后端 13 * 有单独优化线程,在Map更新时启动优化 14 * Map更新由前端触发 15 */ 16 class Backend { 17 public: 18 EIGEN_MAKE_ALIGNED_OPERATOR_NEW; 19 typedef std::shared_ptr<Backend> Ptr; 20 21 /// 构造函数中启动优化线程并挂起 22 Backend(); 23 24 // 设置左右目的相机,用于获得内外参 25 void SetCameras(Camera::Ptr left, Camera::Ptr right) { 26 cam_left_ = left; 27 cam_right_ = right; 28 } 29 30 /// 设置地图 31 void SetMap(std::shared_ptr<Map> map) { map_ = map; } 32 33 /// 触发地图更新,启动优化 34 void UpdateMap(); 35 36 /// 关闭后端线程 37 void Stop(); 38 39 private: 40 /// 后端线程 41 void BackendLoop(); 42 43 /// 对给定关键帧和路标点进行优化 44 void Optimize(Map::KeyframesType& keyframes, Map::LandmarksType& landmarks); 45 46 std::shared_ptr<Map> map_; 47 std::thread backend_thread_; 48 std::mutex data_mutex_; 49 50 std::condition_variable map_update_; 51 std::atomic<bool> backend_running_; 52 53 Camera::Ptr cam_left_ = nullptr, cam_right_ = nullptr; 54 }; 55 56 } // namespace myslam 57 58 #endif // MYSLAM_BACKEND_H
这是节选自其中一个头文件的源代码,既然是头文件,c/c++中一般习惯在开头和结尾加上#ifndef、#define和#endif。然后include头文件时,如果是自己写的头文件,就需要写成#include “xxxx.h”的形式,如果要include一个标准库的头文件,就要写成#include <xxxx>的形式。然后在c++中,定义自己的命名空间是非常重要的,为了避免多人合作开发时出现命名冲突。声明命名空间时,其范围内的代码不需要缩进,为了便于阅读。
作者在命名函数名时,统一采用单词首字母大写的格式,而命名变量时采用全小写,不同单词间用下划线隔开,结尾都加上一个下划线。这样就可以明显的区分函数和变量,无论时采用什么样的命名格式,函数和变量各自统一最重要。
在声明变量时,变量类型有时候会很长而且复杂,为了避免每次声明变量时都要写一遍较复杂的变量类型,可以使用类型重定义typedef,如typedef std::shared_ptr<Backend> Ptr,将一个较复杂的类型重定义为较简单明了的名称,这样有助于提高编码效率和源码清晰,也有助于阅读源码。