• Effective C++学习记录


     

    1. 基础部分

    Item 1: View C++ as a federation of languages.

    对于内建类型,按值传递优于按引用传递,对于自定义类型则相反。

    C++可视为4中子语言的联合:C,Object-Oriented C++,Template C++和STL。

    Item 2: Prefer consts, enums, and inlines to #defines.

    对于单纯常量,最好用const, enum 对象替换宏定义#define

    enum类型也可以在类内部初始化。

    Item 3: Use const whenever possible.

    对于指针p,const在*左边时,p指向的值为常量,const出现在*右边时,p指向的地址为常量。

    const int * p1;
    int const * p2;
    int * const p3;
    ​
    //以上代码,p1和p2一样,其指向的值为常量;p3指向的地址为常量

    如果类Klass有同名的两个成员函数foo,其中一个为const类型,那么const实列优先调用const版的foo。

    const函数不能改变非static的成员变量。

    mutable修饰的成员,在const函数中也可以改变其值。

    在const和非const函数中避免代码重复:用非const版调用const版。

    从非const类型转换为const类型是安全的:static_cast<const Klass & >(kObj)。

    Item4: Make sure that objects are initialized before they're used.

    C++规定,类的非内建类型(类类型)成员在进入构造函数之前初始化。如果在构造函数中对对象成员赋值,那么在进入构造函数之前,该对象已经通过其对应的类的默认构造函数初始化。所以,构造函数的实现应该尽量使用成员初始化列表。

    类的数据成员是const类型或者引用类型,则必须放在初始化列表中。

    函数里面定义的static对象,称作本地静态对象。非本地静态对象(Non-local static object)的初始化顺序是不确定的。将非本地静态对象置于一个对应的函数中,并返回该对象,可以确保其初始化。更明确的说是:非const的static成员对象应该置于对应函数中并返回。

    2. 构造函数、析构函数、赋值运算符

    Item 5: Know what functions C++ silently writes and calls.

    如果定义的类中有引用成员,那么就要自己定义赋值操作符即operator=(...),已经引用了一个对象,无法再给其赋值。

    类中有const成员变量的情况也是如此。

    编译器会为你的类自动生成默构造和拷贝构造,析构,如果用户未定义的话。

    Item 6: Explicitly disallow the use of compiler-generated functions you do not want.

    将默认版本的赋值构造函数和赋值运算符声明在private里面,并且不定义它们,可以阻止调用默认版本。

    在C++11中可以直接在成员函数声明后面添加“= delete”,就可以阻止调用。

    Item 7: Declare destructors virtual in polymorphic base classes.

    如果一个类会被继承,就应该将其析构函数声明为virtual类型,这样才能实现多态。

    string类和所有的STL容器类型,都没有定义virtual析构函数,所以不要继承这些类。

    如果一个类不是设计为基类,即不被继承,或者不需要实现多态,那么就不要其析构函数声明为virtual类型。

    Item 8: Prevent exceptions from leaving destructors.

    一般来讲,静默处理异常不可取,可能会隐藏重要信息。

    会抛出异常的代码不要放在析构函数中。

    Item 9: Never call virtual functions during construction or destruction.

    构造函数和析构函数中不要调用任何虚函数,也不要调用那些调用虚函数的函数。

    Item 10: Have assignment operators return a reference to *this.

    Item 11: Handle assignment to self in operator=.

    赋值运算符重构时,一定要处理将自己赋值给自己的情况。

    Item 12: Copy all parts of an object.

    继承类(子类)在定义复制构造函数和赋值运算符时,必须考虑其基类部分。在定义其赋值运算符时,要显示调用其基类的赋值运算符。

    DerivedClass& DerivedClass::operator=(const DerivedClass& rhs)
    {
        BaseClass::operator=(rhs);//调用基类赋值运算符
        //处理继承类数据...
        
        return *this;
    }

    3. 资源管理

    Item 13: Use objects to manage resources.

    使用对象来管理资源(特别是指针),这正是C++11智能指针的思想:通过把指针放在对象中,对象的析构函数释放指针。

    如果手动释放资源,那么表明该做法不对,应该使用类似智能指针的方式来释放资源。

    Item 14: Think carefully about copying behavior in resource-managing classes.

    智能指针只支持堆空间分配的数据,因为智能指针在最后是通过delete释放指针对象。

    shared_ptr<>智能指针,还有第二个参数,deleter:shared_ptr<Klass>(p,deleter);

    拷贝一个资源管理对象时,应该进行深拷贝。

    Item 15: Provide access to raw resources in resource-managing classes.

    智能指针就是一种资源管理类,其指向的原始指针就是被管理的资源。

    智能指针的get()成员函数返回该智能指针所管理的原始指针。

    Item 16: Use the same form in corresponding uses of new and delete.

    typedef用来定义数组时和定义函数类型一样比较复杂,下面代码以定义一个包含4个int元素的数组。

    typedef int ip_addr[4];//注意[4]放在类型名之后
    ip_addr server_ip = {192,168,56,1};

    尽量不要对数组使用typedef,以免错用delete释放数组对象。

    delete数据要用 delete[]关键字。

    Item 17: Store newed objects in smart pointers in standalone statements.

    不要在给函数传参数时直接定义临时(匿名)的智能指针。应该先定义好智能指针对象,再将该对象传递给函数。

    4. 设计和声明

    Item 18: Make interfaces easy to use correctly and hard to use incorrectly.

    接口设计应该使得犯错很难!

    尽量让自定义类型和内建类型行为一致。

    与其让客户定义智能指针来管理返回的指针对象,不如直接返回智能指针。

    Item 19: Treat class design as type design.

    设计高效的类,考虑一下问题:

    • 类对象如何创建、销毁?

    • 对象初始化和赋值如何区别?

    • 类对象的按值传递意味着什么?复制构造函数。

    • 类的值有何限制条件?

    • 类是否适用于继承?

    • 类允许进行何种转换?

    • 新类可以定义哪些合理的运算符和函数?

    • 哪些标准函数应该被禁用?可以声明为private,或者在声明后面添加=delete。

    • 谁可以访问新类的成员?

    • 新类的非“未声明接口”有哪些?提供性能、异常安全。

    • 新类的通用性如何?也许可以定义成一个类模板。

    • 新类真的是必要的吗?

    Item 20: Prefer pass-by-reference-to-const to pass-by-value.

    对于内建类型和STL迭代器、函数对象,pass-by-value是高效的。

    Item 21: Don't try to return a reference when you must return an object.

    永远不要返回一个指向局部变量内存的指针或者引用。

    Item 22: Declare data members private.

    将数据成员声明为private,可以提供统一的访问途径,开发者更自由地定义类。

    Item 23: Prefer non-member non-friend functions to member functions.

    能访问数据成员的函数越多,封装性越差。

    类只提供基础操作,由外部函数、工具类提供便利、组合操作。

    Item 24: Declare non-member functions when type covnersions should apply to all parameters.

    如果能通过类的公共接口实现,尽量避免使用友元函数。如果需要为某个函数的所有参数进行类型转换,则提供non-memeber的函数

    Item 25: Consider support for a non-throwing swap.

    在自定义函数中,声明“using std::swap;”,编译器会先查找函数所在命名空间中自定义的具体化的swap函数,如果没有找到,则使用std中定义的swap函数模板。

    提高某些类中只需要交换指针的效率而写的成员swap

    如果默认的swap函数模板可以满足需要,就用默认的swap。否则,定义一个public成员函数版的swap,再定义一个non-member版的swap。non-member版的swap调用成员函数版的swap。

    5. 实现

    Item 26: Postpone variable definitions as long as possible.

    尽可能推迟定义变量,最好在需要初始化时定义,即定义时就立刻初始化。

    Item 27: Minimizing casting.

    const_cast<T>(obj):去除obj的const属性,obj应该时一个指针(地址)或者引用,对应用于接收const_cast的也应该是指针或者引用类型,这样才能就地修改obj的值。只有const_cast可以移除const属性。

    void castPoint(const Point& pp)
    {
        //const对象只能调用const函数,所以getX()原型后面应该有const;
        //const int Point::getX() const;
        cout << "Before cast:(" << pp.getX() << "," << pp.getY() << ")" << endl;
        Point& pp2=const_cast<Point&>(pp);
        pp2.setX(100);                    
        //或者
        //Point* pp2=const_cast<Point*>(&pp);
        //pp2->setX(100);
        
        //或者
        //const_cast<Point&>(pp).setX(100);
        cout << "After cast:(" << pp.getX() << "," << pp.getY() << ")" << endl;
    }

    迭代器本事就是一个指针,要取其所指向的值,必须解引用(dereference),即*iter。

    尽量避免使用cast,必须使用cast的情况下,应尽量将cast置于函数内部。

    Item 28: Avoid returning "handles" to object internals.

    handle指的是指针、引用、迭代器等。永远不要返回指向类内部成员的handle,否则容易产生悬挂(dangling)handle。

    Item 29: Strive for exception-free code.

    Item 30: Understand the ins and outs of inlining.

    inline函数如果足够短,那么编译后占用空间可能比调用函数还少。

    在类声明中定义的函数都是inline函数。

    inline函数一般在头文件中定义。大多数编译器不会对复杂的函数进行内联,如包含循环的函数。virtual函数也不会被内联。

    Item 31: Minimize compilation dependencies between files.

    尽量让头文件不依赖其他文件,以对声明的依赖取代对定义的依赖,如用前向声明和指针。

    尽量用指针或者引用,避免使用对象。

    通过纯虚函数的方式,定义接口。纯虚函数的实例(指向子类对象)应该以指针或引用的形式出现,因为不可能实例化一个纯虚函数。

    6. 继承和面向对象设计

    Item 32: Make sure public inheritance models "is-a".

    Item 33: Avoid hiding inherited names.

    如果基类中函数foo有几个重载版,子类只重新定义了其中一个版本,那么会隐藏基类中其他的重载版本。

    //子类中用 using,可以避免隐藏父类的name
    using Base::name1;
    using Base::name2;

    Item 34: Differentiate between inheritance of interface and inheritance of implementation.

    纯虚函数可以有定义,但是子类必须重载(override)该函数,唯一调用该纯虚函数的方法是通过类名:AbsClass::pureFunction().

    如果一个基类的成员函数是非虚函数,该函数在各个子类中应该表现一致,即不应被重载。非虚成员函数的目的就是让子类强制继承一致的实现。

    Item 35: Consider alternatives to virtual functions.

    模板方法模式:通过一个public非虚函数调用一个private虚函数。

    策略模式:strategy pattern

    Item 36: Never redefine an inherited non-virtual function.

    非虚成员函数的目的就是让子类强制继承一致的实现。

    Item 37: Never redifine a function's inherited default parameter value.

    虚函数如果有默认参数,子类中重新定义该虚函数时,不能改变该默认参数。

    Item 38: Model "has-a" or "is-implemented-in-terms-of" throught composition.

    Item 39: Use private inheritance judiciously(谨慎地).

    私有继承,基类的所有成员在子类中都变为private。

    私有继承,其实就是“is-implemented-in-terms-of”,只需要考虑继承基类的实现,基类的接口全忽略。

    尽量使用包含(composition),即”has-a“;必须使用时,才使用私有继承。

    Item 40: Use multiple inheritance judiciously.

    虚继承类的对象比非虚继承类的对象占用空间多一些,访问基类的数据成员速度更慢一些。

    7. Templates and Generic Programming

    Item 41: Understand implicit interfaces and compile-time polymorphism.

    编译期多态

    Item 42: Understand the two meanings of typename.

    Item 43: Know how to access names in templatized base classes.

    在子类模板中,如果要引用父类模板的name时,用this->name、using Base<T>::name声明或者基类限定(Base<T>::name)的方式。

    Item 44: Factor parameter-independent code out of templates.

    Item 45: Use member function templates to accept "all compatible types".

    STL容器的迭代器几乎都是智能指针。

    Item 46: Declare non-member functions inside templates when type conversions are desired.

    在类模板里面,对于函数参数为模板的名称的,可以省略<T> 部分。

    在类内部定义一个非成员函数的唯一方法是“友元”。

    Item 47: Use traits classes for information about types.

    Item 48: Be ware of template metaprogramming(TMP).

    元编程:编写编译期间执行的基于模板的C++程序。

    模板元程序(TMP)是在C++编译器内部执行的C++程序。TMP缺点是编译时间长。

    8. Customizing new and delete

    Item 49: Understand the behavior of the new-handler.

    如果new不能满足空间分配的需求,会抛出一个异常。头文件<new>中声明了一个处理该异常的函数.程序中可以使用std::set_new_handler(...)来给new指定处理异常的函数。

    //<new>
    namespace std{
        typedef void (*new_handler)();
        new_handler set_new_handler(new_handler p) throw();
    }

    Item 50: Understand when it makes sense to replace new and delete.

    Item 51: Adhere to convention when writing new and delete.

    只有new-handling函数为null的时候,new失败时会抛出异常。

    delete一个null指针是安全的。

    Item 52: Write placement delete if you write placement new.

    定位new运算符。

    如果定义了一个包含额外参数的new运算符,那么就应该定义一个包含同样额外参数的delete运算符。这样自定义的这个new运算符分配空间失败时,就可以通过该delete运算符来处理new分配的空间。

    定义了一个定位new运算符,就应该定义一个定位delete运算符和一个常规的delete运算符。

    9. Miscellany

    Item 53: Pay attention to compiler warnings.

    Item 54: Familiarize yourself with the standard library, including TR1.

    C++不允许王std名称空间添加任何东西。

    Item 55: Familiarize yourself with Boost.

    Boost is the most awesome api for the future of cpp

    modified from https://www.cnblogs.com/1994july/p/12442358.html

  • 相关阅读:
    sql删除重复数据,保留一条
    sql列转行
    异步线程:一次性发送大量邮件
    限制接口的访问次数
    Kibana(安装及其简单crud)
    Elasticsearch(简介及其单节点搭建)
    大数据(日志分析)项目
    大数据(sqoop)
    大数据(Hive数据库、表的详解及其Hive数据导入导出)
    大数据(Hive的MetaStore切换及其Hive的语法细节)
  • 原文地址:https://www.cnblogs.com/ChrisInsistPy/p/12991732.html
Copyright © 2020-2023  润新知