《C++编程规范》读书笔记(中)

来源:互联网 发布:性感淘宝买家秀的网店 编辑:程序博客网 时间:2024/04/29 12:02

 《C++编程规范——101条规则、准则与最佳实践》(C++ Coding Standards——101 Rules, Guidelines and Best Practices)
类的设计与继承

第32条(C):弄清所要编写的是哪种类
第33条(C):用小类代替巨类

    分而治之。用类表示概念。

第34条(B):用组合代替继承

     即优先使用委托而非继承。

第35条(B):避免从并非要设计成基类的类中继承

    本意是要独立使用的类所遵守的设计蓝图和基类大不相同,将独立类用作基类是一种严重的设计错误。

第36条(C):优先提供抽象接口

    偏爱抽象艺术吧。抽象接口是完全由(纯)虚函数构成的抽象类,没有状态(即没有成员数据),通常也没有成员函数实现。注意:在抽象接口中避免使用状态能够简化整个层次结构的设计。

    依赖倒置原理(DIP):1)高层模块不应该依赖低层模块。相反,两者都应该依赖于抽象。2)抽象不应该依赖细节(实现)。相反,细节应该依赖抽象。通常说的“要面向接口编程,而不要面向实现编程”也是这个意思。

第37条(C):公有继承即可替换性。继承,不是为了重用,而是为了被重用

    继承塑模的是“是一个(is a kind of )”关系【Liskov替换原则】。组合塑模的是“有一个(has a kind of )”关系。

第38条(D):实施安全的覆盖

第39条(D):考虑将虚拟函数声明为非公有的,将公有函数声明为非虚拟的

     在基类中进行修改代价是高昂的(尤其对于框架或库)。非虚拟接口模式(NonVirtual Interface, NVI)。

第40条(A):要避免提供隐式转换

     explicit构造函数和命名的转换函数。

第41条(A):将数据成员设为私有的,无行为的聚集除外(即C语言形式的struct)

     保护数据具有公有数据的所有缺点。可以考虑使用Pimpl惯用法用来隐藏类的私有数据成员。

第42条(C):不要公开内部数据

     隐藏数据却又暴露句柄是一种自欺欺人的做法,就像锁上了自家的门,却把钥匙留在了锁上。

第43条(D):明智的使用Pimpl惯用法

// 将类的私有数据隐藏在一个不透明的指针后面class Map{ public:   // .... 接口private:      struct PrivateImpl; // 类Map的嵌套类型     shared_ptr<PrivateImpl> m_Impl;};


第44条(D):优先编写非成员非友元函数    

       要避免较成员费:尽可能优先指定为非成员非友元函数。1)减少依赖;2)分离巨类;3)提供通用性。

第45条(D):总是一起提供new和delete

第46条(D):如果提供类专门的new,应该提供所有的标准形式(普通,就地和不抛出)

构造、析构与复制

第47条(A):以同样的顺序定义和初始化成员变量     

       如果违反了该条规则,也会违反第1条 在高警告级别下干净利落地进行编译。

第48条(A):在构造函数中用初始化代替赋值     

       这可不是不成熟的优化,这是在避免不成熟的劣化。

第49条(B):避免在构造函数和析构函数中调用虚拟函数     

        这一点其实很好理解:因为在构造期间,对象还是不完整的,如果在基类的构造函数中调用了虚拟函数,那么调用的将是基类的虚拟函数(不管派生类是否对该虚拟函数进行了改写)。C++标准为什么这样?试想:如果调用的是派生类改写后的虚拟函数版本,那么会发生什么事情?派生类改写该虚拟函数势必会调用派生类的成员数据吧?而在构造基类期间,派生类的数据成员还没有被初始化,使用了未初始化的数据,正是通往未定义行为的快速列车。

// 使用工厂函数插入“后构造函数”调用class B{  protect:   B() {/*.... */ }   virtual void PostInitialize() {/*....*/}  public:    template<typename T>   static shared_ptr<T> create() // 函数模板   {     shared_ptr<T> p(new T);     p->PostInitialize();     return p;   }};class D : public B{// ....};shared_ptr<D> pD = D::create<D>();  // 创建一个D的对象


第50条(A):将基类析构函数设为公用且虚拟的,或者保护且非虚拟的

第51条(D):析构函数、释放和交换绝对不能失败

第52条(D):一致地进行复制和销毁   

        通常,拷贝构造函数,复制赋值操作符函数,析构函数要么都定义,要么都不定义。

第53条(D):显示地启用或者禁止复制

第54条(D):避免切片。在基类中考虑用克隆代替复制

       将基类的拷贝构造函数声明为受保护的protected, 这样就不能将派生类对象直接传递给接收基类对象的函数,从而防止了对象切片。取而代之在基类中增加一个克隆函数clone()的定义,并采用NVI模式实现。在公有的非虚拟接口clone()函数中采用断言检查继承自基类的所有派生类是否忘记了重写virtual B *doClone()。

第55条(D):使用赋值的标准形式

第56条(D):只要可行,就提供不会失败的swap()(而且要正确的提供)

模板与泛型

第64条(C):理智的结合静态多态性和动态多态性

       动态多态性是以某些类的继承体系出现的,通过虚拟函数和(指向继承层次中的对象的)指针或引用来实现的。静态多态性则是通过类模板和函数模板实现。

第65条(D):有意的进行显示自定义

第66条(D):不要特化函数模板

第67条(D):不要无意地编写不通用的代码

STL:容器

第76条(A):默认时使用vetor。否则,选择其他合适的容器

第77条(B):从vector和string代替数组

第78条(A):使用vector和string::c_str与非C++的API交换数据

       vector的存储区总是连续的;大多数标准库对string的实现,也使用连续内存区(但是不能得到保证)。string::c_str总是返回一个空字符'\0'结束的C风格字符串。string::data也是返回指向连续内存的指针,但不保证以空字符'\0'结束。

第79条(D):在容器中只存储值和智能指针

第80条(B):用push_pack代替其他扩展序列的方式

第81条(D):多用范围操作,少用单元素操作

第82条(D):使用公认的惯用法真正的压缩容量,真正的删除元素

container<T>(c).swap(c); // 去除多余容量的

shrink-to-fit惯用法container<T>().swap(c); // 清空容器c

c.erase(remove(c.begin(), c.end(), value), c.end()); // 删除容器c中所有等于value的元素, erase-remove惯用法

STL:算法

算法即循环——只是更好。算法是循环的模式。开始使用算法,也就意味着开始使用函数对象和谓词。

第83条(D):使用带检查的STL实现

      什么是带检查的STL实现?

第84条(C):用算法调用代替手工编写的循环

      有意识的熟悉,使用STL算法吧。

第85条(C):使用正确的STL查找算法

      find/find_if, count/count_if, binary_search, lower_bound, upper_bound, equal_range

第86条(C):使用正确的STL排序算法

      partition, stable_partition, nth_element, partial_sort, partial_sort_copy, sort, stable_sort

第87条(C):使谓词成为纯函数

第88条(C):算法和比较器的参数应多用函数对象少用函数

第89条(D):正确编写函数对象 

       模板与泛型编程,C++标准模板库STL一直是自己很薄弱的地方,因为在工作中很少使用。这一来是因为自己起初就对这一块不熟悉,进而导致编程时很少使用(都不知道用有哪些功能啊),而越是这样,使用得越少,就更没有机会是熟悉STL,正是形成一个循环。STL有很多的实用功能,以后要有意识的加以使用,学习,争取掌握它。

原创粉丝点击