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