学习第二阶段小总结之STL

本文深入解析C++标准模板库(STL),涵盖容器、算法、迭代器等核心组件,讲解其工作原理与应用,同时探讨了文件操作、日期类运算符重载及map应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识串联及知识点细节

C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
STL内的所有组件都由模板(template)构成,其元素可以是任意类型。
不要忘记<>!!!!
C++ 标准模板库的核心包括以下三个组件:
容器:容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。
注意: 容器类自动申请和释放内存,因此无需new和delete操作。
算法:算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。
迭代器:迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。

<algorithm>:【算法】所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等
<deque>:【容器】【双队列】连续存储的指向不同元素的指针所组成的数组
<functional>:【算法】定义了一些模板类,用以声明函数对象
<iterator>:【迭代器】提供了迭代器使用的许多方法
<vector>:【容器】【向量】连续存储的元素
<list>:【容器】【列表】由节点组成的双向链表,每个结点包含着一个元素
<map>:【容器】【映射】由{键,值}对组成的集合,以某种作用于键对上的谓词排列【多重映射】允许键对有相等的次序的映射(multimap)
<memory>:【迭代器】以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生的临时对象提供机制,主要部分是模板类allocator,它负责产生所有容器中的默认分配器
<numeric>:【算法】体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作
<queue>:【容器】【队列】先进先出的执的排列 【优先队列】元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列(priority_queue)
<set>:【容器】【集合】由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序【多重集合】允许存在两个次序相等的元素的集合(multiset)
<stack>:【容器】【栈】后进先出的值的排列
<utility>:【迭代器】一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明

常用操作

大小

size()-返回当前容器的元素数量
empty()-判断容器是否为空
max_size()-返回容器能容纳的最大元素数量
如果两个容器内的所有元素按序相等,那么这两个容器相等(==),一般不作比较。

赋值交换

swap()

迭代器

begin()-返回一个迭代器,指向第一个元素
end()-返回一个迭代器,指向最后一个元素之后
rbegin()-返回一个逆向迭代器,指向逆向遍历的第一个元素
rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后

元素

insert(pos,e)-将元素e的拷贝安插于迭代器pos所指的位置
erase(beg,end)-移除[beg,end]区间内的所有元素
clear()-移除所有元素

文件操作

C++将文件看成无结构字节流。
文件可以分为两种类型:
1.文本文件:文件以文本的ASCII码形式存储在计算机中。
2.二进制文件:文件以文本的二进制形式存储在计算机中,用户一般情况下不能读懂。

C++输入/输出标准库 iostream:

istream 输入流
ostream 输出流

iostream 输入/输出流,由上述两个类派生而得;
而iostream库中包含的主要头文件就包含fstream;
对文件操作主要设计以下3类
1.ifstream 文件读(输入)操作类.
2.ofstream 文件写(输出)操作类.
3.fstream 文件读(输入)/写(输出)操作类.

ifstream

//对文件进行读(输入)操作
int main()
{
	ifstream ifd(“text1.txt”, ios::in);
	if(!ifd.is_open())
	{
		cout << "Error: open() " << endl;
		exit(1);
	}
	string str = “”;
	while(getline(ifd, str))//以行为单位从文件流中读取文件,放入str中
	{
		cout << str << endl;
	}
	cout << endl;
	ifd.close(); //打开文件后,一定要记得关闭
	exit(0);
}

ofstream

//对文件进行写(输出)操作
int main()
{
	ofstream ofd(“text2.txt”, ios::out | ios::trunc);
	if(!ofd.is_open())
	{
		cout << "Error: open() " << endl;
		exit(1);
	}
	string str1 = “hello, world”;
	for(auto x : str1)
	{
		ofd << x;
	}
	ofd.close(); //打开文件后一定要记得关闭
	cout << endl;
	exit(0);
}

fstream

先写后读

int main()
{
	//read file
	fstream fd(“text1.txt”, ios::in | ios::out | ios::app);
	if(!fd.is_open())
	{
		cout << “error: open()<< endl;
		exit(1);
	}
	//先写
	string str1 = “\nline4:lalala”;
	fd << str1;
	//重置文件流指针到文件首
	fd.seekg(fd.beg);
	//读文件内容
	string str = “”;
	while(getline(fd, str))
	{
		//从文件流中读取数据放入str中,每次循环会刷新str
		if(str.size() == 0) cout << ‘\n’; //读到空格自动换行
			cout << str;
	}
	fd.close();
	exit(0);
}

先读后写

在实际中,需要对一个txt文件先读后写,这时可以使用fstream对象、 然后使用流对象的定位函数。seekg,seekp,tellg,tellp来进行相应位置的写。

注意:

在先读后写时,读完这个文件需要对流进行clear操作,否则无法写入。

fstream fd(“text1.txt”, ios::in | ios::out | ios::app);
相当于
fstream fd;
fd.open(“text1.txt”, ios::in | ios::out | ios::app);

int main()
{
	//read file
	fstream fd(“text1.txt”, ios::in | ios::out | ios::app);
	if(!fd.is_open())
	{
		cout << “error: open()<< endl;
		exit(1);
	}
	string str = “”;
	while(getline(fd, str))
	{
		//从文件流中读取数据放入str中,每次循环会刷新str
		if(str.size() == 0) cout << ‘\n’; //读到空格自动换行
			cout << str;
	}
	//由于读操作已经读到了文件的末尾,这时输入流标记为文件结束
	//若不清空流标记clear()的话,写操作是写不进去的
	//streampos pos = fd.tellg();
	//fd.seekg(fd.end);
	fd.clear();
	string str1 = “\nline7:lalala”;
	fd << str1;
	fd.close();
	exit(0);
}

关于查找(map)

1.count()函数的使用
count函数只能返回0和1,无法定为数据出现的位置。如果待查元素在map容器中有,则返回1,没有则返回0
2.find()函数的使用
用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器
不能使用数组下标的方法来查找,因为若使用数组下标查找,则对于map中没有的元素,会直接将这个元素插入
例:find

int val2 = 100;
// 没有找到这个值,返回vec.cend()
auto res = find(vec.begin(), vec.end(), val2);
if (res == vec.cend())
{
    cout << "没找到元素!" << endl;
}
else
{
    cout << *res << endl;
}
  1. 访问序列中的元素
  2. 比较此元素与我们要查找的值
  3. 如果此元素与我们要查找的值匹配,find返回标示此元素的值
  4. 否则,find前进到下一个元素,重复执行步骤2和3
  5. 如果到达序列尾,find停止
  6. 如果find到达序列末尾,它应该返回一个指出元素未找到的值。此值和步骤3中返回的值必须具有相同的类型

<1> adjacent_find: 在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的ForwardIterator。否则返回last。重载版本使用输入的二元操作符代替相等的判断。
<2> binary_search: 在有序序列中查找value,找到返回true。重载的版本实用指定的比较函数对象或函数指针来判断相等。
<3> count: 利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。
<4> count_if: 利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。
<5> equal_range: 功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。
<6> find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的一个InputIterator。
<7> find_end: 在指定范围内查找"由输入的另外一对iterator标志的第二个序列"的最后一次出现。找到则返回最后一对的第一个ForwardIterator,否则返回输入的"另外一对"的第一个ForwardIterator。重载版本使用用户输入的操作符代替等于操作。
<8> find_first_of: 在指定范围内查找"由输入的另外一对iterator标志的第二个序列"中任意一个元素的第一次出现。重载版本中使用了用户自定义操作符。
<9> find_if: 使用输入的函数代替等于操作符执行find。
<10> lower_bound: 返回一个ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器顺序的第一个位置。重载函数使用自定义比较操作。
<11> upper_bound: 返回一个ForwardIterator,指向在有序序列范围内插入value而不破坏容器顺序的最后一个位置,该位置标志一个大于value的值。重载函数使用自定义比较操作。
<12> search: 给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作。
<13> search_n: 在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作。

例题与技巧分析、细节处理

基础类:读者最大借书量、最长借书时间(按天计算)。这两个都是类成员

看到类成员的时候很茫然,老师上课解释了一番才晓得指的static,就讲到的运用。

class JC
{
	private:
		static int MaxMany;
		static int MaxDay;
	public:
		JC(){}
		~JC(){}
		static int getMM(){return MaxMany;}
		static int getMD(){return MaxDay;}
};
int JC::MaxMany=5;
int JC::MaxDay=30;

static的赋值形式为

int JC::MaxMany=5;
int JC::MaxDay=30;

这两句,同时注意get函数也需要static

日期类的重载+运算符

Date Date::operator + (int x)
{
	int mo=0,da,i;
	da=Day+x;
	if(da>getTS(Year,Month))
	{
		i=Month;
		while(da>getTS(Year,i))
		{
			da-=getTS(Year,i);
			mo++;
			i++;
			i%=12;
		}
		Month+=mo;
		if(Month>12)
		{
			Year+=(Month/12);
			Month%=12;
		}
	}
	Day=da;
	return *this;
}

初期用的day自减来操作,但是时间会很长,每一天都需要计算,后来在老师的提醒下进行了改进,直接+天数然后判断进位;

天数计算

		bool getRYear(int year)
    	{
    		if(((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
			{
    			return true;
    		}
    		return false;
		}
		int getTS(int year,int month)
		{
			int ts[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
			int day=ts[month];
			if(month==2&&getRYear(year))
			{
				day++;
			}
			return day;
		}

例题里对于map的应用主要在操作类里

操作类:
数据成员:图书/学生/借阅记录向量
成员函数:对图书进行文件读写、在图书向量内完成对图书基本信息的增删查改;
对学生进行文件读写、在学生向量内完成对学生基本信息的增删查改;
借阅记录的管理和统计功能后续添加;

class operate
{
    private:
       vector<Book>b1;
       vector<Reader>r1;
       vector<Date>d1;
       multimap<string,int>bookname;
       multimap<string,int>booknumber;
       multimap<string,int>readername;
       multimap<string,int>readernumber;
    public:
       operate()
       {
       		BookRead();
		    ReaderRead();
       }
       ~operate()
       {
       		BookWrite();
    		ReaderWrite();
       }
       operate(vector<Book>b1,vector<Reader>r1,vector<Date>d1)
       {
       		this->b1=b1;
    		this->r1=r1;
    		this->d1=d1;
       }
       vector<Book>getB(){return b1;}
       vector<Reader>getR(){return r1;}
       vector<Date>getD(){return d1;}
       void BookRead();
       void BookWrite();
       void BookAdd(Book &b);
       void BookSelect1(string bn);
       void BookSelect2(string t);
       void BookModify(string bn);
       void BookDelete(string booknumber);
       void ReaderRead();
       void ReaderWrite();
       void ReaderAdd(Reader &r);
       void ReaderDelete(string Sno);
       void ReaderModify(string Sno);
       void ReaderSelect1(string Sno);
       void ReaderSelect2(string name);
};

定义里一定要注意<>中要声明类型,类型可以是类的形式。
map属于关联容器,提供一对一的数据处理能力。内部是由红黑树实现的,具有自动排序能力。因此map内部的所有数据是有序的。

文件的输入输出:(以book为例)

void operate::BookRead()
{   Book t;
    int a,b;
    ifstream ins("d:\\book.text");
    if(!ins)
    {
        return ;
    }
   while(ins.eof())
   {
       ins>>t;
       ins>>a>>b;
       t.setBookSum(a);
       t.setBookIn(b);
       b1.push_back(t);
       bookname.insert(make_pair(t.getTitle(),b1.size()-1));
       booknumber.insert(make_pair(t.getBookNumber(),b1.size()-1));
   }
   ins.close();
}
void operate::BookWrite()
{
    fstream outs("d:\\book.txt");
    vector<Book>::iterator i;
    for(i=b1.begin();i!=b1.end();i++)
    {
        outs<<*i;
    }
    outs.close();
}

book的添加:
插入操作
在map中插入数据有三种方法:

1.使用数组 :用数组方式插入数据
2.insert函数插入pair数据
3.insert插入value_type数据

注意: 使用后两者插入数据,利用的是集合的唯一性,也就是说当map容器中已经有了所要插入的数据,则insert操作是插入不了数据的。而使用数组操作时,会将已有的数据覆盖

void operate::BookAdd(Book &b)
{
    multimap<string,int>::iterator i;
    i=booknumber.find(b.getTitle());
    if(i==booknumber.end())
    {
        b.setBookSum(b.getBookSum()+1);
        b.setBookIn(b.getBookIn()+1);
        b1.push_back(b);
        bookname.insert(make_pair(b.getTitle(),b1.size()-1));
        booknumber.insert(make_pair(b.getBookNumber(),b1.size()-1));
    }
    else{
        b.setBookSum(b1[i->second].getBookSum()+1);
        b.setBookIn(b1[i->second].getBookIn()+1);
        b1[i->second]=b;
    }
}

book的删除:

移除某个map中某个条目用erase()

该成员方法的定义如下:
iterator erase(iterator it);//通过一个条目对象删除
iterator erase(iterator first,iterator last)//删除一个范围
size_type erase(const Key&key);//通过关键字删除
clear()就相当于enumMap.erase(enumMap.begin(),enumMap.end());

void operate::BookDelete(string booknumber)
{
    vector<Book>::iterator pos;
    for(pos=b1.begin();pos<b1.end();pos++)
    {
        if(pos->getBookNumber()==booknumber)
        {
            b1.erase(pos);
        }
    }
}

学习感悟

这段时间的学习感觉到了一段倦怠期,能明显感觉到自制力的下降,每天都有各科作业在压榨精神力,拿到例题的时候也是力不从心,感觉灵感泉近乎干涸,很多次都是在同学或者老师的提醒下才开始有一点思路,在此之前甚至不知如何下手;整个学期都在一种特别快的节奏里摄取知识,这个学期可以说是目前所有的学期里压力最大的一个学期,不管是难度还是任务量,很多大课集中在了一起,越来越感觉力不从心,这几天在努力调整状态,学无止境,庞大的知识结构慢慢的建立过程,充实其中,立一个小flag吧:在接下来一段时间里把结课了的好好复习整理,还在进行的每节课努力跟上进度,课后尽力挤时间巩固复习一下,要让节奏紧凑起来,回到最初的状态,加油加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值