到目前为止,我们编写的程序中所使用的对象都有着严格定义的生存期:
我们的程序到目前为止只使用过静态内存或栈内存:
📒除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称作自由空间(free store)或堆(heap)。程序用堆来存储动态分配(dynamically allocate)的对象。动态对象的生存期由程序来控制,也就是说,当动态对象不再使用时,我们的代码必须显式地销毁它们。
📒除了局部和static对象外,C++还支持动态分配对象。动态分配的对象的生存期与它们在哪里创建是无关的,只有当显式地被释放时,这些对象才会销毁。动态对象的正确释放是编程中极其容易出错的地方。为了更安全地使用动态对象,C++标准库定义了两个智能指针类型来管理动态分配的对象。当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它。
但是动态内存的使用很容易出问题;
📒C++11为了更容易(同时也更安全)地使用动态内存,新的标准库提供了智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。具体说明如下:
从比较简单的层面来看,智能指针是 RAII(Resource Acquisition Is Initialization,资源获取即初始化) 机制对普通指针进行的一层封装,利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以方便管理一个对象的生命周期。
其实质如下:
注意:智能指针的头文件为<memory>
📒在C++编程中,内存管理一直是一个不可或缺的话题。传统的C++程序员依赖new和delete(新建和删除)来手动管理内存,但是由于new和delete不能自动管理资源也不支持自定义删除器,导致使用该方式容易导致内存泄漏或是双重释放等问题。这就是智能指针(Smart Pointers)登场的原因。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效。 在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。 这种做法有两大好处:
📒智能指针不仅仅是一个指针,它是一个对象,拥有生命周期(Lifetime)。当智能指针的生命周期结束时,它会自动释放所拥有的资源。这种自动管理机制极大地减少了程序员的负担,也降低了出错的可能性。
“The best code is no code at all.” - Jeff Atwood
这句话在这里非常合适。【越少的代码用于管理内存,越少的地方会出错。】智能指针就是这样一种工具,让你能更专注于业务逻辑而非内存管理。
下面将用一个自定义智能指针SmartPtr为例:
// 模板
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr) //当智能指针未初始化时,赋予nullptr缺省值
: _ptr(ptr)
{}
~SmartPtr(){
if (_ptr)
delete _ptr;
}
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
private:
T* _ptr;
};
int main()
{
SmartPtr<int> p(new int(1));
cout << *p << endl;
return 0;
}
其中重载了*
和->
运算符,使得使用这个类就像使用指针一样。智能指针是一个模板类,以能够管理任何类型的指针引用的内存,如果模板参数是一个有公有成员的类,那么还能使用->
访问其成员。
上面实现的智能指针SmartPtr
是极不完善的,如果想实现拷贝构造和拷贝赋值:
int main(){
SmartPtr<int> ap1(new int(1));
SmartPtr<int> ap2(ap1); //拷贝构造
SmartPtr<int> ap3(new int(2));
SmartPtr<int> ap4(new int(2));
ap4 = ap3; // 拷贝赋值
return 0;
}
错误(Clion):
malloc: *** error for object 0x600003c84030: pointer being freed was not allocated
造成程序崩溃的原因:是在这个类中没有实现拷贝构造函数和拷贝赋值函数,而编译器默认生成的全都是对内置类型的浅拷贝(值拷贝):相当于ap1和ap2、ap3和ap4共同管理同一块空间。当出了ap1的作用域后,调用析构函数,释放空间,ap2再次调用析构函数时导致这块已经被释放的空间再次被释放。ap3和ap4同理。
要解决浅拷贝造成的二次析构问题,就必须要去实现深拷贝的拷贝构造函数和拷贝赋值函数吗?
答案是不用的,智能指针的功能需求是模拟指针的使用,本质是帮指针托管资源,那么指针的拷贝或赋值操作就相当于两个指针指向同一块内存空间。资源管理权转移,通过不负责任的拷贝,会导致被拷贝对象悬空。虽然资源能得到释放,但是会造成垂悬指针。智能指针将内存资源的管理和对象的生命周期绑定在一起,如果只是像上面一样简单地满足RAII,那么一定会发生二次析构的问题,因为创建的智能指针对象一定会调用析构函数,且不论程序是否正常结束。
后面以标准库中(C++98)智能指针auto_ptr
为例。
在c++中,智能指针一共定义了4种: auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。 📝其中,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用。 📙虽然auto_ptr有很多问题,但是也没有取消,因为可能有人在用,因此后面弄了一个 Boost库,属于C++扩展库(第三方库),不属于C++标准库(C++标准库是写了头文件就能用) 注意:本文主要讲的就是后面的 三种智能指针。
智能指针 | 类型 |
---|---|
unique_ptr | 独占的智能指针,该指针独占对象的所有权,每个对象智能有一个该指针 |
shared_ptr | 共享的智能指针,多个共享指针可以指向同一个对象 |
weak_ptr | 弱引用的智能指针,该指针是对象的一个非拥有性引用,不共享指针,不能操作资源,用来指向一个shared_ptr,主要用来避免shared_ptr循环引用导致的内存泄露 |
为了方便我们对下面智能之类的理解,我们定义如下的类
// Date类
struct Date{
int _year;
int _month;
int _day;
Date (int year = 1, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
~Date() {
cout << "~Date()" << endl;
}
};
//指定删除器模板
template<class T>
class DeleteArray{
public:
void operator()(T* ptr){
delete[] ptr;
}
};
//文件删除的模板
class Fclose{
public:
void operator()(FILE* ptr){
cout << "fclose:" << ptr << endl;
fclose(ptr);
}
};
//Test类
class Test
{
public:
Test() : _num(0)
{
cout << "Test() 构造成功..." << endl;
}
Test(int x) : _num(0)
{
cout << "Test(int x) 构造成功, x = " << x << endl;
}
Test(string str) : _num(0)
{
cout << "Test(string str) 构造成功, str = " << str << endl;
}
~Test()
{
cout << "~Test() 析构成功..." << endl;
}
void setValue(int v)
{
this->_num = v;
}
void print()
{
cout << "_num: " << this->_num << endl;
}
private:
int _num;
};
为了解决上面1.4 里面的问题,由于出现多次析构问题的本质是同一块内存空间被多个对象通过管理,因此如果将资源的管理权只交给一个对象,就不会出现多次析构问题。
int main(){
auto_ptr<Date> ap1(new Date);
//拷贝时,管理权限转移,被拷贝悬空
auto_ptr<Date> ap2(ap1);
//下面这样会使程序会崩,因为auto_ptr的拷贝是管理权转移,这样就会导致ap1空了
ap1->_year++;
return 0;
}
然而,将一个对象对资源的管理权转移后,就意味着这个对象再对资源访问是一个非法操作,程序会因此崩溃。如果让不熟悉auto_ptr原理的人使用,因为拷贝操作而造成非法指针或内存泄漏是有可能的(拷贝悬空),而这也是致命的错误,因此许多公司明文规定禁止auto_ptr的使用,进而由C++11的unique_ptr和shared_ptr取代。
出现多次析构问题的本质是同一块内存空间被多个对象通过管理,如果将资源的管理权只交给一个对象,就不会出现多次析构问题。
C++98的auto_ptr因为拷贝和赋值操作而造成内存泄漏和悬垂指针的问题而饱受诟病,C++11引入的unique_ptr则粗暴地砍掉了它的拷贝和赋值功能。这通过C++11引入的关键字delete的新功能实现。
在C++11之前,可以通过将构造函数和拷贝赋值函数私有声明实现。
🌞它实现对对象的独占所有权语义。这意味着一个unique_ptr在任何时候都指向一个对象,而且这个对象只能由一个unique_ptr拥有。当unique_ptr被销毁(例如离开其作用域)时,它所指向的对象也会被自动删除。
样例1:(初概)
int main() {
unique_ptr<Date> up1(new Date);
//不支持拷贝
//unique_ptr<Date> up2(up1);
//unique_ptr<Date> up2(new Date[6]); //error,因为类型不匹配,由于我们是用new []构造的,那么就要用delete[]去销毁
//定制删除器解决上述问题
unique_ptr<Date, DeleteArray<Date>> up2(new Date[6]); //用定制的删除器
//unique_ptr<Date[]> up2(new Date[5]);
unique_ptr<FILE, Fclose> up3(fopen("test.cpp", "r"));
return 0;
}
样例2:(详细)
1. 初始化
2. 指定删除器
int main()
{
/*-------------------------- 一,初始化智能指针unique_ptr ------------------------------*/
// 1.通过构造函数初始化
unique_ptr<int> up1(new int(3));
//unique_ptr<int> up = up1; // 编译错误,不能复制
// 2.通过移动函数初始化
unique_ptr<int> up2 = move(up1); // 现在up2独占资源,up1变为空
// 3.通过reset,释放资源并将指针置为空,然后再初始化
up1.reset(new int(7));
//对比reset,release会释放资源的所有权但不删除,返回原始指针
up1.release();
/*-------------------------- 二,unique_ptr的使用 ------------------------------*/
//1.方法一
unique_ptr<Test> up3(new Test(666));
Test* pt = up3.get();
pt->setValue(6);
pt->print();
//2.方法二
up3->setValue(777);
up3->print();
/*------------------------------------ 三,指定删除器 -----------------------------------*/
1.函数指针类型
//using ptrFunc = void(*)(Test*);
//unique_ptr<Test, ptrFunc> up4(new Test("hello"), [](Test* t) {
// cout << "-----------------------" << endl;
// delete t;
// });
//2.仿函数类型(利用可调用对象包装器)
unique_ptr<Test, function<void(Test*)>> up4(new Test("hello"), [](Test* t) {
cout << "-----------------------" << endl;
delete t;
});
/*---------- 四,独占(共享)的智能指针可以管理数组类型的地址,能够自动释放 ---------*/
unique_ptr<Test[]> up5(new Test[3]);
//在c++11中shared_ptr不支持下面的写法,c++11以后才支持的
shared_ptr<Test[]> up6(new Test[3]);
return 0;
}
🌙shared_ptr是C++11的智能指针,通过引用计数的方式解决智能指针的拷贝问题。 🌙每个被管理的资源有有一个对应的引用计数,这个引用计数记录当前有多少对象在管理这块资源。 🌙每新增加一个对象管理这块资源则对该资源的引用计数++,当一个对象不在管理这块资源或对象析构时那么该资源对应的引用计数 – –,当一个资源的引用计数为0时那么就说明已经没有对象在管理这块资源了,这时候就可以进行释放了。 引用计数的方式能够支持多个对象一起管理一个资源,也就支持智能指针的拷贝,只有当资源的引用计数减为0时才会释放,保证了同一个资源不会被多次释放: 🌙实现了对象的共享所有权语义。多个shared_ptr可以指向同一个对象,并且每个shared_ptr持有一个引用计数。当最后一个指向某个对象的shared_ptr被销毁或重置时,该对 象才会被删除。
样例1:(初概)
int main() {
//vector<shared_ptr<Date>> v;
shared_ptr<Date> sp1(new Date);
shared_ptr<Date> sp2(sp1);
shared_ptr<Date> sp3(sp2);
cout << sp1.use_count() << endl; //查看其引用计数
//定制删除器
shared_ptr <Date[]> sp4(new Date[5]);
//shared_ptr<FILE, Fclose> up5(fopen("test.cpp", "r")); //不支持传这个模板参数
shared_ptr<FILE> up5(fopen("test.cpp", "r"), Fclose());
shared_ptr<Date> sp6 = make_shared<Date>(2024, 8, 5);
shared_ptr<int> sp7((int*)malloc(40), [](int* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
});
return 0;
}
1. 初始化
shared_ptr通过一个指针保持对一个对象的共享所有权。多个shared_ptr对象可以拥有同一个对象。当以下情况之一发生时,对象被销毁并释放其内存:
2. 获取原始指针
3. 指定删除器
4. 引用计数
样例2:(详细)
int main()
{
/*---------------------------- 一,初始化智能指针shared_ptr ------------------------------*/
// 1.通过构造函数初始化
shared_ptr<int> sp1(new int(3));
cout << "sp1管理的内存引用计数:" << sp1.use_count() << endl;
// 2.通过移动和拷贝构造函数初始化
shared_ptr<int> sp2 = move(sp1);
cout << "sp1管理的内存引用计数:" << sp1.use_count() << endl;
cout << "sp2管理的内存引用计数:" << sp2.use_count() << endl;
shared_ptr<int> sp3 = sp2; //赋值
cout << "sp2管理的内存引用计数: " << sp2.use_count() << endl;
cout << "sp3管理的内存引用计数: " << sp3.use_count() << endl;
// 3.通过 std::make_shared初始化
shared_ptr<int> sp4 = make_shared<int>(8);
shared_ptr<Test> sp5 = make_shared<Test>(7);
shared_ptr<Test> sp6 = make_shared<Test>("Love Life");
// 4.通过reset初始化
sp6.reset();//重置sp6, ps6的引用基数为0
cout << "sp6管理的内存引用计数: " << sp6.use_count() << endl;
sp5.reset(new Test("hello")); //重置了指针的指向对象,原来的对象已经释放
cout << "sp5管理的内存引用计数: " << sp5.use_count() << endl;
cout << endl << endl;
/*----------------------------- 二,共享智能指针shared_ptr的使用 ------------------------------*/
// 1.方法一
Test* t = sp5.get();
t->setValue(1000);
t->print();
// 2.方法二
sp5->setValue(7777);
sp5->print();
cout << endl << endl;
///*------------------------------ 三,指定删除器 -----------------------------------*/
// 1.简单举例
shared_ptr<Test> ppp(new Test(100), [](Test* t) {
//释放内存
cout << "Test对象的内存被释放了......." << endl;
delete t;
});
printf("----------------------------------------------------------------------\n");
2.如果是数组类型的地址,就需要自己写指定删除器,否则内存无法全部释放
// //shared_ptr<Test> p1(new Test[5], [](Test* t) {
// // delete[]t;
// // });
3.也可以使用c++给我们提供的 默认删除器函数(函数模板)
shared_ptr<Test> p2(new Test[3], default_delete<Test[]>());
4.c++11以后可以这样写 也可以自动释放内存
shared_ptr<Test[]> p3(new Test[3]);
return 0;
}
此外我们还可以封装一个函数模板make_shared_array方法来让shared_ptr支持数组:
template <typename T>
shared_ptr<T> make_share_array(size_t size)
{
//返回匿名对象
return shared_ptr<T>(new T[size], default_delete<T[]>());
}
int main()
{
shared_ptr<int> ptr1 = make_share_array<int>(10);
cout << ptr1.use_count() << endl;
shared_ptr<string> ptr2 = make_share_array<string>(7);
cout << ptr2.use_count() << endl;
}
shared_ptr中的引用计数是存放在堆区,因为这样可以让所有指向同一个对象的shared_ptr。如果引用计数在栈区,那么当一个shared_ptr改变指向或者离开作用域时,就无法通知其他shared_ptr更新引用计数了。因此,引用计数也不能是静态成员,每个类型实例化的智能指针对象时共用静态成员,这会导致管理相同资源的对象和管理不同资源的对象共用同一个引用计数。 由于在堆区的引用计数和同一类型的智能指针是绑定在一起的,当智能指针释放资源时,也需要释放引用计数占用的内存。
⭐弱引用智能指针std::weak_ptr可以看做是shared_ptr的助手,是对shared_ptr所管理对象的一个非拥有性引用,它不管理shared_ptr内部的指针。std::weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数。最后一个指向对象的shared_ptr被销毁时,无论是否还有weak_ptr指向该对象,对象都会被删除。它的主要作用就是作为一个旁观者监视shared_ptr中管理的资源是否存在,解决shared_ptr可能导致的循环引用问题。 在这部分利用率局部变量的特性,C++的局部变量存在栈中,当变量的生命周期结束后,那栈会自动释放空间。而智能指针同样为局部变量,存在栈中。
样例1:(初概)
// ———————— weadk_ptr ——————————
int main()
{
// weak_ptr不支持管理资源,不支持RAII
//std::weak_ptr<Date> wp1(new Date);
weak_ptr<Date> wp;
shared_ptr<Date> sp; //保证不过期
{
shared_ptr<Date> n1(new Date);
wp = n1;
cout << wp.expired() << endl;
n1->_day++;
//sp = wp.lock();
}
// 出了作用域就失效了
cout << wp.expired() << endl;
}
样例2:(详细)
std::weak_ptr底层与std::shared_ptr共享相同的引用计数机制,但不会增加计数。
int main()
{
/*---------------------------- 一,初始化智能指针weak_ptr ------------------------------*/
// weak_ptr不支持管理资源,不支持RAII
shared_ptr<int> sp(new int(2));
weak_ptr<int> wp1;; //构造了一个空weak_ptr对象
weak_ptr<int> wp2(wp1); //通过一个空weak_ptr对象构造了另一个空weak_ptr对象
weak_ptr<int> wp3(sp); //通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象
weak_ptr<int> wp4;
wp4 = sp; //通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象(这是一个隐式类型转换)
weak_ptr<int> wp5;
wp5 = wp3; //通过一个weak_ptr对象构造了一个可用的weak_ptr实例对象
/*---------------------------- 二,weak_ptr常用函数 ------------------------------*/
// 1.通过调用weak_ptr类提供的use_count()方法可以获得当前共享该资源的shared_ptr数量
cout << "wp1管理的shared_ptr内存引用计数:" << wp1.use_count() << endl;
cout << "wp2管理的shared_ptr内存引用计数:" << wp2.use_count() << endl;
cout << "wp3管理的shared_ptr内存引用计数:" << wp3.use_count() << endl;
cout << "wp4管理的shared_ptr内存引用计数:" << wp4.use_count() << endl;
cout << "wp5管理的shared_ptr内存引用计数:" << wp5.use_count() << endl;
cout << endl << endl;
// 2.通过调用std::weak_ptr类提供的expired()方法来判断
//判断指针所指的内存空间是否被释放掉 / 指针是否为空 / 是否还有shared_ptr指针指向weak_ptr指向的内存空间
shared_ptr<int> sp2 = make_shared<int>(10);
weak_ptr<int> wp6(sp2); // shared_ptr初始化weak_ptr
sp2.reset(new int); // 此时,已没有一个shared_ptr指针指向weak_ptr指向的内存区域
cout << "是否已没有shared_ptr指针指向该内存区域:" << wp6.expired() << endl;
shared_ptr<int> sp22 = nullptr;
weak_ptr<int> wp7(sp22);
cout << "weak_ptr指针是否为空:" << wp7.expired() << endl;
// 3.通过调用weak_ptr类提供的lock()方法来获取管理所监测资源的shared_ptr对象,返回一个shared_ptr,增加引用计数
shared_ptr<int> sp3 = wp3.lock(); // 利用返回的shared_ptr初始化
cout << "share_ptr的初始化为:" <<*sp3 << endl;
cout << "wp3管理的shared_ptr内存引用计数:" << wp3.use_count() << endl;
return 0;
}
假如存在这样的一个结构体
struct ListNode{
int _data;
shared_ptr<ListNode> _next;
shared_ptr<ListNode> _prev;
//用weak_ptr就可以避免内存泄露
//weak_ptr<ListNode> _next;
//weak_ptr<ListNode> _prev;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
情况一:当我们在shaed_ptr的正常情况下未出现内存泄露
int main()
{
// 下面这就是循环引用 --> 内存泄露
std::shared_ptr<ListNode> n1(new ListNode);
std::shared_ptr<ListNode> n2(new ListNode);
n1->_next = n2;
//n2->_prev = n1; //互相指向,就会出现内存泄露
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
return 0;
}
情况二:当我们增加一句n2->_prev = n1; 到函数中,此时就出现了互相指向,导致了内存泄露
情况三:造成问题的本质是引用计数永不为0,那么只要将其中一个智能指针改为weak_ptr即可:
🌸由上面可知:weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期。也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
🌸它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造,它的构造和析构不会引起引用记数的增加或减少。弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
也就是说,weak_ptr是为了弥补shared_ptr循环引用而生的,它没有RAII的特性,不直接管理资源,只是shared_ptr的跟班,这也是weak_ptr支持使用shared_ptr构造的原因。 在使用上,weak_ptr支持指针所有的操作。它不是一个功能型的智能指针,而是辅助型,它的使命是解决shared_ptr造成的循环引用问题。
🌸与 shared_ptr 不同,weak_ptr 不能直接访问所指向的对象。要访问对象,需要先调用 lock() 方法将其转换为 shared_ptr。如果所指向的对象已经被销毁,则 lock() 方法返回空指针。
📚看完前面其智能指针的实现,相信大家对智能指针也有个大概的了解了,下面我们来实现部分的智能指针吧,如unique_ptr、shared_ptr、weak_ptr.
智能指针的实现,必须解决下面三个问题:
其中最容易实现的是像指针一样使用,只要重载使用指针的运算符即可。其次是实现RAII,最后是智能指针对象的拷贝。
auto_ptr的实现原理:管理权转移的思想
namespace qian {
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& sp)
:_ptr(sp._ptr)
{
// 管理权转移
sp._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
// 检测是否为自己给自己赋值
if (this != &ap)
{
// 释放当前对象中资源
if (_ptr)
delete _ptr;
// 转移ap中资源到当前对象中
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
~auto_ptr()
{
if (_ptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
}
}
// 像指针一样使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
模拟实现的过程:去除auto_ptr中拷贝和赋值的函数。
📚注意:C++98中,delete的意思是不让编译器自动生成默认函数,而C++11为了实现这个智能指针,赋予delete一个新功能:不允许调用,因此我们在下面的拷贝和赋值中用到了delete。
namespace qian
{
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr != nullptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
}
unique_ptr(auto_ptr<T>& ap) = delete;
unique_ptr& operator=(auto_ptr<T>& ap) = delete;
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
int main()
{
qian::unique_ptr<int> up1(new int);
qian::unique_ptr<int> up2(new int);
up2 = up1; // error
return 0;
}
其中,operator = 的重载需要注意两个问题:
namespace qian
{
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
, _pcount(new int(1))
{}
~shared_ptr()
{
if (--(*_pcount) == 0)
{
if (_ptr != nullptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
delete _pcount;
_pcount = nullptr;
}
}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pcount(sp._pcount)
{
(*_pcount)++;
}
shared_ptr<T>& operator=(shared_ptr<T>& sp)
{
if (_ptr != sp._ptr) // 管理同一资源的智能指针赋值无意义
{
if (--(*_pcount) == 0) // 将管理的资源的引用计数-1
{
cout << "delete:" << _ptr << endl;
delete _ptr;
delete _pcount;
}
_ptr = sp._ptr; // 与传入的智能指针共享资源
_pcount = sp._pcount; // 将自己的引用计数和传入的智能指针同步
(*_pcount)++; // 引用计数+1,表示自己是新增的管理者
}
return *this;
}
// 获取引用计数
int use_count()
{
return *_pcount;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _pcount; // 引用计数
};
}
int main(){
qian::shared_ptr<int> sp1(new int(1));
cout << sp1.use_count() << endl;
qian::shared_ptr<int> sp2(sp1);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
qian::shared_ptr<int> sp3(new int(0));
cout << sp3.use_count() << endl;
qian::shared_ptr<int> sp4(new int(2));
sp3 = sp4;
cout << sp3.use_count() << endl;
cout << sp4.use_count() << endl;
return 0;
}
get()
接口,所以返回指针时可以调用namespace qian
{
template<class T>
class weak_ptr
{
public:
weak_ptr()
:_ptr(nullptr)
{}
weak_ptr(const shared_ptr<T>& sp)
:_ptr(sp.get()) //sp的get接口在share_pr中已实现,如上
{}
weak_ptr& operator=(const shared_ptr<T>& sp)
{
//_ptr = sp._shared_ptr;
_ptr = sp.get();
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
以上就是智能指针的全部内容啦,后面我会单独出一篇关于自定义删除器的博客,敬请期待咯!!! 💞 💞 💞那么本篇到此就结束,希望我的这篇博客可以给你提供有益的参考和启示,感谢大家支持!!!祝大家天天开心。