如果我们像下面这样调用 process,会发生什么?
process(shared_ptr(p.get()));
这样会创建一个新的智能指针,它的引用计数为 1,这个智能指针所指向的空间与 p 相同。在表达式结束后,这个临时智能指针会被销毁,引用计数为 0,所指向的内存空间也会被释放。而导致 p 所指向的空间被释放,使得 p 成为一个空悬指针。
p 和 sp 的定义如下,对于接下来的对 process 的每个调用,如果合法,解释它做了什么,如果不合法,解释错误原因:
auto p = new int();
auto sp = make_shared();
(a) process(sp);
(b) process(new int());
(c) process(p);
(d) process(shared_ptr(p));
如果执行下面的代码,会发生什么?
auto sp = make_shared();
auto p = sp.get();
delete p;
智能指针 sp 所指向空间已经被释放,再对 sp 进行操作会出现错误。
编写你自己版本的用 shared_ptr 管理 connection 的函数。
#include
#include
#include
struct connection
{
std::string ip;
int port;
connection(std::string i, int p) : ip(i), port(p) {}
};
struct destination
{
std::string ip;
int port;
destination(std::string i, int p) : ip(i), port(p) {}
};
connection connect(destination* pDest)
{
std::shared_ptr pConn(new connection(pDest->ip, pDest->port));
std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
return *pConn;
}
void disconnect(connection pConn)
{
std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;
}
void end_connection(connection* pConn)
{
disconnect(*pConn);
}
void f(destination &d)
{
connection conn = connect(&d);
std::shared_ptr p(&conn, end_connection);
std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}
int main()
{
destination dest("220.181.111.111", 10086);
f(dest);
return 0;
}
重写第一题的程序,用 lambda 代替end_connection 函数。
#include
#include
#include
struct connection
{
std::string ip;
int port;
connection(std::string i, int p) : ip(i), port(p) {}
};
struct destination
{
std::string ip;
int port;
destination(std::string i, int p) : ip(i), port(p) {}
};
connection connect(destination* pDest)
{
std::shared_ptr pConn(new connection(pDest->ip, pDest->port));
std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
return *pConn;
}
void disconnect(connection pConn)
{
std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;
}
void f(destination &d)
{
connection conn = connect(&d);
std::shared_ptr p(&conn, [] (connection* p){ disconnect(*p); });
std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}
int main()
{
destination dest("220.181.111.111", 10086);
f(dest);
return 0;
}
如果你试图拷贝或赋值 unique_ptr,编译器并不总是能给出易于理解的错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。
#include
#include
#include
int main()
{
std::unique_ptr p1(new std::string("hello"));
//std::unique_ptr p2 = p1; ³¢ÊÔÒýÓÃÒÑɾ³ýµÄº¯Êý
std::cout << *p1 << std::endl;
p1.reset(nullptr);
return 0;
}
尝试引用已删除的函数
下面的 unique_ptr 声明中,哪些是合法的,哪些可能导致后续的程序错误?解释每个错误的问题在哪里。
int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef unique_ptr IntP;
(a) IntP p0(ix);
(b) IntP p1(pi);
(c) IntP p2(pi2);
(d) IntP p3(&ix);
(e) IntP p4(new int(2048));
(f) IntP p5(p2.get());
shared_ptr 为什么没有 release 成员?
release 成员的作用是放弃控制权并返回指针,因为在某一时刻只能有一个 unique_ptr 指向某个对象,unique_ptr 不能被赋值,所以要使用 release 成员将一个 unique_ptr 的指针的所有权传递给另一个 unique_ptr。而 shared_ptr 允许有多个 shared_ptr 指向同一个对象,因此不需要 release 成员。
定义你自己版本的 StrBlobPtr,更新 StrBlob 类,加入恰当的 friend 声明以及 begin 和 end 成员。
#ifndef EX12_19_H
#define EX12_19_H
#include
#include
#include
#include
#include
using std::vector; using std::string;
class StrBlobPtr;
class StrBlob
{
public:
using size_type = vector::size_type;
friend class StrBlobPtr;
StrBlobPtr begin();
StrBlobPtr end();
StrBlob() : data(std::make_shared>()) {}
StrBlob(std::initializer_list il) : data(std::make_shared>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string& s) { data->push_back(s); }
void pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
std::string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
std::string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const std::string& front() const
{
check(0, "front on empty StrBlob");
return data->front();
}
const std::string& back() const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
void check(size_type i, const string& msg) const
{
if (i >= data->size())
throw std::out_of_range(msg);
}
private:
std::shared_ptr> data;
};
class StrBlobPtr
{
public:
StrBlobPtr() :curr(0) {}
StrBlobPtr(StrBlob &a, size_t sz = 0) :wptr(a.data), curr(sz) {}
bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
string& deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
private:
std::shared_ptr> check(size_t i, const string &msg) const
{
auto ret = wptr.lock();
if (!ret) throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size()) throw std::out_of_range(msg);
return ret;
}
std::weak_ptr> wptr;
size_t curr;
};
StrBlobPtr StrBlob::begin()
{
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
return StrBlobPtr(*this, data->size());
}
#endif
编写程序,逐行读入一个输入文件,将内容存入一个 StrBlob 中,用一个 StrBlobPtr 打印出 StrBlob 中的每个元素。
#include
#include
#include "exercise12_19.h"
using namespace std;
int main()
{
ifstream ifs("./data/books.txt");
StrBlob sb;
string s;
while (getline(ifs, s))
{
sb.push_back(s);
}
for (StrBlobPtr sbp = sb.begin(); sbp != sb.end(); sbp.incr())
{
cout << sbp.deref() << endl;
}
return 0;
}