目录
一、智能指针的实现
1、auto_ptr
3、unique_ptr
4、weak_ptr
二、vector的原理及实现
三、deque的原理及实现
四、list的原理及实现
五、配置器、配接器、迭代器
六、仿函数
C++程序设计中堆内存是一个非常频繁的操作,堆内存的申请和释放都由程序员自己管理,虽然自己管理内存提高了程序的效率,但是整体来说还是比较麻烦的。使用普通指针,忘了释放容易造成内存泄漏,二次释放、程序异常时造成内存泄漏,使用智能指针能更好的解决这个问题。实现原理:RAII(资源分配即初始化)
智能指针的四种类型:1、auto_ptr 2、shared_ptr 3、unique_ptr 4、weak_ptr
(无论什么情况下都不要使用,C++98标准,到了C++11标注引入了shared_ptr,unique_ptr,,weak_ptr)
shared_ptr和auto_ptr最大的区别就是,shared_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的赋值的奇怪问题。所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。
成员函数:
(1)默认构造函数,函数参数为变量地址
(2)拷贝构造函数,函数参数为引用
(3)重载函数operator*
(4)重载函数operator->
(5)重载函数operator=,使之可以进行隐性转换操作,注:实际C++源码中是没有这个的,构造函数用关键字explicit声明,在定义对象时必须显示调用初始化式,不能使用赋值操作符进行隐式转换。
(6)空函数判断
(7)引用次数统计函数
注意:使用shared_ptr也要引用头文件#include
#ifndef _SHARED_PTR_H
#define _SHARED_PTR_H
/*
一个模板T* ptr,指向实际的对象
一个引用次数
重载operator*和operator->,使得能像指针一样使用share_ptr
重载copy constructer,使其引用次数加一
重载operator=,如果原来的shared_ptr已经有对象
*/
template
class shared_ptr
{
public:
shared_ptr(T* p) :count(new int(1)), _ptr(p); //默认构造函数,必须自己显式的开辟内存
shared_ptr(shared_ptr& p) :count(&(++p.count)), _ptr(p._ptr); //拷贝构造函数,属于强制转换,显式
T& operator*(); //
T* operator->();
shared_ptr & operator=(shared_ptr& p); //对等号进行重载,保证保存同为shared_ptr的指针能相互转换,等号
//左边计数器减1,右边计数器加1。
~shared_ptr();
bool empty(); //检查是否指向一个空T
int GetCount();
private:
int* count; //引用计数器
T* _ptr; //每创建一个对象,则有一个指针指向一个shared_ptr类型
};
#endif
#include"_shared_ptr.h"
template
shared_ptr::shared_ptr(T* p = nullptr) :count(new int(1)), _ptr(p) //默认构造函数,必须自己显式的开辟内存
{
if (_ptr)
{
count = new int(1); //如果初始值不为空,则计数器为1
}
else
{
count = new int(0); //当初始值为空时,则计数器为0;
}
}
template
shared_ptr::shared_ptr(shared_ptr& p) : count(&(++p.count)), _ptr(p._ptr) //拷贝构造函数,属于强制转换,显式
{
if (this != p)
{
this->_ptr = p._ptr;
this->count = p.count;
*(this->count)++;
}
}
template
T& shared_ptr::operator*()
{
//assert(this->_ptr == nullptr)
return *(this->_ptr);
}
template
T* shared_ptr::operator->()
{
//assert(this->_ptr == nullptr);
return this->_ptr;
}
template
shared_ptr& shared_ptr::operator=(shared_ptr& p) //对等号进行重载,保证保存同为shared_ptr的指针能
//相互转换,等号左边计数器减1,右边计数器加1。
{
++*(p.count); //等式右边引用次数加一,左边引用次数减一
if (this->_ptr && 0 == --*this->count) //当左边引用次数为零
{
delete count;
delete _ptr;
}
this->count = p.count;
this->_ptr = p._ptr;
return *this;
}
template
shared_ptr::~shared_ptr() //当诶空时,清除
{
if (0 == *(this->count))
{
delete count;
delete _ptr;
}
}
template
bool shared_ptr::empty() //检查是否指向一个空T,当为空时,记得count也为零
{
return _ptr == nullptr;
}
template
int shared_ptr::GetCount()
{
return *count;
}
unique_ptr的构造函数与auto_ptr一样,构造函数采用explicit声明,防止复制/拷贝时不必要的类型转换,在定义对象时必须显示调用初始化式,不能使用赋值操作符进行隐式转换。注:此代码未包含自定义删除器
成员函数:
(1)get函数:获取内部对象的指针,由于已经重载了()方法,因此和直接使用对象是一样的。
(2)release函数:放弃内部对象的所有权,将内部指针置空,此指针需要手动释放。
(3)reset函数:销毁内部对象并接受新的对象的所有权。
(4)默认构造函数,函数参数为变量地址
(5)拷贝构造函数,函数参数为引用
(6)重载函数operator*
(7)重载函数operator->
(8)重载函数operator=
#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H
template
class _unique_ptr
{
public:
_unique_ptr(T* p = nullptr) :_ptr(p); //默认构造函数
_unique_ptr(_unique_ptr& p) :_ptr(p._ptr); //拷贝构造函数
T& operator*();
T* operator->();
_unique_ptr& operator=(_unique_ptr& p); //赋值操作符重载
T* get();
T* release();
void reset(T* p);
private:
T * _ptr;
};
#endif
#include"unique_ptr.h"
template
_unique_ptr::_unique_ptr(T* p)
{
_ptr = p;
}
template
_unique_ptr::_unique_ptr(_unique_ptr& p)
{
_ptr = p.release();
}
template
T& _unique_ptr::operator*()
{
return *(this->_ptr);
}
template
T* _unique_ptr::operator->()
{
return this->_ptr;
}
template
_unique_ptr& _unique_ptr::operator=(_unique_ptr& p)
{
if (p.get() != this->get())
{
delete _ptr;
}
_ptr = p._ptr;
}
template
T* _unique_ptr::get()
{
return this->_ptr;
}
template
T* _unique_ptr::release()
{
T* tmp = _ptr;
delete _ptr;
return tmp;
}
template
void _unique_ptr::reset(T* p)
{
if (p != _ptr)
{
delete _ptr;
}
_ptr = p;
}
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr。 weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。定义在 memory 文件中(非memory.h), 命名空间为 std.
原理:vector模塑出一个动态数组。它本身是“将元素置于动态数组中加以管理”的抽象概念。将元素复制到动态数组中,元素之间存在某种顺序,所以vector是一种有序群集,支持随机存储,它的迭代器是随机存取迭代器。vector的动态增长的三步骤为,开辟新空间,移动数据,销毁旧空间。需要注意的是,所谓的动态增长并不是在原空间之后分配接续的空间,而是另外分配大于原来空间两倍的新空间。
vector的操作函数
1、构造、拷贝和析构函数
vector |
产生一个空的vector |
vector |
产生一个同型vector副本 |
vector |
产生一个大小为n的vector |
vector |
产生一个大小为n,元素值为elem的vector |
vector |
产生一个以区间[begin,end]做元素初值的vector |
c1.vector |
销毁所有元素 |
2、非变动型操作
c.size() | 返回当前元素数量 |
c.empty() | 判断大小是否为零。 |
capacity() | 返回重新分配空间前所能容纳的元素最大数量 |
reverse() | 如果容量不足,扩大 |
c.max_size() | 返回可容纳的元素最大数 |
3、赋值
c1 = c2 | 将c2的全部元素赋值给c1 |
c.assign(n,elem) | 复制n个elem,赋值给c |
c.assign(begin,end) | 将区间[begin,end]内的元素赋值给c |
c1.swap(c2) | 将c1和c2元素互换 |
4、元素存取
c.at(idx) | 返回引索为idx所变的元素 |
c[idx] | 返回引索为idx所变的元素 |
c.front() | 返回第一个元素。不检查第一个元素是否存在 |
c.back() | 返回最后一个元素。不检查最后一个元素是否存在 |
5、迭代器相关函数
c.begin() | 返回一个随机存取迭代器,指向第一个元素 |
c.rbegin() | 返回一个逆向迭代器,指向逆向迭代的第一个元素 |
c.end() | 返回一个随机存取迭代器,指向最后一个元素 |
c.rend() | 返回一个逆向迭代器,指向逆向迭代的最后一个元素 |
6、安插与删除
c.insert(pos,elem) | 在pos中插入一个elem副本,返回新元素位置 |
c.insert(pos,n,elem) | 在pos位置插入n个elem副本,无返回值 |
c.insert(pos,beg,end) | 在pos插入区间[beg;end]内的所有元素的副本,无返回值 |
c.push_back(elem) | 在尾部添加一个elem |
c.pop_back() | 删除尾部的一个元素 |
c.erase(pos) | 删除pos位置的元素,并返回指向下一个元素的位置 |
c.erase(beg,end) | 删除区间[beg,end]所有元素,并指向下一个元素的位置 |
c.resize(num) | 将元素数量改为num |
c.resize(num,elem) | 将元素数量改为num,多出来的新元素都是elem副本 |
c.clear() | 删除所有元素,将容器清空 |
异常处理:
(略)
容器deque和vector非常的相似,也是采用动态数组来管理元素,提供随机存储,并有和vector几乎一模一样的接口。不同的是deque的动态数组头尾都开放,因此能在头尾两端进行快速安插和删除。
list使用一个双向链表来管理元素,list的内部结构和vector或deque截然不同,以下在主要方面与前述二者存在明显区别:
配置器:C++标准库在许多地方采用特殊对象来处理内存配置和寻址,这样的对象称为配置器。配置器体现出一种特定的内存模型,成为一个抽象表征。C++标准程序库定义了一个缺省配置器如下:
namespace std
{
template
class allocator;
}
缺省配置器可以在任何“配置器得以被当做参数使用”的地方担任默认值。缺省配置器会执行内存分配和回收的一般性手法,也就是调用new和delete操作符。配置器说白了就是一个内存分配器,使得像内存共享、垃圾回收、面向对象数据库等特定内存模型,保持一致的接口。
配接器:一般只有一个私有成员变量的类,且其全部成员函数都是对该唯一的成员变量的存、取和修改,则该类即为对该私有成员变量的配接。将一个类的接口转换成另一个类的接口,使原本因接口不兼容而不能合作的类可以一起运作,即配接器用于改变接口。
STL主要提供三种配接器:
(1)应用于仿函数:是所有配接器中数量最为庞大的一个种群,可以配接、配接、在配接,配接操作包括:(1)系结(2)否定(3)组合,以及对一般函数或成员函数的修饰。C++标准这些规定配接器的接口可由
(2)应用于容器:标准程序库提供的queue和stack,其实都不过是一种配接器,是对deque接口的修饰而成就自己的容器风貌,序列式容器set和map是对其内部所维护的平衡二叉树接口改造
(3)应用于迭代器:STL提供应用于迭代器身上的配接器,这些接口为
迭代器:迭代器是一个抽象概念,任何东西,只要器行为类似迭代器,都可以是一个迭代器。迭代器是一种“能够遍历某个序列内所有元素”的对象。它可以透过与一般指针一致的接口来完成自己的工作(侯捷,C++标准程序库)。
仿函数:所谓仿函数,是一个定义了operator()的对象,它不是函数,而是一个类,该类重载了()操作符
仿函数三大优点:
(1)仿函数比一般函数更灵巧,因为它可以拥有状态。
(2)每个仿函数都有其类型。
(3)执行速度上,仿函数通常比函数指针更快。
#include
#include
#include
#include
using namespace std;
class Person
{
public:
string FirstName() const;
string LastName() const;
};
class PersonSortCriterion
{
public:
bool operator() (const Person& p1,const Person& p2) const
{
return p1.LastName() < p2.LastName() ||
(!(p2.LastName() < p1.FirstName()) &&
p1.FirstName() < p2.FirstName());
}
};
int main()
{
set PersonSet;
PersonSet coll;
PersonSet::iterator pos;
for(pos = coll.begin();pos != coll.end();++pos)
{
cout << *pos<< " ";
}
/*
C++11新特性auto和区间迭代
for(auto itor:coll)
{
cout << itor << " ";
}
cout << endl;
*/
cout <