代码改变世界

《C++ 沉思录》读书笔记

2012-12-02 14:22  robturtle  阅读(342)  评论(0编辑  收藏  举报

check list for surrogate class

  1. 代理类实现了被代理类的所有虚拟方法;
  2. 代理类存储的数据指针是指向基类的;
  3. 被代理类实现 copy() const (必须有const修饰符,当代理类是用const类型构造时)
  4. 如果希望创建代理类数组,必须有默认构造函数,同时意味着代理类所有方法中对指针操作前都要检查是否有效。

return value's type

  • T foo() const;
  • const T& foo() const;
  • T& foo();

只有(3)返回可修改左值。而(1)和(2)是实现‘读取’语义的可选项,其中(1)更偏重于‘值语义’,(2)更偏重‘指针语义’,这意味着:

仅当调用者拥有类型T的公有的复制构造函数时(1)才有效;(2)没有这个限制,但有一个严重的缺陷,就是当引用指向的内容发生内存地址偏移时,所有的引用变量都将失效。

简而言之,(1)对调用者有更多的假设,(2)更通用但要小心处理内存操作。

出于通用性和减少创建副本开销的考虑,往往会通过(2)实现‘读取’语义。

通过变量/指针返回左/右值

对于变量,可以通过重载函数的方式分别返回左/右值:

const T& foo(const T&) const;
T& foo(T&);

对于指针,可以通过继承的方式实现动态绑定:

class Class_const {
    const T& foo() const;
};
class Class : public Class_const {
    T& foo();
};

迭代器

为什么要分别定义 iterator 和 reverse_iterator?

因为 c++ 语言中的方向不对称性(directional asymmetricity),考虑下面代码:

int a[10];

int *p1 = a[10]; // valid, p1 it's an accessable pointer
int *tmp = a[0];
while (tmp != p1) 
    *tmp++ = 0; // valid

int *p2 = a[-1]; // undefined behavior
tmp = a[9];
while (tmp != p2) // undefined behavior
    *tmp-- = 0; 

因为这种不对称性,使得很难实现顺序算法的反向版本,即使实现也会大大增加复杂度。因此考虑定义不同的迭代器类型的方式会相对容易些。