Compare commits

26 Commits

Author SHA1 Message Date
9a540ba114 更新 办公室文件共享.md 2024-08-18 15:54:22 +08:00
a462997aba Merge branch 'main' of http://192.168.1.11:7777/gnibuoz/wiki into main 2024-08-18 11:44:23 +08:00
75f601f053 01.添加目录
02.添加公网访问内容
2024-08-18 11:43:46 +08:00
d31f971874 标题
第一行
第二行
第三行
2024-08-03 11:49:52 +08:00
11bafbc6eb 测试提交标题
测试提交内容
01.第一行
02.第二行
2024-08-03 11:47:38 +08:00
d8febd67cf 测试提交标题
测试提交内容
01.第一行
02.第二行
2024-08-03 11:44:31 +08:00
69627b61ad 概要信息
补充信息:
01.第一行
02.第二行
2024-08-03 11:24:53 +08:00
a5f45aff17 更新 README.md 2024-08-03 11:14:26 +08:00
995f5d8b99 更新 README.md 2024-08-03 11:09:38 +08:00
99682762e5 更新 README.md 2024-08-03 11:05:45 +08:00
391b7353f3 revert b8220e9aaf
revert 更新 README.md
2024-08-03 10:42:11 +08:00
b8220e9aaf 更新 README.md 2024-08-03 10:22:17 +08:00
5a4e55d527 更新 README.md 2024-08-03 10:10:13 +08:00
de9f09e369 更新 README.md 2024-08-03 10:05:00 +08:00
01c1882ea0 添加好用的资源分享页面 2024-07-20 17:28:50 +08:00
fe961b1421 修订格式及部分示例 2024-07-16 19:11:04 +08:00
5ec2e64021 修复代码片段高亮 2024-07-15 18:10:07 +08:00
b34028f8bf 移除原仓库链接 2024-07-15 16:44:55 +08:00
8d82385299 修正目录索引 2024-07-15 16:42:46 +08:00
08ad7783fe 修正目录索引错误 2024-07-15 16:41:35 +08:00
8e7550bfee 添加LLVM代码规范和Qt代码规范 2024-07-15 16:35:05 +08:00
f7cbd0f1d1 基于Qt编码规范,修改部分规范,并调整对应示例 2024-07-15 16:33:48 +08:00
c42c006b9a 修复缩进及高亮 2024-07-15 15:33:18 +08:00
e033bc8291 修复代码高亮 2024-07-15 15:32:29 +08:00
b3c844ba2d 修复格式及部分缩进 2024-07-15 15:31:24 +08:00
e000cd71d2 首次提交,当前状态为Qt编码规范 2024-07-15 15:25:08 +08:00
12 changed files with 2502 additions and 90 deletions

267
Qt码规范.md Normal file
View File

@ -0,0 +1,267 @@
# 开发团队C++编码规范
- [开发团队C++编码规范](#开发团队c编码规范)
- [代码风格](#代码风格)
- [缩进](#缩进)
- [变量声明](#变量声明)
- [空白](#空白)
- [大括号](#大括号)
- [圆括号](#圆括号)
- [Switch 语句](#switch-语句)
- [断行](#断行)
- [继承与关键字 `virtual`](#继承与关键字-virtual)
- [通用例外](#通用例外)
- [附录A 参考列表](#附录a-参考列表)
## 代码风格
### 缩进
- 采用4个空格
- 空格不要用TAB
### 变量声明
- 每行一个变量
- 尽可能避免短的变量名(比如"a", "rbarr", "nughdeget")
- 单字符的变量只在临时变量或循环的计数中使用
- 等到真正需要使用时再定义变量
```cpp
// 错误示例
int a, b;
char *c, *d;
// 正确
int height;
int width;
char *nameOfThis;
char *nameOfThat;
```
- 以小写字符开头,后续单词以大写开头
- 避免使用缩写
```cpp
// 错误示例
short Cntr;
char ITEM_DELIM = '';
// 正确
short counter;
char itemDelimiter = '';
```
- 类名总是以大写开头。公有类以Q开头(QRgb)公有函数通常以q开头(qRgb)。
### 空白
- 利用空行将语句恰当地分组
- 总是使用一个空行(不要空多行)
- 总是在每个关键字和大括号前使用一个空格
```cpp
// 错误示例
if(foo){
}
// 正确
if (foo) {
}
```
- 对指针和引用,在类型和*&之间加一个空格,但在*&与变量之间不加空格
```cpp
char *x;
const QString &myString;
const char* const y = "hello";
```
- 二元操作符前后加空白
- 类型转换后不加空白
- 尽量避免C风格的类型转换
```cpp
// 错误示例
char* blockOfMemory = (char* ) malloc(data.size());
// 正确
char *blockOfMemory = reinterpret_cast<char*>(malloc(data.size()));
```
### 大括号
- 基本原则:左大括号和语句保持在同一行:
```cpp
// 错误示例
if (codec)
{
}
// 正确
if (codec) {
}
```
- 例外:函数定义和类定义中,左大括号总是单独占一行:
```cpp
static void foo(int g)
{
qDebug("foo: %i", g);
}
class Moo
{
};
```
- 控制语句的body中只有一行时不使用大括号
```cpp
// 错误示例
if (address.isEmpty()) {
return false;
}
for (int i = 0; i < 10; +''i) {
qDebug("%i", i);
}
// 正确
if (address.isEmpty())
return false;
for (int i = 0; i < 10;i)
qDebug("%i", i);
```
- 例外1如果父语句跨多行则使用大括号
```cpp
// 正确
if (address.isEmpty() || !isValid()
|| !codec) {
return false;
}
```
- 例外2在if-else结构中有一处跨多行则使用大括号
```cpp
// 错误示例
if (address.isEmpty())
return false;
else {
qDebug("%s", qPrintable(address));
it;
}
// 正确
if (address.isEmpty()) {
return false;
} else {
qDebug("%s", qPrintable(address));
it;
}
// 错误示例
if (a)
if (b)
else
// 正确
if (a) {
if (b)
else
}
```
- 如果控制语句的body为空则使用大括号
```cpp
// 错误示例
while (a);
// 正确
while (a) {}
```
### 圆括号
- 使用圆括号将表达式分组
```cpp
// 错误示例
if (a && b || c)
// 正确
if ((a && b) || c)
// 错误示例
a | b & c
// 正确
(a + b) & c
```
### Switch 语句
- case 和 switch 位于同一列
- 每一个case必须有一个break(或renturn)语句或者用注释说明无需break
```cpp
switch (myEnum) {
case Value1:
doSomething();
break;
case Value2:
doSomethingElse();
// fall through
default:
defaultHandling();
break;
}
```
### 断行
- 保持每行短于100 个字符,需要时进行断行
- 逗号放一行的结束,操作符放到一行的开头。如果你的编辑器太窄,一个放在行尾的操作符不容易被看到。
```cpp
// 正确
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression) {
}
// Wrong
if (longExpression +
otherLongExpression +
otherOtherLongExpression) {
}
```
### 继承与关键字 `virtual`
- 重新实现一个虚函数时,头文件中 不 放置 virtual 关键字。
### 通用例外
如果它使你的代码看起来不好,你可以打破任何一个规则 。
## 附录A 参考列表
1. [Qt编码风格/Qt Coding Style](https://wiki.qt.io/Qt_Coding_Style)
2. [LLVM编码标准/LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html)
3. [谷歌C++风格指南/Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
4. [C++核心指南/Cpp Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)

View File

@ -1,3 +1,7 @@
# wiki
企业知识库通过Markdown构建内部知识分享平台
企业知识库通过Markdown构建内部知识分享平台
- 创建企业开发团队代码规范
- 技术分享
- ...

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -2,7 +2,7 @@
  在osgEarth中添加了osg::Geode绘制蜂窝状六边形网格效果包括墨卡托平面地图地理坐标平面地图三维地球等多种模式
![alt text](assets/osgEarth六边形网格/image.png)
```c++
```cpp
// 函数:创建六边形蜂窝网格线,三维地球
osg::ref_ptr<osg::Geometry> createHexagonWireframe(const osgEarth::GeoExtent& extent, float radius, int rows, int cols) {
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;

60
blog/资源分享.md Normal file
View File

@ -0,0 +1,60 @@
# 好用的资源分享
- [好用的资源分享](#好用的资源分享)
- [网站类](#网站类)
- [Qt](#qt)
- [工具软件](#工具软件)
- [开发人员工具](#开发人员工具)
- [逆向·破解](#逆向破解)
- [逆向工具](#逆向工具)
- [破解软件网站](#破解软件网站)
## 网站类
### Qt
- [Qt官网](https://www.qt.io/)
- [Qt下载站](https://download.qt.io/)
- [Qt官方论坛](https://forum.qt.io/)
- [Qt中文论坛](http://www.qtcn.org/)
- [Qt开发库二进制下载](https://build-qt.fsu0413.me/)
- [飞扬青云博客](https://blog.csdn.net/feiyangqingyun)
## 工具软件
- Everything 文件搜索
- PicPick 批量截屏及简单编辑
- Camtasia 视频剪辑
- OBS Studio 开源屏幕录制软件
- FormatFactory 格式工厂
- PowerToys 微软官方出品的常用操作集合,官方出品,必属精品!
- MasterPDFEditor PDF编辑工具合并、分割...
- MindManager思维导图工具
- 向日葵 远程桌面工具
- SQLiteManager SQLite数据库管理工具
- 太乐地图下载工具 卫星影像下载软件
- ScreenToGif 屏幕录像为GIF动画
- 文件时间修改
- 批量文本文件转UTF-8 BOM
## 开发人员工具
- Visual Assist X
## 逆向·破解
### 逆向工具
- x64Dbg
- Die
- ResourceHacker 修改exe图标等程序资源
### 破解软件网站
- [吾爱破解](https://www.52pojie.cn/)
- [飘云阁](http://www.chinapyg.com/)

1465
llvm-coding-standards.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
# [代码规范](./cppStyleGuide/StyleGuide.md)
引用模板
* [google cpp guide](https://google.github.io/styleguide/cppguide.html)
* [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)
* Qt
* osg
* 源码文件编码utf-8
# [git仓库管理](./gitStore/git.md)
* 仓库创建流程
* git钩子
# 代码审查
* 代码规范检查
* C/C++ 静态代码检查工具cppCheck
# CMake
* 快速搭建基础项目(持续更新)
* 编译器配置(持续更新)

View File

@ -1,61 +0,0 @@
# c++ 代码风格规范
## 源代码文件命名
##### 1. 前要
**通常情况下每个.cpp文件应该有一个配套的.h文件。当然也有例外的情况例如c++模板类的实现)。正确使用头文件会大大改善代码的可读性和执行文件的大小和性能.**
> 头文件应能独立编译,就是头文件的使用者和构建工具在导入文件时无需任何特殊的前提条件就能完成代码编译。具体来说,头文件要有头文件防护符,并导入它所需的所有其它头文件。
> 当编写c++模板时应保证模板的实现声明和实现都在同一个文件里。一般,写模板时源码文件以.hpp命名。
> 避免编写依赖某个构建工具的代码。例如项目的构建依赖于VSCode的一个插件使用其他的构建工具项目无法构建且生成
> 避免前向声明
##### 2. 文件命名
* **新建c++类[.h 或 .hpp]**
> 新建类文件时,尽量保证文件名与导出(外部调用)的类名保持一致(一个头文件中可能包含好几个类)。
> Help.h
```cpp
class Help{}
```
Help.hpp
```cpp
template<typename T>
class Help{}
```
##### 3. 防护符
* **要保证头文件带有防护重复引用预处理头**
```cpp
#pragma once
```
```cpp
#ifndef HELP_H
#define HELP_H
#endif
```
## 命名规范
##### 1. 命名空间[namespace]
##### 2. 类[class]
##### 3. 结构体[struct]
##### 4. 模板[template]
##### 5. 私有函数或变量[private]
##### 6. 共有函数或变量[public]
##### 7. 受保护函数或变量[protected]
##### 8. 预处理[#define]
##### 9. 内联函数[inline]
##### 10. 常量[const]
##### 11. 静态变量或函数[static]

View File

@ -1 +0,0 @@


View File

@ -1,5 +1,15 @@
# 办公室文件共享
- [办公室文件共享](#办公室文件共享)
- [简介](#简介)
- [1. 文件存储服务介绍](#1-文件存储服务介绍)
- [2. 使用流程](#2-使用流程)
- [2.1 内网使用(优先使用)](#21-内网使用优先使用)
- [2.1.1 账户初始化](#211-账户初始化)
- [2.1.2 连接共享目录](#212-连接共享目录)
- [2.2 外网环境使用](#22-外网环境使用)
## 简介
  办公室近日引入了共享存储服务,这项举措旨在提升团队间的协作效率和数据管理的便捷性及安全性。通过共享存储服务,团队成员可以将文件、文档、图片等资料集中存储在一个统一的平台上,而无需依赖传统的文件共享方式,如电子邮件附件或移动存储设备。这意味着团队成员可以随时随地访问所需的文件,无论是在办公室、外出办事的途中,甚至在家里。此外,共享存储服务还提供了权限管理功能,团队管理员可以灵活地控制不同成员对文件的访问权限,确保机密性和安全性。总的来说,共享存储服务的引入将大大简化团队间的文件共享和管理流程,为团队协作提供了更高效、更便捷的工具和平台。
---
@ -13,11 +23,19 @@
---
## 2. 使用流程
### 2.1 账户初始化
### 2.1 内网使用(优先使用)
  在办公室办公时优先使用内网方式此时文件访问和传输不需要经过公网服务器带宽不受限制速度可达50-100MB/s具体数据取决于传输的文件。
#### 2.1.1 账户初始化
  系统默认为团队成员创建了以自己姓名命名的账户,首次使用需要通过该链接:[密码重置链接](http://192.168.1.11:9888/) 设置用户密码使用(每个用户可设置一次密码),如忘记密码,可联系管理员重置。
[![账户初始化](assets/办公室文件共享/image-1.png)](http://192.168.1.11:9888/)
### 2.2 连接共享目录
#### 2.1.2 连接共享目录
![操作流程图](assets/办公室文件共享/image.png)
1. 打开计算机文件管理器
2. 在【此电脑】右键
@ -29,4 +47,18 @@
8. 点击【确定】按钮
9. 如无误,则在计算机界面中会成功添加一个新的网络磁盘。
![网络驱动器](assets/办公室文件共享/image-2.png)
![网络驱动器](assets/办公室文件共享/image-2.png)
### 2.2 外网环境使用
  当不在办公室时临时需要访问共享目录的资料此时无法通过内网地址直接访问服务器需要通过公网文件管理页面访问共享目录可在网页中完成文件的查看、预览、编辑、删除、上传、下载等操作需要注意的是此种访问方式受公司租用的公网服务器带宽限制目前为3Mbps因此如果通过该种方式上传和下载文件时速度会比较慢因此应尽量避免使用该种方式操作大文件。
访问地址为:[http://pan.vuegic.com/](http://pan.vuegic.com/)
[![公网访问页面预览图](assets/办公室文件共享/image-3.png)](http://pan.vuegic.com/)
注:用户名和密码与内网访问时一致,如无法访问,请联系管理员开通账号。
登录成功后,可查看共享目录的文件,并对其进行操作:
![alt text](assets/办公室文件共享/image-4.png)

669
编码规范.md Normal file
View File

@ -0,0 +1,669 @@
# 开发团队C++编码规范
- [开发团队C++编码规范](#开发团队c编码规范)
- [代码风格](#代码风格)
- [缩进](#缩进)
- [变量声明](#变量声明)
- [空白](#空白)
- [大括号](#大括号)
- [圆括号](#圆括号)
- [Switch 语句](#switch-语句)
- [断行](#断行)
- [继承与关键字 `virtual`|`override`](#继承与关键字-virtualoverride)
- [include 规范](#include-规范)
- [使用class和struct关键字](#使用class和struct关键字)
- [使用auto类型推导提高代码可读性](#使用auto类型推导提高代码可读性)
- [小心auto带来的不必要拷贝](#小心auto带来的不必要拷贝)
- [头文件独立](#头文件独立)
- [库层次](#库层次)
- [尽可能的减少 #include](#尽可能的减少-include)
- [保持私有的内部头文件](#保持私有的内部头文件)
- [使用 namespace 限定符莱实现前置声明的函数](#使用-namespace-限定符莱实现前置声明的函数)
- [提前退出或 continue 以简化代码](#提前退出或-continue-以简化代码)
- [return 之后不要使用 else](#return-之后不要使用-else)
- [将判断循环转换为判断函数](#将判断循环转换为判断函数)
- [通用例外](#通用例外)
- [附录A 参考列表](#附录a-参考列表)
## 代码风格
### 缩进
- 采用4个空格进行缩进
- 使用空格不要用TAB
### 变量声明
- 每行一个变量
```cpp
// 错误示例
int width, height;
// 正确
int height;
int width;
```
- 尽可能避免短的无意义的变量名(比如"a", "rbarr", "nughdeget")
```cpp
// 错误示例
int a;
double b;
// 正确
int number;
double speed;
```
- 单字符的变量只在临时变量或循环的计数中使用
```cpp
// 错误示例
int i = 1025;
// 正确
int count = 1025;
for ( auto i = 0; i < count; ++i )
{
...
}
```
- 等到真正需要使用时再定义变量
```cpp
// 错误示例
void exampleFunction()
{
int step = 0; // 变量定义在函数开头,但尚未使用
int number = 0; // 变量定义在函数开头,但尚未使用
// 此处是其它业务逻辑代码
...
// 很久之后才使用 变量 x 和 y
step = 5;
number = 10;
}
// 正确
void exampleFunction()
{
// 变量在需要时定义
int step = 5;
int number = 10;
std::cout << "step: " << step << ", number: " << number << "\n";
}
```
- 首字母小写,后续单词以大写开头(驼峰式)
- 避免使用缩写
```cpp
// 错误示例
short Cntr;
char ITEM_DELIM = '';
// 正确
short counter;
char itemDelimiter = '';
```
- 类名总是以大写开头
```cpp
// 错误示例
class person
{
...
}
// 正确
class Person
{
...
}
```
### 空白
- 利用空行将语句恰当地分组
- 总是使用一个空行(不要空多行)
- 总是在每个关键字和大括号前使用一个空格
```cpp
// 错误示例
if(foo)
{
}
// 正确
if (foo)
{
}
```
- 对指针和引用,在类型和*、&之间加一个空格,但在*、&与变量之间不加空格?
```cpp
char *something;
const QString &myString;
const char* const message = "hello";
```
- 二元操作符前后加空白
- 类型转换后不加空白
- 尽量避免C风格的类型转换
```cpp
// 错误示例
char* blockOfMemory = (char* ) malloc(data.size());
// 正确
char *blockOfMemory = reinterpret_cast<char*>(malloc(data.size()));
```
### 大括号
- 基本原则:左大括号和语句之间换行,左大括号总是单独占一行
```cpp
// 错误示例
if (codec){
}
// 正确
if (codec)
{
}
```
- 例外1定义命名空间时左大括号与命名空间同行
```cpp
namespace Test {
qDebug("foo: %i", g);
}
```
- 例外2控制语句的body为空时大括号与控制语句同行
```cpp
while(a) {}
```
- 控制语句的body中只有一行时不使用大括号
```cpp
// 错误示例
if (address.isEmpty())
{
return false;
}
for (int i = 0; i < 10; +''i)
{
qDebug("%i", i);
}
// 正确
if (address.isEmpty())
return false;
for (int i = 0; i < 10;i)
qDebug("%i", i);
```
- 例外1如果父语句跨多行则使用大括号
```cpp
// 正确
if (address.isEmpty() || !isValid()
|| !codec)
{
return false;
}
```
- 例外2在if-else结构中有一处跨多行则使用大括号
```cpp
// 错误示例
if (address.isEmpty())
return false;
else
{
qDebug("%s", qPrintable(address));
it;
}
// 正确
if (address.isEmpty())
{
return false;
}
else
{
qDebug("%s", qPrintable(address));
it;
}
// 错误示例
if (a)
if (b)
else
// 正确
if (a)
{
if (b)
else
}
```
- 如果控制语句的body为空则使用大括号
```cpp
// 错误示例
while (a);
// 正确
while (a) {}
```
### 圆括号
- 使用圆括号将表达式分组,避免需要考虑运算符优先级的情况
```cpp
// 错误示例
if (a && b || c)
// 正确
if ((a && b) || c)
// 错误示例
a | b & c
// 正确
(a + b) & c
```
### Switch 语句
- case 和 switch 位于同一列
- 每一个case必须有一个break(或renturn)语句或者用注释说明无需break
```cpp
switch (myEnum)
{
case Value1:
doSomething();
break;
case Value2:
doSomethingElse();
// fall through
default:
defaultHandling();
break;
}
```
### 断行
- 保持每行短于100个字符需要时进行断行
- 逗号放一行的结束,操作符放到一行的开头。如果你的编辑器太窄,一个放在行尾的操作符不容易被看到。
```cpp
// 错误示例
if (longExpression +
otherLongExpression +
otherOtherLongExpression)
{
...
}
// 正确
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression)
{
...
}
```
### 继承与关键字 `virtual`|`override`
- 子类中重新实现一个虚函数时,头文件中不放置 virtual 关键字但必须显式的标注override关键字。
### include 规范
在文件首部注释(和包含保护,如果是头文件)之后,该文件需要的最小 `#include` 列表应该被列出来。要求的 `#include` 顺序如下:
- 主模块头文件
- 局部的/私有的头文件
- 项目/子项目头文件 clang/..., lldb/..., llvm/..., etc
- 系统文件
并且每一个种类的头文件完整路径应该做一个排序。
主模块头文件适用于声明实现接口的 `.cpp` 文件的 `.h` 文件。该 `#include` 不管处在什么文件系统下总是应该第一个被包含。通过在实现该接口的第一包含头文件可以确保头文件没有隐藏需要但没有显式包含的依赖。同样的,这也是在 `.cpp`中一种指明接口声明在何处的记录。
项目和子项目的头文件应该从最高优先级到最低优先级分组理由同上。举个例子LLDB 同时依赖 clang 和 LLVM并且 clang 依赖 LLVM。所以一个 LLDB 源文件应该最先包含 LLDB ,其次是 clang 头文件,其次是 LLVM 头文件这样做是为了在源文件中的头文件或更前的头文件包含情况下降低LLDB头文件包含缺失的可能。clang 同样的应该在包含LLVM文件之前包含其自身的头文件。
### 使用class和struct关键字
在C++中,`class` 和 `struct` 关键字在绝大数的情况下可以互换。仅仅的区别在于定义一个 class 时所有的成员默认为 private而struct默认为 public。
- 给定 `class` 和 `struct` 的所有生命和定义必须使用同一个关键字
```cpp
// Avoid if `Example` is defined as a struct.
class Example;
// OK.
struct Example;
struct Example { ... };
```
- 当所有的成员都为公开声明时使用 `struct`
```cpp
// Avoid using `struct` here, use `class` instead.
struct Foo {
private:
int Data;
public:
Foo() : Data(0) { }
int getData() const { return Data; }
void setData(int D) { Data = D; }
};
// OK to use `struct`: all members are public.
struct Bar {
int Data;
Bar() : Data(0) { }
};
```
### 使用auto类型推导提高代码可读性
一些人主张在C++11种几乎总是使用auto的原则然而LLVM保持更温和的态度。仅在如果使用auto可以提升代码的可读性或者更利于维护的情况下使用。并不几乎总是使用 `auto`,而是在初始化像 `cast<Foo>(...)` 或者类型可以明显的从上下文中获取的地方中使用。其它时候 `auto` 良好用于其用途是无论如何该类型都会被抽象化,经常出现在容器的 `typedef` 之后如 `std::vector<T>::iterator`。
同样的C++14 新增类型可以为auto的通用lambda表达式可以在原本使用模板的地方使用它。
### 小心auto带来的不必要拷贝
`auto` 的便利性更容易遗忘它默认是拷贝的行为,尤其是在基于范围的循环,粗心的代价很昂贵。
值使用 `auto &` 指针使用 `auto *` 除非你需要进行拷贝。
```cpp
// Typically there's no reason to copy.
for (const auto &Val : Container) { observe(Val); }
for (auto &Val : Container) { Val.change(); }
// Remove the reference if you really want a new copy.
for (auto Val : Container) { Val.change(); saveSomewhere(Val); }
// Copy pointers, but make it clear that they're pointers.
for (const auto *Ptr : Container) { observe(*Ptr); }
for (auto *Ptr : Container) { Ptr->change(); }
```
### 头文件独立
头文件应该保持独立(独立编译)并且以`.h`结尾。非头文件的包含应该以`.inc`结尾并且谨慎使用。
所有的头文件都应该是独立的。用户或者重构工具不应该强制附加特定条件才能够包含这个头文件。特别的,一个头文件应该存在头文件保护和所有需要的其他头文件。
有一些及少见的情况,设计被包含的文件不是独立的。它们往往有意的被包含在不常用的地方,比如一个文件的中间位置。它们可能不使用头文件保护并且可能不包括必要的先决条件。这些文件的名称以 `.inc` 扩展。谨慎使用这种文件,尽可能使用独立的头文件。
通常而言,一个头文件应该被一个或多个 `.cpp` 实现。每一个 `.cpp` 文件都应该首先包含定义接口的头文件。确保头文件所有的依赖都能够显式正确的添加至该头文件中。
### 库层次
头文件目录(如 include/llvm/Foo定义了库Foo。库之间的依赖在它们实现lib/Foo中的 LLVMBuild.txt 定义。一个库应该仅使用依赖所列出库的内容。
一些经典的Unix链接器Mac & Windows 链接器比如lld不强制执行可以强制执行某些约束。Unix 链接器从左往右在命令行中搜索特定的库并且不会重复访问同一个库。这种情况下,库之间就不存在循环依赖关系。
这样不会完全强制的执行所有的相互库依赖,且重要的是不会执行由内联函数带来的头文件循环依赖。回答是否正确分层的一个好方法是判断 Unix 链接器是否可以正确链接使用非内联函数代替内联函数的程序。(对于所有依赖的有效顺序 - 由于链接的方案是线性的因此可能潜伏一些隐式的依赖A 依赖于B和C所以有效的顺序为“CBA” 或者“BCA”两种显示的依赖都在使用之前出现。但对于第一种情况B如果隐式的依赖于C仍然可以链接成功或者在第二种情况相反的依赖也可以链接成功
### 尽可能的减少 #include
`#include` 降低了编译的性能,在不是必需的时候不要包含,尤其是在头文件中。
有一些情况是你需要获取到类的定义再去使用或者继承它,这些情况则在文件的首部进行 `#include`。然而也要想到,存在非常多的情况是不需要拥有类的完整定义的。如果正在使用类指针或引用,则不必包含该头文件。如果只是的从原型函数或者方法中返回一个类的实例,也不需要头文件。实际上,在大多数的情况下,你根本不需要类的定义。不进行 `#include` 可以加速编译。
这个建议很容易导致偏激的态度,然而,你**必须**包含所有正在使用的头文件,无论是直接还是间接从其它文件包含。为确保你不会意外的忘记在你的模块头文件中包含头文件,确认在实现文件中**第一个**包含你的模块头文件(就像上面提到的)。这样,你后面就会发现不会再有隐藏的依赖了。
### 保持私有的内部头文件
很多模块实现非常复杂导致使用了多个实现文件(`.cpp`文件)。不要尝试将公共交互接口(帮助类,扩展函数等)放入到公共的模块头文件中。
如果你真的需要这么做的话,在相同的目录下放入一个私有的头文件让源文件局部包含。这样确保你的私有接口保留了私有属性并且不向外发布。
> 允许将扩展实现的方法放入到一个公共类自身中,但是需要让它们设为私有(保护),一切就正常。
### 使用 namespace 限定符莱实现前置声明的函数
当源文件中提供一个非内联(或者叫外部实现)的函数实现时,不要在源文件中打开 namespace 块namespace xx {})。相反的,使用 namespace 标记符来帮助确保你的定义匹配上已经存在的声明。像这样:
```cpp
// Foo.h
namespace llvm {
int foo(const char *s);
}
// Foo.cpp
#include "Foo.h"
using namespace llvm;
int llvm::foo(const char *s) {
// ...
}
```
这样做能够避免实现不能匹配头文件中声明的bug。举个例子下面的C++代码定义了一个新的重载`llvm::foo`来取代在头文件中存在函数声明的实现:
```cpp
// Foo.cpp
#include "Foo.h"
namespace llvm {
int foo(char *s) { // Mismatch between "const char *" and "char *"
}
} // end namespace llvm
```
这个错误在快构建完成之前不能够被捕获直到产生函数调用找不到函数定义的链接错误。如果这个函数使用namespace标记符来代替这个错误直接在这个定义编译时被捕获。
类方法实现必须类已经被命名并且新的重载不能够引入到外部,所以这个建议对它们不起作用。
### 提前退出或 continue 以简化代码
在阅读代码时,读者需要在记住有多少状态及前置条件去阅读代码块。为了减少尽可能缩进而又不是代码变得更难理解,在长循环中使用提前退出或者`continue`关键字是一个好的方法。观察未使用提前退出的代码:
```cpp
Value *doSomething(Instruction *I) {
if (!I->isTerminator() &&
I->hasOneUse() && doOtherThing(I)) {
... some long code ....
}
return 0;
}
```
这份代码当`if`作用体非常庞大时有一些问题。当你正在查看这个函数的顶部时对于是只做和结束指令相关的事情还是其他的判断操作是不够清晰的。其二由于if语句使注释难以布局相对的也更难以去描述这个判断为何很重要。其三当你深入代码时它已经被缩进了一级。最后当阅读函数的顶部代码时if判断的结构是不是为真的结果不够清晰你必须要阅读到函数的底端才知道返回了 null。
更好的代码格式如下:
```cpp
Value *doSomething(Instruction *I) {
// Terminators never need 'something' done to them because ...
if (I->isTerminator())
return 0;
// We conservatively avoid transforming instructions with multiple uses
// because goats like cheese.
if (!I->hasOneUse())
return 0;
// This is really just here for example.
if (!doOtherThing(I))
return 0;
... some long code ....
}
```
这修复了上面提到的那些问题。一个类似的问题频繁的出现在`for`循环中,一个简单的例子如下:
```cpp
for (Instruction &I : BB) {
if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
Value *LHS = BO->getOperand(0);
Value *RHS = BO->getOperand(1);
if (LHS != RHS) {
...
}
}
}
```
当你的循环非常非常简短的循环时这种结构是良好的。但是当循环超过10-15行时用户在扫视时会变得难以阅读和理解。这种代码存在的问题是缩进的非常的快这意味着读者脑袋必须要保持大量的上下文来记住在这个循环中随即发生的事情因为他们不知道`if`条件是否或者何时存在 `else`。更强烈推荐的循环结构如下:
```cpp
for (Instruction &I : BB) {
auto *BO = dyn_cast<BinaryOperator>(&I);
if (!BO) continue;
Value *LHS = BO->getOperand(0);
Value *RHS = BO->getOperand(1);
if (LHS == RHS) continue;
...
}
```
在函数中使用尽快退出的好处是:减少循环的缩进,更易于描述为何条件为真,而没有 `else` 出现则在读者的脑袋中变得更加明显。如果一个循环非常的庞大,这些做法具有巨大的理解优势。
### return 之后不要使用 else
同样的理由如上(减少缩进和易于理解),请不要使用 `else`或者`else if`在终止的控制流之后,就像是 `return`, `break`, `continue`, `goto` 等等,如:
```cpp
case 'J': {
if (Signed) {
Type = Context.getsigjmp_bufType();
if (Type.isNull()) {
Error = ASTContext::GE_Missing_sigjmp_buf;
return QualType();
} else {
break; // Unnecessary.
}
} else {
Type = Context.getjmp_bufType();
if (Type.isNull()) {
Error = ASTContext::GE_Missing_jmp_buf;
return QualType();
} else {
break; // Unnecessary.
}
}
}
```
更好的做法如下:
```cpp
case 'J':
if (Signed) {
Type = Context.getsigjmp_bufType();
if (Type.isNull()) {
Error = ASTContext::GE_Missing_sigjmp_buf;
return QualType();
}
} else {
Type = Context.getjmp_bufType();
if (Type.isNull()) {
Error = ASTContext::GE_Missing_jmp_buf;
return QualType();
}
}
break;
```
这个例子更好的写法如下:
```cpp
case 'J':
if (Signed)
Type = Context.getsigjmp_bufType();
else
Type = Context.getjmp_bufType();
if (Type.isNull()) {
Error = Signed ? ASTContext::GE_Missing_sigjmp_buf :
ASTContext::GE_Missing_jmp_buf;
return QualType();
}
break;
```
这样做的目的是减少缩进和在阅读代码时需要跟踪的代码量。
### 将判断循环转换为判断函数
通过短循环计算一个 boolean 值是非常常见的写法。有一系列的常见方式,比如这种:
```cpp
bool FoundFoo = false;
for (unsigned I = 0, E = BarList.size(); I != E; ++I)
if (BarList[I]->isFoo()) {
FoundFoo = true;
break;
}
if (FoundFoo) {
...
}
```
相对这种循环的方式,我们更偏向使用一个使用尽早退出的判断函数(可能是静态):
```cpp
/// \returns true if the specified list has an element that is a foo.
static bool containsFoo(const std::vector<Bar*> &List) {
for (unsigned I = 0, E = List.size(); I != E; ++I)
if (List[I]->isFoo())
return true;
return false;
}
...
if (containsFoo(BarList)) {
...
}
```
这样做有很多理由减少了缩进并且分离出可以让其它代码检测相同判断的共享代码。更重要的是让你强制为这个函数起一个名字并且强制让你再为它写上注释。在这个简短的例子中没有添加很多值然而如果这个if条件非常复杂读者通过这个判断可以非常容易地理解代码。而不是一开始就面对如何检测 `BarList` 中是否包含了 `foo` 的内联细节,我们可以信任这个函数名称并且继续在更好的位置进行阅读。
### 通用例外
如果它使你的代码看起来不好,你可以打破任何一个规则 。
## 附录A 参考列表
1. [Qt编码风格/Qt Coding Style](https://wiki.qt.io/Qt_Coding_Style)
2. [LLVM编码标准/LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html)
3. [谷歌C++风格指南/Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
4. [C++核心指南/Cpp Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)