笔记 | C++现代实用教程:智能指针

C++现代实用教程:智能指针

课程地址:https://www.bilibili.com/video/BV18B4y187uL

​ 在C++11中通过引入智能指针的概念,使得C++程序员不需要手动释放内存
​ 智能指针的种类
std::unique_ptr
​ std::shared_ptr
std::weak_ptr

​ 注: std::auto_ptr 已被遗弃

目录

1:智能指针概述
2:独占指针: unique_ptr
3:unique_ptr与函数
4:计数指针: shared_ptr
5:shareed_ptr与函数
6:shared_ptr与unique_ptr的转化
7:weak_ptr: shared_ptr的补充

第一节: 智能指针概述

c++的指针包括两种

  • 原始指针(raw pointer)
  • 智能指针

智能指针是原始指针的封装,其优点是会自动分配内存,不用担心潜在的内存泄露.

并不是所有的指针都可以封装成智能指针,很多时候原始指针要更方便

各种指针中,最常用的是裸指针,其次是unique_ptr和shared_ptr

weak_ptr是shared_ptr的一个补充,应用场景较少

智能指针与Rust的内存安全

智能指针只解决一部分问题,即独占/共享所有权指针的释放、传输.
智能指针没有从根本上解决C++内存安全问题,不加以注意依然会造成内存安全问题

第二节:独占指针: unique_ptr

​ unique_ptr

​ 在任何给定的时刻,只能有一个指针管理内存

当指针超出作用域时,内存将自动释放

该类型指针不可Copy,只可以Move

三种创建方式

  • 通过已有裸指针创建
  • 通过new来创建
  • 通过std::make_unique创建(推荐)

unique_ptr可以通过get()获取地址

unique_ptr实现了->与*

  • 可以通过->调用成员函数
  • 可以通过*调用dereferencing

例子

  • 裸指针与unique_ptr的不同
  • 创建的三种方式
  • get()与->和解引用
//
//  main.cpp
//  c1_unique_pointer
//
//  Created by ONEE on 2023/2/17.
//

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc, const char * argv[]) {
    //stack
//    Cat c1("OK");
//    c1.cat_info();
//    {
//        Cat c1("OK");
//        c1.cat_info();
//    }
    
    
    //heap raw pointer
//    Cat *c_p1=new Cat("yy");
//    int *i_p1=new int(100);
//    cout<<*i_p1<<endl;
//    c_p1->cat_info();
//    {
//
//        int *i_p1=new int(200);
//        Cat *c_p1=new Cat("yy");
//        c_p1->cat_info();
//        delete c_p1;
//        delete i_p1;
//    }
//    cout<<*i_p1<<endl;
//    delete i_p1;//重复delete 会崩溃
//
//    delete c_p1;
//
//    Cat *c_p2=new Cat("yz");
//    unique_ptr<Cat> u_c_p2(c_p2);
//    c_p2->cat_info();
//    u_c_p2->cat_info();
//    c_p2->set_cat_name("aa");
//    u_c_p2->cat_info();
    
//    delete c_p2;
//    c_p2=nullptr;
//    u_c_p2->cat_info();
    
    
//    unique_ptr<Cat> u_c_p3{new Cat("dd")};
//    u_c_p3->cat_info();
//    u_c_p3->set_cat_name("aa");
//    u_c_p3->cat_info();
//
//    //推荐方式
//    unique_ptr<Cat> u_c_p4=make_unique<Cat>();
//    u_c_p4->cat_info();
//    u_c_p4->set_cat_name("bb");
//    u_c_p4->cat_info();
    
    unique_ptr<int> u_i_p3(new int(100));
    unique_ptr<int> u_i_p4=make_unique<int>(200);
    cout<<*u_i_p4<<endl;
    cout<<"int address: "<<u_i_p3.get() << endl;
    
    
    //std::cout << "Hello, World!\n";
    return 0;
}

第三节:unique_ptr与函数调用

  • unique_ptr是不可Copy,只可以Move

  • 在做函数参数或是返回值中一定要注意所有权

  • Passing by value

需要用std::move来转移内存拥有权
如果参数直接传入std::make_unique语句 自动转换为move

  • Passing by reference
    如果设置参数为const则不能改变指向
    比方说reset()
    reset()方法为智能指针清空方法

  • Return by value
    指向一个local object
    可以用作链式函数

//
//  main.cpp
//  c2_unique_pointer_func
//
//  Created by ONEE on 2023/2/17.
//
#include <memory>
#include "cat.h"
#include <iostream>
using namespace std;

void do_with_cat_pass_value(unique_ptr<Cat> c){
    c->cat_info();
}

void do_with_cat_pass_ref(const unique_ptr<Cat> &c){
    c->set_cat_name("ref");
    c->cat_info();
//    c.reset();
}

unique_ptr<Cat> get_unique_ptr(){
    unique_ptr<Cat> p_dog=make_unique<Cat>("Local dog");
    cout<<"unique addredd: "<<p_dog.get()<<endl;
    cout<<"unique addredd: "<<&p_dog<<endl;

    return p_dog;
}
int main(int argc, const char * argv[]) {
    unique_ptr<Cat> c1=make_unique<Cat>("ff");
    do_with_cat_pass_value(move(c1));
//    c1->cat_info();
    //make_unique
    do_with_cat_pass_value(make_unique<Cat>());
    
    //2 pass ref
    //不加const
    unique_ptr<Cat> c2=make_unique<Cat>("f2");
    do_with_cat_pass_ref(c2);
    //c2->cat_info();
    cout<<"cat address: "<<c2.get()<<endl;
    c2->cat_info();
    
    
    //链式
    cout<<"--------------"<<endl;
    get_unique_ptr()->cat_info();
    
    
    
    std::cout << "~~~~~~~~~~~~~~~~~~~~~~~"<<endl;;
    return 0;
}

第四节:计数指针: shared_ptr

  • shared_ptr 计数指针又称共享指针
  • 与unique_ptr不同的是它是可以共享数据的
  • shared_ptr创建了一个计数器与类对象所指的内存相关联
  • Copy则计数器加一,销毁则计数器减一
  • api为use_count()

常量类型的shared_ptr

自定义类型的shared_ptr

//
//  main.cpp
//  c3_shared_pointer
//
//  Created by ONEE on 2023/2/17.
//

#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;


int main(int argc, const char * argv[]) {
    //常量类型
    
    shared_ptr<int> i_p1=make_shared<int>(10);
    //shared_ptr<int> i_p1=make_shared<int>{new int(10};
    cout<<"value : "<<*i_p1<<endl;
    cout<<"use count : "<<i_p1.use_count()<<endl;
    
    //copy
    shared_ptr<int> i_p2=i_p1;
    cout<<"p1 use count : "<<i_p1.use_count()<<endl;
    cout<<"p2 use count : "<<i_p2.use_count()<<endl;
    
    *i_p2=30;
    cout<<"i_p1 value : "<< *i_p1<<endl;

   // i_p2=nullptr;
    shared_ptr<int> i_p3=i_p1;
    i_p1=nullptr;
    cout<<"p1 use count : "<<i_p1.use_count()<<endl;
    cout<<"p2 use count : "<<i_p2.use_count()<<endl;
    cout<<"p3 use count : "<<i_p3.use_count()<<endl;

    
    //自定义类型
    shared_ptr<Cat> c_p1=make_shared<Cat>();
    shared_ptr<Cat> c_p2=c_p1;
    shared_ptr<Cat> c_p3=c_p1;
    
    cout<<"c_p1 usecount : "<<c_p1.use_count()<<endl;
    cout<<"c_p2 usecount : "<<c_p2.use_count()<<endl;
    cout<<"c_p3 usecount : "<<c_p3.use_count()<<endl;
    
    c_p1.reset();
    
    cout<<"c_p1 usecount : "<<c_p1.use_count()<<endl;
    cout<<"c_p2 usecount : "<<c_p2.use_count()<<endl;
    cout<<"c_p3 usecount : "<<c_p3.use_count()<<endl;
    
    
    std::cout << "Hhhhhhhhhhhhhhhhhh\n";
    return 0;
}

第五节: shared_ptr与函数

  • shared_ptr passed by value
    copy
    函数内部计数器加一
  • shared_ptr passed by ref
    const表示不可改变指向
  • returning by value
    链式调用
//
//  main.cpp
//  c4_shared_pointer_func
//
//  Created by ONEE on 2023/2/17.
//

#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;
void cat_by_value(shared_ptr<Cat> cat){
    cout<<cat->get_name()<<endl;
    cat->set_cat_name("value");
    cout<<"func use count : "<<cat.use_count()<<endl;
}
void cat_by_ref(shared_ptr<Cat> cat){
    cout<<cat->get_name()<<endl;
    cat->set_cat_name("value");
    cat.reset(new Cat());
    cout<<"func use count : "<<cat.use_count()<<endl;
}
shared_ptr<Cat> get_shared_ptr(){
    shared_ptr<Cat> cat_p=make_shared<Cat>("local cat");
    return cat_p;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    shared_ptr<Cat> c1 = make_shared<Cat>("dd");
    
    cat_by_value(c1);
    c1->cat_info();
    cout<<c1.use_count()<<endl;
    
    cout<<"~~~~~~~~~~~~~`\n";
    cat_by_ref(c1);
    c1->cat_info();
    std::cout << "Hello, World!\n";
    cout<<"~~~~~~~~~~~~~~~`\n";
    shared_ptr<Cat> c_p=get_shared_ptr();
    c_p->cat_info();
    return 0;
}

第六节: shared_ptr与unique_ptr

  • 不能将shared_ptr转换为unique_ptr

  • unique_ptr可以转换为shared_ptr
    通过std::move

常见的设计

将你的函数返回unique_ptr是一种常见的设计模式,这样可以提高代码的复用度,你可以随时改变为shared_ptr

unuque_ptr可以直接赋值给shared_ptr类型,可以随时改变返回类型为shared_ptr

//
//  main.cpp
//  c5_shared_unique
//
//  Created by ONEE on 2023/2/17.
//
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;

unique_ptr<Cat> get_unique_ptr(){
    unique_ptr<Cat> cat_p=make_unique<Cat>("local cat");
    return cat_p;
}

int main(int argc, const char * argv[]) {
    unique_ptr<Cat> c_p1=make_unique<Cat>("dd");
    shared_ptr<Cat> c_p2=move(c_p1);
    cout<<"c_p2 use count : "<< c_p2.use_count()<<endl;
    
    shared_ptr c_p3=get_unique_ptr();
    if(c_p3){
        c_p3->cat_info();
        cout<<"c_p3 use count : "<<c_p3.use_count()<<endl;
    }
    // insert code here...
    std::cout << "Hello, World!\n";
    return 0;
}

第七节:weak_ptr

weak_ptr并不拥有所有权

并不能调用 -> 和 解引用*

weak_ptr为什么会存在呢

  • A类中有一个需求需要存储其他A类对象的信息
  • 如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(Cyclic dependency problem)
  • 所以我们这里需要用一个不需要拥有所有权的指针来标记该同类对象
    weak_ptr可以通过lock()函数来提升为shared_ptr(类型装换)
//
//  main.cpp
//  c6_weak_pointer
//
//  Created by ONEE on 2023/2/17.
//

#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;


int main(int argc, const char * argv[]) {
    shared_ptr<Cat> s_pc1=make_shared<Cat>("c1");
    weak_ptr<Cat> w_pc1(s_pc1);
    shared_ptr<Cat> s_pc2=s_pc1;

    
    cout<<"w_pc1 :" <<w_pc1.use_count()<<endl;
    cout<<"s_pc1 :" <<s_pc1.use_count()<<endl;
    // insert code here...
    
    shared_ptr<Cat> s_pc3=w_pc1.lock(); //lock 使得weakptr转化为sharedptr
    
    cout<<"w_pc1 :" <<w_pc1.use_count()<<endl;
    cout<<"s_pc1 :" <<s_pc1.use_count()<<endl;
    cout<<"s_pc2 :" <<s_pc2.use_count()<<endl;
    cout<<"s_pc3 :" <<s_pc3.use_count()<<endl;
    
    cout<<"~~~~~~~~~~~~~`"<<endl;
    shared_ptr<Cat> c3=make_shared<Cat>("c3");
    shared_ptr<Cat> c4=make_shared<Cat>("c4");
    c3->set_friend(c4);//销毁不了c3 c4 把shared改成weak
    c4->set_friend(c3);
    
    
    std::cout << "Hello, World!\n";
    return 0;
}

cat.h

#ifndef CAT_H
#define CAT_H
#include<string>
#include<iostream>
class Cat{
private:
    std::string name=("MOren");
    std::weak_ptr<Cat> m_friend;
public:
    Cat(std::string name);
    Cat()=default;
    ~Cat();
    void cat_info()const{
        std::cout<<"cat info name :"<< name << std::endl; 
    }
    std::string get_name()const{
        return name;
    }
    void set_cat_name(const std::string &name){
        this->name=name;
    }
    void set_friend(std::shared_ptr<Cat> c){
        m_friend=c;
    }

};

#endif

cat.cpp

#include "cat.h"

Cat::Cat(std::string name):name(name){
    std::cout<<"Constructor of cat :"<< name << std::endl;
}

Cat::~Cat(){
    std::cout<<"Destructor of cat :"<< name << std::endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值