C++ 智能指针

C++ 智能指针,第1张

C++ 智能指针

        智能指针是为了解决内存泄漏问题,与普通指针相比,他能够自动释放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管理的对象。
弊端

        弊端不是错误,是规则设计不合理。虽然auto_prt能解决自动释放内存的问题,但是又引入了新问题,导致使用的时候反而要更小心,所以在17版就被废止。如下示例:

auto_ptr  p2=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)< 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5691756.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存