C++编码规范解读

规范

C++文件名和类名保持一致

好处:代码整体结构清晰、明了。java里强制如此。

类型命名采用大驼峰

比如:UrlEncoder

FileParser

优先使用 头文件中的基本类型

如:

有符号类型 无符号类型 描述
int8_t uint8_t 宽度恰为8的有/无符号整数类型
int16_t uint16_t 宽度恰为16的有/无符号整数类型
int32_t uint32_t 宽度恰为32的有/无符号整数类型
int64_t uint64_t 宽度恰为64的有/无符号整数类型
intptr_t uintptr_t 足以保存指针的有/无符号整数类型

尽量不要使用int、long等,因为其宽度取决于操作系统和编译器。比如long可能是32bit或64bit。

少用全局变量

使用单例,封装性更好。

条件/循环语句必须要使用大括号

在已有条件/循环语句代码上增加新代码时不容易出错

switch语句要有default分支,除非switch的条件变量是枚举类型

switch语句中要有default分支,保证在遗漏case标签处理时能够有一个缺省的处理行为。

如果switch条件变量是枚举类型,加上default分支处理有些多余,现代编译器都具备检查是否在switch语句中遗漏了某些枚举值的case分支的能力,会有相应的warning提示。 另外,加上default分支处理,一些静态代码分析工具会报告default分支是死代码(deadcode)的warning。java也有这种情况。

不允许使用魔鬼数字

缺点:没有任何业务含义;修改麻烦

//xx

const MAX_VALUE = 120; //xx

if (a > MAX_VALUE )

if(b < MAX_VALUE )

不用的代码段直接删除,不要注释掉

现在有git、svn这样的版本管理工具,删掉也不怕丢失。

所有头文件都应当使用#define 防止头文件被多重包含

#ifndef VOS_INCLUDE_TIMER_TIMER_H
#define VOS_INCLUDE_TIMER_TIMER_H
...
#endif

.h文件用于声明需对外公开的类与接口,若没有对外接口,.h文件可不要

头文件包含顺序按照稳定度排序

建议按照稳定度排序:

  • cpp对应的头文件
  • C/C++标准库
  • 系统库的.h
  • 其他库的.h
  • 本项目内的.h

举例来说,Foo.cpp中包含头文件的次序如下::

#include "Foo/Foo.h"
#include <cstdlib>
#include <string>
#include <linux/list.h>
#include <linux/time.h>
#include "platform/Base.h"
#include "platform/Framework.h"
#include "project/public/Log.h"

不要在头文件中或者#include之前使用using指示符

原因:容易造成符号冲突。

在CPP文件的实现代码处可以视情况使用。

类的成员变量必须显式初始化

这跟java是不同的,java里类的成员变量会由编译器自动初始化。

若不使用拷贝构造函数或赋值操作符,使用private修饰,且不实现

class Foo {
private:Foo(const Foo&);Foo& operator=(const Foo&);
};

C++11下也可用delete关键字(注意这里的修饰符是public):

class Foo {
public:Foo(const Foo&) = delete;Foo& operator=(const Foo&) = delete;
};

禁止在构造函数和析构函数中调用虚函数

结果不可知。

比如在基类的构造函数里调用虚函数,有可能触发派生类的构造,要知道这时候基类还没构造完呢。父之不存,子将焉附!

同样的道理,基类的析构函数调用虚函数,这个行为可能下放到派生类,但此时派生类已销毁了!(派生类销毁在基类之前,构造则在基类之后)。

基类的析构函数应该声明为public和virtual的

原因:只有基类析构函数是虚拟的,才能保证派生类的析构函数被调用。

输入参数使用引用取代指针,尽可能传递const应用

好处:引用肯定非空,避免繁琐的NULL检查。这是C++强于java的地方,java里即使引用也可能为null。

把:
f(A* a){

}

改成
f(const A& a) {

}

少用多重继承

因为多重继承使用过程中有下面的典型问题:

  1. 菱形继承所带来的数据重复,以及名字二义性。因此,C++引入了virtual继承来解决这类问题;
  2. 即便不是菱形继承,多个父类之间的名字也可能存在冲突,从而导致的二义性;
  3. 如果子类需要扩展或改写多个父类的方法时,造成子类的职责不明,语义混乱;
  4. 相对于委托,继承是一种白盒复用,即子类可以访问父类的protected成员, 这会导致更强的耦合。而多重继承,由于耦合了多个父类,相对于单根继承,这会产生更强的耦合关系。

多使用const来保证数据的不变性

数据不可变多安心啊,且并发编程下可以少费脑子去考虑锁的问题。

类型转换

dynamic_cast:downcast,类似于java的instanceof,尽量少用,往往是类的继承关系设计的不合理。

reinterpret_cast:这个就是强转,很危险。

const_cast:去掉数据的不可变性,不利于代码的稳定和并发操作

智能指针

f(A* a)

A* p = new A();

auto_ptr p(new A());

C++11普遍推广之前,为方便指针管理,一般要用auto_ptr。但这玩意也有缺点:它有一个隐式的所有权转移行为,容易出问题,比如不能放容器里、也不建议通过函数参数传递。

使用auto_ptr常见的有两种场景,一是作为智能指针传递到产生auto_ptr的函数外部,二是使用auto_ptr作为RAII管理类,在超出auto_ptr的生命周期时自动释放资源。 对于第1种场景,可以使用std::shared_ptr来代替。 对于第2种场景,可以
使用C++11标准中的std::unique_ptr来代替。其中std::unique_ptr是std::auto_ptr的代替品,支持显式的所有权转移。

具体到unique_ptr和shared_ptr,优先使用前者。因为:

  • shared_ptr有专门的内存存放引用计数,有额外的开销;
  • shared_ptr在循环依赖的时候,释放不了内存,参考python早期的垃圾回收问题

严格的说来,unique_ptr是C++14的标准,晚于shared_ptr。我们只需使用gcc5及以上的版本就没问题。

unique_ptr的使用

unique_ptr可以认为是“禁掉了隐式所有权转移的auto_ptr”(实现策略是禁用了拷贝构造和赋值运算符重载)。如果需要明确的所有权转移,使用std::move。所以有:

auto foo = std::make_unique<Foo>();
auto foo1 = foo; //Cannot compile!
auto foo2 = std::move(foo); //it's ok, foo has nothing now

不推荐复杂的模板编程

比如“模板元编程”,当年很痴迷,觉得很适合炫技,现在觉得太烧脑,代码不好维护。

auto

auto用于自动类型推断,本质上是一个类型占位符,而非真实的类型。可用于替代冗长的类型名(比如iterator)

注意:auto只能替代值类型,所以如果传一个引用给auto,它会自动去除引用,比如下面的例子:

class Foo 
{
public:Foo() = default;Foo(const Foo&) = delete;Foo& operator=(const Foo&) = delete;void doSth(){std::cout << "do sth" << std::endl;}
};Foo s;
Foo& ref = s;
auto s1 = ref; //Cannot compile

要编译正确,需改成:

auto& s1 = ref;

override

这个是照抄了Java的@override注解,用于检查派生类的虚函数定义是否与基类一致,重构的时候能起一定的保护作用。

Lambda

Lambda有几个优点:

  • 写起来简单
  • 可以引用(C++里叫捕获)所在环境的局部变量(术语叫闭包,不过我更喜欢lua里的叫法:upvalue)

Lambda在Java里是使用匿名类来实现的,upvalue作为匿名类构造器的参数传递进来,从而做到长期持有,C++估计是类似的实现机制。由于java有gc,所以不用考虑upvalue生存周期的问题。但C++则不然,在C++里使用Lambda,我们引用的upvalue可能只是栈上的对象,在lambda执行时已经不存在了,这是需要特别注意的

Lambda的形式:

[upvalue] (lambda参数) mutable|exception -> 返回值类型 {函数体}

upvalue(也叫函数对象参数)有以下形式:

  • 空。没有任何函数对象参数。
  • =。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
  • &。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量)。
  • this。函数体内可以使用 Lambda 所在类中的成员变量。
  • a。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的,要修改传递进来的拷贝,可以添加 mutable 修饰符。
  • &a。将 a 按引用进行传递。
  • a,&b。将 a 按值传递,b 按引用进行传递。
  • =,&a,&b。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
  • &,a,b。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。

lambda参数有一点不好,必须明确给出参数类型,做不到自动推断(java和scala里都是可以自动推断的)。

返回值类型不是必给的,编译器可以推断出来。

一个例子:

std::vector<std::string> l, r;
l.push_back("e0");
l.push_back("e1");std::transform(l.begin(), l.end(), std::back_inserter(r), [](const std::string& v){return v + "_wrapper";});
std::for_each(r.begin(), r.end(), [](const std::string& v){std::cout << v << std::endl;});

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

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

相关文章

C++并发多线程--std::async创建异步任务是否创建线程

1--std::async创建异步任务 std::async 创建一个异步任务&#xff0c;其不一定会创建一个新线程去执行该任务&#xff1b; 使用 std::launch::deferred 时&#xff0c;异步任务不会创建一个新线程&#xff1b; 使用 std::launch::async 时&#xff0c;操作系统会强制创建一个新…

一个炫酷的头像悬停效果 2

基于上次翻译的 &#x1f525;&#x1f525;一个炫酷的头像悬停效果 收获了不少同学的喜欢&#xff0c;原作者近期进行了优化升级。本文将升级后的核心实现过程进行梳理讲解&#xff0c;如果没看过第一期的推荐先看看第一期的实现过程。升级后的效果如下图所示。 gif动画效果如…

Baklib是比语雀、Notion、石墨文档更好用的在线知识库管理工具

在当今信息爆炸的时代&#xff0c;如何高效地管理和利用知识成为了每个人都面临的问题。在线知识库管理工具应运而生&#xff0c;帮助用户整理、存储和共享知识。在这篇文章中&#xff0c;我将介绍一个更好用的在线知识库管理工具——Baklib&#xff0c;并探讨它相对于其他知识…

变上限积分求导

y ∫ 0 x t f ( t 2 − x 2 ) d t y \int _0^x t f( t^2 - x^2)dt y∫0x​tf(t2−x2)dt 设 t 2 − x 2 u , 那么 t u x 2 , d t d u 2 u x 2 &#xff0c; 并且当 t x 时 u 0 , 当 t 0 时&#xff0c; u − x 2 设 t^2 - x^2 u,那么t \sqrt{ux^2},dt \frac{du}{2\s…

Spring系列篇--关于IOC【控制反转】的详解

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是Spring 二.Spring的特点 三.什…

【O2O领域】Axure外卖订餐骑手端APP原型图,外卖众包配送原型设计图

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;外卖配送、生鲜配送 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为外卖订餐骑手端APP原型设计图&#x…

Datawhale Django 后端开发入门 Task05 DefaultRouter、自定义函数

一、DefaultRouter是Django REST framework中提供的一个路由器类&#xff0c;用于自动生成URL路由。路由器是将URL与视图函数或视图集关联起来的一种机制。Django REST framework的路由器通过简单的配置可以自动生成标准的URL路由&#xff0c;从而减少了手动编写URL路由的工作量…

Redis Lua脚本执行原理和语法示例

Redis Lua脚本语法示例 文章目录 Redis Lua脚本语法示例0. 前言参考资料 1. Redis 执行Lua脚本原理1.1. 对Redis源码中嵌入Lua解释器的简要解析&#xff1a;1.2. Redis Lua 脚本缓存机制 2. Redis Lua脚本示例1.1. 场景示例1. 请求限流2. 原子性地从一个list移动元素到另一个li…

基于郊狼算法优化的BP神经网络(预测应用) - 附代码

基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.郊狼优化BP神经网络2.1 BP神经网络参数设置2.2 郊狼算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

【深入解析:数据结构栈的魅力与应用】

本章重点 栈的概念及结构 栈的实现方式 数组实现栈接口 栈面试题目 概念选择题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数…

指针(一)【C语言进阶版】

大家好&#xff0c;我是深鱼~ 【前言】&#xff1a; 指针的主题&#xff0c;在初阶指针章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址的唯一标识一块内存空间&#xff08;指针变量&#xff09;&a…

【云原生|Docker系列第3篇】Docker镜像的入门实践

欢迎来到Docker入门系列的第三篇博客&#xff01;在前两篇博客中&#xff0c;我们已经了解了什么是Docker以及如何安装和配置它。本篇博客将重点介绍Docker镜像的概念&#xff0c;以及它们之间的关系。我们还将学习如何拉取、创建、管理和分享Docker镜像&#xff0c;这是使用Do…

jenkins同一jar包部署到多台服务器

文章目录 安装插件配置ssh服务构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍&#xff0c;我这篇主要讲jenkins同一jar包部署到多台服务器 【Jenkins】部署Springboot项目https://blog.csdn.net/qq_39017153/article/details/131901613 安装插件 Publish Over SSH 这…

stm32g070的PD0/PD2 PA8和PB15

目前在用STM32G070做项目&#xff0c;其中PD2TIMER3去模拟PWM&#xff0c;PD0用作按键检测&#xff0c;测试发现PD0低电平检测没有问题&#xff0c;高电平检测不到&#xff0c;电路图如下图所示&#xff1a; 用万用表测试电平&#xff0c;高电平1.0V左右&#xff0c;首先怀疑硬…

PyTorch安装教程:从头开始配置PyTorch环境

PyTorch是一个开源的机器学习框架&#xff0c;广泛用于深度学习任务。要开始使用PyTorch&#xff0c;您需要在计算机上正确配置PyTorch环境。本文将为您提供一步步的指南&#xff0c;帮助您成功安装和配置PyTorch。 第一部分&#xff1a;安装Python和相关工具 第一步&#xf…

rust踩雷笔记(4)——刷点Vec相关的题(持续更新)

俗话说&#xff0c;孰能生巧&#xff0c;今天是第六天接触Rust&#xff0c;感觉基础语法和特性没什么问题了&#xff08;当然如果你整天都学这个可能2天半就够了&#xff09;&#xff0c;但是想达到熟练使用&#xff0c;还需要刷点题。算法我相信能来看rust博客的人都是大牛&am…

【项目实践】基于LSTM的一维数据扩展与预测

基于LSTM的一维数据拟合扩展 一、引(fei)言(hua) 我在做Sri Lanka生态系统服务价值计算时&#xff0c;中间遇到了一点小问题。从世界粮农组织(FAO)上获得Sri Lanka主要农作物产量和价格数据时&#xff0c;其中的主要作物Sorghum仅有2001-2006年的数据&#xff0c;而Millet只有…

算法通关村第4关【黄金】| 表达式问题

1. 计算器问题 思路&#xff1a;此题不考虑括号和负数情况&#xff0c;单纯使用栈即可解决。注意的是数字可能是多位数需要保留完整的num&#xff0c; 保留数字的前缀符号&#xff0c;当碰到加号&#xff0c;存进去&#xff1b;当碰到减号&#xff0c;存相反数进去&#xff1b;…

Apinto 网关进阶教程,插件开发入门指南

Apinto 是基于Go语言&#xff0c;由 Eolink 自主研发的一款高性能、可扩展、易维护的云原生 API 网关。Apinto 能够帮助用户简单、快速、低成本、低风险地实现&#xff1a;系统微服务化、系统集成、向合作伙伴、开发者开放功能和数据。 通过 Apinto&#xff0c;企业能够专注于…

clickhouse修改默认密码

1.明文密码 vim /etc/clickhouse-server/users.xml找到下面的语句,增加明文密码 <password>123456789</password> 2. sha256密码 # echo -n 123456789 | openssl dgst -sha256 (stdin) 15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 修改…