在本文中,我们将讨论 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。

#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()***方法维护一个*引用计数器。

#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。在这种情况下,指针在对象上不会有据点。原因是如果假设指针持有对象并请求其他对象,那么它们可能会形成死锁。
