• 编写高质量代码_改善C++程序的150个建议 读书笔记


    这几天看了下这本书《编写高质量代码_改善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 --> 算法 --> 数据结构 --> 实现细节。

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

  • 相关阅读:
    Codeforces 1316B String Modification
    Codeforces 1305C Kuroni and Impossible Calculation
    Codeforces 1305B Kuroni and Simple Strings
    Codeforces 1321D Navigation System
    Codeforces 1321C Remove Adjacent
    Codeforces 1321B Journey Planning
    Operating systems Chapter 6
    Operating systems Chapter 5
    Abandoned country HDU
    Computer HDU
  • 原文地址:https://www.cnblogs.com/cherishui/p/9853282.html
Copyright © 2020-2023  润新知