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; };