在这篇文章中,我们将带领您了解简明Python编程规范的全貌,包括python的编程规范的相关情况。同时,我们还将为您介绍有关ARTS第15周|LeetCode最长回文子序列|来自Uber的Go编程规
在这篇文章中,我们将带领您了解简明 Python 编程规范的全貌,包括python的编程规范的相关情况。同时,我们还将为您介绍有关ARTS 第15周 | LeetCode 最长回文子序列 | 来自 Uber 的 Go 编程规范、c 编程规范、C++ 编程规范、C++ 编程规范:养成良好编程习惯,打造高品质代码的知识,以帮助您更好地理解这个主题。
本文目录一览:- 简明 Python 编程规范(python的编程规范)
- ARTS 第15周 | LeetCode 最长回文子序列 | 来自 Uber 的 Go 编程规范
- c 编程规范
- C++ 编程规范
- C++ 编程规范:养成良好编程习惯,打造高品质代码
简明 Python 编程规范(python的编程规范)
赖勇浩(http://laiyonghao.com)
注:之前发布一篇《简明 Python 编程规范》(见:http://blog.csdn.net/lanphaday/article/details/2834883),本是我给当时所在的公司制定的,发表到博客的时候,对齐等问题都有一些错误,还包括一些错别字。到后来,我又有做过修正。大概在 2010 年 8 年,我又由 C++ 开发转到 Python 开发,便在那之前的基础上润饰和完善,形成第二版。前段时间 simple-is-better.com 网站转发了我写的前一版本规范(见:http://simple-is-better.com/news/534),引起大家的讨论,我一一解释不来,所以今天抽点时间把 v2 编辑一下发布上来,求交流、指正。
以下是为规范全文:
Python编程规范v2
执行
- 本规范使用 pylint 及对应的配置文件来进行检测,关于 pylint 的安装和配置见:http://blog.csdn.net/lanphaday/article/details/6089902
编码
- 所有的 Python 脚本文件都应在文件头标上如下标识或其兼容格式的标识:
[python] view plain copy
- # -*- coding:utf-8 -*-
- 设置编辑器,默认保存为 utf-8 格式。
注释
- 业界普遍认同 Python 的注释分为两种的概念,一种是由 # 开头的“真正的”注释,另一种是 docstrings。前者表明为何选择当前实现以及这种实现的原理和难点,后者表明如何使用这个包、模块、类、函数(方法),甚至包括使用示例和单元测试。
- 坚持适当注释原则。对不存在技术难点的代码坚持不注释,对存在技术难点的代码必须注释。但与注释不同,推荐对每一个包、模块、类、函数(方法)写 docstrings,除非代码一目了然,非常简单。
格式
缩进
- Python 依赖缩进来确定代码块的层次,行首空白符主要有两种:tab 和空格,但严禁两者混用。
- 公司内部使用 2 个空格的 tab 进行缩进。
空格
- 空格在 Python 代码中是有意义的,因为 Python 的语法依赖于缩进,在行首的空格称为前导空格。在这一节不讨论前导空格相关的内容,只讨论非前导空格。非前导空格在 Python 代码中没有意义,但适当地加入非前导空格可以增进代码的可读性。
- 在二元算术、逻辑运算符前后加空格,如:
[python] view plain copy
- a = b + c
- “:”用在行尾时前后皆不加空格,如分枝、循环、函数和类定义语言;用在非行尾时两端加空格,如 dict 对象的定义:
[python] view plain copy
- d = {''key'' : ''value''}
- 括号(含圆括号、方括号和花括号)前后不加空格,如:
[html] view plain copy
- do_something(arg1, arg2)
[python] view plain copy- do_something( arg1, arg2 )
- 逗号后面加一个空格,前面不加空格;
空行
- 适当的空行有利于增加代码的可读性,加空行可以参考如下几个准则:
- 在类、函数的定义间加空行;
- 在 import 不同种类的模块间加空行;
- 在函数中的逻辑段落间加空行,即把相关的代码紧凑写在一起,作为一个逻辑段落,段落间以空行分隔;
断行
- 尽管现在的宽屏显示器已经可以单屏显示超过 256 列字符,但本规范仍然坚持行的最大长度不得超过 78 个字符的标准。折叠长行的方法有以下几种方法:
- 为长变量名换一个短名,如:
应改为:[python] view plain copy
- this._is.a.very.long.variable_name = this._is.another.long.variable_name
[python] view plain copy
- variable_name1 = this._is.a.very.long.variable_name
- variable_name2 = this._is.another.variable_name
- variable_name1 = variable_name2s
- 在括号(包括圆括号、方括号和花括号)内换行,如:
[python] view plain copy
- class Edit(Widget):
- def __init__(self, parent, width,
- font = FONT, color = BLACK, pos = POS, style = 0): # 注意:多一层缩进
- pass
或:
[python] view plain copy
- very_very_very_long_variable_name = Edit(parent,
- width,
- font,
- color,
- pos) # 注意:多一层缩进
- do_sth_with(very_very_very_long_variable_name)
- 如果行长到连第一个括号内的参数都放不下,则每个元素都单独占一行:
[python] view plain copy
- very_very_very_long_variable_name = ui.widgets.Edit(
- panrent,
- width,
- font,
- color,
- pos) # 注意:多一层缩进
- do_sth_with(very_very_very_long_variable_name)
- 在长行加入续行符强行断行,断行的位置应在操作符前,且换行后多一个缩进,以使维护人员看代码的时候看到代码行首即可判定这里存在换行,如:
[html] view plain copy
- if color == WHITE or color == BLACK \
- or color == BLUE: # 注意 or 操作符在新行的行首而不是旧行的行尾,上一行的续行符不可省略
- do_something(color);
- else:
- do_something(DEFAULT_COLOR);
命名
- 一致的命名可以给开发人员减少许多麻烦,而恰如其分的命名则可以大幅提高代码的可读性,降低维护成本。
常量
- 常量名所有字母大写,由下划线连接各个单词,如:
[python] view plain copy
- WHITE = 0xffffffff
- THIS_IS_A_CONSTANT = 1
变量
- 变量名全部小写,由下划线连接各个单词,如:
[python] view plain copy
- color = WHITE
- this_is_a_variable = 1
- 不论是类成员变量还是全局变量,均不使用 m 或 g 前缀。私有类成员使用单一下划线前缀标识,多定义公开成员,少定义私有成员。
- 变量名不应带有类型信息,因为 Python 是动态类型语言。如 iValue、names_list、dict_obj 等都是不好的命名。
函数
- 函数名的命名规则与变量名相同。
类
- 类名单词首字母大写,不使用下划线连接单词,也不加入 C、T 等前缀。如:
[python] view plain copy
- class ThisIsAClass(object):
- passs
模块
- 模块名全部小写,对于包内使用的模块,可以加一个下划线前缀,如:
[python] view plain copy
- module.py
- _internal_module.py
包
- 包的命名规范与模块相同。
缩写
- 命名应当尽量使用全拼写的单词,缩写的情况有如下两种:
- 常用的缩写,如 XML、ID等,在命名时也应只大写首字母,如:
[python] view plain copy
- class XmlParser(object):pass
- 命名中含有长单词,对某个单词进行缩写。这时应使用约定成俗的缩写方式,如去除元音、包含辅音的首字符等方式,例如:
- function 缩写为 fn
- text 缩写为 txt
- object 缩写为 obj
- count 缩写为 cnt
- number 缩写为 num,等。
特定命名方式
- 主要是指 __xxx__ 形式的系统保留字命名法。项目中也可以使用这种命名,它的意义在于这种形式的变量是只读的,这种形式的类成员函数尽量不要重载。如:
[python] view plain copy
- class Base(object):
- def __init__(self, id, parent = None):
- self.__id__ = id
- self.__parent__ = parent
- def __message__(self, msgid):
- # …略
其中 __id__、__parent__ 和 __message__ 都采用了系统保留字命名法。
语句
import
- import 语句有以下几个原则需要遵守:
- import 的次序,先 import Python 内置模块,再 import 第三方模块,最后 import 自己开发的项目中的其它模块;这几种模块中用空行分隔开来。
- 一条 import 语句 import 一个模块。
- 当从模块中 import 多个对象且超过一行时,使用如下断行法(此语法 py2.5 以上版本才支持):
[python] view plain copy
- from module import (obj1, obj2, obj3, obj4,
- obj5, obj6)
- 不要使用 from module import *,除非是 import 常量定义模块或其它你确保不会出现命名空间冲突的模块。
赋值
- 对于赋值语句,主要是不要做无谓的对齐,如:
[python] view plain copy
- a = 1 # 这是一个行注释
- variable = 2 # 另一个行注释
- fn = callback_function # 还是行注释
没有必要做这种对齐,原因有两点:一是这种对齐会打乱编程时的注意力,大脑要同时处理两件事(编程和对齐);二是以后阅读和维护都很困难,因为人眼的横向视野很窄,把三个字段看成一行很困难,而且维护时要增加一个更长的变量名也会破坏对齐。直接这样写为佳:
[python] view plain copy
- a = 1 # 这是一个行注释
- variable = 2 # 另一个行注释
- fn = callback_function # 还是行注释
分枝和循环
- 对于分枝和循环,有如下几点需要注意的:
- 不要写成一行,如:
[python] view plain copy
- if not flg: pass
和
[python] view plain copy
- for i in xrange(10): print i
都不是好代码,应写成
[python] view plain copy
- if not flg:
- pass
- for i in xrange(10):
- print i
注:本文档中出现写成一行的例子是因为排版的原因,不得作为编码中不断行的依据。
- 条件表达式的编写应该足够 pythonic,如以下形式的条件表达式是拙劣的:
[python] view plain copy
- if len(alist) != 0: do_something()
- if alist != []: do_something()
- if s != "": do_something()
- if var != None: do_something()
- if var != False: do_something()
上面的语句应该写成:
[python] view plain copy
- if seq: do_somethin() # 注意,这里命名也更改了
- if var: do_something()
- 用得着的时候多使用循环语句的 else 分句,以简化代码。
已有代码
- 对于项目中已有的代码,可能因为历史遗留原因不符合本规范,应当看作可以容忍的特例,允许存在;但不应在新的代码中延续旧的风格。
- 对于第三方模块,可能不符合本规范,也应看作可以容忍的特例,允许存在;但不应在新的代码中使用第三方模块的风格。
- tab 与空格混用的缩进是''''''不可容忍''''''的,在运行项目时应使用 -t 或 -tt 选项排查这种可能性存在。出现混用的情况时,如果是公司开发的基础类库代码,应当通知类库维护人员修改;第三方模块则可以通过提交 patch 等方式敦促开发者修正问题。
已有风格
- 开发人员往往在加入项目之前已经形成自有的编码风格,加入项目后应以本规范为准编写代码。特别是匈牙利命名法,因为带有类型信息,并不适合 Python 编程,不应在 Python 项目中应用。
ARTS 第15周 | LeetCode 最长回文子序列 | 来自 Uber 的 Go 编程规范
ARTS
ARTS 是陈浩(网名左耳朵耗子)在极客时间专栏里发起的一个活动,目的是通过分享的方式来坚持学习。
每人每周写一个 ARTS:Algorithm 是一道算法题,Review 是读一篇英文文章,Technique/Tips 是分享一个小技术,Share 是分享一个观点。
本周内容
本周的 ARTS 你将看到:
- LeetCode 516 最长回文子序列.
- 来自 Uber 的 Golang 编程规范.
Algorithm
本周的算法题是 LeetCode 516.longest-palindromic-subsequence, 最长回文子序列.
回文序列和会问字符串的最大区别就是序列
是可以不连续的, 但是串
必须是连续的.
所以这道题和之前第 5 题最长回文子串的区别就很明显了, 或者说这道题要求更加宽泛一些.
// 没错我就是抄答案的 https://leetcode-cn.com/problems/longest-palindromic-subsequence/solution/zi-xu-lie-wen-ti-tong-yong-si-lu-zui-chang-hui-wen/
// dp[i][j] 代表 s 的下标冲 i 到 j 范围内的回文子序列长度
// base case: dp[i][i] 代表一个字符一定是回文子序列
func longestPalindromeSubseq(s string) int {
l := len(s)
dp := make([][]int, l)
for i := range dp {
dp[i] = make([]int, l)
}
for i := 0; i < l; i++ {
dp[i][i] = 1
}
for i := l - 1; i >= 0; i-- {
for j := i + 1; j < l; j++ {
if s[i] == s[j] {
dp[i][j] = dp[i+1][j-1] + 2
} else {
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
}
}
}
return dp[0][l-1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
Review 文章推荐
本周推荐的英文文章是 Uber 公开的内部 Golang编程规范.
这个规范包括一些 Golang 的编程技巧和惯例, 也包括一些代码格式这种更加表面一些的规范. 下面是我认为比较重要的一些点.
- 使用
golint
和go vet
命令. - 依赖编译对 interface 的实现类型进行判断.
- 注意错误的类型和封装.
- 如果使用原子操作但又觉得官方包太捡漏的话, 可以试试 Uber 对原子操作的封装.
- 少用全局变量.
- 少用匿名组合, 因为匿名组合不得不暴露匿名属性的封装细节, 需要注意的地方比如这些.
- 尽量少用 init() 来做初始化的工作.
- 用 strconv 代替 Sprintf 来做 string 类型转换.
- 少用 string 到 []byte 的类型转换.
- 使用 make 创建 slice 和 map 时最好指定 capacity.
- 使用小写和单数形式明明 package.
- 不对外暴露的包内全局变量, 最好用下划线 _ 开头.
- 使用
if len(s) == 0
而不是if s == nil
来判断 slice 是否为空 - 少用裸参数(字面量参数?)
- 多用反单引号包裹的原生字符串来逃逸特殊符号.
- 使用打表的方式组建单元测试的数据.
- 使用变长函数数组作为配置类型的可选参数
Tip 编程技巧
本周没有技巧.
Share 灵光一闪
本周也没有灵光.
本周阅读列表
Go语言设计与实现 6.5调度器
- Go 携程调度三个角色 GMP
G 结构中包含 stack 相关信息,还有与协程抢占式调度相关的字段 stackguard0,defer 和 -panic 链表。还包括当前 G 占用的线程,调度器相关数据 sched 以及 goid 等等。
M 中包含的字段比较重要的有 g0 和 currg,其中 g0 代表持有调度栈的 G 他会深度参与协程的调度, curg 是在当前线程上运行的用户 G, 这也是操作系统线程唯一关心的两个 Goroutine. 除此之外还有在运行代码的处理器 p、暂存的处理器 nextp 和执行系统调用之前的使用线程的处理器 oldp.
P 的数量通过 GOMAXPROCS 设置,最多只会有 GOMAXPROCS 个活跃的操作系统线程能够正常运行. P 是线程和 Goroutine 的中间层,也会负责调度线程上的等待队列. 它能在 Goroutine 进行一些 I/O 操作时及时切换, 提高线程的利用率. P 本身也有 status 字段代表其状态, 比如_Pidle
代表 P 上的 G 队列为空,_Prunning
代表正在执行用户代码,_Pgcstop
代表 P 被 GC 停止, 等等.
- Go 携程调度三个角色 GMP
- golang自动检测死锁deadlock的实现
一个用于检测死锁的工具, 实现方式就是封装了 Go 的 sync 库的锁, 然后在每次加锁前检测是否有可能死锁.
c 编程规范
1) 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。程序中的英文单词一般不会太复杂,用词应当准确。例如不要把 CurrentValue 写成 NowValue。
2) 不允许使用汉语拼音命名,更不要使用汉语,必须使用英文来命名标识符。尽量避免名字中出现数字编号,如 Value1,Value2 等,除非逻辑上的确需要编号。
3) 函数名、变量名从第二个单词起首字母大写。如:printName (), oldName 等
4) 在 C 程序中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量。常量定义以大写字母 K 开头,字母全部使用大写,如:const int KSIZE = 100。
5) 代码缩进使用 4 个空格,不推荐使用 TAB 键
6) 版权和版本的声明位于文件的开头;函数头部应进行注释,列出:函数的功能、输入参数、输出参数、返回值等。关键代码注释位于被注释代码上方,且用空行与其他代码分割开,如:
文件头注释:
author: SDL team
function: testing hardware drivers and IDE
date: 2009.10
copy right: Software College Of HeBei Normal University
函数注释:
function:SDL initial
parameter: none
return: bool
true: initial SDL normally
false: initial SDL abnormally
关键代码注释:
pTemp = SDL_LoadBMP(aFileName);
//format BMP file
*aImg = SDL_DisplayFormat(pTemp);
SDL_FreeSurface(pTemp);
7) 静态变量加前缀 s_,如:static int s_count = 0;
8) 尽量避免应用全局变量,如果有特殊情况必须定义的话,全局变量以小写 g_开头,如:int g_count = 0;
9) 结构体的名字开头字母要大写。如:
struct Student
{
};
10) 枚举和它的成员的定义,应该采用有意义的命名方式,避免出现歧义;枚举变量的命名以 T 开头,枚举成员以 E 开头。
C++ 编程规范
组织和策略问题
第 0 条 不要拘泥于小节(又名:了解哪些东西不应该标准化)
无需在多个项目或者整个公司范围内强制实施一致的编码格式。
第 1 条 在高警告级别干净利落地进行编译
高度重视警告:使用编译器的最高警告级别。通过修改代码而不是降低警告级别来排除警告。
第 2 条 使用自动构建系统
一键构建。
第 3 条 使用版本控制系统
svn。
第 4 条 在代码审查上投入
做好同行评审。
设计风格
第 5 条 一个实体应该只有一个紧凑的职责
一次只解决一个问题。一个实体(变量、类、函数、名称空间、模块和库)或一个模块,只赋予一个良好的职责,不要乱发散。
第 6 条 正确、简单和清晰第一
代码是写给人看的,要简单、清晰、可靠。
第 7 条 编程中应知道何时和如何考虑可伸缩性
关注算法的优化,注意复杂性。
第 8 条 不要进行不成熟的不要进行不成熟的优化,优化应该使代码更清晰易读,易理解,易重构,而不要为了所谓的性能,让代码变得更复杂和更差的可读性。
第 9 条 不要进行不成熟的劣化
不要使用低效的用法,比如 ++,应倾向于使用前 ++,而不是会产生临时变量的后 ++。
第 10 条 尽量减少全局和共享数据
全局和共享数据,会增加耦合度,降低可维护性。
第 11 条 隐藏信息
模块或对象设计,内部实现与外部接口要分离,减少依赖性。
第 12 条 懂得何时和如何进行并发性编程
多线程,与平台相关。小心使用各种多线程技术。 尽量减少共享对象,安全地共享必须共享的对象。
第 13 条 确保资源为对象所拥有。使用显式的 RAII 和智能指针
“资源获取即初始化”,是处理资源获取和释放的 C++ 惯用法。局部对象的构造函数和析构函数,竟然可以解决资源自动释放的难题,C++ 真是无时无刻不让人惊叹。
编程风格
第 14 条 宁要编译时和连接时错误,也不要运行时错误
C++ 属于静态类型语言,应当好好利用其静态类型检查。多依赖编译时检查,而不要过多的依赖运行时检查。
第 15 条 积极使用 const
Const 是我们的好朋友。
第 16 条 避免使用宏
C++ 中几乎不需要宏:const/enum,inline,template,namespace 等多种机制,分别取代宏大作用。当然头文件中的 #ifndef 那个还是没有好的选择。
第 17 条 避免使用 “魔数”
硬编码的数字,应当使用常量代替。
第 18 条 尽可能局部地声明变量
变量用的着的时候才定义,定义马上初始化。
第 19 条 总是初始化变量
第 20 条 避免函数过长,避免嵌套过深
第 21 条 避免跨编译单元的初始化依赖
不同编译单元中的名字空间级对象,其初始化顺序是未定义的。
第 22 条 尽量减少定义性依赖。避免循环依赖
只需要声明的时候,就不要提供定义。
第 23 条 头文件应该自给自足
应该确保每个头文件都能够独立进行编译,需要包含其内容所依赖的所有头文件。
第 24 条 总是编写内部 #include 保护符,决不要编写外部 #include 保护符
#ifndef xxx #define xxx #endif,这个破玩意。
函数与操作符
第 25 条 正确地选择通过值、(智能)指针或者引用传递参数
分清输入 / 输出参数。
第 26 条 保持重载操作符的自然语义
不要定义和操作符名称不符的重载。
第 27 条 优先使用算术操作符和赋值操作符的标准形式
a+b 定义为 a+=b
第 28 条 优先使用 ++ 和 -- 的标准形式。优先调用前缀形式
T& T::operator++(); // 前缀形式
T& T::operator++(int); // 后缀形式
第 29 条 考虑重载以避免隐含类型转换
第 30 条 避免重载 &&、|| 或,(逗号)
第 31 条 不要编写依赖于函数参数求值顺序的代码
不同的编译器处理函数参数求值顺序可能不一样。
类的设计与继承
第 32 条 弄清所要编写的是哪种类
不同种类的类适用于不同用途。值类?基类?traits 类?策略类?异常类?
第 33 条 用小类代替巨类
小类更易于编写,更易于保证正确、测试和使用。小类更有可能适用于各种不同情况。
小的类粒度层次恰到好处,被人使用和重用的可能性也越大,更易于部署。
第 34 条 用组合代替继承
继承是紧密的耦合关系。“组合” 就是指在一个类型中嵌入另一个类型的成员变量。用这种方式能够保存和使用对象,还能控制耦合强度。
第 35 条 避免从并非要设计成基类的类中继承
不要再不需要的情况下使用继承。要继承的话,就要设计专门的基类。
第 36 条 优先提供抽象接口
抽象接口是完全由(纯)虚函数构成的抽象类,没有状态(成员数据)。抽象基类必须负责定义功能,而不是实现功能。策略应该上推,而实现应该下放。
第 37 条 公用继承即可替换性。继承,不是为了重用,而是为了被重用
公用继承能够使基类的指针或者引用实际指向某个派生类的对象,既不会破坏代码的正确性,也不需要改变已有代码。
不要通过公用继承重用代码,公用继承是为了被重用的。
第 38 条 实施安全的改写
改写一个虚拟函数时,应该保持可替换性,就是要保持基类中函数的前后条件。不要改变虚拟函数的默认参数。
第 39 条 考虑将虚拟函数声明为非公用的,将公用函数声明为非虚拟的
在基类中进行修改代码代价高昂(尤其是库中和框架中的基类):请将公用函数设为非虚拟的。应该将虚拟函数设为私有的,或者如果派生类需要调用基类版本,则设为保护的。(不适用于析构函数)
第 40 条 要避免提供隐式转换
隐式转换会在最意料不到的地方抛出异常;并不总是能与语言的其他元素有效的配合。尽量使用显式转换。
第 41 条 将数据成员设为私有的,无行为的聚集(C 语言形式的 struct)除外
要避免将公用数据和非公用数据混合在一起。拥有公用数据意味着类的部分状态的变化可能是无法控制的、无法预测的、与其他状态异步发生的。
第 42 条 不要公开内部数据
避免返回类所管理的内部数据的句柄,这样类的客户就不会不受控制的修改对象自己拥有的状态。
数据隐藏是一种强大的抽象方式,也是强大的模块化机制。
第 43 条 明智地使用 Pimpl
如果创建 “编译器防火墙” 将调用代码与类的私有部分完全隔离是明智的,就应该使用 Pimpl 惯用法:将私有部分隐藏在一个不透明的指针(即指向已经声明但是尚未定义的类的指针,最好是选择合适的智能指针)后面。
第 44 条 优先编写非成员非友元函数
非成员非友元函数通过尽量减少依赖提高了封装性:函数体不能依赖于类的非公用成员。
第 45 条 总是一起提供 new 和 delete
第 46 条 如果提供类专门的 new,应该提供所有标准形式(普通、就地和不抛出)
构造、析构与复制
第 47 条 以同样的顺序定义和初始化成员变量
成员变量初始化的顺序要与类定义中声明的顺序始终保持一致;不用考虑构造函数初始化列表中编写的顺序。要确保构造函数代码不会导致混淆地指定不同的顺序。
C++ 语言之所以采取这样的设计,是因为要确保销毁成员的顺序是唯一的;否则,析构函数将以不同顺序销毁对象,具体顺序取决于构造对象的构造函数。
第 48 条 在构造函数中用初始化代替赋值
在初始化列表中初始化成员变量,代码表达意图更加明确,代码通常还会更小、更快。A ():s1_(“hello”), s2_(“world”){}
第 49 条 避免在构造函数和析构函数中调用虚拟函数
第 50 条 将基类析构函数设为公用且虚拟的,或者保护且非虚拟的
第 51 条 析构函数、释放和交换绝对不能失败
第 52 条 一致地进行复制和销毁
如果定义了复制构造函数、复制赋值操作符或者析构函数中的任何一个,那么也需要定义另外两个。
第 53 条 显式地启用或者禁止复制
要清醒的知道自己选择什么样的行为:是使用编译器生成的默认复制构造函数和赋值操作符;还是编写自己的版本;或者如果不允许复制的话,显式地禁用前两种方式。
第 54 条 避免切片。在基类中考虑用克隆代替复制
在基类中,如果客户需要进行多态(完整的、深度的)复制的话,考虑禁止复制构造函数和复制赋值操作符而改为提供虚拟的 Clone 成员函数。
第 55 条 使用赋值的标准形式
第 56 条 只要可行,就提供不会失败的 swap(而且要正确地提供)
名字空间与模块
第 57 条 将类型及其非成员函数接口置于同一名字空间中
非成员也是函数:如果要将非成员函数(特别是操作符和辅助函数)设计成类 X 的接口的一部分,那么就必须与 X 相同的名字空间中定义它们,以便正确调用。
第 58 条 应该将类型和函数分别置于不同的名字空间中,除非有意想让它们一起工作
第 59 条 不要在头文件中或者 #include 之前编写名字空间 using
不要在 #include 之前编写 using 指令;
头文件中,不要编写名字空间级的 using 指令或 using 声明,应该显式的用名字空间限定所有的名字。
第 60 条 要避免在不同的模块中分配和释放内存
在一个模块中分配内存,而在另一个模块中释放它,会在这两个模块之间产生微妙的远距离依赖使程序变得脆弱。
第 61 条 不要在头文件中定义具有链接的实体
具有链接的实体,包括名字空间级的变量或函数,都需要分配内存。在头文件中定义这样的实体将导致链接时错误或者内存的浪费。应将所有具有链接的实体放入实现文件。
第 62 条 不要允许异常跨越模块边界传播
C++ 异常处理没有普遍通用的二进制标准。不要在两段代码之间传播异常,除非能够控制用来构建两段代码的编译器和编译选项;否则模块可能无法支持可兼容地实现异常传播。
第 63 条 在模块的接口中使用具有良好可移植性的类型
模板与泛型
第 64 条 理智地结合静态多态性和动态多态性
第 65 条 有意地进行显式自定义
编写模板时,应该有意地、正确地提供自定义点,并清晰地记入文档。在使用模板时,应该了解模板想要你如何进行自定义以将其用于你的类型,并且正确地自定义。
第 66 条 不要特化函数模板
第 67 条 不要无意地编写不通用的代码
依赖抽象而非细节:使用最通用、最抽象的方法来实现一个功能。
错误处理与异常
第 68 条 广泛地使用断言记录内部假设和不变式
一个事件中所含的信息量与该事件发生的概率是成反比的。如果 assert 触发的可能性越低,它触发时所提供的信息量就越大。尽量使用 assert (!”informational message”)。
第 69 条 建立合理的错误处理策略,并严格遵守
应该在设计早期开发实际、一致、合理的错误处理策略,并予以严格遵守。策略必须包含:
鉴别:哪些情况属于错误
严重程度:每个错误的严重性或紧急性
检查:哪些代码负责检查错误
传递:用什么机制在模块中报告和传递错误通知
处理:哪些代码负责处理错误
报告:怎样将错误记入日志,或通知用户
只在模块边界处改变错误处理机制。
第 70 条 区别错误与非错误
错误就是阻止函数成功操作的任何失败。
第 71 条 设计和编写错误安全代码
第 72 条 优先使用异常报告错误
出现问题时,就使用异常:应该使用异常而不是错误码来报告错误。
异常处理很自然地将错误检查和恢复都放进了独立的 catch 代码块,使错误处理变得清晰有形。
第 73 条 通过值抛出,通过引用捕获
通过值(而非指针)抛出异常,通过引用(通常是 const 的引用)捕获异常。重新抛出相同的异常时,优先使用 throw,避免使用 throw e;。
第 74 条 正确地报告、处理和转换错误
第 75 条 避免使用异常规范
STL:容器
第 76 条 默认时使用 vector。否则,选择其他合适的容器
意思就是,在使用数组、链表等数据结构时,优先使用标准库;然后选择你认为的最好的容器即可。
第 77 条 用 vector 和 string 代替数组
不要使用 C 语言风格的数组、指针运算和内存管理原语操作实现数组抽象。使用 vector 或者 string 不仅更轻松,而且还有助于编写更安全、伸缩性更好的软件。
第 78 条 使用 vector(和 string::c_str)与非 C++ API 交换数据
第 79 条 在容器中只存储值和智能指针
在容器中存储值对象:容器假设他们所存放的是类似值的类型,包括值类型、智能指针和迭代器。
第 80 条 用 push_back 代替其他扩展序列的方式
第 81 条 多用范围操作,少用单元素操作
调用范围操作通常更易于编写,也更易于阅读,而且比显式循环的效率更高。
第 82 条 使用公认的惯用法真正地压缩容量,真正地删除元素
要真正地压缩容器的多余容量,应该使用 “swap 魔术” 惯用法。要真正地删除容器中的元素,应该使用 erase-remove 惯用法。
STL:算法
第 83 条 使用带检查的 STL 实现
第 84 条 用算法调用代替手工编写的循环
编写算法调用代替手工编写的循环,可以使表达力更强、维护性更好、更不易出错,而且同样高效。
算法的正确性也很可能比循环好。手工编写的循环很容易犯使用无效迭代器这样的错误。
算法的效率经常比原始循环要好。我们所使用的标准算法是由实现标准容器的那些人实现的,就凭他们对内幕的了解,所编写的算法的效率,就绝非你我编写的任何版本所能相提并论。但是最重要的还在于,许多算法的实现精巧绝伦,你我这样的一线程序员手工编写的代码也是不可能与之一较短长的。
第 85 条 使用正确的 STL 查找算法
正确的查找方式应该使用 STL(虽然比光速慢,但已经非常快了)。
查找无序范围,应使用 find/find_if 或者 count/count_if。查找有序范围,应使用 lower_bound/upper_bound/equal_range 或 binary_search。
第 86 条 使用正确的 STL 排序算法
第 87 条 使谓词成为纯函数
保持谓词纯洁性:谓词就是返回是或否的函数对象。
不要让谓词保存或访问对其 operator () 结果有影响的状态,包括成员状态和全局状态。应该使 operator () 成为谓词的 const 成员函数。
第 88 条 算法和比较器的参数应多用函数对象少用函数
对象的适配性比函数好:应该向算法传递函数对象,而非函数。关联容器的比较器必须是函数对象。函数对象的适配性好,而且与直觉相反,他们产生的代码一般比函数要快。
第 89 条 正确编写函数对象
函数对象模仿的就是函数指针。与函数指针一样,一般函数对象应该通过值来传递。所有标准算法都是通过值来传递对象的,我们自己的算法也应如此。
类型安全
第 90 条 避免使用类型分支,多使用多态
避免通过对象类型分支来定制行为。使用模板和虚函数,让类型自己来决定行为。
第 91 条 依赖类型,而非其表示方式
第 92 条 避免使用 reinterpret_cast
不要尝试使用 reinterpret_cast 强制编译器将某个类型对象的内存表示重新解释成另一种类型的对象。这违反了维护类型安全性的原则,尤其可怕的是,reinterpret_cast 甚至不能保证是否能够达到这一目的,也无法保证其他功能。
第 93 条 避免对指针使用 static_cast
不要对动态对象的指针使用 static_cast:安全的替代方法有很多,包括使用 dynamic_cast,重构,乃至重新设计。
第 94 条 避免强制转换 const
强制转换有时会导致未定义的行为,即使合法,也是不良的编程风格的主要表现。
第 95 条 不要使用 C 风格的强制转换
C 语言风格的强制转换根据上下文具有不同的语义,而所有这些都隐藏在相同的语法背后。用 C++ 风格的强制转换代替 C 风格的强制转换有助于防范意想不到的错误。
第 96 条 不要对非 POD 进行 memcpy 操作或者 memcmp 操作
第 97 条 不要使用联合重新解释表示方式
第 98 条 不要使用可变长参数(...)
要避免使用可变长参数,应改用高级的 C++ 结构和库。
第 99 条 不要使用失效对象。不要使用不安全函数
第 100 条 不要多态地处理数组
数组的可调整性很差:多态地处理数组是绝对的类型错误,而且编译器有可能不会做出任何提示。
C++ 编程规范:养成良好编程习惯,打造高品质代码
C++ 编程规范:养成良好编程习惯,打造高品质代码
良好的编程规范是编写高质量、可维护代码的关键。本文将介绍 C++ 编程中遵循的最佳实践,帮助您养成良好的编程习惯。
命名约定
- 使用小写字母,特殊情况用下划线隔开(如 snake_case)。
- 类名以大写字母开头,成员变量和方法以小写字母开头(如 ClassName::member_variable)。
- 避免使用匈牙利命名法(如 m_member_variable)。
代码格式
立即学习“C++免费学习笔记(深入)”;
- 使用缩进表示代码块(通常为 4 个空格)。
- 使用花括号包围所有代码块,即使只有一个语句。
- 使用空格进行对齐,例如对齐函数参数或成员变量初始化。
变量声明
- 变量应声明在需要使用它们的最小
作用域。 - 首选 const 和 constexpr 修饰符以提高性能和安全性。
- 避免全局变量,因为它们难以维护。
函数定义
- 函数应简短、目标明确,并且只做一件事。
- 参数应通过引用或值传递,具体取决于需要。
- 异常应明确处理并记录在日志中。
实战案例:自定义排序算法
下面的代码演示了如何实现自定义排序算法:
#include <vector> #include <algorithm> class CustomComparator { public: bool operator()(const int& a, const int& b) const { // 自定义排序逻辑 return a % 2 > b % 2; } }; int main() { std::vector<int> numbers = {1, 3, 9, 2, 8, 7, 0, 5}; // 使用自定义比较器对向量进行排序 std::sort(numbers.begin(), numbers.end(), CustomComparator()); // 输出排序后的向量 for (int num : numbers) { std::cout << num << " "; } std::cout << std::endl; return 0; }
在上面的代码中,CustomComparator 类实现了自定义排序逻辑,使得奇数先于偶数排序。通过将该比较器传递给 std::sort 函数,我们可以按照自定义逻辑对向量进行排序。
其他建议
- 使用版本控制系统进行协作和代码跟踪。
- 对代码进行单元测试和集成测试以确保正确性。
- 定期审查代码并遵循最佳实践以提高代码质量。
以上就是C++ 编程规范:养成良好编程习惯,打造高品质代码的详细内容,更多请关注php中文网其它相关文章!
我们今天的关于简明 Python 编程规范和python的编程规范的分享就到这里,谢谢您的阅读,如果想了解更多关于ARTS 第15周 | LeetCode 最长回文子序列 | 来自 Uber 的 Go 编程规范、c 编程规范、C++ 编程规范、C++ 编程规范:养成良好编程习惯,打造高品质代码的相关信息,可以在本站进行搜索。
本文标签: