shared_ptr
允许多个指针指向同一个对象,unique_ptr
则独占所指向的对象,weak_ptr
是一种弱引用,指向shared_ptr
所管理的对象
12.1.1 shared_ptr类
需要指定想要创建的对象的类型
shared_ptr<int> p3 = make_shared<int>(42);
shared_ptr<string> p4 = make_shared<string>(10, '9');
shared_ptr<int> p5 = make_shared<int>();
make_shared
用其参数来构造给定类型的对象
也可以用auto
auto p6 = make_shared<vector<string>>();
shared_ptr
的拷贝和赋值
auto p = make_shared<int>(42);
auto q(p);
cout << p.use_count() << endl; // 2
当进行拷贝或赋值时,每个shared_ptr
都会记录有多少个其他shared_ptr
指向相同的对象
我们可以认为每个shared_ptr
都有一个关联的计数器,称为引用计数,拷贝一个shared_ptr
时,计数器会递增
一旦一个shared_ptr
的计数器变为0,它就会自动释放自己所管理的对象
auto r = make_shared<int>(42);
r = q;
给r
赋值后,r
指向另一个地址,这样会递增q
指向的对象的引用计数,递减r
原来指向对象的引用计数,如果r
原来的对象已没有引用者,会自动释放
定义StrBlob
class StrBlob {
public:
typedef vector<string>::size_type size_type;
StrBlob(): data(make_shared<vector<string>>()) {}
StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string& t) { data->push_back(t); }
void pop_back();
string& front();
string& back();
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string& msg) const;
};
StrBlob
中有个共享指针的成员,指针指向的是一个动态分配的vector
,这样就可以在多个对象间共享这个vector
check
函数确保访问不会越界
void StrBlob::check(size_type i, const string& msg) const {
if (i >= data->size())
throw out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
12.1.3 shared_ptr和new结合使用
可以用new
返回的指针来初始化智能指针
shared_ptr<double> p2(new int(42));
接受指针参数的智能指针构造函数是explicit
的,必须使用直接初始化形式
shared_ptr<int> p1 = new int(1024); // 错误,不能使用拷贝初始化
shared_ptr<int> p2(new int(1024)); // 正确,使用直接初始化
下面的函数接受一个按值传递的智能指针
void process(shared_ptr<int> ptr) {
// 使用ptr
} // ptr离开作用域,被销毁
shared_ptr<int> p(new int(42));
process(p); // 拷贝p会增加引用计数,在process中引用计数为2
int i = *p; // 正确:引用计数为1
按值传递时,实参会被拷贝到ptr
中,使得对象的引用计数增加为2,当函数结束时,ptr
的引用计数会减1,但不会变成0,所以,指向的内存不会被释放
但是,不能传递一个普通指针或临时的shared_ptr
,因为函数结束时,智能指针指向对象的引用计数会变成0,将内存释放,此时普通指针就变成空悬指针
int *x(new int(42)); // x是一个普通指针
process(x); // 错误,不能将int*转换成shared_ptr<int>
process(shared_ptr<int>(x)); // 合法,但内存会被释放
int j = *x; // x是一个空悬指针
get
会返回一个内置指针,使用场景是:我们需要向不能使用智能指针的代码传递一个内置指针,使用get
返回的指针的代码不能delete
此指针,如果这边delete
了,智能指针那边就变成空悬指针了
shared_ptr<int> p(new int(42));
int *q = p.get(); // 正确,但不能让q被delete
{
shared_ptr<int>(q); // 用q又创建了一个智能指针,此时p和q指向同一块内存
} // 代码块结束时,内存就被释放了
int foo = *p; // 此时p变成空悬指针了
get
用来将指针的访问权限传递给代码,只有在确定代码不会delete
指针的情况下,才能使用get
,特别是,永远不要用get
初始化另一个智能指针或者为另一个智能指针赋值
注意
- 不使用相同的内置指针值初始化多个智能指针
- 不
delete
掉get
返回的指针 - 不使用
get
初始化或reset
另一个智能指针 - 如果使用
get
返回的指针,记住当最后一个对应的智能指针销毁后,指针就变为无效了 - 如果使用智能指针管理的资源不是
new
分配的内存,记住传递给它一个删除器
12.1.5 unique_ptr
一个unique_ptr
拥有它指向的对象,某个时刻只能有一个unique_ptr
指向一个给定对象,需要绑定到一个new
返回的指针上,必须使用直接初始化方式
unique_ptr<double> p1; // 可以指向一个double的unique_ptr
unique_ptr<int> p2(new int(42)); // 直接初始化
不支持普通的拷贝或赋值操作
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1); // 错误,不支持拷贝
unique_ptr<string> p3;
p3 = p2; // 错误,不支持赋值
可以使用release
或reset
将指针的所有权从一个unique_ptr
转移给另一个unique_ptr
unique_ptr<string> p2(p1.release()); // 将所有权从p1转移给p2,release将p1置为空
unique_ptr<string> p3(new string("world"));
p2.reset(p3.release()); // reset释放p2原来指向的内存,并将p3指向的对象的所有权转移给p2
p2
接管了p1
的对象后,调用reset
会先释放p2
指向的内存,然后将p3
对指针的所有权转移给p2
,并将p3
置为空
调用release
会放弃对指针的控制权,并返回指针,所以应该有人来接管这个指针,并负责后续的资源释放
p2.release(); // 错误,p2不会释放内存,而且我们失去了指针
auto p = p2.release(); // 正确,记得delete p
可以拷贝或赋值一个将要被销毁的unique_ptr
,比如从函数返回一个unique_ptr
unique_ptr<int> clone(int p) {
return unique_ptr<int>(new int(p));
}
还可以返回一个局部对象的拷贝
unique_ptr<int> clone(int p) {
unique_ptr<int> ret(new int(p));
return ret;
}
指定删除器时,必须在尖括号中提供删除器的类型
unique_ptr<objT, delT> p(new objT, fcn);
p
指向一个类型为objT
的对象,并使用一个类型为delT
的对象释放objT
对象,它会调用一个名为fcn
的delT
类型对象
12.1.6 weak_ptr
weak_ptr
是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr
管理的对象
当我们创建一个weak_ptr
时,要用一个shared_ptr
来初始化它
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp弱共享p,p的引用计数未改变
由于对象可能不存在,我们不能使用weak_ptr
直接访问对象,而必须调用lock
,此函数检查weak_ptr
指向的对象是否仍存在。如果存在,lock
返回一个指向共享对象的shared_ptr
,否则返回空的shared_ptr
if (shared_ptr<int> np = wp.lock()) {
// 只要np不为空则条件成立
// 使用np访问共享对象
}
定义StrBlobPtr
class StrBlobPtr;
class StrBlob {
public:
friend class StrBlobPtr;
StrBlobPtr begin();
StrBlobPtr end();
public:
typedef vector<string>::size_type size_type;
StrBlob(): data(make_shared<vector<string>>()) {}
StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string& t) { data->push_back(t); }
void pop_back();
string& front();
string& back();
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string& msg) const;
};
void StrBlob::check(size_type i, const string& msg) const {
if (i >= data->size())
throw out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
class StrBlobPtr {
public:
friend bool operator==(const StrBlobPtr&, const StrBlobPtr&);
friend bool operator!=(const StrBlobPtr&, const StrBlobPtr&);
StrBlobPtr(): curr(0) {}
StrBlobPtr(StrBlob& a, size_t sz = 0): wptr(a.data),curr(sz) {}
string& deref() const;
StrBlobPtr& operator++();
private:
shared_ptr<vector<string>> check(size_t, const string&) const;
weak_ptr<vector<string>> wptr;
size_t curr;
};
shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string& msg) const {
auto ret = wptr.lock();
if (!ret) {
throw runtime_error("unbound StrBlobPtr");
}
if (i >= ret->size()) {
throw out_of_range(msg);
}
return ret;
}
string& StrBlobPtr::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::operator++() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
bool operator==(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
return lhs.curr == rhs.curr;
}
bool operator!=(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
return !(lhs == rhs);
}
// 这两个函数要放到StrBlobPtr定义之后
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
int main() {
StrBlob s1{"kavin", "jack", "lisa"};
for (auto it = s1.begin(); it != s1.end(); ++it) {
cout << it.deref() << endl;
}
return 0;
}
// kavin
// jack
// lisa