c++11总结12——四个智能指针:shared_ptr、uinque_ptr、weak_ptr和auto_ptr

前言

智能指针的作用是管理一个指针。申请的空间在函数结束时忘记释放,就会造成内存泄漏。使用智能指针可以很大程度上避免这个问题,因为智能指针是一个类,当超出类的作用域时,类会自动调用析构函数来释放资源。所以智能指针的原理是在函数结束时自动释放内存空间,而不需要手动释放内存空间。

1. unique_ptr

1.1 独享管理对象所有权

unique_ptr独享被管理对象指针的所有权

unique_ptr对象包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。

一个错误的例子(不允许直接复制):

std::unique_ptr<string> p1(new string("c++"));
std::unique_ptr<string> p2;
p2 = p1;  //error

一个正确的例子:

class A {
public:
	A(int id) :mId(id) {
		cout << "A::A" << endl;
	}

	~A() {
		cout << "~A::A" << endl;
	}

	int getId() const
	{
		return mId;
	}
private:
	int mId;
};

int main()
{
	{
		std::unique_ptr<A> pA(new A(1));
		cout << pA->getId() << endl;
	}

	system("pause");
    return 0;
}

打印如下:

std::unique<A>的对象pA接收原始指针作为参数。当出作用域(正常退出或异常退出)时,在pA的析构函数中,调用类A(原始指针)的析构函数,删除关联的原始指针,这样就不用手动删除原始指针了。

1.2 创建对象——make_unique

auto pA = std::make_unique<A>(1);

1.3 获取被管理对象的原始指针

A* a1 = pA.get();

1.4 重置unique_ptr对象

调用reset()函数,将释放相关联的原始指针并使unique_ptr对象为空

std::unique_ptr<A> pA = std::make_unique<A>(1);
//A* a1 = pA.get();
pA.reset();

if (nullptr == pA)
{
	cout << "pA is NULL" << endl;
}

1.5 转移unique_ptr的控制权

std::unique_ptr<A> pA = std::make_unique<A>(1);
std::unique_ptr<A> pB = std::move(pA);

if (nullptr == pA)
	cout << "pA is NULL" << endl;

if(nullptr != pB)
	cout << "pB is not NULL" << endl;

cout << "pB->getId() is " << pB->getId() << endl;

利用std::move将pA的控制权转移给pB,转移后pA的指针为空。

1.6 释放关联的原始指针

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

std::unique_ptr<A> pA = std::make_unique<A>(1);
A* a1 = pA.release();

if (nullptr == pA)
{
	cout << "pA is NULL" << endl;
}

2. shared_ptr

shared_ptr是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象。利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个shared_ptr共同管理同一个对象。

每个shared_ptr对象在内部指向两个内存位置:

1)指向对象的指针;

2)用于控制引用计数数据的指针;

共享所有权如何在参考计数的帮助下工作:
1)当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。

2)当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。

2.1 使用原始指针创建shared_ptr对象

方法1:

std::shared_ptr<int> p1(new int());
cout << "p1.use_count() = " << p1.use_count() << endl;  //1

方法2:

auto p1 = std::make_shared<int>();
cout << "p1.use_count() = " << p1.use_count() << endl;  //1

std::make_shared 一次性为int对象和用于引用计数的数据都分配了内存,而new操作符只是为int分配了内存

2.2 分离原始指针

auto p1 = std::make_shared<int>();
cout << "p1.use_count() = " << p1.use_count() << endl;  //1

p1.reset();
cout << "p1.use_count() = " << p1.use_count() << endl;  //0

执行reset后引用计数会减一,如果引用计数为0,则删除指针。

auto p1 = std::make_shared<int>();
cout << "p1.use_count() = " << p1.use_count() << endl;  //1

p1.reset(new int(1));
cout << "p1.use_count() = " << p1.use_count() << endl;  //1

p1 = nullptr;
cout << "p1.use_count() = " << p1.use_count() << endl;  //0

这种情况下,它将在内部指向新指针,因此其引用计数将再次变为1。p1置为nullptr,引用计数变为0。

2.3 自定义删除器Deleter

1)回调函数

shared_ptr的析构函数中删除内部原始指针,默认调用的delete()函数。当我们需要析构delete[]时,需要自己设计回调函数删除。

class A
{
public:
	A() {
		cout << "A" << endl;;
	}
	~A() {
		cout << "~A" << endl;;
	}
};

void deleter(A *p)
{
	cout << "DELETER FUNCTION CALLED" << endl;
	delete[] p;
}

int main(){
   
   std::shared_ptr<A> pA(new A[3], deleter);

}

打印结果:

2)使用函数对象作为删除器

class Deleter
{
public:
	void operator() (A *p) {
		std::cout << "DELETER FUNCTION CALLED\n";
		delete[] p;
	}
};

int main()
{
    std::shared_ptr<A> p3(new A[3], Deleter());
}

3)使用lamba作为删除器(该方式较简洁)

std::shared_ptr<A> p4(new A[3], [](A * x) {
	std::cout << "DELETER FUNCTION CALLED\n";
	delete[] x;
});

2.4 NULL检测

不分配值的情况下默认为nullptr。

std::shared_ptr<int> p1;

if (nullptr == p1)
{
	cout << "p1 is NULL" << endl;
}

2.5 常见问题

2.5.1 不要使用同一个原始指针构造 shared_ptr

int *num = new int(10);
std::shared_ptr<int> p1(num);

std::shared_ptr<int> p2(p1);  // 正确使用方法
std::shared_ptr<int> p3(num); // 不推荐

std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 输出 2
std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 输出 2
std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 输出 1

上面的代码实际测试会崩溃。原因在于,使用原始指针num创建了p1,又同样方法创建了p3,当p1超出作用域时会调用delete释放num内存,此时num成了悬空指针,当p3超出作用域再次delete的时候就可能会出错。

2.5.2 不要用栈中的指针构造 shared_ptr 对象

int x = 12;
std::shared_ptr<int> ptr(&x);

上面的代码在vs环境下运行会崩溃。原因在于,shared_ptr 默认的构造函数中使用的是delete来删除关联的指针,所以构造的时候也必须使用new出来的堆空间的指针。

2.5.3 建议使用make_shared()

auto p1 = make_shared<int>();
std::shared_ptr<int> p2(p1);

3. weak_ptr

3.1 一个循环引用的例子

class ClassB;

class ClassA
{
public:
	ClassA() { cout << "ClassA Constructor..." << endl; }
	~ClassA() { cout << "ClassA Destructor..." << endl; }
	shared_ptr<ClassB> pb;  // 在A中引用B
};

class ClassB
{
public:
	ClassB() { cout << "ClassB Constructor..." << endl; }
	~ClassB() { cout << "ClassB Destructor..." << endl; }
	shared_ptr<ClassA> pa;  // 在B中引用A
};

int main()
{
	{
		shared_ptr<ClassA> spa = make_shared<ClassA>();
		shared_ptr<ClassB> spb = make_shared<ClassB>();
		spa->pb = spb;
		spb->pa = spa;
	}

	system("pause");
    return 0;
}

打印结果:

3.2 weak_ptr的概念

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放

3.3 weak_ptr创建

std::shared_ptr<int> sp(new int(5));
cout << "创建前sp的引用计数:" << sp.use_count() << endl;    // 1

std::weak_ptr<int> wp(sp);
cout << "创建后sp的引用计数:" << sp.use_count() << endl;    // 1

3.4 判断weak_ptr指向对象是否存在

C++11中通过lock()函数判断weak_ptr指向的对象是否存在。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr。

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}
	~A() 
	{
		cout << "~A()" << endl; 
	}
};

int main()
{
	{
		std::shared_ptr<A> sp(new A());
		std::weak_ptr<A> wp(sp);
		sp.reset();

		std::shared_ptr<A> p1 = wp.lock();
		if (nullptr == p1)
		{
			cout << "wp指向的对象为空" << endl;
		}
	}
	

	system("pause");
    return 0;
}

3.5 weak_ptr的使用

class ClassB;

class ClassA
{
public:
	ClassA() { cout << "ClassA Constructor..." << endl; }
	~ClassA() { cout << "ClassA Destructor..." << endl; }
	std::weak_ptr<ClassB> pb;  // 在A中引用B
};

class ClassB
{
public:
	ClassB() { cout << "ClassB Constructor..." << endl; }
	~ClassB() { cout << "ClassB Destructor..." << endl; }
	std::weak_ptr<ClassA> pa;  // 在B中引用A
};

int main()
{

	{
		shared_ptr<ClassA> spa = make_shared<ClassA>();
		shared_ptr<ClassB> spb = make_shared<ClassB>();
		spa->pb = spb;
		spb->pa = spa;
	}
	

	system("pause");
    return 0;
}

打印结果:

猜你喜欢

转载自blog.csdn.net/www_dong/article/details/113850156
今日推荐