智能指针是为了解决内存泄漏问题,与普通指针相比,他能够自动释放malloc或者new的内存的空间。智能指针本质上的实现是函数连带自动释放,先来看段代码:
class smartp
{
public:
smartp(T size)
{
p=new T(size);
}
~smartp()
{
if(p !=NULL) delete[] p;
}
private:
T *p;
};
int main(void)
{
{
smartp a(5); //大括号结束时释放a对象
}
while(1);
}
其实就是利用了函数执行结束时会自动释放栈上局部变量内存,而我们将对象定义在了栈上(在函数内部定义的局部对象),所以函数结束时不例外的会把对象释放,但是不会管对象在堆上申请空间,在销毁对象时会调用对象的析构函数,利用这个特点,我们把delete堆内存放在析构函数中,则会被自动调用,从而实现自动delete堆内存的目的。
智能指针auto_prt
auto_prt已过时,在c++17版弃用,因此在新开发程序中不要使用,但是对我们学习智能指针的使用还是有帮助。auto_ptr是一个类模板,在使用的时候需要指定类型。
头文件:
构造函数
auto_ptr p1(new people("hu")); //创建智能指针并指向新建的对象;
auto_ptr p2=p1; //P1转移到P2,再访问P1会崩溃。
观察器
get() people *p3 = p2.get(); //获得智能指针p2管理的对象的指针。
operator-> cout<name; //通过→访问管理的对象内的元素
operator* cout<<(*p3).name; //通过*访问管理的对象内的元素,( )优先级。
修改器
reset() p2.reset(new people("dai")); //替换,创建新对象,释放原对象,绑定新对象。
release() people *p4 = p2.release(); //释放管理,释放后,不能再访问p2管理的对象。
弊端
get() people *p3 = p2.get(); //获得智能指针p2管理的对象的指针。 operator-> cout<name; //通过→访问管理的对象内的元素 operator* cout<<(*p3).name; //通过*访问管理的对象内的元素,( )优先级。
修改器
reset() p2.reset(new people("dai")); //替换,创建新对象,释放原对象,绑定新对象。
release() people *p4 = p2.release(); //释放管理,释放后,不能再访问p2管理的对象。
弊端
弊端不是错误,是规则设计不合理。虽然auto_prt能解决自动释放内存的问题,但是又引入了新问题,导致使用的时候反而要更小心,所以在17版就被废止。如下示例:
auto_ptrp2=p1;
所有权转移,P1所有权转移P2后,如果再操作P1,就会出错。
func(p1);
函数调用传值转移所有权,函数传参时,值传递,导致智能指针所有权被转移。
p2.release();
没有接收返回值,对象指针就会永久丢失,泄漏内存。
unique_ptr
由于auot_ptr存在弊端被遗弃,所以c++又发明的新的智能指针来替代, unique_ptr就是其中之一,与auto_ptr不同的是,它不允许使用赋值语句进行权限转移(包括函数传参、返回值),在unique_ptr对象被销毁时,会先去调用与其绑定的删除器(一个对象),该删除器由用户给入,并要在内部实现构造函数、拷贝构造函数、移动构造函数和最重要的operator()(people *p)运算符重载,在销毁前会将管理的对象的指针传入到删除器的operator(),在这里要去删除申请的空间,同时调用管理的对象的析构。典型的删除器如下:
struct Dlete
{
Dlete(){cout<<"Dlete()"<
头文件:
构造函数
unique_ptr a;
unique_ptr a(nullptr);
unique_ptr a(new people,del); //默认构造people对象
unique_ptr a(new people,del); //引用del
unique_ptr a(new people,Dlete()); //创建再移动
unique_ptr a(new people,move(del)); //转移
unique_ptr a(new people[5]); //调用5次构造和5次析构
修改器
release() people *ptr = aa.release(); //解绑aa管理的对象,并返回对象指针
reset() bb.reset(new people(6)); //替换bb管理的对象
swap () dd.swap(cc); //交换智能指针dd与cc管理的对象
观察器
get() people *p =dd.get(); //获取dd管理的对象
get_deleter() Dlete &pd = ee.get_deleter(); //获取删除器
operator bool if(ee) {cout<<"y"<
unique_prt与auto_prt对比
- auto_prt允许两个对象用赋值语句赋值(a=b), 而unique_prt不允许,包括函数传参和return返回值,因为涉及管理权限转移,容易出错,unique_ptr要直接赋值必须使用a= move(b)。
- 都支持匿名对象赋值auto_ptr
aa = auto_ptr(new int),也包括函数传参和return返回值。
unique释义为独一无二的,强调的就是他只能管理一个对象,当他管理一个新对象时,原来的对象就会被释放。同样也不能多个智能指针同时管理一个对象。会导致在释放的时候出错。
当unique_ptr本身被释放时,他会调用“删除器”,在删除器中去调用被管理的对象的析构,然后在析构自己。
在使用智能指针的时候,尽量不要与普通指针混合使用,因为c++语法并没有规避两个智能指针去管理两个普通指针。如下列:
int *p(new int);
unique_ptr kk(p);
unique_ptr yy(p);
shared_ptr
shared_ptr共享指针,意思是同一个对象可以有多个智能指针管理,auto_ptr和unique_ptr是独占式智能指针,语法上只能一个对象一个智能指针,即任何时候对象与智能指针一对一对应。而shared_ptr是基于引用计数式的设计,因此可以多个智能指针绑定1个对象,使得更加接近普通指针,shared的基本使用与前面两个智能指针类似。同样可以使用构造函数初始化,也可以使用make_shared来创建。
shared_ptr aa(new people);
auto bb = make_shared(10);
引用计数
shared_ptr是引用计数来记录同一个对象被几个智能指针所管理,因为内部有计数机制,只有最后一个智能指针被释放时才会去析构管理的对象,所以最后在析构的时候才能避免重复去析构而出错,为了避免重复释放出错的问题,auto_ptr会对管理权限进行转移,来保证析构不出错,而unique_ptr是干脆就不允许左值赋值语句。
shared_ptr aa(new people);
shared_ptr bb = aa;
use_count() int cnt = bb.use_count(); //获取bb所管理的对象有几个智能指针。
unique() if(aa.unique()); //判断aa是否独享对象
reset() aa.reset(new people) //对aa指向的对象更新,bb指针计数值减1;
weak_ptr
weak_ptr是一个非独立的智能指针,用来给shared_ptr打辅助的,shared_ptr算是一个近乎完美的智能指针,但是他还是有一个由计数而引起缺陷,而这个缺陷则需要使用weak_ptr来配合使用来避免。
weak_ptr本身也是一个模板类,但是不能直接用来创建智能指针对象,只能用来接收shared_ptr智能指针对象,且不会引shared_ptr智能指针计数。weak_ptr能使用的成员少得可怜,甚至连operator*和operator->都没有,不同的是他比其他只能指针多了lock和expired。
weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象,然后进行后续操作。
构造函数
shared_ptr sptr(new people(12));
weak_ptr wptr=sptr;
观察器
expired() if(!wptr.expired());//管理的对象被删除返回true,否则false
lock() cout<< wptr.lock()->value<
迭代器适配器
STL中迭代器分类有输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器等,这些迭代器是STL中标准迭代器,很多时候我们遍历容器的场景比已提供的不同。所以就有了迭代器适配器,迭代器适配器也是一个模板类,是基于前面提到的基础迭代器而实现的,仍属于迭代器,可以理解为是“升级版”迭代器。
反向迭代器
反向迭代器(reverse_iterator),又称“逆向迭代器”,其内部重新定义了递增运算符(++)和递减运算符(--),专门用来实现对容器的逆序遍历。
list aa{1,2,3,4,5}; //定义一个list
reverse_iterator::iterator> begin = aa.rbegin(); //读逆序迭代器作为begin
reverse_iterator::iterator> end = aa.rend();//读逆序迭代器作为begin
while(begin != end)
{
cout<<*begin<<" ";
++begin;
}
安插型迭代器
安插型迭代器(inserter或者insert_iterator),用于在容器的任何位置添加新的元素,需要注意的是,此类迭代器不能被运用到元素个数固定的容器(比如 array)上。
流迭代器
流迭代器(istream_iterator / ostream_iterator)流缓冲区迭代器(istreambuf_iterator / ostreambuf_iterator),输入流迭代器用于从文件或者键盘读取数据;相反,输出流迭代器用于将数据输出到文件或者屏幕上。
输入流缓冲区迭代器用于从输入缓冲区中逐个读取数据;输出流缓冲区迭代器用于将数据逐个写入输出流缓冲区。
移动迭代器
移动迭代器(move_iterator),此类型迭代器是 C++ 11 标准中新添加的,可以将某个范围的类对象移动到目标范围,而不需要通过拷贝去移动。
function函数包装器
在c++中有多种可调用的对象,如函数、函数指针、lambda表达式、bind创建的对象、函数对象。对这些东西的调用各有差异,而函数包装器就是来实现将这些东西放在一起,并统一接口后对外预留同样的调用方法。即不同类型的可调用对象共享同一种调用方法。他的底层其实是使用了map来实现,map的格式是map,我们把key用来做运算符标识,value写成函数,即实现了函数的包装,示例如下
int add(int a,int b){return a+b;}; //定义一个普通函数
auto sub = [](int a,int b){return a-b;}; // 定义一个lambda表达式
struct div //函数对象
{
int operator()(int a,int b)
{
return a / b;
}
};
map> bing
{
{"+",add},
{"-",sub},
{"/",div}
{"*",[](int a,int b){return a*b;}},
};
cout<< "2+3= "<< bing["+"](3,2)<



