一、容器元素深浅拷贝
STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是说当我们给容器中插入元素的时候,容器内部实施了拷贝动作,将我们要插入的元素再另行拷贝一份放入到容器中,而不是将原数据元素直接放进容器中,也就是说我们提供的元素必须能够被拷贝。
class Maker
{
public:
Maker(const char * name, int age){
cout << "构造函数" << endl;
this->pName = new char[strlen(name)+1];
strcpy(this->pName,name);
this->age = age;
}
Maker(const Maker &m){
cout << "拷贝构造函数" << endl;
// 在堆区分配了strlen(m.pName)+1字节内存
this->pName = new char[strlen(m.pName)+1];
strcpy(this->pName,m.pName);
this->age = m.age;
}
Maker& operator=(const Maker& m) {
cout << "赋值运算" << endl;
if(this->pName != NULL) {
delete[] this->pName;
this->pName = NULL;
}
// 在堆区分配了strlen(m.pName)+1字节内存
this->pName = new char[strlen(m.pName)+1];
strcpy(this->pName,m.pName);
this->age = m.age;
return *this;
}
~Maker() {
cout << "析构函数" << endl;
if(this->pName != NULL){
delete[] this->pName;
this->pName = NULL;
}
}
public:
char * pName;
int age;
};
void test01() {
vector<Maker> v;
// 是将Maker("Emily",18)拷贝一份放到v中
// 1. 拷贝构造要能调用
// 2. 注意浅拷贝问题
v.push_back(Maker("Emily",18));
}
二、STL容器使用时机
- vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。
-
deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
vector与deque的比较:
(1):vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置 却是不固定的。
(2):如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。
(3):deque支持头部的快速插入与快速移除,这是deque的优点。 - list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
- set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
- map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。
三、空间配置器
容器通过空间配置器取得数据存储空间,空间配置器管理容器的空间
在程序中动态申请,释放空间,存在的问题
1.出现内存碎片问题 2. 一直在因为小块内存而进行内存申请,调用malloc,系统调用产生性能问题空间配置器 策略:
如果申请的内存大小超过128,那么空间配置器就自动调用一级空间配置器。反之调用二级空间配置器。
一级空间配置器,STL源码中的一级空间配置器命名为class __malloc_alloc_template ,它很简单,就是对malloc,free,realloc等系统分配函数的一层封装。
二级空间配置器,由一个内存池和自由链表配合实现的。