Effective C++ 笔记

原创 2015年11月18日 15:45:23

item2: Prefer consts, enums, and inlines to #defines.

reason

  • define会造成代码膨胀,目标代码里替换所有define为同一个东西,重复。
  • define通常没有scope的概念,也没有封装的概念
  • define出来的函数有隐含的出错风险。
#define MAX(a, b) (a) > (b) ? (a) : (b);
void f() {
    int a(1), b(0);
    MAX(++a, b);        // a 被累加两次
    MAX(++a, b+10); // a 被累加1次
}

conclusion

  • 常量:使用const或enum代替
  • 函数形式的宏:改用inline函数代替

item3: Use const whenever possible.

reason

  • 避免不必要的错误,帮助编译器侦测出错误的用法。对象、函数参数、返回类型、成员函数,都可以用const来修饰。
  • 对于member function,const 函数可以使const对象可以调用。

conclusion

  • 编译器只保证bitwise constness,程序员应该做到logical constness(const成员函数不应该返回可以修改对象内部状态的引用或指针)
  • 当const和non-const版本的函数有等价的实现的时候,让non-const版本调用const版本
const int& value() const {
    // ...
}
int& value() {
    return const_cast<int&>(static_cast<const T&>(*this).value());
}

item4: Make sure that objects are initialized before they are used.

conclusion

  • 使用member initialization list初始化类成员,以声明次序为准。

  • “跨编译单元的初始化次序”问题,把non-local static对象替换为local static对象。使用reference-returning函数。

//---------------------------- use local static ----------------------------
// non-local static object in A.cpp
static Object a;
// used in B.cpp
static ObjectB b = a.getB(); // a may be uninitialized.

//---------------------------- reference-returning: local static ----------------------------
Object& getA() {
    static Object a;
   return a;
}

ObjectB& getB() {
    static ObjectB b = getA().getB();
   return b;
}

item7: Declare destructors virtual in polymorphic base class.

reason

  • avoid resource leak.

conclusion

  • 为polymorphic base class声明一个virtual析构函数。如果base class带有任何的virtual函数,它就应该有一个virtual析构函数

polymorphic base class是指:为实现“要通过base class接口来调用derived class对象”而设计的基类。

  • 不是为了继承而定义的base class就不需要声明virtual析构函数,浪费空间(带virtual函数需要该class有vtbl)。并且不要继承这种不带虚析构函数的类(比如stl里面的容器)。

item9: Never call virtual functions during construction or destruction.

reason

  • 在base class构造(析构)期间,virtual函数不是virtual函数。

item10: Have assignment operators return a reference to *this.

reason

  • 可以实现连锁赋值的效果。非强制,但是这样可以与内置类型和stl保持一致。

item11: Hanle assignment to self in operator=.

conclusion

  • 使用证同测试(identity test), ( if (this == &rhs) return *this;)
  • 为了异常安全性(exception safety),调整语句执行顺序
Object& Object::operator=(const Object& rhs) {
    Mem * pOrig = mem_;         // save original mem;
    mem_ = new Mem(*rhs.mem_);  // if throw exception here, mem_ is still available.
    delete pOrig;
    return *this;
}
  • copy-and-swap 技术
class Object {
    void swap(Object& rhs);
};
Object& Object::operator=(const Object& rhs) {
    Object temp(rhs);
    swap(temp);
    return *this;
}

item13: Use objects to manage resource

resource 包括内存、文件描述器(file description)、互斥锁、字型和画刷、数据库连接、网络sockets。

reason

  • 程序员会容易忘记delete obj;

conclusion

  • 使用RAII对象来防止资源泄露

RAII: Resource Acquisition Is Initialization. 获得资源的同时立刻放进管理对象中。
管理对象析构的时候,资源会同时被释放。(如果析构函数抛出异常,参见条款8(不要让异常逃离析构函数)进行处理)

example:

// nake, without RAII.
void f() {
    Object * p = new Object();
    // ... forget to delete p;
}

// use shared_ptr<T> as a RAII object.
void f() {
    shared_ptr<Object> rP(new Object);
    // when f() return , rP is destructed and the resource it managed is freed.
}

ps: shared_ptr is a RCSP: reference-counting smart pointer


item14: Think carefully about copying behaviour in resource-managing classes.

conclusion

  • 禁止复制:让RAII对象继承noncopyable。
  • RCSP: 对底层资源使用引用计数。
class Lock {
public:
    explicit Lock(Mutex* pm) 
    : mutexPtr(pm, unlock) {    // 指定shared_ptr的deleter为unlock。
        lock(mutexPtr.get());
    }
private:
    std::shared_ptr<Mutex> mutexPtr;
};
  • 复制底部资源,深拷贝
  • 转移底部资源所有权,如auto_ptr.

常用的是前两种方法


item20: Prefer pass-by-reference-to-const to pass-by-value.

reason

  • 节省开销,避免不必要的copy constructor 和 destructor。
  • 避免对象切割(slicing)

conclusion

  • 尽量使用pass-by-reference-to-const代替pass-by-value,高效,避免slicing。
  • pass-by-reference-to-const的方式不适用于:内置类型、stl的迭代器和函数对象,这些类型使用pass-by-value更为适当。

ps: references往往以指针实现,因此pass by reference通常意味着真正传递的是指针。因此内置类型使用pass by value往往比使用pass by reference更为高效。stl的迭代器和函数对象习惯上都被设计为pass by value。


关于封装

如果某些东西被封装,它就不再可见。越多东西被封装,越少人可以看到它。而越少人看到它,我们就有越大的弹性去改变它,因为我们的改变仅仅直接影响看到改变的那些人事物。因此,愈多东西被封装,我们改变那些东西的能力也愈大。这就是我们推崇封装的原因:它使我们能够改变事物而只影响到有限客户。


item22: Declare data members private.

reason

  • private == 不可见 == 封装 (除了friend,member function)
  • public意味着不封装,不封装意味着不可改变,因为改变了就会直接影响到class的用户。
  • protected并不比public的封装性好,如果修改或删掉一个protected的成员,那么所有derived class都会被破坏,因此protected的成员变量也意味着不封装。

conclusion

  • 从封装的角度来看,class的访问类型其实只有两种:private == 封装;protected,public == 不封装;
  • 应该将成员变量声明为private。这样做,可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者充分的实现弹性。

item23: Prefer non-member non-friend functions to member functions.

reason

  • non-member non-friend functions 不增加private成员的访问次数,意味着更好的封装性(较之于member function和friend function)。

conclusion

  • 从封装角度来看,尽量使用non-member non-friend函数替换member函数。这样不破坏封装性,增加包裹弹性和技能扩充性。

为什么non-member non-friend functions的封装性更好呢?
从item22说起,成员变量应该是private的,因为如果不是,就有无限量的函数可以访问它们,它们也就毫无封装性。我们可以这样来衡量封装性:能直接访问(private)数据成员的函数越多,我们就说数据的封装性越低。能够访问private成员的函数只有两种,friend和member函数。而non-member non-friend函数并没有增加private直接被访问的次数,因此说它有更好的封装性。也正因为如此,在firend和member函数,与non-member non-friend函数之间选择,后者更受欢迎。

版权声明:本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名elloop(包含链接)

相关文章推荐

Effective_C++_3rd笔记.pdf

  • 2013年03月21日 15:35
  • 334KB
  • 下载

Effective_C++_笔记

  • 2011年10月12日 17:52
  • 314KB
  • 下载

Effective C++读书笔记(15)

条款25:考虑写出一个不抛异常的swap函数 Consider support for a non-throwing swap swap是一个有趣的函数。最早作为STL的一部分被引入,后来它成为异...

effective C++ 读书笔记

  • 2014年11月20日 11:07
  • 261KB
  • 下载

Effective c++ 学习笔记(三)

条款03:尽可能使用const const指定不能被改动的对象,编译器会强制实施这项约束。 char name[] = "April" const char *p = name; ...

《Effective C++》学习笔记(二)

原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38706483 闲谈 这两天都是先在地铁里看了会《Effective...

【Effective C++读书笔记】篇十三(条款31~条款33)

条款31:将文件间的编译依存关系降至最低                                                                         请记住...

Effective C++ 改善程序与设计的55个具体做法 二周目笔记01

开篇:决定再读一遍《More Effective C++ 改善程序与设计的55个具体做法》,这次每个条款我都做下笔记,记录在这个系列博客上。这是第一篇。 条款01:视C++为一个语言联邦 C Ob...

Effective Modern C++ 笔记(一)——类型推断

本系列主要介绍Effective Modern C++中比较重要的知识,其中第一篇主要介绍类型推断的基础知识:主要包括模板、auto、decltype。...

effective C++笔记之条款34: 将文件间的编译依赖性降至最低

1.    先看如下函数: class Person { public: Person(const sting& name, const Date& birthday, const ...
  • lifu119
  • lifu119
  • 2012年03月24日 15:08
  • 877
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective C++ 笔记
举报原因:
原因补充:

(最多只允许输入30个字)