本书为Primer C++ 中文第五版
练习题笔记
(12.1)
#include "strBlob.h"
void test1() {
StrBlob b1;
{
StrBlob b2 = {"a", "an", "the"};
b1 = b2;
b2.push_back("about");
cout << b2.size() << endl;
}
cout << b1.size() << endl;
}
b1和b2共享动态内存vector<string>,代码的结尾,b2离开作用域,b2被销毁,b1包含四个元素。
(12.2)
//可以编写这样一个头文件,到时候使用的时候如上面的12.1
#include <iostream>
#include <vector>
#include <string>
#include <memory>
using namespace std;
class StrBlob {
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> 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();
string &front();
const string &front() const;
string &back();
const string &back() const;
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob(): data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) { }
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();
}
const string & StrBlob::front() const{
check(0, "front on empty StrBlob");
return data->front();
}
string & StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string & StrBlob::back() const{
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
(12.3)
不需要,因为push_back和pop_back都需要对vector元素进行增删操作,并不仅仅只是访问元素。
(12.4)
因为check函数是private的,只有StrBlob内部的函数才能调用它,并且在函数内部使用的时候已经确保i的值不会出现问题
(12.5)
未编写接受一个初始化列表参数的显示构造函数,意味着可以进行列表向StrBlob的隐式类型转换,也就是,在需要StrBlob的地方(如函数的参数),可以使用列表进行替代。而且,可以进行拷贝形式的初始化(如赋值)。这令程序编写更为简单方便。
但这种隐式转换并不总是好的。例如,列表中可能并非都是合法的值。再如,对于接受StrBlob的函数,传递给它一个列表,会创建一个临时的StrBlob对象,用列表对其初始化,然后将其传递给函数,当函数完成后,此对象将被丢弃,再也无法访问了。对于这些情况,我们可以定义显式的构造函数,禁止隐式类类型转换。
(12.6)
vector<int>* write(vector<int>* v) {
int n;
while(cin >> n && n != -1) {
v->push_back(n);
}
return v;
}
void read(vector<int> *v) {
for(auto i = v->begin(); i != v->end(); ++i) cout << *i << " ";
cout << endl;
}
vector<int>* creat() {
vector<int> *v = new vector<int>;
return v;
}
void test6() {
vector<int> *v = write(creat());
read(v);
delete(v);
}
(12.7)
shared_ptr<vector<int>> creatPtr() {
auto p = make_shared<vector<int>>();
return p;
}
shared_ptr<vector<int>> writePtr(shared_ptr<vector<int>> p) {
int n;
while(cin >> n && n != -1) p->push_back(n);
return p;
}
void readPtr(shared_ptr<vector<int>> p) {
for(auto i = p->begin(); i != p->end(); ++i) cout << *i << " ";
cout << endl;
}
void test7() {
auto p = writePtr(creatPtr());
readPtr(p);
}
(12.8)
当一个程序用光了可用的内存,new表达式就会失败。如果不能分配内存失败,则会抛出一个类型bad_alloc的异常,可用使用下面的方式阻止抛出异常:
int *p2 = new (nothrow) int;//分配失败的时候返回一个空指针
如果该函数是想返回一个类型为int的指针,应该修改为int *b() {return new int;}
如果该函数是想判断是否new成功,应该改为 bool b() {return new (nothrow) int;}
(12.9)
int *q = new int(42)/*new一个指针q指向42*/, *r = new int(100)/*new一个指针指向100*/;
r = q;//指针r指向q
//由于r指向q之后,之前分配指向100的内存存在但无法再访问,并且也没有delete,造成内存泄漏
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);//创建两个智能指针q2和r2分别指向42和100
r2 = q2;//r2指向q2,r2之前指向的内存被销毁释放
(12.10)
正确。显示构造智能指针,计数加1
函数调用时会创建一个临时的智能指针,计数加1
函数结束,临时指针被销毁,计数减1
最终计数还是1,不改变
(12.11)
错误,因为p.get()不会使计数加1,但是函数结束会被销毁计数减1计数为0,内存被释放
(12.12)
void process(shared_ptr<int> ptr) {
}
void test12() {
auto p = new int(5);
auto sp = make_shared<int>(5);
process(sp);//a
cout << *sp << endl;
//process(new int()); //b
//process(p); //c
process(shared_ptr<int>(p));//d
cout << *p << endl;
}
a 合法,正确的用法
b 不合法,不允许隐式替换
c 不合法,不允许隐式替换
d 合法,但是内存会被释放,p变成一个空悬指针
(12.13)
会删除sp管理的内存,使得sp变成空悬指针
/**
*智能指针使用规范
*不使用相同的内置指针初始化或reset多个智能指针
*不delete get()返回的指针
*不使用get()初始化或reset另一个智能指针
*如果使用get()返回的指针,当最后一个对应的智能指针销毁后,你的指针就变为无效了
*如果使用的智能指针管理的资源不是new分配的内存,记住传递给它一个删除器
*/
(12.14)
struct destination;
struct connection;
connection connect(destination*);
void disconnect(connection);
void end_connection(connection *p) {disconnect(*p);}
void f(destination &d) {
connection c = connect(&d);
shared_ptr<connection> p (&c, end_connection);
}
(12.15)
void f(destination &d) {
connection c = connect(&d);
shared_ptr<connection> p (&c, [](connection *p){disconnect(*p););
}
(12.16)
error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
正确用法为:
void test16() {
unique_ptr<int> q1(new int(1));
cout << *q1 << endl;
unique_ptr<int> q2(q1.release());//release将q1置空
cout << *q2 << endl;
unique_ptr<int> q3(new int(3));
cout << *q3 << endl;
q2.reset(q3.release());//reset释放了q2原来指向的内存
cout << *q2 << endl;
}
(12.17)
void test17() {
int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef unique_ptr<int> IntP;
//IntP p0(ix);
//IntP p1(pi);
IntP p2(pi2);
//IntP p3(&ix);
IntP p4(new int(2048));
IntP p5(p2.get());
//p2.reset();
cout << "*p0" << "*p1" << *p2 << "*p3" << *p4 << *p5 << endl;
}
(a) 不合法,不能用非指针类型初始化。
(b) 合法。编译成功,运行出错。因为它用一个普通的int变量的地址初始化p1,p1销毁时会释放此内存,其行为是未定义的。
(c) 合法。
(d) 合法。和b一样的问题
(e) 合法。
(f) 合法。但是有问题。会造成两个unique_ptr指向相同的内存地址。当其中一个unique_ptr被销毁时(或者被reset释放对象时),该内存被释放,另一个unique_ptr会变成空悬指针。
(12.18)
因为shared_ptr是允许多个指针管理一个内存,为了对其他指针负责,所以不允许release()
(12.19)
//strBlobPtr.h
#include "strBlob.h"
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob& a, size_t sz = 0):
wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr();
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::incr() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr( *this, data->size() );
return ret;
}
//strBlob.h
#include <iostream>
#include <vector>
#include <string>
#include <memory>
using namespace std;
class StrBlobPtr;
class StrBlob {
public:
friend class StrBlobPtr;
//这两个在strBlobPtr.h里面实现
StrBlobPtr begin();
StrBlobPtr end();
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> 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();
string &front();
const string &front() const;
string &back();
const string &back() const;
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob(): data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) { }
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();
}
const string & StrBlob::front() const{
check(0, "front on empty StrBlob");
return data->front();
}
string & StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string & StrBlob::back() const{
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
(12.20)
#include "StrBlobPtr.h"
void test20() {
StrBlob *file = new StrBlob();
string name;
int n = 0;
//因为没有重载operate== 和 operate!=,用n偷个懒
while(cin >> name && name != "q") {
file->push_back(name);
n++;
}
auto p = file->begin();
while(n != 0) {
cout << p.deref() << endl;
p.incr();
n--;
}
}
(12.21)
这样写尽可能的缩短了代码,但是可读性降低了。
(12.22)
#include "strBlob.h"
class ConstStrBlobPtr {
public:
ConstStrBlobPtr() : curr(0) { }
ConstStrBlobPtr(const StrBlob& a, size_t sz = 0):
wptr(a.data), curr(sz) { }
string& deref() const;
ConstStrBlobPtr& incr();
private:
shared_ptr<vector<string>> check(size_t, const string&) const;
weak_ptr<vector<string>> wptr;
size_t curr;
};
shared_ptr<vector<string>> ConstStrBlobPtr::check(size_t i, const string &msg) const {
auto ret = wptr.lock();
if(!ret) {
throw runtime_error("unbound ConstStrBlobPtr");
}
if(i >= ret->size())
throw out_of_range(msg);
return ret;
}
string& ConstStrBlobPtr::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
ConstStrBlobPtr& ConstStrBlobPtr::incr() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
ConstStrBlobPtr StrBlob::begin() const{
return ConstStrBlobPtr(*this);
}
ConstStrBlobPtr StrBlob::end() const{
auto ret = ConstStrBlobPtr( *this, data->size() );
return ret;
}
(12.23)
void test23() {
const char *c1 = "hello,";
const char *c2 = "world!";
char *res = new char[strlen(c1)+strlen(c2)+1];
strcpy(res, c1);
strcat(res, c2);
cout << res << endl;
string s1 = "HELLO,";
string s2 = "WORLD!";
s1 = s1.append(s2);
res = &s1[0];
cout << res << endl;
}
(12.24)
//好像没有问题,但是这样总是不好的,可能会导致其他的错误
void test24() {
char *r = new char[3];
char c;
char *q = r;
int n = 0;
while(cin >> c && c != 'q') {
*q++ = c;
n++;
}
char *s = r;
while(s != q) {
cout << *s++ << endl;
}
delete[] r;
}
(12.25)
delete [] pa;
(12.26)
void test26() {
int n = 10;
allocator<string> alloc;
auto const p = alloc.allocate(n);
string s;
auto q = p;
while(cin >> s && q != p + n && s != "q") {
alloc.construct(q++, s);
}
const size_t size = q - p;
cout << size << endl;
while(q != p)
alloc.destroy(--q);
alloc.deallocate(p, n);
}
(12.27)
//TextQuery和QueryResult
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <set>
using namespace std;
typedef vector<string>::size_type line_no;
class QueryResult;
class TextQuery {
public:
TextQuery(ifstream&);
QueryResult query(const string &) const;
private:
shared_ptr<vector<string>> file;
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(string s, shared_ptr<set<line_no>> p,
shared_ptr<vector<string>> f):
sought(s), lines(p), file(f){ }
private:
string sought;
shared_ptr<set<line_no>> lines;
shared_ptr<vector<string>> file;
};
ostream &print(ostream & os, const QueryResult &qr) {
os << qr.sought << " occurs " << qr.lines->size() << " "
<< (((qr.lines->size()) > 1)? " times " : " time ") << endl;
for(auto num : *qr.lines) {
os << "\t(line " << num + 1 << ") "
<< *(qr.file->begin() + num) << endl;
}
return os;
}
TextQuery::TextQuery(ifstream &is) : file(new vector<string>) {
string text;
while(getline(is, text)) {
file->push_back(text);
int n = file->size() - 1;
istringstream line(text);
string word;
while(line >> word) {
auto &lines = wm[word];
if(!lines)
lines.reset(new set<line_no>);
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const string &sought) const {
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if(loc == wm.end())
return QueryResult(sought, nodata, file);
else
return QueryResult(sought, loc->second, file);
}
(12.28)
#include "TextQuery.h"
int main() {
ifstream fin;
fin.open("test.txt");
TextQuery tq(fin);
string s;
while(cin>>s && s != "q") {
QueryResult qr = tq.query(s);
print(cout, qr);
}
}
(12.29)
#include "TextQuery.h"
int main() {
ifstream fin;
fin.open("test.txt");
TextQuery tq(fin);
string s;
cin >> s;
do{
QueryResult qr = tq.query(s);
print(cout, qr);
} while(cin>>s && s != "q")
}