[GeekBand][C++ STL与泛型编程]第六周笔记

课堂大纲
这周的课程大致上应该是这三个部分

  1. C++模板介绍
  2. 泛型编程
  3. 容器

概述STL与泛型编程

泛型编程作为一种编程思维和想法,它是一种编程方法,不依赖于具体的语言。
STL中主要由容器迭代器算法三个部件构成。

容器用来管理对象的集合,每一种容器都有自己的优缺点,储存的方式等都有所不同,使用时需根据程序的需求考虑不同容器的效率来选择
迭代器为所有容器提供了一组公共接口,并且,每一种容器都提供自己的迭代器
STL中把数据和算法分开,赋予了STL极大的弹性
下图演示了三个部件之间的交互关系

stl.png

可以看出,迭代器是容器和算法之间的接口,总体说来,STL使容器与算法分离,使其二者不需要相互依赖,而迭代器又将算法和不同的容器stick在一起,从而使需要的算法能够运用到不同的容器上

Templates and Generic Programming

  • 泛型技术(Generic Programming)。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
  • 模板(Templates)是泛型编程的基础

C++模板介绍

C++主要有两种类型的模板

  1. 类模板(Class template):使用泛型参数的类
  2. 函数模板(Function template):使用泛型参数的函数

模板实例化:显示和隐式两种方式
类模板实参可以是某一型别或常量(仅限int或enum)
C++类模板的声明注意事项:

  1. 声明类模板和声明函数模板类似
  2. 关键字class和typename都可以用,但还是倾向于使用typename
templateclass Stack{......};
template class Stack{......};

3.在类模板内部,T可以像其他型别一样(比如int,char等)定义成员变量和成员函数

void Push(const T const& element);
int Pop(T& element);
int Top(T& element) const;
std::vector m_Members;

泛型编程(Generic programming)

泛型编程作为一种编程思维和想法,它是一种编程方法,不依赖于具体的语言。

  1. 关联特性(Traits)
    Traits可以实现为模板类, 而关联则是针对每个具体型别T的特化.
template< typename T > class Simg{ };
template<  > class Simg< char>{ 
public: typedef int ReturnType ;
};
template<  > class Simg< short>{ 
public: typedef int ReturnType ;
};
template<  > class Simg< int>{ 
public: typedef long ReturnType ;
};

Traits实现: (函数定义)

template< typename T>
typename Simg< T >::ReturnType Sigma (  const T const* Start, const T const* end)
{
    Simg< T >::ReturnType type;
    type r  = ReturnType();
    return r ;                        //这里返回的就是特性的新类型}
  1. 迭代器(Iterators):迭代器是指针的泛化(generalization of pointers)
    2.1 迭代器本身是一个对象,指向另外一个(可以被迭代的)对象。
    2.2 用来迭代一组对象,即如果迭代器指向一组对象中的某个元素,则通过increment以后它就可以指向下一组对象中的一个元素。

迭代器是指针的泛化(generalization of pointers)

  • 迭代器本身是一个对象,指向另外一个(可以被迭代的)对象
  • 用来迭代一组对象,即如果迭代器指向一组对象中的某个元素,则通过increment以后它就可以指向这组对象的下一个元素
  • 在STL中迭代器是容器与算法之间的接口
  • 算法通常以迭代器作为输入参数
  • 容器只要提供一种方式,可以让迭代器访问容器中的元素即可。

迭代器的基本思想

  1. 在STL中,迭代器最重要的思想就是分离算法和容器,使之不需要相互依赖
  2. 迭代器将算法和容器粘合(stick)在一起从而使得一种算法的实现可以运用到多种不同的容器上,如下面的例子所示,find算法接收一对迭代器,分别指向容器的开始和终止位置:
templalte
inline _InIt find(_InIt _First,_InIt _Last,const _Ty& _Val){
//find frist matching _Val
for(;_First != _Last;++_First)
if(*_First == _Val)
break;
return (_First);
}

容器container

总的来说,容器可以分为两类:

  1. 顺序式容器sequence containers
    排列顺序与置入次序一致,例如vector,deque,list
  2. 关联式容器associative containers
    sort群集,元素的顺序取决于特定的排序规则,例如set,multiset,map,multimap.

顺序容器

Vector
  1. Vector是一个能够存放任意型别的动态数组
  2. Vector的数据结构和操作于数组类似,在内存中的表示是一段地址连续的空间。
  3. Vector与数组的区别在于,数组大小往往是定义的时候已经指定,而Vector支持动态空间大小调整,随着元素的增加,Vector内部会自动扩充内存空间。
  4. 为了使用Vector,必须用include指令包含如下文件,并通过std命名空间去访问:
#include
int main(){
std::vector v;
}

创建一个vector,提醒两个比较特殊的:

vector<T> v(v,i);  //创建一个容量为n的T型别的vector,并都初始化为i
int a[] = {1,2,3,4,5,6,7,8,9,10};
vecotr<int> v(a,a+10);  //用数组创建vector

相关函数:

vector我们平时使用的比较多,push_back(),empty()都是常用的就不说了,注意vector没有push_front(),因为对于vector来说如果要push_front()效率很低所以不提供这个函数。
对于vector的at()和operator[],at会做边界检查,但效率比operator低。

删除元素的操作:

clear()直接清空
pop_back()弹出尾部
erase()借助迭代器清除(传入迭代器,也就是位置,也可以是一个范围)
iterator erase (iterator position);
iterator erase (iterator first, iterator last);

另外,我们可以用eraze结合remove_if(定义在<algorithm>中)来删除我们指定的元素:
首先,我们需要定义一个删选器:用一个一元对象(unary_function),其关键在于重载operator():

struct ContainsString:public unary_function<string,bool>
{
ContainsString(const string& szMatch) :m_szMatch(szMatch) {}
bool operator()(const string& szStringToMatch)const {
  return (szStringToMatch.find(m_szMatch) != -1);
}

string m_szMatch;
};

还记的我们上面所说的erase的用法么,第二个用法第一个参数为eraze的起点,我们的erase函数这样写:

v.erase(remove_if(
  v.begin(),
  v.end(),
  ContainsString("bushuang")
),
  v.end());

我们看remove_if的用法:

template <class ForwardIterator, class UnaryPredicate>
ForwardIterator remove_if (ForwardIterator first, ForwardIterator last,
                           UnaryPredicate pred);

第三个参数可以看到就是一个一元条件,而前两个参数为起止迭代器,返回值是迭代器,所以现在就很清楚了,通过remove_if得到一个迭代器位置,然后从该位置把后面的东西都删了。

remove_if属于移除性算法,根据元素值或某一准则,在一个区间内移除某些元素。这些算法并不能改变元素的数量,它们只是以逻辑上的思考,将原本置于后面的“不移除元素”向前移动,覆盖那些被移除元素而已,它们都返回新区间的逻辑终点(也就是最后一个“不移除元素”的下一位置)。

Deque

Deque是一个能够存放任意型别的双向队列,故比vector多了push_front和pop_front两个函数,而我们之前提到过vector因为效率原因不提供这两个函数,所以deque必然与vector有不同的内存管理方法:大块的内存分配。
如果不是要在前面插入,一般我们不会去用Deque,所以也就不多说了,其用法和vector差不多,只是效率上的差别

List

list相较于vector和deque,其优势在于可以很快的随意插入和删除元素,对于插入,删除,替换等操作,效率极高,合并list,也仅仅只需要节点的链接

相关函数:

void remove (const value_type& val);直接删除指定内容的元素
erase与上面差不多,不多说了

具体说一下splice

void splice (iterator position, list& x);  //entire list 
void splice (iterator position, list& x, iterator i);  //single element 
void splice (iterator position, list& x, iterator first, iterator last);  //element range

简单说明一下参数,第二个函数剪贴单个元素,第三个参数为传入list x的迭代器;
第三个函数后两个参数为传入list x的迭代器范围
用以下代码说明用法:

// splicing lists
#include <iostream>
#include <list>
int main ()
{
std::list<int> mylist1, mylist2;
std::list<int>::iterator it;

// set some initial values:
for (int i=1; i<=4; ++i)
   mylist1.push_back(i);      // mylist1: 1 2 3 4

for (int i=1; i<=3; ++i)
   mylist2.push_back(i*10);   // mylist2: 10 20 30

it = mylist1.begin();
++it;                         // points to 2

mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
                              // mylist2 (empty)
                              // "it" still points to 2 (the 5th element)

mylist2.splice (mylist2.begin(),mylist1, it);
                              // mylist1: 1 10 20 30 3 4
                              // mylist2: 2
                              // "it" is now invalid.
it = mylist1.begin();
std::advance(it,3);           // "it" points now to 30

mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());
                              // mylist1: 30 3 4 1 10 20

std::cout << "mylist1 contains:";
for (it=mylist1.begin(); it!=mylist1.end(); ++it)
  std::cout << ' ' << *it;
std::cout << '\n';

std::cout << "mylist2 contains:";
for (it=mylist2.begin(); it!=mylist2.end(); ++it)
  std::cout << ' ' << *it;
std::cout << '\n';

return 0;
}

里面用到的advance函数作用是移动迭代器(第二参数可为负数)
我们在使用splice的时候,注意iterator 的位置,advance函数第二参数不要让迭代器超过范围,否则将导致不可预知的问题。另外,list还有sort()与merge()函数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容