26. 智能指针
一、什么是智能指针
当我们使用 new 关键字为指针变量动态申请内存时,但是使用完毕之后,我们可能会忘记使用 delete 关键字手动回收内存。因此,C++ 中提供了智能指针。当智能指针过期时,其析构函数将使用 delete 来释放内存。因此,如果将 new 返回的地址赋给智能指针对象,将无需手动释放内存,在智能指针过期时,这些内存将自动被释放。
C++ 11 引入了 3 个智能指针类型:
std::unique_ptr<T>
:独占资源所有权的指针。也就是说,同时只有一个 unique_ptr 指向同一个对象,当这个 unique_ptr 被销毁时,指向的对象也随即被销毁。std::shared_ptr<T>
:共享资源所有权的指针。多个 share_ptr 可以指向(关联)相同的对象,在内部采用计数机制来实现。当新的 shared_ptr 与对象关联时,引用计数增加 1。当 shared_ptr 超出作用域时,引用计数减 1。当引用计数变为 0 时,则表示没有任何 shared_ptr 与对象关联,则释放该对象。std::weak_ptr<T>
:共享资源的观察者,需要和std::shared_ptr
一起使用,不影响资源的生命周期。
要创建智能指针对象,必须包含头文件 memory,该文件模板定义。然后使用通常的模板语法来实例化所需类型的指针。所有的智能指针都有一个 explicit 构造函数,该构造函数将指针作为参数。因此不需要自动将指针转换为智能指针对象。
二、独占智能指针
独占智能指针独占资源所有权的指针。也就是说,同时只有一个独占指针指向同一个对象,当这个独占指针被销毁时,指向的对象也随即被销毁。
unique_ptr 指针的初始化方法如下:
unique_ptr<T> ptr(new T(args...));
unique_ptr<T> ptr = make_unique<T>(args...);
class Person
{
private:
string name;
int age;
public:
Person(void);
Person(string name, int age);
~Person(void);
string getName(void) const;
void setName(string name);
int getAge(void) const;
void setAge(int age);
};
Person::Person(void)
{
cout << "Person()" << endl;
}
Person::Person(string name, int age) : name(name), age(age)
{
cout << "Person(string name, int age)" << endl;
}
Person::~Person(void)
{
cout << "~Person()" << endl;
}
string Person::getName(void) const
{
return name;
}
void Person::setName(string name)
{
this->name = name;
}
int Person::getAge(void) const
{
return age;
}
void Person::setAge(int age)
{
this->age = age;
}
#include <iostream>
#include <memory>
using namespace std;
int main(void)
{
unique_ptr<Person> p1(new Person("Sakura", 10));
cout << "{name: " << (*p1).getName() << ", age: " << p1->getAge() << "}" << endl;
unique_ptr<Person> p2 = make_unique<Person>("Sakura", 12);
cout << "{name: " << (*p2).getName() << ", age: " << p2->getAge() << "}" << endl;
// 重置智能指针,解除对原始内存的管理,重新指定指针管理的原始内存
p1.reset(new Person("Mikoto", 14));
cout << "{name: " << (*p1).getName() << ", age: " << p1->getAge() << "}" << endl;
// 重置智能指针,解除对原始内存的管理
p2.reset();
p2 = make_unique<Person>("Shana", 15);
cout << "{name: " << (*p2).getName() << ", age: " << p2->getAge() << "}" << endl;
return 0;
}
在 C++ 11 之前的版本,如果智能指针管理的数组内存时,需要手动指定删除函数。
int main(void)
{
// C++ 11 之前的版本,如果智能指针管理的数组内存时,需要手动指定删除函数
// 需要在模板中指定第二个参数指定删除器的地址,使用函数指针即可
typedef void (*func_ptr)(Person *);
unique_ptr<Person, func_ptr> p1(new Person[5], [](Person *p) { delete[] p; });
return 0;
}
unique_ptr 智能指针对象没有拷贝构造函数,也不能进行赋值操作和普通指针操作。
三、共享智能指针
多个共享智能指针可以指向(关联)相同的对象,在内部采用计数机制来实现。当新的共享智能指针与对象关联时,引用计数增加 1。当共享智能指针超出作用域时,引用计数减 1。当引用计数变为 0 时,则表示没有任何共享指针与对象关联,则释放该对象。
shared_ptr 指针的初始化方法如下:
shared_ptr<T> ptr(new T(args...));
shared_ptr<T> ptr = make_shared<T>(args...);
shared_ptr<T> ptr = ptr1;
#include <iostream>
#include <memory>
using namespace std;
int main(void)
{
shared_ptr<Person> p1(new Person("Sakura", 10));
shared_ptr<Person> p2 = make_shared<Person>("Sakura", 12);
shared_ptr<Person> p3 = p2;
cout << "{name: " << (*p1).getName() << ", age: " << p1->getAge() << "}" << endl;
cout << "{name: " << (*p2).getName() << ", age: " << p2->getAge() << "}" << endl;
cout << "{name: " << (*p3).getName() << ", age: " << p3->getAge() << "}" << endl;
cout << "p1 use_count(): " << p1.use_count() << endl;
cout << "p2 use_count(): " << p1.use_count() << endl;
cout << "p3 use_count(): " << p3.use_count() << endl;
// 重置智能指针,解除对原始内存的管理,重新指定指针管理的原始内存
p1.reset(new Person("Mikoto", 14));
// 重置智能指针,解除对原始内存的管理
p2.reset();
p2 = make_shared<Person>("Shana", 15);
cout << "{name: " << (*p1).getName() << ", age: " << p1->getAge() << "}" << endl;
cout << "{name: " << (*p2).getName() << ", age: " << p2->getAge() << "}" << endl;
cout << "{name: " << (*p3).getName() << ", age: " << p3->getAge() << "}" << endl;
cout << "p1 use_count(): " << p1.use_count() << endl;
cout << "p2 use_count(): " << p1.use_count() << endl;
cout << "p3 use_count(): " << p3.use_count() << endl;
return 0;
}
在 C++ 11 之前的版本,如果智能指针管理的数组内存时,需要手动指定删除函数。
int main(void)
{
// C++ 11 之前的版本,如果智能指针管理的数组内存时,需要手动指定删除函数
shared_ptr<Person> p1(new Person[5], [](Person *p) { delete[] p; });
// 使用默认的删除函数
shared_ptr<Person> p2(new Person[5], default_delete<Person[]>());
return 0;
}
三、弱引用智能指针
弱引用智能指针需要和共享指针一起使用,它不影响资源的生命周期。弱引用智能指针可以看作共享指针的的助手,它不管理共享指针内部的指针。弱引用智能指针没有重载运算符 *
和 ->
,这是因为它不共享指针,也不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视共享指针中管理的资源是否存在。
weak_ptr 指针的初始化方法如下:
weak_ptr<T> ptr(const weak_ptr & x);
weak_ptr<T> ptr(const share_ptr & x);
weak_ptr<T> ptr = ptr1
int main(void)
{
// 如果智能指针管理的数组内存时,需要手动指定删除函数
shared_ptr<Person> sp(new Person("Tom", 18));
weak_ptr<Person> wp1;
weak_ptr<Person> wp2(wp1);
weak_ptr<Person> wp3(sp);
weak_ptr<Person> wp4 = wp1;
return 0;
}
class ClassA
{
private:
std::shared_ptr<ClassB> bptr;
public:
void setB(std::shared_ptr<ClassB> b)
{
bptr = b;
}
};
class ClassB
{
private:
std::weak_ptr<ClassA> aptr; // 使用弱引用避免循环引用
public:
void setA(std::shared_ptr<ClassA> a)
{
aptr = a;
}
};