`
lovecontry
  • 浏览: 1035694 次
文章分类
社区版块
存档分类
最新评论

C++ primer 笔记(一)

 
阅读更多

在一个博客中看到的 整理了一些自己认为有帮助的 点击打开链接

最大收获008:
——左值:lvalue,左值可以出现在赋值语句的左边或右边。
——右值:rvalue,右值只能出现在赋值的右边,不能出现在赋值语句的左边。
——变量是左值,因此可以出现在赋值语句的左边,数字字面是右值

最大收获010:
——初始化变量不是赋值。
——初始化:指创建变量并给它赋初始值。
——赋值:是擦除对象的当前值并用新值代替。

最大收获011:
——初始化变量有两种形式。
——1. int ival(1024); // direct-initialization 直接初始化
——2.int ival = 1024; // copy-initialization 复制初始化

初始化的参数的类型并不一定是该对象的类型,所以复制初始化不一定是会

直接调用复制构造函数,而是创建一个临时对象,再调用复制构造函数。


当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参

匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象(7.3.2节),
然后使用复制构造函数将那个临时对象复制到正在创建的对象

explicit,和构造函数一起使用.
explicit constructor指明构造函数只能显示使用,目的是为了防止不必要的隐式转化.
举个例子:
有这样一段代码:

class A
{
public:
A(int);
private:
int num;
};

int Test(const A&) // 一个应用函数
{
...
}

Test(2); // 正确
过程是这样的: 编译器知道传的值是int而函数需要的是A类型,但它也同时知道调

用A的构造函数将int转换成一个合适的A,所以才有上面成功的调用.换句话说,编译器处理这个调用时的情形类似下面这样:
const A temp(2); // 从2产生一个临时A对象
Test(temp); // 调用函数


如果代码写成如下样子:
class A
{
public:
explicit A(int);
private:
int num;
};

int Test(const A&) // 一个应用函数
{
...
}

Test(2); // 失败,不能通过隐式类型转换将int类型变量构造成成A类型变量

收大收获014:
——extern声明不是定义,也不分配存储空间。
——事实上,它只是说明变量定义在程序的其他地方。

最大收获015:
——只有当声明也是定义时,声明才可以有初始化式,因此只有定义才分配存储空间。

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函
数是在别处定义的,要在此处引用”。

1. extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中
变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。
这里需要注意的是,被引用的变量v的链接属性必须是外链接
(external)的,也就是说a.c要引用到v,不只是取决于在a.c
中声明extern int v,还取决于变量v本身是能够被引用到的。
这涉及到c语言的另外一个话题--变量的作用域。
能够被其他模块以extern修饰符引用到的变量通常是全局变量。
还有很重要的一点是,extern int v可以放在a.c中的任何地方,
比如你可以在a.c中的函数fun定义的开头处声明extern int v,
然后就可以引用到变量v了,只不过这样只能在函数fun作用域中
引用v罢了,这还是变量作用域的问题。
对于这一点来说,很多人使用的时候都心存顾虑。
好像extern声明只能用于文件作用域似的。

2. extern修饰函数声明。从本质上来讲,变量和函数没有区别。
函数名是指向函数二进制块开头处的指针。如果文件a.c需要引
用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可
以在a.c中声明extern int fun(int mu),然后就能使用fun来
做任何事情。就像变量的声明一样,extern int fun(int mu)
可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。
对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。
使用extern和包含头文件来引用函数有什么区别呢?
extern的引用方式比包含头文件要简洁得多!
extern的使用方法是直接了当的,想引用哪个函数就用
extern声明哪个函数。这大概是KISS原则的一种体现吧!
这样做的一个明显的好处是,会加速程序的编译
(确切的说是预处理)的过程,节省时间。在大型
C程序编译过程中,这种差异是非常明显的。

3. 此外,extern修饰符可用于指示C或者C++函数的调用规范。
比如在C++中调用C库函数,就需要在C++程序中用
extern “C”声明要引用的函数。这是给链接器用的,
告诉链接器在链接的时候用C函数规范来链接。
主要原因是C++和C程序编译完成后在目标代码中命名规则不同。

最大收获023:
——避免多重包含,为了避免多重包含,避免名字冲突,
预处理器变量经常用全大写字母表示。
#ifndef ABC_H
#define ABC_H // Definition of ABC class and related functions goes here
。。。。。。。
#endif

最大收获044:
——C++提供了一个特殊的指针类型void*,它可以保存任何类型对象的地址。
——void*主要用于以下三种操作:
——1. 与另一个指针进行比较
——2. 向函数传递void*指针或从函数返回void*指针
——3. 给另一个void*指针赋值
double obj = 3.14;
double *pd = &obj;
// ok, void * can hold the address value of any data pointer type
void * pv = & obj; // obj can be an object of anytype
pv = pd; // pd can be a pointer to any type

最大收获046:
——C++允许计算数组或对象的超出未端的地址,但不允许对
此进行解引用操作(*解引用操作),而计算数组超出未端
位置之后或数组首地址之前的地址都是不合法的。

@ 学习摘录068:缓冲区的刷新
——下面五种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:
——1. 程序正常结束。作为main返回工作的一部分,将清空所有的输出缓冲区。
——2. 在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会写到下一个值之前刷新。
——3. 用操纵符(manipulator)显式地刷新缓冲区,例如行结束符endl.
——4. 在每次输出操作执行完后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区。
——5. 可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流将刷新其关联的输出缓冲区。

@学习摘录072:顺序容器
——将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素,这就是顺序容器。
——vector 支持快速随机访问
——list 支持快速插入/删除
——deque 双端队列

@学习摘录073:顺序容器适配器
——适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。
——stack 后进先出(LIFO)栈
——queue 先进先出(FIFO)队列
——priority_queue 有优先级管理的队列


@学习摘录074:新建一个容器初始化为另一个容器的副本
——vector <int> ivec;
——vector<int> ivec2(ivec); // ok, ivec is vector<int>
——list<int> ilist(ivec); // error: ivec is not list<int>
——vector<double> dvec(ivec); // error: ivec holds int now double
——将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。


@学习摘录075:初始化为一段元素的副本
——系统允许通过传递一对迭代器间接实现该功能。使用迭代器时,不要求容器类型相同,容器内的元素类型也可以不相同,只要它们相互兼容能进行转换即可。
——// initialize slist with copy of each element of svec
——list<string> slist(svec.begin(), svec.end() );
——// find midpoint in the vector
——vector<string>::iterator mid = svec.begin() + svec.size() / 2;
——// initialize front with first half of svec: The elements up to but note include *mid
——deque<string> front(svec.begin(), mid);
——// initialize back with second half of svec: The elements *mid through end of svec
——deque<string> back(mid, svec.end() );

@学习摘录076:容器内元素的类型约束
——元素类型必须支持赋值运算。
——元素类型的对象必须可复制。
摘录有想076:
——这个问题,虽然不常发生,int型之类的内置类型不用考虑这个问题,而一些自己定义的类类型的话,真的得注意了。

@学习摘录077:容器的容器
——定义容器的容器时,有一个问题必须得注意的:
——vector< vector<string> > lines; // ok:space required between cblose>
——vector<vector<string>> lines; // error: >> treated as shift operator
——必须用空格隔开两个相邻的>符号,以示这是两个分开的符号。
——否则,系统会认为>>是单个符号,为右移操作符,并结果导致编译时的错误。
摘录有想077:
——网上的帖子看过很多人出现这个错,经典错误啊,就一个空格之差,让人看得纠结,写程序时,细节决定成败。

第二节:迭代器和迭代器范围

@学习摘录078:迭代器范围
——C++语言使用一对迭代器标记迭代器范围,这两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置。
——通常,这两个迭代器命名为first和last,或 beg和end,用于标记容器中的一段元素范围。
——该范围内的元素包括迭代器first指向的元素,以及从first开始一直到迭代器last指向的位置之前的所有元素。
——此类元素范围称“左闭合区间(left-inclusive interval) 表达式: [first, last]
摘录有想078:
——要记得啊,常常出现的字眼“超出末端的下一位置”。这一知识点,记住左闭区间是关键。

@学习摘录079:使用左闭合区间的编程意义
——左闭合区间有两个方便使用的性质,得记住:
——1. 当first 与last 相等时,迭代器范围为空;
——2. 当first与 last 不相等时,迭代器范围内至少有一个元素,而first指向该区间的第一个元素。
——while(first != last)
——{ // safe to use *first because we know there is at least one element
—— ++first;
——}
摘录有想079:
——我想:左闭合区间的使用,总的来说可以概括为三个字吧“安全性”。

第三节:顺序容器的操作
——每种顺序容器都提供了一组有用的类型定义以及以下操作:
——1. 在容器中添加元素
——2. 在容器中删除元素
——3. 设置容器的大小
——4. (如果有的话)获取容器内的第一个和最后一个元素。

@学习摘录080:在顺序容器中添加元素
——在容器中添加元素时,系统是将元素值复制到容器里。
摘录有想080:
——这令我想到了指针,使用指针,改变的是值,而地址不变。这个原理吧。

@学习摘录081:在容器中的指定位置添加元素
——看代码后,你懂的! s.insert(迭代器, 插入的东西); 新元素是插入在迭代器指向的位置之前。返回指向新添加元素的迭代器。
——s.insert(iter, element); // insert element just before iter
摘录有想081:
——考虑到有一种特例:push_back和push_front可以相当于iter为s.begin()和s.end() 时。

@学习摘录082:插入一段元素
——看代码后,你懂的!迭代器插入位置加上迭代器的前后位置构成的左闭合区间。
——string sarray[4] = {“quasi”, “samba”, “frollo”, “scar”};
——// insert all the element in sarray at end of slist
——slist.insert(slist.end(), sarray, sarray + 4);

@学习摘录083:关系操作符(比较大小)
——/*
—— ivec1: 1 3 5 7 9 12
—— ivec2: 0 2 4 6 8 10 12
—— ivec3: 1 3 9
—— ivec4: 1 3 5 7
—— ivec5: 1 3 5 7 9 12
——*/
—— // ivec 1 and ivec2 differ at element[0]: ivec1 greater than ivec2
——// ivec1 < ivec2 // false
——// ivec2 < ivec1 // true
——// ivec1 and ivec3 differ at element[2]: ivec1 less than ivec3
——ivec1 < ivec3 // true
——// all elements equal, but ivec4 has fewer elements, so ivec1 is greater than ivec4
——ivec1 < ivec4 // false
——ivec1 == ivec5 // true; each element equal and same number of elements
——ivec1 == ivec4 // false; ivec4 has fewer element
—— ivec1 != ivec4 // true; ivec4 has fewer elements than ivec1
摘录有想083:
——很明显,在容器中,比较大小;
——1.逐位对比,先比大小。 (大者为大)
——2.再比容器长度。(长者为大)

@学习摘录084:删除容器内所有的元素
——slist.clear(); // delete all the element within the container
——slist.erase(slist.begin(), slist.end() ); // equivalent
摘录有想084:
——要删除容器内所有的元素,可以调用clear函数,或将begin和end迭代器传递给erase函数。

@学习摘录085:容器中的赋值操作符
——赋值操作符首先删除其左操作数容器中的所有元素;
——然后将右操作数容器的所有元素插入到左边容器中;
——赋值后,左右两边的容器相等;
——赋值前可能两个容器长度不相等,但赋值后两个容器都具有右操作数的长度。
——c1 = c2; // replace contents of c1 with a copy of elements in c2
——// equivalent operation using erase and insert
——c1.erase(c1.begin(), c1.end() ); // delete all elements in c1
——c1.insert(c1.begin(), c2.begin(), c2.end() ); // insert c2

@学习摘录086:重设容器
——c.assign(b,e) // 重新设置c的元素:将迭代器b和e标记的范围内所有的元素复制到c中。b和e必须不是指向c中元素的迭代器。
——c.assign(n, t) // 将容器c重新设置为存储n个值为t 的元素
——// equivalent to: slist1.clear();
——// followed by slist1.insert(slist1.begin(), 10, “Hiya!”);
——slist1.assign(10, “Hiya!”); // 10 elements; each one is Hiya!
——执行了上述语句后,容器slist1有10个元素,每个元素的值都是Hiya!
摘录有想086:
——assign操作跟赋值操作符的操作原理差不多,都是先清空一个容器,然后再对已清空的容器进行插入操作。

@学习摘录087:交换容器
——swap操作实现交换两个容器内所有元素的功能。
——vector<string> svec1(10); // vector with 10 elements
——vector<string> svec2(24); // vector with 24 elements
——svec1.swap(svec2);
——执行swap后,容器svec1 中存储24个string类型的元素,而svec2则存储10个元素。
——关于swap的一个重要问题:
——1.该操作不会删除或插入任何元素;
——2.保证在常量时间内实现交换。
——3.由于容器内没有移动任何元素,因此迭代器不会失效。
摘录有想087:
——对此表示疑惑,为什么没有移动元素就没失效?它的原理是?猜测可能是变了变量的地址,其它一切无发生改变。

第四节:vector容器的自增长

@学习摘录088:vector的增长效率
——为了使vector容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。
——vector容器预留了这些额外的存储区,用于存放新添加的元素。
——于是,不必为每个新元素重新分配容器。
——所分配的额外内存容量的确切数目因库的实现不同而不同。
——比起每添加一个新元素就必须重新分配一次容器,这个分配策略带来显著的效率。
——事实上,其性能非常好,因此在实际应用中,比起list和deque容器,vector的增长效率通常会更高。

@学习摘录089:capacity成员
——弄清capacity(容量)与size(长度)的区别非常重要。
——size指容器当前拥有的元素个数;
——而capacity则指容器在必须分配新存储空间之前可以存储的元素总数。
——vector<int> ivec;
——// size should be zero; capacity is implementation defined
——cout << “ivec: size: “ << ivec.size()
—— << “ capacity: “ << ivec.capacity() << endl;
——// give ivec 24 elements
——for (vector<int>::size_type ix = 0; ix != 24; ++x)
—— ivec.push_back(ix);
——// size should be 24; capacity will be >= 24 and is implementation defined
——cout << “ivec: size: “ << ivec.size()
—— << “ capacity: “ << ivec.capacity() << endl;
——结果:
—— ivec: size: 0 capacity: 0
—— ivec: size: 24 capacity: 32

@学习摘录090:选择容器
——下面列举了四种选择容器的法则。
——1. 如果程序要求随机访问元素,则应使用vector或deque容器。
——2. 如果程序必须在容器的中间位置插入或删除元素,则应采用list容器。
——3. 如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用deque容器。
——4. 如果只需在读取输入时在容器的中间位置插入元素,然后需要随机记问元素,则可考虑在输入时将元素读入到一个list容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的list容器复制到一个vector容器。

第六节:string类型

@学习摘录091:string类型的查找操作
——几乎所有的查找操作,返回的是string::size_type类型的值,以下标形式标记查找匹配所发生的位置;
——当查找没有匹配值,将返回名为string::npos的特殊值。

@学习摘录092:string类型的定义(一少部分)
——s.find(args) 在s中查找args的第一次出现
——s.find_first_of(args) 在s中查找args的任意字符的第一次出现
——s.find_last_of(args) 在s中查找args的任意字符的最后一次出现
——s.find_first_not_of(args) 在s中查找第一个不属于args的字符
——s.find_last_not_of(args) 在s中查找最后一个不属于args的字符
摘录有想092:
——这几个定义,看上去,个人将其归纳两点:
——1. find操作可以分为第一次出现的关键字和最后次出现的关键字进行查找的标准。
——2. find操作可以分为查找属于args的字符和不属于args的字符。


《C++ Primer》 第10章学习笔记
第10章:关联容器
——关联容器(associative container)支持通过键来高效地查找和读取元素。

@学习摘录093:关联容器和顺序容器的本质差别
——关联容器通过键(key)存储和读取元素;
——顺序容器则通过元素在容器中的位置顺序存储和访问元素。

@学习摘录094:关联容器的主要特点
——关联容器的独特之处在于支持键的使用。
——关联容器根据键的次序排。
——在迭代遍历关联容器时,我们可以确保按键的顺序访问元素,而与元素在容器中的存放位置完全无关。(与插入顺序也无关)

@学习摘录095:关联容器的类型
——1. map     关联数组;元素通过键来存储和读取
——2. set 大小可变的集合,支持通过键实现的快速读取
——3. multimap 支持同一个键多次出现的map类型
——4. multiset 支持同一个键多次出现的set类型

@学习摘录096:两种基本关联容器的基本特点
——1. map的元素以键-值(key-value)对的形式组织。
—— 附:键,用作元素在map的索引。
——2. set仅包含一个键,能有效地支持关于某个键是否存在的查询。

@学习摘录097:四种关联容器需要注意的地方
——1. set 和 map类型的对象所包含的元素都具有不同的键,不允许为同一个键添加第二个元素。
——2. multimap和multiset 类型允许多个元素拥有相同的键,即用于一个键必须对应多个实例的情况下。(这两种类型不支持下标运算)

第一节:pair类型——#include <utility>

@学习摘录098:pair的创建与初始化
——pair<string, int> elem(“OK”, 110); // holds a string and an int
——pair<string, vector<int> > line; // holds string and vector<int>

@学习摘录099:pair对象的操作
——pair类,可直接访问其数据成员:成员都是公有的,分别命名为first和second
——pair< string, int> elem;
——elem.first = “OK”;
——elem.second = 3;
——cout << elem.first << elem.second; // 就这样,就给它们赋值了。

@学习摘录100:生成新的pair对象(make_pair)
——pair<string, string> next_auth;
——string first, last;
——while(cin >> first >> last)
——{
——// generate a pair from first and last
——next_auth = make_pair(first, last);
——// process next_auth..
——}

第三节:map类型——#include <map>
——map类型通常可理解为“关联数组”——通过键获取值,键-值相关联。

@学习摘录101:键类型的约束
——默认情况下标准库使用键类型定义 < 操作符来实现键的比较。
——所用的比较函数必须在键类型上定义严格弱排序(strict weak ordering)
摘录有想101:
——这也就是说,map<first, second> 中的first类型必须支持 < (小于)操作符。

@学习摘录102:map类定义的类型
——map<K, V>::key_type 在map容器中,用作索引的键的类型
——map<K, V>::mapped_type 在map容器中,键所关联的值的类型
——map<K, V>::value_type 一个pair类型,它的first元素具有key_type类型,second元素具有mapped_type类型

@学习摘录103:map类定义类型的访问方法及需要注意的地方
——当使用了map<K, V>::value_type时,该类型中的key_type类型是const性质,不可修改。
——当使用了map<K, V>::iterator时,该类型中的key_type类型是const性质,不可修改。
摘录有想103:
——即 map<string, int> ok; map<string, int>::value_type vt; map<string, int>::iterator = ok.begin();
——vt -> first; 只能读取,不能赋值。 iterator -> first也是只能读取不能赋值。

@学习摘录104:下标在map中的行为
——假如定义了一个名为ok的map<string, int>对象
——用下标操作ok[“in”] = 12 会有以下结果:
——当in存在,赋ok -> second值为12;
——当 in 不存在,新建(即插入) ”in” 元素并赋值为12.
摘录有想104:
——与顺序容器不同的一点是,用下标访问有可能会使map容器添加一个新的元素。

@学习摘录105:map中常用的insert操作
——m.insert(make_pair(“ok”, 12);
——m.insert(map<string, int>::value_type(“ok”, 1);
——m.inert(map<string, int>:: iterator a = m.begin(), map<string, int>::iterator b=++a );
——这三种操作,返回的都是void类型。

@学习摘录106:查找map中的元素
——m.find(k); 返回迭代器,存在,返回符合位置的迭代器,不存在,返回超出末端迭代器。
——m.count(k); 返回m中k的出现次数。
摘录有想106:
——count 操作根据map的性质,只能回返回0或1.

@学习摘录107:从map中删除元素
——// erase of a key returns number of element removed
—— if(word_count.erase(removal_word) )
—— cout << “ok: “ << removal_word << “ removed\n”;
—— else cout << “oops: “ << removal_word << “ not found! \n”;
——m.erase(k) 删除m中键为k的元素。返回size_type类型的值,表示删除的元素个数。

第五节:multimap和multiset类型

@学习摘录108:multimap和multiset类型元素的添加
——由于键不要求是唯一的,因此每次调用insert总会添加一个元素。
——multimap<string, string> authors;
——// adds first elemt with key a
—— authors.insert(make_pair( string(“a”), string(“b”) );
——ok: adds second element with key a
——authors.insert(make_pair(string(“a”), string(“c”) );

@学习摘录109:multimap和multiset类型元素的删除
——带有一个键参数的erase版本将删除拥有该键的“所有”元素,并返回删除元素的个数。
——multimap<string, string> ::size_type cnt = authors.erase( “a”);

@学习摘录110:multimap和multiset类型元素的查找――第一种方法
——直接见实例,当使用iter与find结合时,iter能保存所有find找到的键的结果。
// author we’ll look for
string search_item(“A”);
typedef multimap<string, string>::size_type sz_type;
sz_type entries = authors.count(search_item);
// get iterator to the first entry for this author
multimap<string, string>:: iterator iter = authors.find(search_item);
// loop through the number of entries there are for this author
for(sz_type cnt = 0; cnt != entries; ++cnt, ++iter)
cout << iter -> second << end; // print each title
——上例中,count函数求出某键出现次数,而find则返回一个迭代器,指向第一个正在查找的实例。

@学习摘录110:multimap和multiset类型元素的查找――第二种方法
——m.lower_bound(k) 返回一个迭代器,指向键不小于k的第一个元素
——m.upper_bound(k) 返回一个迭代器,指向键大于k的第一个元素
// definitions of authors and search_item as above
// beg and end denote range of elements for this author
typedef multimap<string, string>::iterator authors_it;
authors.it beg = authors.lower_bound(search_item);
authors.it end = authors.upper_bound(search_item);
// loop through the number of entries there are for this author
while( beg != end)
{
cout << beg -> second << endl; // print each title
++beg;
}
摘录有想110:
——这里利用了迭代器取得元素在multimap中的区间,能这样操作是因为,在multimap中,它的元素是以“键”的 < (小于)的顺序排序的

@学习摘录111:multimap和multiset类型元素的查找――第三种方法
——m.equal_range(k) 返回一个迭代器的pair对象,它的first成员等价于m.lower_bound(k),它的second成员等价于m.upper_bound(k);
——直接见实例:
typedef multimap<string, string>::iterator authors_it;
// definitions of authors and search_item as above
// pos holds iterators that denote range of elements for this key
pair<authors_it, authors_it> pos = authors.equal_range(search_item);
// loop through the number of entries there are for this author
while(pos.first != pos.second)
{
cout << pos.first -> second << endl; // print each title
++pos.first;
}
摘录有想111:
——这种使用方法,相当于第二种方法的简化,只是引用了一种pair类型。

@学习摘录112:*operator(解引用操作符在map、set、multimap、multiseet)
——在这些应用的时候,解引用操作符将生成一个value_type类型的值。
——对于map和multimap容器,value_type是pair类型。


《C++ Primer》 第11章学习笔记
第11章:泛型算法
——标准库提供一组不依赖特定的容器类型的算法作用在不同类型的容器和不同类型的元素上。

@学习摘录113:算法重要性质
——算法也许会改变存储在容器中的元素的值,也许会在容器内移动元素。
——但是,算法从不直接添加或删除元素。如果需要添加或删除元素,则必须使用容器操作。

第二节:初窥算法
—— #include <algorithm> // 使用泛型算法
—— #include <numeric> // 泛化的算术算法(generalized numeric algorithm)

@学习摘录114:迭代器实参类型
——大多数算法(前两个参数指定范围):
——通常:泛型算法都是在标记容器(或其他序列)内的元素范围的迭代器上操作的。
——标记范围的两个实参类型必须精确匹配,而迭代器本身必须标记一个范围:
——它们必须指向同一个容器中的元素(或者超出容器末端的下一位置),
——并且如果两者不相等,则第一个迭代器通过不断地自增,必须可以到达第二个迭代器。
——有些算法(带有两对迭代器参数)
——每对迭代器中,两个实参的类型必须精确匹配,但不要求两对之间的类型匹配。
——当元素存储在不同类型的序列中时,只要这两个序列中的元素可以比较即可。

第三节:再谈迭代器
——C++语言还提供了另外三种迭代器

@学习摘录115:三种容器的简单介绍
——插入迭代器(insert iterator):这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。
——iostream迭代器(iostream iterator):这类迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的IO流。
——反向迭代器(reverse iterator):这类迭代器实现向后遍历,而不是向前遍历。

@学习摘录116:三种插入迭代器
——这里有三种插入器,其差别在于插入元素的位置不同:
——1. back_inserter, 创建使用push_back实现插入迭代器
——2. front_inserter, 使用push_front实现插入。
——3. insert, 使用insert实现插入操作。

@学习摘录117:三种inserter的综合应用
——在使用front_inserter时,元素始终在容器的第一个元素前面插入。
——使用inserter时,元素则在指定位置前面插入。
list<int> ilst, ilst2, ilst3; // empty lists
// after this loop ilst contains: 3 2 1 0
for (list<int>::size_type i = 0; i != 4; ++i)
ilst.push_front(i);
// after copy ilst2 contains: 0 1 2 3 // 与原容器顺序相反
copy(ilst.begin(), ilst.end(), front_inserter(ilst2));
// after copy, ilst3 contains: 3 2 1 0 // 与原容器顺序没变
copy(ilst.begin(), ilst.end(), inserter(ilst3, ilst3.begin() ));

@学习摘录118:iostream迭代器的构造函数
——istream_iterator<T> in(strm); 创建从输入流strm中读取T类型对象的istream_iterator对象
——istream_iterator<T> in; istream_iterator 对象的超出末端迭代器
——ostream_iterator<T> in(strm); 创建将T类型的对象写到输出流strm的ostream_iterator对象
——ostream_iterator<T> in(strm, delim); 创建将T类型的对象写到输出流strm的ostream_iterator 对象,在写入过程中使用delim作为元素的分隔符。delim是以空字符结束的字符数组。

@学习摘录119:流迭代器的定义
istream_iterator<int> cin_in(cin); // reads ints from cin
istream_iterator<int> end_of_stream; // end iterator value
// writes Sales_items from the ofstream named outfile
// each element is followed by a space
ofstream outfile;
ostream_iterator<Sales_item> output(outfile, “ “);

@学习摘录120:istream_iterator对象上的操作
——下面的程序,衔从cin中读取int型数据,然后,使用eof作为结束符;
——在循环中,从cin中读取int型数据,并将读入的内容保存在vec中,再实现自增。
istream_iterator<int> in_iter(cin); // read ints from cin
istream_iterator<int> eof; // istream “end” iterator
// read until end of file, storing what was read in vec
while(in_iter != eof)
// increment advances the stream to the next value
// dereference reads next value from the istream
vec.push_back(*in_iter++);
——也能直接用构造函数的方法新建对象:如下
istream_iterator<int> in_iter(cin); // read ints from cin
istream_iterator<int> eof; // istream “end” iterator
vector<int> vec(in_iter, eof); // construct vec from an iterator range

@学习摘录121:ostream_iterator对象的使用
——可使用ostream_iterator对象将一个值序列写入流中,其操作的过程与使用迭代器在将一个组值逐个赋给容器中的元素相同。
// write one string per line to the standard output
ostream_iterator<string> out_iter(cout, “\n”);
// read strings from standard input and the end iterator
istream_iterator <string> in_iter(cin), eof; // 此处会先输入第一次
// read until eof and write what was read to the standard output
while(in_iter != eof)
// write value of in_iter to standard output
// and then increment the iterator to get the next value from cin
*out_iter++ = *in_iter++; // 由于那次已输入了第一次,这次会再要求输入一次再显示
摘录有想121:
——程序经测试,每当输入一行字符后,则在下次输入另一行字符后显示上一行输入的字符。
——该程序可从while语句入手去想,逐行输入,放入in_iter,之后out_iter读取并显示。
——解引操作符能够使用数据。

@学习摘录122:与算法一起使用迭代器
——先看下面的例子:
istream_iterator<int> cin_it(cin); // reads ints from cin
istream_iterator<int> end_of_stream; // end iterator value
// initialize vec from the standard input:
vector<int> vec (cin_it, end_of_stream);
sort(vec.begin(), vec.end() );
// writes ints to cout using “ “ as the delimiter
ostream_iterator<int> output(cout, “ “);
// write only the unique elements in vec to the standard output
unique_copy(vec.begin(), vec.end(), output);
摘录有想122:
——以前总是看不懂这类程序,我想,现在看完这章的内容后,基本上还是理解了这类程序写的是什么了。
——前两行不断输入,直到遇到结束符,第三行,将输入的全部int逐个放入vec中;
——第四行,对容器进行排序。
——第五行,在每个cout 之间输入” “;
——最后行,符合算法的格式,unique_copy算法,unique的“复制”版本,将输入范围内不重复的值输出,每个复制值后输出一个空格。

第四节:泛型算法的结构
——算法最基本的性质是需要使用的迭代器种类

@学习摘录123:根据对元素的操作将算法分为下面三种:
——1. 只读算法,不改变元素的值和顺序。
——2. 给指定元素赋新值的算法。
——3. 将一个元素的值移给另一个元素的算法。

@学习摘录124:算法的形参模式
——1. alg(beg, end, other parms);
——2. alg(beg, end, dest, other parms);
——3. alg(beg, end, dest, other parms);
——4.alg(beg, end, beg2, end2, other parms);
——注:dest形参是一个迭代器,用于指定存储输出数据的目标对象。

@学习摘录125:迭代器的分类
——迭代器可通过其所支持的操作来分类。
——1. 输入迭代器(input iterator):读,不能写;只支持自增运算
——2. 输出迭代器(output iterator):写,不能读;只支持自增运算
——3. 前向迭代器(forward iterator):读和写,只支持自增运算
——4. 双向迭代器(bidirectional iterator):读和写,支持自增和自减运算
——5. 随机访问迭代器(random-access iterator):读和写,支持完整的迭代器算术运算

@学习摘录139:合成的默认构造函数
——只要一个类定义了一个构造函数,编译器再也不会生成默认构造函数。
——只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。

@学习摘录148:复制构造函数与默认构造函数
——一般来说,最好显式或隐式定义默认构造函数和复制构造函数。
——只有不存在其他构造函数时才合成默认构造函数。
——如果定义了复制构造函数,也必须定义默认构造函数。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics