Compare commits

...

20 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
8 changed files with 717 additions and 233 deletions

View File

@ -1,3 +1,7 @@
# wiki
企业知识库通过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/)

View File

@ -1,6 +1,6 @@
# LLVM 编码规范
**LLVM Coding Standards [官网](https://llvm.org/docs/CodingStandards.html) | 历史翻译版本 [Github](https://github.com/zxhio/coding)**
**LLVM Coding Standards [官网](https://llvm.org/docs/CodingStandards.html)**
- [LLVM 编码规范](#llvm-编码规范)
- [导论](#导论)
@ -29,7 +29,7 @@
- [不要使用静态构造函数](#不要使用静态构造函数)
- [使用class和struct关键字](#使用class和struct关键字)
- [不要使用花括号初始化列表调用构造函数](#不要使用花括号初始化列表调用构造函数)
- [ 使用auto类型推导提高代码可读性](#-使用auto类型推导提高代码可读性)
- [使用auto类型推导提高代码可读性](#使用auto类型推导提高代码可读性)
- [小心auto带来的不必要拷贝](#小心auto带来的不必要拷贝)
- [小心对指针排序带来的不确定性](#小心对指针排序带来的不确定性)
- [小心相同元素不确定的排序顺序](#小心相同元素不确定的排序顺序)
@ -63,7 +63,7 @@
- [Dont Use Braces on Simple Single-Statement Bodies of if/else/loop Statements](#dont-use-braces-on-simple-single-statement-bodies-of-ifelseloop-statements)
- [其它参考](#其它参考)
## <span id="intro">导论</span>
## 导论
This document describes coding standards that are used in the LLVM project. Although no coding standards should be regarded as absolute requirements to be followed in all instances, coding standards are particularly important for large-scale code bases that follow a library-based design (like LLVM).
@ -85,13 +85,13 @@ The ultimate goal of these guidelines is to increase the readability and maintai
这些准则的最终目标就是提高我们公共源码库的可读性和可维护性。
## <span id="lang">语言、库和标准</span>
## 语言、库和标准
Most source code in LLVM and other LLVM projects using these coding standards is C++ code. There are some places where C code is used either due to environment restrictions, historical restrictions, or due to third-party source code imported into the tree. Generally, our preference is for standards conforming, modern, and portable C++ code as the implementation language of choice.
大部分在 *LLVM* 和其它 *LLVM* 项目中使用该代码规范的源码为C++代码。由于一些环境、历史限制或者引入至源码树中的第三方源码导致在某些地方存在C代码。通常而言我们倾向于符合标准现代和可移植的C++代码作为选择实现的语言。
## <span id="cppver">C++ 标准版本</span>
## C++ 标准版本
Unless otherwise documented, LLVM subprojects are written using standard C++14 code and avoid unnecessary vendor-specific extensions.
@ -111,7 +111,7 @@ Each toolchain provides a good reference for what it accepts:
- GCC: <https://gcc.gnu.org/projects/cxx-status.html#cxx14>
- MSVC: <https://msdn.microsoft.com/en-us/library/hh567368.aspx>
## <span id="stdlib">C++ 标准库</span>
## C++ 标准库
Instead of implementing custom data structures, we encourage the use of C++ standard library facilities or LLVM support libraries whenever they are available for a particular task. LLVM and related projects emphasize and rely on the standard library facilities and the LLVM support libraries as much as possible.
@ -131,7 +131,7 @@ LLVM支持的库例如ADT实现了标准库缺少的特有数据结构或
更多关于LLVM数据结构和做出权衡的信息请参考 [that section of the programmers manual](https://llvm.org/docs/ProgrammersManual.html#picking-the-right-data-structure-for-a-task)。
## <span id="go_guid">Go 代码准则</span>
## Go 代码准则
Any code written in the Go programming language is not subject to the formatting rules below. Instead, we adopt the formatting rules enforced by the gofmt tool.
@ -141,17 +141,17 @@ Go code should strive to be idiomatic. Two good sets of guidelines for what this
Go 代码应该尽量符合习惯。[Effective Go](https://golang.org/doc/effective_go.html) 和 [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) 是两套好的准则。
## <span id="src_issues">机械的代码问题</span>
## 机械的代码问题
### <span id="src_fmt">代码格式化</span>
### 代码格式化
#### <span id="comment">注释</span>
#### 注释
Comments are important for readability and maintainability. When writing comments, write them as English prose, using proper capitalization, punctuation, etc. Aim to describe what the code is trying to do and why, not how it does it at a micro level. Here are a few important things to document:
注释对于可读性和可维护性非常重要。当写注释时,使用适当的大小写、标点符号等将其写成英文句子。专注于描述代码试图做什么和为什么这么做,不要从微观的角度去描述代码做的事情。以下是一些重要的记录:
#### <span id="comment_header">头文件</span>
#### 头文件
Every source file should have a header on it that describes the basic purpose of the file. The standard header looks like this:
@ -188,13 +188,13 @@ The main body is a Doxygen comment (identified by the /// comment marker instead
主体部分是一些描述该文件用途的*Doxygen*注释(通过 `///` 注释记号而不是 `//`来标记)。第一句(段落以 `\brief`开始)用作于摘要。任何附加的信息都应该以空行分隔。如果一个算法是基于一篇论文或者在其它来源说明的,则提供一个引用。
#### <span id="class_overview">类概述</span>
#### 类概述
Classes are a fundamental part of an object-oriented design. As such, a class definition should have a comment block that explains what the class is used for and how it works. Every non-trivial class is expected to have a doxygen comment block.
类是面向对象设计的基础部分。一个类定义应该有一个描述该类的用途和如何工作的注释块。每一个非平凡类都应该有一个*doxygen*注释块。
#### <span id="method_info">方法信息</span>
#### 方法信息
Methods and global functions should also be documented. A quick note about what it does and a description of the edge cases is all that is necessary here. The reader should be able to understand how to use interfaces without reading the code itself.
@ -204,7 +204,7 @@ Good things to talk about here are what happens when something unexpected happen
这里更值得讨论的事情是当意外出现时会发生什么例如方法是否返回null
### <span id="comment_fmt">注释格式化</span>
### 注释格式化
In general, prefer C++-style comments (// for normal comments, /// for doxygen documentation comments). There are a few cases when it is useful to use C-style (/**/) comments however:
@ -244,7 +244,7 @@ Commenting out large blocks of code is discouraged, but if you really have to do
不推荐对大规模代码块进行注释,如果必须要注释的话(记录意图或者作为调试打印的建议),使用 `#if 0` 和 `#endif`。 适当的嵌套是比一般的使用C风格注释更好的行为。
### <span id="comment_doxygen">使用doxygen注释</span>
### 使用doxygen注释
Use the \file command to turn the standard file header into a file-level comment.
@ -336,7 +336,7 @@ void example();
void example() { ... }
```
### <span id="err_warn_msg">错误和警告消息</span>
### 错误和警告消息
Clear diagnostic messages are important to help users identify and fix issues in their inputs. Use succinct but correct English prose that gives the user the context needed to understand what went wrong. Also, to match error message styles commonly produced by other tools, start the first sentence with a lower-case letter, and finish the last sentence without a period, if it would end in one otherwise. Sentences which end with different punctuation, such as “did you forget ;?”, should still do so.
@ -366,7 +366,7 @@ When using report_fatal_error, follow the same standards for the message as regu
当使用 `report_fatal_error`,遵循相同的规范保持错误消息的规律。断言消息和`llvm_unreachable`调用则没有必要遵循这种自动格式化的格式,因此这些参考不一定合适。
### <span id="include_style">include 风格</span>
### include 风格
Immediately after the header file comment (and include guards if working on a header file), the minimal list of #includes required by the file should be listed. We prefer these #includes to be listed in this order:
@ -394,7 +394,7 @@ LLVM project and subproject headers should be grouped from most specific to leas
LLVM 项目和子项目的头文件应该从最高优先级到最低优先级分组理由同上。举个例子LLDB 同时依赖 clang 和 LLVM并且 clang 依赖 LLVM。所以一个 LLDB 源文件应该最先包含 LLDB ,其次是 clang 头文件,其次是 LLVM 头文件这样做是为了在源文件中的头文件或更前的头文件包含情况下降低LLDB头文件包含缺失的可能。clang 同样的应该在包含LLVM文件之前包含其自身的头文件。这个规则适用于所有的 LLVM 的子项目。
### <span id="src_width">代码宽度</span>
### 代码宽度
Write your code to fit within 80 columns.
@ -404,7 +404,7 @@ There must be some limit to the width of the code in order to allow developers t
代码的宽度限制是为了允许开发者同时在一个窗口中并排打开多个文件而可以合适的显示。如果你打算选择一个宽度限制这个限制是任意的但是你同样需要选择一个标准。选择90列代替80列并不增加任何有意义的值并且将对输出代码有坏处。此外有非常多个其它项目对80列进行了标准化因此有些人为它对编辑器做了配置对比其它的宽度例如90列
### <span id="whitespace">空格</span>
### 空格
In all cases, prefer spaces to tabs in source files. People have different preferred indentation levels, and different styles of indentation that they like; this is fine. What isnt fine is that different editors/viewers expand tabs out to different tab stops. This can cause your code to look completely unreadable, and it is not worth dealing with.
@ -418,7 +418,7 @@ Do not add trailing whitespace. Some common editors will automatically remove tr
不要在结尾添加空格一些常用的编辑器会在保存文件时自动的移除结尾的空格这样导致在diff或者commit时产生不相关的改变。
#### <span id="lambda_fmt">类代码块格式化lambda</span>
#### 类代码块格式化lambda
When formatting a multi-line lambda, format it like a block of code. If there is only one multi-line lambda in a statement, and there are no expressions lexically after it in the statement, drop the indent to the standard two space indent for a block of code, as if it were an if-block opened by the preceding part of the statement:
@ -458,7 +458,7 @@ dyn_switch(V->stripPointerCasts(),
});
```
#### <span id="init_list_fmt">花括号初始化列表</span>
#### 花括号初始化列表
Starting from C++11, there are significantly more uses of braced lists to perform initialization. For example, they can be used to construct aggregate temporaries in expressions. They now have a natural way of ending up nested within each other and within function calls in order to build up aggregates (such as option structs) from local variables.
@ -481,9 +481,9 @@ llvm::Constant *Mask[] = {
这个格式化方案同样让工具例如 clang-format 可以非常简单的预测、一致的自动格式化。
### <span id="lang_compile_issue">语言和编译器问题</span>
### 语言和编译器问题
#### <span id="compile_warn">像错误一样对待编译警告</span>
#### 像错误一样对待编译警告
Compiler warnings are often useful and help improve the code. Those that are not useful, can be often suppressed with a small code change. For example, an assignment in the if condition is often a typo:
@ -505,13 +505,13 @@ if ((V = getValue())) {
}
```
#### <span id="portable_code">编写可移植的代码</span>
#### 编写可移植的代码
In almost all cases, it is possible to write completely portable code. When you need to rely on non-portable code, put it behind a well-defined and well-documented interface.
在绝大数的情况下,尽可能的编写完全可移植的代码。当你需要依赖于不可移植的代码时,将其放入良好声明和良好的文档接口中。
#### <span id="no_rtti_exception">不要使用 RTTI 或 Exceptions</span>
#### 不要使用 RTTI 或 Exceptions
In an effort to reduce code and executable size, LLVM does not use exceptions or RTTI (runtime type information, for example, dynamic_cast<>).
@ -519,7 +519,7 @@ That said, LLVM does make extensive use of a hand-rolled form of RTTI that use t
为了尽量减小代码和可执行文件的大小LLVM 未使用异常或者RTTI运行时信息如 dynamic_cast<>。也就是说LLVM确实使用了大量的手动滚动的RTTI的形式该形式使用了像 [isa<>cast<> 和 dyn_cast<>](https://llvm.org/docs/ProgrammersManual.html#isa) 之类的模板。这种形式的RTTI是可选的并且可以[添加至任何类](https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html)中。
#### <span id="no_static_ctor">不要使用静态构造函数</span>
#### 不要使用静态构造函数
Static constructors and destructors (e.g., global variables whose types have a constructor or destructor) should not be added to the code base, and should be removed wherever possible.
@ -533,7 +533,7 @@ Static constructors have negative impact on launch time of programs that use LLV
将LLVM作为库使用时静态构造函数在程序启动的时候具有负面的影响。我们真正希望将额外的LLVM目标或者其它库链接至应用程序中的开销为零但是静态构造函数违背了这个目标。
#### <span id="class_struct">使用class和struct关键字</span>
#### 使用class和struct关键字
In C++, the class and struct keywords can be used almost interchangeably. The only difference is when they are used to declare a class: class makes all members private by default while struct makes all members public by default.
@ -574,7 +574,7 @@ In C++, the class and struct keywords can be used almost interchangeably. The on
};
```
#### <span id="no_brace_call_ctor">不要使用花括号初始化列表调用构造函数</span>
#### 不要使用花括号初始化列表调用构造函数
Starting from C++11 there is a “generalized initialization syntax” which allows calling constructors using braced initializer lists. Do not use these to call constructors with non-trivial logic or if you care that youre calling some particular constructor. Those should look like function calls using parentheses rather than like aggregate initialization. Similarly, if you need to explicitly name the type and call its constructor to create a temporary, dont use a braced initializer list. Instead, use a braced initializer list (without any type for temporaries) when doing aggregate initialization or something notionally equivalent. Examples:
@ -607,7 +607,7 @@ If you use a braced initializer list when initializing a variable, use an equals
int data[] = {0, 1, 2, 3};
```
#### <span id="auto"> 使用auto类型推导提高代码可读性</span>
#### 使用auto类型推导提高代码可读性
Some are advocating a policy of “almost always auto” in C++11, however LLVM uses a more moderate stance. Use auto if and only if it makes the code more readable or easier to maintain. Dont “almost always” use auto, but do use auto with initializers like cast<Foo>(...) or other places where the type is already obvious from the context. Another time when auto works well for these purposes is when the type would have been abstracted away anyways, often behind a containers typedef such as std::vector<T>::iterator.
@ -617,7 +617,7 @@ Similarly, C++14 adds generic lambda expressions where parameter types can be au
同样的C++14 新增类型可以为auto的通用lambda表达式可以在原本使用模板的地方使用它。
#### <span id="beware_auto_copy">小心auto带来的不必要拷贝</span>
#### 小心auto带来的不必要拷贝
The convenience of auto makes it easy to forget that its default behavior is a copy. Particularly in range-based for loops, careless copies are expensive.
@ -640,7 +640,7 @@ for (const auto *Ptr : Container) { observe(*Ptr); }
for (auto *Ptr : Container) { Ptr->change(); }
```
#### <span id="beware_order_pointer">小心对指针排序带来的不确定性</span>
#### 小心对指针排序带来的不确定性
In general, there is no relative ordering among pointers. As a result, when unordered containers like sets and maps are used with pointer keys the iteration order is undefined. Hence, iterating such containers may result in non-deterministic code generation. While the generated code might work correctly, non-determinism can make it harder to reproduce bugs and debug the compiler.
@ -650,17 +650,17 @@ In case an ordered result is expected, remember to sort an unordered container b
如果期望结果有序切记在迭代器之前对无序容器进行排序。或者你想迭代指针的key则使用顺序容器像 `vector/MapVector/SetVector`。
#### <span id="beware_equal_order">小心相同元素不确定的排序顺序</span>
#### 小心相同元素不确定的排序顺序
std::sort uses a non-stable sorting algorithm in which the order of equal elements is not guaranteed to be preserved. Thus using std::sort for a container having equal elements may result in non-deterministic behavior. To uncover such instances of non-determinism, LLVM has introduced a new llvm::sort wrapper function. For an EXPENSIVE_CHECKS build this will randomly shuffle the container before sorting. Default to using llvm::sort instead of std::sort.
`std::sort` 使用了一个不稳定的排序算法,相同元素的顺序不能保证被保留下来。因此使用`std::sort`对一个具有相同元素的容器排序时可能出现不确定的行为。为了发现这些不确定的情况LLVM 引入了一个新的 `llvm::sort` 封装函数。使用 `EXPENSIVE_CHECKS` 编译时在排序之前随机的打乱容器内的顺序。默认使用 `llvm::sort` 代替 `std::sort`。
## <span id="fmt_issue">风格问题</span>
## 风格问题
### <span id="high_level_issue">上层问题</span>
### 上层问题
#### <span id="header_self_include">头文件独立</span>
#### 头文件独立
Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly.
@ -678,7 +678,7 @@ In general, a header should be implemented by one or more .cpp files. Each of th
通常而言,一个头文件应该被一个或多个 `.cpp` 实现。每一个 `.cpp` 文件都应该首先包含定义接口的头文件。确保头文件所有的依赖都能够显式正确的添加至该头文件中。
#### <span id="lib_layering">库层次</span>
#### 库层次
A directory of header files (for example include/llvm/Foo) defines a library (Foo). Dependencies between libraries are defined by the LLVMBuild.txt file in their implementation (lib/Foo). One library (both its headers and implementation) should only use things from the libraries listed in its dependencies.
@ -692,7 +692,7 @@ This doesnt fully enforce all inter-library dependencies, and importantly doe
这样不会完全强制的执行所有的相互库依赖,且重要的是不会执行由内联函数带来的头文件循环依赖。回答是否正确分层的一个好方法是判断 Unix 链接器是否可以正确链接使用非内联函数代替内联函数的程序。(对于所有依赖的有效顺序 - 由于链接的方案是线性的因此可能潜伏一些隐式的依赖A 依赖于B和C所以有效的顺序为“CBA” 或者“BCA”两种显示的依赖都在使用之前出现。但对于第一种情况B如果隐式的依赖于C仍然可以链接成功或者在第二种情况相反的依赖也可以链接成功
#### <span id="little_include">尽可能的减少 #include</span>
#### 尽可能的减少 #include
`#include` hurts compile time performance. Dont do it unless you have to, especially in header files.
@ -706,7 +706,7 @@ It is easy to try to go too overboard on this recommendation, however. You must
这个建议很容易导致偏激的态度,然而,你**必须**包含所有正在使用的头文件,无论是直接还是间接从其它文件包含。为确保你不会意外的忘记在你的模块头文件中包含头文件,确认在实现文件中**第一个**包含你的模块头文件(就像上面提到的)。这样,你后面就会发现不会再有隐藏的依赖了。
#### <span id="intenal_header">保持私有的内部头文件</span>
#### 保持私有的内部头文件
Many modules have a complex implementation that causes them to use more than one implementation (.cpp) file. It is often tempting to put the internal communication interface (helper classes, extra functions, etc) in the public module header file. Dont do this!
@ -720,7 +720,7 @@ If you really need to do something like this, put a private header file in the s
> 允许将扩展实现的方法放入到一个公共类自身中,但是需要让它们设为私有(保护),一切就正常。
#### <span id="namespace_impl">使用 namespace 限定符莱实现前置声明的函数</span>
#### 使用 namespace 限定符莱实现前置声明的函数
When providing an out of line implementation of a function in a source file, do not open namespace blocks in the source file. Instead, use namespace qualifiers to help ensure that your definition matches an existing declaration. Do this:
@ -761,7 +761,7 @@ Class method implementations must already name the class and new overloads canno
类方法实现必须类已经被命名并且新的重载不能够引入到外部,所以这个建议对它们不起作用。
#### <span id="early_exit_continue">提前退出或 continue 以简化代码</span>
#### 提前退出或 continue 以简化代码
When reading code, keep in mind how much state and how many previous decisions have to be remembered by the reader to understand a block of code. Aim to reduce indentation where possible when it doesnt make it more difficult to understand the code. One great way to do this is by making use of early exits and the continue keyword in long loops. Consider this code that does not use an early exit:
@ -842,7 +842,7 @@ This has all the benefits of using early exits for functions: it reduces nesting
在函数中使用尽快退出的好处是:减少循环的缩进,更易于描述为何条件为真,而没有 `else` 出现则在读者的脑袋中变得更加明显。如果一个循环非常的庞大,这些做法具有巨大的理解优势。
#### <span id="no_else_after_return">return 之后不要使用 else</span>
#### return 之后不要使用 else
For similar reasons as above (reduction of indentation and easier reading), please do not use 'else' or 'else if' after something that interrupts control flow — like return, break, continue, goto, etc. For example:
@ -911,7 +911,7 @@ The idea is to reduce indentation and the amount of code you have to keep track
这样做的目的是减少缩进和在阅读代码时需要跟踪的代码量。
#### <span id="predicate_loop_func">将判断循环转换为判断函数</span>
#### 将判断循环转换为判断函数
It is very common to write small loops that just compute a boolean value. There are a number of ways that people commonly write these, but an example of this sort of thing is:
@ -953,9 +953,9 @@ There are many reasons for doing this: it reduces indentation and factors out co
这样做有很多理由减少了缩进并且分离出可以让其它代码检测相同判断的共享代码。更重要的是让你强制为这个函数起一个名字并且强制让你再为它写上注释。在这个简短的例子中没有添加很多值然而如果这个if条件非常复杂读者通过这个判断可以非常容易地理解代码。而不是一开始就面对如何检测 `BarList` 中是否包含了 `foo` 的内联细节,我们可以信任这个函数名称并且继续在更好的位置进行阅读。
### <span id="low_level_issue">底层问题</span>
### 底层问题
#### <span id="name">合适的类型、函数、变量和枚举命名</span>
#### 合适的类型、函数、变量和枚举命名
Poorly-chosen names can mislead the reader and cause bugs. We cannot stress enough how important it is to use descriptive names. Pick names that match the semantics and role of the underlying entities, within reason. Avoid abbreviations unless they are well known. After picking a good name, make sure to use consistent capitalization for the name, as inconsistency requires clients to either memorize the APIs or to look it up to find the exact spelling.
@ -1009,7 +1009,7 @@ Vehicle makeVehicle(VehicleType Type) {
}
```
#### <span id="assert">善用断言</span>
#### 善用断言
Use the “assert” macro to its fullest. Check all of your preconditions and assumptions, you never know when a bug (not necessarily even yours) might be caught early by an assertion, which reduces debugging time dramatically. The “<cassert>” header file is probably already included by the header files you are using, so it doesnt cost anything to use it.
@ -1091,7 +1091,7 @@ bool NewToSet = Myset.insert(Value); (void)NewToSet;
assert(NewToSet && "The value shouldn't be in the set yet");
```
#### <span id="no_namespace_std">不要使用 using namesapce std</span>
#### 不要使用 using namesapce std
In LLVM, we prefer to explicitly prefix all identifiers from the standard namespace with an “std::” prefix, rather than rely on “using namespace std;”.
@ -1109,13 +1109,13 @@ The exception to the general rule (i.e. its not an exception for the std name
一般规则的例外(`std`命名空间不是例外是实现文件。打个比方LLVM项目中所有实现的代码都位于llvm命名空间中。所以在`.cpp`文件include之后使用 `using namespace std;`指令是可行的,且实际上更清晰。在基于花括号缩进的源码编辑器可以减少主体的缩进,并且让上下文在概念上更加清晰。此规则的一般形式是,在任何命名空间内实现代码的任何`.cpp`文件都可以使用该命名空间(及其父命名空间),但不应该使用其它的命名空间。
#### <span id="virtual_method">为头文件中的类提供虚函数锚</span>
#### 为头文件中的类提供虚函数锚
If a class is defined in a header file and has a vtable (either it has virtual methods or it derives from classes with virtual methods), it must always have at least one out-of-line virtual method in the class. Without this, the compiler will copy the vtable and RTTI into every .o file that #includes the header, bloating .o file sizes and increasing link times.
如果一个定义在头文件内的类存在虚表(含有虚表或者从有虚表继承),在类中必须总是至少含有一个非内联的虚方法。否则,编译器将复制 vtable 和 RTTI 信息到每一个包含该头文件中的 `.o` 文件中,导致 `.o` 文件大小膨胀并且增加链接耗时。
#### <span id="no_default_in_switch">不要在全覆盖枚举的switch 中使用 default</span>
#### 不要在全覆盖枚举的switch 中使用 default
-Wswitch warns if a switch, without a default label, over an enumeration does not cover every enumeration value. If you write a default label on a fully covered switch over an enumeration then the -Wswitch warning wont fire when new elements are added to that enumeration. To help avoid adding these kinds of defaults, Clang has the warning -Wcovered-switch-default which is off by default but turned on when building LLVM with a version of Clang that supports the warning.
@ -1125,7 +1125,7 @@ A knock-on effect of this stylistic requirement is that when building LLVM with
这种风格上的需求带来一系列连锁反应是当使用 GCC 编译 LLVM 时如果你从每种枚举都覆盖的case中返回可能得到 "control may reach end of non-void function" 相关的警告,这是因为 GCC 假设枚举表达式可以取任何可以表达的值而不仅仅是那些独立的枚举值。在switch后使用 `llvm_unreachable`来消除这些警告。感觉有点像是在黑GCC
#### <span id="range_based_loop">尽可能的使用基于范围的循环</span>
#### 尽可能的使用基于范围的循环
The introduction of range-based for loops in C++11 means that explicit manipulation of iterators is rarely necessary. We use range-based for loops wherever possible for all newly added code. For example:
@ -1137,7 +1137,7 @@ for (Instruction &I : *BB)
... use I ...
```
#### <span id="eval_end">不要每次通过循环计算 end()</span>
#### 不要每次通过循环计算 end()
In cases where range-based for loops cant be used and it is necessary to write an explicit iterator-based loop, pay close attention to whether end() is re-evaluated on each loop iteration. One common mistake is to write a loop in this style:
@ -1175,7 +1175,7 @@ While the second form of the loop is a few extra keystrokes, we do strongly pref
即使第二种循环的形式有一些额外的按键,我们依然强烈推荐。
#### <span id="forbidden_iostream">禁止包含 iostream</span>
#### 禁止包含 iostream
The use of #include `<iostream>` in library files is hereby forbidden, because many common implementations transparently inject a static constructor into every translation unit that includes it.
@ -1187,7 +1187,7 @@ Note that using the other stream headers (`<sstream>` for example) is not proble
> 新代码应该总是使用 raw_ostream 来写文件,或者 llvm::MemoryBuffer 来读文件。
#### <span id="raw_stream">使用 raw_stream</span>
#### 使用 raw_stream
LLVM includes a lightweight, simple, and efficient stream implementation in llvm/Support/raw_ostream.h, which provides all of the common features of std::ostream. All new code should use raw_ostream instead of ostream.
@ -1197,7 +1197,7 @@ LLVM 包含一个轻量的,简单和高效的流实现,位于 `llvm/Support/
和 `std::ostream` 不同,`raw_ostream` 不是模板并且作为 `class raw_ostream` 可以被前向声明。公共头文件应该不包含该 raw_ostream 头文件,而是应该使用前向声明和常量`raw_ostream`实例。
#### <span id="avoid_std_endl">避免 std::endl</span>
#### 避免 std::endl
The `std::endl` modifier, when used with `iostreams` outputs a newline to the output stream specified. In addition to doing this, however, it also flushes the output stream. In other words, these are equivalent:
@ -1212,7 +1212,7 @@ Most of the time, you probably have no reason to flush the output stream, so it
大多数情况下,你可能没有理由去 flush 输出流,所以更好的做法使用字面量 `'\n'`.
#### <span id="no_class_inline">不要在class中声明的函数中使用 inline</span>
#### 不要在class中声明的函数中使用 inline
A member function defined in a class definition is implicitly inline, so dont put the inline keyword in this case.
@ -1240,13 +1240,13 @@ public:
};
```
### <span id="micro_details">微观细节</span>
### 微观细节
This section describes preferred low-level formatting guidelines along with reasoning on why we prefer them.
这个部分描述了推荐低级的格式化规则及推荐它们的理由。
#### <span id="space_before_parentheses">括号之前的空格</span>
#### 括号之前的空格
Put a space before an open parenthesis only in control flow statements, but not in normal function call expressions and function-like macros. For example:
@ -1267,7 +1267,7 @@ The reason for doing this is not completely arbitrary. This style makes control
这样做的理由并不是任意的,这种格式让控制流操作符更加突出并且让表达式更流畅。
#### <span id="preincrement">推荐前置自增</span>
#### 推荐前置自增
Hard fast rule: Preincrement (++X) may be no slower than postincrement (X++) and could very well be a lot faster than it. Use preincrementation whenever possible.
@ -1277,7 +1277,7 @@ The semantics of postincrement include making a copy of the value being incremen
后置自增的语义上包含拷贝被递增的值,返回它,再前递增这个 “工作值”。对于基本类型,这样做没什么大不了的。但是对于迭代器,这样做是一个很大的问题(举个例子,一些迭代器中包含 stack 和 set 对象,拷贝这些迭代器同样可能会调用这些对象的拷贝构造函数)。通常而言,养成总是使用前置自增的习惯,就不会有问题。
#### <span id="namespace_indent">namespace 缩进</span>
#### namespace 缩进
In general, we strive to reduce indentation wherever possible. This is useful because we want code to fit into 80 columns without excessive wrapping, but also because it makes it easier to understand the code. To facilitate this and avoid some insanely deep nesting on occasion, dont indent namespaces. If it helps readability, feel free to add a comment indicating what namespace is being closed by a }. For example:
@ -1307,7 +1307,7 @@ Feel free to skip the closing comment when the namespace being closed is obvious
当由于任何原因而关闭的命名空间很明显时,请随时跳过结束注释。例如,头文件中最外面的名称空间很少引起混乱。但是匿名命名空间和源文件中半途被关闭的命名空间可能需要说明。
#### <span id="anonymous_namespace">匿名 namespace</span>
#### 匿名 namespace
After talking about namespaces in general, you may be wondering about anonymous namespaces in particular. Anonymous namespaces are a great language feature that tells the C++ compiler that the contents of the namespace are only visible within the current translation unit, allowing more aggressive optimization and eliminating the possibility of symbol name collisions. Anonymous namespaces are to C++ as “static” is to C functions and global variables. While “static” is available in C++, anonymous namespaces are more general: they can make entire classes private to a file.
@ -1450,7 +1450,7 @@ if (auto *D = dyn_cast<FunctionDecl>(D)) {
}
```
## <span id="see_also">其它参考</span>
## 其它参考
A lot of these comments and recommendations have been culled from other sources. Two particularly important books for our work are:

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. 在【此电脑】右键
@ -30,3 +48,17 @@
9. 如无误,则在计算机界面中会成功添加一个新的网络磁盘。
![网络驱动器](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)

View File

@ -9,7 +9,19 @@
- [圆括号](#圆括号)
- [Switch 语句](#switch-语句)
- [断行](#断行)
- [继承与关键字 `virtual`](#继承与关键字-virtual)
- [继承与关键字 `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-参考列表)
@ -23,36 +35,99 @@
### 变量声明
- 每行一个变量
- 尽可能避免短的变量名(比如"a", "rbarr", "nughdeget")
```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
// 错误示例
int a, b;
char *c, *d;
```cpp
// 错误示例
void exampleFunction()
{
int step = 0; // 变量定义在函数开头,但尚未使用
int number = 0; // 变量定义在函数开头,但尚未使用
// 正确
int height;
int width;
char *nameOfThis;
char *nameOfThat;
```
// 此处是其它业务逻辑代码
...
// 很久之后才使用 变量 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 = '';
```cpp
// 错误示例
short Cntr;
char ITEM_DELIM = '';
// 正确
short counter;
char itemDelimiter = '';
```
// 正确
short counter;
char itemDelimiter = '';
```
- 类名总是以大写开头
- 类名总是以大写开头
```cpp
// 错误示例
class person
{
...
}
// 正确
class Person
{
...
}
```
### 空白
@ -60,214 +135,527 @@
- 总是使用一个空行(不要空多行)
- 总是在每个关键字和大括号前使用一个空格
```cpp
// 错误示例
if(foo)
{
}
```cpp
// 错误示例
if(foo)
{
}
// 正确
if (foo)
{
}
```
// 正确
if (foo)
{
}
```
- 对指针和引用,在类型和*&之间加一个空格,但在*&与变量之间不加空格
- 对指针和引用,在类型和*、&之间加一个空格,但在*、&与变量之间不加空格
```cpp
char *x;
const QString &myString;
const char* const y = "hello";
```
```cpp
char *something;
const QString &myString;
const char* const message = "hello";
```
- 二元操作符前后加空白
- 类型转换后不加空白
- 尽量避免C风格的类型转换
```cpp
// 错误示例
char* blockOfMemory = (char* ) malloc(data.size());
```cpp
// 错误示例
char* blockOfMemory = (char* ) malloc(data.size());
// 正确
char *blockOfMemory = reinterpret_cast<char*>(malloc(data.size()));
```
// 正确
char *blockOfMemory = reinterpret_cast<char*>(malloc(data.size()));
```
### 大括号
- 基本原则:左大括号和语句之间换行,左大括号总是单独占一行
- 基本原则:左大括号和语句之间换行,左大括号总是单独占一行
```cpp
// 错误示例
if (codec){
}
```cpp
// 错误示例
if (codec){
}
// 正确
if (codec)
{
}
```
// 正确
if (codec)
{
}
```
- 例外1定义命名空间时左大括号与命名空间同行
```cpp
namespace Test {
```cpp
namespace Test {
qDebug("foo: %i", g);
}
```
}
```
- 例外2控制语句的body为空时大括号与控制语句同行
```cpp
while(a) {}
```
```cpp
while(a) {}
```
- 控制语句的body中只有一行时不使用大括号
```cpp
// 错误示例
if (address.isEmpty())
{
return false;
}
```cpp
// 错误示例
if (address.isEmpty())
{
return false;
}
for (int i = 0; i < 10; +''i)
{
qDebug("%i", i);
}
for (int i = 0; i < 10; +''i)
{
qDebug("%i", i);
}
// 正确
if (address.isEmpty())
return false;
// 正确
if (address.isEmpty())
return false;
for (int i = 0; i < 10;i)
qDebug("%i", i);
```
for (int i = 0; i < 10;i)
qDebug("%i", i);
```
- 例外1如果父语句跨多行则使用大括号
```cpp
// 正确
if (address.isEmpty() || !isValid()
|| !codec)
{
return false;
}
```
```cpp
// 正确
if (address.isEmpty() || !isValid()
|| !codec)
{
return false;
}
```
- 例外2在if-else结构中有一处跨多行则使用大括号
```cpp
// 错误示例
if (address.isEmpty())
return false;
else
{
qDebug("%s", qPrintable(address));
it;
}
```cpp
// 错误示例
if (address.isEmpty())
return false;
else
{
qDebug("%s", qPrintable(address));
it;
}
// 正确
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 (a)
if (b)
else
}
```
// 正确
if (a)
{
if (b)
else
}
```
- 如果控制语句的body为空则使用大括号
```cpp
// 错误示例
while (a);
```cpp
// 错误示例
while (a);
// 正确
while (a) {}
```
// 正确
while (a) {}
```
### 圆括号
- 使用圆括号将表达式分组
- 使用圆括号将表达式分组,避免需要考虑运算符优先级的情况
```cpp
// 错误示例
if (a && b || c)
```cpp
// 错误示例
if (a && b || c)
// 正确
if ((a && b) || c)
// 错误示例
a | b & c
// 正确
if ((a && b) || c)
// 错误示例
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;
}
```
```cpp
switch (myEnum)
{
case Value1:
doSomething();
break;
case Value2:
doSomethingElse();
// fall through
default:
defaultHandling();
break;
}
```
### 断行
- 保持每行短于100 个字符,需要时进行断行
- 保持每行短于100个字符需要时进行断行
- 逗号放一行的结束,操作符放到一行的开头。如果你的编辑器太窄,一个放在行尾的操作符不容易被看到。
```cpp
// 正确
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression)
{
...
}
```cpp
// 错误示例
if (longExpression +
otherLongExpression +
otherOtherLongExpression)
{
...
}
// Wrong
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(); }
```
### 继承与关键字 `virtual`
### 头文件独立
- 重新实现一个虚函数时,头文件中 不 放置 virtual 关键字
头文件应该保持独立(独立编译)并且以`.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` 的内联细节,我们可以信任这个函数名称并且继续在更好的位置进行阅读。
### 通用例外