12.32
class QueryResult;
using line_no = vector<string >::size_type;
class TextQuery {
public:
TextQuery(ifstream&);
QueryResult query(const string &)const;
private:
StrBlob file;
map<string, shared_ptr<set<line_no>>> wm;
};
TextQuery::TextQuery(ifstream & ifs):file(StrBlob()){
string lineStr, word;
while (getline(ifs, lineStr))
{
file.push_back(lineStr);
int lineCount = file.size();
istringstream ist(lineStr);
while (ist >> word)
{
shared_ptr<set<line_no>>& line = wm[word];
if(!line){
line.reset(new set<line_no>);
}
line->insert(lineCount);
}
}
}
class QueryResult {
friend ostream& print(ostream&, const QueryResult& );
public:
QueryResult(string s, shared_ptr<set<line_no>> ss, StrBlob sp):s(s),ss(ss),sp(sp){};
private:
string s;
shared_ptr<set<line_no>> ss;
StrBlob sp;
};
QueryResult TextQuery::query(const string & word)const{
static shared_ptr<set<line_no>> noData(new set<line_no>);
auto result = wm.find(word);
if(result == wm.end()){
return QueryResult(word, noData, file);
}else{
return QueryResult(word, wm.find(word)->second, file);
}
}
ostream& print(ostream& os, const QueryResult & ptr){
cout << "you find " << ptr.ss->size() << " in file" << endl;
ConstStrBlobPtr sbp(ptr.sp);
for (size_t i = 0; i < ptr.sp.size(); i++, sbp.incr())
{
set<line_no>::iterator haveLine= ptr.ss->find(i);
if(haveLine!= ptr.ss->end()){
cout << sbp.deref() << endl;
}
}
return os;
}
12.33
class QueryResult {
friend ostream& print(ostream&, const QueryResult& );
public:
QueryResult(string s, shared_ptr<set<line_no>> ss, shared_ptr<vector<string>> sp):s(s),ss(ss),sp(sp){};
set<line_no>::iterator& begin() { return ss->begin(); }
set<line_no>::iterator& end() { return ss->end(); }
shared_ptr<vector<string>>& get_file() { return sp; }
private:
string s;
shared_ptr<set<line_no>> ss;
shared_ptr<vector<string>> sp;
};
13.1:
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数
在用=定义变量时会发生
将一个对象作为实参传递给一个非引用类型的形参
从一个返回类型为非引用类型的函数返回一个对象
用花括号列表初始化一个数组中的元素或一个聚合类中的成员
13.2
在函数调用过程中,具有非引用类型的参数要进行拷贝初始化,类似的,当一个函数具有非引用的返回类形时,返回值会被用来初始化调用方的结果
拷贝构造函数被用来初始化非引用类类型参数,这一特性解释了为什么拷贝函数自己的参数必须是引用类形,如果其参数不是引用类型,则调用永远也不会成功--为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环
13.3
按照参数列表,依次调用成员变量的拷贝构造函数
13.4
Point global;
Point foo_bar(Point arg){
Point local = arg, *heap = new Point(global);
*heap = local;
Point pa[4] = {local, *heap};
return *heap;
} 共6次
13.5
class HasPtr {
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)),i(0){}
HasPtr(const HasPtr& has): ps(new std::string(*has.ps)),i(has.i){}
private:
std::string *ps;
int i;
};
13.6
拷贝赋值运算符会将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员,当=两侧为相同类类型时候会使用,当一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个
13.7
将右侧的strblob的非static成员依次调用拷贝赋值函数给左侧的strblob变量
13.8
HasPtr& operator = (HasPtr& has){
ps = new string(*has.ps);
i = has.i;
}
拷贝时需要动态内存重新声请一块string内存,不然两个对象会同时指向一个string,导致空悬指针产生
13.9
析构函数执行与构造函数相反的操作,构造函数初始化对象的非static数据成员,还可能做一些其他工作;析构函数 释放对象使用的资源,并销毁对象的非static数据成员,当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数
13.10
strblob成员变量会依次调用自己的析构函数来销毁自己
13.11
~HasPtr(){}
13.12
3次
形参传入的accum,和局部变量item1,item2
13.13
13.14
猜测,输出同样的值
13.15
会改变,除了a,b和c都是调用的拷贝构造函数,会生成与a不同的序号,f函数调用的时候也会执行拷贝构造函数
13.16
会改变,但f函数调用时候使用的引用,所以输出的值会比使用形参小一号
13.17
13.14测试结果
13.15测试结果
13.16测试结果
与猜测相同
进一步验证
13.18
static int number = 0;
class Employee{
public: Employee():no(number){ number ++;}; Employee(string name) :name(name),no(number) { number++; }private: string name; int no;};13.19
需要,每位雇员都需要一个自己的雇员证号,在使用默认拷贝构造函数时候不会增加雇员证号,会出现重复的号码
Employee(const Employee& emp) :name(emp.name), no(number) { number++; }
13.20
调用其默认的拷贝构造参数,拷贝赋值运算符,析构函数,
13.21
需要,否则经过拷贝构造函数和拷贝赋值运算符生成TextQuery和QueryResult的成员会指向同一片内存,虽然有智能指针不会导致delete出现问题,但是在操作时候会和原先设想不符
TextQuery(const TextQuery& tq):file(new vector<string>(tq.file->begin(), tq.file->end())){
for(auto it : tq.wm){
wm[it.first].reset(new set<line_no>(it.second->begin(), it.second->end()));
}
} ;
QueryResult(const QueryResult& qr):s(qr.s),ss(new set<line_no>(qr.ss->cbegin(), qr.ss->cend())), sp(new vector<string>(qr.sp->cbegin(), qr.sp->cend())){}
默认拷贝构造函数
自定义拷贝构造函数后
13.22
HasPtr& operator = (HasPtr& has){
ps = new string(*has.ps);
i = has.i;
return *this;
}
HasPtr(const HasPtr& has) : ps(new std::string(*has.ps)), i(has.i) {}
值拷贝如上
13.23
没考虑到会销毁运算符左侧资源,验证后,的确发生了内存泄露
13.24
如果未定义析构函数,在变量退出作用域时候,申请的内存不被释放,将导致内存泄露
如果未定义拷贝构造函数会导致a和b的成员指针都指向同一块内存,当其中变量退出作用域时,会释放该区域导致另一个变量内的成员指针变成空悬指针
13.25
因为StrBlob的data是用智能指针定义的,会自动释放,所以不需要析构函数
13.26
StrBlob& operator=(StrBlob& str){
data = make_shared<vector<string>>(*str.data);
return *this;
}
StrBlob(StrBlob& str): data(make_shared<vector<string>>(*str.data)){}
13.27
HasPtr(const HasPtr& has) : ps(has.ps), i(has.i), use_count(has.use_count){ ++*use_count; }
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)),i(0){}
~HasPtr() {
if(--*use_count == 0){
delete ps;
delete use_count;
}
}
13.28
class TreeNode {
TreeNode() :left(0), right(0) {}
TreeNode(const TreeNode& tn) :value(tn.value), count(tn.count),left(tn.left), right(tn.right) {}
TreeNode& operator=(TreeNode& tn){
value = tn.value;
count = tn.count;
left = tn.left;
right= tn.right;
return *this;
}
private:
string value;
int count;
TreeNode *left;
TreeNode *right;
};
class BinStrTree {
public:
BinStrTree():root(0){}
BinStrTree(const BinStrTree& bst):root(bst.root){}
BinStrTree& operator=(BinStrTree& bst){
root = bst.root;
return *this;
}
private:
TreeNode * root;
};