【C++】 STL相关初步学习

C++中对STL相关内容的初步整理

  • 泛型程序设计和STL概述
  • 容器(vector)
  • 迭代器(iterator)
  • 算法
  • string类型

泛型程序设计和STL概述

C++有两种特性来支持数据独立:模板和运算符重载
泛型程序设计是以类型需求为中心的程序设计方法。其主要推动来自C++的标准模板库,STL是迄今最为成功和卓越的泛型程序设计范例。
STL的一个重要特点是数据结构和算法的分离。STL中包含的是堆栈、队列和其他许多标准数据结构的实现和许多重要泛化算法的实现。
STL的另一个重要特点是它不是面向对象的。STL中的类都是模板类。STL主要依赖于模板而不是封装、继承和多态(3要素)。在STL中找不到任何明显的类继承关系。
由于STL是基于模板、内联函数的使用,追求的是运行的效率,避免了虚函数的开销,这使得生成的代码短小高效。
STL提供了大量的模板类和函数,可以在面向对象程序设计和常规编程中使用。最有用的泛型组件就是迭代器、容器和算法。
为了避免和其他头文件冲突,确保可移植性,在包含头文件时缺省.h后缀。

容器(vector)

STL对最常用的数据结构提供支持,这些模板的参数允许容器中元素的数据类型,可以将许多重复而乏味的工作简化。
容器类库中包括7种基本容器:向量< vector >、列表< list >、双端队列< deque >、集合 < set >、映射< map >、栈< stack >和队列< queue >。
1、顺序容器
顺序容器将同类型的对象集合按严格的线性关系组成起来。STL提供了向量、列表和双端队列3个顺序容器
std::vector向量容器,其数据安排和操作方式和数组非常相似,但向量容器是动态的,会根据需要自动扩充空间以容纳新的元素。
std::list列表容器,提供随机访问的可变长度表。可在任意位置插入删除元素,每次插入和删除元素就分配一个元素的空间。
std::deque双端队列,可在两端分别进行插入和删除元素的操作,长度可变。
2、顺序容器的接口
除了所有容器公用的运算符和方法外,顺序容器还拥有一个插入和删除方法集,插入方法是把元素添加到容器中,可以通过构造函数或使用插入成员函数来完成;删除方法是从容器中移走元素,但是移走元素调用的是容器中的构造函数而不是容器的析构函数,容器出作用域或进行删除操作才调用容器的析构函数。
每个顺序容器至少有3个插入方法和3个删除方法。
插入方法有:
(1)push_front()和push_back(),分别将一个元素加入到容器的首部或尾部;
(2)insert(),这个函数在不同的顺序容器中有不同的版本,不会使列表容器的迭代器失效,但可以让向量和双端队列的迭代器失效,故在插入和删除后面使用迭代器时要谨慎;
(3)通过使用赋值运算符向容器中添加元素,可以把一个容器赋值到另一个容器。
删除方法有:
(1)pop_front()和pop_back(),分别从容器的首部和尾部移走元素;
(2)erase(L),移走由L指定的元素,而erase(L1,L2)移走L1和L2之间的元素;
(3)clear(),移走容器中的所有元素,对每个删除方法在移走元素时都要调用析构函数。

向量容器

可以将向量(vector)认为是能在程序运行时改变长度的数组。向量的用途和数组相同,只是它的长度能在程序运行期间自由改变。
向量元素的索引编号从0开始,这和数组相同。可通过符号“[ ]”来读取和更改这些元素,但是将“[ ]”应用到向量时有一项限制:只能用v[i]更改一个已经赋值的元素,但不能使用v[i]来初始化编号为i的元素。当需要向向量中首次添加一个元素时,弹出要使用成员函数push_back().
向量容器具有自动存储管理功能,可以将程序员从乏味容易出错的分配和释放内存中解放出来。例如,vector可以用从键盘输入的数来作为元素的个数,即定义可变的容量。
例:

//容量可变的向量容器
#include <vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> sample(3,5);             //向量sample的无素初始化为3个5
vector<int> intv;
int temp,i;
cout<<"Enter numbers and press 0 to end"<<endl;
cin>>temp;
while(temp!= 0)
{
intv.push_back(temp);                //向向量intv中添加元素
cin>>temp;
}
cout<<"There are "<<intv.size()<<" elements in intv."<<endl;
for(i=0;i<intv.size();i++)
{
cout<<intv[i]<<"  ";
}
cout<<endl;
cout<<"Sample element is :";
for(i=0;i<3;i++)                    //输出向量sample中的元素
{
cout<<sample[i]<<"  ";
}
cout<<endl;
return 0;
}

运行结果:
Enter numbers and press 0 to end
1 2 3 4 5 6 0
There are 6 elements in intv.
1 2 3 4 5 6
Sample enlement is :5 5 5

分析:此例中向量intv没有预定大小,如果需要更大的容量来储存更多的元素,它的容量就会自动扩充。当向量中的一个元素位置获得了它的第一个元素之后(无论是通过push_back()还是构造函数来初始化),以后可以使用[ ] 来访问这个元素位置,就像普通的数组元素。

容器重分配(reserve)

如果必须考虑效率问题,可考虑由程序员自己来管理容量,而不是接受每次都是容量倍增的默认行为。可使用成员函数reserve()来显式地增加一个向量的容量。
reserve()保证容器预分配足够内存来存储至少n个元素。例如:
v.reserve(32); //将容器设置成至少32个元素;
v.reserve(v.size() + 10); //将容量设置成当前元素数目加上10
注意:虽然可以用v.reserve()来增加一个向量的容量,但如果参数值小于当前容量的前提下,它不一定能减小向量的容量。

capacity()和size()

向量的长度是向量中元素的个数,容量则是当前实际分配的内存的长度和个数。size()返回容器现在存储元素的数量。capacity()返回容器不需重分配就能存储的元素的总数量。
换句话说,capacity()-size()是在不重新分配内存的情况下容器中可用单位的总数。resize()分配n个对象的内存并初始化(可提供不同的初始值作为第二个可选参数)。reserve()分配内存而不初始化。另外,reserve()不改变size()返回的值,它只改变capacity()返回的值。resize()都改变。
例:容器重分配即容量测定举例

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector <int> vs;
vs.reserve(10);                              //得到至少10个整数的空间
	vs.push_back(1);                             //插入元素1  
	vs.push_back(2);
	cout<<"size: "<< vs.size()<<endl;            //输出有效元素个数
	cout<<"capacity: "<<vs.capacity()<<endl;     //输出vs所占空间大小
	cout<<"there's "<<vs.capacity()-vs.size()<<" empty elements before reallocation"<<endl;
	vs.resize(20);                               //resize()后会给予其所有空间初始值,故此后size()和capacity()返回的值相同
	cout<<"size: "<<vs.size()<<endl;
	cout<<"capacity: "<<vs.capacity()<<endl;
	return 0;

}

运行结果:
size:2
capacity:10
there’s 8empty elements before reallocation
size:20
capacity:20

成员函数 at()

at()会检查引用是否是有效的元素。它将执行范围检查,并且它将在试图访问范围以外的成员时抛出类型 std::out_of_range的异常。

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> vi(10,4);         //使用默认构造函数申请10个元素并初始化为4
	vi[0]=100;                    //重载运算符[]
	try
	{
		cout<<vi.at(10)<<endl;    //测试下标10是否越界,并抛出异常
	}
	catch(std::out_of_range)
	{
		cout<<"out of range"<<endl;    //异常处理
	}
	for(int i=0;i<10;i++)
		cout<<vi[i]<<"  ";
	cout<<endl;
	return 0;
}

运行结果:
out of range //测试出10已越界
100 4 4 4 4 4 4 4 4 4

front 和 back 操作

成员函数front()和back()函数分别访问容器开始和结尾的一个元素。成员函数 push_back()在容器的结尾附加一个元素。当容器容量不足时函数重分配内存再附加元素。成员函数 pop_back()将最后一个元素移出容器。

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<char> v_ch;
	v_ch.push_back('A');
	v_ch.push_back('B');
	v_ch.push_back('C');
	cout<<"front: "<<v_ch.front()<<endl;
	cout<<"back: "<<v_ch.back()<<endl;
	v_ch.pop_back();                        //移走最后一个元素
	cout<<"back: "<<v_ch.back() <<endl;
	return 0;
}

运行结果:
front:A
back:C
back:B

另:STL容器重载赋值运算符,因此允许同类型的容器互相赋值。 每一个vector元素都必须是同样的大小。因为派生对象可能有额外的成员,其大小可能比基类要大。

迭代器(iterator)

迭代器相当于容器中的一种抽象的“指针”。迭代器本质上不是指针,但不妨把它当成指针来用。STL中每一个容器类模板都定义了一组对应的迭代器类。使用迭代器,算法函数可以访问容器中指定的元素,而无需关心元素的具体类型
迭代器可以使用以下的重载操作符来操作迭代器,这些操作符将应用于迭代器对象。
(1)前置和后置“++”,用于将迭代器指向下一个数据项。
(2)前置和后置“–”, 用于将迭代器指向上一个数据项。
(3)等于操作符“==”和不等于操作符“!=”,用于测试两个迭代器是否指向同一个数据位置。
(4)一个间接访问运算符“ * ”,如果后面的变量P是一个迭代器,那么使用*P就能访问唯于P处的数据。

  1. using 声明
    STL中的每种容器类型都有它自己的迭代器类型,不过所有的迭代器的基本使用方法都是相同的。讨论迭代器时,通常将::操作符应用于另一个级别。
    using std::vector< int > ::iterator;
    在这个例子中,标识符iterator命名了一种迭代器类型。所以以下语句是允许的:
    iterator p;
    如果习惯事先声明:
    using namespace std;
    则可以直接使用:
    vector< int > ::iterator p;

例:

#include<iostream>                            //iterator 迭代器类型初应用
#include<vector>
using namespace std;
int main()
{
	vector <char> v;
	v.push_back('a');                         //在容器尾部添加字母'a'  
	v.push_back('b');
	v.push_back('c');
	v.push_back('d');
	for(int i=0;i<4;i++)
	{
		cout<<"v["<<i<<"]=="<<v[i]<<endl;     //对容器的访问
	}
	vector <char>::iterator p=v.begin();      //定义迭代器p并初始化为指向容器的起始位置
	cout<<"The third is "<<v[2]<<endl;
	p++;                                      //迭代器移向下一个数据项
	cout<<*p<<endl;                           //输出第二个字母'b'
	p++;
	cout<<*p<<endl;
	p--;
	cout<<*p<<endl;
	cout<<*(p+2)<<endl;
	return 0;
}

运行结果:
v[0]==a
v[1]==b
v[2]==c
v[3]==d
The third is c
b
c
d

  1. 指针用作迭代器
    一个指针也是一个迭代器。
    例:
#include<iostream>               //把指针作为迭代器用于STL的find()算法来搜索普通的数组
#include<algorithm>
using namespace std;
#define SIZE 10
int iarray[SIZE];
int main()
{
	iarray[5]=50;
	int* ip= find(iarray,iarray + SIZE,50);
	if(ip==iarray + SIZE)                 //若不存在需要寻找的元素,则ip指向数组末尾之后的区域,不存在
	{
		cout<<"50 not found in array"<<endl;
	}
	else
	{
		cout<<*ip<<" found in array"<<endl;
	}
	return 0;
}

程序定义了尺寸为SIZE的全局数组。由于是全局变量,所以运行时数组自动初始化为0;
find()函数接受3个参数,头两个定了搜素的范围,第三个参数是待定位的值。find()函数返回和前两个参数相同类型的迭代器,这里是一个指向整数的指针ip。
为了判断find()是否成功,例子中测试ip和past-the-end的值是否相等:if(ip== iarray+ SIZE),如果表达式为真,则表示在搜索的范围内没有指定的值,否则就是指向一个合法对象的指针。

  1. 容器中的迭代器
    尽管C++指针也是迭代器,但用的更多的是容器迭代器。两个典型的容器类方法是begin()和end()。他们在大多数容器中表示整个容器范围。begin()返回指向容器第一个元素的迭代器。end()返回一个指向容器最后一个元素之后(past)的迭代器。其他一些容器还使用rbegin()和rend()的方法提供反向迭代器,以按反方向指定对象范围。

例:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	vector<int> intVector(20,5);
	intVector[5] = 50;
	vector<int>::iterator intIter = find(intVector.begin(),intVector.end(),50);
	if(intIter != intVector.end())
	{
		cout<<*intIter<<" found in vector "<<endl;
	}
	else
	{
		cout<<"50 not found in vector "<<endl;
	}
	return 0;
}

运行结果:
50 found in vector
注意:end()成员函数返回一个指向位于容器最后一个元素之后的迭代器。类似C语言风格字符串的表示方法。

算法

STL中定义了丰富的算法集合。算法是通用的,每个算法都适合于若干种不同的数据结构,而不是仅仅能用于一种数据结构。算法不是直接使用容器作为参数,而是使用迭代器类型。STL几乎所有的算法的头文件都是< algorithm >,根据算法的语义,将算法主要分为3种:不改变序列的算法、改变序列的算法和排序算法。

  1. 不改变序列的算法
    不改变序列的算法是不直接改变序列的操作,包括查找、检查一致性和计数等。
    find()函数返回指向第1个与要查找值相匹配元素的迭代器。如果不能找到要求的值,它返回最后一个元素之后一个位置的迭代器。
    find()算法举例:
#include<iostream>                           //分别输出从键盘输入的字符串第一个'e'字符前后的字符串
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	vector<char> line;
	cout<<"Enter a line of text: "<<endl;
	char next;
	cin.get(next);
	while(next!='\n')
	{
		line.push_back(next);
		cin.get(next);
	}
	vector<char> ::const_iterator where;
	where =find(line.begin(),line.end(),'e');
	vector<char>::const_iterator p;
	cout<<"Before first e :\n";
	for(p=line.begin();p!=where;p++)
		cout<<*p;
	cout<<endl;
	cout<<"After first e:\n";
	for(p=where;p!=line.end();p++)
		cout<<*p;
	cout<<endl;
	cout<<"The end\n";
	return 0;

}

运行结果:
Enter a line of text:
Good morning everyone!
Befor first e :
Good morning
After first e:
everyone!
The end.

不改变序列算法的应用举例:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
int main()
{
	int intarr_1[]={3,4,5,7,7,7,6,8,9,8};
	//以数组intarr_1中的元素初始化向量ivect_1
	vector <int> ivect_1(intarr_1,intarr_1 + sizeof(intarr_1)/sizeof(int));
	int intarr_2[]={3,4,5,7,7,7,9,7};
	vector <int> ivect_2(intarr_2,intarr_2 + sizeof(intarr_2)/sizeof(int));
	//找出相邻两个元素值相等的第一个元素
	vector <int>::iterator ip;
	ip= adjacent_find( ivect_1.begin(),ivect_1.end());
	cout<<*ip<<endl;
	//统计ivect_1中元素值为7的元素个数
	cout<<count(ivect_1.begin(),ivect_1.end(),7)<<endl;
	//找出ivect_1中元素值小于7的元素个数
	cout<<count_if(ivect_1.begin(),ivect_1.end(),bind2nd(less<int>(),7))<<endl;
	//判断两个容器ivect_1和ivect_2是否相等(0为假,1为真)
	cout<<equal(ivect_1.begin(),ivect_1.end(),ivect_2.begin())<<endl;
	return 0;
}

运行结果:
7
3
4
0
注意:此例中使用大量泛型算法

  1. 改变序列的算法
    copy()算法举例:
#include<algorithm>
#include<list>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
	list<char> line;
	vector<char> word(3);
	line.push_back('S');
	line.push_back('T');
	line.push_back('L');
	list<char>::iterator q=line.begin();
	//拷贝List的元素到vector,从vector开始
	copy(line.begin(),line.end(),word.begin());
	vector<char>::iterator p = word.begin();
	while(p!= word.end())
	{
		cout<<*p++;
	}
	cout<<endl;
	return 0;
}

运行结果:
STL

  1. 排序算法
    这种类型包含了用于排序、合并序列的算法和操作已排序序列类集合算法,所有这些算法都是通过对序列元素进行比较操作来完成的。
    4个排序算法:sort()、parital()、parital_copy()、和 stable_sort();
    4个二分查找算法:binary_search()、lower_bound()、uper_bound()和equal_range();

    举例:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	int intarr_1[]={3,14,5,17,27,7,6,18,9,8};
	vector <int> ivect_1(intarr_1 , intarr_1 + sizeof(intarr_1)/sizeof(int));
	sort(ivect_1.begin(),ivect_1.end());
	vector <int>::iterator p = ivect_1.begin();
	while (p!=ivect_1.end())
	{
		cout<<*p++<<"  ";
	}
	cout<<endl;
    return 0;
}

运行结果:
3 5 6 7 8 9 14 17 18 27

string 类型

  1. 标准类string
    string类型在名称同样为< string >,需要预定义;
    使用string类 ,可以使用“+”来连接两个字符串。
    标准类string有一个默认构造函数和多个重载构造函数。默认构造函数将一个string对象初始化成空字符串。
    string初始化应用举例:
#include<string>
#include<iostream>
using namespace std;
int main()
{
	const char text[] = "hello world";
	string s1 = text;
	string s2(s1);
	string s3(&text[0], &text[5]);
	string s4(10, '0');
	string s5(&*s2.begin(), s2.find(' '));
	cout<<s1<<endl;
	cout<<s2<<endl;
	cout<<s3<<endl;
	cout<<s4<<endl;
	cout<<s5<<endl;
	return 0;
}

运行结果:
hello world
hello world
hello
0000000000
hello

  1. string 类的 I/O
    可以用流操作符输出string对象。但输入时使用>>和cin时,会忽略最初的空白符号,并在接受输入字符后遇到第一个空白字符就停止输入。
    如果希望输入整行读入一个变量,可以使用getline函数。
#include<string>
#include<iostream>
using namespace std;
int main()
{
	
	string subject("We love ");
	string object;
	getline( cin, object, '.');         //第3个参数为输入停止信号,读入该字符时停止读入;
	cout<< subject << object <<endl;    //如果想一次读入一个字符,可使用 cin.get 函数。用法:char x = cin.get();或cin.get(x);
	return 0;
}

运行结果:
our homeland. //此行由键盘输入
We love our homeland.

如果想一次读入一个字符,可使用cin.get.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值