读书笔记 | More Effective C++ | 基础议题

2017-07-30  本文已影响70人  yiekue
c++

个人阅读《More Effective C++》一书的笔记。

条款1:仔细区别pointers和references

注意:定义一个空指针的引用会引起未定义行为的问题

当考虑“不指向任何对象”的可能性时,或“不同时间指向不同对象”的能力时,考虑使用指针;确定“总会代表某个对象”而且“一旦确定就不在改变”时,就使用引用。

一般运算符重载常使用引用。

条款2:最好使用C++转型操作符

C++引入了4个新的转型操作符:static_cast,const_cast,dynamic_cast,reinterpret_cast。使用方式是static_cast<type>(expression) 。例如,要将一个 int 型转换为 double 型的值:

int firstNumber,secondNumber;
double result = static_cast<double>(firstNumber)/secondNumber;

如果编译器没有不支持这些语法,可以使用宏来模仿这些语法:

#define static_cast(TYPE,EXPR)  ((TYPE)(EXPR))
#define dynamic_cast(TYPE,EXPR)  ((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR)  ((TYPE)(EXPR))

条款3:绝对不要以动态(polymorphically)方式处理数组

继承(inheritance)的最重要的性质之一就是:指向基类对象的指针或引用,可以操作派生类对象。我们说这种指针和引用的行为是多态(polymorphically)的。虽然C++也允许通过基类对象的指针和引用来操作派生类的对象数组,但是程序却几乎无法如预期一样的运行。考虑如下的示例程序:

class BST {...};
class BalancedBST {...};

void printBSTArray(ostream& s, const BST array[], int numElements)
{
  for(int i = 0; i < numElements; ++i){
    s << array[i]; //假设BST对象有一个<<操作符可用
  }
}

BST BSTArray[10];
...
printBSTArray(cout, BSTArray, 10);//运行良好

BanlancedBST bBSTArray[10];
...
printBSTArray(cout, bBSTArray, 10);//能正确运行吗?

这段程序在编译时是没有问题的,但是运行程序时却很可能不会得到我们预想的结果。问题出在printBSTArray 函数里面的循环中,array[i] 其实是一个指针算术表达式,代表的是*(array+i) ,array是一个指向数组起始的指针。在内存中array+i 所指向的地址和array 所指向的地址相差i*sizeof(数组中对象) ,因为它们之间有i个对象。这里编译器假定array中的对象的大小和BST对象的大小是一致的,所以第一个printBSTArray函数运行良好;但是第二个却会出现问题,一般来说,派生类的具有更多的数据成员,因此派生类对象的大小一般都要比基类对象大,而这个程序中由于函数的参数设置,编译器还是认为对象大小为BST对象的大小。这样通过指针算术表达式算出来的地址就是错误的,从而产生意想不到的结果。

简单来说,就是多态和指针运算不能混用,数组对象几乎总会涉及到指针运算,因此数组和多态不要混用。

条款4:避免冗余的 default constructor

本条款的英文原文为:Avoid gratuitous default constructor

default constructor是指在没有任何外来信息的情况将对象初始化。有的类不需要外部信息即可完成初始化,例如数字可以默认的初始化为0或者无意义的值,指针可以初始化为null,但是有的类却必须要由外部的信息参与才可以完成初始化,对这种情况,如果没有default constructor将会在三种情况出问题:

由于缺乏default constructor 有这些缺点,所有就有人认为都应该为类提供一个default constructor。但是这样做几乎总是使得类的成员函数变得复杂。同时添加无意义的default constructor 会影响类的效率。

上一篇下一篇

猜你喜欢

热点阅读