C++产品开发讲座-(一)编程规范
1. 编程风格
文件夹文件名:⼩写+下划线: demo_data_type.h
枚举类名:
enum class EnumDataType
{
EnumDataTypeA,
EnumDataTypeB
};
class,struct名:单词首字母大写
class DemoData
{
};
函数名:首字母小写,后面单词首字母大写
void procData(uint32_t index_pipeline);
变量名:全小写,单词之间加下划线
uint32_t num_buffer;//变量
2.注释
注释是法外之地
最好代码⾃注释(能通过代码变量命名、代码风格直接体现出编程逻辑最好)
3. 命名
(1)最大对其原则
同一类型,同一属性的变量、函数、枚举等,名称最好都按照相同的属性、类型对齐。
strcut School
{
uint32_t index_grade;
uint32_t index_class;
uint32_t index_student;
uint32_t index_teacher;
};
//index_开头
(2)常用命名前缀,后缀
// 指针
sp_ std::shared_ptr<DemoData> sp_xxx
p_ float* p_xxx
// 函数
procXXX
genXXX
// stl容器
vec_ std::vector<DemoData> vec_demo_data;
arr_ std::array<DemoData, N> arr_xxx
lut_ // look up table
map_ // tsd::map/std::unordered_map
// 其他常⽤
flag_
thread_ //thread
temp_ // 临时
param_ // parameter配置
struct/class ParamXXX //配置参数
cnt_ // counter
num_ // number num_buffer
len_ // length len_vec_data
index_ //index/id
name_ // 名称
msg_ // message
_info // information
val_xxx // value_xxx val_signal
type_xxx // type
// ui控件名
txt_ // textbox, txt_username
lb_ // label
dlg_ // dialog
win_ // windows
4. 编程要求
4.1 凡是有可能失败的函数均返回值为bool、具有资源申请相关的class具有返回值为bool的init
bool procPanMeasGeo(const cv::Mat& img);
原则上,构造函数不做任何可能失败的事情,可能失败的事情交由init函数
class DemoData
{
public:
DemoData()
{
p_data = new float[...](错误:不应再构造函数内申请内存)
}
bool init()
{
p_data = new float[...](正确)
}
private:
float* p_data;
}
4.2 任何资源都需要释放,任何运行时资源均需要停止,具有资源申请的类需具有可重入的close/stop函数并在析构时调用
资源申请均需要释放,malloc/free配对,new/delete配对,fopen/fclose配对
运⾏时资源std::thread,需if (thread.joinable()) thread.join() 结束线程资源
close(), stop()需可重入,即被调⽤多次也没有问题(如free指针前先判断nullptr,free后将指针置为
nullptr),因为⽤户可能主动调,也可被析构调⽤。
struct DemoData
{
~DemoData()
{
stop();
}
void stop()
{
if (thread_proc.joinable()) thread_proc.join();
if (p_data != nullptr)
{
free(p_data);
p_data = nullptr;
}
}
bool init()
{
thread_proc = std::thread(...)
p_data = (float*)malloc(1024);
return p_data == nullptr;
}
std::thread thread_proc;
float* p_data;
}
4.3 原则上严禁在运⾏时new/malloc
原则上在init函数内预先开空间,运⾏时复⽤,运⾏时new/malloc非常消耗资源
4.4 整型变量不采用int,short等而采用uint32_t,int64_t
不同操作系统、硬件构架对int等数据类型的解释不同,其字节数不能确定,所以使用uint32_t等直接指定字节数。
<stdint.h>
int x;(x)
uint32_t, int64_t, uint8_t
4.5 严禁常量,常量采⽤宏或者枚举,枚举更优
严禁hardcode(字符串、数值写死)
枚举可读性佳
hardcode 写死
if (val_str == "key:") 不推荐
if (val_str == TAG_KEY_STR) 推荐
enum EnumDataType : uint32_t
{
EnumDataTypeA = 0,
EnumDataTypeB = 1
};
if (type == EnumDataType::ENumDataTypeA) 推荐
if (type == 0)// 0 is data type a 不推荐
4.6 采用do while(false),RAII避免遗漏
涉及多个资源步骤的流程,每个步骤可能失败,第i步失败需将前i-1次申请的资源全部释放将导致代码逻辑爆炸
不建议的写法
FILE* p_file = fopen(...)
if (p_file == nullptr) return -1;
uint8_t* p_buffer = malloc(...)
if (p_buffer == nullptr)
{
fclose(p_file);
return -1;
}
uint32_t size_read = xfread(p_file, p_buffer)
if (size_read == size)
建议的写法,在申请资源、打开文件失败后break,然后统一释放已申请的资源。
FILE* p_file = nullptr;
uint8_t* p_buffer = nullptr;
....
do
{
p_file = fopen(...)
if (p_file == nullptr) break;
p_buffer = malloc(...)
if (p_buffer == nullptr) break;
//...
}
while (false);
if (p_file != nullptr) fclose(...);
if (p_buffer != nullptr) free(p_buffer)...
采⽤RAII来避免遗忘,如加锁解锁配对、log进入log退出配对、资源申请资源释放配对。
RAII 是 resource acquisition is initialization 的缩写,意为“资源获取即初始化”。它是 C++ 之父 Bjarne Stroustrup 提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在 RAII 的指导下,C++ 把底层的资源管理问题提升到了对象生命周期管理的更高层次。
RAII
struct RAIILog
{
RAIILog()
{
log("start");// 在构造析构函数内指定期望做的操作,可⾃动调⽤
}
~RAIILog()
{
log("end");
}
}
struct RAIIBuffer;
bool procPanMeasGeo(const cv::Mat& img)
{
RAIILog raii_log;// RAII在函数的花括号内具有⽣命周期,花括号进入⾃动创建,花括号离开⾃动析构
log("start");
//....
if ()
{
log("end");
return true;
}
for ()
{
if ()
{
while ()
{
}
log("end");
}
}
log("end");
//随着代码逻辑的增加可能新增return分⽀,但遗忘了做配对的log("end")操作,RAII可⾃动做
return true;
}
4.7 函数行数与文件行数
为了易于代码的维护,增加可读性
函数不宜超过150⾏,最好保持在100以内,多了考虑拆分为⼦函数
⼀个文件不宜超过1000行
4.8 函数入参如果不修改需加const
bool procPanMeasGeo(const cv::Mat& img)
4.9 函数内最好是同⼀层次的代码
为了增加可读性和降低逻辑门槛
不建议
bool procPanMeasGFeo()
{
findROI();
fileterROI();
findEdge();
// 以下是实现细节,和周围的函数格格不入,容易使得阅读者思路断开
for (uint32_t i = 0; i < rows; i++)
{
for (uint32_t i = 0; i < rows; i++)
{
//...
}
}
procMeas();
return true;
}
建议
bool procPanMeasGFeo()
{
findROI();
fileterROI();
findEdge();
findPeak();// 封装
procMeas();
return true;
}