目录
一.STL容器
1.array
- 使用时要包含 <array> 头文件
其实用起来很简单,只要明白他其实是一个模板类就足够了。下面自己来简单实现一下(只是让他看起来像,没有内置迭代器,后面会讲):
class myArray
{
public:
myArray()
{
memory = new _Ty[size];
}
~myArray()
{
delete[] memory;
}
_Ty& operator[](int idex)
{
return memory[idex];
}
protected:
_Ty* memory;
};
int main()
{
myArray<int, 3> arr;
for (int i = 0; i < 3; i++)
{
arr[i] = i;
}
return 0;
}
可以定义,可以插入,但是跟标准库中的还是有很大差别,因为标准库中很多操作都是建立在迭代器之上的,前面我有粗略地提过迭代器的使用,现在来仔细地讲一讲:
-
迭代器: 就是一个类中类,通过运算符重载通过类中类的对象去遍历容器
-
迭代器的分类
-
正向迭代器: iterator
- begin();
- end();
-
反向迭代器: reverse_iterator
- rbegin();
- rend();
-
常正向迭代器: const_iterator
- cbegin();
- cend();
-
常反向迭代器:const_reverse_iterator
- crbegin();
- crend();
-
-
按功能分类
- 正向迭代
- 双向迭代
- 随机访问迭代器
根据这个,我们可以自己简单实现一下(之前推文有写过):
class myArray
{
public:
myArray()
{
memory = new _Ty[size];
}
~myArray()
{
delete[] memory;
}
_Ty& operator[](int idex)
{
return memory[idex];
}
_Ty* begin()
{
return memory;
}
_Ty* end()
{
return memory + size;
}
class iterator
{
public:
iterator(_Ty* pmove = nullptr):pmove(pmove){}
//运算符重载
void operator=(_Ty* pmove)
{
this->pmove = pmove;
}
bool operator!=(_Ty* pmove)
{
return this->pmove != pmove;
}
iterator operator++(int)
{
this->pmove++;
return *this;
}
_Ty operator*()
{
return *pmove;
}
protected:
_Ty* pmove;
};
protected:
_Ty* memory;
};
int main()
{
myArray<int, 3> arr;
for (int i = 0; i < 3; i++)
{
arr[i] = i;
}
myArray<int, 3>::iterator iter;
for (iter = arr.begin(); iter != arr.end(); iter++)
{
cout << *iter << '\t';
}
return 0;
}
可以发现,array跟数组没什么差别,所以,实际上数组也可以看作是容器。
下面是容器的一些基本操作:
class Man
{
public:
Man(){}
Man(string name,int age):name(name),age(age){}
void print()
{
cout << name << '\t' << age << endl;
}
protected:
string name;
int age;
};
int main()
{
//可以定义时初始化
array<int, 3> arr1 = { 1,2,3 };
array<int, 3> arr2;
//可以交换,但长度一定要一样长
arr1.swap(arr2);
//有迭代器可以使用新版本的for循环打印
for (auto v : arr2)
{
cout << v << '\t';
}
//处理自定义类型数据
array<Man, 3> arr3;
for (int i = 0; i < arr3.size(); i++)
{
string name = "name";
arr3[i] = Man(name + to_string(i), i);
}
for (auto v : arr3)
{
v.print();
}
return 0;
}
2.vector
- 使用时要包含 <vector> 头文件
- 使用时可以带长度,也可以不带,可以初始化,也可以不初始化
下面是各种初始化方式汇总:
//辅助函数,遍历容器
template <class _Ty>
void printVector(vector<_Ty>& temp)
{
for (auto v : temp)
{
cout << v << '\t';
}
cout << endl;
}
int main()
{
vector<int> vecData;
//采用成员函数的方式做插入.插入多少个元素都可以--->会自动扩建
for (int i = 0; i < 3; i++)
{
vecData.push_back(i); //尾插法 0 1 2
}
printVector(vecData);
//带初始化
vector<double> dData = { 1.1,1.23,1.44 }; //自动推断长度为3
printVector(dData);
vector<string> strData(3); //当前动态数组的长度是3
for (int i = 0; i < 3; i++) //确定长度,可以直接使用数组法插入
{
string name = "name";
//只有在确定长度范围以内的可以直接采用数组法(下标)插入
strData[i] = name + to_string(i);
}
printVector(strData);
//strData[3] = "name3"; vector subscript out of range 引发中断
strData.push_back("name3"); //超过的必须用成员函数插入(会自动扩增)
printVector(strData);
return 0;
}
如果数据是自定义类型,那最好重载一下流运算符,老生常谈了,这里就不演示,接下来附上vector常用操作:
int main()
{
vector<int> vec = { 1,2,3,4 };
cout << vec.size() << endl; //当前容器中的元素个数
cout << vec.empty() << endl; //判断是否为空
cout << vec.front() << endl; //访问第一个元素
cout << vec.back() << endl; //访问最后一个元素
cout << vec.at(2) << endl; //直接用下标法访问,返回当前下标下面的值
cout << vec[2] << endl; //和at(2)一样效果 不能&vec[2]没有重载&
//修改某个元素,通过下标来修改
vec.emplace(vec.begin() + 2, 100); /*在指定位置插入一个新的元素,用迭代器去指位置,传入
的是位置,往第2个位置插入100 */
vec.emplace(vec.begin(), 1111); //往第一个位置插入元素
printVector(vec);
vec.emplace_back(999); //和 push_back 一样的功能
//vec.erase(vec.begin() + 2); //数组只有伪删除,没有删除操作(万金油函数,链表中有用)
printVector(vec);
//批量复制 想把一个数组的内容拷贝到一个向量中去--->用迭代器去指定位置
int array[] = { 1,2,3 }; //一个普通数组赋值为1,2,3
vector<int> vecData; //不需要起始长度--->构建的是对象,会自动释放
vecData.assign(array, array + 3); /*赋值的方式,把数组中的东西赋值给另一个数组(把元素的
第一个位置到最后一个位置拷贝到容器中)*/
printVector(vecData);
return 0;
}
3.list
- 包含头文件 <list>
双向链表可以头插和尾插。
基本操作如下:
int main()
{
list<int> iNum;
list<string> strNum;
//插入
strNum.push_back("string1"); //尾插发
strNum.push_back("string2");
strNum.push_front("string3");
//string3 string1 string2
//遍历
//不删除方式遍历
list<string>::iterator iter;
for (iter = strNum.begin(); iter != strNum.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
cout << "是否为空:" << boolalpha << !strNum.empty() << endl;
cout << "元素个数:" << strNum.size() << endl;
//删除方式遍历
//string3 string1 string2
while (!strNum.empty())
{
cout << strNum.front() << " "; //back()
strNum.pop_front(); //头部删除 pop_front();
}
cout << endl;
cout << "元素个数:" << strNum.size() << endl;
//指定位置操作
//iterator find(iterator begin,iterator end,data);
for (int i = 0; i < 3; i++)
{
iNum.push_back(i);
}
auto result = find(iNum.begin(), iNum.end(), 2);//find返回的是迭代器
//没找到返回是end结束的位置
if (result == iNum.end())
{
cout << "未找到指定位置" << endl;
}
//insert:在指定的迭代器前插入,返回指向插入数据的迭代器
iNum.insert(result, 100);
//删除函数:使作为参数的迭代器失效,并返回指向该迭代器下一参数的迭代器
iNum.erase(result);
//其他操作
iNum.reverse(); //反转链表
iNum.sort(less<int>()); //排序
return 0;
}
其中要注意的是erase函数,如果用它做删除,应该这么写:
int main()
{
int array[4] = { 1,2,2,3 };
list<int>data;
data.assign(array, array + 4); //初始化list---把数据拷贝到data中,把2全部删除
//相同元素的删除
for (list<int>::iterator iter = data.begin(); iter != data.end(); )
{
if (*iter == 2)
{
iter = data.erase(iter); //函数返回下一个迭代器
}
else
{
iter++;
}
}
return 0;
}
4.stack
- 包含头文件 <stack>
使用方式很简单,先进后出,可以用在游戏数据保存,实现回退功能。下面是常用的方法:
int main()
{
stack<int> intStack;
for (int i = 0; i < 3; i++)
{
intStack.push(i);//入栈
}
while (!intStack.empty())
{
cout << intStack.top() << "\t";
intStack.pop();//出栈
}
return 0;
}
5.queue
- 包含头文件 <queue>
先进先出,跟栈相反。
int main()
{
queue<int> qData;
for (int i = 0; i < 3; i++)
{
qData.push(i); //入队
}
while (!qData.empty())
{
cout << qData.front() << " "; //获取队头元素
qData.pop(); //出队
}
cout << endl;
cout << qData.size() << endl;
return 0;
}
6.deque
- 包含头文件 <deque>
双向队列,头尾能进能出
//从头出来
void pop_front_dequeue(deque<int> dData)
{
while (!dData.empty())
{
cout << dData.front() << " ";
dData.pop_front();
}
cout << endl;
}
//从尾出来
void pop_back_dequeue(deque<int> dData)
{
while (!dData.empty())
{
cout << dData.back() << " ";
dData.pop_back();
}
cout << endl;
}
int main()
{
deque<int> deData;
for (int i = 0; i < 3; i++)
{
deData.push_back(i); //尾插法入队
deData.push_front(i); //头插法入队
}
deData.push_back(999);
//0 0
//1 0 0 1
//2 1 0 0 1 2
pop_front_dequeue(deData);
pop_back_dequeue(deData);
return 0;
}
7.priority_queue
- 包含头文件 <queue>
优先队列,有自动排序,默认大的先出队
//默认的方式,一级下面三种 都一样,大的先出队
priority_queue<int> pqData;
priority_queue<int, vector<int>> pqData2; //默认排序准则
priority_queue<int, vector<int>, less<int>> pqData3; //所有参数都完整
//优先队列,是按照数据的优先权出队,VIP服务
pqData.push(12);
pqData.push(0);
pqData.push(34);
while (!pqData.empty())
{
cout << pqData.top() << " ";
pqData.pop(); //出队
}
cout << endl;
//贪心算法
priority_queue<int, vector<int>, greater<int>> pqDataG;
pqDataG.push(12);
pqDataG.push(0);
pqDataG.push(34);
while (!pqDataG.empty())
{
cout << pqDataG.top() << " ";
pqDataG.pop(); //出队
}
cout << endl;
8.set/multiset/bitset
- 包含头文件 <set>/<bitset>
- set集合:1.数据自带排序;2.数据具有唯一性
- multiset:只有排序,没有数据唯一性
- bitset:二进制位集合
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
bool operator<(const MM& object)const
{
return this->name < object.name;
}
void print()
{
cout << name << " " << age << endl;
}
protected:
string name;
int age;
};
void testSet()
{
srand((unsigned int)time(nullptr));
set<int> setData; //默认方式 从小到大
set<int, less<int>> setData2; //和默认方式一样
set<int, greater<int>> setData3; //从大到小
int array[10] = { 0 };
for (int i = 0; i < 10; i++)
{
int temp = rand() % 10;
array[i] = temp;
setData.insert(temp);
}
for (auto v : array)
{
cout << v << " ";
}
cout << endl;
for (set<int>::iterator iter = setData.begin(); iter != setData.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
void testUserData()
{
set<MM> mmData; //less<int> <
mmData.insert(MM("name3", 19));
mmData.insert(MM("name2", 28));
mmData.insert(MM("name3", 188));
for (auto v : mmData)
{
v.print();
}
set<char> cData;
set<string> strData;
}
//多重集合: 只具有排序功能,不具有去重功能
void testmultiset()
{
multiset<int> mulData;
for (int i = 0; i < 10; i++)
{
mulData.insert(rand() % 10);
//rand()%26+'A';
//rand()%26+'a';
}
for (auto v : mulData)
{
cout << v << " ";
}
cout << endl;
}
int main()
{
testSet();
testUserData();
testmultiset();
return 0;
}
bitset的使用比较有有趣:尖括号的参数是位数
int main()
{
//多个二进制位
bitset<8> bData("11110000");
cout << bData << endl;
bData.flip();
cout << bData << endl;
cout << bData.all() << endl;
cout << bData.any() << endl;
cout << bData.size() << endl;
cout << bData.none() << endl;
bitset<8> num(7);
cout << num << endl;
return 0;
}
9. map/multimap
- 包含头文件 <map>
- map储存的是数对类型的数据
- map自带排序,默认是从小到大,也具有数据唯一性
- 可以用map映射进行资源的综合管理,增强代码可读性
- 第一个参数对map有决定性作用,比较时通过它来进行
- map[first]=second;
int main()
{
map<int, string> mapData;
map<int, string, less<int>> mapData1; //和上面创建方式一样,从小到大
map<int, string, greater<int>> mapData2; //从大到小
//1.insert插入
mapData.insert(pair<int, string>(1, "string"));
//2.make_pair构建数对插入
mapData.insert(make_pair<int, string>(3, "string3"));
//3.单映射,可以直接采用数组下标方式进行插入
//数组在一定程度上来说可以看作映射
//map[first]=second;
//相比数组来说,这个下标是没有任何要求
mapData[-1] = string("string-1"); //等效插入一个数对类型
//上面代码等效:mapData.insert(pair<int,string>(-1,"string-1"))
mapData[1] = "string1"; //相同键 采用的是覆盖方式
//遍历:
for (auto iter = mapData.begin(); iter != mapData.end(); iter++)
{
//*iter指的是pair类型
cout << iter->first << " " << iter->second << endl;
}
for (auto v : mapData)
{
cout << v.first << "\t" << v.second << endl;
}
//map<string, IMAGE*> img;
//img["墙"] = new IMAGE;
//img["路"] = new IMAGE;
//putimage(0, 0, img["墙"]);
//putimage(0, 0, img["路"]);
cout << mapData[1] << endl; //用的时候直接使用即可
mapData.erase(1);
for (auto v : mapData)
{
cout << v.first << "\t" << v.second << endl;
}
cout << endl;
return 0;
}
多重映射没什么限制,什么样的关系都能插入到映射之中,因为可能存在相同的first参数,所以不能采用下标法进行访问。
int main()
{
multimap<int, string> mulData;
mulData.insert(pair<int, string>(1, "string"));
mulData.insert(pair<int, string>(1, "string1"));
mulData.insert(pair<int, string>(2, "string"));
mulData.insert(pair<int, string>(3, "string"));
mulData.insert(make_pair<int, string>(3, "string"));
for (auto v : mulData)
{
cout << v.first << "\t" << v.second << endl;
}
return 0;
}
10.initializer_list
- 包含头文件 <initializer_list>
- 列表,可无限增长,所以多用为函数参数,这样可以做到无限制的参数个数。
void print(initializer_list<int> list)
{
for (auto iter = list.begin(); iter != list.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main()
{
array<int, 3> arrData = { 1,2,3 };
vector<int> vecData1 = { 1,2,3,4 };
vector<int> vecData2 = { 1,2,3,4,5 };
initializer_list<int> listOne = { 1,2,3,4,5 };
initializer_list<int> listTwo = { 1,2,3 };
print({ 1 });
print({ 1,2 });
print({ 1,2,3,4 });
print({ 1,2,3,4,5 });
return 0;
}
11.tuple
- 包含头文件 <tuple>
- 可以把它当成结构体去用,很方便
- 访问方式比较特殊,不能用for循环,用get方法,或者是tie访问数据储存后,再用流的形式打印出来。
void testTuple()
{
//把任何类型的一系列数据当做一组处理
tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
tuple<string, string> value = forward_as_tuple("小张", "小美");
tuple<string, int, int, string> array[3];
}
void visitedData()
{
tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
//get方法,不能用for循环
cout << get<0>(mmInfo) << "\t";
cout << get<1>(mmInfo) << "\t";
cout << get<2>(mmInfo) << "\t";
cout << get<3>(mmInfo) << endl;
//tie的方式访问数据
string name;
int age;
int num;
string tel;
tie(name, age, num, tel) = mmInfo;
cout << name << "\t" << age << "\t" << num << "\t" << tel << endl;
}
void Exoperator()
{
tuple<string, int, int, string> mmInfo = { "MM",18,1001,"123445" };
tuple<double, double, double> mmscore = make_tuple(98, 98, 88);
//合并两个元组
auto result = tuple_cat(mmInfo, mmscore);
}
int main()
{
visitedData();
return 0;
}
二.迭代器汇总
1.容器中迭代器分类
容器名 | 迭代器类型 |
---|---|
array | 随机访问 |
vector | 随机访问 |
deque | 随机访问 |
stack/queue/priority_queue(特定顺序存取) | 不支持 |
list | 双向 |
set/multiset | 双向 |
map/multimap | 双向 |
2.拓展内容
-
迭代器辅助函数
- 移动:advance(iterator iter,n);
- 间距:distance(iterator begin,iterator end);
- 交换:iter_swap(iterato first,iterator end);
-
拓展内容:特殊迭代器 流型迭代器-->一般用在辅助打印
- 输出流型
- ostream_iterator<_Ty> iter(ostream& out);
- ostream_iterator<_Ty> iter(ostream& out,char* str);
- 输出流型迭代做赋值运算,意味着就是打印数据到屏幕上
- 输入流型
- istream_iterator<_Ty> iter; //构造无参对象,是一个错误流 end_of_ostream
- istream_iterator<_Ty> iter(istream& in);
- *iter 等效cin>>操作
- 输出流型