概述
指针让我们在域边界之外拥有reference语义,然而,确保“pointer的寿命”和“其所指对象的寿命”一致却是件棘手的事。
C++11提供两大类型智能指针:shared_ptr和unique_ptr。
- shared_ptr实现共享式拥有的概念。多个shared_ptr指向同一个对象,当最后一个shared_ptr析构时,指向的对象也被释放,标准库还提供weak_ptr, bad_weak_ptr, enable_shared_from_this等辅助类。
- unique_ptr实现独占式拥有的概念。保证同一时间内只有一个指针指向对象,你可以移交所有权。
shared_ptr使用
shared_ptr的目标就是在其所指的对象不再被需要后,自动释放与之相关的资源。
构造:
shared_ptr<string> pStr1(new string("abc"));
shared_ptr<string> pStr2 = make_shared<string>("abc");
其中模板参数类型是指针指向的数据类型。
接受单一指针的构造函数是explicit的,因此不能进行隐式转换
shared_ptr<string> pStr = new string("abc"); // ERROR
shared_ptr<string> pStr{new string("abc")}; // OK
你也可以先声明shared_ptr再赋值一个指针给它。
shared_ptr<string> pStr;
pStr.reset(new string("abc"));
pStr = new string("abc"); // ERROR
你可以像使用指针一样使用shared_pointer
pStr->replace(0, 1, "J);
(*pStr)[0] = 'N'; // 改变string第一个元素
容器总是为传入的元素创建属于容器自己的拷贝,因此当把同一个shared_ptr重复添加到vector中时,多个shared_ptr将指向同一个string对象,当最后一个shared_ptr销毁时,会对其指向的对象调用delete
。
然而对于array,我们应该调用的是delete[]
,而不是delete
,因此创建一个指向array的shared_ptr是错误的
shared_ptr<int> p(new int[10]); // ERROR
我们在第二个参数指定deleter
shared_ptr<int> p(new int[10], [](int*p) { delete[] p; });
shared_ptr<int> p(new int[10], std::default_delete<int[]>()); // default_delete默认调用delete[]
C++17之前,只有unique_ptr支持数组类型的模板
unique_ptr<int[]> p(new int[10]); // 正确。并且能够正确调用delete[]
shared_ptr<int[]> p(new int[10]); // 编译错误
C++17之后,shared_ptr也支持数组类型的模板,并且也可以像原生数组一样取数组元素
shared_ptr<int[]> p(new int[10]); // C++17以后正确
p[0] = 0; // C++17之后正确
p->get()[1] = 1; // C++11不支持operator[]可使用此写法,与上述语句等价
注:本人在visual studio上使用shared_ptr<int[]> p(new int[10]);
没有检测到内存泄漏,可能是vs进行了优化?
shared_ptr误用
当我们为两组shared_ptr设定相同的指针,会造成double free或者资源被提前释放。如下:
int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
// double free
另一个例子:
#include <memory>
#include <iostream>
using namespace std;
class Person
{
public:
Person(const string& name) : m_name(name)
{
}
~Person()
{
cout << "destroy: " << m_name << endl;
}
void meetSomeone(shared_ptr<Person> someone)
{
m_otherOne = someone;
someone->m_otherOne = shared_ptr<Person>(this); // --> 2
}
public:
string m_name;
std::weak_ptr<Person> m_otherOne;
};
int main()
{
auto Alice = std::make_shared<Person>("Alice"); // --> 1
auto Bob = std::make_shared<Person>("Bob");
Alice->meetSomeone(Bob);
return 0;
}
当我们执行程序会发生错误,这是因为Alice Person对象被1 2处两组shared_ptr管理,并且meetSomeone结束后,2处的临时shared_ptr会导致Alice Person对象被释放,程序将崩溃。关于weak_ptr的介绍详见这里,这里只要知道weak_ptr不影响shared_ptr的资源释放和use_count就行。
为了避免多组owner的问题,使用shared_ptr的原则是当我们为某个指针创建了shared_ptr后,之后应该都使用其相关的shared_ptr进行赋值和构造其他shared_ptr
int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(sp1); // 使用sp1构造另一个shared_ptr
所谓“一组”shared_ptr,就是所有指针关联的shared_ptr的use_count都相同,假设有n个shared_ptr,那么use_count应都等于n**。对前一段代码而言,有2个shared_ptr,且两个shared_ptr的use_count()都为1,这就是“两组”shared_ptr。
使用class std::enable_shared_from_this
当我们需要在类内部的成员函数中使用当前对象关联的shared_ptr时,一种方法是通过参数传入shared_ptr
void meetSomeone(shared_ptr<Person> someone, shared_ptr<Person> this_shared_ptr)
{
m_otherOne = someone;
someone->m_otherOne = this_shared_ptr; // --> 2
}
...
Alice->meetSomeone(Bob, Alice); // pass Alice shared_ptr
另一种简洁的方法就是让类继承class std::enable_shared_from_this,然后在成员函数中调用shared_from_this()。
class Person : public enable_shared_from_this<Person>
{
public:
...
void meetSomeone(shared_ptr<Person> someone)
{
m_otherOne = someone;
someone->m_otherOne = shared_from_this();
}
...
};
...
Alice->meetSomeone(Bob);
参考:
- https://stackoverflow.com/questions/13061979/can-you-make-a-stdshared-ptr-manage-an-array-allocated-with-new-t
- https://blog.csdn.net/mrbone11/article/details/141873828?spm=1001.2014.3001.5501