c++ 智能指针shared_ptr

概述

共享指针 shared_ptr是c++11的新增特性,主要用于包装一个原始指针(裸指针),从而实现堆内存上的对象自动释放,省去手动调用delete去释放对象。
优点:由于shared_ptr的管理,在一个堆上对象不再有用时(没有指针指向这个对象),将被自动释放内存。coder只负责new 出对象,而不需要关注何时应该调用delete删除对象。
头文件:

使用

shared_ptr本质是有个模板类,传入裸指针作为shared_ptr的构造函数的参数即可。

导入头文件

#include 

构造

常见的构造方式有两种。

构造方式一:传入裸指针直接构造

//	推荐 
shared_ptr<int> sp = shared_ptr<int>(new int(2)); 

// 不推荐
int *p = new int(2);
shared_ptr<int> sp = shared_ptr<int>(p); 

因为shared_ptr管理的对象的裸指针p暴露在代码中,coder有机会直接delete对象,这样是不安全的。
对于coder在代码中通过裸指针直接delete对象的行为,shared_ptr是不可知的,因此shared_ptr会自以为正常的再次delete对象,导致对象的重复删除。

构造方式二:make_shared<>()

shared_ptr<int> sp = make_shared<int>(2);

传入创建shared_ptr管理的对象(int)所需的参数,make_shared内部会自动调用new创建对象。
这种方式同样也不会暴露裸指针,是安全的。也是我们经常使用的一种方式。

调用管理的对象属性和方法 -> 、*

借助经重载的->运算符,可直接访问shared_ptr所管理的对象的属性和方法。

#include 
#include 

using namespace std;

// 测试类
struct Sample
{
    Sample() : val(0){
        std::cout << "Sample\n";
    }
    Sample(int _val) : val(_val){
        std::cout << "val Sample\n";
    }
    ~Sample() {
        std::cout << "~Sample\n";
    }
    void hello() {std::cout << "hello world " << std::endl;}
    int val;
};


int main()
{
	// 方式一:借助 -> 运算符
    shared_ptr<Sample> sp = shared_ptr<Sample>(new Sample(123));
    sp->hello();				// 调用对象的函数
    cout << sp->val << endl; 	// 调用对象的属性
    sp->val = 9999;
    cout << sp->val << endl;	// 修改对象的属性
	
	// 方式二:借助 * 运算符
	shared_ptr<Sample> sp = shared_ptr<Sample>(new Sample(123));
    (*sp).hello();
    cout << sp->val << endl;
    (*sp).val = 9999;
    cout << sp->val << endl;
    
    return 0;
}

/* --output--
	-> -----------------
	val Sample
	hello world
	123
	9999
	* -----------------
	val Sample
	hello world
	123
	9999
	~Sample
	~Sample
*/

shared_ptr的两个重要成员变量

在介绍shared_ptr的成员函数前,先简单说说shared_ptr的实现原理。
内部维护两个指针, _ptr、_cnt。(名字可能不是这样,但不重要。便于理解。)
T* _ptr :保存shared_ptr管理的对象的指针。
int* _cnt:shared_ptr所指的这个对象的引用次数。即有多少个shared_ptr同时管理这个指针。
shared_ptr对象允许进行赋值运算和显式拷贝(每次赋值或拷贝*cnt加一),使得被管理的对象是多个shared_ptr所共享的。
那谁该负责被管理的对象的析构呢?

“活”到最后的那个shared_ptr对象。每次shared_ptr对象在析构或调用reset()方法时(后面会说),*_cnt会进行减一操作,如果发现减一前*_cnt为1,说明不存在其他share_ptr对象管理这个对象,此时就应该在失去对象管理权限前,将管理的对象析构。

shared_ptr类本身的方法

get()

返回存储的指针。

reset()

替换所管理的对象。

swap()

交换两个shared_ptr所管理的对象。

mShared_ptr<Sample> p(new Sample(123));
mShared_ptr<Sample> p1(new Sample(456));
p->hello();
p.swap(p1);
p->hello();

use_count()

返回shaerd_ptr所指对象的引用次数。

mShared_ptr<Sample> p;	
cout << p.use_count() << endl;   // 1
mShared_ptr<Sample> p1(p);
cout << p1.use_count() << endl; // 2

常见的错误使用

1、错误的共享管理
int *p = new int(123);
shared_ptr<int> sp1 = shared_ptr<int>(p);
shared_ptr<int> sp2 = shared_ptr<int>(p);

cout << sp1.use_count() << std::endl;  // 1
cout << sp2.use_count() << std::endl;  // 1

两个shared_ptr对象虽然管理同一个对象指针,但二者确都不知道对方的存在,导致各自都认为是p指针的唯一管理者,会导致内存重复释放。
正确的共享方法:

int *p = new int(123);
shared_ptr<int> sp1 = shared_ptr<int>(p);
// shared_ptr sp2 = shared_ptr(p);
shared_ptr<int> sp2 = shared_ptr<int>(sp1);

cout << sp1.use_count() << std::endl;  // 2
cout << sp2.use_count() << std::endl;  // 2

简单实现

mShared_ptr.h
mShared_ptr.cpp
test.cpp
Shared_ptr.h

// Shared_ptr.h
#pragma once

// 测试类
struct Sample
{
    Sample() : val(0){
        std::cout << "Sample\n";
    }
    Sample(int _val) : val(_val){
        std::cout << "val Sample\n";
    }
    ~Sample() {
        std::cout << "~Sample\n";
    }
    void hello() {std::cout << "hello world " << val << std::endl;}
    int val;
};

template <class T>
class mShared_ptr
{
public:
    mShared_ptr() : _ptr(new T()), _cnt(new int (1)) {std::cout << "默认构造 mShared_ptr \n";}
    mShared_ptr(T* p) :  _ptr(p), _cnt(new int (1))  {std::cout << "有参构造 mShared_ptr\n";}
    ~mShared_ptr();
    explicit mShared_ptr(const mShared_ptr& ms_ptr);
    mShared_ptr (const mShared_ptr&& ms_ptr);

    T* operator->();
    T& operator*() ;
    operator bool() const;
    mShared_ptr& operator=(const mShared_ptr& ms_ptr);
    bool operator==(const mShared_ptr& ms_ptr) const;

    void fun();
    int use_count();
    void reset ();
    void swap(mShared_ptr& ms_ptr);
    T* get();
    bool unique() const ; // 此函数于 C++17 中被弃用并于 C++20 中移除
    
private:
    T* _ptr;
    int* _cnt;
};

Shared_ptr.cpp

#include 
#include "mShared_ptr.h"

template <class T>
void mShared_ptr<T>::fun()
{
    puts("6666");
}

template <class T> 
mShared_ptr<T>::~mShared_ptr() 
{
    std::cout << "mShared_ptr 析构函数\n";
    reset();
}

template <class T> 
mShared_ptr<T>::mShared_ptr(const mShared_ptr& ms_ptr)
{
    (*ms_ptr._cnt) ++;
    std:: cout << "拷贝构造函数 mShared_ptr\n";
    this->_cnt = (ms_ptr._cnt) ;
    this->_ptr = (ms_ptr._ptr) ;
}

template <class T> 
mShared_ptr<T>::mShared_ptr(const mShared_ptr&& ms_ptr)
{
    std:: cout << "移动构造函数 mShared_ptr\n";
    _cnt = ms_ptr._cnt;
    _ptr = ms_ptr._ptr;
}

template <class T> 
T* mShared_ptr<T>::operator->()
{
    return _ptr;
}

template <class T> 
T& mShared_ptr<T>::operator*()
{
    return *_ptr;
}

template <class T> 
mShared_ptr<T>::operator bool() const
{
    return  !(_ptr == nullptr);
}

template <class T> 
mShared_ptr<T>& mShared_ptr<T>::operator=(const mShared_ptr& ms_ptr)
{
    if (!ms_ptr)
        reset();
    else {
        (*ms_ptr._cnt) ++;
        std:: cout << "重载运算符= mShared_ptr\n";
        this->_cnt = (ms_ptr._cnt) ;
        this->_ptr = (ms_ptr._ptr) ;
    }
    
}

template <class T> 
bool mShared_ptr<T>::operator==(const mShared_ptr& ms_ptr) const
{
    return _ptr == ms_ptr._ptr;
}

template <class T> 
T* mShared_ptr<T>::get()
{
    return _ptr;
}

template <class T> 
int mShared_ptr<T>::use_count()
{
    int res;
    if (!_cnt) {res = 0; std::cout << "use count " << 0 << std::endl;}
    else {res = *(_cnt); std::cout << "use count " << res << std::endl;}
    return res;
}

template <class T> 
void mShared_ptr<T>::reset()
{
    if (!_ptr && !_cnt) return;

    (*_cnt) --;
    if (*_cnt == 0) {
        std::cout << "0 释放内置对象\n";
        delete _ptr;
        _ptr = nullptr;

        delete _cnt;
        _cnt = nullptr;
    }
    else {
        std::cout << *_cnt << "不释放内置对象\n";
        _ptr = nullptr;
        _cnt = nullptr;
    }

}

template <class T> 
void mShared_ptr<T>::swap(mShared_ptr& ms_ptr)
{
    T* tmp_ptr = _ptr;
    int* tmp_cnt = _cnt;

    _ptr = ms_ptr._ptr;
    _cnt = ms_ptr._cnt;

    ms_ptr._ptr = tmp_ptr;
    ms_ptr._cnt = tmp_cnt;
}

// 此函数于 C++17 中被弃用并于 C++20 中移除
template <class T> 
bool mShared_ptr<T>::unique() const
{
    return *_cnt == 1;
}


template <typename T, typename... Args>
mShared_ptr<T> make_shared(Args&& ...args)
{
    return mShared_ptr<T>(new T(std::forward<Args>(args)...));;
}

test.cpp

#include 
#include "mShared_ptr.cpp"

// use_count  reset
void test1(){
    mShared_ptr<Sample> p;
    p.use_count();
    mShared_ptr<Sample> p1(p);
    p1.use_count();

    p1.reset();
    p.use_count();

    if(!p1) {
        std::cout << "p1 为空 shared_ptr" << std::endl;
    }
}

// * -> == 
void test2()
{
    mShared_ptr<Sample> p(new Sample(789));
    p->hello();
    (*p).hello();

    mShared_ptr<Sample> p1;
    p1 = p;
    std::cout << "p == p1  " << (p == p1) << std::endl;
}

// swap unique
void test3()
{
    mShared_ptr<Sample> p(new Sample(123));
    mShared_ptr<Sample> p1(new Sample(456));
    p->hello();
    p.swap(p1);
    p->hello();
    std::cout << std::boolalpha << p.unique() << std::endl;
}

void test4()
{
    int a = 5656;
    mShared_ptr<Sample> p = make_shared<Sample> (a);
    std::cout << p->val << std::endl;
}

int main()
{
    test1();
    // test2();
    // test3();
    // test4();
    return 0;
}

你可能感兴趣的:(c++,算法,开发语言)