C++ 中的智能指针以及如何使用它们

在本文中,我们将讨论 C++ 中的智能指针。什么是智能指针,为什么以及如何正确使用它们?

指针用于访问程序外部的资源——比如堆内存。因此,为了访问堆内存(如果在堆内存中创建了任何东西),需要使用指针。当访问任何外部资源时,我们只使用资源的副本。如果我们对其进行任何更改,我们只需在复制的版本中进行更改。但是,如果我们使用指向资源的指针,我们将能够更改原始资源。

普通指针的问题

看看下面的代码。

#include <iostream>
using namespace std;

class Rectangle {
private:
	int length;
	int breadth;
};

void fun()
{
	// By taking a pointer p and
	// dynamically creating object
	// of class rectangle
	Rectangle* p = new Rectangle();
}

int main()
{
	// Infinite Loop
	while (1) {
		fun();
	}
}

在函数fun中,它创建一个指向Rectangle对象的指针。对象Rectangle包含两个整数,长度宽度。当函数fun结束时, p 将被销毁,因为它是一个局部变量。但是,它消耗的内存不会被释放,因为我们忘记使用*delete p;*在函数的最后。这意味着内存不会被其他资源免费使用。但是,我们不再需要变量,但我们需要内存。

在函数main中,fun在无限循环中被调用。这意味着它将继续创建p。它会分配越来越多的内存,但不会释放它们,因为我们没有释放它。被浪费的内存不能再使用了。这是内存泄漏。由于这个原因,整个内存可能变得无用。C++11 提出了解决这个问题的方法,Smart Pointer。

智能指针介绍

正如我们所知道的,无意识地不释放指针会导致内存泄漏,从而可能导致程序崩溃。语言 Java、C# 具有垃圾收集机制,可以巧妙地释放未使用的内存以再次使用。程序员不必担心任何内存泄漏。C++11 提出了自己的机制,即Smart Pointer。当对象被销毁时,它也会释放内存。所以,我们不需要删除它,因为 Smart Pointer 会处理它。

智能指针是指针上的包装类,其中重载了 * 和 -> 等运算符。智能指针类的对象看起来像普通指针。但是,与普通指针不同,它可以解除分配和释放被破坏的对象内存。

这个想法是采用一个带有指针、析构函数重载运算符(如 * 和 ->)的类。由于当对象超出范围时会自动调用析构函数,因此动态分配的内存将被自动删除(或者引用计数可以减少)。考虑以下简单的SmartPtr类。

#include <iostream>
using namespace std;

class SmartPtr {
	int* ptr; // Actual pointer
public:
	// Constructor: Refer https:// www.geeksforgeeks.org/g-fact-93/
	// for use of explicit keyword
	explicit SmartPtr(int* p = NULL) { ptr = p; }

	// Destructor
	~SmartPtr() { delete (ptr); }

	// Overloading dereferencing operator
	int& operator*() { return *ptr; }
};

int main()
{
	SmartPtr ptr(new int());
	*ptr = 20;
	cout << *ptr;

	// We don't need to call delete ptr: when the object
	// ptr goes out of scope, the destructor for it is automatically
	// called and destructor does delete ptr.

	return 0;
}

输出:

20

这仅适用于int。那么,我们必须为每个对象创建智能指针吗?不,有一个解决方案,模板。在下面的代码中,您可以看到T可以是任何类型。在此处阅读有关模板的更多信息。

#include <iostream>
using namespace std;

// A generic smart pointer class
template <class T>
class SmartPtr {
	T* ptr; // Actual pointer
public:
	// Constructor
	explicit SmartPtr(T* p = NULL) { ptr = p; }

	// Destructor
	~SmartPtr() { delete (ptr); }

	// Overloading dereferencing operator
	T& operator*() { return *ptr; }

	// Overloading arrow operator so that
	// members of T can be accessed
	// like a pointer (useful if T represents
	// a class or struct or union type)
	T* operator->() { return ptr; }
};

int main()
{
	SmartPtr<int> ptr(new int());
	*ptr = 20;
	cout << *ptr;
	return 0;
}

输出:

20

**注意:**智能指针在资源管理中也很有用,例如文件句柄或网络套接字。

智能指针的类型

1.unique_ptr

unique_ptr只存储一个指针。我们可以通过从指针中删除当前对象来分配不同的对象。注意下面的代码。首先,unique_pointer指向P1。但是,然后我们删除P1并分配P2,因此指针现在指向P2

unique_ptr

#include <iostream>
using namespace std;
#include <memory>

class Rectangle {
	int length;
	int breadth;

public:
	Rectangle(int l, int b){
		length = l;
		breadth = b;
	}

	int area(){
		return length * breadth;
	}
};

int main(){

	unique_ptr<Rectangle> P1(new Rectangle(10, 5));
	cout << P1->area() << endl; // This'll print 50

	// unique_ptr<Rectangle> P2(P1);
	unique_ptr<Rectangle> P2;
	P2 = move(P1);

	// This'll print 50
	cout << P2->area() << endl;

	// cout<<P1->area()<<endl;
	return 0;
}

输出:

50
50

2.shared_ptr

通过使用shared_ptr多个指针一次可以指向这个对象,并且它将使用use_count()***方法维护一个*引用计数器。

shared_ptr

#include <iostream>
using namespace std;
#include <memory>

class Rectangle {
	int length;
	int breadth;

public:
	Rectangle(int l, int b)
	{
		length = l;
		breadth = b;
	}

	int area()
	{
		return length * breadth;
	}
};

int main()
{

	shared_ptr<Rectangle> P1(new Rectangle(10, 5));
	// This'll print 50
	cout << P1->area() << endl;

	shared_ptr<Rectangle> P2;
	P2 = P1;

	// This'll print 50
	cout << P2->area() << endl;

	// This'll now not give an error,
	cout << P1->area() << endl;

	// This'll also print 50 now
	// This'll print 2 as Reference Counter is 2
	cout << P1.use_count() << endl;
	return 0;
}

输出:

50
50
50
2

3.weak_ptr

它与 shared_ptr 更相似,只是它不会维护Reference Counter。在这种情况下,指针在对象上不会有据点。原因是如果假设指针持有对象并请求其他对象,那么它们可能会形成死锁。

弱点


版权声明:本文为huihuige092原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。