【C/C++】Code Style

命名规范

代码元素命名风格注释
Namespaceunder_scored为了跟类名做区分
Class nameCamelCase为了跟标准库的类名做区分 (建议不要使用大写"C" 或者 “T” 作为前缀)
Function namecamelCase小写开头的函数名基本是通用的,除了.Net自成一格
Parameters/Localsunder_scored这个在c++世界里面是占大多数的
Member variablesunder_scored_with_"_"作为前缀在c++标准里面是不建议的,所以使用后缀
Enums and its membersCamelCase除了很老的标准外,大多数都支持这种风格
Globalsg_under_scored你一开始就不该写全局变量!
File names与类名相符优劣参半

代码规范

条款1——重要的Specifiers

  1. override:重写基类的方法时,必须在函数后面加上override。
  2. const:类的方法没有修改类成员时,必须在函数后面加上const。比较典型的是所有的Getter函数。
  3. noexcept:当确定类的方法不会抛出异常时,尽可能加上noexcept。

条款2——try catch

当你不知道具体要catch什么异常时,就不要try了。

条款3——常量

定义常量,尤其是数字常量,禁止使用#define,一律用constexpr或者static constexpr。

constexpr float STEP = 0.02f;
constexpr size_t MAX_SIZE = 100000;

条款4——尽可能使用显式类型转换

int a = -10;
size_t b = 5;
auto c = a / b;
std::cout << "c = " << c << std::endl;
// c = 3689348814741910321auto d = a / static_cast<int>(b);
std::cout << "d = " << d << std::endl;
// d = -2

条款5——auto关键字

能用auto的地方,多用auto,特别是很长的、一层套一层的类型,比如迭代器。

std::vector<OpenMesh::Point<float, 4>>::Iterator iter = vec.begin(); // no good
// prefer
auto iter = vec.begin();

条款6——Type Alias

使用using关键词来创建别名,停止使用typedef,just for consistency.

using AliasType = Type;
// typedef Type AliasType;

条款7——stl算法

尽可能使用stl里面的算法,不要自己手写相同功能的算法,不仅很难写的比stl好,还容易有bug,不好排查。

// 如求和
int sum = 0;
for(auto i : vec){sum += i;
}
// maybe prefer std::accumulate or std::reduce
std::accumulate(vec.begin(), vec.end(), 0, [](auto value, auto item){return value + item;});
// or
std::reduce(vec.begin(), vec.end(), 0, [](auto value, auto item){return value + item;});
// or even parallelism
std::reduce(std::execution::par, vec.begin(), vec.end(), 0, [](auto value, auto item){return value + item;});

条款8——stl容器

  1. 使用合适的容器做合适的事情。
  2. 如果你不知道用哪个容器合适,就用vector。
  3. 如果知道vector将会用到的大小,先reserve。
  4. 尽量使用std::array,而不是原生数组,std::array可以直接使用stl算法,并且没有额外开销。
  5. 如果键值顺序无所谓,优先使用unordered_map。

条款9——传值

// 需要修改
auto func(T& v) -> void;// 只读,sizeof(T) >= 16,经验值
// 当T是float时,如果传const T&就很搞笑了,本身就只占4byte,现在变成了8byte(64位计算机)
auto func(const T& v) -> void; // 只读,sizeof(T) < 16
auto func(T v) -> void;// 虽然没错,但没必要
auto func(const T v) -> void;

条款10——返回值

// 返回局部变量,禁止返回引用。
auto func() -> T
{T val;// ...// RVOreturn val;
}// 返回成员变量
struct A
{T val_;// 如果sizeof(T) < 16,经验值auto getValue() const noexcept -> T{return val_;}// 根据接收类型,会选择拷贝或者引用。auto getValue2() const noexcept -> const T&{return val_;}
};

条款11——默认参数

虽然个人不建议使用默认参数,但是不可否认默认参数的确一定程度上提供了不少方便。但是有一种情况下,坚决抵制使用默认参数,那就是基类虚函数。

// 动态绑定会出现问题,不明白原理,会造成难以排查的错误。
class Base
{
public:virtual auto setValue(int v = 10) noexcept -> void{// ...}
}
class Derived: public Base
{
public:auto setValue(int v = 20) noexcept -> void override{// ...}
}

此外,可变引用不要带默认参数,虽然msvc能编过,但是标准不支持,是未定义行为。

auto func(double& in_out = 5.f); // 达咩!!!

条款12——内存

  1. 优先栈分配,除非你能找到充分的理由向操作系统申请堆内存。
  2. 当申请堆内存时,尽量避免裸指针,除非你很强。
  3. 使用智能指针,unique_ptr优于shared_ptr,除非你找到充分的理由使用shared_ptr。

条款13——Trailing Comma

在数组或者枚举中,建议加上Trailing Comma。

std::vector<int> v{0,1,2,3,};// 特别是枚举,强烈建议加上Trailing Comma,会方便很多。
enum class Color
{Red,    ///< commentGreen,  ///< commentBlue,   ///< commentAlpha,  ///< comment
}

条款14——使用nullptr而不是NULL

在c++代码中,空指针应该尽可能使用nullptr,而非NULL。

#define NULL 1 // 使用NULL你就G了。// 如果有两个重载函数,一个接收integer,一个接收指针。
auto fun(int) -> { std::print("{}", "This is an integer");}
auto fun(int*) -> { std::print("{}", "This is a pointer to an integer");}auto p_int = NULL;
fun(p_int); // the first overload will be called.auto p_int = nullptr;
fun(p_int); // the second overload will be called, which is generally expected.

条款15——不要把子线程detach掉,无论出于何种原因。

你找不到任何合情合理,并且负责任的理由,把子线程detach掉。将线程detach掉只会增加后续维护的困难度。C++ Core Guidelines也不建议使用detach。

CP.26: Don’t detach() a thread
This rule sounds strange. The C++11 standard supports detaching a thread, but we should not do it! The reason is that detaching a thread can be quite challenging. As rule, C.25 said: CP.24: Think of an thread as a global container. Of course, this means you are magnificent if you use only variables with global scope in the detached threads. NO! Even objects with static duration can be critical. For example, look at this small program with undefined behavior.

#include <iostream>  
#include <string>  
#include <thread>  void func(){std::string s{"C++11"};std::thread t([&s]{ std::cout << s << std::endl;});t.detach();
}  int main(){  func();  
}

This is easy. The lambda function takes s by reference. This is undefined behavior because the child thread t uses the variable s, which goes out of scope. STOP! This is the apparent problem but the hidden issue is std::cout. std::cout has a static duration. This means the lifetime of std::cout ends with the end of the program, and we have, additionally, a race condition: thread t may use std::cout at this time.

条款16——使用RAII的lock,不要直接lock和unlock

一般来说,要找到一个合适的unlock时机是不容易的,如果直接调用lock和unlock,很可能会导致程序死锁。

// 不推荐的方式
auto fun() -> int
{mutex.lock();// your code// 此处可能会提前return,可能会抛出异常,这种情况下,mutex没有unlock,程序就死锁了。mutex.unlock();return 0;
}// 推荐的方式
auto fun() -> int
{std::lock_guard<Mutex> guard(mutex);// your code// 此处无论是否提前return,或者抛出异常,都可以保证unlock会被调用。return 0;
}// 需要手动解锁的时候
auto fun() -> int
{std::unique_lock<Mutex> u_lock(mutex);// your codeu_lock.unlock();// your codeu_lock.lock();// your codereturn 0;
}// c++17 or later
auto fun() -> int
{// scoped_lock相较于lock_guard,支持多把锁。std::scoped_lock s_lock(mutex1, mutex2, mutex3);// your codereturn 0;
}

条款17——重视编译器警告

要特别重视编译器警告,不能当做没有看到。有些警告特别致命,相当于编译器告诉你,这里十有八九出错了,但是根据标准我不能管你,只能提示你,你自己看着办。这种类型的警告建议直接当做错误处理,我在这里列出了一些:

/we4172 # 返回局部变量或临时变量的地址
/we4715 # 不是所有的控件路径都返回值
/we4265 # 类包含虚函数,但其不常用的析构函数不是虚函数;该类的实例可能无法进行正确析构
/we4390 # 找到空的受控语句;这是否是有意的?
/we4146 # 一元负运算符应用于无符号类型,结果仍为无符号类型
/we4308 # 负整型常量转换为无符号类型
/we4700 # 使用了未初始化的局部变量
/we4703 # 使用了可能未初始化的本地指针变量
/we4365 # “参数”: 从“int”转换到“size_t”,有符号/无符号不匹配
/we4245 # 从常量“int”转换到“size_t”,有符号/无符号不匹配

举例:

auto func() -> int&
{//we4172 # 返回局部变量或临时变量的地址int a = 0;return a;
}auto func(bool b) -> int
{//we4715 # 不是所有的控件路径都返回值if(b){return 1;}
}class VClass
{
public:virtual auto help() -> void = 0;~VClass(){}//we4265 # 类包含虚函数,但其不常用的析构函数不是虚函数;该类的实例可能无法进行正确析构
};auto func(bool b)
{if(b);...//we4390 # 找到空的受控语句;这是否是有意的?
}auto func()
{size_t a = 10;int b = -9;//we4146 # 一元负运算符应用于无符号类型,结果仍为无符号类型if(b > -a) {}
}auto func()
{unsigned int a = -10;//we4308 # 负整型常量转换为无符号类型
}auto func()
{int a;++a;//we4700 # 使用了未初始化的局部变量
}auto func(size_t size)
{int* p;if(size > 255){p = new int(10);}if(p){delete p;}//we4703 # 使用了可能未初始化的本地指针变量// 解决:int* p{nullptr};
}auto func()
{int a = -10;unsigned int b = a;//we4365 # “参数”: 从“int”转换到“size_t”,有符号/无符号不匹配
}auto func()
{const int a = -10;unsigned int b = a;//we4245 # 从常量“int”转换到“size_t”,有符号/无符号不匹配
}

条款18——尽量使用标准c++,避免使用编译器的扩展功能,除非几个主流的编译器都支持的扩展功能。

使用MSVC的时候,可以加上/permissive-编译选项,强制让编译器遵守c++标准。

条款19——重写虚函数必须使用override关键字

class Base
{
public:virtual auto listen(int k) -> void = 0;virtual ~Base(){}
};class Derived: public Base
{
public:auto listen(int k) -> void override{// do something}
};

如果不写override,那么当Base类把listen的接口改变的时候,编译器不会报错,甚至多数编译器连警告都不会有,这个时候程序运行的结果就会出错。一旦写override,那么当Base类把listen的接口改变的时候,编译就会失败,就很容易修改了。

代码优化建议

条款1——Ahmdal定律

S p e e d U p = 1 ( 1 − f u n c c o s t ) + f u n c c o s t / f u n c s p e e d u p SpeedUp=\frac{1}{(1-func_{cost})+func_{cost}/func_{speedup}} SpeedUp=(1funccost)+funccost/funcspeedup1
也就是说,如果某个函数占50%的运行时间,你把他速度提升到了原来的两倍,那么根据这个公式, S p e e d U p = 1 ( 1 − 0.5 ) + 0.5 / 2 = 1 0.75 = 4 3 = 1.33 3 ‾ SpeedUp = \frac{1}{(1-0.5)+0.5/2}=\frac{1}{0.75}=\frac{4}{3}=1.33\overline{3} SpeedUp=(10.5)+0.5/21=0.751=34=1.333也就是整体提速了1.333倍。

条款2——先保证正确,再进行优化

Premature optimization is the root of all evil. —— Donald Knuth
过早优化是万恶之源

条款3——使用likely和unlikely

如果无法避免地要使用条件分支,那么做好用上likely和unlikely。

if( a > 3 ) [[likely]] {std::cout<<"a is greater then 3"<<std::endl;
}else[[unlikely]]{std::cout<<"a is smaller then 3"<<std::endl;
}

上面的例子中,如果a>3的概率远大于a<=3的概率,那么加上likely和unlikely会帮助编译器更好地优化。

条款4——把多次访问的本地变量放到register中

auto func()
{register int a = 1;while(condition){++a;}
}

当然,编译器可能已经帮你优化了,don’t bother.

消除重复

PS:机能一次,使用多次

// .h
virtual bool hasFormat(const QString &mimetype) const;//.cpp
bool QMimeData::hasFormat(const QString &mimeType) const
{return formats().contains(mimeType);
}bool QMimeData::hasUrls() const
{return hasFormat(textUriListLiteral());
}bool QMimeData::hasText() const
{return hasFormat(textPlainLiteral()) || hasUrls();
}bool QMimeData::hasHtml() const
{return hasFormat(textHtmlLiteral());
}bool QMimeData::hasImage() const
{return hasFormat(applicationXQtImageLiteral());
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/35045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】操作符(上)

目录 1. 操作符的分类 2. 原码、反码、补码 3. 移位操作符 3.1 左移操作符 3.2 右移操作符 4. 位操作符&#xff1a;&、|、^、~ 5. 单目操作符 6. 逗号表达式 最近准备期末考试&#xff0c;好久不见啦&#xff0c;现在回归—— 正文开始—— 1. …

fc-list命令使用指南

fc-list命令使用指南 一、什么是fc-list? fc-list是FontConfig库的一部分&#xff0c;最初为Linux和其他Unix-like系统开发。我们可以用这个命令行快速查询和列出系统中安装的字体。 现在&#xff0c;Windows用户也集成了这个工具&#xff0c;所以我们来讲解一下用法。 二、…

路经src里的文件是?

说明&#xff1a; src 目录指的是源代码&#xff08;source code&#xff09;目录&#xff0c;存放项目应用的源代码&#xff0c;包含项目的逻辑和功能实现&#xff0c;实际上线之后在浏览器中跑的代码就是它们 apis - 业务接口assets - 静态资源 &#xff08;图片&#xff09…

《昇思 25 天学习打卡营第 7 天 | 模型训练 》

《昇思 25 天学习打卡营第 7 天 | 模型训练 》 活动地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 签名&#xff1a;Sam9029 模型训练 本章节-结合前几张的内容所讲-算是一节综合实践 mindscope 框架使用张量 数据类型数据集下载与加载网络构建函…

mulesoft --环境安装与搭建

1.mavenjdkpostman 2.anypoint statdio 下载安装 下载 Anypoint Studio & Mule |骡子软件 (mulesoft.com) 填好基本信息后&#xff0c;会发邮件&#xff0c;在邮件中下载&#xff0c;跳到官网下载 3注册账号 Download Anypoint Studio & Mule | MuleSoft 4.Connect…

PostgreSQL 高可用性与容错性(十三)

1. 备份与恢复策略 1.1 数据备份 1.1.1 基于 pg_dump 的逻辑备份 pg_dump -U username -d dbname -f backup_file.sql 1.1.2 基于 pg_basebackup 的物理备份 pg_basebackup -U username -D /path/to/backup/directory -Ft -Xs -P -R 1.2 恢复数据库 1.2.1 恢复逻辑备份 …

详解 ClickHouse 的分片集群

一、简介 分片功能依赖于 Distributed 表引擎&#xff0c;Distributed 表引擎本身不存储数据&#xff0c;有点类似于 MyCat 之于 MySql&#xff0c;成为一种中间件&#xff0c;通过分布式逻辑表来写入、分发、路由来操作多台节点不同分片的分布式数据 ClickHouse 进行分片集群的…

C语言基础——函数(2)

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 文章目录 前言 一、return语句 二、数组做函数参数 三、嵌套调用和链式访问 3.1 嵌套调用 3.2 链式访问 四、函数声明和定义 4.1 单个文件 4.2 多个文件 总结 前言 大家好啊&#xff0c;继我们上一…

优化系统小工具

一款利用VB6编写的系统优化小工具&#xff0c;系统优化、桌面优化、清理垃圾、查找文件等功能。 下载:https://download.csdn.net/download/ty5858/89432367

构造,析构,垃圾回收

构造函数 基本概念 在实例化对象时 会调用的用于初始化的函数 如果不写&#xff0c;默认存在一个无参构造函数 构造函数的写法 1.没有返回值 2.函数名和类名必须相同 3.没有特殊需求时&#xff0c;一般都是public的 4.构造函数可以被重载 5.this代表当前调用该函数的对…

前端JS计算精度损失的问题

例子: 0.10.2 //0.30000000000000004 1.2-1 //0.19999999999999996 1.15*100 //114.99999999999999 1.2/0.2 //5.999999999999999 方式1 // 两个浮点数求和 function num_add(num1,num2){var r1,r2,m;try{r1 num1.toString().split(.)[1].length;}catch(e){r1 0;}try{r2num…

【UE5.3】笔记1

内容浏览器&#xff1a;存放项目中所有的资源&#xff1a;关卡、蓝图类...... 关卡--Map 至少有一个关卡&#xff0c;可以有多个关卡 -漫游 视野漫游&#xff1a;鼠标右键WASD QE 鼠标滑轮控制摄像机速度 运行&#xff0c;ESC退出运行,快捷键F8不停止运行单独弹出功能 -创…

计算机视觉全系列实战教程 (十三):图像形态学操作

1.基本概述 (1)What 图像的形态学操作的本质&#xff1a;集合间的运算 几何学 (2)Why(有什么用途&#xff09; 消除噪声、边缘提取、区域填充、细化和粗化、分割独立的图像元素、求图像梯度、求极大值区域或极小值区域等。 (3)Which(有哪些常见的形态学操作) A.膨胀 使得…

Express的模块化路由

Express的模块化路由是一种组织和管理路由的有效方式&#xff0c;它使得代码更加清晰、易于维护和扩展。 1. 模块化路由的概念 模块化&#xff1a;在编程中&#xff0c;模块化是将程序拆分成多个独立但相互依赖的模块&#xff0c;每个模块都包含特定的功能和数据。Express模块…

使用了CDN,局部访问慢,如何排查

如果是局部访问慢&#xff0c;则可从如下角度查看 是否DNS设置错误导致&#xff1f; 个别用户可能存在local DNS设置错误&#xff0c;导致出现跨地域或跨运营商访问。因为CDN的权威DNS是基于用户请求的localDNS来判断所属的地区和运营商&#xff0c;从而将请求引导至对应最近…

《编译原理》阅读笔记:p19-p24

《编译原理》学习第 4 天&#xff0c;p19-p24总结&#xff0c;总计 5 页。 一、技术总结 1.grouping of phases 这里谈到分组(group)&#xff0c;那么就会有一个疑问&#xff0c;分组的依据是什么&#xff1f;即根据什么来分组。 (1) front end & back end 编译器包含…

找不到d3dcompiler_47.dll如何修复,这几种修复方法可搞定

最近&#xff0c;我在尝试运行一款游戏时遇到了一个问题&#xff0c;系统提示我丢失了d3dcompiler_47.dll文件。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行游戏。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#xff0c;并成功解决了这…

【内网穿透】FRP 跨平台内网穿透 支持windows linux x86_64 arm64 端口范围映射

AI提供的资料&#xff1a; FRP&#xff08;Fast Reverse Proxy&#xff09;是一个专为内网穿透设计的高性能反向代理程序。以下是一些关于FRP的详细资料&#xff0c;帮助您更好地理解和使用这一工具&#xff1a; 核心特点&#xff1a; 内网穿透&#xff1a;能够将位于内网的…

都2024年了,现在互联网行情怎样?

都2024年了&#xff0c;互联网行情是怎样的&#xff1f; 很直白的说&#xff0c;依旧是差得很&#xff0c;怎么说&#xff1f; 我刚在掘金上看到一个掘友写的文章&#xff0c;他是四月领了大礼包&#xff0c;据他的描述如下&#xff1a; 互联网行情依旧是差得很&#xff0c;很…