抄来的,很多人转,不知哪里是原创...
语言本身并没有限制变量名的长度,但考虑到将会阅读和/或修改我们的代码的其他人,变量名不应太长
命名习惯最重要的是保持一致
初始化不是赋值。初始化是指创建变量并给它赋初值,而赋值则是擦除对象的当前值并用新值代替
变量必须且仅能定义一次,而且在使用变量之前必须定义或声明变量
通常把一个对象定义在它首次使用的地方是一个很好的办法
头文件应该含有保护符,即使这些头文件不会被其他头文件包含
头文件中应该只定义确实必要的东西
任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量
如果可能的话,除非所指向的对象已经存在,否则不要先定义指针,这样可避免定义一个未初始化的指针。如果必须分开所定义指针和其所指向的对象,则将指针初始化为0
尽管C++支持C风格字符串,但不应该在C++程序中使用这个类型。C风格字符串常常带来许多错误,是导致大量安全问题的根源,应尽量使用string
一般而言,标准库提供的bitset操作更直接、更容易阅读和书写、正确使用的可能性更高。而且,bitset对象的大小不变unsigned数的位数限制。通常来说,bitset优于整形数据的低级直接位操作
应该尽量使用前置操作符(如++i而非i++),原因在于前置操作需要做的工作更少,只需加1后返回左值即可,而后置操作符则必须先保存操作数原来的值,以便返回未加1之前的值作为操作的结果
正如我们总是要初始化定义为变量的对象一样,在动态创建对象时,总是对它做初始化也是一个好方法
一旦删除了指针所指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象
void,可以保存任何类型对象的地址,可以向函数传递void指针或从函数返回void指针以及给另一个void指针赋值,但不允许使用void*指针操纵它所指向的对象
虽然标准C++仍然支持旧式强制转换符号,但是我们建议,只有在C语言或标准C++之前的编译器上编写代码时,才使用这种语法
如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const引用
应该将不需要修改的引用形参定义为const引用。普通的非const引用形参在使用时不太灵活。这样的形参既不能用const对象初始化,也不能用字面值或产生右值的表达式实参初始化
通常,函数不应该有vector或其他标准库容器类型的形参。调用含有普通的非引用vector形参的函数将会复制vector的每一个元素,事实上,C++程序员倾向于传递指向容器中需要处理的元素的迭代器来传递容器
定义函数的源文件应包含声明该函数的头文件
在实际应用中,调用重载函数时应尽量避免对实参做强制类型转换,需要使用强制类型转换意味着所设计的形参集合不合理
使用迭代器编写程序时,必须留意哪些操作会使迭代器失效。使用无效迭代器将会导致严重的运行时错误
通常来说,除非找到选择使用其他容器的更好理由,否则vector容器都是最佳选择
本质上,适配器是使一事物的行为类似与另一事物行为的一种机制
stack适配器所关联的基础容器可以是任意一种顺序容器类型,而queue适配器要求关联的基础容器必须提供push_front运算,priority_queue适配器要求提供随机访问功能
通常,如果要以一个已存在的容器为副本创建新容器,更好的方法是直接用输入范围作为新构造容器的初始化式
迭代器的纯抽象概念:任何东西,只要行为类似迭代器,就是一种迭代器
对于list对象,应该优先使用list容器特有的成员版本,而不是泛型算法
在普通的非const成员函数中,this的类型是一个指向类类型的const指针,而在const成员函数中,this的类型是一个指向const类类型对象的const指针
我们更喜欢使用默认实参,因为它减少代码重复(构造函数)
实际上,如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的,通常,在默认构造函数中给成员提供的初始值应该指出该对象是“空”的
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit,将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显式地构造对象
定义和使用构造函数几乎总是较好的,当我们为自己定义的类型提供一个默认构造函数时,允许编译器自动运行那个构造函数,以保证每个类对象在初次使用之前正确地初始化
对许多类而言,只包含类类型成员或内置类型(非指针)成员的类,无须显式地定义复制构造函数;然而,有些类必须对复制对象时发生的事情加以控制,这样的类经常有一个数据成员是指针或者有成员表示在构造函数中分配的其他资源,而另一些类在创建新对象时必须做一些特定的工作
一般来说,最好显式或隐式定义默认构造函数和复制构造函数,只有不存在其他构造函数时才合成默认构造函数,如果定义了复制构造函数,也必须定义默认构造函数
当操作符为成员函数时,它的第一个操作符隐式绑定到this指针
一般而言,如果类需要复制构造函数,它也会需要赋值操作符
为了防止复制,类必须显式声明其复制构造函数为private,此时,类的友元和成员仍可以进行复制,如果想要连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义
动态分配的对象只有在指向对象的指针被删除时才撤销,如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄露,而且,对象内部使用的任何资源也不会释放
当对象的引用或指针超出作用域时,不会运行析构函数,只有删除指向动态分配对象的指针或实际对象(而不是对象的引用)超出作用域时,才会运行析构函数
对象构造三法则:如果类需要析构函数,则它也需要赋值操作符和复制构造函数
赋值操作符通常要做复制构造函数和析构函数也要完成的工作,在这种情况下,通用工作应放在private实用函数中
定义复制控制函数最为困难的部分通常在于认识到它们的必要性,分配内存或其他资源的类几乎总是需要定义复制控制成员来管理所分配的资源
一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符
为了与内置操作符保持一致,加法返回一个右值,而非引用
既定义了算术操作符又定义了相关复合赋值操作符的类,一般应使用复合赋值实现算术操作符
一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用
下标操作符必须为类成员函数
自增与自减操作符倾向于作为类成员
后缀式操作符函数接受一个额外的(无用的)int形参作为区分
类定义下标和解引用操作符时,一般需要定义两个版本,一个为非const成员并返回引用,另一不为const成员 并返回const引用
转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空
转换函数一般不应该改变被转换的对象,因此,转换操作符通常应定义为const成员
类类型转换之后不能再跟另一个类类型转换,如果需要多个类类型转换,则代码将出错
基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数
通过基类的引用(或指针)调用虚函数时,发生动态绑定,引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键,用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的
派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限
C++不要求编译器将对象的基类部分和派生类部分连续排列
要触发动态绑定,必须满足两个条件:第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;第二,必须通过基类类型的引用或指针进行函数调用
引用和指针的静态类型与动态类型可以不同,这是C++用以支持多态性的基石
友元关系不能继承
如果基类定义了static成员,则整个继承层次中只有一个这样的成员
析构函数的工作于复制构造函数和赋值操作符不同:派生类析构函数不负责撤销基类对象的成员,编译器总是显式调用派生类对象基类部分的析构函数,每个析构函数只负责消除自己的成员
基类析构函数是三法则的一个重要例外,实际上,即使析构函数没有工作要做,继承层次的跟类也应该定义一个虚析构函数
设计派生类时,只要可能,最好避免与基类成员的名字冲突
派生类不用重定义所继承的每一个基类版本,它可以为重载成员提供using声明,一个using声明只能指定一个名字,不能指定形参表,因此,为基类成员函数名称而作的using声明将该函数的所有重载实例加到派生类的作用域,将所有名字加入作用域之后,派生类只需要重定义本类型确实必须定义的那些函数,对其他版本可以使用继承的定义
含有(或继承)一个或多个纯虚函数的类是抽象基类,除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象
如果拿不准是否需要以typename指明一个名字是一个类型,那么指定它是个好主意,在类型之前指定typename 没有害处
类模板成员函数的定义形式:必须以关键字template开头,后接类的模板形参表;必须指出它是哪个类的成员;类名必须包含其模板形参
当在类模板作用域外部定义成员模板的时候,必须包含两个模板形参表
函数模板的特化类型的返回值类型应与通用类型返回值一致
类模版特化可以定义与模板本身完全不同的成员,如果一个特化无法从模板定义某个成员,该特化类型的对象就不能使用该成员,类模板的定义不会用于创建显式特化成员的定义
异常对象由throw创建,并被初始化为被抛出的表达式的副本,异常对象将传给对应的catch,并且在完全处理了异常之后撤销
异常对象通过复制被抛出表达式的结果创建,该结果必须是可以复制的类型
抛出指针通常是个坏主意,执行throw后,如果接受throw的catch不在同一函数,则会发生,沿着调用链的函数提早结束,抛出异常的块中的局部存储不存在了
栈展开期间,释放局部对象所用的内存并运行类类型局部对象的析构函数
如果一个块直接分配资源,而且在释放资源之前发生异常,在栈展开期间将不会释放该资源
析构函数应该从不抛出异常,因为,在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未经处理的另一个异常,将会导致调用标准库terminate函数,后者调用abort函数,一般导致程序的非正常结束
与析构函数不同,构造函数内部所做的事情经常会抛出异常
不能不处理异常,异常是足够重要的,使程序不能继续正常执行的事件,如果找不到匹配的catch,程序就调用库函数terminate
异常说明符的类型决定了处理代码能够捕获的异常种类,类型必须是完全类型,即必须是内置类型或者是已经定义的程序员自定义类型,类型的前向声明不行
当catch为了处理异常只需要了解异常的类型的时候,异常说明符可以省略形参名,如果处理代码需要已发生异常的类型之外的信息,则异常说明符就包含形参名,catch使用这个名字访问异常对象
在查找catch期间,找到的catch不必是与异常最匹配的那个catch,相反,将选中第一个找到的可以处理该异常的catch,因此,在catch子句列表中,最特殊的catch必须最先出现
通常,如果catch子句处理因继承而相关的类型的异常,它就应该将自己的形参定义为引用
空异常说明列表指出函数不抛出任何异常,如果一个函数声明没有指定异常说明,则该函数可以抛出任意类型的异常
异常说明是函数接口的一部分,函数定义以及该函数的任意声明必须具有相同的异常说明
如果函数抛出了没有在其异常说明中列出的异常,就会调用标准库函数unexpected,后者会调用terminate函数
异常说明有用的一种重要情况是,如果函数可以保证不抛出任何异常
基类中虚函数的异常说明,可以与派生类中对应虚函数的异常说明不同,但派生类虚函数的异常说明必须与对应基类函数的异常说明同样严格,或更受限,即派生类的异常说明不会增加新的可抛出异常
在用另一指针初始化带异常说明的函数指针,或将后者赋值给函数地址的时候,两个指针的异常说明不必相同,但源指针的异常说明必须至少与目标指针的一样严格
91.通过定义一个类来封装资源的分配和释放,可以保证正确释放资源,这一技术即RAII(资源分配即初始化)
应该设计资源管理类,以便构造函数分配资源而析构函数释放资源
可能存在异常的程序以及分配资源的程序应该使用类来管理那些资源,这样可以保证如果发生异常就释放资源
命名空间可以在几个部分中定义,命名空间由它的分离定义部分的总和构成,命名空间是累积的
定义多个不相关类型的命名空间应该使用分离的文件,表示该命名空间定义的每个类型
C++不赞成文件静态声明,不赞成的特征是在未来版本中可能不支持的特征,应该避免文件静态而使用未命名的命名空间代替
除了在函数或其他作用域内部,头文件不应该包含using指示或using声明,在其顶级作用域包含using指示或using声明的头文件,具有将该名字注入包含该头文件的文件中的效果,头文件应该只定义作为其接口的一部分的名字,不要定义在其实现中的名字
对于带虚函数的类,在运行时执行RTTI操作符,但对于其他类型,在编译时计算RTTI操作符
在条件中执行dynamic_cast保证了转换和其结果测试在一个表达式中进行
只有当typeid的操作数是带虚函数的类类型的对象的时候(基类指针的解引用),才返回动态类型信息,测试指针(相对于指针指向的对象)返回指针的静态的,编译时类型