共享指针 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对象,导致对象的重复删除。
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的实现原理。
内部维护两个指针, _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所管理的对象。
mShared_ptr<Sample> p(new Sample(123));
mShared_ptr<Sample> p1(new Sample(456));
p->hello();
p.swap(p1);
p->hello();
返回shaerd_ptr所指对象的引用次数。
mShared_ptr<Sample> p;
cout << p.use_count() << endl; // 1
mShared_ptr<Sample> p1(p);
cout << p1.use_count() << endl; // 2
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;
}