练习12.1 在此代码的结尾,b1和b2各包含多少个元素?
StrBlob b1{
StrBlob b2 = { "a","an","the" };
b1 = b2;
b2.push_back( "about" );
}
b1和b2共享数据,b1和b2均包含4个元素。
练习12.2 编写你自己的StrBlob类,包含const版本的front和back。
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
class StrBlob {
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob():data(std::make_shared<std::vector<std::string>>()){}
StrBlob(std::initializer_list<std::string> il):data(std::make_shared<std::vector<std::string>>(il)){}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
std::string& front() { front_check(); return data->front(); }
std::string& back() { back_check(); return data->back(); }
const std::string& front() const { front_check(); return data->front(); }
const std::string& back() const { back_check(); return data->back(); }
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const;
void front_check();
void back_check();
};
void StrBlob::check(StrBlob::size_type i, const std::string &msg) const
{
if (i >= data->size()) {
throw std::out_of_range(msg);
}
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
void StrBlob::front_check()const
{
StrBlob::check(0,"front on empty StrBlob");
}
void StrBlob::back_check()const
{
StrBlob::check(0, "back on empty StrBlob");
}
练习12.3 StrBlob需要const版本的push_back和pop_back吗?如果需要,添加进去,否则,解释为什么不需要。
不需要。
常量成员函数不改变对象的数据。而push_back和pop_back改变容器的元素。
练习12.4 在我们的check函数中,没有检验i是否大于0.为什么可以忽略这个检查?
size_type是类型是vector<T>::size_type 不会小于0;
练习12.5 我们未编写接受一个initializer_list explict参数的构造函数,讨论这个设计的策略的优点和缺点。
优点:explict构造函数只能用于直接初始化,不能用于拷贝初始化。
缺点:可能在意想不到的情况下使用了隐式转化。
练习12.6 编写函数,返回一个动态分配的Int的vector将此vector传递给另一个函数,这个函数读取标准输入,将读入的值保存在vector元素中。再将vector传递给另一个函数,打印读入的值。记得在恰当的时刻delete vector。
#include <iostream>
#include <new>
#include <vector>
using namespace std;
vector<int>* func_1();
void func_2(vector<int>*);
void func_3(vector<int>*);
int main()
{
auto p_vec = func_1();
func_2(p_vec);
func_3(p_vec);
}
// 生成指向vector<int>的动态指针
vector<int>* func_1()
{
vector<int> *p = new vector<int>;
return p;
}
// 输入数据
void func_2(vector<int>* p)
{
int val;
while (cin >> val)
{
p->push_back(val);
}
}
// 输出数据,并释放内存
void func_3(vector<int>* p)
{
for (auto &c : *p)
{
cout << c << " ";
}
delete p;
cout << endl;
}
练习12.7 重做上一题,这次使用shared_ptr而不是内置指针。
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
// 返回类型为智能指针
shared_ptr<vector<int>> func_1();
void func_2(shared_ptr<vector<int>>);
void func_3(shared_ptr<vector<int>>);
int main()
{
auto p_vec = func_1();
func_2(p_vec);
func_3(p_vec);
}
shared_ptr<vector<int>> func_1()
{
auto it = make_shared<vector<int>>();
return it;
}
void func_2(shared_ptr<vector<int>> p)
{
int val;
while (cin >> val)
{
p->push_back(val);
}
}
void func_3(shared_ptr<vector<int>> p)
{
for (auto &c : *p)
{
cout << c << " ";
}
cout << endl;
}
练习12.8 下面的函数是否有错误? 如果有,解释错误原因。
bool b()
{
int * p = new int;
return p;
}
错误。p是指向int的指针,返回类型是bool类型,不匹配。
练习12.9 解释下面代码执行的结果:
int *q = new int (42), *r = new int (100);
r = q; // 指针r和q指向同一个对象42
auto q2 = make_shared<int> (42), r2 = make_shared<int>(100);
r2 = q2; // 指针r2和q2指向同一个对象42,同时q2对象被删除,内存被释放
练习12.10 下面的代码调用了第413页中定义的process函数,解释此调用是否正确。如果不正确,应该如何修改?
shared_ptr<int> p (new int (42));
process(shared_ptr <int>(p));
正确。 实参创建一个临时的shared_ptr指向42,process中引用计数值为2。
练习12.11 如果我们像下面这样调用process,会发生什么?
process (shared_ptr<int>(p.get()) );
产生未定义的行为:ptr和p两个独立的shared_ptr指向相同的内存。当process结束ptr释放内存,p成为空悬指针。
将另一智能指针也绑到get返回的指针上,这一做法是错误的。
练习12.12 p和sp的定义如下,对于接下来的process的每个调用,如果合法,解释它做了什么,如果不合法,解释错误原因:
auto p = new int();
auto sp = make_shared<int>();
(a) process(sp); //合法,在process中引用计算值为2
(b) process(new int ()); //非法,不能将一个内置指针转化成智能指针
(c) process(p); //非法,同上
(d) process(shared_ptr<int>(p)); //合法,但内存会被释放
练习12.13 如果执行下面的代码,会发生什么?
auto sp = make_shared<int>();
auto p = sp.get();
delete p;
p和sp是指向相同的内存,但他们是相互独立创建的,因此各自的引用计数都是1。当delete p后所指的内存被释放,sp成为空悬指针。使用get返回的指针不能delete此指针。
练习12.14 编写你自己版本的用shared_ptr管理connection的函数。
#include <iostream>
#include <memory>
#include <string>
using namespace std;
struct destination {};
struct connection {};
connection connect(destination *);
void disconnection(connection);
void f(destination &);
void end_connection(connection *p) { disconnection(*p); }
int main()
{
destination des_item;
f(des_item);
return 0;
}
void f(destination &d)
{
connection c = connect(&d);
// shared_ptr<connection> p(&c, end_connection);
shared_ptr<connection> p2(&c, [] (connection *p2){disconnection(*p2); });
}
connection connect(destination *p)
{
connection item;
cout << "here is connect function" << endl;
return item;
}
void disconnection(connection)
{
cout << "bey bye" << endl;
}
练习12.15 编写你自己版本的用shared_ptr管理connection函数。
shared_ptr<connection> p2(&c, [] (connection *p2){disconnection(*p2); });
练习12.16 如果你试图拷贝或赋值unique_ptr,编译器并不总是能给出易于理解的错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2280 “std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 尝试引用已删除的函数 Cpprimer c:\users\pierce\documents\visual studio 2015\projects\cpprimer\12.16.cpp 7
。。。
练习12.17 下面的unique_ptr声明中,哪些是合法的,哪些是可能导致后续的程序错误?解释每个错误的问题在哪里?
Intp p0(ix); //非法,ix不是指针
Intp p1(pi); //非法,定义unique_ptr时,须将其绑定到一个new返回的指针上
Intp p2(pi2); //合法
Intp p3(&ix); //非法,同p1
Intp p4(new int(2048)); //合法
Intp p5(p2.get()); //非法,p2.get()返回的是内置指针,同p1
练习12.18 shared_ptr为什么没有release成员?
release成员的作用是放弃指针对对象的管理,不操作对象内存。
shared_ptr类型内置的引用计数在不为0的情况下能够实现上述的功能。
练习12.19 定义你自己版本的StrBlobPtr,更新StrBlob类,加入恰当的friend声明以及end成员。
//写入begin和end成员函数程序报错。
//StrBlobPtr类在StrBlob中是未定义的不完全类
class StrBlobPtr {
public:
StrBlobPtr() :curr(0) {}
StrBlobPtr(StrBlob &a, size_t sz = 0) :wptr(a.data), curr(sz) {}
std::string& deref() const;
StrBlobPtr& incr();
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t, const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(size_t i, const std::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::string& StrBlobPtr::deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
练习12.20 编写程序,逐行读入一个文件,将内容存入一个StrBlob中,用一个StrBlobPtr打印出StrBlob中的每个元素。
#include <iostream>
#include <fstream>
#include <string>
#include "StrBlob.h"
using namespace std;
int main()
{
ifstream text("map_file.txt");
string line;
StrBlob item;
while (getline(text, line)) {
item.push_back(line);
}
StrBlobPtr p(item, 0);
size_t i = 0;
for (auto iter = item.size(); i < iter; ++i,p.incr()) {
cout << p.deref() << endl;
}
}
练习 12.21 也可以这样编写StrBlobPtr的deref成员:
std::string& deref() const
{
return (*check(curr, "dereference past end")[curr]);
}
你认为哪个版本更好?为什么?
先前版本更好。 可读性更高。
练习12.22 为了能让StrBlobPtr使用const StrBlob,你觉得应该如何修改?定义一个名为constStrBlobPtr类,使其能够指向const StrBlobPtr。
StrBlobPtr(const StrBlob &a, size_t = 0) :wptr(a.data), curr(sz) {}
重载构造函数的(const参数版本)。