第九周 C++标准库 体系结构与内核分析 Boolan 侯捷

0. 首先先说作业

因为这次的作业看起来和第十周的课程更相近,所以把第十周看了。导致并没有时间回顾第九周及之前的课程,结果就是在写作业的时候一脸懵比,出现各种错误。这次出现的错误非常有启发性,所以列在最前。

0.1 错误的编译环境

更换系统后用的是官方的MinGW,几次编译都很正常,并没有什么问题。
但是今天在查询代码的时候出现了无法使用C++11中string的几个数值转换函数的问题。现象就是使用to_string等函数时显示无法解析名字。
后来更换Code::Blocks才编译正常, 怀疑是MinGW版本(5.3.0)太旧的问题,查看文档后得知Code::Blocks中用的版本是6.2.0,于是按编译说明找到了下载地址MinGW-w64。更换编译环境后编译正常。

0.2 错误的设计模式

昨天在编程之前并没有仔细思考,是从测试代码和题目要求分别从输出函数和基础类两方面推进,最后并没有汇合,代码微妙的错开了。导致两边的接口完全对不上,一边请求的是函数,而另一边提供的是类型。没有仔细思考会遇到的问题导致错误的接口。

0.3 并不理解Traits的本质

现在也仍然弄不清楚Traits到底是什么,一开始的感觉就是利用泛化和特化来获得指定的struct数值。但是现在感觉并不是这样。如果没有充分理解Traits到底是什么,那就没有办法灵活的运用。这次的混乱大都产生于此。

0.4 并不扎实的基本功

虽然老师说“勿在浮沙筑高台”,但是短时间没有办法弥补巨大的差距。
典型的问题出现在对基类与子类的关系,为此专门拿出书本仔细阅读,发现缺少大量的相关知识。
对于private中数值的继承、对于构造函数的继承、对于基类函数的继承,虽然有所了解,但是完全不知道是怎么回事,以及如何使用。

class Value{
public:
    Value(double m = 0):mvalue(m){}
    double key() const {
        return mvalue;
    }
protected:
    double mvalue;
};

class kilometer:public Value {
public:
    using Value::Value;
};

这段代码就是以前从来没有见过的,如果没有看书也不会想到还可以这么写。由于对OOP认识并不清楚,所以只能写继承关系,但是感觉其他的可能会更好。

0.5 缺少实践经验以及理解不足

对于模板的使用完全没有概念,仅仅是照猫画虎。所以对于引用传递的内容非常模糊,就会出现下面这种疑问:“为什么传引用会失效?”如果写成print(T& x),传递过来的类型T就会变成Value,导致Traits失效。之前还有很多类似的问题,所以不仅要知道reference传递的内容是什么,也要知道传递的类型是什么。

// 此处传引用会失效??为什么?
template<typename T>
string print(T x){
    string str;
    int ts=x.key()*dimension<T>::scale+0.5;
    str= to_string(ts); //Clion不支持to_string,为什么??
//    str.push_back(to_string(x.key()*dimension<T>::scale));
    str.push_back(dimension<T>::unit);
    return str;
}

之前还出现过这样的问题,现在看来非常愚蠢,但是因为第一次遇到,所以就毫无意识。
下面这段代码直接重载了所有的<<,导致这个操作符废了。如何避免这种情况,避免过度重载?这些还要再回顾一下特化等知识。

//过度重载
template<typename T>
inline
ostream& operator<<(ostream& os, const T& x)
{
    return os << x.key()*dimension<T>::scale << dimension<T>::unit;
}

算法的形式

C++标准库的算法,是什么东西?

迭代器必须可以回答算法提出的所有问题。

类型+() 直接生成临时对象

30. 算法源代码剖析(11个例子)

先前示例中出现的算法

算法 accumulate

可以传函数,也可以传像函数的东西

struct myclass {
  int operator()(int x, int y){return x+3*y;}
} myobj;

算法 for_each

算法 replace, replace_if, replace_copy

Predicate 判断式

算法count,count_if

算法find,find_if

算法sort

算法 rbegin rend

reverse_iterator

算法 binary_search

仿函数 functors

最简单的能够写出来融入STL的

  • 算术类
  • 逻辑运算类
  • 相对关系类

要仿函数,就必须要重载()
因为要把+-操作传给算法,所以要用仿函数。

仿函数functors的可适配条件

没有继承,就说明没有融入STL
继承不继承有什么差别
继承的是binary_function
一个没有数据的类,占用大小是0或者1,被继承时,占用大小是0。
子类会 继承typedef

存在多种 Adapters

adapters就是换个衣服
会出现在三个地方:

  • 迭代器适配器
  • 仿函数适配器
  • 容器适配器

容器适配器:stack,queue

set,multiset?

函数适配器:binder2nd

注意bind2nd是辅助函数,binder2nd是对象

函数适配器:binder2nd

没看懂,一个函数适配器需要回答那几个问题?
typename 通知编译器,后面是一个类型,可以编译通过。
既要能提出问题,又能回答问题
既可以适配,又能被适配
所以要继承unary

函数适配器:not1

用compose把一大堆adapter组合在一起?

新型适配器,bind Since C++11

..\include\c++\backward\backward_warning.h
binder2nd过时了,要用bind取代

std::bind 可以绑定:

  1. functions
  2. function objects
  3. member functions _1 必须是某个object地址
  4. data members _1 必须是某个object地址

_1 占位符号

using namespace std::placeholders; //add visibility _1,_2,_3,...

bind<T>,只能绑定一个,绑定的是返回值

member function 其实有个参数 this
可以返回函数值,也可以返回data值

迭代器适配器 reverse_iterator

rbegin() rend()
解引用时先--、】 【【

迭代器适配器:inserter

普通的copy不检查空间是否越界
所以给iterator增加适配器
inserter 对象被赋值时调用赋值的操作符重载,从而在不更改copy代码的前提下更改流程

x适配器:ostream_iterator

不知道该算什么适配器
delimiter 分隔符

x适配器:istream_iterator

std::istream_iterator<double> eos; // 没有参数,空的,是一个end-of-stream iterator

为什么一创建就要读取呢?

第二个例子相当于文本框?
copy不止能把确定的序列复制到目标中去,也可以把可++的Iterator指向的对象。

推荐阅读更多精彩内容