关于智能指针(更新中)

前言        

        在Java中,其自带垃圾回收机制,但c++不同。c++必须释放掉分配的内存,否则会造成内存泄露。由此c++11引入了智能指针。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能确保在离开指针所在作用域使,自动的销毁分配的对象,防止内存泄漏。智能指针的核心技术是引用计数,每使用它一次内部引用计数+1,每析构一次引用计数-1,减为0时,删除原始指针指向的堆区内存。

注:使用智能指针要引入头文件<memory>

  • std::shared_ptr:共享的智能指针
  • std::unique_ptr:独占的智能指针
  • std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的,解决循环引用问题。

share_ptr(共享智能指针)

(1)初始化

        共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针share_ptr 是一个模板类,如果进行初始化有三种方式如下:

  • 通过构造函数初始化
  • std::make_shared 辅助函数
  • reset 方法

        共享智能指针对象初始化完毕之后就指向了要管理的那块堆区内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数use_count.

1.构造函数初始化

        使用智能指针管理一块int型的堆内存

shared_ptr<int>ptr1(new int(520));
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;

shared_ptr<char>ptr2(new char[520]);
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
shared_ptr<int>ptr3;
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;

shared_ptr<int>ptr4(nullptr);
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

运行结果:

        如果智能指针被初始化了一块有效内存,那么这块内存的引用计数+1,如果智能指针没有被初始化或者被初始化为 nullptr 空指针,引用计数为 0。另外,不要使用一个原始指针初始化多人 shared_ptr。

2.拷贝和移动构造函数初始化

        当一个智能指针被初始化之后,就可以通过这个智能指针初始化其他新对象。在创建对象的时候,对应的拷贝构造函数或者移动构造函数就被调用了。

//构造函数
	shared_ptr<int>ptr1(new int(520));
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;

	//拷贝构造函数
	shared_ptr<int>ptr2(ptr1);
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
	shared_ptr<int>ptr3 = ptr1;
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;

	//移动构造函数
	shared_ptr<int>ptr4(std::move(ptr1));
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;
	shared_ptr<int>ptr5 = std::move(ptr2);
	cout << "ptr5管理的内存引用计数:" << ptr5.use_count() << endl;

3.std::make_shared初始化

通过c++11提供的std::make_shared()就可以完成内存对象的创建并将其初始化给智能指针

class Test {
public:
	Test() {
		cout << "无参构造函数" << endl;
	}
	Test(int x) {
		cout << "int型构造函数" << endl;
	}
	Test(string str) {
		cout << "string型参构造函数" << endl;
	}
	~Test() {
		cout << "析构函数" << endl;
	}
};
int main() {
	//使用智能指针管理一块int型内存,内部引用计数为1
	shared_ptr<int>ptr1 = make_shared<int>(520);
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;

	shared_ptr<Test>ptr2 = make_shared<Test>();
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;

	shared_ptr<Test>ptr3 = make_shared<Test>(520);
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;

	shared_ptr<Test>ptr4= make_shared<Test>("QQQQ");
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

	return 0;
}

        如果使用拷贝的方式初始化共享智能指针,这两个对象会同时管理同一块内存,堆内存对应的引用技术也会增加。如果使用移动构造的方式初始化智能指针对象,只是转让了内存的所有权,管理内存的对象不会增加,因此内存引用技术不会增加。

4.rest方法初始化
int main() {
	//使用智能指针管理一块int型内存,内部引用计数为1
	shared_ptr<int>ptr1 = make_shared<int>(520);
	shared_ptr<int>ptr2 = ptr1;
	shared_ptr<int>ptr3 = ptr1;
	shared_ptr<int>ptr4 = ptr1;
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

	cout << "reset后:" << endl;
	ptr4.reset();
	cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;
	cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
	cout << "ptr3管理的内存引用计数:" << ptr3.use_count() << endl;
	cout << "ptr4管理的内存引用计数:" << ptr4.use_count() << endl;

	shared_ptr<int>ptr5;
	ptr5.reset(new int(250));
	cout << "ptr5管理的内存引用计数:" << ptr5.use_count() << endl;
	return 0;
}

        对于一个未初始化的共享智能指针,可以通过 reset 方法来初始化,当智能指针中有值得时候,调用 reset 会使引用计数减 1

(2)获取原始指针

get()函数

int main() {
	shared_ptr<int>p(new int);
	*p = 100;
	cout << *p.get() << " " << *p << endl;
	return 0;
}

unique_ptr(独占智能指针)

(1)初始化

        std::unique_ptr是一个独占型指针,他不允许其他智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针,但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。

int main() {
	//通过构造函数初始化对象
	unique_ptr<int>ptr1(new int(10));
	 
	//下面这个将一个 unique_ptr 赋值给另一个 unique_ptr,报错
	//unique_ptr<int>ptr2 = ptr1;
	return 0;
}

        unique_ptr 不允许被复制,但是可以通过函数返回给其他的 unique_ptr,还可以通过std::move()转移给其他的 unique_ptr。还是一个 unique_ptr 独占一个地址。

(2)reset()

        使用reset方法可以让unique_ptr 解除对原始内存的管理,也可以用来初始化一个独占的智能指针。

int main() {
	//通过构造函数初始化对象
	unique_ptr<int>ptr1(new int(10));
	 
	unique_ptr<int>ptr2;
	ptr1.reset();//解除对原始内存的管理
	ptr2.reset(new int(250));//重新指定智能指针管理的原始内存
	return 0;
}

如果要获取独占智能指针的原始地址,可以调用get()方法.

int main() {
	//通过构造函数初始化对象
	unique_ptr<int>ptr1(new int(10));
	 
	unique_ptr<int>ptr2 = move(ptr1);

	ptr2.reset(new int(250));
	cout << *ptr2.get() << endl;//得到内存地址中存储的实际数据值250
	return 0;
}

weak_ptr(弱引用智能指针)

        弱引用智能指针std::weak_ptr 可以看做是shared_ptr 的助手,它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数。它的主要作用就是作为一个旁观者监视 shared_ptr 中管理的资源是否存在

(1)初始化方式

int main() {
	shared_ptr<int>sp(new int);

	//weak_ptr<int>wp1;构造了一个空的weak_ptr对象
	weak_ptr<int>wp1;

	//weak_ptr<int>wp2;通过一个空的weak_ptr对象构造另一个空weak_prt实例对象
	weak_ptr<int>wp2(wp1);
	
	//weak_ptr<int>wp3;通过一个空的shared_ptr对象构造一个可用的weak_prt实例对象
	weak_ptr<int>wp3(sp);

	//weak_ptr<int>wp4;通过一个shared_ptr对象构造一个可用的weak_ptr实例对象(这是一个隐式类型转换)
	weak_ptr<int>wp4;
	wp4 = sp;

	//wp5 = wp3;通过一个weak_ptr对象构造一个可用的weak_ptr实例对象
	weak_ptr<int>wp5;
	wp5 = wp3;

	return 0;
}

(2)use_count()

        通过调用std::weak_ptr 类提供的use_count()方法可以获得当前所观测源的引用计数。

int main() {
	shared_ptr<int>sp(new int);

	//weak_ptr<int>wp1;构造了一个空的weak_ptr对象
	weak_ptr<int>wp1;

	//weak_ptr<int>wp2;通过一个空的weak_ptr对象构造另一个空weak_prt实例对象
	weak_ptr<int>wp2(wp1);
	
	//weak_ptr<int>wp3;通过一个空的shared_ptr对象构造一个可用的weak_prt实例对象
	weak_ptr<int>wp3(sp);

	//weak_ptr<int>wp4;通过一个shared_ptr对象构造一个可用的weak_ptr实例对象(这是一个隐式类型转换)
	weak_ptr<int>wp4;
	wp4 = sp;

	//wp5 = wp3;通过一个weak_ptr对象构造一个可用的weak_ptr实例对象
	weak_ptr<int>wp5;
	wp5 = wp3;

	cout << "ues_count:" << endl;
	cout << "wp1:" << wp1.use_count() << endl;
	cout << "wp2:" << wp2.use_count() << endl;
	cout << "wp3:" << wp3.use_count() << endl;
	cout << "wp4:" << wp4.use_count() << endl;
	cout << "wp5:" << wp5.use_count() << endl;

	return 0;
}

        通过打印的结果可以知道,虽然弱引用智能指针 wp3、wp4、wp5 监测的资源是同一个,但是它的引用计数并没有发生任何的变化,也进一步证明了 weak_ptr 只是监测资源,并不管理资源. 

(3)expired()

通过调用std::weak_ptr 类提供的expired()方法来判断观测的资源是否已经被释放。

int main() {
	shared_ptr<int>shared(new int(10));
	weak_ptr<int>weak(shared);
	cout << "1.weak:" << (weak.expired() ? "is" : "is not") << "expired" << endl;

	shared.reset();
	cout << "2.weak:" << (weak.expired() ? "is" : "is not") << "expired" << endl;

	return 0;
}

         weak ptr 监测的就是 shared_ptr 管理的资源当共享智能指针调用 shared.reset(); 之后管理的资源被释放,因此 weak.expired() 函数的结果返回 true,表示监测的资源已经不存在了

(4)lock()

通过调用std::weak_ptr 类提供的lock()方法来获取管理所监测资源的shared_ptr 对象

int main() {
	shared_ptr<int>sp1, sp2;
	weak_ptr<int>wp;

	sp1 = std::make_shared <int>(520);
	wp = sp1;
	sp2 = wp.lock();
	cout << "use_count:" << wp.use_count() << endl;

	sp1.reset();
	cout << "use_cout:" << wp.use_count() << endl;

	sp1 = wp.lock();
	cout << "use_coutn:" << wp.use_count() << endl;

	cout << "*sp1:" << *sp1 << endl;
	cout << "*sp2:" << *sp2 << endl;

	return 0;
}

  • sp2 = wp.lock(); 通过调用 lock() 方法得到一个用于管理 weak_ptr 对象所监测的资源的共享智能指针对象,使用这个对象初始化 sp2,此时所监测资源的用计数为 2。
  • sp1.reset();共享智能指针 sp1 被重置,weak_ptr 对象所监测的资源的引用计数减1。
  • sp1 = wp.lock(); sp1 重新被初始化,并且管理的还是 weak ptr 对象所监测的资源,因此引用计数加 1。
  • 共享智能指针对象 sp1 和 sp2 管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是 520。

(5)reset()

        通过调用std::weak_ptr 类提供的reset()方法来清空对象,使其不监测任何资源。

int main() {
	shared_ptr<int>shared(new int(10));
	weak_ptr<int>weak(shared);
	cout << "1.weak:" << (weak.expired() ? "is" : "is not") << "expired" << endl;

	weak.reset();
	cout << "2.weak:" << (weak.expired() ? "is" : "is not") << "expired" << endl;
	return 0;
}

(6)返回管理this的share_ptr

struct Test {
	shared_ptr<Test>getSharedPtr() {
		return shared_ptr<Test>(this);
	}
	~Test() {
		cout << "析构函数" << endl;
	}
};
int main() {
	shared_ptr<Test>sp1(new Test);
	cout << "引用个数:" << sp1.use_count() << endl;
	shared_ptr<Test>sp2 = sp1->getSharedPtr();
	cout << "引用计数:" << sp1.use_count() << endl;
	return 0;
}

 报错:

        通过输出的结果可以看到一个对象被析构了两次,其原因是这样的: 在这个例子中使用同一个指this 构造了两个智能指针对象 sp1 和 sp2,这二者之间是没有任何关系的,因为 sp2 并不是通过sp1 初始化得到的实例对象。在离开作用域之后 this 将被构造的两个智能指针各自析构,导致重复析构的错误。

改正:
struct Test :public enable_shared_from_this<Test> {
	shared_ptr<Test>getSharePtr() {
		return shared_from_this();
	}
	~Test() {
		cout << "析构函数" << endl;
	}
};
int main() {
	shared_ptr<Test>sp1(new Test);
	cout << "引用个数:" << sp1.use_count() << endl;
	shared_ptr<Test>sp2 = sp1;
	cout << "引用计数:" << sp1.use_count() << endl;
	return 0;
}

(7)循环引用问题

class A;
class B;

class A {
public:
	shared_ptr<B>bptr;
	~A() {
		cout << "class TA is distruct ..." << endl;
	}
};
class B {
public:
	shared_ptr<A>aptr;
	~B() {
		cout << "class TB is distruct ..." << endl;
	}
};

void testPtr() {
	shared_ptr<A>ap(new A);
	shared_ptr<B>bp(new B);
	cout << "A的引用计数:" << ap.use_count() << endl;
	cout << "Bde 引用计数:" << bp.use_count() << endl;

	ap->bptr = bp;
	bp->aptr = ap;
	cout << "A的引用计数2:" << ap.use_count() << endl;
	cout << "Bde 引用计数2:" << bp.use_count() << endl;
}
int main() {
	testPtr();
	return 0;
}

         共享智能指针 ap、bp 对 A、B 实例对象的用计数变为 2,在共享智能指针离开作用域之后引用计数只能减为 1,这种情况下不会去删除智能指针管理的内存,导致类 A、B 的实例对象不能被析构,最终造成内存泄露。通过使用 weak_ptr 可以解决这个问题,只需要将类 A 或者 B 的任意一个成员改为 weak_ptr.

class A;
class B;

class A {
public:
	weak_ptr<B>bptr;
	~A() {
		cout << "A的析构函数" << endl;
	}
};
class B {
public:
	shared_ptr<A>aptr;
	~B() {
		cout << "B的析构函数." << endl;
	}
};

void testPtr() {
	shared_ptr<A>ap(new A);
	shared_ptr<B>bp(new B);
	cout << "A的引用计数:" << ap.use_count() << endl;
	cout << "Bde 引用计数:" << bp.use_count() << endl;

	ap->bptr = bp;
	bp->aptr = ap;
	cout << "A的引用计数2:" << ap.use_count() << endl;
	cout << "Bde 引用计数2:" << bp.use_count() << endl;
}
int main() {
	testPtr();
	return 0;
}

        上面程序中,在对类A成员赋值时 ap->bptr = bp;由于 bptr 是 weak_ptr 类型,这个赋值操作并不会增加引用计数,所以bp 的引用计数仍然为 1,在离开作用域之后 bp 的引用计数减为0,类B的实例对象被析构。

        在类B的实例对象被析构的时候,内部的 aptr 也被析构,其对A对象的管理解除,内存的引用计数减为1,当共享智能指针ap 离开作用域之后,对A对象的管理也解除了,内存的引用计数减为0,类A的实例对象被析构。

share_ptr实现

共享智能指针需要的方法:

  • 无参构造,传递指针构造,拷贝构造,移动构造,拷贝赋值,移动赋值
  • reset () 替换对象 reset () 销毁对象
  • operator*() operator->()
  • get() 获取原始指针
  • use_count 获得引用计数
#pragma once
#include <iostream>
using namespace std;

template<class T>
class Ref {
	int r_count = 0;//当前资源数计数

	T* object = nullptr;//被社区管理的堆区内存
public:
	Ref(T *target) :object(target) {
		r_count++;
	}

	//引用计数加1
	inline void increase() {
		r_count++;
	}

	//引用计数减一并判断是否释放掉原始堆区内存
	inline void reduce() {
		r_count--;//引用计数-1
		if (r_count == 0)//如果引用计数减为0 释放掉被管理的堆区内存和自己
		{
			delete object;
			delete this;
		}
	}
	T *get() {
		return object;
	}

	int getCount() {
		return r_count;
	}
};


//共享智能指针需要的方法:
/*
	无参构造,传递指针构造,拷贝构造,移动构造,拷贝赋值,移动赋值
	reset () 替换对象 reset () 销毁对象
	operator*()
	operator->()
	get() 获取原始指针
	use_count 获得引用计数
*/
template<class T>
class Share_ptr {
	Ref<T>* ref = nullptr;
public:
	Share_ptr() {
		if (ref) ref->reduce();//引用计数减一
	}
	Share_ptr(T *newP) {
		cout << "----------------调用构造函数-----------------" << endl;
		ref = new Ref<T>(newP);
	}
	Share_ptr(const Share_ptr &other) {
		cou << "------------------调用拷贝构造---------" << endl;
		this->ref = other.ref;
		if (ref) ref->increase();//引用计数+1
	}
	Share_ptr(Share_ptr &&other) {
		cout << "----------------调用移动构造构造函数-----------------" << endl;
		ref = other.ref;
		other.ref = nullptr;
	}
	Share_ptr& operator=(const Share_ptr &other) {
		cout << "----------------调用赋值函数-----------------" << endl;
		if (ref) {
			ref->reduce();
		}
		ref = other.ref;
		if (ref) {
			ref->increase();
		}
		return *this;
	}
	Share_ptr& opatror = (Share_ptr &&other) {
		cout << "----------------调用移动赋值函数-----------------" << endl;
		if (ref) {
			ref->reduce();
		}
		ref = other.ref;
		other.ref=nullptr;
		return *this;
	}

	void reset(T* target) {
		if (ref) {
			ref->reduce();
		}
		ref = new Ref<T>(target);
	}
	void reset() {
		if (ref) {
			ref->reduce();
		}
		ref = nullptr;
	}

	T& operator*() {
		return *ref->get();
	}
	T& operator->() {
		if(ret) return ref->get();
	}
	int use_count() {
		if (ret) return ref->getCount();
		else return 0;
	}
};
  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值