Compare commits
39 Commits
0f30fd8655
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a540ba114 | |||
| a462997aba | |||
| 75f601f053 | |||
| d31f971874 | |||
| 11bafbc6eb | |||
| d8febd67cf | |||
| 69627b61ad | |||
| a5f45aff17 | |||
| 995f5d8b99 | |||
| 99682762e5 | |||
| 391b7353f3 | |||
| b8220e9aaf | |||
| 5a4e55d527 | |||
| de9f09e369 | |||
| 01c1882ea0 | |||
| fe961b1421 | |||
| 5ec2e64021 | |||
| b34028f8bf | |||
| 8d82385299 | |||
| 08ad7783fe | |||
| 8e7550bfee | |||
| f7cbd0f1d1 | |||
| c42c006b9a | |||
| e033bc8291 | |||
| b3c844ba2d | |||
| e000cd71d2 | |||
| a7ea4e35bd | |||
| 439a8c9a80 | |||
| dd9dc480fa | |||
| 2b6ad265a6 | |||
| 57b98b7f2d | |||
| fe3be0a3a0 | |||
| a8b48821f9 | |||
| 841f5ac800 | |||
| ab85d05e51 | |||
| 193f52f86d | |||
| 5706f8c05a | |||
| 786d2a91db | |||
| 264828fc3a |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.vscode/settings.json
|
||||||
61
Qt库编译-银河麒麟-RK3588.md
Normal file
61
Qt库编译-银河麒麟-RK3588.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# RK3588实机编译Qt5.15.13
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
- [RK3588实机编译Qt5.15.13](#rk3588实机编译qt51513)
|
||||||
|
- [目录](#目录)
|
||||||
|
- [一、准备工作](#一准备工作)
|
||||||
|
- [二、编译安装](#二编译安装)
|
||||||
|
- [2.1 配置Qt(configure)](#21-配置qtconfigure)
|
||||||
|
- [2.2 编译](#22-编译)
|
||||||
|
- [2.3 安装](#23-安装)
|
||||||
|
- [附录A:常见编译错误](#附录a常见编译错误)
|
||||||
|
- [1. QBasicMutex::lockInternal(int)重定义](#1-qbasicmutexlockinternalint重定义)
|
||||||
|
|
||||||
|
## 一、准备工作
|
||||||
|
|
||||||
|
- 下载Qt源码包(本文以5.15.13为例,如需编译其它版本,下载对应版本的源代码即可,注意版本不同时,命令中的版本号与实际版本同步),下载Qt源码包[Qt5.15.13源码链接](https://download.qt.io/archive/qt/5.15/5.15.13/single/)
|
||||||
|
|
||||||
|
- 通过以下命令行解压源代码至编译目录(需足够的磁盘空间,一般在用户目录新建)
|
||||||
|
> sudo xz -d qt-everywhere-src-5.15.13.tar.xz
|
||||||
|
> sudo tar -xvf qt-everywhere-src-5.15.13.tar
|
||||||
|
- 安装编译环境
|
||||||
|
> sudo apt-get install build-essential perl python git
|
||||||
|
- 安装依赖库
|
||||||
|
> sudo apt-get install libx11-*
|
||||||
|
> sudo apt-get install libx11*
|
||||||
|
>
|
||||||
|
> sudo apt-get install libxcb-*
|
||||||
|
> sudo apt-get install libxcb*
|
||||||
|
>
|
||||||
|
> sudo apt-get install libxkbcommon-dev
|
||||||
|
> sudo apt-get install libxkbcommon-x11-dev
|
||||||
|
> sudo apt-get install libxcb-xinerama0
|
||||||
|
> sudo apt-get install '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev
|
||||||
|
|
||||||
|
## 二、编译安装
|
||||||
|
|
||||||
|
### 2.1 配置Qt(configure)
|
||||||
|
|
||||||
|
切换至解压出来的Qt目录中,运行以下命令(如果权限不足,使用sudo执行),注意其中的-prefix参数为最终Qt库的安装目录(/home/firefly/Qt5.15.13),应根据实际环境进行修改,在命令行中执行修改后的命令:
|
||||||
|
> ./configure -prefix /home/firefly/Qt5.15.13 -confirm-license -opensource -release -make libs -xplatform linux-aarch64-gnu-g++ -pch -qt-libjpeg -qt-libpng -qt-zlib -no-sse2 -no-openssl -no-cups -no-glib -no-dbus -xcb -no-separate-debug-info -opengl es2 -egl -eglfs -qpa xcb -linuxfb -recheck-all -skip webengine -nomake examples -nomake tests -shared -skip purchasing -skip quick3d -skip sensors -skip speech -skip datavis3d -skip andriodextras -skip gamepad
|
||||||
|
|
||||||
|
### 2.2 编译
|
||||||
|
|
||||||
|
配置成功后,运行以下命令编译代码(-j8表示使用8线程并行编译,可根据需要或实际情况修改该数值):
|
||||||
|
> sudo make -j8
|
||||||
|
|
||||||
|
### 2.3 安装
|
||||||
|
|
||||||
|
编译成功后,运行以下命令打包安装Qt库,打包安装后的库可直接压缩后在其它同样或类似环境中解压使用
|
||||||
|
> sudo make install
|
||||||
|
|
||||||
|
## 附录A:常见编译错误
|
||||||
|
|
||||||
|
### 1. QBasicMutex::lockInternal(int)重定义
|
||||||
|
|
||||||
|
- 错误描述:
|
||||||
|
> qmutex_linux.cpp:multiple definition of 'QBasicMutex::lockInternal(int)',
|
||||||
|
- 解决问题:
|
||||||
|
> As in qmutex.cpp it directly" #include qmutex_linux.cpp", as a result the content of qmutex_linux.cpp will be part of qmutex.cpp.And in the thread.pro, the SOURCES +=qmutex.cpp qmutex_linux.cpp, then during link, the two file have same symbols will be detected.When I delete qmutex_linux.cpp from SOURCES, there is no problem any more.
|
||||||
|
[参考文章](https://blog.csdn.net/qq_43417144/article/details/130680388)
|
||||||
267
Qt码规范.md
Normal file
267
Qt码规范.md
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,3 +1,7 @@
|
|||||||
# wiki
|
# wiki
|
||||||
|
|
||||||
企业知识库,通过Markdown,构建内部知识分享平台
|
企业知识库,通过Markdown,构建内部知识分享平台
|
||||||
|
|
||||||
|
- 创建企业开发团队代码规范
|
||||||
|
- 技术分享
|
||||||
|
- ...
|
||||||
BIN
assets/办公室文件共享/image-3.png
Normal file
BIN
assets/办公室文件共享/image-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
assets/办公室文件共享/image-4.png
Normal file
BIN
assets/办公室文件共享/image-4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
14
blog/assets/osgEarth六边形网格/2dmap.earth
Normal file
14
blog/assets/osgEarth六边形网格/2dmap.earth
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<map name = "ddmap" type="projected" version="2">
|
||||||
|
<options>
|
||||||
|
<terrain driver = "rex" lighting = "true" skirt_ratio="0.05" min_tile_range_factor = "6" first_lod = "0" blending = "false" color = "#ffffffff" tile_size = "17" normalize_edges = "true" compress_normal_maps = "false" normal_maps = "true" min_expiry_frames = "0" min_expiry_time = "0"/>
|
||||||
|
<!-- <profile>plate-carre</profile> -->
|
||||||
|
<profile>spherical-mercator</profile>
|
||||||
|
</options>
|
||||||
|
|
||||||
|
<image name="world" driver="tms" opacity = "1.0" format="png">
|
||||||
|
<url>E:/GISData/Imagery/tms.xml</url>
|
||||||
|
</image>
|
||||||
|
|
||||||
|
</map>
|
||||||
BIN
blog/assets/osgEarth六边形网格/image.png
Normal file
BIN
blog/assets/osgEarth六边形网格/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 535 KiB |
174
blog/osgEarth六边形网格.md
Normal file
174
blog/osgEarth六边形网格.md
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# osgEarth构建六边形网格兵棋地图示例
|
||||||
|
|
||||||
|
在osgEarth中添加了osg::Geode,绘制蜂窝状六边形网格效果,包括墨卡托平面地图,地理坐标平面地图,三维地球等多种模式
|
||||||
|

|
||||||
|
```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;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
|
||||||
|
|
||||||
|
auto fromSRS = osgEarth::SpatialReference::get("wgs84");
|
||||||
|
auto srs = extent.getSRS();
|
||||||
|
|
||||||
|
// 计算每个六边形中心点的地理坐标
|
||||||
|
float height = std::sqrt(3.0f) * radius;
|
||||||
|
float lonDelta = radius;// extent.width() / cols;
|
||||||
|
float latDelta = radius;// extent.height() / rows;
|
||||||
|
for (int row = 0; row < rows; ++row) {
|
||||||
|
for (int col = 0; col < cols; ++col) {
|
||||||
|
|
||||||
|
float lonDelta = col * 1.5f * radius;
|
||||||
|
float latDelta = row * height;
|
||||||
|
if (col % 2 == 1)
|
||||||
|
latDelta += height / 2.0f;
|
||||||
|
|
||||||
|
float lon = 108.0 + lonDelta;
|
||||||
|
float lat = 35.0 + latDelta;
|
||||||
|
osg::Vec3d centerGeo(lon, lat, 3000.0);
|
||||||
|
|
||||||
|
// 计算六边形的顶点
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
{
|
||||||
|
float angle = osg::PI * 2.0f * float(i) / 6.0f;
|
||||||
|
float x = radius * std::cos(angle);
|
||||||
|
float y = radius * std::sin(angle);
|
||||||
|
osg::Vec3d vertexXYZ;
|
||||||
|
osgEarth::GeoPoint pppp(fromSRS, osg::Vec3d(lon + x, lat + y, 1.0));
|
||||||
|
pppp.transformInPlace(srs);
|
||||||
|
pppp.toWorld(vertexXYZ);
|
||||||
|
//vertices->push_back(centerXYZ + vertexXYZ - centerGeo);
|
||||||
|
vertices->push_back(vertexXYZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry->setVertexArray(vertices);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_LINES);
|
||||||
|
int numHexagons = rows * cols;
|
||||||
|
|
||||||
|
for (int i = 0; i < numHexagons; ++i) {
|
||||||
|
int offset = i * 6;
|
||||||
|
for (int j = 0; j < 6; ++j) {
|
||||||
|
indices->push_back(offset + j);
|
||||||
|
indices->push_back(offset + (j + 1) % 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry->addPrimitiveSet(indices);
|
||||||
|
|
||||||
|
// 启用 VBO
|
||||||
|
geometry->setUseVertexBufferObjects(true);
|
||||||
|
|
||||||
|
// 设置线宽
|
||||||
|
osg::ref_ptr<osg::StateSet> stateSet = geometry->getOrCreateStateSet();
|
||||||
|
osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(1.0f); // 设置线宽为 1.0
|
||||||
|
stateSet->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 函数:创建六边形蜂窝网格线,平面地图
|
||||||
|
osg::ref_ptr<osg::Geometry> createHexagonWireframe(double startX,double startY, float radius, int rows, int cols) {
|
||||||
|
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
|
||||||
|
|
||||||
|
|
||||||
|
// 计算每个六边形中心点的地理坐标
|
||||||
|
float height = std::sqrt(3.0f) * radius;
|
||||||
|
for (int row = 0; row < rows; ++row)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < cols; ++col)
|
||||||
|
{
|
||||||
|
float xDelta = col * 1.5f * radius;
|
||||||
|
float yDelta = row * height;
|
||||||
|
if (col % 2 == 1)
|
||||||
|
yDelta += height / 2.0f;
|
||||||
|
|
||||||
|
float lon = startX + xDelta;
|
||||||
|
float lat = startY + yDelta;
|
||||||
|
|
||||||
|
// 计算六边形的顶点
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
{
|
||||||
|
float angle = osg::PI * 2.0f * float(i) / 6.0f;
|
||||||
|
float x = radius * std::cos(angle);
|
||||||
|
float y = radius * std::sin(angle);
|
||||||
|
//vertices->push_back(centerXYZ + vertexXYZ - centerGeo);
|
||||||
|
vertices->push_back(osg::Vec3d(lon + x, lat + y, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry->setVertexArray(vertices);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_LINES);
|
||||||
|
int numHexagons = rows * cols;
|
||||||
|
|
||||||
|
for (int i = 0; i < numHexagons; ++i) {
|
||||||
|
int offset = i * 6;
|
||||||
|
for (int j = 0; j < 6; ++j) {
|
||||||
|
indices->push_back(offset + j);
|
||||||
|
indices->push_back(offset + (j + 1) % 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry->addPrimitiveSet(indices);
|
||||||
|
|
||||||
|
// 启用 VBO
|
||||||
|
geometry->setUseVertexBufferObjects(true);
|
||||||
|
|
||||||
|
// 设置线宽
|
||||||
|
osg::ref_ptr<osg::StateSet> stateSet = geometry->getOrCreateStateSet();
|
||||||
|
osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(1.0f); // 设置线宽为 1.0
|
||||||
|
stateSet->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
auto ddMap = osgDB::readNodeFile("E:/GISData/Imagery/2dmap.earth");
|
||||||
|
|
||||||
|
auto mapNode = osgEarth::MapNode::findMapNode(ddMap);
|
||||||
|
|
||||||
|
osgEarth::GeoPoint start(osgEarth::SpatialReference::get("wgs84"), 108.0, 35.0);
|
||||||
|
start.transformInPlace(mapNode->getMapSRS());
|
||||||
|
|
||||||
|
auto hexagon222 = createHexagonWireframe(start.x(), start.y()
|
||||||
|
, 500, 100, 100);
|
||||||
|
|
||||||
|
hexagon222->getOrCreateStateSet()->setAttribute(new osg::Depth(osg::Depth::ALWAYS, 0.0, 1.0, false), osg::StateAttribute::ON);
|
||||||
|
osgEarth::GLUtils::setLighting(hexagon222->getOrCreateStateSet(), osg::StateAttribute::OFF);
|
||||||
|
|
||||||
|
mapNode->addChild(hexagon222);
|
||||||
|
|
||||||
|
auto m = new osgEarth::EarthManipulator;
|
||||||
|
|
||||||
|
osgViewer::Viewer viewer;
|
||||||
|
viewer.setSceneData(ddMap);
|
||||||
|
|
||||||
|
viewer.setCameraManipulator(m);
|
||||||
|
viewer.getCamera()->addCullCallback(new osgEarth::AutoClipPlaneCullCallback(mapNode));
|
||||||
|
|
||||||
|
viewer.realize();
|
||||||
|
|
||||||
|
|
||||||
|
m->setViewpoint(osgEarth::Viewpoint("", 108, 35, 0.0, 0.0, -89.9, 100000));
|
||||||
|
|
||||||
|
return viewer.run();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 附件
|
||||||
|

|
||||||
|
|
||||||
|
## 性能问题
|
||||||
|
* 考虑使用instanced drawing
|
||||||
|
* 使用kd-tree
|
||||||
|
* ...
|
||||||
60
blog/资源分享.md
Normal file
60
blog/资源分享.md
Normal 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
1465
llvm-coding-standards.md
Normal file
File diff suppressed because it is too large
Load Diff
84
vcpkg安装库错误及解决方法.md
Normal file
84
vcpkg安装库错误及解决方法.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# vcpkg安装库失败常见错误及其解决方法
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
- [vcpkg安装库失败常见错误及其解决方法](#vcpkg安装库失败常见错误及其解决方法)
|
||||||
|
- [目录](#目录)
|
||||||
|
- [1. 科学上网](#1-科学上网)
|
||||||
|
- [1.1 控制台设置代理](#11-控制台设置代理)
|
||||||
|
- [1.2 vcpkg包下载失败](#12-vcpkg包下载失败)
|
||||||
|
- [1.3 osg/osgEarth网络代理环境变量](#13-osgosgearth网络代理环境变量)
|
||||||
|
- [2. 遇到某些库编译错误时,在本地修改代码并使用修改后的代码编译](#2-遇到某些库编译错误时在本地修改代码并使用修改后的代码编译)
|
||||||
|
- [3. vcpkg ports内可以修改库的配置,如工程目录,windows SDK版本,以及部分错](#3-vcpkg-ports内可以修改库的配置如工程目录windows-sdk版本以及部分错)
|
||||||
|
- [4. 找不到Windows SDK库的错误修改:https://github.com/microsoft/vcpkg/issues/8288](#4-找不到windows-sdk库的错误修改httpsgithubcommicrosoftvcpkgissues8288)
|
||||||
|
- [5. vc错误: `error MSB4115: 函数“exists”只接受标量值,但其参数“$(ForceImportBeforeCppTargets)”的计算结果为`](#5-vc错误-error-msb4115-函数exists只接受标量值但其参数forceimportbeforecpptargets的计算结果为)
|
||||||
|
- [5. 编译GDAL 出现 +{}; 加号不明确](#5-编译gdal-出现--加号不明确)
|
||||||
|
- [6. Windows 7 Python 3.8](#6-windows-7-python-38)
|
||||||
|
|
||||||
|
|
||||||
|
## 1. 科学上网
|
||||||
|
|
||||||
|
vcpkg中大部份依赖均托管在github仓库中,由于国内特殊的网络环境,导致github大部分时间无法正常访问,尤其是下载较大源码包或文件时会出现网络错误,此时需要通过科学上网措施(目前统一使用Mielink)建立代理上网,建立代理后,一般会有一个本地代理端口,如果配置的不是全局代理,那么有时需要在控制台设定上网代理,下面介绍代理配置方法及一些网络相关的其它错误...
|
||||||
|
|
||||||
|
### 1.1 控制台设置代理
|
||||||
|
|
||||||
|
控制台设置网络代理,需要将2801修改成本机的科学上网代理端口
|
||||||
|
|
||||||
|
* set HTTP_PROXY=http://127.0.0.1:2801
|
||||||
|
* set HTTPS_PROXY=http://127.0.0.1:2801
|
||||||
|
|
||||||
|
### 1.2 vcpkg包下载失败
|
||||||
|
|
||||||
|
* 有时使用vcpkg安装指定库时,由于网络抽风,有时会出现无法成功下载某些源码包的情况,此时可先使用vcpkg安装一次,确认源码包的下载目录及保存的文件名(此二者有时不一致),然后使用以下方法下载压缩包,重命名后放在vcpkg的download目录中即可
|
||||||
|
* 关于库下载错误,可以通过迅雷或其它工具下载,然后添加到download目录,注意从命令行确认保存的文件名
|
||||||
|
|
||||||
|
### 1.3 osg/osgEarth网络代理环境变量
|
||||||
|
|
||||||
|
* set OSGEARTH_HTTP_DEBUG=1
|
||||||
|
* set OSG_CURL_PROXY="127.0.0.1"
|
||||||
|
* set OSG_CURL_PROXYPORT=2801
|
||||||
|
|
||||||
|
## 2. 遇到某些库编译错误时,在本地修改代码并使用修改后的代码编译
|
||||||
|
|
||||||
|
正常使用vcpkg install xxxx安装库时,出现编译错误,通过编译日志可以定位到具体的编译错误,但是默认情况下每次install时,都会从压缩包中重新解压一份新的代码到本地临时目录,因此无法对源代码进行修改并使用修复后的代码进行编译安装,此时,只需要在install时,额外指定--editable选项,即可使本次编译不生成临时源码目录(在一个固定的源码目录中),然后可手工编辑修改此目录中源代码,然后再使用install(此时仍需指定--editable选项)安装,这时,就会使用修改后的代码进行编译了...
|
||||||
|
通过prompt启动,然后进入vcpkg目录
|
||||||
|
VCPKG install 库名 --editable,可以在编译前修改库源码
|
||||||
|
|
||||||
|
## 3. vcpkg ports内可以修改库的配置,如工程目录,windows SDK版本,以及部分错
|
||||||
|
|
||||||
|
## 4. 找不到Windows SDK库的错误修改:https://github.com/microsoft/vcpkg/issues/8288
|
||||||
|
|
||||||
|
## 5. vc错误: `error MSB4115: 函数“exists”只接受标量值,但其参数“$(ForceImportBeforeCppTargets)”的计算结果为`
|
||||||
|
|
||||||
|
* 报错信息:
|
||||||
|
> 1>节点 1 上的项目“xxxxxx\buildtrees\libpq\x64-windows-dbg\libpq.vcxproj”(默认目标)。
|
||||||
|
> 1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.Cpp.Current.targets(14,11): error MSB4115: 函数“exists”只接受标量值,但其参数“$(ForceImportBeforeCppTargets)”的计算结果为“C:/Users/gz/Desktop/vcpkg-2023.08.09/scripts/buildsystems/msbuild/vcpkg.targets;C:/Users/gz/Desktop/vcpkg-2023.08.09/buildtrees/libpq/x64-windows-dbg/vcpkg-libs.props”,不是标量值。 [xxxxxx\buildtrees\libpq\x64-windows-dbg\libpq.vcxproj]
|
||||||
|
> 1>已完成生成项目“xxxxxx\buildtrees\libpq\x64-windows-dbg\libpq.vcxproj”(默认目标)的操作 - 失败。
|
||||||
|
* 参考链接:https://github.com/conan-io/conan/issues/7806
|
||||||
|
* 解决方案:
|
||||||
|
> 修改对应库ports目录内的msvc.cmake文件
|
||||||
|
> /p:ForceImportBeforeCppTargets=\"${SCRIPTS}/buildsystems/msbuild/vcpkg.targets\"
|
||||||
|
>
|
||||||
|
> /p:ForceImportBeforeCppTargets=\"${build_path}/vcpkg-libs.props\"
|
||||||
|
|
||||||
|
## 5. 编译GDAL 出现 +[](){}; 加号不明确
|
||||||
|
|
||||||
|
* 错误信息:
|
||||||
|
> xxxxxx\buildtrees\gdal\src\v3.7.1-30395dad39\ogr\ogrsf_frmts\openfilegdb\filegdbindex_write.cpp(1298): error C2593: “operator +”不明确
|
||||||
|
> xxxxxx\buildtrees\gdal\src\v3.7.1-30395dad39\ogr\ogrsf_frmts\openfilegdb\filegdbindex_write.cpp(1298): note: 可能是“内置 C++ operator+(void (__cdecl *)(std::vector<GByte,std::allocator<_Ty>> &,const __int64 &,int))”
|
||||||
|
> with
|
||||||
|
> [
|
||||||
|
> _Ty=GByte
|
||||||
|
> ]
|
||||||
|
> xxxxxx\buildtrees\gdal\src\v3.7.1-30395dad39\ogr\ogrsf_frmts\openfilegdb\filegdbindex_write.cpp(1298): note: 或 “内置 C++ operator+(void (__vectorcall *)(std::vector<GByte,std::allocator<_Ty>> &,const __int64 &,int))”
|
||||||
|
> with
|
||||||
|
> [
|
||||||
|
> _Ty=GByte
|
||||||
|
> ]
|
||||||
|
> xxxxxx\buildtrees\gdal\src\v3.7.1-30395dad39\ogr\ogrsf_frmts\openfilegdb\filegdbindex_write.cpp(1298): note: 尝试匹配参数列表“(OpenFileGDB::FileGDBTable::CreateSpatialIndex::<lambda_0019e43095a67889bd1988df31ec838d>)”时
|
||||||
|
* 原因分析:由于C++版本原因,低版本并不支持该转换,因此直接去掉转换即可
|
||||||
|
* 解决方法:尝试去掉lambda前面的加号(该加号用于将lambda表达式转为传统函数指针,兼容老代码)
|
||||||
|
|
||||||
|
## 6. Windows 7 Python 3.8
|
||||||
|
|
||||||
|
如果用到的库依赖了Python3.8以上的版本,是无法在windows7正常运行的,解决办法是用3.8狸猫换太子,无痛替换掉vcpkg自动下载的python
|
||||||
|
貌似打补丁也可以实现让windows7支持3.9.2运行
|
||||||
44
办公室文件共享.md
44
办公室文件共享.md
@ -1,5 +1,15 @@
|
|||||||
# 办公室文件共享
|
# 办公室文件共享
|
||||||
|
|
||||||
|
- [办公室文件共享](#办公室文件共享)
|
||||||
|
- [简介](#简介)
|
||||||
|
- [1. 文件存储服务介绍](#1-文件存储服务介绍)
|
||||||
|
- [2. 使用流程](#2-使用流程)
|
||||||
|
- [2.1 内网使用(优先使用)](#21-内网使用优先使用)
|
||||||
|
- [2.1.1 账户初始化](#211-账户初始化)
|
||||||
|
- [2.1.2 连接共享目录](#212-连接共享目录)
|
||||||
|
- [2.2 外网环境使用](#22-外网环境使用)
|
||||||
|
|
||||||
|
## 简介
|
||||||
办公室近日引入了共享存储服务,这项举措旨在提升团队间的协作效率和数据管理的便捷性及安全性。通过共享存储服务,团队成员可以将文件、文档、图片等资料集中存储在一个统一的平台上,而无需依赖传统的文件共享方式,如电子邮件附件或移动存储设备。这意味着团队成员可以随时随地访问所需的文件,无论是在办公室、外出办事的途中,甚至在家里。此外,共享存储服务还提供了权限管理功能,团队管理员可以灵活地控制不同成员对文件的访问权限,确保机密性和安全性。总的来说,共享存储服务的引入将大大简化团队间的文件共享和管理流程,为团队协作提供了更高效、更便捷的工具和平台。
|
办公室近日引入了共享存储服务,这项举措旨在提升团队间的协作效率和数据管理的便捷性及安全性。通过共享存储服务,团队成员可以将文件、文档、图片等资料集中存储在一个统一的平台上,而无需依赖传统的文件共享方式,如电子邮件附件或移动存储设备。这意味着团队成员可以随时随地访问所需的文件,无论是在办公室、外出办事的途中,甚至在家里。此外,共享存储服务还提供了权限管理功能,团队管理员可以灵活地控制不同成员对文件的访问权限,确保机密性和安全性。总的来说,共享存储服务的引入将大大简化团队间的文件共享和管理流程,为团队协作提供了更高效、更便捷的工具和平台。
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -13,18 +23,42 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 2. 使用流程
|
## 2. 使用流程
|
||||||
### 2.1 账户初始化
|
|
||||||
|
### 2.1 内网使用(优先使用)
|
||||||
|
|
||||||
|
在办公室办公时,优先使用内网方式,此时文件访问和传输不需要经过公网服务器,带宽不受限制,速度可达50-100MB/s,具体数据取决于传输的文件。
|
||||||
|
|
||||||
|
#### 2.1.1 账户初始化
|
||||||
|
|
||||||
系统默认为团队成员创建了以自己姓名命名的账户,首次使用需要通过该链接:[密码重置链接](http://192.168.1.11:9888/) 设置用户密码使用(每个用户可设置一次密码),如忘记密码,可联系管理员重置。
|
系统默认为团队成员创建了以自己姓名命名的账户,首次使用需要通过该链接:[密码重置链接](http://192.168.1.11:9888/) 设置用户密码使用(每个用户可设置一次密码),如忘记密码,可联系管理员重置。
|
||||||

|
|
||||||
### 2.2 连接共享目录
|
[](http://192.168.1.11:9888/)
|
||||||
|
|
||||||
|
#### 2.1.2 连接共享目录
|
||||||
|
|
||||||

|

|
||||||
1. 打开计算机文件管理器
|
1. 打开计算机文件管理器
|
||||||
2. 在【此电脑】右键
|
2. 在【此电脑】右键
|
||||||
3. 在弹出的菜单中,点击【映射网络驱动器】
|
3. 在弹出的菜单中,点击【映射网络驱动器】
|
||||||
4. 在弹出的界面中,【文件夹】输入框输入 **\\192.168.1.11\Shared**
|
4. 在弹出的界面中,【文件夹】输入框输入 **\\\\192.168.1.11\\Shared**
|
||||||
5. 勾选输入框下面的【登陆时重新连接】和【使用其他凭据连接】
|
5. 勾选输入框下面的【登陆时重新连接】和【使用其他凭据连接】
|
||||||
6. 点击【完成】按钮
|
6. 点击【完成】按钮
|
||||||
7. 在弹出的界面中输入用户名和密码,并勾选【记住我的凭据】
|
7. 在弹出的界面中输入用户名和密码,并勾选【记住我的凭据】
|
||||||
8. 点击【确定】按钮
|
8. 点击【确定】按钮
|
||||||
9. 如无误,则在计算机界面中会成功添加一个新的网络磁盘。
|
9. 如无误,则在计算机界面中会成功添加一个新的网络磁盘。
|
||||||

|
|
||||||
|

|
||||||
|
|
||||||
|
### 2.2 外网环境使用
|
||||||
|
|
||||||
|
当不在办公室时,临时需要访问共享目录的资料,此时无法通过内网地址直接访问服务器,需要通过公网文件管理页面访问共享目录,可在网页中完成文件的查看、预览、编辑、删除、上传、下载等操作,需要注意的是,此种访问方式受公司租用的公网服务器带宽限制(目前为3Mbps),因此如果通过该种方式上传和下载文件时,速度会比较慢,因此应尽量避免使用该种方式操作大文件。
|
||||||
|
|
||||||
|
访问地址为:[http://pan.vuegic.com/](http://pan.vuegic.com/)
|
||||||
|
|
||||||
|
[](http://pan.vuegic.com/)
|
||||||
|
|
||||||
|
注:用户名和密码与内网访问时一致,如无法访问,请联系管理员开通账号。
|
||||||
|
|
||||||
|
登录成功后,可查看共享目录的文件,并对其进行操作:
|
||||||
|
|
||||||
|

|
||||||
669
编码规范.md
Normal file
669
编码规范.md
Normal 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)
|
||||||
Reference in New Issue
Block a user