【Effective C++ 笔记】让自己习惯C++

最近在读 Effective C++,想要做点笔记,归类和书中的每个模块一样,但跟模块里的具体顺序可能不太一致。不会对书中每个细节都涉及,主要记下自己觉得重要的内容。

What is C++?

C++ 是一个多重范型编程语言( multiparadigm programming language),一个同时支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(genetic)、元编程形式(metaprogramming)的语言。

我们 可以视 C++ 为主要的四个次语言组成,包括:C、Object-Oriented C++、Template C++、STL。(觉得这个归类好有道理...废话...)

const 那些事

语法:如果关键字 const 出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

STL 迭代器的作用就像个 T* 指针,声明迭代器为 const 就像声明指针为 const 一样(即声明一个 T* const 指针),表示这个迭代器不得指向不同的东西,但所指的东西的值是可以改动的。如果要迭代器所指的东西不可被改动(即希望 STL 模拟一个 const T* 指针),则要用 const_iterator.

     const vector<int>::iterator iter = vec.begin(); // 所指物可变,迭代器不可变
     vector<int>::const_iterator cIter = vec.begin(); // 所指物不可变,迭代器可变

尽可能不要用 #define 定义常量,而是用 const 变量。

定义常量指针时有必要将指针(而不只是指针所指之物)声明为 const。(成为指向 const 的 const 指针)

class 专属常量应该使其成为 class 的一个 static 成员,这样既可以限制常量作用域,而且只有一个实体。

令函数返回一个常量值,可以降低因客户错误造成的意外,又不至于放弃安全性和高效性。

例如重载运算符 *,加入返回的不是 const, 可能会出现这样的错误。

if (a*b = c) ...  // 将比较误写成了赋值

两个成员函数如果只是常量性不同,可以被重载。例如:

     const char& operator[](size_t position) const { }
     char& operator[](size_t position){ }

bitwise constness

成员函数只有在不更改对象之任何成员变量时才可以说是 const。bitwise constness 正是 C++ 对常量性的定义。

结果是很多成员函数不十足具备 const 性质,却能通过编译。例如 char* 属于对象而非其所指物,那么就可以通过成员函数返回的指针去修改其所指物,可以通过编译器检测。

logical constness:

一个成员函数可以修改它所处理的对象内的某些 bits,但只有在客户端侦测不出的情况下才行。

要在 const 成员里修改数据,需要将变量声明为 mutable。mutable 释放掉 non-static 成员变量的 bitwise constness 约束。例如: mutable size_t textLength;

编译器强制实施 bitwise constness,但你编写程序时应该使用“概念上的常量性”。

const 与 非 const  的复用

当 const 和 non-const 成员函数有着实质等价的实现时,令 non-const 版本调用 const 版本代码可避免重复。

注意:const 成员函数调用 non-const 成员函数是一种错误行为。

class TextBlock{
     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 TextBlock&>(*this)[position]
                         );    //要看懂这个转化
          }
}

函数的 little tips

参数传递:

对于内置(也就是 C-like)类型而言 pass-by-value 通常比 pass-by-reference 高效。在 Object-Oriented C++ 中,由于用户自定义构造函数和析构函数的存在, pass-by-reference-to-const 往往更好,Template C++ 也如是。而对 STL 的迭代器和函数对象而言,旧式的 C pass-by-value 更加适用。

inline:用 inline 函数代替形似函数的宏,以避免不必要的错误。

对象初始化

为内置对象进行手工初始化,因为 C++ 不保证初始化它们。

构造函数最好使用成员初始初值列(member initialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序相同。使用成员初值列有时候绝对必要,又往往比赋值更高效。

class 的成员变量总是以其声明次序被初始化。为避免问题,你初始化的顺序应该和声明的顺序一样。

为免除”跨编译单元之初始化次序“问题,请以 local static 对象替换 non-local static 对象。因为 C++ 对”定义于不同的编译单元内的 non-local static 对象“初始化相对顺序并无明确定义,要消除该问题,应该如下设计:将每个 non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为 static )。C++ 保证,函数内的 local static 对象会在”该函数被调用期间“”首次遇上该对象之定义式“时被初始化。

class FileSystem{};

FileSystem& tfs()
{
     static FileSystem fs;
     return fs;
}

class Directory{...};
Directory::Directory(params){
     ...;
     std::size_t disks = tfs().numDisks();
     ...;
}

Directory& tempDir()
{
     static Directory td;
     return td;
}

然而,任何一种 non-const static 对象,不论它是 local 还是 non-local,在多线程的环境下”等待某事发生“都会有麻烦,处理这个麻烦的一个做法是:

在程序的单线程启动阶段(single-threaded startup portion)手工调用所有 reference-returning 函数,这可消除与初始化有关的"竞速形势”(race condition)。

时间: 2024-08-29 20:24:42

【Effective C++ 笔记】让自己习惯C++的相关文章

Effective c++(笔记) 之 类与函数的设计声明中常遇到的问题

1.当我们开始去敲代码的时候,想过这个问题么?怎么去设计一个类? 或者对于程序员来说,写代码真的就如同搬砖一样,每天都干的事情,但是我们是否曾想过,在c++的代码中怎么样去设计一个类?我觉得这个问题可比我们"搬砖"重要的多,大家说不是么? 这个答案在本博客中会细细道来,当我们设计一个类时,其实会出现很多问题,例如:我们是否应该在类中编写copy constructor 和assignment运算符(这个上篇博客中已说明),另外,我们是让编写的函数成为类的成员函数还是友元还是非成员函数,

Effective c++(笔记)----类与函数之实现

上篇博客中集中说明了在设计一个类的时候常遇到的问题,当然博客中还夹杂着我随时想到的一些知识,发现自己写博客没很多人写的好,可能是自己语言不会组织,要么就是写的东西大家不愿意看,反正是有这方面的专业问题或者博客中有什么明显的错误和问题,大家提出来,我也好改进哈! 回归正题,这篇博客就大概的把Effective c++中类与函数这节看到的知识点做个笔记. 设计好一个类后,自己就要去实现这个类(实现类中的成员函数.友元.非成员函数等) 可能大家会遇到以下问题 1.在类的成员函数中,尽量避免返回内部数据

Effective C++笔记:构造/析构/赋值运算

条款05:了解C++默默编写并调用哪些函数 默认构造函数.拷贝构造函数.拷贝赋值函数.析构函数构成了一个类的脊梁,只有良好的处理这些函数的定义才能保证类的设计良好性. 当我们没有人为的定义上面的几个函数时,编译器会给我们构造默认的. 当成员变量里有const对象或引用类型时,编译器会不能合成默认的拷贝赋值函数:当一个基类把它的拷贝赋值函数定义为private时,它的派生类也不无生成默认的拷贝赋值函数,因为它无法完成基类成份的赋值. 条款06:若不想使用编译器自动生成的函数,就该明确拒绝 将拷贝构

Effective C++笔记06:继承与面向对象设计

关于OOP 博客地址:http://blog.csdn.net/cv_ronny 转载请注明出处! 1,继承可以是单一继承或多重继承,每一个继承连接可以是public.protected或private,也可以是virtual或non-virtual. 2,成员函数的各个选项:virtual或non-virtual或pure-virtual. 3,成员函数和其他语言特性的交互影响:缺省参数值与virtual函数有什么交互影响?继承如何影响C++的名称查找规则?设计选项有如些?如果class的行为

Effective c++(笔记)之继承关系与面向对象设计

1.公有继承(public inheritance) 意味着"是一种"(isa)的关系 解析:一定要深刻理解这句话的含义,不要认为这大家都知道,本来我也这样认为,当我看完这章后,就不这样认为了. 公有继承可以这样理解,如果令class D以public 的形式继承了class B ,那么可以这样认为,每一个类型为D的对象同时也可以认为是类型为B的对象,但反过来是不成立的,对象D是更特殊化更具体的的概念,而B是更一般化的概念,每一件事情只要能够施行于基类对象身上,就一定可以应用于派生类对

Effective C++笔记05:实现

条款26:尽可能延后变量定义式的出现时间 博客地址:http://blog.csdn.net/cv_ronny 转载请注明出处! 有些对象,你可能过早的定义它,而在代码执行的过程中发生了导常,造成了开始定义的对象并没有被使用,而付出了构造成本来析构成本. 所以我们应该在定义对象时,尽可能的延后,甚至直到非得使用该变量前一刻为止,应该尝试延后这份定义直到能够给它初值实参为止. 这样做的好处是:不仅可以避免构造(析构)非必要对象,还可以避免无意义的default构造行为. 遇到循环怎么办?此时往往我

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

java effective 读书笔记

java effective 读书笔记 [1]创建和销毁对象 1 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 2 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 3 枚举 singleton 不知道怎么操作,觉得意义不大 单例模式 4 私有化构造器不能实例化,也不可被子类继承 5 能用原生类的就尽量不用对象 [2]对于所有对象都通用的方法 reflexivity 自反性 symmetry 对称性 [3]类成员 降低可访问性 尽量把公有域 变成私有域,并提供

Effective c++(笔记) 之 杂项讨论

看到了Effective c++的最后一章,最开始的那章---内存管理还没搞清楚,准备那章搞清楚完也写篇博客,不管怎样,有好的开始就应该让它有个完美的结束,杂项讨论这章是作者将那些分到哪章都不合适的就索性放到了最后讨论,我看完后从中摘出自己认为重要的坐下笔记,如果能帮得到大家,那就更荣幸了哈! 1.当我们定义一个类时,编译器会自动给我产生哪些成员函数? 解析:我们都知道,当我们定义类时,如果我们没有定义某些成员函数的话,编译器会总会给我们自动合成,这就是编译器默默为我们完成和调用函数,这些函数主