这几天看了下这本书《编写高质量代码_改善C++程序的150个建议》,觉的蛮有收获的,再次记录下自己以前不清晰的知识点,以供学习。

编写符合标准的main函数

C语言标准规定了main函数的两种标准形式:


int main(void)

int main(int argc, char* argv[])

C++03标准中给出了两种main函数的定义方式


int main()

int main(int argc, char *argv[])

上述标准都要求main函数返回一个整型数值

extern C的作用

extern C的作用是当程序被C++编译器编译时,让后续的链接器以C方式来寻找函数,方便C++程序调用C程序。

注释风格

C++风格注释形如:// ....,推荐使用这样的注释。但是,头文件说明和函数默认参数的注释,还是用C风格(/**/)的较好。

不要写与编译器依赖紧密的代码

例如:printf("a %d %d", p(), q()),p和 q函数的执行前后顺序与编译器实现相关,应当避免此类代码。类似的还有 c = p() * q() * r()

尽量用const,enum,inline代替#define

inline关键字用在函数调用展开,在类声明中定义并且实现的函数自动为内联函数。如果需要将其他函数定义为内联函数,则需要在函数实现头声明此关键字,才让编译器尝试去内联,至于具体是否内联,还要求函数体满足一定条件才行,总体原则是短小精悍。

在使用define的场合,注意用()来保护宏函数。例如:#define MAX(a, b) ((a) > (b) ? (a) : (b))

struct在C和C++下的异同

C语言的struct不允许定义函数程序,而C++语言下的struct可以。

成员初始化

const成员变量只能通过"成员初始化列表"来进行初始化。在C++中允许声明一个函数但不无函数体实现。

类中成员变量如果是自定义类型,那么它的构造是先于类本身的构造函数,其构造顺序与在类中定义的顺序一致。

RAII手法:将资源的申请和释放操作包裹到类中的构造和析构函数中,隐式地保护资源。

所有数据成员一律为private类型。如果派生类需要用到,那么在用到的时候再将其改为protect类型,否则,一律声明为private类型,对外隐藏。
在具体声明时,可以按类型来多段声明,比如私有控件,来一个private,私有数据来另外一个private。

派生类初始化

在继承体系中,对象的构造是从最根处开始的。

虚函数相关

当类中至少包含一个虚函数时,才需要将其析构函数设置为虚函数。
在派生类被正确地构造出来之前,调用派生类的虚函数是没有意义的。
不要在构造/析构函数中调用虚函数。

虚函数需要配合继承机制,它生效于运行期,属于OOP范畴,适用于接口相同,逻辑不同的代码应用于不同的场合,实现了接口与实现的分离。

在C++中,还有一种成为pimpl idiom的手法,来更加彻底的分离接口和实现,大体实习思路是将对象中全部的数据成员封装在另一个实现类中,在本类中通过类成员指针或者是智能指针来引用。对外暴露的接口在内部都转发给实现类处理。

模板在编译器生效,它的本意是让不同类型的数据作用在相同的代码逻辑,属于GP范畴,体现为算法的普适性。

基类中虚函数的保护级别:

  • protect:标明派生类一定要重写该虚函数
  • private:标明派生类可以修改基类虚函数,如果无特殊要求的话,可以复用基类虚函数
  • public:禁止将虚函数声明为public权限

接口设计

设计目标:能覆盖完整功能并且达到接口最小化

以行为为中心的类设计,对外的public函数放在前面,需要继承的protect虚函数紧随其后,再后面是private的虚函数、普通函数以及成员变量。

语法的背后含义是语义,接口设计要有明确的语义,不可模棱两可、职责不清。

拷贝相关

如果类中无动态分配的资源类型,那么默认的浅拷贝可以满足要求。一旦有指针类型,那么,就必定有动态申请的资源,在拷贝时,需要重写赋值运算符,在目标类中重新申请空间,并且将原动态资源内容原封不动的拷贝过来。

如果重写派生类的赋值操作符,那么需要调用基类的赋值操作符,完整的进行赋值,而不是只赋值派生类数据,不赋值基类数据。

一般,只有在类宏成员变量动态申请了资源,才需要重写赋值操作符和拷贝构造函数。考虑到A = A这种情况,还需要重写==操作符,一般来说,重写的==,那么!=也顺带写上,保持相关操作的一致性。


A& A::operator=(const A& rhs)

{
    if(this == &rhs)
        return *this;

    parent::operator=(rhs)

    // 派生类成员赋值

    return *this;

}

重载相关

overloading:具有相同函数名,但是不同参数形式的函数集合

overriding:在类中,派生类重写基类提供的虚函数,建议使用 override关键字来声明,在派生类中,不要用virtual来标明此为虚函数。

Hiding:派生类中重写和基类一样签名的非虚函数

模板相关

模板的声明和定义需要放在同一个头文件中,如果分离声明和实现,则外部在使用时,需要包含实现的cpp文件,这违反惯用法。

.inl文件可以将模板头文件和复杂模板定义分离,一般将其在头文件尾部进行包含。

异常相关

如底层发生异常,则需要逐级上报,直到有能力处理此异常的层级来处理。如果程序都没处理,则会被C++系统捕获并终止程序运行。异常可以将发生错误和处理错误分离。

一般以传值来抛出异常,以 const 引用来捕获异常,不涉及到异常对象的清理工作,无对象切割问题,如本层级处理后还需要继续抛出异常,可调用throw来。

智能指针

优先使用shared_ptr,它内部工作原理是引用计数,线程安全,支持扩展,推荐使用。

不要在stl容器中存储auto_ptr对象,因为容器元素是允许拷贝构造和赋值的,而auto_ptr是不能满足这一条件的。

STL相关

多用算法调用,少用手写循环。

杂项优化

不要返回局部变量的引用。

双重循环时,短循环放在外面,长循环放在里面。

优化常规框架:profiling --> 算法 --> 数据结构 --> 实现细节。

代码的主要功能是供给别人阅读,其次才是让编译器编译,让计算机执行,除了修改代码外,还有很多不用修改代码就能带来的优化,作为一个有追求的程序员,多了解代码之外的优化和设计方法是很有必要的。

编写高质量代码_改善C++程序的150个建议 读书笔记的更多相关文章

  1. 《编写高质量代码:改善Java程序的151个建议》笔记

    To fight the unthinkable,you have to be willing to do the unthinkable.   不要在循环中使用try catch,应该放在循环的外面 ...

  2. 《编写高质量代码:改善Python程序的91个建议》读后感

    编写高质量代码:改善Python程序的91个建议  http://book.douban.com/subject/25910544/ 1.(建议16)is 用于判断两个对象的id是否相等,==才是判断 ...

  3. 编写高质量代码:改善Java程序的151个建议(第二章:基本类型)

    编写高质量代码:改善Java程序的151个建议(第二章:基本类型) 目录 建议21:用偶判断,不用奇判断 建议22:用整数类型处理货币 建议23:不要让类型默默转换 建议24:边界还是边界 建议25: ...

  4. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  5. 《编写高质量代码:改善C#程序的157个建议》源码下载

    ==== 目录 前 言第一部分 语言篇第1章 基本语言要素 / 2建议1:正确操作字符串 / 2建议2:使用默认转型方法 / 6建议3:区别对待强制转型与as和is / 9建议4:TryParse比P ...

  6. 读《编写高质量代码:改善JavaScript程序的188个建议》1

    建议3:减少全局变量污染 定义全局变量有3种方式: ❑在任何函数外面直接执行var语句. var f='value'; ❑直接添加一个属性到全局对象上.全局对象是所有全局变量的容器.在Web浏览器中, ...

  7. 读书笔记--《编写高质量代码:改善Python程序的91个建议》

      第一章 引论 建议写Pythonic式的代码,我理解为充分利用pythonAPI,用最简洁方式写出代码 1.两个变量交换: a, b = b, a 2.翻转list: a = [1, 2, 3, ...

  8. 编写高质量代码:改善C#程序的157个建议

    目录 前 言第一部分 语言篇第1章 基本语言要素 / 2建议1:正确操作字符串 / 2建议2:使用默认转型方法 / 6建议3:区别对待强制转型与as和is / 9建议4:TryParse比Parse好 ...

  9. 《编写高质量代码:改善JavaScript程序的188个建议》学习小记(二)

    建议3:减少全局变量污染 1.把多个全局变量都追加在一个名称空间下,将显著降低与其他应用程序产生冲突的概率,应用程序也会变得更容易阅读. var My = {}; My.name = { " ...

随机推荐

  1. Unity: Passing Constructor Parameters to Resolve

    In this tutorial we will go through of couple different ways of using custom constructor parameters ...

  2. 解决1130 Host 'localhost' is not allowed to connect to this MySQL server

  3. WPF 蒙层罩,正在加载

    参考园子里的一篇文章,比较好用.可以直接用,可以自己改. 动画效果: 容器的触发器,旋转容器: 属性配置:使用依赖属性,并且在xaml中写绑定.

  4. android XML解析器全解案例

    1.使用pull解析 package com.example.myxml; import java.io.InputStream; import java.util.ArrayList; import ...

  5. Wince6 RIL层移植

    RIL移植: 因为不同的模组,支持的AT命令有所不同,或是格式不一样,还有就是返回不一样,我们有必要对不同的模组进行RIL驱动移植. 在Response.cpp,Msg.cpp等 文件中ParseXX ...

  6. 给Cygwin重新安装curl

    之前已经安装过了cygwin了,但是重装了系统了. 不过发现cygwin倒是还可以继续使用. 现在想要使用其中的curl工具. 但是却在cygwin安装目录 E:\dev_install_root\c ...

  7. Zookeeper的安装和初步使用

    1. Zookeeper集群角色 Zookeeper集群的角色:  Leader 和  follower  (Observer) zk集群最好配成奇数个节点 只要集群中有半数以上节点存活,集群就能提供 ...

  8. [Kafka] |FAIL|rdkafka#producer-1 : Receive failed: Disconnected

    Why am I seeing Receive failed: Disconnected? 1. broker 的空闲连接回收器关闭不活跃连接. 由 broker 的配置属性 connections. ...

  9. Redis学习-string数据类型

    Redis 是一个开源的使用 ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志 型.Key-Value 数据库. redis提供五种数据类型string,hash,list,set及sor ...

  10. Angular $broadcast和$emit和$ond实现父子通信

    <!DOCTYPE html><html ng-app="myApp"><head lang="en"> <meta ...