《Effective c++》读书笔记1

本章一共有四个条款:

  • 视c++为一个语言联邦
  • 尽量以constenuminline替换#define
  • 尽可能使用const
  • 确定对象被使用前已被初始化

视c++为一个语言联邦

  这个条款介绍c++总共由四个次语言部分组成,分别为:

  • C part of c++:这一点表示c++是以c为基础的,其中的区块(blocks)、语句(statements)、数组(arrays)和指针(pointers)都来自c。注意,c语言部分和专属于c++部分的初始化方式不一样。

  • Object-oriented c++: 这一部分即面向对象部分,包括封装、继承、多态和虚函数等等。

  • Template C++ :这是c++的泛型编程部分。

  • STL

尽量以const, enum, inline替换#define

  本条款也可称之为“宁以编译器替换预处理器”。#define与前三者的差别在于,它不被视为语言的一部分,在编译器处理源码之前就由预处理器来处理了,其所定义的名称不记入记号表。

  • 对于单纯常量,最好以const对象或enums替换#define
  • 对于形似函数的宏,最好改用inline函数替换#define

尽可能使用const

  对于关键字const,考虑其对指针的修饰:

const char* p=greeting;      //(1)
char const* p=greeting;      //(2)
char* const p=greeting;      //(3)

如果const出现在星号左边表示*p所指的字符串是常量,即不能对*p重新赋值,如(1)(2)式所示,且(1)(2)式的意义完全一样。如果const出现在星号右边表示指针p是常指针,即不能对p重新赋值使其指向其他的字符串。值得注意的是,在STL中迭代器是以指针为根据塑模出来的,所以迭代器的作用就像一个指针,声明迭代器为const与声明指针为const的含义一样,如果希望迭代器所指的东西为const则需要定义const_iterator,这里非常容易混淆!

  关于const关键词有一个很重要的概念,即const成员函数,并引出另外两个流行概念:bitwise constlogical constness.,其中bitwise const流派主张const成员函数不可以更改对象内任何non-static成员变量,考虑下述代码:

class CTextBlock
{
public:
    std::size_t length() const;             //(1)式

private:
    char* pText;                            //(2)式
    std::size_t textLength;                 //(3)式
    bool lengthIsValid;                     //(4)式
};

std::size_t CTextBlock::length() const
{
    if (!lengthIsValid)
    {
        textLength = std::strlen(pText);
        lengthIsValid = true;
    }
    char* temp= "Hello";
    pText =temp;                            //(5)式
    *pText = *temp;                         //(6)式
    return textLength;
}

其中(1)式的length()被声明为const成员函数,但是在函数的定义中对象的成员变量都被进行了重新赋值,所以上述代码无法通过编译。值得注意的是,(6)式改变了对象的某些bits,因为修改了指针指向的字符串,但是不幸的是它却能通过bitwise const的测试,事实上这就是所谓的logical constness。在有些时候,即使我们声明了const成员函数,我们也希望某些变量可以被重新赋值,可以通过关键字mutable释放掉bitwise constness约束。

class CTextBlock
{
public:
    std::size_t length() const;                     //(1)式

private:
    char* pText;                                    //(2)式
    mutable std::size_t textLength;                 //(3)式
    mutable bool lengthIsValid;                     //(4)式
};

std::size_t CTextBlock::length() const
{
    if (!lengthIsValid)
    {
        textLength = std::strlen(pText);
        lengthIsValid = true;
    }
    return textLength;
}

上述代码就可以正常通过编译。

在某些时候,我们可能在定义了一个const成员函数的同时也需要定义一个对应的non-const的成员函数,两者的功能代码可能会有大部分是重复的。为了避免重复代码,可以通过转型动作使得non-const成员函数调用const成员函数,注意不能反过来操作,因为会破坏const成员函数的bitwise constness约束。

class CTextBlock
{
public:
    const char& operator[](std::size_t position) const
    {
        ...
        ...
        ...
        return text[position];
    }

    char& operator[](std::size_t position)
    {
        return
            const_cast<char&>(static_cast<const CTextBlock&>(*this))[position];   //(1)式
    }
private:
    char* text;
};

其中(1)式经过了两次强制转型动作。

确定对象被使用前已先被初始化

  前面我们说过在c++的c语言部分和非c语言部分的初始化规则并不是相同的,在C part of C++中,如果初始化可能招致运行期成本那么就不保证发生初始化,一旦进入non-C parts of C++,规则就有些变化。这就是为什么array不保证其内容被初始化,而vector确有此保证。针对这种情况,一个保险的情况是:永远在使用对象之前先将它初始化。对于内置类型之外的任何其他东西,初始化责任落在构造函数身上,这里需要注意的是别混淆赋值初始化的区别。

class ABEntry
{
public:
    ABEntry(const std::string& name);

private:
    std::string theName;
};

ABEntry::ABEntry(const std::string& name) :theName(name)   //(成员初值列,初始化)
{
    //theName = name;                                      //(赋值操作)
}

上述代码展示了赋值操作与初始化的区别,c++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前,所以在本例中,如果成员变量theName是以赋值的方式进行“初始化”,那么他实际上进行的是:先调用theName自身的默认构造函数为其设初值,再用name给它赋予新值。而如果直接使用成员初值列的方式对其进行初始化执行的操作是:利用nametheName进行拷贝构造。后一种方法比前一种方法高效很多,所以建议尽可能使用成员初值列的方式进行初始化,值得注意的是:成员变量的初始化顺序只与其声明顺序有关,而与其成员初值列的顺序无关

所谓编译单元是指产出单一目标文件的那些源码,基本上它是单一源码文件加上其所含入的头文件。如果涉及至少两个源码文件,每一个内含至少一个non-local static对象,且某个non-local static对象的初始化使用了另一个编译单元的某个non-local static对象,它所用的这个对象可能尚未被初始化,因为c++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。

/*第一个源码文件*/
class FileSystem
{
public:
    std::size_t numDisks() const;
};
extern FileSystem tfs;
/*第二个源码文件*/
class Directory
{
public:
    Directory();
};
Directory::Directory()
{
    std::size_t disks = tfs.numDisks();
}
/*创建一个Directory对象*/
Directory tempDir();           //(1)式

上述代码中的(1)式,除非tfstempDir之前先被初始化,否则tempDir的构造函数会用到尚未初始化的tfs,但实际上这是无法保证的。解决这个问题的办法是:将每个non-local static对象 搬到自己的专属函数内,这些函数返回一个reference指向它所含的对象,然后用户调用这些函数。

/*第一个源码文件*/
class FileSystem
{
public:
    std::size_t numDisks() const;
};
FileSystem& tfs()
{
    static FileSystem fs;
    return fs;
}
/*第二个源码文件*/
class Directory
{
public:
    Directory();
};
Directory::Directory()
{
    std::size_t disks = tfs().numDisks();
}
Directory& tempDir()
{
    static Directory td;
    return td;
}

如上进行修改,调用的方式由直接使用tfstempDir改为tfs()tempDir()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,500评论 1 51
  • 1 让自己习惯 C++ 条款01:视 C++ 为一个语言联邦 将C++视为一个由相关语言组成的联邦而非单一语言。在...
    暗夜望月阅读 359评论 0 1
  • 1.C和C++的区别?C++的特性?面向对象编程的好处? 答:c++在c的基础上增添类,C是一个结构化语言,它的重...
    杰伦哎呦哎呦阅读 9,473评论 0 45
  • 1. 让自己习惯C++ 条款01:视C++为一个语言联邦 为了更好的理解C++,我们将C++分解为四个主要次语言:...
    Mr希灵阅读 2,778评论 0 13
  • 01.为什么要写作,写作的目的是什么? 在写作上,我是一个新手。自然刚开始时完全是自嗨型写作,想到那里就写到那里。...
    跑者子牛阅读 143评论 2 6