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

c++

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

条款1:仔细区别pointers和references

  • pointer(指针): 可以为空(null),可以重新赋值,使用前需要测试其有效性。
  • refences(引用): 不可为空,必须初始化,无法重新赋值。

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

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

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

条款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;
  • static_cast: 基本上具有和旧式类型转换相同的能力和相同的限制。例如不能把struct 转成int 或将double转成指针。

  • const_cast: 可用于切仅可用于改变表达式中的常量性(constness)或易变性(volatileness),如果用于其他用途,转型动作会被拒绝。const_cast最常见的用途是用来去掉某个对象的常量性。例如:

    class Widget {...};
    class SpecialWidget: public Widget {...};
    void update(SpecialWidget *psw);
    SpecialWidget sw;                              //sw是一个non-const对象
    const SpecialWidget& csw=sw;                   //csw是一个sw的const引用
    
    update(&csw);                                  //错误!update函数要求一个const的参数
    
    update(const_cast<SpecialWidget*>(&csw));      //正确!&csw的常量性被取出掉了。
    update((SpecialWidget*)&csw);                  //同上,只是采用了旧式的转型语法。
    
    Widget *pw = new SpecialWidget;
    update(pw);                                      //错误!pw的类型是Widget*,但是update函数要求SpecialWidget*。
    
    update(const_cast<SpecialWidget*>(pw));        //错误!const_cast只能用来改变常量性或变易性,无法进行继承的向下转型(cast down)动作。
    
  • dynamic_cast: 用来指向继承体系中“安全的向下转型或跨系转型动作”。也就是将“指向基类对象(base class objects)的指针或引用”转型为“指向派生类对象的指针或对象”,并返回转型是否成功。如果转型失败,并以一个空指针(当转型对象是指针时),或一个异常(exception)(当转型对象是引用时)表现出来。dynamic_cast只能用于继承体系中,不能应用于缺乏虚函数的类型身上(条款24),也不能改变类型的常量性。例如:

    Widget *pw;
    ...
    update(dynamic_cast<SpecialWidget*>(pw));//正确!
    
  • reinterpret_cast: 这个操作符的转换结果总是和编译平台相关,所以reinterpret_cast不具有移植性。它最常用的用途是转换“函数指针”类型。由于其不具有移植性,所以某些情况这样的转型会导致不正确的结果,所以应该尽量避免将函数指针转型。

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

#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将会在三种情况出问题:

  • 产生数组的时候;当产生类的对象数组的时候,将会由于无法调用该类的构造函数而出错。
  • 无法适用于基于模板的容器类(template-based container classes)。对模板而言,被实例化的“目标类型”必须有一个default constructors,因为模板内几乎总是会产生一个以“模板类型参数” 作为类型二架构起来的数组。
  • 虚基类如果缺少default constructor,与之合作将会很痛苦。因为虚基类的构造函数的参数必须有派生层次最深的类提供,这就导致一个缺乏default constructor的虚基类的所有派生类都必须知道这个基类构造函数参数的意义,并提供这个参数。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容