CPP(c++) 智能指针

智能指针

参考:https://blog.csdn.net/flowing_wind/article/details/81301001

我们知道除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆。程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们。
在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
动态内存管理经常会出现两种问题:一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。
为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的两种智能指针的区别在于管理底层指针的方法不同,
shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。


常用智能指针:shared_ptr,unique_ptr和weak_ptr
shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。

shared_ptr<int> p3 = make_shared<int>(42);
shared_ptr<string> p4 = make_shared<string>(10,'9');
shared_ptr<int> p5 = make_shared<int>();

//可以通过调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique 
//将所有权从p1(指向string Stegosaurus)转移给p2
unique_ptr<string> p2(p1.release());//release将p1置为空
unique_ptr<string> p3(new string("Trex"));
//将所有权从p3转移到p2
p2.reset(p3.release());//reset释放了p2原来指向的内存

shared_ptr和unique_ptr用法:

shared_ptr<T> sp;  //空指针,可以执行类型为T 的对象
unique_ptr<T> up;    
p                  //p看做一个条件判断,若p指向一个对象,则为true
*p                 //获取它指向的对象
p->mem             //等价于(*p).mem
p.get()            //返回P中保存的指针。
swap(p,q)          //交换p和q中的指针
p.swap(q)   

share_ptr:

make_share<T> (args)  //返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化。
share_ptr<T> p(q)     //p是shared_ptr q的拷贝,此时会增加q中计数器,q中的指针必须转换为T*;
p = q                 //p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,
                      //递增q的引用计数。若p的引用计数为0,则将其管理的内存释放。
p.unique()            //若p.use_count()为1,则返回 true;否则返回 false
p.use_count()         //返回与p共享对象的智能指针数量;可能很慢,主要用于调试。

unique_ptr: 

//由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。

unique_ptr<T> u1      // 空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;
unique_ptr<T,D> u2    // u2会使用一个类型为D的可调用对象来释放它的指针。 
unique_ptr<T,D> u(d)  // 空unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete
u=nullptr             // 释放u指向的对象,将u置为空
u.release()           // u放弃对指针的控制,将u置为空

u.reset()             //释放u指向的对象
u.reset(q)            //如果提供了指针q,将u指向这个对象,否则将u置为空。
u.reset(nullptr)

weak_ptr:

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。

weak_ptr<T> W   // 空weak_ptr  可以指向为T的对象
weak_ptr<T> W(sp) //与shared_ptr  sp 指向相同对象 weak_ptr

w = p        // P可以是share_ptr  或weak_ptr
w.reset()    //w 为空
w.use_count  //与w共享对象的shared_ptr竖向
w.expired()  //w.use_count() 为0 ,返回true,否则返回为false
w.lock()     //如果expired 为true,则返回一个空shared_ptr,否则返回一个指向w对象的shared_ptr

weak_ptr  举例

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

class Test{
public:
  Test(int d = 0) : data(d){cout << "new" << data << endl;}
  ~Test(){cout << "del" << data << endl;}
  void func(){cout << "func" << endl;}
private:
  int data;
};

class teacher{
public:
  teacher(){cout << "teacher()" << endl;}
  ~teacher(){cout << "del teacher" << endl;}
  shared_ptr<student> stu;
};
class student{
public:
  student(){cout << "student()" << endl;}
  ~student(){cout << "del student" << endl;}
  //如果换成shared_ptr<teacher> tea;就会形成循环引用,导致内存泄漏    
  weak_ptr<teacher> tea;
};
int main(){                          
  //用weak_ptr解决了share_ptr循环引用,导致的内存不能释放的问题                          
  shared_ptr<teacher> tptr(new teacher);//计数器1                               
  shared_ptr<student> sptr(new student);//计数器1                               
  tptr->stu = sptr;//sptr的计数器2                                              
  sptr->tea = tptr;//不增加tptr的引用计数,因为tea是weak指针,如果tea是shared_ptr容易导致循环引用                    
  cout << tptr.use_count() << endl;//1                                          
  cout << sptr.use_count() << endl;//2                                          
  return 0;
}

 


 

std::move

类型转换static_cast<typename remove_reference<T>::type &&>(t);
std::move语句可以将左值变为右值而避免拷贝构造。将对象的状态或者所有权从一个对象转移到另一个对象。
左值:放在等号左边的值,可以被赋值: a; ++a; *a;**a;a.m;a->m;a[m]
右值:放在等号右边的值,不能被赋值;a+b; a++; a&&b; a<b; &a

深拷贝:A先申请出一片新的空间,完全复制B的内容到新空间中;
浅拷贝:A复制B指针,将自己的指针指向B的内容地址,A、B公用一块内存地址;
A = std::move(B); A 对 B 实现浅层拷贝,为了防止B的改变影响到A ,对B清除。
参考:https://www.lanindex.com/stdmove%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%88%86%E6%9E%90/

举例:

#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //调用常规的拷贝构造函数,新建字符数组,拷贝数据
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n";//str is "hello"
    //调用移动构造函数,掏空str,掏空后,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";//str is ""
    std::cout << "The contents of the vector are \"" << v[0]
                                         << "\", \"" << v[1] << "\"\n";
}
posted @ 2020-01-02 16:31  heimazaifei  阅读(1431)  评论(0编辑  收藏  举报