目录
12.1.5 unique_ptr(12.16 ~ 12.18)
12.1.6 weak_ptr(12.19 ~ 12.22)
12.3.1 文本查询程序设计(12.27 ~ 12.29)
12.3.2 文本查询程序类的定义(12.30 ~ 12.33)
工作的间隙看的,所以输出比较慢,希望能巩固基础,再往后深入。
一直有参考这位同学的blog的答案:C++Primer第五版——习题答案+详解(完整版)_MISAYAONE的博客-CSDN博客,不过好像这位同学看的很快有很一些些不是很正确,看评论也有都一一修正(收费了,又找了这个:安月月 - 博客园,又顺藤摸瓜找到了这个:GitHub - Mooophy/Cpp-Primer: C++ Primer 5 answers,YYDS~)。
这个答案也是自己看书然后输出的,也可能有问题,如果有发现什么问题,欢迎评论一起讨论!!
默认大家都有了第5版的纸质书或电子书,这里就只记录题号和答案(其实对原书的截图有点侵犯版权的感觉,狗头保命)
第十二章 动态内存
12.1 动态内存与智能指针
12.1.1 shared_ptr类(12.1 ~ 12.5)
12.1:
b1和b2都包含4个元素。
12.2:
string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
12.3:
不需要,因为这两个函数都不会对参数进行修改。
12.4:
因为data_size类型是size_type,是一个无符号类型,即使是负数,也会自动转化为非负。
12.5:
explicit的作用就是抑制构造函数的隐式转换。
优点:不会自动的进行类型转换,必须清楚的知道类类型。
缺点:必须用构造函数显示创建一个对象,不够方便简单。
12.1.2 直接管理内存(12.6 ~ 12.9)
12.6:
void Input(vector<int>* ivec){
if(!ivec) return;
string s;
while(cin >> s){
stringstream a(s);
int i;
a >> i;
ivec->push_back(i);
}
}
void Output(vector<int>* ivec) {
if(!ivec) return;
for(const auto& i : *ivec){
cout << i << " ";
}
cout << endl;
}
int main() {
auto ivec = IntVectorPointerFactory();
Input(ivec);
Output(ivec);
delete ivec;
ivec = nullptr;
}
// 1 1 1 sdf
// ^Z
// 1 1 1 0
12.7:
shared_ptr<vector<int>> IntVectorPointerFactory(){
return make_shared<vector<int>>();
}
void Input(const shared_ptr<vector<int>>& ivec){
if(!ivec) return;
string s;
while(cin >> s){
stringstream a(s);
int i;
a >> i;
ivec->push_back(i);
}
}
void Output(const shared_ptr<vector<int>>& ivec) {
if(!ivec) return;
for(const auto& i : *ivec){
cout << i << " ";
}
cout << endl;
}
int main() {
auto ivec = IntVectorPointerFactory();
Input(ivec);
Output(ivec);
}
// 1 1 1 sdf
// ^Z
// 1 1 1 0
12.8:
p的类型是int*,函数返回值却是bool型。(错误答案)
p是一个内置类型的指针,返回p会使得p的类型转化为bool类型,其指向的动态内存空间将无法得到释放。
12.9:
r = q,r指向q指向的内存地址,r原指向的内存未发生任何改变;(补充:r原来指向的内存空间将得不到释放,造成内存泄漏)
r2 = q2,q2智能指针的引用计数加一,r2智能指针的引用计数减一,原指针指向的内存释放,现r2指针指向q2。
12.1.3 shared_ptr和new结合使用(12.10 ~ 12.13)
12.10:
正确的,shared_ptr<int> p(new int(42)),引用计数+1,shared_ptr<int>(p)引用计数+1,process调用后,引用计数减1,最后还剩1,可以正确使用。
12.11:
process调用结束后,指针析构,p被销毁,p指向的内存被释放了,再使用p就是未定义。
12.12:
(a) 合法,复制sp引用计数+1,调用完引用计数-1,不影响原sp的使用。
(b) 不合法,不能传递内置指针给shared_ptr,无法隐式转换。
(c) 不合法,与(b)同样的原因,不能传递内置指针给shared_ptr,无法隐式转换。
(d) 合法,但process调用完后不能再使用p,因为p指向的内存被释放。
12.13:
抄的答案:删除p之后,会导致p指向的内存被释放,此时sp就会变成空悬指针,在sp指针被销毁时,该块内存会被二次delete,执行后产生错误:double free
12.1.4 智能指针和异常(12.14 ~ 12.15)
12.14:
没有想到其他写法。
12.15:
void f(destination &d /* 其他参数 */) {
connection c = connect(&d);
shared_ptr<connection> p(&c , [](connection *p){
disconnect(*p);
});
}
12.1.5 unique_ptr(12.16 ~ 12.18)
12.16:
unique_ptr<int> p1(new int(10));
unique_ptr<int> p2(p1);
// unique_ptr<int> p2 = p1;
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>]'|
以下网上答案,没理解
error C2248: “std::unique_ptr<Ty>::unique_ptr”: 无法访问 private 成员(在“std::unique_ptr<Ty>”类中声明)
12.17:
(a)不合法,不能用对象初始化unique_ptr;
(b)合法,pi野指针;
(c)合法,pi2野指针;
(d)合法,ix对象被清空;
(e)合法;
(f)不合法,一块内存对象只能被一个unique_ptr持有。
和网络答案不一致,但也没有很理解,以上为个人理解,欢迎指正。
12.18:
shared_ptr指向的对象可以同时被多个shared_ptr持有,release会将指针置为空,导致其他持有这个指针的shared_ptr对象失效。
12.1.6 weak_ptr(12.19 ~ 12.22)
12.19:
// 这里的代码主要有两个问题
// 1.循环引用 可以在类中声明 在StrBlobPtr后再定义begin end
// 2.需要额外给StrBlobPtr类添加pos函数来比较
class StrBlobPtr;
class StrBlob {
public:
typedef std::vector<std::string>::size_type size_type;
friend class StrBlobPtr;
StrBlob();
StrBlob(std::initializer_list<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();
std::string& back();
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string& msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) {
}
StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {
}
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();
}
void StrBlob::check(size_type i, const string& msg) const {
if(i >= data->size()) {
throw out_of_range(msg);
}
}
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
std::string& deref() const;
StrBlobPtr& incr();
size_t pos() const;
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::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;
}
std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::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;
}
size_t StrBlobPtr::pos() const {
return curr;
}
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
12.20:
int main() {
std::string str{};
StrBlob str_blob{};
while(cin >> str) {
str_blob.push_back(str);
}
for (StrBlobPtr pbeg(str_blob.begin()), pend(str_blob.end()); pbeg.pos() != pend.pos(); pbeg.incr()) {
cout << pbeg.deref() << std::endl;
}
}
12.21:
将合法性检查与元素获取的返回语句分离开来,代码更清晰易读。
12.22:
StrBlobPtr(const StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
12.2 动态数组
12.2.1 new和数组(12.23 ~ 12.25)
12.23:
// dynamically allocated array of char
char *concatenate_string = new char[strlen("hello " "world") + 1]();
strcat(concatenate_string, "hello ");
strcat(concatenate_string, "world");
std::cout << concatenate_string << std::endl;
delete [] concatenate_string;
// std::string
std::string str1{ "hello " }, str2{ "world" };
std::cout << str1 + str2 << std::endl;
// hello world
// hello world
// copy from https://github.com/Mooophy/Cpp-Primer/blob/master/ch12/ex12_23.cpp
12.24:
string str;
cin >> str;
char *input = new char[str.size() + 1]();
strcat(input, str.c_str());
cout << input << endl;
delete [] input;
// input: helloworld
// output: helloworld
12.25:
delete [] pa;
12.2.2 allocator(12.26)
12.26:
allocator<string> alloc; // 可以分配string的allocator对象
auto const p = alloc.allocate(n); // 分配n个未初始化的string
string s;
auto q = p;
while(cin >> s && q != p + n)
alloc.construct(q++, s);
// 输出 & 析构
while(q != p) {
cout << *--q << endl;
alloc.destroy(q);
}
// 释放内存
alloc.deallocate(p, n);
12.3 使用标准库:文本查询程序
12.3.1 文本查询程序设计(12.27 ~ 12.29)
12.27:
class TextQuery{
public:
TextQuery(ifstream &infile);
void query(string s);
private:
vector<string> line_text_{};
map<string, set<size_t>> word_lines_{};
};
TextQuery::TextQuery(ifstream &infile){
string line_string;
size_t line = 1;
while(getline(infile, line_string)){
cout << line_string << endl;
line_text_.push_back(line_string);
istringstream stream(line_string);
string word;
while(stream >> word){
auto iter = word_lines_.find(word);
set<size_t> lines{};
if(iter != word_lines_.end()) {
lines = iter->second;
}
lines.insert(line);
word_lines_[word] = lines;
}
line++;
}
}
void TextQuery::query(string s) {
auto iter = word_lines_.find(s);
if(iter != word_lines_.end()){
auto lines = iter->second;
cout << s << " occurs " << lines.size() << " times" << endl;
for(const auto index : lines) {
if(index >= 1 && index <= line_text_.size()){
cout << "(line "<< index << ") " << line_text_[index - 1] << endl;
}
}
}
}
void runQueries(ifstream &infile) {
// infile 是一个ifstream,指向我们要处理的文件
TextQuery tq(infile); // 保存文件并建立查询map
// 与用户交互:提示用户输入要查询的单词,完成查询并打印结果
while(true) {
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到文件尾或用户输入了'q'时循环终止
if(!(cin >> s) || s == "q") break;
// 指向查询并打印结果
//print(cout, tq.query(s)) << endl;
tq.query(s);
}
}
int main() {
ifstream fstrm1("1.txt");
runQueries(fstrm1);
}
// 自己写的 有点简单 只用了一个类 也没有用到智能指针
// 输入是文件1.txt 会打印出输入的内容
/*
brb be right back
k okay?
y why
r are
u you
pic picture
thk thanks~
18r later
k okay!
enter word to look for, or q to quit: k
k occurs 2 times
(line 2) k okay?
(line 9) k okay!
enter word to look for, or q to quit: r
r occurs 1 times
(line 4) r are
enter word to look for, or q to quit: q
*/
// https://github.com/Mooophy/Cpp-Primer/tree/master/ch12 移除了输入中的标点符号
12.28:
int main() {
ifstream fstrm1("1.txt");
vector<string> line_text{};
map<string, set<size_t>> word_lines{};
string line_string;
size_t line = 1;
while(getline(fstrm1, line_string)){
cout << line_string << endl;
line_text.push_back(line_string);
istringstream stream(line_string);
string word;
while(stream >> word){
auto iter = word_lines.find(word);
set<size_t> lines{};
if(iter != word_lines.end()) {
lines = iter->second;
}
lines.insert(line);
word_lines[word] = lines;
}
line++;
}
while(true) {
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到文件尾或用户输入了'q'时循环终止
if(!(cin >> s) || s == "q") break;
// 指向查询并打印结果
//print(cout, tq.query(s)) << endl;
auto iter = word_lines.find(s);
if(iter != word_lines.end()){
auto lines = iter->second;
cout << s << " occurs " << lines.size() << " times" << endl;
for(const auto index : lines) {
if(index >= 1 && index <= line_text.size()){
cout << "(line "<< index << ") " << line_text[index - 1] << endl;
}
}
}
}
}
// 输出与上面一致
12.29:
void runQueries(ifstream &infile) {
// infile 是一个ifstream,指向我们要处理的文件
TextQuery tq(infile); // 保存文件并建立查询map
// 与用户交互:提示用户输入要查询的单词,完成查询并打印结果
do{
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到文件尾或用户输入了'q'时循环终止
if(!(cin >> s) || s == "q") break;
// 指向查询并打印结果
//print(cout, tq.query(s)) << endl;
tq.query(s);
}
while(true);
}
// 倾向于直接用while,清晰
// https://www.cnblogs.com/songshuguiyu/p/9598196.html 这里写do while更简洁…
12.3.2 文本查询程序类的定义(12.30 ~ 12.33)
12.30:
class QueryResult; // 为了定义函数query的返回类型
class TextQuery {
public:
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string&) const;
private:
std::shared_ptr<std::vector<std::string>> file; // 输入文件
// 每个单词到它所在的行号的集合的映射
std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
};
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) { // 对行中每个单词
// 如果单词不在wm中,以之为下标在wm中添加一项
auto &lines = wm[word]; // lines是shared_ptr(如果lines为空没有使用引用是会失败的)
if(!lines) // 在第一次遇到此单词时,此指针为空
lines.reset(new set<line_no>); // 分配一个新的set
lines->insert(n); // 将此行号插入set中
}
}
}
class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&); // 友元函数不需要放在public或private里吗?
public:
QueryResult(std::string s, std::shared_ptr<std::set<TextQuery::line_no>> p, std::shared_ptr<std::vector<std::string>> f) : sought(s), lines(p), file(f) { }
private:
std::string sought; // 查询单词
std::shared_ptr<std::set<TextQuery::line_no>> lines; // 出现的行号
std::shared_ptr<std::vector<std::string>> file; // 输入文字
};
QueryResult TextQuery::query(const string & sought) const {
// 如果未找到sought,将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
// 使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
auto loc = wm.find(sought);
if(loc == wm.end())
return QueryResult(sought, nodata, file); // 未找到
else
return QueryResult(sought, loc->second, file);
}
void runQueries(ifstream &infile) {
// infile 是一个ifstream,指向我们要处理的文件
TextQuery tq(infile); // 保存文件并建立查询map
// 与用户交互:提示用户输入要查询的单词,完成查询并打印结果
while(true) {
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到文件尾或用户输入了'q'时循环终止
if(!(cin >> s) || s == "q") break;
// 指向查询并打印结果
print(cout, tq.query(s)) << endl;
}
}
string make_plural(size_t ctr, const string &word, const string &ending) {
return ctr > 1 ? word + ending : word;
}
ostream &print(ostream &os, const QueryResult &qr) {
// 如果找到了单词,打印出现次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << " " << make_plural(qr.lines->size(), "times", "s") << endl;
// 打印单词出现的每一行
for(auto num : *qr.lines) // 对set中每个单词
// 避免行号从0开始给用户带来的困惑
os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
return os;
}
int main() {
ifstream fstrm1("1.txt");
runQueries(fstrm1);
}
// 12.27写过了,这里将书中的代码copy运行
12.31:
一行中有两个重复的单词,输出时,如果vector不过滤,会将这一行输出两遍,使用set不用过滤。
12.32:
class StrBlobPtr;
class StrBlob {
public:
typedef std::vector<std::string>::size_type size_type;
friend class StrBlobPtr;
StrBlob();
StrBlob(std::initializer_list<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();
std::string& back();
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string& msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) {
}
StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {
}
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();
}
void StrBlob::check(size_type i, const string& msg) const {
if(i >= data->size()) {
throw out_of_range(msg);
}
}
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(const StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
std::string& deref() const;
StrBlobPtr& incr();
size_t pos() const;
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::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;
}
std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::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;
}
size_t StrBlobPtr::pos() const {
return curr;
}
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
class QueryResult; // 为了定义函数query的返回类型
class TextQuery {
public:
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string&) const;
private:
std::shared_ptr<std::vector<std::string>> file; // 输入文件
std::shared_ptr<StrBlob> file2;
// 每个单词到它所在的行号的集合的映射
std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
};
TextQuery::TextQuery(ifstream &is) : file2(make_shared<StrBlob>()) {
string text;
while(getline(is, text)) { // 对文件中每一行
file2->push_back(text); // 保存此行文本
int n = file2->size() - 1; // 当前行号
istringstream line(text); // 将行文本分解为单词
string word;
while(line >> word) { // 对行中每个单词
// 如果单词不在wm中,以之为下标在wm中添加一项
auto &lines = wm[word]; // lines是shared_ptr(如果lines为空没有使用引用是会失败的)
if(!lines) // 在第一次遇到此单词时,此指针为空
lines.reset(new set<line_no>); // 分配一个新的set
lines->insert(n); // 将此行号插入set中
}
}
}
class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&); // 友元函数不需要放在public或private里吗?
public:
QueryResult(std::string s, std::shared_ptr<std::set<TextQuery::line_no>> p, std::shared_ptr<StrBlob> f) : sought(s), lines(p), file2(f) { }
private:
std::string sought; // 查询单词
std::shared_ptr<std::set<TextQuery::line_no>> lines; // 出现的行号
std::shared_ptr<std::vector<std::string>> file; // 输入文字
std::shared_ptr<StrBlob> file2; // 输入文字
};
QueryResult TextQuery::query(const string & sought) const {
// 如果未找到sought,将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
// 使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
auto loc = wm.find(sought);
if(loc == wm.end())
return QueryResult(sought, nodata, file2); // 未找到
else
return QueryResult(sought, loc->second, file2);
}
void runQueries(ifstream &infile) {
// infile 是一个ifstream,指向我们要处理的文件
TextQuery tq(infile); // 保存文件并建立查询map
// 与用户交互:提示用户输入要查询的单词,完成查询并打印结果
while(true) {
cout << "enter word to look for, or q to quit: ";
string s;
// 若遇到文件尾或用户输入了'q'时循环终止
if(!(cin >> s) || s == "q") break;
// 指向查询并打印结果
print(cout, tq.query(s)) << endl;
}
}
string make_plural(size_t ctr, const string &word, const string &ending) {
return ctr > 1 ? word + ending : word;
}
ostream &print(ostream &os, const QueryResult &qr) {
// 如果找到了单词,打印出现次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << " " << make_plural(qr.lines->size(), "times", "s") << endl;
// 打印单词出现的每一行
for(auto num : *qr.lines) { // 对set中每个单词
// 避免行号从0开始给用户带来的困惑
StrBlobPtr p(*qr.file2, num);
os << "\t(line " << num + 1 << ") " << p.deref() << endl;
}
return os;
}
int main() {
ifstream fstrm1("1.txt");
runQueries(fstrm1);
}
// 将之前的StrBlob和StrBlobPtr也放过来了
// 里面有一些上题无用的变量file没有删哇~
12.33:
class QueryResult {
using ResultIter = std::set<StrBlob::size_type>::iterator;
friend std::ostream& print(std::ostream&, const QueryResult&); // 友元函数不需要放在public或private里吗?
public:
QueryResult(std::string s, std::shared_ptr<std::set<TextQuery::line_no>> p, std::shared_ptr<StrBlob> f) : sought(s), lines(p), file2(f) { }
ResultIter begin() const { return nos->begin(); }
ResultIter end() const { return nos->end(); }
shared_ptr<StrBlob> get_file() const { return file2; }
private:
std::string sought; // 查询单词
std::shared_ptr<std::set<TextQuery::line_no>> lines; // 出现的行号
std::shared_ptr<std::vector<std::string>> file; // 输入文字
std::shared_ptr<StrBlob> file2; // 输入文字
shared_ptr<std::set<StrBlob::size_type>> nos;
};
// copy了一下这里的https://github.com/Mooophy/Cpp-Primer/blob/master/ch12/ex12_33.h