c++11之智能指针以及循环引用的解决

背景

理解智能指针需要从下面三个层次:

1、从较浅的层面看,智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。
2、智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。
3、智能指针还有一个作用是把值语义转换成引用语义。

智能指针的引出实例

#include <memory>
#include <iostream>

using namespace std;

int main()
{
    unique_ptr<int> up1(new int(11));  //无法复制的unique_ptr
    unique_ptr<int> up2 = up1;         //不能通过编译
    
    cout << *up1 << endl;        //11
    
    unique_ptr up3 = move(up1);  //现在up3是数据唯一的unique_ptr只能指针
    
    cout << *up3 << endl;       //11
    cout << *up1 << endl;       //运行时错误
    up3.reset();                //显示释放内存
    up1.reset();                //不会导致运行时错误
    
    cout << *up3 << endl;       //运行时错误
    
    share_ptr<int> sp1(new int(22));
    share_ptr<int> sp2 = sp1;   //OK
    
    cout << *sp1 << endl;       //22
    cout << *sp2 << endl;       //22
    
    sp1.reset();
    cout << *sp2 << endl;       //22
}

例子使用两种不同的智能指针unique_ptr和share_ptr来自动释放堆对象的内存。在析构或者调用reset都能释放对象。

  • unique_ptr与所指的对象内存绑定紧密,不能与其他的unique_ptr类型的指针共享所指对象的内存。每个unique_ptr都唯一的拥有所指对象的内存。但是这种所有权可以通过标准库的move函数来转移,如unique_ptr up3 = move(up1),一旦转移成功,原先的 unique_ptr就失去了对象内存的所有权,再使用已经失去权力的unique_ptr就会出现运行错误。
    从实现上讲,unique_ptr是删除了拷贝构造函数,保留了移动构造函数的封装类型,程序仅可以使用右值对unique_ptr进行构造,一旦构造成功,右值对象中的指针失效。

  • share_ptr则不同,它允许多个该智能指针共享的永远同一堆分配对象的内存。与unique_ptr不同的是在实现上采用了引用计数,所以,一旦某个share_ptr失去了所有权,其他指针并没有影响,例子中sp1和sp2都共享指针,在sp1 reset之后只会使计数器降低,而不会导致内存的释放,当sp2 reset之后,导致引用计数器为零,share_ptr才会真正释放占有的内存空间。

unique_ptr、share_ptr、weak_ptr

unique_ptr

<memory> 头文件

1. 定义:
unique_ptr 是 C++ 11 提供的用于防止内存泄漏的智能指针中的一种实现,独享被管理对象指针所有权的智能指针。unique_ptr对象包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。
2. 性质:
1)、 unique_ptr是一个独享所有权的智能指针,无法进行复制构造、赋值操作操作,只能进行移动操作。无法使两个unique_ptr指向同一个对象;

//通过原始指针创建unique_ptr对象
std::unique_ptr<Task> taskPtr2(new Task(55));
 
//编译错误,unique_ptr对象不可复制
std::unique_ptr<Task> taskPtr3 = taskPtr2;
 
//编译错误,unique_ptr对象不可复制
taskPtr = taskPtr2;

2)、unique_ptr智能指向一个对象,如果当它指向其他对象时,之前所指向的对象会被摧毁。
3)、unique_ptr对象会在它们自身被销毁时使用删除器自动删除它们管理的对象。
4)、unique_ptr支持创建数组对象方法。
5)、unique_ptr具有->和*运算符重载符,因此它可以像普通指针一样使用。

创建unique_ptr 对象

方法1、
std::unique_ptr<int> u1;// 创建一个空的 unique_ptr 对象
检查 unique_ptr 对象是否为空:

// 方法1
if(!ptr1)
	std::cout<<"ptr1 is empty"<<std::endl;
// 方法2
if(ptr1 == nullptr)
       std::cout<<"ptr1 is empty"<<std::endl;

std::unique_ptr<int> u2 (nullptr);
方法2、
std::unique_ptr<int> u3 (new int(1));
使用原始指针创建 unique_ptr 对象,要创建非空的 unique_ptr 对象,需要在创建对象时在其构造函数中传递原始指针,即:
不能通过赋值的方法创建对象,下面的这句是错误的

// std::unique_ptr<Task> taskPtr2 = new Task(); // 编译错误

方法3、
std::unique_ptr<int> u4 (new int, d);
方法4、
std::unique_ptr<int> u5 (new int, std::default_delete<int>());

重置 unique_ptr 对象

reset()
reset函数的主要作用:
1、销毁当前由unique_ptr(如果有)管理的对象;2、取得p的所有权。
如果p是空指针(例如默认初始化指针),则unique_ptr变为空,在调用后不管理任何对象。
reset():重置,显式释放内存资源
reset(new XX):重置,重新指定对象

转移 unique_ptr 对象的所有权

无法复制 unique_ptr 对象,但我们可以转移它们。这意味着 unique_ptr 对象可以将关联的原始指针的所有权转移到另一个 unique_ptr 对象。

std::move将所有权由一个unique_ptr对象转移到另一个unique_ptr对象上

   // 通过原始指针创建 taskPtr2
std::unique_ptr<Task> taskPtr2(new Task(55));
// 把taskPtr2中关联指针的所有权转移给taskPtr4
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 现在taskPtr2关联的指针为空
if(taskPtr2 == nullptr)
	std::cout<<"taskPtr2 is  empty"<<std::endl;

// taskPtr2关联指针的所有权现在转移到了taskPtr4中
if(taskPtr4 != nullptr)
	std::cout<<"taskPtr4 is not empty"<<std::endl;

// 会输出55
std::cout<< taskPtr4->mId << std::endl;

std::move() 将把 taskPtr2 转换为一个右值引用。因此,调用 unique_ptr 的移动构造函数,并将关联的原始指针传输到 taskPtr4。在转移完原始指针的所有权后, taskPtr2将变为空。

release()释放关联的原始指针

在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。

std::unique_ptr<Task> taskPtr5(new Task(55));
// 不为空
if(taskPtr5 != nullptr)
	std::cout<<"taskPtr5 is not empty"<<std::endl;
// 释放关联指针的所有权
Task * ptr = taskPtr5.release();
// 现在为空
if(taskPtr5 == nullptr)
	std::cout<<"taskPtr5 is empty"<<std::endl;

unique_ptr 应用举例

#include <iostream>
#include <memory>

struct Task {
    int mId;
    Task(int id ) :mId(id) {
        std::cout<<"Task::Constructor"<<std::endl;
    }
    ~Task() {
        std::cout<<"Task::Destructor"<<std::endl;
    }
};

int main()
{
    // 空对象 unique_ptr
    std::unique_ptr<int> ptr1;

    // 检查 ptr1 是否为空
    if(!ptr1)
        std::cout<<"ptr1 is empty"<<std::endl;

    // 检查 ptr1 是否为空
    if(ptr1 == nullptr)
        std::cout<<"ptr1 is empty"<<std::endl;

    // 不能通过赋值初始化unique_ptr
    // std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error

    // 通过原始指针创建 unique_ptr,调用Task构造函数
    std::unique_ptr<Task> taskPtr(new Task(23));

    // 检查 taskPtr 是否为空
    if(taskPtr != nullptr)
        std::cout<<"taskPtr is  not empty"<<std::endl;

    // 访问 unique_ptr关联指针的成员
    std::cout<<taskPtr->mId<<std::endl;

    std::cout<<"Reset the taskPtr"<<std::endl;
    // 重置 unique_ptr 为空,将删除关联的原始指针,调用Task析构函数
    taskPtr.reset();

    // 检查是否为空 / 检查有没有关联的原始指针
    if(taskPtr == nullptr)
        std::cout<<"taskPtr is  empty"<<std::endl;

    // 通过原始指针创建 unique_ptr,调用Task构造函数
    std::unique_ptr<Task> taskPtr2(new Task(55));

    if(taskPtr2 != nullptr)
        std::cout<<"taskPtr2 is  not empty"<<std::endl;

    // unique_ptr 对象不能复制
    //taskPtr = taskPtr2; //compile error
    //std::unique_ptr<Task> taskPtr3 = taskPtr2;

    {
        // 转移所有权(把unique_ptr中的指针转移到另一个unique_ptr中)
        std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
        // 转移后为空
        if(taskPtr2 == nullptr)
            std::cout << "taskPtr2 is  empty" << std::endl;
        // 转进来后非空
        if(taskPtr4 != nullptr)
            std::cout<<"taskPtr4 is not empty"<<std::endl;

        std::cout << taskPtr4->mId << std::endl;

        //taskPtr4 超出下面这个括号的作用于将delete其关联的指针
        //离开作用域,,调用Task析构函数
    }
	
    std::unique_ptr<Task> taskPtr5(new Task(66));

    if(taskPtr5 != nullptr)
        std::cout << "taskPtr5 is not empty" << std::endl;

    // 释放所有权
    Task * ptr = taskPtr5.release();

    if(taskPtr5 == nullptr)
        std::cout << "taskPtr5 is empty" << std::endl;

    std::cout << ptr->mId << std::endl;

    delete ptr;

    return 0;
}

share_ptr

share_ptr概念

<memory>头文件
  1. 定义:
    shared_ptr 对象可以与相同的指针相关联,并在内部使用引用计数机制来实现这一点,也就是说它允许多个指针指向同一个对象。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
    1、当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。
    2、当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。
  2. 应用原理
    每个 shared_ptr 对象在内部指向两个内存位置:
    1、指向对象的指针。
    2、用于控制引用计数数据的指针。

share_ptr初始化方法

方法1:
std::shared_ptr<int> p1(new int());
1.通过一个指向堆上申请的空间的指针初始化,上面这行代码在堆上创建了两块内存:1:存储int。2:用于引用计数的内存,管理附加此内存的 shared_ptr 对象的计数,最初计数将为1。
检查 shared_ptr 对象的引用计数
p1.use_count();
注意:
int * a = new int(100);
std::shared_ptr ptr(a); //我们不能写成std::shared_ptr ptr = a,一个对象直接赋给了指针
方法2:
std::shared_ptr<int> p1 = std::make_shared<int>();
std::make_shared 一次性为int对象和用于引用计数的数据都分配了内存
方法3:

std::shared_ptr<int> ptr2(ptr1);\\拷贝初始化

share_ptr重置

当智能指针调用了reset() 函数的时候,就不会再指向这个对象了,所以如果还有其它智能指针指向这个对象,那么另外一个智能指针的use_count()函数结果会减1

#include <iostream>
#include <memory>


using namespace std;
class Person
{
public:
	Person(int v) {
		value = v;
		std::cout << "Person: " << value << std::endl;
	}
	~Person() {
		std::cout << "~Person: " << value << std::endl;
	}

	int value;

};

int main()
{
	std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1

	std::shared_ptr<Person> p2 = std::make_shared<Person>(2);

	p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
							// 最后将新对象的指针交给智能指针

	std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2

	p1.reset();//Person(3)的引用计数为1
	p3.reset();//Person(3)的引用计数为0,析构Person(3)
	return 0;//当我们程序结束进行return的时候,智能指针的引用计数会减1,析构Person(2)
}

在这里插入图片描述
reset包含两个操作:
1、当智能指针中有值的时候,调用reset()会使引用计数减1.
2、当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。

share_ptr获取原始指针

std::shared_ptr<int> ptr(new int(100));
if (ptr.get()) {
    std::cout << "ptr is not null" << std::endl;
} else {
    std::cout << "ptr is null" << std::enel;
}

weak_ptr

 #include <memory>    

1、定义:
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。
注意:
weak_ptr本身也是一个模板类,但是不能直接用它来定义一个智能指针的对象,weak_ptr并不拥有资源的所有权,所以不能直接使用资源。可以从一个weak_ptr构造一个shared_ptr以取得共享资源的所有权。
2、作用:
协助shared_ptr工作,像旁观者那样观测资源的使用情况。
3、应用举例:

#include <iostream>
#include <memory>

using namespace std;

class CB;
class CA
{
public:
	CA() { cout << "CA() called! " << endl; }
	~CA() { cout << "~CA() called! " << endl; }
	void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
	void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
	void show() { cout << "this is class CA!" << endl; }
private:
	shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
	CB() { cout << "CB() called! " << endl; }
	~CB() { cout << "~CB() called! " << endl; }
	void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
	void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
	void show() { cout << "this is class CB!" << endl; }
private:
	weak_ptr<CA> m_ptr_a;
};

void test1()
{
	// 编译错误 // error C2665: “std::weak_ptr<CA>::weak_ptr”: 3 个重载中没有一个可以转换所有参数类型
	// weak_ptr<CA> ptr_1(new CA());

	shared_ptr<CA> ptr_1(new CA());

	cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 1

	shared_ptr<CA> ptr_2 ( ptr_1);

	cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2
	cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2

	weak_ptr<CA> wk_ptr = ptr_1;

	cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2
	cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2

	// 编译错误
	 // error C2440 : “初始化”: 无法从“std::weak_ptr<CA>”转换为“std::shared_ptr<CA>”
	//shared_ptr<CA> ptr_3 = wk_ptr;
}

int main() 
{
	test1();
	return 0;
}

在这里插入图片描述
4、测试weak_ptr常用函数的用法:
在这里插入图片描述


weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象,然后进行后续操作:

#include <iostream>
#include <memory>

using namespace std;

class CB;
class CA
{
public:
	CA() { cout << "CA() called! " << endl; }
	~CA() { cout << "~CA() called! " << endl; }
	void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
	void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
	void show() { cout << "this is class CA!" << endl; }
private:
	shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
	CB() { cout << "CB() called! " << endl; }
	~CB() { cout << "~CB() called! " << endl; }
	void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
	void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
	void show() { cout << "this is class CB!" << endl; }
private:
	weak_ptr<CA> m_ptr_a;
};


void test2()
{
	shared_ptr<CA> ptr_a(new CA());     // 输出:CA() called!
	shared_ptr<CB> ptr_b(new CB());     // 输出:CB() called!

	cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1
	cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1

	weak_ptr<CA> wk_ptr_a = ptr_a;
	weak_ptr<CB> wk_ptr_b = ptr_b;

	if (!wk_ptr_a.expired())
	{
		wk_ptr_a.lock()->show();        // 输出:this is class CA!
	}

	if (!wk_ptr_b.expired())
	{
		wk_ptr_b.lock()->show();        // 输出:this is class CB!
	}

	// 编译错误
	// 编译必须作用于相同的指针类型之间
	// wk_ptr_a.swap(wk_ptr_b);         // 调用交换函数

	wk_ptr_b.reset();                   // 将wk_ptr_b的指向清空
	if (wk_ptr_b.expired())
	{
		cout << "wk_ptr_b is invalid" << endl;  // 输出:wk_ptr_b is invalid 说明改指针已经无效
	}

	wk_ptr_b = ptr_b;
	if (!wk_ptr_b.expired())
	{
		wk_ptr_b.lock()->show();        // 输出:this is class CB! 调用赋值操作后,wk_ptr_b恢复有效
	}

	// 编译错误
	// 编译必须作用于相同的指针类型之间
	// wk_ptr_b = wk_ptr_a;


	// 最后输出的引用计数还是1,说明之前使用weak_ptr类型赋值,不会影响引用计数
	cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1
	cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1
}

int main() 
{
	test2();
	return 0;
}

在这里插入图片描述

weak_ptr总结

1、weak_ptr虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。
2、weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。
3、weak_ptr设计之初就是为了服务于shared_ptr的,所以不增加引用计数就是它的核心功能。
4、由于不知道什么之后weak_ptr所指向的对象就会被析构掉,所以使用之前请先使用expired函数检测一下。

循环引用

定义两个类,每个类中又包含一个指向对方类型的智能指针作为成员变量

在这里插入图片描述

#include <iostream>
#include <memory>

using namespace std;

class CB;
class CA
{
public:
	CA() { cout << "CA() called! " << endl; }
	~CA() { cout << "~CA() called! " << endl; }
	void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
	void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
	void show() { cout << "this is class CA!" << endl; }
private:
	shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
	CB() { cout << "CB() called! " << endl; }
	~CB() { cout << "~CB() called! " << endl; }
	void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
	void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
	void show() { cout << "this is class CB!" << endl; }
private:
	shared_ptr<CA> m_ptr_a;
};

void test_refer_to_each_other()
{
	shared_ptr<CA> ptr_a(new CA());
	shared_ptr<CB> ptr_b(new CB());

	cout << "a use count : " << ptr_a.use_count() << endl;
	cout << "b use count : " << ptr_b.use_count() << endl;

	ptr_a->set_ptr(ptr_b);
	ptr_b->set_ptr(ptr_a);

	cout << "a use count : " << ptr_a.use_count() << endl;
	cout << "b use count : " << ptr_b.use_count() << endl;
}

int main() 
{
	test_refer_to_each_other();
	return 0;
}

在这里插入图片描述
通过结果可以看到,最后CA和CB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a和ptr_b时,只有①③两条引用,然后调用函数set_ptr后又增加了②④两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,也就是①③两条引用会被断开,但是②④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。
在这里插入图片描述
解决办法:
就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:

#include <iostream>
#include <memory>

using namespace std;

class CB;
class CA
{
public:
	CA() { cout << "CA() called! " << endl; }
	~CA() { cout << "~CA() called! " << endl; }
	void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
	void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
	void show() { cout << "this is class CA!" << endl; }
private:
	shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
	CB() { cout << "CB() called! " << endl; }
	~CB() { cout << "~CB() called! " << endl; }
	void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
	void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
	void show() { cout << "this is class CB!" << endl; }
private:
	weak_ptr<CA> m_ptr_a;
};


void test_refer_to_each_other()
{
	shared_ptr<CA> ptr_a(new CA());
	shared_ptr<CB> ptr_b(new CB());

	cout << "a use count : " << ptr_a.use_count() << endl;
	cout << "b use count : " << ptr_b.use_count() << endl;

	ptr_a->set_ptr(ptr_b);
	ptr_b->set_ptr(ptr_a);

	cout << "a use count : " << ptr_a.use_count() << endl;
	cout << "b use count : " << ptr_b.use_count() << endl;
}

int main() 
{
	test_refer_to_each_other();
	return 0;
}

在这里插入图片描述
CA和CB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,也就是①③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。
在这里插入图片描述

智能指针的设计

智能指针类将一个计数器类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。
智能指针就是模拟指针动作的类,还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。

  • 每次创建类的新对象时,初始化指针并将引用计数置为1;
  • 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
  • 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;
  • 调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
  • 智能指针都会重载 -> 和 * 操作符
#include <iostream>
#include <memory>

using namespace std;

template<typename T>
class SmartPointer {
private:
	T* _ptr;
	size_t* _count;
public:
	SmartPointer(T* ptr = nullptr) :_ptr(ptr) //构造函数
	{
		cout << "SmartPointer Constructor: " << endl;
		if (_ptr) {
			_count = new size_t(1);
		}
		else {
			_count = new size_t(0);
		}
	}
	//当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
	SmartPointer(const SmartPointer& ptr) //拷贝构造函数
	{
		cout << "SmartPointer Copy Constructor: " << endl;
		if (this != &ptr) {
			this->_ptr = ptr._ptr;
			this->_count = ptr._count;
			(*this->_count)++;
		}
	}

	SmartPointer& operator=(const SmartPointer& ptr) //= 重载
	{
		cout << "SmartPointer operator=: " << endl;
		if (this->_ptr == ptr._ptr) {//避免相同对象重复赋值
			return *this;
		}
		// 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数
		if (this->_ptr) {
			(*this->_count)--;
			if (this->_count == 0) {
				delete this->_ptr;
				delete this->_count;
			}
		}
		//增加右操作数所指对象的引用计数
		this->_ptr = ptr._ptr;
		this->_count = ptr._count;
		(*this->_count)++;
		return *this;
	}

	T& operator*() {
		assert(this->_ptr == nullptr);
		return *(this->_ptr);

	}

	T* operator->() {
		assert(this->_ptr == nullptr);
		return this->_ptr;
	}

	~SmartPointer() 
	{
		cout << "~SmartPointer Destruction: " << endl;
		(*this->_count)--;
		if (*this->_count == 0) {
			delete this->_ptr;
			delete this->_count;
		}
	}
	//计数器
	size_t use_count() {
		return *this->_count;
	}
};

int main() {
		SmartPointer<int> sp(new int(10));//普通构造
		SmartPointer<int> sp2(sp);//拷贝构造
		std::cout << sp.use_count() << std::endl;//输出 2
		SmartPointer<int> sp3(new int(20));//普通构造
		sp2 = sp3;//对一个对象进行赋值,sp计数减一,sp3计数加1
		std::cout << sp.use_count() << std::endl;//输出 1
		std::cout << sp3.use_count() << std::endl;//输出 2
		return 0;
}

在这里插入图片描述

参考

1、深入理解C++11:C++11新特性解析与应用
2、https://blog.csdn.net/albertsh/article/details/82286999
3、https://www.cnblogs.com/wxquare/p/4759020.html

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值