习题答案置于一个 .h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行12.22题为#define NUM1222;
chapter 12
1. 新标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。新标准提供的这两种智能指针的区别在于
管理底层指针的方式:shared_ptr允许多个指针指向同一个对象,unique_ptr则‘独占’所指向的对象。还定义了一个名为week_ptr的伴随类,它是一种弱引用,指向shared_ptr
所管理的对象。三种都定义在memory头文件中.
最安全的分配是使用动态内存的方法是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并开始初始化它,返回指向此对象的shared_ptr。
shared_ptr<int> p3 = make_shared<int>(42); //auto p3
auto q(p3); //p和q指向相同对象,次对象有两个引用
每个shared_ptr都有一个关联的计数器,通常称为引用计数。当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,
它所关联的计数器就会递增,当shared_ptr被销毁或离开局部作用域时,计数器递减。
2. 如果将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。
使用动态生存期的资源类:
a 程序不知道自己需要使用多少对象
b 程序不知道所需对象的准确类型
c 程序需要在多个对象间共享数据
使用动态内存的一个常见原因是允许多个对象共享相同的状态。
3. 默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值将是未定义的,而类类型对象将用默认构造函数进行初始化。
如果new不能分配所要求的内存空间,它会抛出一个类型为bad_alloc的异常。可以改变new的方式来阻止抛出异常。
int *p2 = new(nothrow) int; //如果分配失败,new返回一个空指针
bad_alloc和nothrow定义在new头文件中。
4. 我们传递给delete的指针必须指向动态分配的内存,或者是一个空指针。释放一块并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的。
delete一个指针后,指针值变成无效。但很多机器上指针仍然保存着动态内存的地址。delete之后指针就变成了空悬指针。
避免空悬指针的方法: 在指针即将要离开其作用域之前释放掉它所关联的内存。
5. 接受指针参数的智能指针构造函数是explicit的,因此我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针。
shared_ptr<int> p1 = new int(1024); //错误。必须使用直接初始化形式
shared_ptr<int> p2(new int(1024)); //正确,使用了直接初始化形式
同理,一个返回的shared_ptr的函数不能在其返回语句中隐式转换一个普通指针。
5. 使用一个内置指针访问一个智能指针所负责的对象很危险,因为我们不知道对象何时会销毁。也不要使用get初始化另一个智能指针或为智能指针赋值。
智能指针类型定义了一个名为get的函数。它返回一个内置指针,指向智能指针管理的对象。 因为我们有时需要向不能使用智能指针的代码传递一个内置指针,
用reset来将一个新的指针赋予一个shared_ptr:
shared_ptr<int> p(new int(42));
p = new int(1024); //错误,不能将一个指针赋予shared_ptr
p.reset(new int(1024)); //正确,p指向一个新对象
6. unique_ptr'拥有'它所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。
由于unique_ptr拥有它所指向的对象,因此unique_ptr不支持普通的拷贝和赋值操作。但是可以通过调用release和reset将指针的所有权从一个unique_ptr转移给另一个
unique_ptr。当然有一个例外,我们可以拷贝或赋值一个将要被销毁的unique_ptr,常见的是从函数返回一个unique_ptr:
7. week_ptr是一个不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将week_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
创建一个week_ptr时用一个shared_ptr来初始化它。
由于对象可能不存在,我们不能使用week_ptr直接访问对象,而必须调用lock,它检查week_ptr指向的对象是否存在。
8. 当new分配一个数组时,我们并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。即使使用类型别名定义了一个数组类型,new也不会分配一个数组类型的对象。
由于分配的内存不是一个数组类型,所以不能对动态数组调用begin和end。
记住我们所说的动态数组并不是数组类型。
当释放一个指向数组的指针时,空方括号是必须的:它指示编译器此指针指向一个对象数组的第一个元素。如果忽略了[]的行为是未定义的。
allocator类定义在memory中,它帮助我们将内存分配和对象构造分离开来。提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。
alloc.construct(q++, "hi"); //*q为hi
为了使用allocate返回的内存,我们必须用construct构造对象,使用未构造的内存,其行为是未定义的。
//Chapter12.h
#ifndef CHAPTER12_H
#define CHAPTER12_H
#include <iostream>
#include <vector>
using namespace std;
/*12.2*/
class StrBlobPtr; //12.19
class ConstStrBlobPtr;
class StrBlob{
friend class StrBlobPtr;
friend class ConstStrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob(): data(make_shared<vector<string>>()){ }
StrBlob(initializer_list<string> li) : data(make_shared<vector<string>>(li)) {}
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();
// void push_back(const string &t)const { data-> push_back(t); }
string& back() {
check(0, "back on empty StrBlob");
return data->back();
}
string& front(){
check(0, "front on empty StrBlob");
return data->front();
}
const string& front()const{
check(0, "front on empty StrBlob");
return data->front();
}
const string& back() const{
check(0, "back on empty StrBlob");
return data->back();
}
StrBlobPtr begin(); //12,19
StrBlobPtr end(); //12.19
ConstStrBlobPtr begin() const; // should add const 12.22
ConstStrBlobPtr end() const; // should add const 12.22
private:
void check(size_type i, const string &msg) const{
if(i >= data->size())
throw out_of_range(msg);
}
shared_ptr<vector<string>> data;
};
/*12.27*/
class QueryResult;
class TextQuery{
public:
typedef vector<string>::size_type LineNo;
TextQuery(ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<vector<string>> input;
map<string, shared_ptr<set<LineNo>>> result;
};
class QueryResult{
friend ostream& print(ostream&, const QueryResult&);
public:
typedef set<StrBlob::size_type>::iterator ResultIter;
QueryResult(const string& s, shared_ptr<set<TextQuery::LineNo>> set,
shared_ptr<vector<string>> pvec): word(s), nos(set), input(pvec){ }
ResultIter begin() const { return nos->begin(); } //12.33
ResultIter end() const { return nos->end(); }
shared_ptr<vector<string>> get_file() const { return input; } //12.33
private:
string word;
shared_ptr<set<TextQuery::LineNo>> nos;
shared_ptr<vector<string>> input;
};
/*12.32*/
class QueryResult_32;
class TextQuery_32 {
public:
TextQuery_32(ifstream&);
QueryResult_32 query(const string&) const;
private:
shared_ptr<StrBlob> input;
map<string, shared_ptr<set<StrBlob::size_type>>> result;
};
class QueryResult_32 {
public:
friend ostream& print(ostream&, const QueryResult_32&);
public:
QueryResult_32(const string& s, shared_ptr<set<StrBlob::size_type>> set,
shared_ptr<StrBlob> v)
: word(s), nos(set), input(v) { }
private:
string word;
shared_ptr<set<StrBlob::size_type>> nos;
shared_ptr<StrBlob> input;
};
ostream& print(ostream&, const QueryResult_32&);
#endif
//main.cc
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include "Chapter12.h"
using namespace std;
#define NUM1232
/*12.6*/
#ifdef NUM126
vector<int>* vector_create(){
vector<int>* pvec = new vector<int>();
return pvec;
}
void vector_input(vector<int>* pvec){
cout << "Input number: " <<endl;
for(int i=0; cin >> i; pvec->push_back(i), ++i)
;
}
void vector_print(vector<int>* pvec){
for(auto &i : *pvec) cout << i << " ";
cout <<endl;
}
#endif
/*12.7*/
#ifdef NUM127
shared_ptr<vector<int>> vector_create(){
auto pvec = make_shared<vector<int>>();
return pvec;
}
void vector_input(shared_ptr<vector<int>> pvec){
cout << "Input number: " <<endl;
for(int i=0; cin >> i; pvec->push_back(i), ++i)
;
}
void vector_print(shared_ptr<vector<int>> pvec){
for(auto &i : *pvec) cout << i << " ";
cout <<endl;
}
#endif
/*12.10*/
void process(shared_ptr<int> ptr)
{
std::cout << "inside the process function:" << ptr.use_count() << "\n";
}
/*12.14*/
struct destination{
string ip;
int port;
destination(string _ip, int _port):ip(_ip), port(_port){}
};
struct connection{
string ip;
int port;
connection(string _ip, int _port): ip(_ip), port(_port){}
};
connection connect(destination* pdes){
shared_ptr<connection> pcon = make_shared<connection>(pdes-> ip, pdes->port);
cout << "Connection... "<< pcon.use_count() <<endl;
return *pcon;
}
void disconnect(connection pcon){
cout <<"connection close "<< pcon.ip <<":"<<pcon.port<<endl;
}
void end_connection(connection *p){
disconnect(*p);
}
void f(destination &d){
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
cout << "maked connection: " <<p.use_count() <<endl;
}
/*12.15*/
void f_15(destination &d){
connection c = connect(&d);
shared_ptr<connection> p(&c, [](connection *p){disconnect(*p); });
cout << "maked connection: " <<p.use_count() <<endl;
}
/*12.19*/
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;
StrBlobPtr& incr();
private:
shared_ptr<vector<string>> check(size_t, const string&)const;
weak_ptr<vector<string>> wptr;
size_t curr;
};
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob:: end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
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;
}
/*12.22*/
class ConstStrBlobPtr {
public:
ConstStrBlobPtr() : curr(0) {}
ConstStrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {} // should add const
bool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; }
const string& deref() const
{ // return value should add const
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
ConstStrBlobPtr& incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
private:
std::shared_ptr<vector<string>> 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<vector<string>> wptr;
size_t curr;
};
ConstStrBlobPtr StrBlob::begin() const{ //add const
return ConstStrBlobPtr(*this);
}
ConstStrBlobPtr StrBlob::end() const{ //add const
return ConstStrBlobPtr(*this, data->size());
}
/*12.27*/
TextQuery::TextQuery(ifstream& ifs) : input(new vector<string>){
LineNo lineNo{0};
for(string line; getline(ifs, line); ++lineNo){
input -> push_back(line);
istringstream line_stream(line);
for(string text, word; line_stream >> text; word.clear()){
remove_copy_if(text.begin(), text.end(), back_inserter(word), ::ispunct);
//防止shared_ptr的计数增加,用引用
auto &nos = result[word];
if(!nos)
nos.reset(new set<LineNo>);
nos -> insert(lineNo);
}
}
}
/*12.30*/
QueryResult TextQuery::query(const string& str)const{
static shared_ptr<set<LineNo>> nodata(new set<LineNo>);
auto found = result.find(str);
if(found == result.end())
return QueryResult(str, nodata, input);
else
return QueryResult(str, found->second, input);
}
ostream& print(ostream& out, const QueryResult& res){
out << res.word << " occurs " <<res.nos->size() <<
(res.nos->size() >1 ? " times" : " time") <<endl;
for(auto i : *res.nos)
out << "\t(line " << i+1<<") "<<res.input->at(i) <<endl;
return out;
}
void runQueries(ifstream& infile){
TextQuery tq(infile);
while (true) {
cout << "Enter word to find, input 'quit' out: ";
string s;
if (!(cin >> s) || s == "q") break;
print(cout, tq.query(s)) << endl;
}
}
/*12.32*/
TextQuery_32::TextQuery_32(ifstream& ifs) : input(new StrBlob){
StrBlob::size_type lineNo{0};
for (string line; getline(ifs, line); ++lineNo) {
input->push_back(line);
istringstream line_stream(line);
for (string text, word; line_stream >> text; word.clear()) {
remove_copy_if(text.begin(), text.end(),
back_inserter(word), ::ispunct);
auto& nos = result[word];
if (!nos) nos.reset(new set<StrBlob::size_type>);
nos->insert(lineNo);
}
}
}
QueryResult_32 TextQuery_32::query(const string& str) const{
static shared_ptr<set<StrBlob::size_type>> nodate(
new set<StrBlob::size_type>);
auto found = result.find(str);
if (found == result.end())
return QueryResult_32(str, nodate, input);
else
return QueryResult_32(str, found->second, input);
}
ostream& print(ostream& out, const QueryResult_32& qr){
out << qr.word << " occurs " << qr.nos->size()
<< (qr.nos->size() > 1 ? " times" : " time") << endl;
for (auto i : *qr.nos) {
ConstStrBlobPtr p(*qr.input, i);
out << "\t(line " << i + 1 << ") " << p.deref() << endl;
}
return out;
}
int main(){
/*12.1*/
#ifdef NUM121
cout << "对象b2被销毁,但是b2中的元素并没有被销毁,因为b1正在使用,所以b1和b2都有4个元素. " <<endl;
#endif
/*12.2*/
#ifdef NUM122
const StrBlob csb{"hello", "world", "linux"};
StrBlob sb{"hello", "world", "c++"};
cout << csb.front() << " " << csb.back() << endl;
sb.back() = "primer";
cout << sb.front() << " " << sb.back() << endl;
#endif
/*12.3*/
#ifdef NUM123
cout <<"可以加,因为data并没有被修改,它是指向vetor类型的指针,但是没有意义,"<<endl;
#endif
/*12.4*/
#ifdef NUM124
cout << "因为vector<string>::size_type是unsigned类型,当小于0时会自动转换为大于0的数." <<endl;
#endif
/*12.5*/
#ifdef NUM125
cout <<"explicit防止initializer_list到StrBlob的自动转换. "<<endl;
#endif
/*12.6*/
#ifdef NUM126
vector<int>* ptr = vector_create();
vector_input(ptr);
vector_print(ptr);
delete ptr;
#endif
/*12.7*/
#ifdef NUM127
shared_ptr<vector<int>> ptr = vector_create();
vector_input(ptr);
vector_print(ptr);
#endif
/*12.8*/
#ifdef NUM128
cout << "p转换成bool类型,最后没有释放,内存泄漏. "<<endl;
#endif
/*12.9*/
#ifdef NUM129
cout << "智能指针会自动释放,但r被赋值之后没有释放,内存泄漏. "<<endl;
#endif
/*12.10*/
#ifdef NUM1210
shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p));
cout << p.use_count() << endl;
auto q = p;
cout << p.use_count() << "\n";
cout << "the int p now points to is:" << *p << "\n";
#endif
/*12.11*/
#ifdef NUM1211
shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p.get()));
cout << "shared_ptr<int>(p.get())创建一个临时的智能指针,并且拷贝到参数中.因为其并不是p的拷贝,函数结束内存就被释放."<<endl;
#endif
/*12.12*/
#ifdef NUM1212
cout << "(a)合法(b)不合法,普通指针不能隐式的转换成智能指针.(c)不合法,原因同上.(d)合法,但不提倡,很容易导致问题. "<<endl;
#endif
/*12.13*/
#ifdef NUM1213
cout <<"导致错误,两次释放同一内存. "<<endl;
auto sp = std::make_shared<int>();
auto p = sp.get();
delete p;
#endif
/*12.14*/
#ifdef NUM1214
destination dest("192.168.93.129", 8080);
f(dest);
#endif
/*12.15*/
#ifdef NUM1215
destination dest("192.168.93.129", 8080);
f_15(dest);
#endif
/*12.16*/
#ifdef NUM1216
unique_ptr<string> p1(new string("Selinux"));
unique_ptr<string> p2(p1); //拷贝操作:error
unique_ptr<string> p3;
p3 = p2; //赋值:error
#endif
/*12.17*/
#ifdef NUM1217
int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef std::unique_ptr<int> IntP;
IntP p0(ix); //invalid conversion from ‘int’ to ‘std::unique_ptr<int>::pointer
IntP p1(pi); //能编译通过,但运行时出错, unique_ptr p1处理完成时要delete这时没有对象来free
IntP p2(pi2); //编译通过,但导致悬吊指针
IntP p3(&ix); //同Intp p1(pi);
IntP p4(new int(2048)); //正确
IntP p5(p2.get()); //error, double free
#endif
/*12.18*/
#ifdef NUM1218
cout <<"因为shared_ptr可以拷贝,其他指向对象的指针也能够释放该对象的内存,因此release是无效的."<<endl;
#endif
/*12.19*/
#ifdef NUM1219
StrBlob b1;
{
StrBlob b2{ "a", "an", "the" };
b1 = b2;
b2.push_back("about");
cout << b2.size() << endl;
}
cout << b1.size() << endl;
for (StrBlobPtr it = b1.begin(), pend = b1.end(); it != pend; it.incr())
cout << it.deref() << endl;
#endif
/*12.20*/
#ifdef NUM1220
fstream ifs("./book_sales");
StrBlob blob;
for(string str; getline(ifs, str); blob.push_back(str))
;
for(StrBlobPtr pbeg(blob.begin()), pend(blob.end()); pbeg !=pend; pbeg.incr())
cout << pbeg.deref() <<endl;
#endif
/*12.21*/
#ifdef NUM1221
cout << "运来的版本更好,因为其看上去更为简单." <<endl;
#endif
/*12.22*/
#ifdef NUM1222
cout <<"见Chapter12.h声明. "<<endl;
#endif
/*12.23*/
#ifdef NUM1223
{
char *pchar = new char[256]();
const char* str1 = "hello"; const char* str2 = "world";
strcat(pchar, str1);
strcat(pchar, str2);
cout << pchar << endl;
delete []pchar;
}
// string
string str1{"hello "}, str2{"world"};
cout << str1 + str2 << endl;
#endif
/*12.24*/
#ifdef NUM1224
cout << "How long do you want the string be ? ";
int size{0};
cin >> size;
char* input = new char[size + 1]();
cin.ignore(); //cin.ingore(256, '\n');
cout << "input the string: ";
cin.get(input, size + 1);
cout << input <<endl;
delete[] input;
#endif
/*12.25*/
#ifdef NUM1225
int *pa = new int[10];
delete []pa;
#endif
/*12.26*/
#ifdef NUM1226
int n = 5;
allocator<string> alloc;
auto const p = alloc.allocate(n); //allocator<T> a, a.allocate(n),保存n个类型为T的对象.
string s;
auto q = p;
while(cin >> s && q != p + n)
alloc.construct(q++, s); //p为T*的指针
while(q != p){
cout << *--q <<" ";
alloc.destroy(q);
}
alloc.deallocate(p, n);
#endif
/*12.27*/
#ifdef NUM1227
cout <<"见Chapter.h 及 main()外声明. "<<endl;
#endif
/*12.28*/
#ifdef NUM1228
ifstream file("./storyDataFile");
vector<string> input;
map<string, set<decltype(input.size())>> dictionary;
decltype(input.size()) LineNo{0};
for(string line; getline(file, line); ++LineNo){
input.push_back(line);
istringstream line_stream(line);
for(string text, word; line_stream >> text; word.clear()){
remove_copy_if(text.begin(), text.end(), back_inserter(word), ::ispunct);
dictionary[word].insert(LineNo);
}
}
while(1){
cout << "Enter word to query, or input 'quit' out"<<endl;
string s;
if(!(cin >> s) || s == "quit")
break;
auto found = dictionary.find(s);
if(found != dictionary.end()){
cout << s <<" occurs: " << found->second.size()
<<(found->second.size() > 1 ? " times" : " time")<<endl;
for(auto i : found->second)
cout << "\t(line " << i+1 << ") "<<input.at(i) <<endl;
}else
cout << s <<" occurs 0 time"<<endl;
}
#endif
/*12.29*/
#ifdef NUM1229
string rap;
do{
cout << "Enter word to query, or input 'quit' out"<<endl;
string s;
if(!(cin >> s))
print(cout, tq.query(s)) << endl;
cin >> rap;
}while(!rap.empty() && rap[0] != 'n');
cout << "do while 版本更符合直觉. "<<endl;
#endif
/*12.30*/
#ifdef NUM1230
ifstream file("./storyDataFile");
runQueries(file);
#endif
/*12.31*/
#ifdef NUM1231
cout << "set中不存在重复的关键字,vector并不能保证这一点.所以这里set更合适."<<endl;
#endif
/*12.32*/
#ifdef NUM1232
ifstream infile("./storyDataFile");
TextQuery_32 tq(infile);
while (true) {
cout << "Enter word to find, input 'quit' out: ";
string s;
if (!(cin >> s) || s == "q") break;
print(cout, tq.query(s)) << endl;
}
#endif
/*12.33*/
#ifdef NUM1233
cout <<"见Chapter12.h声明. "<<endl;
#endif
return 0;
}
参考资料:
c++ primer中文版第五版,电子工业出版社。
c++ primer第四版习题解答,人民邮电出版社。
pezy@github https://github.com/pezy/CppPrimer