一、模板
1、什么是模板
C++语言中提供一种自动生成代码的技术,这种技术可以让程序员在编程时不需要考虑数据类型,而专注思考业务逻辑和算法,程序员只需要编写好代码的整体框架,具体的数据类型由使用者提供,这就叫模板技术,也被称为泛型编程技术。
2、为什么使用模板
1、C、C++是一种静态编程语言,这种语言的特点是,需要把代码经历 预处理-> 编译-> 汇编-> 连接 -> 生成可执行文件 过程最后才能执行所编写好的代码,这种编程语言最大的优点就是执行速度快,缺点是在程序执行过程中只能按照预先设计好的路线执行,并且在程序执行过程中,数据的类型不能发生变化,这种特点就需要程序员预先把所有可能性都考虑清楚,所以C、C++的程序员一直为实现通用型的代码而努力(通用指针、回调函数、宏函数、函数重载、运算符重载)。
2、使用void类型的指针配合回调函数实现通用型代码(qsort排序函数为代表),该方法的缺点是既危险,实现难度大,使用麻烦。
3、使用宏函数实现通用型的代码,不会对数据进行检查,最重要的是没有返回值,还会造成冗余以及代码段增大。
4、借助函数重载,为各种数据类型的参数都实现一遍功能相同的代码,需要耗费大量的人力,并且会使代码段增大,但无法预料所有的数据类型,无法解决未知的数据类型。
5、基于以上原因,C++之父设计了模板技术,程序员只需要把模具制作好,然后调用者给模具提供原料(数据类型),然后生产出"产品",也就是符合我们预期的代码,最终再调用该代码完成任务。
3、函数模板
定义函数模板:
template <typename T1,typename T2,...>
T3 函数名(T1 arg1,T2,arg2)
{
T3 ret = arg1+arg2;
return ret;
}
可以给未知的类型取任何名字,但约定俗成使用T。
函数模板的原理:
函数模板会经历两次编译:
1、编译器会检查模板的代码是否有语法错误,此时函数模板仅仅是生成代码的工具,并不会产生二进制指令。
2、使用模板,编译器会根据使用者提供的类型参数生成相关代码,并且会再次编译生成的代码,如果没有语法错误,就会生成二进制的指令,然后把二进制的指令存储在代码段。
函数模板的调用:
自动提供类型参数:
模板需要的类型参数,都可以根据函数的参数列表自动提取。
#include <iostream>
using namespace std;
template <typename T>
void show_arr(T arr[],int len)
{
for(int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main(int argc,const char* argv[])
{
double arr[9] = {0,1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
show_arr(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
练习1:使用模板技术,实现通用的排序函数,可使用任何排序算法。
template <typename T>
void mySort(T arr[],size_t len)
{
for(size_t i=0; i<len-1; i++)
{
size_t m = i;
for(int j=i+1; j<len; j++)
{
if(arr[j] < arr[m])
m = j;
}
if(m != i)
swap(arr[m],arr[i]);
}
}
练习2:设计一个学生类,定义学生数组,使用模板排序算法对学生数组进行排序(以成绩为排序标准)。
class Student
{
string name;
char sex;
short age;
float score;
public:
Student(const string& name="",char sex=0,short age=0,float score=0):
name(name),sex(sex),age(age),score(score){}
bool operator<(const Student& that)
{
return score < that.score;
}
friend ostream& operator<<(ostream& os,const Student& s)
{
return os << s.name << " "
<< s.sex << " "
<< s.age << " "
<< s.score;
}
};
手动提供类型参数:
未知的类型没有出现参数列表,就需要调用者手动提供类型参数。
函数名<类型>(实参);
template <typename T>
T strToNumber(const string& str)
{
T num;
if(typeid(T) == typeid(int))
sscanf(str.c_str(),"%d",&num);
else if(typeid(T) == typeid(short))
sscanf(str.c_str(),"%hd",&num);
else if(typeid(T) == typeid(float))
sscanf(str.c_str(),"%f",&num);
else
sscanf(str.c_str(),"%lf",&num);
return num;
}
int main(void)
{
string str = "12345";
cout << strToNumber<int>(str) << endl;
str = "12";
cout << strToNumber<short>(str) << endl;
string str = "3.14";
cout << strToNumber<float>(str) << endl;
}
注意:需要手动提供的类型参数尽量放在最左边,这样使用者可以只提供部分类型即可。
默认形参:
template <typename T3=float,typename T2,typename T1>
T3 avg(T1 n1,T2 n2)
{
if(typeid(T3) == typeid(float) || typeid(T3)== typeid(double))
return n1/2.0+n2/2.0;
else
return (n1+n2)/2;
}
给模板的类型参数,设置默认值,当使用者不提供类型参数时,使用默认类型,但该语法只有C++11标准才支持。
思考:字符串数组能不使用模板排序算法,如果不能该怎么办?
const char* arr[] = {"hehe1","hehe2","hehe3"};
mySort(arr,5);
模板的特化:
当有一些特殊类型,不能使用通用操作时,就需要为它提供一个特殊版本的函数(真正的函数),编译器会优先调用真正的函数,而不会再调用模板生成的函数,这就叫模板的特化。
注意:一般char* 类型都需要进行特化。
#include <iostream>
#include <string.h>
using namespace std;
template <typename T>
void bullue_sort(T arr[],int len)
{
bool flag = true;
for(int i=len-1; flag && i>0; i--)
{
flag = false;
for(int j=0; j<i; j++)
{
if(arr[j] > arr[j+1])
{
swap(arr[j],arr[j+1]);
flag = true;
}
}
}
cout << "sort:";
for(int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void bullue_sort(const char* arr[],int len)
{
bool flag = true;
for(int i=len-1; flag && i>0; i--)
{
flag = false;
for(int j=0; j<i; j++)
{
if(0 < strcmp(arr[j],arr[j+1]))
{
swap(arr[j],arr[j+1]);
flag = true;
}
}
}
cout << "sort:";
for(int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main(int argc,const char* argv[])
{
int arr1[] = {1,3,5,7,9,2,4,6,8,10};
bullue_sort(arr1,sizeof(arr1)/sizeof(arr1[0]));
const char* arr2[] {
"abc",
"cba",
"bba",
"aab",
"bab"
};
bullue_sort(arr2,sizeof(arr2)/sizeof(arr2[0]));
return 0;
}
练习1:通用的变量交换函数。
练习2:通用的求数组最大值、最小值函数。
4、类模板
什么是类模板:
使用到了未知的类型来设计一种类(模板类在使用时,必须需要提供类型参数)。
template <typedef M,typedef T,typedef R>
class Test
{
M num;
public:
Test(T t) {}
R func(void)
{
R ret;
return ret;
}
};
类模板的使用:
由于创建类对象时可以不提供参数(无参构造),所以必须手动提供类模板的类型参数。
类名<类型参数> 对象;
注意:在构造函数执行前,对象必须先创建出来(分配内存),所以必须先提供模板的类型参数,让模板类先完成实例化,然后再创建对象、调用构造,具体过程如下:
1、类名+类型参数 -> 模板实例化->生成类
2、类再实例化对象 -> 分配内存
3、父类构造,成员构造,自己的构造
注意:模板类的声明与定义不能分开实现,必须在头文件中完成,模板类和模板函数预先编译成二进制没有意义,它们只是生成代码的工具。
练习、实现一个 链式队列 类模板。
类模板的静态成员变量:
类模板的静态成员变量与普通成员变量一样都需要在类内存声明,类外定义,但静态成员定义时就需要提供类型参数了。
一般类模板的设计者只负责声明静态成员变量,而由类模板的使用定义静态成员。
template<> 类型 类名<类型参数>::静态成员 = 初始化数据;
#include <iostream>
using namespace std;
template <typename T>
class Test
{
T num;
public:
static T static_num;
Test(T t):num(t)
{
cout << num << endl;
}
};
template<> int Test<int>::static_num = 34;
int main(int argc,const char* argv[])
{
Test<int> t(1234);
cout << t.static_num << endl;
return 0;
}
类模板可以递归创建:
什么类型都以是类模板的类型参数,包含类模板。
类< 类<类型> > 对象;
Array<Array<int> > arr; // 二维数组
类模板的默认形参:
类模板也可以像函数模板一样设置默认形参,规则与函数模板一样。
template <typename T1,typename T2=int>
class Test
{
T1 val;
public:
Test(T2 val)
{
}
};
类的局部特化:
当类模板的成员函数不能支持所有的类型时,可以为这个成员函数实现一个特殊版本。
// 需要在类外定义:
template<> 返回值类型 类名<特殊类型>::函数名(参数列表)
{
}
#include <iostream>
#include <string.h>
using namespace std;
template <typename T>
class Array
{
T* ptr;
size_t cnt;
const size_t cap;
public:
Array(size_t cap):cap(cap)
{
ptr = new T[cap];
cnt = 0;
}
~Array(void)
{
delete[] ptr;
}
// 满
bool full(void)
{
return cnt >= cap;
}
// 空
bool empty(void)
{
return 0 == cnt;
}
// 添加
void back(const T& t)
{
if(full())
throw invalid_argument("满了");
ptr[cnt++] = t;
}
// 插入
void insert(size_t index,const T& t)
{
if(full() || index >= cnt)
throw invalid_argument("满了");
for(int i=cnt; i>index; i--)
{
ptr[i] = ptr[i-1];
}
ptr[index] = t;
cnt++;
}
// 删除
void remove(size_t index)
{
if(index >= cnt)
throw invalid_argument("空了");
for(int i=index; i<cnt-1; i++)
{
ptr[i] = ptr[i+1];
}
cnt--;
}
// 查找
int query(const T& key)
{
for(int i=0; i<cnt; i++)
{
if(key == ptr[i])
return i;
}
return -1;
}
// 排序
void sort(void)
{
for(int i=0; i<cnt-1; i++)
{
int min = i;
for(int j=i+1; j<cnt; j++)
{
if(ptr[j] < ptr[min])
min = j;
}
if(min != i)
swap(ptr[min],ptr[i]);
}
}
// 遍历
void show(void)
{
cout << "array:";
for(int i=0; i<cnt; i++)
{
cout << ptr[i] << " ";
}
cout << endl;
}
};
// 类的局部特化
template<> void Array<const char*>::sort(void)
{
for(int i=0; i<cnt-1; i++)
{
int min = i;
for(int j=i+1; j<cnt; j++)
{
if(-1 == strcmp(ptr[j],ptr[min]))
min = j;
}
if(min != i)
swap(ptr[min],ptr[i]);
}
}
template<> int Array<const char*>::query(const char* const & key)
{
for(int i=0; i<cnt; i++)
{
if(0 == strcmp(ptr[i],key))
return i;
}
return -1;
}
int main(int argc,const char* argv[])
{
const char* str[] = {"B","C","A","Z","F","E"};
Array<const char*> arr(10);
for(int i=0; i<6; i++)
{
arr.back(str[i]);
}
arr.show();
arr.sort();
arr.show();
string s = "Z";
cout << arr.query(s.c_str()) << endl;
return 0;
}
类的全局特化:
如果类的大部分操作都不支持特殊类型,就需要为特殊类型实一个特殊的类模板。
template <>
class Test<特殊类型>
{
// 实现类的所有功能
};
二、STL模板库
1、什么是STL模板库
STL是 Standard Template Library 缩写,也就是C++标准模板库。
C++标准库是C++标准委员会为C++语言提供的一些基础类库,相当于C语言标准库。
STL库是惠普公司以模板技术为基础,而实现的一些常用的数据结构和算法的函数模板和类模板,由于该库的质量比较高,被C++标准委员会认可,所以被包含中C++标准库中。
该模板库主要有三部分内容:
函数模板:常用的算法有,sort、find、 search、max、min、copy、remove。
类模板:常用的数据结构有,也叫容器,vector,deque,list,priority_queue,queue、stack、set、multiset、map,multimap,bitset。
迭代器iterator:是一种辅助使用容器的工具,把它当作指针使用即可(指向着容器中的数据元素),它支持*、->,*.,++、--,it+n,it-n运算。
iterator begin(); const_iterator begin() const; 功能:返回一个正向迭代器,指向数据结构中的第一个元素 iterator end(); const_iterator end() const; 功能:返回一个正向迭代器,指向数据结构中的最后一个元素的下一位置 reverse_iterator rbegin(); const_reverse_iterator rbegin() const; 功能:返回一个逆向迭代器,指向数据结构中的最后一个元素 reverse_iterator rend(); const_reverse_iterator rend() const; 功能:返回一个逆向迭代器,指向数据结构中的第一个元素的上一个位置
2、STL中为什么要使用迭代器?
1、迭代器能加快、方便链式容器的访问(相当于建立了索引)。
2、能适用于大部分的容器(除了stack、queue),给所有的容器提供一种统一的遍历接口。
3、保护容器类的封装性。
for(auto it=容器.begin(); it!=容器.end(); it++)
{
it->成员函数|成员变量;
*it.成员函数|成员变量;
}
3、vector容器
构造函数:
// 无参构造
vector();
// 拷贝构造
vector(const vector &from);
// 有参构造
vector(size_type num, const TYPE &val);
num:数组的长度
val:各元素的初始化
// 使用迭代器来构造
vector(input_iterator start, input_iterator end);
功能:使用一个数据结构的迭代器来构造vector
start:迭代器的开头
end:迭代器的末尾,end只是边界,它指向的元素不会插入进当前容器
支持的运算符:
TYPE& operator[]( size_type index ); const TYPE& operator[]( size_type index ) const; 功能:访问元素,不检查下标,速度快但有安全隐患 vector operator=(const vector& c2); 功能:赋值函数 bool operator==(const vector& c1, const vector& c2); bool operator!=(const vector& c1, const vector& c2); bool operator<(const vector& c1, const vector& c2); bool operator>(const vector& c1, const vector& c2); bool operator<=(const vector& c1, const vector& c2); 功能:比较两个vector的内容,以全局运算符函数实现
成员函数:
void assign(input_iterator start, input_iterator end); 功能:使用一个迭代器区间给当前容器中元素赋值 void assign(size_type num, const TYPE &val); 功能:给当前对容器中的前num个元素,赋值为val,如果容器中元素不足num个,会自动往里添加 TYPE& at(size_type loc); const TYPE& at(size_type loc) const; 功能:使用loc作为下标访问成员,它比[]访问更安全,会检查loc是否会法,如果超出范围会抛出std::out_of_range异常。 TYPE& back(); const TYPE& back() const; 功能:获取容器对象的最后一个元素,如果容器为空则会产生段错误,应该通过size()确保容器中有元素。 size_type capacity() const; 功能:获取当前容器的容量,当vector容器的容量为空时,它会自动把存储容量扩展一倍 void clear(); 功能:清空容器中的所有元素 bool empty() const; 功能:判断当前容器是否为空 iterator erase(iterator loc); iterator erase(iterator start, iterator end); 功能:使用迭代器,删除容器中的元素 返回值:所删除元素下个位置的迭代器 TYPE& front(); const TYPE& front() const; 功能:获取容器的第一个元素,如果容器为空则会产生段错误 iterator insert( iterator loc, const TYPE& val ); 功能:在迭代器loc指向的元素前面插入一个值为val的元素 void insert( iterator loc, size_type num, const TYPE& val ); 功能:在迭代器loc指向的元素前面插入num个值为val的元素 void insert( iterator loc, input_iterator start, input_iterator end ); 功能:在迭代器loc指向的元素前面插入一个区别的元素 size_type max_size() const; 功能:计算出当前容器能存储多个元素,它与所存储元素的类型有关。 void pop_back(); 功能:删除容器的最后一个元素,如果容器为空则段错误。 void push_back(const TYPE& val); 功能:在容器的末尾添加一个元素 void reserve(size_type size); 功能:调整当前容器空间,让它能容纳size个元素的空间,如果capacity() > reserve()则不执行任何操作。 void resize(size_type num, const TYPE& val = TYPE()); 功能:设置容器的元素数量为num num > size() 扩充元素,并对扩充的元素初始化 num < size() 删除尾部的size-num个元素 void swap(container& from); 功能:交换两个容器的元素
4、list容器
list容器与vector最大的区别是底层的存储结构不同,vector采用的是顺序存储相当于数组,而list容器采用链式存储也就是数据结构讲过的双向链表,所以list插入、删除速度较快,但随机访问速度较慢,所以不支持[]、at函数,只能使用迭代器遍历。
与vector功能参数相同的成员函数:
// 无参、拷贝、有参、迭代器构造 list(); list( const list& c ); list( size_type num, const TYPE& val = TYPE() ); list( input_iterator start, input_iterator end ); // 赋值与关系运算符 list operator=(const list& c2); bool operator==(const list& c1, const list& c2); bool operator!=(const list& c1, const list& c2); bool operator<(const list& c1, const list& c2); bool operator>(const list& c1, const list& c2); bool operator<=(const list& c1, const list& c2); bool operator>=(const list& c1, const list& c2); // 批量赋值 void assign( size_type num, const TYPE& val ); void assign( input_iterator start, input_iterator end ); // 获取尾部元素 TYPE& back(); const TYPE& back() const; // 清空与判断是为空 比vector慢 void clear(); bool empty() const; // 基于迭代器的删除,按位置、区间 比vector快 iterator erase( iterator loc ); iterator erase( iterator start, iterator end ); // 头部元素 TYPE& front(); const TYPE& front() const; // 最大元素数量 size_type max_size() const; // 调整链表长度 比vector慢 void resize( size_type num, const TYPE& val = TYPE()); // 获取链表长度 可能比vector慢 size_type size() const; //头删除与头添加 比vector快 void pop_front(); void push_front( const TYPE& val ); // 尾删除与尾添加 相同 void pop_back() void push_back( const TYPE &val ); // 插入元素 比vector快 iterator insert( iterator loc, const TYPE& val ); 功能:在指定的位置前插入一个元素 void insert( iterator loc, size_type num, const TYPE& val ); 功能:在指定的位置前插入num个元素 void insert( iterator loc, input_iterator start,input_iterator end ); 功能:在指定的位置前插入一个区间的元素 void swap( container& from )
list新增的成员函数:
void merge( list &lst );
功能:合并两个链表,会按照从小到大的顺序进行合并,如果之前链表是无序则合并的结果是不确定的,所以在调用前最好对链表进行排序,所有的容器只要有排序的功能则成员必须支持 < 运算符函数。
注意:作为参数的lst链表,合并完成后会被清空。
void merge( list &lst, BinPred compfunction );
功能:合并两个链表
compfunction:比元素回调用函数
// 成员是int的可以用比较函数
bool comper(int t1,int t2)
{
return t1 > t2;
}
void remove( const TYPE &val );
功能:删除所有与val相等的元素
void remove_if( UnPred pr );
功能:按条件删除
pr:判断元素是符合删除的条件
// 成员是int的判断函数
bool is_check(int ele)
{
return ele > 2 && ele < 8;
}
void reverse();
功能:链表逆序
void sort();
功能:对链表进行排序,此时会使用元素的 < 运算符,自定义的类型需要重载<运算函数。
void sort( BinPred p );
功能:对链表进行排序,此时使用回调用函数对元素进行比较并排序
BinPred函数原型:
bool xxxcmp(const T&,const T&);
void splice( iterator pos, list& lst );
功能:把lst链表的所有元素插入到当前链表的pos位置
void splice( iterator pos, list& lst, iterator del );
功能:把lst链表的del指向的元素,插入到当前链表的pos位置
void splice( iterator pos, list& lst, iterator start, iterator end );
功能:把lst链表的[start,end)范围的元素,插入到当前链表的pos位置
void unique();
功能:删除链表中值相同的元素,默认使用 < 判断元素是否相等,只保留一个。
void unique( BinPred pr );
功能:删除链表中值相同的元素,使用回调用函数pr判断元素是否相等,只保留一个。
常见的面试问题:顺序存储结构与链式存储结构的优缺点,vector和list优缺点?
5、stack容器
没有迭代器,访问受限的线性表(只有一个端口进出数据元素),底层采用链式结构存储,所以没有满的状态,但支持条件运算符(比较两栈的元素大小和顺序),所以进而存储的元素需要支持 <,== 运算符。
支持的运算符:
== <= >= < > != 注意:相等指栈内有相同的元素并有着相同的顺序
bool empty() const; 功能:判断是否是空栈 void pop(); 功能:出栈 void push( const TYPE& val ); 功能:入栈 size_type size() const; 功能:获取栈的元素个数 TYPE& top(); 功能:返回栈顶元素
练习:使用模板技术,实现一个stack容器。
6、queue
不支持运算符,也没有迭代器,访问受限的线性表,底层采用链式结构存储,所以没有满的状态。
TYPE& back(); const TYPE& back() const; 功能:返回队尾元素 bool empty() const; 功能:判断是否是空队 TYPE& front(); const TYPE& front() const; 功能:返回队头元素 void pop(); 功能:出队 void push( const TYPE& val ); 功能:入队 size_type size() const; 功能:获取队列的元素个数
7、priority_queue容器
priority_queue 优先队列,使用时包含#include <queue>,添加元素入队后,会按照元素值进行自动排序(从大到ih的顺序排序),出队时会按照排序后的结果进行出队,里面存储的元素必须支持 < 运算符函数,或者在创建时提供比较元素的回调函数。快速造词
priority_queue( const Compare& cmp = Compare(), const Container& c = Container() ); priority_queue( input_iterator start, input_iterator end, const Compare& comp = Compare(), const Container& c = Container()); 功能:构造优先队列时,提供比较元素的回调函数。 bool empty() const; 功能:判断是否是空队 void pop(); 功能:出队一个优先级最高的元素 void push( const TYPE& val ); 功能:入队,不会直接加入队尾,而是根据优先级插入到合适的位置 size_type size() const; 功能:获取队列的元素个数 TYPE& top(); 功能:获取队列中优先级最高的元素
练习:基于queue数据结构,实现一个优先队列模板。
8、set容器
set集合容器,里面存储元素除了同属一个容器外,没有任何关系,最大的特点就是集合中的元素不能重复,所以它的元素也需要支持 < 运算符(set容器会调用它判断元素是否相等,也就是是否重复),并且会对集合中的元素自动排序,适用于对数据进行排名的情况,底层数据结构是红黑树(平衡、有序的二叉树,基于有序二叉树的快速查询功能实现的元素不重复,数据排序只是顺带功能)。
支持的运算符:
set operator=(const set& c2); bool operator==(const set& c1, const set& c2); bool operator!=(const set& c1, const set& c2); bool operator<(const set& c1, const set& c2); bool operator>(const set& c1, const set& c2); bool operator<=(const set& c1, const set& c2); bool operator>=(const set& c1, const set& c2); 注意:相等指集合内有相同的元素
set容器的成员函数:
void clear(); 功能:删除所有元素 bool empty() const; 功能:判断集合是否是为空 iterator begin(); const_iterator begin() const; 功能:返回一个正向迭代器,指向值最小的元素 iterator end(); const_iterator end() const; 功能:返回一个正向迭代器,指向最大值元素的下一个位置 注意: begin与end得到的是从小到大的序列。 void erase( iterator pos ); 功能:删除pos指向的元素 void erase( iterator start, iterator end ); 功能:删除[start,end)区间的元素 size_type erase(const key_type& key); 功能:删除所以值为key的元素 返回值:删除了多少个元素,在set容器中只有两种可能0或1 size_type count( const key_type& key); 功能:统计值为key的元素个数,实际意义是判断值为key的元素是否存在,返回值只有两种可能0或1 reverse_iterator rbegin(); const_reverse_iterator rbegin() const; 功能:返回一个逆向迭代器,指向集合中的最大值元素 reverse_iterator rend(); const_reverse_iterator rend() const; 功能:返回一个逆向迭代器,指向集合中的最小值元素的下一个位置 注意:rbegin与rend得到的是从大到小的序列 size_type size() const; 功能:获取集合中元素的个数 void swap( container& from ); 功能:交换两个集合 iterator find( const key_type& key ); 功能:查询值key的元素,并返回指向它的迭代器,如果返回的是 end()的值 则说明没有找到 void insert( input_iterator start, input_iterator end); 功能:添加一个区间的元素[start,end) pair<iterator,bool> insert( const TYPE& val ); 功能:添加一个值为val的元素,并返回两个值,一个指向元素的迭代器,另一个添加元素是否成功 size_type max_size() const; 功能:计算出该集合最多能存储多少个元素,会根据元素的字节而变化 iterator lower_bound( const key_type& key ); 功能:返回一个正向迭代器,指向第一个小于等于key的元素 iterator upper_bound( const key_type& key ); 功能:返回一个正向迭代器,指向第一个大于key的元素。 pair<iterator, iterator> equal_range(const key_type& key); 功能:返回一个对迭代器区间,获取集合中所以值为key的元素,该函数在set集合中没有意义 iterator insert(iterator i, const TYPE& val ); 功能:在i位置前插入一个值为val的元素,并返回一个指向新元素的迭代器。 value_compare value_comp() const; 功能:获取set集合元素比较函数,但可以直接调用,s.key_comp()(k1,k2),在set集合中与key_compare函数没有区别,在map集合中这两个函数是不同的。 key_compare key_comp() const; 功能:获取set集合元素比较函数,但可以直接调用,s.key_comp()(k1,k2);
9、multiset多重集合
multiset 容器与set容器最大的区别是,multiset元素可以重复,其它功能与set集合相同,它们的成员函数都相同,但有部分函数在set集合中没有意义,但multiset集合中意义重大。
size_type count( const key_type& key); 功能:计算出值为key的元素个数 pair<iterator, iterator> equal_range(const key_type& key); 功能:获取一个迭代器区间,指向多重集合中值为key的元素
10、map容器
map容器在C++中被称为映射容器,里面存储的 key/value 型的对值,在其它编程语言中也被称为字典,它的存储结构、使用方法与redis数据库非常像,底层使用红黑树作为存储结构,所以它的查询效率非常高,一般存储类、结构等对象,使用对象的ID作为key,例如:学生、老师、员工等,使用学号、工号作为key。
map容器中一个key只能对应一个value,对值的key不能重复。
map会自动以key为判断条件对value进行排序(红黑树:平衡且有序的二叉树),所以元素的key必须支持 < 运算符。
注意:该容器最大的特点就是会自动排序、键值不重复,可以根据键值快速查询value,查询的时间复杂度是O(logn)
常见面试题:map的底层数据结构是什么,为什么使用它。
二叉搜索树->平衡二叉树->AVL树->红黑树
支持的运算符:
TYPE& operator[]( const key_type& key ); 注意:该运算符的功能比较多,可以查询、添加、遍历, map operator=(const map& c2); bool operator==(const map& c1, const map& c2); bool operator!=(const map& c1, const map& c2); bool operator<(const map& c1, const map& c2); bool operator>(const map& c1, const map& c2); bool operator<=(const map& c1, const map& c2); bool operator>=(const map& c1, const map& c2); 注意:相等指容器内有相同key的元素
与其它容器功能相同的成员函数:
iterator begin();
const_iterator begin() const;
功能:返回一个正常迭代器,指向容器中key值最小的元素,该迭代器指向 pair<key,value> 对象,first就是key,second就是value。
void clear();
功能:删除所有元素
size_type count(const key_type& key);
功能:统计键值等于key的元素有多少个,由于map容器不能重复,所以该函数在map容器中只能返回0/1,适合与[]运算符配合使用。
bool empty() const;
功能:判断该容器是否为空
iterator end();
const_iterator end() const;
功能:返回一个正常迭代器,指向容器中key值最在的元素的下一个位置
pair<iterator, iterator> equal_range(const key_type& key);
功能:返回一个对迭代器区间,获取容器中所有键值等于key的元素,该函数在map容器中没有意义
void erase( iterator pos );
功能:删除pos指向的元素
void erase( iterator start, iterator end );
功能:删除[start,end)区间的元素
size_type erase( const key_type& key );
功能:删除键值等于key的元素
iterator find(const key_type& key);
功能:返回一个正常迭代器,指向键值等于key的元素
iterator insert(iterator i, const TYPE& pair );
功能:在i位置插入一个对值(key/value)
void insert( input_iterator start, input_iterator end );
功能:添加一个区间的元素[start,end)
pair<iterator,bool> insert( const TYPE& pair );
功能:往容器中添加一个对值(key/value),如果容器中已经有key存储则添加失败
make_pair(key,value);
key_compare key_comp() const;
功能:获取key值的比较函数
value_compare value_comp() const;
功能:获取value的比较函数
iterator lower_bound( const key_type& key );
功能:返回一个正常迭代器,指向第一个键值大于等于key的元素
size_type max_size() const;
功能:统计容器中最多能存储多少个元素,会根据元素的字节数而变化
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
功能:返回一个逆向迭代器,指向容器中键值最大的元素
reverse_iterator rend();
const_reverse_iterator rend() const;
功能:返回一个逆向迭代器,指向容器中键值最小的元素下一个位置
size_type size() const;
功能:获取容器中有多少个元素
void swap( container& from );
功能:交换两个容器中的元素
iterator upper_bound( const key_type& key );
功能:返回一个正常迭代器,指向第一个键值大于key的元素
#include <iostream>
#include <map>
using namespace std;
struct Student
{
int id;
string name;
char sex;
short age;
float score;
Student(int id=0,const string& name="",char sex='x',short age=0,float score=0)
{
this->id = id;
this->name = name;
this->sex = sex;
this->age = age;
this->score = score;
}
friend ostream& operator<<(ostream& os,const Student& stu)
{
os << stu.id << " ";
os << stu.name << " ";
os << (stu.sex?"女":"男") << " ";
os << stu.age << " ";
return os << stu.score << endl;
}
};
int main(int argc,const char* argv[])
{
// 创建映射/关联/字典容器
map<int,Student> m;
for(int i=0; i<10; i++)
{
char name[20];
sprintf(name,"hehe%d",i+1);
Student stu(1001+i%5,name,i%2?'w':'m',18+i,rand()%100+1);
// 以make(key,value)插入元素
// m.insert(make_pair(stu.id,stu));
// 以key = value添加元素
m[stu.id] = stu;
}
// 使用正向迭代器遍历容器
map<int,Student>::iterator it = m.begin();
while(it != m.end())
{
// 由于存储的是key/value对值,所以map的迭代器有两个成员
cout << it->first << " " << it->second;
it++;
}
for(int i=1001; i<1010; i++)
{
map<int,Student>::iterator it = m.find(i);
if(it != m.end())
cout << it->second;
}
return 0;
}
11、multimap容器
multimap 容器与map容器最大的区别是,multimap容器key可以重复,也就是说一个key可以对应多个value,所以multimap 容器不支持[]运算符,所有只能使用insert函数插入数据。
其它功能与map容器相同,它们的成员函数都相同,但有部分函数在map容器中没有意义,但multimap集合中意义重大。
size_type count(const key_type& key); 功能:键值key对应了多个value pair<iterator, iterator> equal_range(const key_type& key); 功能:获取键值key对应的所有元素,first指向第一个键值为key的元素,second指向最后一个键值为key的元素下一个位置
#include <iostream>
#include <map>
using namespace std;
int main(int argc,const char* argv[])
{
multimap<int,string> mm;
for(int i=0; i<10; i++)
{
char name[20];
sprintf(name,"hehe%d",i+1);
int class_id = 1001+i%4;
//mm.insert(make_pair(class_id,name));
}
typedef multimap<int,string>::iterator mi;
for(int i=1001; i<1005; i++)
{
cout << i <<"班级中有"<< mm.count(i) << "位同学" << endl;
pair<mi,mi> p = mm.equal_range(i);
while(p.first != p.second)
{
cout << p.first->first << " " << p.first->second << endl;
p.first++;
}
}
return 0;
}
练习:学校举办跳水比赛,选手的编号为:1001~1015,有10们评委老师会根据选手的表现打出成绩(随机产生)存储在multimap容器里,请你计算出每个选手的最后得分。
12、deque容器
deque容器也叫双端队列容器,但它的操作跟队列没有什么关系,使用方法与vector类似,只是比它多的头部添加和尾部添加,可以把它看作是list+vector=deque
。
支持的运算符:
TYPE& operator[]( size_type index ); const TYPE& operator[]( size_type index ) const; container operator=(const container& c2); bool operator==(const container& c1, const container& c2); bool operator!=(const container& c1, const container& c2); bool operator<(const container& c1, const container& c2); bool operator>(const container& c1, const container& c2); bool operator<=(const container& c1, const container& c2); bool operator>=(const container& c1, const container& c2); 注意:容器对象相等,指的是容器内有相同的元素并有着相同的顺序
deque(size_type num, const TYPE& val = TYPE()); 功能:创建一个双端队列,往里面添加num个元素,每个元素初始化为val deque(input_iterator start, input_iterator end); 功能:创建一个双端队列,往里面添加[start,end)区间的元素 void assign(input_iterator start, input_iterator end); 功能:使用一个迭代器区间给当前容器中元素赋值 void assign(size_type num, const TYPE &val); 功能:给当前对容器中的前num个元素,赋值为val,如果容器中元素不中num个,会自动往里添加 TYPE& at(size_type loc); const TYPE& at(size_type loc) const; 功能:使用loc作为下标访问成员,它比[]访问更安全,会检查loc是否会法,如果超出范围会抛出std::out_of_range异常。 TYPE& back(); const TYPE& back() const; 功能:获取容器中的最后一个元素,如果容器为空则返回的就是悬空引用,会产生段错误 TYPE& front(); const TYPE& front() const; 功能:获取容器中的第一个元素,如果容器为空则返回的就是悬空引用,会产生段错误 void clear(); 功能:清空容器中的所有元素 iterator begin(); const_iterator begin() const; 功能:返回一个正向迭代器,指向容器中的第一个元素 iterator end(); const_iterator end() const; 功能:返回一个正向迭代器,指向容器中的最后一个元素的下一个位置 reverse_iterator rbegin(); const_reverse_iterator rbegin() const; 功能:返回一个逆向迭代器,指向容器中的最后一个元素 reverse_iterator rend(); const_reverse_iterator rend() const; 功能:返回一个逆向迭代器,指向容器中的第一个元素的下一个位置 iterator erase(iterator loc); 功能:删除loc指向的元素 返回:loc的下一位置迭代器 iterator erase(iterator start, iterator end); 功能:删除迭代器区间[start,end)的元素 iterator insert( iterator loc, const TYPE& val ); 功能:在迭代器loc指向的元素前面插入一个值为val的元素,返回一个指向新的元素的迭代器 void insert( iterator loc, size_type num, const TYPE& val ); 功能:在迭代器loc指向的元素前面插入num个值为val的元素 void insert( iterator loc, input_iterator start, input_iterator end ); 功能:在迭代器loc指向的元素前面插入一个区别的元素 size_type max_size() const; 功能:计算出当前容器能存储多个元素,它与所存储元素的类型有关。 void pop_back(); 功能:删除容器的最后一个元素,如果容器为空则段错误。 void push_back(const TYPE& val); 功能:在容器的末尾添加一个元素 void pop_front(); 功能:删除容器的第一个元素,如果容器为空则段错误。 void push_front( const TYPE& val ); 功能:在容器的头部添加一个元素 void reserve(size_type size); 功能:让当容器预留至少共容纳size个元素的空间 void resize(size_type num, const TYPE& val = TYPE()); 功能:设置容器的容量为num num > size() 扩充元素,并对扩充的元素初始化 num < size() 删除尾部的size-num个元素 void swap(container& from); 功能:交换两个容器的元素
13、bitset容器
bitset容器封装了二进制的位运算操作,该功能在C++语言中有点鸡肋,位运算操作常用于嵌入 式、单片机、驱动开发,但C++语言并不擅长做这类工作,所以该容器了解即可。
注意:bitset的模板参数与其它容器不同
容器名<类型> 对象名;
bitset<二进制位数> 对象名;
bitset容器的成员函数:
bitset(unsigned long val); 功能:用val的补码给容器中的二进制赋值 bool any(); 功能:任意一位二进制为1,结果就是true,就是把二进制数据转换成bool类型的数据 size_type count(); 功能:计算二进制数据中有多少个1 bitset<N>& flip(); 功能:对容器的所有二进制位进行按位求反 bitset<N>& flip(size_t pos); 功能:对第pos个二进制位进行求反(pos从低到高计算) bool none(); 功能:如果没有二进制位被设为1返回真,否则返回假,相当于进行了逻辑求反运算 bitset &set(); 功能:设置所有二进制位都为1 bitset &set( size_t pos, int val=1 ); 功能:设置指定的二进制位为val bitset<N>& reset(); 功能:设置所有二进制位都为0 bitset<N>& reset( size_t pos ); 功能:设置指定的二进制位为0 size_t size(); 功能:获取二进制的位数 bool test( size_t pos ); 功能:访问pos二进制位,为0返回false,为1返回true string to_string(); 功能:把二进制转换成字符串 unsigned long to_ulong(); 功能:把二进制转换成无符号整数
支持的运算符:
!=, == 功能:比较二进制位是否全部相等 &=, ^=, |=, ~, <<=, >>= 功能:进行二进制运算 [] 功能:访问指定的二进制位
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc,const char* argv[])
{
unsigned int* ptr = 0xE0200240;
/*
*ptr &= 0xff000fff;
*ptr |= 0x00111000;
*/
bitset<32> bs(*ptr);
bs.set(12);
bs.reset(13);
bs.reset(14);
bs.reset(15);
bs.set(16);
bs.reset(17);
bs.reset(18);
bs.reset(19);
bs.set(20);
bs.reset(21);
bs.reset(22);
bs.reset(23);
*ptr = bs.to_ulong();
}
14、STL模板库总结
STL模板库就是使用C++语言的模板技术对数据结构和算法的封装,不建议在项目中大量使用类模板(容器),因为容器中每存储一种新的类、结构编译器就需要生成一份该数据结构的相关代码,过多使用会使项目的代码段快速增大,编译速度变慢,一般大型项目的编译时间是以小时为单位。
STL模板库默认内存管理机制速度比较慢,因为new不能调整已经有的内存块大小,基本都是释放旧的,重新申请新的,如果对代码的运行速度要求比较高,建议自己实现数据结构。
STL模板库中大多数容器都支持迭代器,begin(),end()函数中有step in操作,会让程序的调试变的复杂。
我们目前对于STL模板库会用即可,如果后期工作中需要大量使用STL模板库,我个人建议你把所有的容器自己手动实现一下(STL模板源码解析-候捷)。
在C++11语法标准中已经加了Boost模块,Boost库的功能比较齐全,我个人认为是以后的发展方向。
vector、list、deque
stack、queue、p queue、
set multiset map multimap
bitset










