我的开发原则
1.错的意外有无数种, 但对的方法只有一个. 只遵循正确的方法运行, 错误的意外不使之矫正, 只是以容错来减少代码量.
2.按事情客观规律办事, 尽量不以个别系统独有的API做. 用也是成体系物理和逻辑上的规划, 减少依赖关系, 增强独立性及普遍性.
3.考虑底层, 国际性等. 先做出成品,再行优化. 不追求华而不实的东西, 只做内秀相关的实用东西. 好用, 方便, 实用, 才是王道.
我的C++程序命名规则
一、逻辑架构
类:FSDirectory, _AtomNode_c - 对于长类名不进行后缀命名处理,对于短类名或易混淆[如用于静态等]的类名加后缀_c
抽象接口:ITweaker, IInjecter, IComparer 或 _Collection_i, _DocumentAction_i - 可以用前缀I表示接口类型,否则加_i表示私有抽象接口类型
* 接口的定义:纯虚类,无变量私有或保护成员,表示一个动作或一组动作的标准。用于表达一个规范准则,常用于多重继承实现。使用接口必须通过指针实现。
结构体:TColor, TRectangle, _WindowInfo_st - 使用T加大写结构体名前缀实现
枚举类型:ESysColor, EAchorType, _MoveDirect_e - 使用E加大写枚举名前缀实现
* 强枚举类型:KSysColor, KMoveDirect, _DockType_k - 使用K大写枚举名前缀实现
类型别名:int32_t, imgIndex_t, fpos_t - 使用骆驼命名法加_t后缀实现
模板名: BinaryTreeT, Matrix44T - 使用后缀T表示模板类型
联合类型:UPropertyValue, _VoidPointer_u - 使用前缀U表示
内部(私有)或别名类型前面加_: _tchar, _sysinfo_st
公用成员函数: queryObjectList(const _tchar* pQueryStr) - 骆驼命名法表示函数
queryInfo(const wstr_t pMIName, ModuleInfo* _pMI);//输出变量在后加_p前缀,输入变量在加前p前缀
私有保护成员函数: get_function_type(void* p_val) - 使用小写下划线表示(私有函数形参前缀用p_或o_)
私有保护成员变量: m_pos, m_text, m_anchor_type - 使用前缀m_加小写下划线表示
公用成员变量: PosX, PosY, Text - 直接大写表示,但尽量不出现公用成员变量,而是封装
私有变量封装: _PosX, _PosY, _Text - 前缀_加大写表示,前缀_表示私有或保护,大写首字母提醒此变量是一个属性
函数形参变量: p_val, _p_result, pObjList, _pOutStr - 使用前缀p_加骆驼命名表示形参
普通变量: bRet, lsObject, entityTest - 骆驼[+匈牙利]命名法表示普通变量,其小写前缀为复杂对象类型[简单如int等可无视]
指针变量: _pOutStr表示堆变量, object_或object_p表示仅仅指向地址的指针(仅使用,不对内存操作)
getter/setter方法[函数]:Object* PosX(const int32_t p_x){} int32_t PosX(void) const{}
* 不能修改变量,需加const修饰符,如遇void* 则getter前后const如下 const _tchar* Str(void) const{}
* 不用getPosX或setPosX设置, 而是PosX()重载getter/setter实现。注意getter/setter类的函数,首字母必须大写
* setter其返回值为Object*,即return this,以实现链式设置
* 注意所有无参函数,形参表必须写void类型明确表示[重载]类型为void
宏定义或替换:_DEBUG, _OUTPRT, BUFFER_SIZE, DIRECT_RIGHT - 对于宏替换前缀用下划加大写,对于宏定义[类似常量]不加前缀下划直接大写
枚举常量: SC_RED, AT_BOTTOM, MD_UP - 枚举类型名简写加具体内容大写表示
普通常量: c_Filename, c_Default_option, c_Moveup - 使用c_前缀加首字母大写余全小写实现,例:for(int32_t i =0; i < c_Maxlength; i++){}, int a = c_Pi;
静态变量或全局函数: g_HTextureMgr, s_WndInfo, ms_Keys, cs_Webcfgpath 或 g_MixMatrix(), s_DfcgFrmProc(), ms_SetNULL(), _Test()
* 全局意义用g_或s_,每音节首字母大写,可与m_等修饰符共用[遇宏修饰或常量修饰,首字母必须大写],尽量少用静态或全局以降低耦合
常用变量前缀:bRet[Bool], arrTest[Array], lsObject[stl::list], hFile[Handle], fStream[FILE], _entity[*], szCaption[String of Zero]
宏检查语句:UI_FRAME_H_INCLUDED, _UI_FRAME_H_
命名空间:string_tools, debug_tools - 小写加下划线
二、物理架构
simple_prj 下面有 bin, obj, lib, design, tools, src, res, test八个主要目录
其中bin 与 obj存放编译中间文件及二进制代码结果
lib下有项目引用到的库文件及子目录\inc存放其库文件头文件
design 与 tools有项目的期望功能、项目的依赖关系说明文件、进度及感悟、示例代码及相关工具
src下为项目的主要代码
res为项目所用到的资源文件,包括图片、声音、字体等
test为项目所单独用到的测试项目,由若干测试子项目组成
* 未来如项目生成为运行库文件或提供给其他人使用时,会增加一个inc目录,该目录提供了运行库的头文件支持
项目的文件后缀名:
main.h - 项目入口处声明的头文件,主要用于包含其他头文件,声明类型,声明函数或导出全局变量,描述入口文档及时间等
main.cc - 项目入口处代码
timer.t.cc - 测试用例文件,不包含在主项目中
connection.i.h - 数据库连接抽象接口的头文件
ui_button.h ui_button.cc uie_event.h cn_font_mgr.h - 普通项目文件,用[有时简化]小写兼下划表示
* 所有c++代码文件以.cc\.cpp或.h表示,模板文件用.hpp表示[增强编译识别],编译器大略使用GCC进行编译,中间静态库文件为.a
项目用名的后缀:
foundation 基础文件夹,包含公用的类,基于本项目不可通用,对特定库较为依赖
module_ref.h 包含对该模块的基础依赖关系
module_base.h 包含对该模块的项目依赖关系及常见的宏定义,亦可简化替换成module.h
module_def.h 包含对该模块的基础类型别名、结构体定义等[通常定义在一个命名空间中]
* module.h 可引申为头文件为其他项目调用,通常用于导出到inc文件夹作第三方用
utility 功能文件夹,包含常见工具或功能,通常可以在其他项目中使用,对特定库不依赖
string_tools.h debug_tools.h 工具类[通常表示为某个静态单例类]
singleton.hpp 模板功能类
app 框架类文件夹,包含常用的自定义类库[也可包含第三方库],以编译形式共享
biz 业务逻辑文件夹,包含所有项目特有的业务逻辑或功能
view 视图文件夹,包含项目中该平台上特有的显示代码,无法独立出项目及库
关于工程路径及自定义[库]路径的约定
在项目中独有的不期望在其他项目中使用,则用工程路径
#include "src/view/main_frm.h"
#include "src/view/dkcp_frm.h"
在自定义[库]或期望共用的代码中则用相对路径以增强独立性
#include ".\frame\win32\appframe.h"
#include "..\foundation\app_base.h"
* 工程路径不使用"./??",相对路径必须使用.和..
* 工程路径需设置头文件目录下添项目路径
* CodeBlocks环境下添加库文件
添加头文件:依次点击project->bulid options->Search directories,在该标签页中点击Compiler,单击Add按钮添加头文件路径
添加静态库路径:依次点击project->bulid options->Linker setting,在该标签页中点击Add按钮添加静态库路径。
三、关于C++的补充说明
1.使用标准C库时,使用C++重载版本库,如
#include <string.h> 替换为 #include <cstring>
#include <stdio.h> 替换为 #include <cstdio>
同时使用strcat等标准库的函数时,应用std::strcat而不是直接strcat
2.弃用using namespace std;
改用 using std::strcat; 在某一段落里声明,减少命名空间混乱,减少编译查找时间
3.关于程序中的基本类型,程序中自定义库及业务逻辑部份只使用如下类型:
允许使用bool型,尽量不用unsigned标识符,用也尽量不作有符号数无符号数转换
使用int8_t定义小数值范围的int类型
使用int32_t明确定义32位int
使用int64_t替换long类型,其值加L
使用double明确替代float单浮点类型,其值加F或D
* 对于基础部份[编译器优化、较小值、标准库或过于基础内容]仍可以用int, float, long及unsigned,但保证其值范围不可过大
使用wchar_t*型数组和const char*(字符串常量指针)作较为基础核心的类型
使用FILE指针操作文件,弃用stream流及重载
使用std标准库std::string, std::vector, std::set, std::stack等类型作为基础类型进行开发。
4.关于扩展库
使用宏统一平台,如direct.h、io.h 或 sys/fcntl.h可用宏简单统一成_create之类的命名空间和函数
使用标准socket操作网络,不用winsock函数等特定类库,不得以不请先用宏或工厂类封装
特定库如glfw引用的基础类型如GLFLOAT则使用其基础类型,不使用标准类型。但封装后一定传入的是标准类型。
尽量使用POSIX类的标准C++特性和最新的稳定版本的开源类库及开发工具
简单功能可以使用自写代码完成基本功能需求即可,尽量减少库依赖
5.关于const限定词和char*的使用详解
a. 凡是用到""符号一律视为字符串常量,其特点是无法修改字符串值内容
b. 定义字符串常量必须用const char* x = "abc"; 字符串abc视为字符常量数组,x储存内容为该数组的首地址。此数组内容不可变更,如x[0]='k'非法
c. const char *x; 指针可指向新的内容,但指向的内容无法修改。在C++中重载时与char*视为不同类型。比如:
x = new char[5];可以,但x[0]='c';非法。
char *b; x=b;可以,但strcpy(a,"xyz");非法
d. char* const y; 指针不可指向其他地址,但指向的内容可以修改。比如:
char* const y = new char[10];首次可以,再次y = new char[10]非法
char* const y; y = new char[10];非法,const必须赋初值!非法,无法修改指针 - const char*无此限制
!!!Warning: 允许y[0]='a';允许delete,但禁止y = NULL
e. const char* const z; 标识此字符串常量为真正的常量,与char* const z = "content";等同
f. 成员函数尾部出现const,表示此成员函数不能改变类成员变量的值,常用做getter
g. 引用的实质是指针,传递参数时,const &实质是* const指针,&&(左值引用)相当于const *
6.关于预编译头文件及调试、发布版本说明
1.请在stdafx.h或其他预编译头文件外创建相应.cpp文件,此文件仅包含一句#include "stdafx.h"。此外请在项目属性设置中设置预编译选项为"使用",而stdafx.h(或其他预编译文件)中设置其预编译选项为"创建"。这样重新构建时将自动重新生成.pch预编译文件。
2.在准备发布程序运行版本时,一定要初始化所有变量的初始值为0。调试版本运行时,编译器自动将变量值初始化为ccc,因此发布版本运行时可能导致未知的内存错误。发布运行版本将不启用assert宏,因此需要仔细检查每个函数体的入口值是否合法(必要时)和断言不启用时是否影响程序逻辑。
7. 使用C++0x特性应用到项目中,含nullptr(显式定义指针为空), auto(自动推导数据类型), decltype(从变量或表达式获取类型), override(显式声明派生类重写基类虚函数), final(显式声明此虚函数不可被派生类重写), 右键引用和强枚举类型。语法如下:
int *var=nullptr;
decltype(var) var2 = var;
auto vec = sample.getVector();
class A {
virtual void a() const final; // do not override
virtual void b();
virtual void c();
virtual void d() const;
void k(); // not virtual
};
class B : A {
void a() const; // error: D::f attempts to override final A::a
void b(); // OK
void c() override; // OK: overrides A::c()
void d() override; // error: wrong type
void k() override; // error: A::k() is not virtual
};
Object&& object;
enum class KEnum1{ VAR1, VAR2 };
enum KEnum2 : unsigned int { VAR1, VAR2 };
8. 关于命名空间及枚举常量
应有项目自己的命名空间,枚举和常量定义必须在类定义里面定义。调用时应加类名前缀,如:
class UIBase {
public:
enum { c_Margin = 40, c_Space = c_Margin + 30 };
enum EBaseDirection : int { UID_UP = 0, UID_DOWN, UID_LEFT, UID_RIGHT, c_Direction_number };
}; //class UIBase
调用时写成:
size_t set_position(UIBase::EBaseDirection pDirection, size_t x, size_t y) {
if(pDirection == UIBase::UID_UP) return UIBase::c_Space;
}//function set_position
这样写便于记忆,不担心重定义,结构清晰,类自上而下分别写作公共(先写枚举常量及定义,后写公共函数和变量)、保护和私有。
附codeblocks默认代码 及代码缩写词 定义
///H
/***
* Filename: $(ACTIVE_EDITOR_STEM).$(ACTIVE_EDITOR_EXT)
* Author: ${#default.author}
* Create: $(TODAY)
* Summary:
*/
namespace ${#default.namespace} {
}//namespace ${#default.namespace}
/CPP
//! create by ${#default.author} @ $(TODAY)
#include "$(ACTIVE_EDITOR_STEM).h"
namespace ${#default.namespace} {
}//namespace ${#default.namespace}
/Class///
//!class $(Class name). |
class $(Class name){
public:
$(Class name)();
~$(Class name)();
protected:
private:
};// class $(Class name)
//!function construction.
$(Class name)::$(Class name)() {}
//!function destruction.
$(Class name)::~$(Class name)() {}
//Struct///
struct ${Struct name} {
|
};//struct $(Struct name)
//fn
/*@time: $NOW
*@author: ${#default.author}
*@params: $(Parameter List)
*@return($(Return Type)):
*/
$(Return Type) $(Class Name)::$(Function Name)($(Parameter List));
//!function $(Function Name). |
$(Return Type) $(Class Name)::$(Function Name)($(Parameter List)) {
}//Function $(Function Name)
npfn
/*@time: $NOW
*@author: ${#default.author}
*@return($(Return Type)):
*/
$(Return Type) $(Class Name)::$(Function Name)(); //!function $(Function Name).
|$(Return Type) $(Class Name)::$(Function Name)() {
}//End Function $(Function Name)
gsfn///
$(Property Type) _$(Property Name);//!Property $(Property Name). |
//!function getter/setter for $(Property Name)
inline $(Property Type) $(Property Name)() const { return _$(Property Name); }
inline void $(Property Name)(const $(Property Type) p$(Property Name)) { _$(Property Name) = p$(Property Name); }
另附上一个非常好的C++跨平台开发的建议 https://blog.youkuaiyun.com/fengbingchun/article/details/44005041