常用STL的使用
STL是什么
STL是一个标准模板库,高效的C++程序库。
stdlib.h库提供了quit-sort函数。
string
//字符指针,字符数组
char str[12]="hello";
char *p=str;
*p='h';//改变第一个字母
char *ptr="hello";
*ptr='h';//错误
第一个字符串用数组开辟,可以改变。
第二个字符串是一个常量,值是不能改变的,只可以改变指针指向。
string类的构造函数和析构函数
string s; // 生成一个空字符串
string s(string str) // 拷贝构造函数生成str的复制品
string s(string str,int stridx) // 将字符串str内“始于位置stridx”的部分当作
// 字符串的初值
string s(char *str,int stridx,int strlen) // 将字符串str内“始于stridx且长度顶多strlen”
// 的部分作为字符串的初值
string s(char *cstr) // 将C字符串作为s的初值
string s(char *chars,int chars_len) // 将C字符串前chars_len个字符作为字符串s的
// 初值
string s(int num,char c) // 生成一个字符串,包含num个c字符
string s(char *beg,char *end) // 以区间beg;end(不包含end)内的字符作为字符
// 串s的初值
s.~string() // 销毁s字符,释放内存
C++字符串和C字符串的转换
左边转右边方法
- data(),返回字符数组,不添加’\0’
- c_str(),返回’\0’结尾的字符数组,它是临时的,当原本数据改变后,数据就会失效。
- copy(),把字符串的内容复制或写入既有c_string或字符数组内。
C++字符串不以’\0’结尾。
注意c_str()。
#include<iostream>
#include<string>
using namespace std;
int main(){
string str="Hello world.";
const char * cstr=str.c_str();
cout<<cstr<<endl;
str="Abcd.";
cout<<cstr<<endl;
return 0;
}
结果
hello world.
abcd.
容易出现错误。
可以考虑把数据深拷贝来解决问题
#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main(){
char * cstr=new char[20];
string str="Hello world.";
strncpy(cstr,str.c_str(),str.size());
cout<<cstr<<endl;
str="Abcd.";
cout<<cstr<<endl;
return 0;
}
结果
hello world.
abcd.
copy(p,n,size_type_Off=0)这句表明从string类型对象中至多复制n个字符到字符指针p指向的空间中,并且默认从首字符开始,也可以指定开始位置(从0开始计数)。不过需要确保p有n个空间存储。
string和int类型的转换
int转string
int snprintf(char *str,size_t size,const char *format,...)
功能是:将可变个参数…按照format格式化成字符串,然后将其复制到str中。
如果格式化后的字符串长度小于size,则全部复制到str中,并且加上结束符’\0’
如果格式化后的字符串长度不小于size,则只将其中的(size-1)个字符全部复制到str中,并且加上结束符’\0’
函数的返回值成功就是写入的字符串长度,出错则返回负值。
string转int函数(细节暂时不追)
- strtol
- strtoll
- strtoul
- stroull
string的其他常用成员函数
- capacity,返回当前容量
- size(),当前字符串大小
- resize(),把字符串当前大小置为len,并用字符c填充不足的部分
vector
reserve()方法可以调整分配的容量大小。
赋值和拷贝构造都是值复制一整份。
vector的增删改查
for_each遍历
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void print(int n)
{
cout<<n<<" ";
}
int main(){
int a[7]={1,2,3,4,5,6,7};
vector<int> ivector(a,a+7);
vector<int>::iterator iter;
for_each(ivector.begin(),ivector.end(),print);//ÓÃfor_each½øÐбéÀú
cout<<endl;
ivector[5]=1;
cout<<ivector[5]<<endl<<ivector.size()<<endl;
for_each(ivector.begin(),ivector.end(),print);//ÓÃfor_each½øÐбéÀú
return 0;
}
vector中存放结构体时的排序
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
typedef struct rect{
int id;
int length;
int width;
//按照id,length,width升序排序
bool operator< (const rect &a) const{
if(id!=a.id)
return id<a.id;
else{
if(length!=a.length)
return length<a.length;
else
return width<a.width;
}
}
}Rect;
int main(){
vector<Rect> vec;
Rect rect;
rect.id=2;
rect.length=3;
rect.width=4;
vec.push_back(rect);
rect.id=1;
rect.length=2;
rect.width=3;
vec.push_back(rect);
vector<Rect>::iterator it=vec.begin();
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
sort(vec.begin(),vec.end());
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
return 0;
}
运行结果
2 3 4
1 2 3
另一种方式
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
typedef struct rect{
int id;
int length;
int width;
}Rect;
int cmp(Rect a,Rect b){
if(a.id!=b.id)
return a.id<b.id;
else{
if(a.length!=b.length)
return a.length<b.length;
else
return a.width<b.width;
}
}
int main(){
vector<Rect> vec;
Rect rect;
rect.id=2;
rect.length=3;
rect.width=4;
vec.push_back(rect);
rect.id=1;
rect.length=2;
rect.width=3;
vec.push_back(rect);
vector<Rect>::iterator it=vec.begin();
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
//调用变化了
sort(vec.begin(),vec.end(),cmp);
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
return 0;
}
运行结果不变
vector的查找
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
int main(){
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
//调用函数
vector<int>::iterator iter=find(vec.begin(),vec.end(),3);
if ( iter==vec.end())
cout << "Not found" << endl;
else
cout << "Found" << endl;
return 0;
}
vector的删除
pop_back删除最后一个元素
vec.erase(vec.begin()+i,vec.end()+j)删除区间[i,j-1]间的元素。
for(vector<int>::iterator iter=veci.begin();iter!=veci.end();iter++)
{
if(*iter==3)veci.erase(iter);
}
代码隐藏严重错误,当veci.erase(iter)语句执行了之后,iter就变成了一个野指针,对一个野指针进行iter++操作出错。
修改代码为
for(vector<int>::iterator iter=veci.begin();iter!=veci.end();iter++)
{
if(*iter==3)iter=veci.erase(iter);
}
还是错的,无法删除两个连续的3,数字3位于vector最后位置也会出错(在vec.end()上执行++操作)。
正确代码
for(;iter!=vec.end();)
{
if(*iter==3)iter=veci.erase(iter);
else ++iter;
}
vector的增加
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
void print( vector<int>v ){
vector<int>::iterator iter=v.begin();
for(;iter!=v.end();iter++)
cout<<*iter<<" ";
cout<<endl;
}
int main(){
vector<int> v; //现在容器中有0个元素
int values[] = {1,3,5,7};
v.insert(v.end(), values+1, values+3);//现在容器中有2个元素分别为:3,5
print(v);
v.push_back(9); //现在容器中有3个元素分别为:3,5,9
print(v);
v.erase(v.begin()+1);//现在容器中有2个元素分别为:3,9
print(v);
v.insert(v.begin()+1, 4);//现在容器中有3个元素分别为:3,4,9
print(v);
v.insert(v.end()-1, 4, 6);//现在容器中有7个元素分别为:3,4,6,6,6,6,9
print(v);
v.erase(v.begin()+1, v.begin()+3); //现在容器中有5个元素分别为:3,6,6,6,9
print(v);
v.pop_back(); //现在容器中有4个元素分别为:3,6,6,6
print(v);
v.clear();//现在容器中有0个元素
print(v);
if (true == v.empty()) //如果容器为空则输出"null"
{
std::cout<<"null"<<std::endl;
}
return 0;
}
vector的内存管理与效率
对于vector容器来说,如果有大量的数据需要进行push_back,应当使用reserve()函数提前设定其容量大小,否则会出现许多次容量扩充导致效率低下。
使用交换技巧来修整vector过剩空间/内存
有一种办法来把它从曾经最大的容量减少到它现在需要的容量
即vector(ivec).swap(ivec)。
.前面的表达式表示建立一个临时vector,它是ivec的一份拷贝。
但是vector的拷贝构造函数只分配拷贝的元素需要的内容,所以这个临时vector没有多余的容量。
然后临时vector和ivec交换数据完成,现在临时变量持有了曾经在ivec中没用到的过剩容量。
然后这个语句结尾处,临时vector被销毁,以释放以前ivec使用的内存,收缩到合适的大小。
用swap方法强行释放vector所占内存
vector<int>().swap(v);
//或者
v.swap(vector<int>());
//或者
{}包括的变量,会在退出{}后析构掉。
vector增加容量是2的幂次方
vector类的简单实现
#include<algorithm>
#include<iostream>
#include <assert.h>
using namespace std;
template<typename T>
class myVector
{
private:
/*walk length*/
/*myVector each time increase space length*/
#define WALK_LENGTH 64;
public:
/*default constructor*/
myVector():array(0),theSize(0),theCapacity(0){ }
myVector(const T& t,unsigned int n):array(0),theSize(0),theCapacity(0){
while(n--){
push_back(t);
}
}
/*copy constructor*/
myVector(const myVector<T>& other):array(0),theSize(0),theCapacity(0){
*this = other;
}
/*= operator*/
myVector<T>& operator =(myVector<T>& other){
if(this == &other)
return *this;
clear();
theSize = other.size();
theCapacity = other.capacity();
array = new T[theCapacity];
for(unsigned int i = 0 ;i<theSize;++i)
{
array[i] = other[i];
}
return *this;
}
/*destructor*/
~myVector(){
clear();
}
/*the pos must be less than myVector.size();*/
T& operator[](unsigned int pos){
assert(pos<theSize);
return array[pos];
}
/*element theSize*/
unsigned int size(){
return theSize;
}
/*alloc theSize*/
unsigned int capacity(){
return theCapacity;
}
/*is empty*/
bool empty(){
return theSize == 0;
}
/*clear myVector*/
void clear(){
deallocator(array);
array = 0;
theSize = 0;
theCapacity = 0;
}
/*adds an element in the back of myVector*/
void push_back(const T& t){
insert_after(theSize-1,t);
}
/*adds an element int the front of myVector*/
void push_front(const T& t){
insert_before(0,t);
}
/*inserts an element after the pos*/
/*the pos must be in [0,theSize);*/
void insert_after(int pos,const T& t){
insert_before(pos+1,t);
}
/*inserts an element before the pos*/
/*the pos must be less than the myVector.size()*/
void insert_before(int pos,const T& t){
if(theSize==theCapacity){
T* oldArray = array;
theCapacity += WALK_LENGTH;
array = allocator(theCapacity);
/*memcpy(array,oldArray,theSize*sizeof(T)):*/
for(unsigned int i = 0 ;i<theSize;++i){
array[i] = oldArray[i];
}
deallocator(oldArray);
}
for(int i = (int)theSize++;i>pos;--i){
array[i] = array[i-1];
}
array[pos] = t;
}
/*erases an element in the pos;*/
/*pos must be in [0,theSize);*/
void erase(unsigned int pos){
if(pos<theSize){
--theSize;
for(unsigned int i = pos;i<theSize;++i){
array[i] = array[i+1];
}
}
}
private:
T* allocator(unsigned int size){
return new T[size];
}
void deallocator(T* arr){
if(arr)
delete[] arr;
}
private:
T* array;
unsigned int theSize;
unsigned int theCapacity;
};
void printfVector(myVector<int>& vector1){
for(unsigned int i = 0 ; i < vector1.size();++i){
cout<<vector1[i]<<",";
}
cout<<"alloc size = "<<vector1.capacity()<<",size = "<<vector1.size()<<endl;
}
int main(){
myVector<int> myVector1;
myVector<int> myVector2(0,10);
myVector2.push_front(1);
myVector2.erase(11);
printfVector(myVector2);
myVector1.push_back(2);
myVector1.push_front(1);
printfVector(myVector1);
myVector1.insert_after(1,3);
printfVector(myVector1);
myVector2 = myVector1;
myVector2.insert_before(0,0);
myVector2.insert_before(1,-1);
printfVector(myVector2);
return 0;
}
Map
map内部数据的组织是一棵红黑树,所有数据都是有序的。
对于key的类型,唯一的约束就是必须支持<操作符。尤其是自定义结构体时要注意重载<操作符
比如
typedef struct tagStudentInfo
{
int iID;
string strName;
bool operator < (tagStudentInfo const& r) const {
//这个函数指定排序策略,按iID排序,如果iID相等的话,按strName排序
if(iID < r.iID) return true;
if(iID == r.iID) return strName.compare(r.strName) < 0;
return false;
}
}StudentInfo;//学生信息
int main(){
/*用学生信息映射分数*/
map<StudentInfo, int>mapStudent;
}
map的插入
三种方式
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(map<int, string>::value_type (1,"student_one"));
mapStudent[1] = "student_one";
用inser函数插入数据,在数据的插入上涉及集合的唯一性,但使用数组方式就可以覆盖以前该关键字对应的值。
判断Insert是否插入成功
pair<map<int, string>::iterator, bool> insert_pair;
insert_pair = mapStudent.insert(pair<int,string>(1,"student_one"));
if(insert_pair.second == true){
cout<<"Insert Successfully"<<endl;
}
else{
cout<<"Insert Failure"<<endl;
}
map遍历
三种方式
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
map<int, string>::reverse_iterator iter;
for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
int iSize = mapStudent.size();
for(int i = 1; i <= iSize; i++){
cout<<i<<" "<<mapStudent[i]<<endl;
}
数组访问vector时,下标是从0-size-1。而map是从1-size。
map的查找
用find
map<int, string>::iterator iter=mapStudent.find(1);
if(iter != mapStudent.end()){
cout<<"Found, the value is "<<iter->second<<endl;
}else{
cout<<"Do not found"<<endl;
}
用count函数
map的删除
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main(){
map<int, string> mapStudent;
mapStudent[1]="student_one";
mapStudent[2]="student_two";
mapStudent[3]="student_three";
mapStudent[4]="student_four";
map<int, string>::iterator iter=mapStudent.begin();
for(;iter!=mapStudent.end();){
if((*iter).second=="student_one"){
mapStudent.erase(iter++);
}
else{
++iter;
}
}
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
erase(iter++),不是erase(iter),然后iter++。
因为iter指针被erase之后就失效了,不能再++了。
它是先让iter++,然后返回原来的iter去erase。这样就没问题。
map的排序
默认key从小到大排序。
map定义
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map
第三个参数
typename _ Compare = std::less<_Key>
默认值是less<_Key>,它是STL里面的一个函数对象。
它的实现就是对()操作符进行了重载。
template<typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
stl还提供了greater
如果要让map中的元素按照key从大到小排序
map<string, int, greater<string> > mapStudent;
如果要按自己的逻辑来比较,比如按照学生姓名的长短排序进行存储。这样做
#include <map>
#include <string>
#include <iostream>
using namespace std;
struct CmpByKeyLength {
bool operator()(const string& k1, const string& k2) {
return k1.length() < k2.length();
}
};
int main(){
map<string, int, CmpByKeyLength > mapStudent;
mapStudent["LiMin"]=90;
mapStudent["ZiLinMi"]=72;
mapStudent["BoB"]=79;
map<string, int>::iterator iter=mapStudent.begin();
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
map按value排序(106-109)
sort只针对线性,map不是线性。
要实现排序,元素必须实现了<操作。
P107 ==问题。
Set
stl里的list封装了链表。
set中每个元素的值都唯一,而且会根据元素的值自动排序。
stl的关联容器set,multiset,map,multimap内部采用的都是红黑树。
不要使用过期的iterator(多线程情况下比如vector的iterator可能失效,扩容情况下)
创建
/*创建set对象,共5种方式,提示如果比较函数对象及内存分配器未出现,即表示采用的是系统默认方式*/
/*创建空的set对象,元素类型为int,*/
set<int> s1;
/*创建空的set对象,元素类型char*,比较函数对象(即排序准则)为自定义strLess*/
set<const char*, strLess> s2( strLess);
/*利用set对象s1,拷贝生成set对象s2*/
set<int> s3(s1);
/*用迭代区间[&first, &last)所指的元素,创建一个set对象*/
int iArray[] = {13, 32, 19};
set<int> s4(iArray, iArray + 3);
/*用迭代区间[&first, &last)所指的元素,及比较函数对象strLess,创建一个set对象*/
const char* szArray[] = {"hello", "dog", "bird" };
set<const char*, strLess> s5(szArray, szArray + 3, strLess() );
增
/*元素插入:
1,插入value,返回pair配对对象,可以根据.second判断是否插入成功。(提示:value不能与set容器内元素重复)
pair<iterator, bool> insert(value)
2,在pos位置之前插入value,返回新元素位置,但不一定能插入成功
iterator insert(&pos, value)
3,将迭代区间[&first, &last)内所有的元素,插入到set容器
void insert[&first, &last);
*/
cout<<"s1.insert() : "<<endl;
for (int i = 0; i <5 ; i++)
s1.insert(i*10);
printSet(s1);
cout<<"s1.insert(20).second = "<<endl;
if (s1.insert(20).second)
cout<<"Insert OK!"<<endl;
else
cout<<"Insert Failed!"<<endl;
cout<<"s1.insert(50).second = "<<endl;
if (s1.insert(50).second){
cout<<"Insert OK!"<<endl;
printSet(s1);
}else
cout<<"Insert Failed!"<<endl;
cout<<"pair<set<int>::iterator, bool> p;\np = s1.insert(60);\nif (p.second):"<<endl;
pair<set<int>::iterator, bool> p;
p = s1.insert(60);
if (p.second){
cout<<"Insert OK!"<<endl;
printSet(s1);
}
else
cout<<"Insert Failed!"<<endl;
删
/*元素删除
1,size_type erase(value) 移除set容器内元素值为value的所有元素,返回移除的元素个数
2,void erase(&pos) 移除pos位置上的元素,无返回值
3,void erase(&first, &last) 移除迭代区间[&first, &last)内的元素,无返回值
4,void clear(), 移除set容器内所有元素*/
cout<<"\ns1.erase(70) = "<<endl;
s1.erase(70);
printSet(s1);
cout<<"s1.erase(60) = "<<endl;
s1.erase(60);
printSet(s1);
cout<<"set<int>::iterator iter = s1.begin();\ns1.erase(iter) = "<<endl;
set<int>::iterator iter = s1.begin();
s1.erase(iter);
printSet(s1);
查
/*元素查找
count(value)返回set对象内元素值为value的元素个数
iterator find(value)返回value所在位置,找不到value将返回end()
lower_bound(value),upper_bound(value), equal_range(value)*/
cout<<"\ns1.count(10) = "<<s1.count(10)<<", s1.count(80) = "<<s1.count(80)<<endl;
cout<<"s1.find(10) : ";
if (s1.find(10) != s1.end())
cout<<"OK!"<<endl;
else
cout<<"not found!"<<endl;
cout<<"s1.find(80) : ";
if (s1.find(80) != s1.end())
cout<<"OK!"<<endl;
else
cout<<"not found!"<<endl;