C++智能指针

1. 智能指针作用

智能指针(类模板):问作用就是防止内存泄露
引入头文件#include <memory>就可以使用啦
常用的四个智能指针为:auto_ptr、shared_ptr、weak_ptr、unique_ptr
  • 有new没有delete,一定会造成内存泄露;一个new只能delete一次(空指针除外)
  • std::auto_ptr:C++98提出,允许赋值,赋值给另一个auto_ptr时原来的指针变为空,不能访问(访问会崩溃),目前被unique_ptr取代(不允许)
  • std::shared_ptr:共享指针(共享所有权),多个指针指向同一个对象(引用计数),最后一个被销毁,对象才被释放(多个指针指向同一个对象)
  • std::weak_ptr:辅助shared_ptr工作,“弱”指针(弱共享、弱引用),绑定shared_ptr不改变强引用计数,使用弱引用次数
  • std::unique_ptr:独占式指针,同一时间只有一个指针指向一个对象,所有权可以移交

2. new和delete

问作用就是动态申请和释放内存
  • new:动态分配内存;delete:释放内存;成对使用;delete后的指针变成了悬空指针,不能操作了,但是仍然保存指向原来动态内存的地址,建议加上=pullptr。
int *p = new int(100);
delete p;
p = nullptr;

int *ptr = new int[100];      // 数组
delete []ptr;
ptr = nullptr;

3. shared_ptr

  • 初始化
### 普通指针
int *pi = new int(100);              // 正确,普通指针
shared_ptr<int> pi = new int(100);   // 错误,智能智能是explicit,不能隐式转换

### 智能指针   // 区别*
shared_ptr<int> pi(new int(100));   // pi指向一个值为100的int型数据
shared_ptr<int> pi = std::make_shared<int>(100) // 使用make_shared<>返回shared_ptr
  • 引用计数
auto p1 = std::make_shared<int>(100);  // 引用为1
auto p2(p1);           // 智能指针复制,引用计数为2 
void func(shared_ptr<int> tmp) {     // 形参值传递,离开作用域后引用计数不变
  return ;
}

void func(shared_ptr<int>& tmp) {     // 形参引用传递,离开作用域后引用计数不变
  return ;
}

func(p2);           // 没有接收返回值,引用计数为2
auto p3 = func(p2);        // p3接收返回值,引用计数为3

p3 = std::make_shared_ptr<int>(200); // p3重新指向,p3引用计数为1,p1、p2的引用计数为2
p2 = std::make_shared_ptr<int>(200); // p2重新指向,p1、p2的引用计数都为1
p2 = std::make_shared_ptr<int>(100); // p1重新指向,p1原来指向的内存被释放,引用计数为1


auto p4 = std::make_shared_ptr<int>(200); 
auto p5 = std::make_shared_ptr<int>(200);
p4 = p5;      // p4指向p5,p4原来的内存释放,p5引用计数为2
  • 常用操作
#1 use_count() 引用计数的个数
auto p = std::make_shared_ptr<int>(200);
count = p.use_count();       // count值为1

# unique() 判断是否独占指针
shared_ptr<int> myp(new int (100));
if(myp.unique()) {}           // true
auto myp1(myp);               // 引用计数为2了
if(myp.unique()) {}           // false

# reset 不带参数
shared_ptr<int> myp(new int (100));
myp.reset();         // 释放myp指向的对象,并置为空

shared_ptr<int> myp(new int (100));
auto myp1(myp);      // myp1引用计数为2
myp.reset();         // 释放myp,myp1引用计数变为1

# reset 带参数
shared_ptr<int> myp(new int (100));
auto myp1(myp);      // myp1引用计数为2
myp.reset(new int(1));         // 释放myp,重新指向1,myp和myp1引用计数都变为1

# *解引用,获得指针指向的对象
shared_ptr<int> pother(int new(100))
*pother // 值为100

# get() 返回保存的指针(内置的裸指针)
shared_ptr<int> myp(new int(100));
int *p = myp.get();
*p = 45;

# swap() 交换两个智能指针所指向的对象,引用计数不发生变化
shared_ptr<string> ps1(new string("I love China1!"));
shared_ptr<string> ps1(new string("I love China2!"));
std::swap(ps1, ps2);    // 使用1
ps1.swap(ps2);          // 使用2

# 智能指针的名字作为判断条件
shared_ptr<string> ps1(new string("I love China!"));
if(ps1) {}     // 条件成立

# 指定删除器和数组问题,自己定义delete函数    ### 可以通过模板来封装指定删除器数组
void myDeleter(int *p) {
  delete p;
}
shared_ptr<int> p(new int(100), myDeleter);  // 自定义删除器
shared_ptr<int> p(new int(100), [](int *p){  // 自定义删除器,lambda表达式实现
  delete p;
});

shared_ptr<A> pA(new A[10]); // 报错,数组需要delete[] pA
shared_ptr<A> pA(new A[10], [](int *p){
    delete []p;              // 正确处理    
})
    
# 提倡使用make_shared来生成shared_ptr,但是这种方式无法自己指定删除器
  • 使用陷阱
#1 慎用裸指针,也不要用裸指针初始化多个shared_ptr
int *p = new int(100)
proc(shared_ptr<int>(p))
*p = 45 // 出错

#2 慎用get返回的裸指针

#3 使用enable_shared_from_this(类模板)返回this

#4 避免循环引用
#include <iostream>
#include <string>
#include <cstdlib>
#include <memory>


class Test
{
private:
  /* data */
public:
  Test() {
    std::cout << "gou zao" << std::endl;
  }
  Test *Print1() {
    std::cout << "print 1" << std::endl;
    return this;
  }
  void Print2() {
    std::cout << "print 2" << std::endl;
  }
  ~Test() {
    std::cout << "xi gou" << std::endl;
  }
};


class Testa;
class Testb;
class Testa
{
public:
  // std::shared_ptr<Testb> m_pbs;
  std::weak_ptr<Testb> m_pbs;
  Testa() {
    std::cout << "执行Testa 构造" << std::endl;
  }
  ~Testa(){
    std::cout << "执行Testa 析构" << std::endl;    
  }
};

class Testb
{
public:
  std::shared_ptr<Testa> m_pas;
  // std::weak_ptr<Testa> m_pas;
  Testb() {
    std::cout << "执行Testa 构造" << std::endl;
  }
  ~Testb(){
    std::cout << "执行Testa 析构" << std::endl;    
  }
};


int main() {
  std::cout << "hello world!!!" << std::endl;

  Test *a = new Test();
  a->Print1()->Print2();
  delete a;
  a = nullptr;
  
  // test shared_ptr
  std::shared_ptr<Testa> pca(new Testa);
  std::shared_ptr<Testb> pcb(new Testb);
  pca->m_pbs = pcb;
  pcb->m_pas = pca;
  return 0;
}
  • 运行结果
    在这里插入图片描述

4. weak_ptr

  • 初始化
auto pi = make_shared<int>(100);  // pi为strong ref,强引用
weak_ptr<int> piw(pi);  // piw弱引用pi,pi的引用计数不变,piw弱引用计数为1,两者指向同一位置
weak_ptr<int> piw1;
piw1 = piw;             // 复制弱引用,pi有一个强引用和两个弱引用


# lock() 检查weak_ptr指向的对象是否存在,存在返回共享对象的shared_ptr(强引用计数加1),否则返回空的shared_ptr
auto piw2 = piw.lock();  // 强引用计数加1,string ref = 2, weak ref = 2
  • 常用操作
# use_count() 获取与该弱指针共享对象的其他shared_ptr,即强引用计数的大小
auto pi = make_shared<int>(100);
auto pi1(pi);
weak_ptr<int> piw(pi);
int count = piw.use_count();      // 获取强次数,大小为2

# expired() 判断是否过期(观测对象是否释放),use_count为0返回true,否则为false
pi.reset();
pi1.reset();
if(piw.expired()) {}         // true成立

# reset() 将弱引用指针置为空,弱引用次数减1,强引用次数不变
auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi);
piw.reset()   // pi是一个强引用,没有弱引用
    
# lock() 检查weak_ptr指向的对象是否存在
  • 尺寸问题
# weak_ptr和shared_ptr指针对象的大小是相同,是弱指针的两倍(包含指向对象的指针和指向控制块的指针(引用计数、自定义删除器等))
shared_ptr<int> pi(new int (100));
weak_ptr<int> piw(pi);
int len1 = sizeof(pi);    // 值为8
int len2 = sizeof(piw);   // 值为8

5. unique_ptr

  • 初始化
# 常规初始化,和new配合
unique_ptr<int> pi;   // 空指针
unique_ptr<int> pi(new int(100));  // 指向对象值为100

# make_unique函数(C++11没有,C++14开始引入),优先选择,具有更高的性能,不支持自定义删除器
unique_ptr<int> p1 = std::make_unique<int>(100);
auto p2 = std::make_unique<int>(200);
  • 重用操作
# 不支持复制,赋值操作
unique_ptr<string> ps1(new string("I love China!"));
unique_ptr<string> ps2(ps1);   // false
unique_ptr<string> ps3 = ps1;  // false
unique_ptr<string> ps4;
ps4 = ps1   // false 
    
# 移动语义 支持移动
unique_ptr<int> pi(new int(100));
unique_ptr<int> pi1 = std::move(pi); // 转移后pi为空了,pi1指向原来pi所指

# release() 放弃对指针的控制权,返回空的裸指针
unique_ptr<int> pi(new int(100));
unique_ptr<int> pi1(pi.release());
pi1.release()  // false,会导致内存泄露
string *tmp = ps1.release();  // true,或者auto temp = ps1.release() 
delete tmp;

# reset() 不带参数释放指针对象,并置为空;带参数重新指向新内存
# = nullptr 释放指针对象,并置为空
# 指向一个数组
unique_ptr<int[]> pi(new int[10])
pi[0] = 1;
pi[1] = 2;

# get() 返回智能指针中保存的对象(裸指针)
# *解应用
# swap() 交换指针所指向的对象
# 名字可用于判断
# 转成shared_ptr,如果unique_ptr为右值,可以赋值给shared_ptr,调用了shared_ptr的显示构造函数,接管unique_ptr所拥有的对象
unique_ptr<int> pi(new int(100));
shared_ptr<int> pi1 = std::move(pi);   // 执行后pi为空,pi1为shared_ptr且引用次数为1
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自动驾驶小哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值