C++标准模版库使用简介

C++标准模版库使用简介
C++标准模版库使用简介

C++标准模板库使用

目录

一、模板简单介绍: (3)

1. 函数模板 (3)

2. 类模板 (4)

二、STL概论 (6)

三、STL的组件以及关系 (6)

四、常用容器介绍 (7)

1. 序列式容器 (7)

1.1 Vector (7)

1.2 List (16)

2. 关联式容器: (22)

2.1 Set (22)

2.2 multiset (24)

2.3 map (27)

2.4 multimap (29)

五、写在后面 (34)

六、附录:如何选择容器 (34)

一、模板简单介绍:

1.函数模板

请看看下面这个题目:

实现一个函数,输入2个变量,输出这两个变量中值比较大的元素。

要求:此函数可以接受int、char以及double类型的参数。

对于这个问题,如果是C语言的话,估计实现会是这个样子:

// 用于比较char的函数

char MaxOfChar( char cNum1, char cNum2 )

{

return ( cNum1 > cNum2 ) ? cNum1 : cNum2;

}

// 用于比较int的函数

int MaxOfInt( int iNum1, int iNum2 )

{

return ( iNum1 > iNum2 ) ? iNum1 : iNum2;

}

// 用于比较double的函数

double MaxOfDouble( double dNum1, double dNum2 )

{

return ( dNum1 > dNum2 ) ? dNum1 : dNum2;

}

但是到了C++时代,由于存在重载的概念,所以实现起来应该是这个样子:

// 用于比较char的函数

char Max( char cNum1, char cNum2 )

{

return ( cNum1 > cNum2 ) ? cNum1 : cNum2;

}

// 用于比较int的函数

int Max( int iNum1, int iNum2 )

{

return ( iNum1 > iNum2 ) ? iNum1 : iNum2;

}

// 用于比较double的函数

double Max( double dNum1, double dNum2 )

{

return ( dNum1 > dNum2 ) ? dNum1 : dNum2;

}

对比上面两个例子,对于函数的实现来说,代码量没有什么变化,只不过函数的名字由3个变成了1个。这样并非没有意义,对于使用这个函数的用户来讲,他的工作将会减少,对于C++的实现方式,完全没有必要去记住哪个函数对应哪种数据类型,因为不管是针对哪种数据类型的比较,只需要简单的调用Max()就可

以了。

那么还有没有更好的解决方式呢?答案是肯定的,那就是模板。请看下面的代码:

template < class T >

T Max( const T &Input1, const T &Input2 )

{

return ( Input1 > Input2 ) ? Input1 : Input2;

}

OK,就这么简单,一个比较函数完成了,它可以接受任何类型的参数,包括上边提到的int、char、double,甚至任何自定义类型(只要你实现了它的比较运算符operator > )。

这就是一个模板(函数模板)的例子,从这个例子可以看出运用模板的好处,更重要的是,具体选用什么样的实际函数是在编译时刻就决定好的,丝毫不会影响运行时的效率。

PS:对于这个特定的例子,还有更简单的方式,就是用宏:

#define Max( Input1, Input2 ) ( ( Input1 > Input2 ) ? Input1 : Input2 )

当然我们不建议这么做,因为宏没有类型检查。

上面是一个函数模板的实现,我们在后面还要提到类模板。现在先让我们来看看模板的格式。

template < class T >————〉○1

T Max( const T &Input1, const T &Input2 )

{

return ( Input1 > Input2 ) ? Input1 : Input2;

}

○1这个是模板头,此项是必须的,其中T是一个替代符,它可以是除去固有符号(int 、long等)的任何字符,用来表示一个类型(可以是原生类型,也可以是自定义类型)。

除去模板头,其它地方的书写和正常的函数定义是完全一样的。

请注意,在上面的Input参数中,我用的是const&的,这样做的好处是减少函数调用时候的开销,因为我们不知道T类型到底有多大。

2.类模板

在很多情况下,你可能会需要设计一个方法类或者容器类,通常对于这种用于实际应用的类会被要求支持多种数据类型。如果用传统的类设计方法来设计的话,可能就需要设计多个类或者在类里面定义多个不同的方法来支持不同的数据类型(就像前面那个比较函数一样),这时候就需要用到模板类的设计方法。请看下面的题目:

设计一个容器类,要求可以支持多种数据类型,支持添加元素、删除元素等功能。

该如何设计这个类呢?是不是还需要很多个支持各种类型的成员函数?比如说AddInt(),AddChar(),DelInt(),DelChar()……。完全不需要,在这里我们用到的应该是类模板,看下面的例子:

#define MAX_NUM 100

#define SUCCESS 0

#define FAILURE -1

template < class T >

class CVector

{

public:

CVector() : m_Size(0)

{

}

unsigned int Size()

{

return m_Size;

}

int push_back( const T& Element )

{

if( m_Size > MAX_NUM - 1 )

{

return FAILURE;

}

DataList[ m_Size ] = Element;

m_Size++;

return SUCCESS;

}

int pop_back( T &Element )

{

if( 0 == m_Size )

{

return FAILURE;

}

Element = DataList[ m_Size - 1 ];

m_Size --;

return SUCCESS;

}

private:

unsigned int m_Size;

T DataList[ MAX_NUM ];

};

这是一个简单的用模板实现的容器类,他可以支持各种数据类型,支持尾部添加和尾部删除操作。使用的时候应该是这种方式:

CVector<

TypeList.pop_back( Element );

………

可以看到类模板的格式与函数模板类似,只是使用的时候存在区别,类模板在使用的时候需要进行特化,就是告诉编译器我需要什么类型的实现(实际上函数模板也需要特化,只不过这个过程是隐式的)。

CVector< TypeName > TypeList;

实际上,我们刚刚实现了一个标准模板库(STL)中的Vector,只不过真正的Vector比这复杂的多,因为它需要考虑到各种性能和空间的因素,行为也很复杂。不过STL设计的思想正是基于模板。

二、STL概论

STL,虽然是一套程序库(Library),确不是一般印象中的程序库,而是一个有着划时代意义,背后拥有着先进技术与深厚理论的产品。说他是产品也可以,说他是规格也可以,说是软件组件技术发展史上一个大突破点,它也当之无愧。

长久以来,软件界一直希望建立一种可重复运用的东西,以及一种可以制造出“可重复运用的东西”的方法,让工程师/程序员的心血不至于随时间的推移、人事异动而烟消云散。从副程式(subroutines)、程序(procedures)、函数(functions)、类别(classes),到函数库(function libraries )、类别库(class libraries )、各种组件(components),从结构化设计、模组化设计、物件导向设计,到样式(patterns)的归纳整理,无一不是软件工程的漫漫奋斗史,为的就是复用性(reusebility)的提升。

————摘自《STL源码分析》

STL就是在这个背景下诞生的。STL的价值在两方面。低层次而言,STL带给我们一套急具价值的零组件,这种价值就像MFC对于Windows开发过程所带来的价值一样,直接而明朗。除此之外STL还带给我们一个高层次的以泛型思维为基础的设计理念。在这个教程里面,我们只会提到如何使用STL,对于深层次的东西,需要读者自己去慢慢体会。

三、STL的组件以及关系

STL提供6大组件,彼此可以组合套用:

1.容器(containers):各种资料结构,如Vector、List、Map等,用来存储各种数据。

2.演算法(algorithms):各种常用的算法,如sort、search、copy等,它的作用是为提供各种常用的

操作。

3.迭代器(iterators):一个非常重要的组件,用来将容器和演算法联系起来。也就是通常所说的泛型指

针。

4.仿函数(functors):行为类似函数,可作为演算法的某种策略(policy)。

5.配接器(adapters):一种用来修饰容器或仿函数界面的东西。

6.配置器(allocators):负责空间配置与管理,用来保证容器空间的正确分配。

STL六大组件的关系如下图所示:Container通过Allocator取得数据存储空间,Algorithm通过Iterator存取Container内容,Functor可以协助Algorithm完成不同的策略变化,Adapter可以修饰或者套接Funtor。

在本文中,只会简单介绍容器的使用方法,可能会涉及一些迭代器或者演算法。更深一步的东西需要自行学习。

四、常用容器介绍

容器基本上可以分为两大类,序列式容器和关联式容器。

1.序列式容器

所谓序列式容器,就是说容器中的元素都可序(Ordered),但未排序(Sorted)。

序列式容器包括:Vector、List、Queue、Stack等。

1.1Vector

1.1.1 Vector基本结构

Vector的行为方式完全是一个数组,它的内存空间完全是连续的,只不过这个数组的大小是可变的,就是说你完全可以不必关心你到底向这个数组里面添加了多少个元素,只管继续添加就对了(当然内存耗尽的情况除外)。

实际上,Vector的实现就像文章开头所举的例子一样,在初始化的时候,会申请一定的空间用来存储数据,一旦所申请的空间不够用了,它会在另外的地方开辟一块新的内存空间,然后将原空间中的元素全部拷贝到新的空间中,如果不特殊指定,每次新申请的通常是原空间的两倍。

Vector的空间操作看起来像下面这个样子:

// 申请一个Vector,向其加入1个元素,默认值是0

vector iv(1)

此时iv.size() == 1; //元素个数

iv. capacity() == 1 //空间大小

//向Vector尾部加入一个元素“1”

iv.push_back(1);

iv.size() == 2

iv.capacity() == 2

//向Vector尾部加入一个元素“2”

iv.push_back(2);

在这个操作之前,Vector空间数为2,向Vector中加入元素的时候发现空间不够,则以原空间的2倍申请一块新的内存。

iv.size() == 3;

iv.capacity() == 4;

可以观察到,Vector申请的空间要比其元素个数多,剩余的空间作为预留空间,用于以后的元素Push操作。这时Vector的一个策略,前面提到过,Vector的空间是连续的,而连续空间的申请和释放操作都相当耗费时间,所以为了避免频繁申请和释放操作,就采用了“预留空间”这个策略。但这样的做的坏处也非常明显,那就是空间浪费。

1.1.2 Vector的常用成员函数

构造函数:

vector()

说明:默认构造函数

Example:vector< int > iv;

vector(SizeType count )

说明:构造一个vector,初始元素个数为count,初始值均为0

Example: vector< int > iv(3);

vector(SizeType count,ConstType &Val )

说明:构造一个vector,初始元素个数为count,初始值均为Val

Example: vector< int > iv( 3, 2 );

vector(const _vector& SourceVector )

说明:构造一个Vector,并将SouceVector中的元素完全Copy到构建的Vector中

Example:vector< int > iv1;

vector< int > iv2( iv1 );

其他成员函数:

reference at(size_type Pos )

说明:取得vector在Pos位置的元素的引用,此函数和数组下标用法类似。

Example:vector < int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

int i = iv.at(2); //此时i的值应该是3,

reference back()

说明:返回vector最尾部元素的引用,相当于at( LastPos )。

Example:vector < int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

int i = iv.back();//此时i的值应该是3,

iterator begin()

说明:返回Vector初始位置的迭代器(iterator)。————〉○2

Example:vector < int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

vector::iterator It = iv.begin();

int i = *It; //此时i的值应该是1,

○2在这里顺便简单介绍一下迭代器(iterator)。为了将容器和演算法联系起来,需要一种指向容器中元素的指针,它的使用方法必须类似于原生指针,也就是说我们可以利用*Iter取得元素本身,也可以利用Iter->调用元素的成员函数,甚至我们可能利用Iter++或者Iter--方式来取得元素序列中前一个或者后一个元素(当然某些容器的迭代器不支持这样的操作)。为了更好的封装迭代器的内部实现以及提高性能,每一个容器都有其独立的迭代器,这些特定的迭代器都是根据容器本身的特性来进行设计的,充分的考虑到了性能和空间上的问题。对于Vector来讲,它的迭代器在使用的时候完全可以等同于一般的指针。

Size_type capacity()

说明:返回vector当前空间大小,以元素个数为单位。

Example:vector < int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

int iSapce = vi.capacity(); //此时iSPace的值应该是4,

void clear()

说明:清空vector中所有元素,但不释放空间

Example:vector < int > iv;

iv.push_back( 2 );

iv.push_back( 3 );

iv.clear();

int iSize = iv.size(); //此时iSize = 0

int iSapce = iv.capacity(); //此时iSapce的值应该是4,

bool empty()const

说明:判断vector是否为空。

Example:vector < int > iv;

iv.push_back( 1 );

iv.empty();//结果应该为假

iv.clear();

iv.empty();//结果应该为真

iterator end()

说明:返回指向vector(最后一个元素+1)的迭代器,通常用来判断循环是否结束。Example:vector < int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

//循环整个Vector

for( vector::iterator it = iv.begin(); it != iv.end(); ++it )

{

Do Som ething….

}

iterator erase(iterator Where )

iterator erase(iterator First,iterator Last )

说明:删除一个位置为“Where”的元素,或者删除从“First”到“Last -1 ”的元素

Example:vector< int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

iv.erase( iv.begin() + 1 ); //删除元素“2”

iv.erase( iv.begin(), iv.begin() + 2 ); //删除元素“1”和“3”

erase的参数Where和First 、Last必须在[ begin(),end()]范围之内,否则会发生非法访问的错误。

reference front()

说明:返回vector最前面元素的引用,相当于at( FirstPos )。

Example:vector< int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

int i= iv.front(); //此时i = 1;

iterator insert(iterator Where,const Type & Val )

说明:在“Where”位置插入指定元素“Val”。

Example:vector< int > iv;

iv.push_back( 1 );

iv.push_back( 3 ); //此时Vector中的元素为1,3

iv.insert(iv.begin() + 1, 2 ); //此时Vector中的元素为1,2,3 insert的位置必须在[ begin(),end()]范围之内,否则会发生非法访问的错误。

void push_back( const Type &Val )

说明:在Vector尾部加入一个元素

void pop_back()

说明:删除Vector尾部的元素

Example:vector< int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );//此时元素为1,2,3

iv.pop_back();//此时元素为1,2

size_type size() const

说明:返回Vector中元素的个数。

reference operator[]( size_type Pos )

说明:[]操作符,使vector可以像数组一样访问

Example:vector< int > iv;

iv.push_back( 1 );

iv.push_back( 2 );

iv.push_back( 3 );

//iv[0] = 1,iv[1] = 2, iv[2] = 3

请注意:

1、operator[]只能用来取得已经存在的元素,而不能向vector中添加元素,例如下面的代码是错误

的:

vector< int > iv;

iv[ 0 ] = 1; //错误,vector的空间还没有被分配

2、operator[]和正常数组的使用方法一样,也同样没有越界检查,比如你通过iv[10] 的方式访问一

个只有1个元素的vector,不会被提示出错,但这样做可能会有不可预计的事情发生。

1.1.3 Vector综合示例:

题目:实现一个存储正整数的类,此类中的数据用随机数的方式进行填充。提供的方法包括:打印所有的整数、打印数据中所有的素数、判断某随机位置上的数字是否为素数、删除所有的素数

分析:考虑到此数据列表需要支持随机操作,并且不用关心元素所处的位置,所以用Vector来实现是个不错的选择

实现:

#include

#include

#include

#include

using namespace std;

class CDataSet

{

public:

static enum ePrintRange

{

ALLDATA = 0, PRIMEDATA = ALLDATA + 1

};

// 用随机数构造一个数据列表

// iInitDataCnt: 随机数的个数; iMaxData: 随机数中的最大数字

CDataSet( unsigned int iInitDataCnt, unsigned int iMaxData )

: m_DataCnt( iInitDataCnt )

{

srand( ( unsigned )time( NULL ) );

for( int iLoop = 0; iLoop < m_DataCnt; ++iLoop )

{

m_DataList.push_back( (unsigned int)rand() % iMaxData );

}

}

// 删除数据列表中的所有素数

int DeletePrimeData()

{

int iDeleteCnt = 0;

vector< int >::iterator it;

for( it = m_DataList.begin(); it != m_DataList.end(); ++it )

{

if( m_bIsPrimeData( *it ) )

{

m_DataList.erase( it );

m_DataCnt--;

iDeleteCnt++;

}

}

// 返回删除元素的个数

return iDeleteCnt;

}

// 按照传入的打印范围进行数据打印

v oid Print( ePrintRange iPrintRange ) const

{

switch ( iPrintRange )

{

case ALLDATA:

{

m_PrintAllData();

break;

}

case PRIMEDATA:

{

m_PrintPrimeData();

break;

}

default:

{

break;

}

}

}

// 判断iDataIndex位置的数字是否为素数

b ool IsPrimeData( unsigned int iDataIndex )

{

if( iDataIndex >= m_DataCnt )

{

return false;

}

return m_bIsPrimeData( m_DataList[ iDataIndex ] ); }

private:

// 判断一个数是否是素数

b ool m_bIsPrimeData( int iInputData ) const

{

for( int iLoop = 2; iLoop < iInputData; ++iLoop )

{

if( 0 == iInputData % iLoop )

{

break;

}

}

return ( ( iLoop == iInputData ) && ( iLoop != 1 ) );

}

//打印所有整数

v oid m_PrintAllData() const

{

cout << " All Data List: " << endl;

for( int iLoop = 0; iLoop < m_DataCnt; ++iLoop )

{

cout << m_DataList[ iLoop ] << "\t";

}

cout << endl;

}

//打印所有素数

v oid m_PrintPrimeData() const

{

cout << "Prime Data List: " << endl;

for( int iLoop = 0; iLoop < m_DataCnt; ++iLoop )

{

if( m_bIsPrimeData( m_DataList[ iLoop ] ) )

{

cout << m_DataList[ iLoop ] << "\t";

}

}

cout << endl;

}

vector< int > m_DataList;

int m_DataCnt;

};

//客户端实现

void main()

{

//构造一个数据列表,里面存储10个数字,每个数字是小于100的随机数字CDataSet DataList( 10, 100 );

DataList.Print( CDataSet::ALLDATA );

DataList.DeletePrimeData();

DataList.Print( CDataSet::ALLDATA );

}

大家可以试一试刚刚实现的这个程序,它可以在大部分的情况下工作的很好,但有的时候可能输出和我们预想的并不一致,甚至可能造成程序的崩溃!这是为什么呢?问题处在DeletePrimeData()这个函数,看一看我们的实现:

for( it = m_DataList.begin(); it != m_DataList.end(); ++it )

{

if( m_bIsPrimeData( *it ) )

{

m_DataList.erase( it );

m_DataCnt--;

iDeleteCnt++;

}

}

如果随机产生的数据系列是:7,7,74,39,49......,让我们看一看函数是怎么执行的。

开始的时候,内存以及Iterator如下所示

在经过一次素数判断及删除之后,内存变为:

第一个元素被删除,所有元素向前移位。

Iterator为:

没错,这时候再判断元素的话,判断的将是74而不是第二个7,也就是说,我们漏掉了第二个元素。如果这种问题发生在Vector的尾部的话,就可能造成内存的非法访问而引起程序的崩溃。那这个问题该怎么解决呢?看下面的方法:

void DeletePrimeData()

{

vector< int > TempList;

vector< int >::iterator it;

for( it = m_DataList.begin(); it != m_DataList.end(); ++it )

{

if( !m_bIsPrimeData( *it ) )

{

TempList.push_back( *it );

}

}

m_DataList.assign( TempList.begin(), TempList.end() );

m_DataCnt = m_DataList.size();

}

在这个函数中,申请了一个新的Vector,将原Vector中的非素数全部Copy到新的Vector中,然后将原Vector中的元素清空并用新Vector的元素进行重新填充(m_DataList.assign( TempList.begin(), TempList.end() );)。这样做既可以避免在循环中删除元素带来的Iterator混乱,有可以在一定程度上提高效率(对于Vector中间元素进行操作将有很大的性能问题)

注意:绝对应该禁止在循环体中对Vector进行插入或者删除操作

1.2List

1.2.1 List的基本结构

相对于Vector的线性存储空间,List就复杂的多,它每次添加或者删除一个元素,就会申请或者释放一个元素的空间,然后用指针将他们联系起来。这样的好处就是精确配置内存,绝对没有一点的浪费。而且对于元素的插入和删除,List都是常数时间。

List的基本构成是节点。一下是节点的结构:

template < class T >

struct _list_node

{

Typedef void *void_pointer;

void_pointer prev; //指向前面元素的指针

void_pointer next; //指向后面元素的指针

T data; //节点实体,用来存储数据

}

从上面的结构可以看出,List实际上是一个双向链表(更确切的说是环状)

ListNode ListNode ListNode

下面根据一段程序来分析List的行为方式:

Example:

list< int > il;

for( int iLoop = 0; iLoop < 4; ++iLoop )

{

il.push_back( iLoop );

}

在定义List之后( list< int > il ),会为这个List申请一个元素的空间,il.begin()和il.end()都是指向这块空间,而这个内存中的实体(data)是一个无效的数值。List的结构看起来是下面的样子:

在连续向List中添加4个元素之后,List的结构变为下面的样子:

1.2.2 List的常用成员函数:

List的构造函数和成员函数比较多,在这里只会介绍一些常用的。并且,有一些函数对于STL中的很多容器都是通用的,在这里也不一一介绍了。

常用构造函数:

l ist()

说明:默认构造函数

Example: list < int > il;

l ist( size_type Count )

说明:构造一个list,初始元素个数为Count,初始值均为0

Example: list < int > il( 3 );

list( size_type count, const Type &_Val )

说明:构造一个List,初始元素个数为count,初始值均为_Val

Example: list< int > il( 3, 5 )

list( const list &SourceList )

说明:构造一个List,将SourceList中的所有元素Copy到新构造的List中。

Example: list< int > SourceList( 3, 5 )

list< int > il( SourceList );

其它常用成员函数:

在List中,有很多成员函数的使用方法是等同于Vector的,这些方法包括:back()、begin()、clear()、empty()、end()等等(具体可参考MSDN)。在这些方法里面值得一提的是一些删除元素的方法(clear()、erase()等),在List中,元素删除的同时会清除其原先所占用的空间,这一点和Vector是有区别的。下面介绍一些不同于Vector的方法:

void merge(list< Type, Allocator> &List2 )

说明:对于两个完全按照递增顺序排序的List,这个函数将把List2中的元素Copy到List1种,并且按照递增的方式排序,Copy之后,将List2中的所有元素清空。

Example:

list< int > List1;

list< int > List2;

List1.push_back( 1 );

List1.push_back( 3 );

List1.push_back( 5 );

List2.push_back( 2 );

List2.push_back( 4 );

List2.push_back( 6 );

List2.merge( List2 );

调用Merge之前,List1 : 1, 3, 5 List2: 2, 4, 6

调用Merge之后,List1 : 1, 2, 3, 4, 5, 6 List2: 空

请注意,调用Merge之前,必须保证两个List都是完全排序的,不然会产生一个比较混乱的结果,而一般情况下,这种情况并不是用户的本意(产生这种结果的原因可参考Merge的实现方法)。如果是针对用户自定义类型的List,在使用这个函数之前,还要保证实现了operator < 。

void pop_front()

说明:删除List中的第一个元素,在Vector中没有实现这个成员函数,因为在Vector中如果需要删除首元素的话,可能会带来效率上的很大损失。

Example:

list< int > il;

il.push_back( 1 );

il.push_back( 2 );//List中元素为:1,2

il.pop_front(); //List中元素为:2

void push_front( const Type &_Val )

说明:向List的首位插入一个元素。

Example:

list< int > il;

il.push_front( 1 );

void sort()

说明:对List按照从小到大的顺序进行排序,对于自定义类型必须实现operator <

Example:

list< int > il;

il.push_back( 3 );

il.push_back( 1 );

il.push_back( 2 );//List中的元素:3,1,2

il.sort(); //List中的元素:1,2,3

1.2.3 List的综合示例:

题目:有一个书架,上面的图书按种类分类(种类自定),现在有一批新的图书需要按照各自的种类插入到此类图书的末尾位置。

分析:每种图书都有自己的种类,并且每类图书按照次序存放,说明原有的顺序不应该被打乱,并且这个书架可能需要频繁的中间插入操作。考虑到List在中间插入和删除的常数效率,这个问题的容器选择List 是很好的。

实现:

#pragma warning(disable: 4786)

#include

#include

#include

using namespace std;

#define NOINDEX -1

enum eBookKind

{

COMPUTER = 0,

ENGLISH = COMPUTER + 1,

CHEMISTRY = ENGLISH + 1,

PHYSICS = CHEMISTRY + 1,

ALLBOOKKIND = PHYSICS + 1,

};

struct Book_t

{

bool operator == ( const Book_t &_Right ) const

{

return ( iBookKind == _Right.iBookKind );

}

int iBookKind; // Book的种类

int iBookIndex; // Book在一类图书中的位置

static int KindCnt[ ALLBOOKKIND ]; // 静态成员,表示每种图书的数量};

int Book_t::KindCnt[ ALLBOOKKIND ] = { 2, 1, 2, 1 };

void Print( list< Book_t> StudentList )

{

list< Book_t >::iterator it;

for( it = StudentList.begin(); it != StudentList.end(); ++it )

{

cout << it->iBookKind << "\t" << it->iBookIndex << endl;

}

}

list< Book_t >::iterator Seek( list< Book_t >::iterator it, int iSeekCnt )

{

for( int iLoop = 0; iLoop < iSeekCnt; ++iLoop )

{

it++;

}

return it;

}

void main()

{

// 现有图书

const Book_t BookList[ ] =

{

COMPUTER, 0,

标准模板库STL学习总结

标准模板库STL学习总结 标准模板库就是类与函数模板的大集合.stl共有6种组件:容器,容器适配器,迭代器,算法,函数对象和函数适配器. 1、容器: 容器是用来存储和组织其他对象的对象.stl容器类的模板在标 准头文件中定义.主要如下所示 ①序列容器 基本的序列容器是上面图中的前三类: 关于三者的优缺点主要是: a:vector矢量容器:可以随机访问容器的内容,在序列末尾添加或删除对象,但是因为是从尾部删除,过程非常慢,因为必须移动插入或删除点后面的所有对象. 矢量容器的操作:(自己以前有个表,贴出来大家看看)

其中的capacity表示容量,size是当前数据个数.矢量容器如果用户添加一个元素时容量已满,那么就增加当前容量的一半的内存,比如现在是500了,用户添加进第501个,那么他会再开拓250个,总共就750个了.所以矢量容器当你添加数据量很大的时候,需要注意这一点哦... 如果想用迭代器访问元素是比较简单的,使用迭代器输出元素的循环类似如下: vector::iterator表示矢量容器vector的迭代器...for(vector::iteratoriter=number.begin();iter::size_typei=0;i头文件中定义的sort()函数模板来对一个矢量容器进行排序.但是有几点要求需要注意 sort()函数模板用<运算符来排列元素的顺序,所以容器中对象必须可以进行<运算,如果是基本类型,可以直接调用sort(),如果是自定义对象,必须对<进行运算符重载两个迭代器的指向必须是序列的第一个对象和最后一个对象的下一个位置.比 如:sort(people.begin(),people.end());//这里两个参数就是迭代器的意思了 b:deque容器:非常类似vector,且支持相同的操作,但是它还可以在序列开头添加和删除.

C++ 标准模板库(STL)学习总结

C++标准模板库(STL) 顺序性容器 1.C++ Vector(向量容器) vector提供如下函数或操作: clear() 移除容器中所有数据 empty() 判断容器是否为空 erase(pos) 删除pos位置的数据 erase(beg,end) 删除[beg,end)区间的数据 front() 传回第一个数据 back()返回vector中末尾元素的引用 data()返回指向vector内存的指针 insert(pos,elem) 在pos位置插入一个elem拷贝 pop_back() 删除最后一个数据 push_back(elem) 在尾部加入一个数据 resize(num) 重新设置该容器的大小 size() 返回容器中实际数据的个数 max_size()函数返回vector能够容纳的最大元素个数 begin() 返回指向容器第一个元素的迭代器 end() 返回指向容器最后一个元素的迭代器 capacity()返回vector中实际分配的内存大小 reverse()改变vector的容量大小,当vector的容量设定时,vector的容 量不会因此动态分配 operator[ ]()获取vector中元素,这个和C中获取数组元素一样 at()获取vector中的元素,这个和[]的作用一样,不过和[]不同的是,at()函数要对数组的边界进行检查,如果越界就会抛出异常,但是[]不会。

2.C++ List(链表) Lists将元素按顺序储存在链表中. 与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢. assign() 给list赋值 back() 返回最后一个元素 begin() 返回指向第一个元素的迭代器 clear() 删除所有元素 empty() 如果list是空的则返回true end() 返回末尾的迭代器 erase() 删除一个元素 front() 返回第一个元素 get_allocator() 返回list的配置器 insert() 插入一个元素到list中 max_size() 返回list能容纳的最大元素数量 merge() 合并两个list pop_back() 删除最后一个元素 pop_front() 删除第一个元素 push_back() 在list的末尾添加一个元素 push_front() 在list的头部添加一个元素 rbegin() 返回指向第一个元素的逆向迭代器 remove() 从list删除元素 remove_if() 按指定条件删除元素 rend() 指向list末尾的逆向迭代器 resize() 改变list的大小 reverse() 把list的元素倒转 size() 返回list中的元素个数 sort() 给list排序 splice() 合并两个list swap() 交换两个list unique() 删除list中重复的元素

企业标准模板

安全标志申办时提交产品标准的相关建议 为进一步提高矿用产品安全标志申办工作的科学性、规范性,突出审核重点,抓住重点关键问题,对安全标志申办时申请人提交的产品标准提出以下建议,供参考。 1.完全执行国家标准或行业标准的产品,申请人不必提交产品标准。 2.执行国家标准或行业标准,但相关标准中规定需由生产单位确定具体技术参数或性能要求,某些参数或功能超出相关标准的产品,申请人只需提交产品技术条件,技术条件中应明确相关内容。格式和内容参见示例1。 3.对于执行多个国家标准或行业标准以及需要补充要求的产品,申请人需提交产品企业标准,产品企业标准应明确执行标准的条款、需要补充的内容等。产品企业标准编制时,对相关的国家标准、行业标准的某条款引用的,只明确标注该条款号(如:×××执行MT×××-×××中5.6的规定,×××性能检验执行MT×××-×××中6.6的规定等),不必照抄该条款内容。格式和内容参见示例2。 4.无可执行的国家标准或行业标准的产品,申请人需提交产品企业标准,产品企业标准的编写尽量采用简化的格式,主要内容包括产品的适用环境、安全要求、主要技术参数、技术要求及检验方法等。格式和内容参见示例3。

示例1 Q/×××××××(单位名称)产品技术条件 Q/××××××-××××煤矿用隔爆型高压电缆连接器 ××××-××-××发布××××-××-××实施 ××××(单位名称)发布

Q/××× ×××–×××× 前言 本技术条件依据MT/T947-2005《煤矿用隔爆型高压电缆连接器》制定。 本技术条件主要起草人:×××××× 本技术条件批准人:×××

标准模板

(可提供给各参赛选手参考)模板六 大学生创业大赛创业策划书模板 项目名称: 创业团队(个人): 日期: 参赛学校名称: 参赛者组长联系电话: 参赛者电子邮件: 保密承诺 本创业策划书内容涉及本参赛项目的商业秘密,仅对评审专家和有投资意向的投资者公开。本企业请求评审专家和投资企业项目经理收到本创业策划书时做出以下承诺:妥善保管本创业策划书,未经本参赛者同意,不得向第三方公开本创业策划书涉及的商业秘密。 目录 第一部分策划书摘要………………… 第二部分产品/服务…………………… 第三部分行业及市场情况…………… 第四部分组织与管理………………… 第五部分营销策略…………………… 第六部分产品制造…………………… 第七部分融资说明…………………… 第八部分财务计划…………………… 第九部分风险评估与防范…………… 第十部分项目实施进度……………… 第十一部分其它………………………… 备查资料清单…………………………… 第一部分策划书摘要

说明:策划书摘要应该尽量控制在2页纸内完成。 创业策划书摘要应该涵盖该策划书的所有要点,浓缩所有精华,并要求简洁、可信、一目了然。 第二部分产品/服务 产品/服务描述(这里主要介绍拟投资的产品/服务的背景、目前所处发展阶段、与同行业其它企业同类产品/服务的比较,本企业产品/服务的新颖性、先进性和独特性,如拥有的专门技术、版权、配方、品牌、销售网络、许可证、专营权、特许权经营等。) 企业现有的和正在申请的知识产权(专利、商标、版权等): 专利申请情况: 产品商标注册情况: 企业是否已签署了有关专利权及其它知识产权转让或授权许可的协议?如果有,请说明(并附主要条款): 目标市场:这里对产品面向的用户种类要进行详细说明。 产品更新换代周期:更新换代周期的确定要有资料来源。 产品标准:详细列明产品执行的标准。 详细描述本企业产品/服务的竞争优势 (包括性能、价格、服务等方面): 产品的售后服务网络和用户技术支持: 第三部分行业及市场情况 1、行业情况(行业发展历史及趋势,哪些行业的变化对产品利润、利润率影响较大,进入该行业的技术壁垒、贸易壁垒、政策限制等,行业市场前景分析与预测):(1)列表说明过去3年或5年各年全行业销售总额:必须注明资料来源。 (2)列表说明未来3年或5年各年全行业销售收入预测:必须注明资料来源。 2、目标市场情况 (1)图表说明目标市场容量的发展趋势 (2)本企业与目标市场内五个主要竞争对手的比较:主要描述在主要销售市场中的竞争对手。(可以列表说明) 第四部分组织与管理 1、企业基本情况: 拟定的企业名称 预期成立时间 预期注册资本 其中:现金出资额及占股份的比例 无形资产出资额及占股份比例 预期注册地点

建筑智能化工程 全套标准模板

一、前期准备阶段 0 1. 项目管理组织机构 0 2. 工程施工进度计划表 (1) 3. 设备材料供货时间表 (2) 4. 工具借用清单 (3) 5. 技术交底纪要 (4) 6. 图纸会审记录 (5) 7. 设备材料进场检验单 (6) 8. 材料(构配件)、设备进场使用报验单 (7) 9. 施工组织设计报审表 (8) 10. 工程开工报告 (9) 二、施工过程阶段 (10) 11. 施工日志 (10) 12. 设计变更表 (11) 13. 工程洽商会议记录 (12) 14. 工程签证 (13) 15. 设备安装记录 (14) 16. 管道、线缆安装记录 (15) 17. 隐蔽工程验收记录 (16) 三、施工收尾阶段 (17) 18. 工程竣工报告 (17) 19. 工程延期说明 (18) 20. 系统调试合格证书 (19) 21. 系统调试及试运转记录 (20) 22. 设备材料验收交接单 (21) 23. 报验申请表 (22) 24. 移交资料清单 (23) 25. 验收报告 (24) 26. 工程总结报告 (25)

一、前期准备阶段 1. 项目管理组织机构 备注: 1、项目经理、业务负责人及现场施工负责人接收并保存。

2. 工程施工进度计划表

3. 设备材料供货时间表 备注: 1、以上内容由产品采购部填写,并分交项目经理、现场施工负责人及仓管人员; 2、如有设备不能按计划供应或型号更换时,应及时通知项目经理与甲方进行协商,以 便不影响工程工期。

4. 工具借用清单 工程名称: 现场施工负责人(签字、日期):仓管员(签字、日期):

最新标准模板库STL练习题

第十一章标准模板库(STL)习题 一. 基本概念与基础知识自测题 11.1填空题 11.1.1 STL大量使用继承和虚函数是(1)(填对或错)。因为(2)。 答案:(1)错 (2)它使用的是模板技术,追求的是运行的效率,避免了虚函数的开销 11.1.2 有两种STL容器:(1)和(2)。STL不用new和delete,而用(3) 实现各种控制内存分配和释放的方法。 答案:(1)第一类容器 (2)近容器 (3)分配子(allocator) 11.1.3 五种主要迭代子类型为(1)、(2)、(3)、(4)和(5)。 STL算法用(6)间接操作容器元素。sort算法要求用(7)迭代子。 答案:(1)输入(InputIterator) (2)输出(OutputIterator) (3)正向(ForwardIterator) (4)双向(BidirectionalIterator) (5)随机访问(RandomAccessIterator) (6)迭代子 (7)随机访问(RandomAccessIterator) 11.1.4 三种STL容器适配器是(1)、(2)和(3)。 答案:(1)stack(栈) (2)queue(队列) (3)priority_queue(优先级队列) 11.1.5 成员函数end()得到容器(1)的位置,而rend得到容器(2)的位置。算法 通常返回(3)。 答案:(1)最后一个元素的后继位置 (2)引用容器第一个元素的前导位置。实际上这是该容器前后反转之后的end() (3)迭代子 11.1.6 适配器是(1),它依附于一个(2)容器上,它没有自己的(3)函数 和(4)函数,而借用其实现类的对应函数。 答案:(1)不独立的 (2)顺序 (3)构造函数 (4)析构函数 11.1.7 返回布尔值的函数对象称为(1),默认的是(2)操作符。 答案:(1)谓词(predicate)

材料技术标准模板

公建GRC/GFRC工程技术标准 GRC部分 1原材料 ①耐碱玻璃纤维无捻粗纱:氧化错含量不低于14%, 符合 JC/T572一1994的规定。选用陕西玻璃纤维总厂的产品。 ②低碱快硬硫铝酸盐水泥:符合JC714一1996的规定, PH值不大于11.7, 选用唐山水泥场的产品。 ③中砂:洁净河砂, 含泥量不大于0.5%。 ④外加剂:上海花王化学品有限公司的高效缓凝减水剂。 ⑤水:符合国际《混凝土拌合用水标准》 ⑥脱模剂:上海花王化学品有限公司的水性脱模剂。 ⑦钢筋、铁件、石子等, 质量符合一般建筑工程规定。 2产品成型工艺 玻璃纤维经纤维切割机切割成规定长度, 经过气压泵和气动喷头喷出;充分拌合的水泥砂浆经过螺杆泵和气压泵从另一个气压喷头喷出。这样纤维和水泥砂浆在工作面混合, 分层喷射, 形成短切玻璃纤维束二维乱向均匀分布料浆层, 最后人工用铁板收光。一定时间后脱模, 经浸水养护, 保证72小时内充分水份, 强度达到设计 要求后方可启运使用。 3安装施工 3.1施工材料:除GRC制品外, 膨胀螺栓、水泥砂浆、丙烯酸型胶粘剂、密封胶、防锈漆、焊条等, 都是施工制作的配套材料。 3.2施工机具:切割机、钻机、电焊机、水准仪、卷尺、墨

线盒、角尺、 直尺、塞尺、麻线等 3.3现场准备 3.3.1基层清理:清除混凝土基层的污垢、砂浆块、垃圾等附着物, 局部若有突出地方, 应作凿除处理。检查基层的质量是否符合要求。 3.3.2基层面弹出水平线、垂直线以及GRC制品位置线, 必须做到横平竖直, 件与件之间的空隙均匀一致, 且在5二左右。 3.4工艺流程 基层清理→弹线→试排→钻孔→挂板→校正→固定→孔洞修补和接缝填装→清理→密封膏→上涂料。 3.5安装固定方法 3.5.1膨胀螺栓固定法在GRC板上预留螺孔(退拔状), 螺孔间距按设计规定, 一般不大于400mm, 用膨胀螺栓将构件固定在混凝土基层上。 3.5.2电焊固定法 GRC板内预埋铁板或钢筋, 混凝土基层相应位置预埋铁板, 安装时用电焊方法将其固定, 必要时增加联接铁件。柱子做法, 柱脚和柱帽均分为二块拼接而成, 柱身根据自重分段, 每段也由二块拼成。

C++标准模版库使用简介

C++标准模板库使用

目录 一、模板简单介绍: (3) 1. 函数模板 (3) 2. 类模板 (4) 二、STL概论 (6) 三、STL的组件以及关系 (6) 四、常用容器介绍 (7) 1. 序列式容器 (7) 1.1 Vector (7) 1.2 List (16) 2. 关联式容器: (22) 2.1 Set (22) 2.2 multiset (24) 2.3 map (27) 2.4 multimap (29) 五、写在后面 (34) 六、附录:如何选择容器 (34)

一、模板简单介绍: 1.函数模板 请看看下面这个题目: 实现一个函数,输入2个变量,输出这两个变量中值比较大的元素。 要求:此函数可以接受int、char以及double类型的参数。 对于这个问题,如果是C语言的话,估计实现会是这个样子: // 用于比较char的函数 char MaxOfChar( char cNum1, char cNum2 ) { return ( cNum1 > cNum2 ) ? cNum1 : cNum2; } // 用于比较int的函数 int MaxOfInt( int iNum1, int iNum2 ) { return ( iNum1 > iNum2 ) ? iNum1 : iNum2; } // 用于比较double的函数 double MaxOfDouble( double dNum1, double dNum2 ) { return ( dNum1 > dNum2 ) ? dNum1 : dNum2; } 但是到了C++时代,由于存在重载的概念,所以实现起来应该是这个样子: // 用于比较char的函数 char Max( char cNum1, char cNum2 ) { return ( cNum1 > cNum2 ) ? cNum1 : cNum2; } // 用于比较int的函数 int Max( int iNum1, int iNum2 ) { return ( iNum1 > iNum2 ) ? iNum1 : iNum2; } // 用于比较double的函数 double Max( double dNum1, double dNum2 ) { return ( dNum1 > dNum2 ) ? dNum1 : dNum2; } 对比上面两个例子,对于函数的实现来说,代码量没有什么变化,只不过函数的名字由3个变成了1个。这样并非没有意义,对于使用这个函数的用户来讲,他的工作将会减少,对于C++的实现方式,完全没有必要去记住哪个函数对应哪种数据类型,因为不管是针对哪种数据类型的比较,只需要简单的调用Max()就可

C++-标准模板库学习

哈尔滨工业大学计算机科学与技术学院 结课论文: 标准模板库学习 课程名称:C++程序设计 课程类型:必修

目录 标准模板库学习 (1) 一容器实现 (4) _List_node_base (4) _List_node (4) _List_iterator (5) _List_const_iterator (6) _List_base (7) List (9) 结构关系 (10) 二构造与分配 (11) List (11) Assign (13) 三成员函数 (14) Iterators (14) Capacity/Size: (15) Element Access (15) Modify methods (16) Insert (16) Swap (18) Unique (18) Sort (18) Merge (19) Reverse (19) Remove (20) 四内存管理 (20) 五使用体会 (21)

一容器实现 由于C++中并未引入接口功能,STL是通过继承的方式实现接口的效用的。 _List_node_base 链结点单元的原型结构体,其功能为,作为双向链表的原型,提供前后指针结构以及对链结点的基本操作。 1. struct _List_node_base 2. { 3. _List_node_base* _M_next; 4. _List_node_base* _M_prev; 5. static void 6. swap(_List_node_base& __x, _List_node_base& __y); 7. void 8. transfer(_List_node_base * const __first, 9. _List_node_base * const __last); 10. void 11. reverse(); 12. void 13. hook(_List_node_base * const __position); 14. void 15. unhook(); 16. }; _List_node 由_List_node_base派生而成的结构,为链结点原型加上数据成员,成为有数据的结点。 1. template 2. struct _List_node : public _List_node_base 3. { 4. ///< User's data. 5. _Tp _M_data; 6.#ifdef __GXX_EXPERIMENTAL_CXX0X__ 7. template 8. _List_node(_Args&&... __args) 9. : _List_node_base(), _M_data(std::forward<_Args>(__args)...) { } 10.#endif 11. };

相关主题
相关文档
最新文档