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

指针与const

// 《STL源码剖析》, p88
iterator_traits<const int*>::value_type;
  • 指向常量的指针(pointer to const)
    指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针:

    • const double pi = 3.14      // pi 是个常量,它的值不能改变
    • double *ptr = &pi               // 错误:ptr 是一个普通指针,不可以指向一个常量
    • const double *cptr = &pi    // 正确:cptr 可以指向一个双精度常量
    • *cptr = 42                           // 错误:cptr 是一个指向常量的指针,所以不能给 *ptr 赋值
    • double dval = 3.14             // dval:是一个双精度浮点数,它的值可以改变
    • cptr = &dval                        // 正确:允许令一个指向常量的指针指向一个非常量对象,只是不能通过该指针改变对象的值,但可以通过其他途径改变该对象的值
  • 常量指针(const pointer)
    常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。

    • int errNumb = 0
    • int *const curErr = &errNumb     // curErr 将一直指向 errNumb
    • const double Pi = 3.14159
    • const double *const pip = &pi    // pip 是一个指向常量对象的常量指针
  • 想要弄清楚这些声明的含义最行之有效的方法是从右向左阅读。在上述例子中,离 curErr 最近的符号是 const,意味着 curErr 本身是一个常量对象,对象的类型由声明符的其余部分确定。声明符中的下一个符号是*,意思是 curErr 是一个常量指针。最后,该声明语句的基本数据类型部分确定了常量指针指向的是一个 int 对象。1

ptrdiff_t

// 《STL源码剖析》, p90
template <class T>
struct iterator_traits<T*> {
	...
	typedef ptrdiff_t differnce_type;
};

两个指针相减的结果的类型是一种名为 ptrdiff_t 的标准库类型,和 size_t 一样,ptrdiff_t 也是一种定义在 cstddef 头文件中的机器相关的类型。因为差值可能为负值,所以 ptrdiff_t 是一种带符号类型。 2

左值和右值

//《STL源码剖析》, p91
当我们对一个 mutable iterators 进行提领操作时,获得的不应该是一个右值(rvalue),应该是一个左值(lvalue),因为右值不允许赋值操作(assignment),左值才允许。

原文中对于左、右值的描述过于简单,这里进行简单的扩充。

  • C++ 的表达式要不然是右值(rvalue,读作 ”are-value“),要不然就是左值(lvalue,读作 ”ell-value“)。3
  • 在 C++11 中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。举个例子,int a = b + c,a 就是左值,其有变量名为 a,通过 &a 可以获取该变量的地址;表达式 b + c 、函数 int func() 的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b + c) 这样的操作则不会通过编译。4

强弱类型

//《STL源码剖析》, p91
它利用“内嵌型别”的编程技巧与编译器的 template 参数推导功能,增强 C++ 未能提供关于型别认证方面的能力,弥补 C++ 不为强型别(stronged typed)语言的遗憾。

  • 由于地域语言差异,我们常称强弱型别为强弱类型。
  • 强类型(strongly typed)和弱类型(weakly typed (loosely typed))这两个术语并没有非常明确的定义,这对术语在短短的电脑历史中,早已含括了更多的意义,而且时常很难知道写的人究竟要表达哪个意思,但主要用以描述编程语言对于混入不同资料类型的值进行运算时的处理方式。
  • 强类型的语言遇到函数引数类型和实际调用类型不符合的情况经常会直接出错或者编译失败;而弱类型的语言常常会实行隐式转换,或者产生难以意料的结果。5

输出迭代器(output iterator)

// 《STL源码剖析》, p109
template <class OutputIterator, class Size, class T>
OutputIterator fill_n(OutputIterator first, Size n, const T& value) {
	for ( ; n > 0; --n, ++first)
		*first = value;
	return first;
}

输出迭代器可以看作输入迭代器功能上的补集——只写而不读元素。输出迭代器必须支持

  • 用于推进迭代器的前置和后置递增运算(++)
  • 解引用运算符(*),只出现在赋值运算符的左侧(向一个已经解引用的输出迭代器赋值,就是将值写入它所指向的元素)

我们只能向一个输出迭代器赋值一次。类似输入迭代器,输出迭代器只能用于单遍扫描算法。用作目的位置的迭代器通常都是输出迭代器。6

non-trivial-xxx

// // 《STL源码剖析》, p110
究竟一个 class 什么时候该有自己的 non-trivial default constructor,non-trivial copy constructor,non-trivial assignment operator, non-trivial destructor 呢?一个简单的判断准则是:如果 class 内含指针成员,并且对它进行内存动态配置,那个这个 class 就需要实现出自己的 non-trivial-xxx。


  1. C++ Primer, p56 ↩︎

  2. C++ Primer, p107 ↩︎

  3. C++ Primer, p121 ↩︎

  4. C++11 左值、右值、右值引用详解 ↩︎

  5. 强弱类型 ↩︎

  6. C++ Primer, p366 ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值