对象跟对象之间是有所有权关系的,这些关系可以用shared_ptr
、weak_ptr
和unique_ptr
来表达。但是今天先不提智能指针的事情,先来谈谈什么是所有权。
直接的讲,C++ 里面的对象所有权的关系,指的是谁负责delete
谁的关系。让我们来看几个例子,假设类 A 的一个成员变量是一个指向了类 B 的实例的指针。
独占所有权:如果 A 对 B 有独占所有权的话,那么 A delete
B。这里面也包含着另一层意思,如果 C 想要持有 B,那么就必须让 A 放弃对 B 的所有权,把它的成员变量设置为nullptr
。现在就由 C 来决定在什么时候 delete
B,这个对象跟 A 再也没有关系了。独占的意思就是不分享,而且所有权也是可以转移的,转移后仍然是独占。
分享所有权:如果 A 对 B 有分享所有权的话,那么 B 由最后一个持有该对象的 A 来delete
。如果 C 想要持有 B,那么 A 这个时候不需要放弃对 B 的所有权,转而将讲所有权分享给 C。如果所有的 A 都没有了,C 还在的话,那么由 C 来决定什么时候 delete
B。如果 C 首先放弃了所有权(譬如说 C 自己被delete
了),那么仍然由最后一个持有 B 的 A 来 delete
B。
弱引用:如果 A 对 B 有弱引用的话,那么 A 不负责 delete
B,但是 A 可以使用 B。如果 B 已经被拥有所有权的对象delete
了,那么 A 会收到通知。这个时候如果 A 还想试图使用 B,就会拿到一个nullptr
。
最后一种就是普通的指针了。普通的指针没有任何功能,指向的对象被delete
了你也不知道,稍微不注意就容易炸裂。所以这就是为什么 C++ 推荐我们使用这一套智能指针来管理对象与对象之间的关系,因为只要好好使用,程序永远不会“Access Violation”。
既然学习了对象的所有权关系,那么 C++ 是怎么来表达它的呢?现在让我们先来看一下独占关系的例子。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class A
{
public:
string name;
A(const string& theName)
:name{ theName }
{
}
};
int main()
{
// unique_ptr<A> x;
auto x = make_unique<A>("A");
cout << x->name << endl;
unique_ptr<A> y(x.release());
if (x)
{
cout << "x不为空" << endl;
}
else
{
cout << "x为空" << endl;
}
cout << y->name << endl;
return 0;
}
如果我们要x
放弃对A*
指针的所有权的话,要调用x.release()
,这个函数返回原本独占的A*
。这个时候我们就可以拿已经不归任何人所有的指针来构造一个新的unique_ptr
。
我们可以通过把unique_ptr
转换为bool
值来判断一个独占的执政指针是否为空。
接下来让我们看一下shared_ptr
和weak_ptr
的例子。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class A
{
public:
string name;
A(const string& theName)
:name{
theName }
{
}
};
int main()
{
// shared_ptr<A> x;
auto x = make_shared<A>("A");
cout << x->name << endl;
{
// shared_ptr<A> y;
auto y = x;
cout << y->name << endl;
}
weak_ptr<A> w = x;
{
// shared_ptr<A> y;
auto y = w.lock();
cout << x->name << endl;
}
x = nullptr;
{
auto y = w.lock();
if (y)
{
cout << "w不为空" << endl;
}
else
{
cout << "w为空" << endl;
}
}
return 0;
}
首先,make_shared
我们都知道了,在之前的章节里面已经对shared_ptr
有所介绍了。
第二,shared_ptr
可以复制很多个,譬如说auto y = x;
。这个时候x
和y
对A
都有所有权。因此在输出了第二行之后,y
被释放了,x
还在,所以对象还在。
第三,我们可以从shared_ptr
构造一个weak_ptr
。如果我们需要使用weak_ptr
所指向的对象的话,要调用lock
函数。这个函数会返回一个shared_ptr
。
接下来就是x = nullptr;
。我们已经把其他指针(在这里是空指针)赋值给x
,这个时候已经没有任何shared_ptr
拥有对A
的所有权了,所以A
就在此刻被delete
。
最后,我们会发现,因为w
指向的原来的对象已经被delete
了,所以lock
会返回一个空的shared_ptr
。
最后要提到的是,unique_ptr
和shared_ptr
都有get
函数,在不放弃所有权的情况下返回指向的对象的指针。它们还有reset
函数,其实跟把nullptr
赋值给他们是一样的。
weak_ptr
有expired
函数来告诉我们它指向的对象是不是还在,如果我们仅仅是为了做判断,不需要去调用lock
函数。
shared_ptr 存在环形引用的情况
以上来自计蒜客 ?