《STL源码剖析》第四章笔记

引用与临时变量

// 《STL源码剖析》,p116
explicit vecotr(size_type n) { fill_initialize(n, T()); }
  • 如我们所知,对于常规引用(为了与右值引用区分开来,我们可以称之为左值引用),我们不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。
  • 右值引用有着完全相反的绑定特性:我们可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用直接绑定到一个左值上。
  • 我们不能将一个左值引用绑定到这类表达式上,但我们可以将一个 const 的左值引用或者一个右值引用绑定到这类表达式上。例如:
    void fill_initialize(size_type n, const T& value);
  • 左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
  • 由于右值引用只能绑定到临时对象,我们得知:所引用的对象将要被销毁,该对象没有其他用户。这两个特性意味着:使用右值引用的代码可以自由地接管所引用的对象的资源。1

迭代器失效

// 《STL源码剖析》,p123
注意,所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原空间之后构造新元素,并释放原空间。因此,对 vector 的任何操作,一旦引起空间重新配置,指向原 vector 的所有迭代器就都失效了。

快速排序

// 《STL源码剖析》,p142
// list 不能使用 STL 算法 sort(),必须使用自己的 sort() member function,
// 因为 STL 算法 sort() 只接受 RamdonAccessIterator
// 本函式采用 quick sort
template <class T, class Alloc>
void list<T, Alloc>::sort() {
	// 以下判断,如果是空白串行,或仅有一个元素,就不做任何动作
	// 使用 size() == 0 || size() == 1 来判断,虽然也可以,但是比较慢
    if (node->next == node || link_type(node->next)->next == node)
        return;
	// 一些新的 lists,作为中介数据存放区
    list<T, Alloc> carry;
    list<T, Alloc> counter[64];
    int fill = 0;
    while (!empty()) {
        carry.splice(carry.begin(), *this, begin());
        int i = 0;
        while(i < fill && !counter[i].empty()) {
            counter[i].merge(carry);
            carry.swap(counter[i++]);
        }
        carry.swap(counter[i]);
        if (i == fill) ++fill;
    }
    for (int i = 1; i < fill; ++i)
        counter[i].merge(counter[i-1]);
    swap(counter[fill-1]);
} 

很显然上述代码并非快速排序,而是归并排序。快速排序代码如下:

void QuickSort(int array[], int low, int high) {
	int i = low;
	int j = high;
	int key = array[i];
	if (low < high) {
		while (i < j) {
			while (i < j && array[j] >= key) {
				j--;
			}
			if (i < j) {
				array[i] = array[j];
			}
			while (i < j && array[i] <= key) {
				i++;
			}
			if (i < j) {
				array[j] = array[i];
			}
		}
		array[i] = key;
		QuickSort(array, low, i - 1);
		QuickSort(array, i + 1, high);
	}
}

sizeof

// 《STL源码剖析》,p146
static size_t buffer_size() { return __deque_buf_size(Bufsize, sizeof(T); }
  • sizeof 运算符返回一条表达式或一个类型名字所占的字节数
  • sizeof 运算符满足右结合律,其所得的值是一个 size_t 类型的常量表达式。
  • C++11 新标准允许我们使用作用域来获取类成员的大小。
  • 对 char 或者类型为 char 的表达式执行 sizeof 运算,结果得1.
  • 对引用类型执行 sizeof 运算得到被引用对象所占空间的大小。
  • 对指针执行 sizeof 运算得到指针本身所占空间的大小。
  • 对解引用指针执行 sizeof 运算得到指针指向的对象所占空间的大小,指针不需有效2

size_t

// 《STL源码剖析》,p151
size_type size() const { return size_type(-1); }

在这里 size_type 等于 size_t,size_t 是一种机器相关的无符号类型3,所以将 -1 强制转换为无符号类型会得到最大的无符号数。

__STL_UNWIND( … )

// 《STL源码剖析》,p156
__STL_TRY {
	construct(finish.cur, t_copy);		// 针对标的元素设值
	finish.set_node(finish.node + 1);	// 改变 finish,令其指向新节点
	finish.cur = finish.first;			// 设定 finish 的状态
}
__STL_UNWIND(deallocate_node(*(finish.node + 1))); 

其中 __STL_TRY 和 __STL_UNWIND 定义如下所示:

// "stl_config.h"
# ifdef __STL_USE_EXCEPTIONS
#   define __STL_TRY try
#   define __STL_CATCH_ALL catch(...)
#   define __STL_THROW(x) throw x
#   define __STL_RETHROW throw
#   define __STL_NOTHROW throw()
#   define __STL_UNWIND(action) catch(...) { action; throw; }
# else
#   define __STL_TRY 
#   define __STL_CATCH_ALL if (false)
#   define __STL_THROW(x) 
#   define __STL_RETHROW 
#   define __STL_NOTHROW 
#   define __STL_UNWIND(action) 
# endif

约束模板友元函数

// 《STL源码剖析》,p167
template <class T, class Sequence = deque<T> >
class stack {
	// 以㆘的 __STL_NULL_TMPL_ARGS 会开展为 <>,见 1.9.1 节
	friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);
	friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);
	...
}; 

声明中的 <> 指出这是模板具体化,<> 可以为空,因为可以从函数参数中推断出模板参数的类型。4


  1. C++ Primer, p471 ↩︎

  2. C++ Primer, p139 ↩︎

  3. C++ Primer, p103 ↩︎

  4. 模板类的约束模板友元函数:template friend functions ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值