练习13.1
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
在用=定义变量时使用;将对象作为实参传递给一个非引用形参时;从一个返回类型非引用的函数返回一个对象;用花括号列表初始化一个数组中的元素或一个聚合类中的成员
练习13.2
因为参数用来初始化的参数是非引用类型,为了调用拷贝构造函数我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数导致无限循环
练习13.3
当我们拷贝StrBlob时,StrBlob的shared_ptr成员的use_count会加一。
当我们复制StrBlobPtr时,StrBlobPtr的weak_ptr成员use_count不会更改。
练习13.4
Point global; Point func(Point p) // (1) foo_bar函数的参数为非引用类型,需拷贝,使用拷贝构造函数 { Point local = p, * heap = new Point(global);//(2)local = p,将p拷贝给local(3)* heap = local,将local拷贝给heap指定的地址 *heap = local; Point pa[4] = { local,*heap }; //(4)(5)Point pa[4] = { local,*heap }; ,将local和* heap拷贝给数组的前两位元素 return *heap; //(6)return* heap; }
练习13.5
class HasPtr { public: HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){} HasPtr(HasPtr &hp):ps(new std::string(*hp.ps)),i(hp.i){} private: std::string* ps; int i; };
练习13.6
拷贝赋值运算符重载赋值运算符“=”,名为operator=,接受一个与所在类相同类型的参数。
为类类型赋值时使用它。
合成拷贝赋值运算符将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。
如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。
练习13.7
int main() { // test codes in main() StrBlob str({ "hello", "world" }); std::cout << "before: " << str.count() << std::endl; // 1 StrBlob str_cp=str; std::cout << "after: " << str_cp.count() << std::endl; // 2 ConstStrBlobPtr p=str; std::cout << "before: " << p.count() << std::endl; // 2 ConstStrBlobPtr p_cp=p; std::cout << "after: " << p.count() << std::endl; // 2 }
练习13.8
class HasPtr { public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0) {} HasPtr(HasPtr& hp) :ps(new std::string(*hp.ps)), i(hp.i) {} HasPtr& operator=(const HasPtr& hp) { ps = new std::string(*hp.ps); i = hp.i; return *this; } private: std::string* ps; int i; };
练习13.9
析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数。
所做工作为:释放对象使用的资源以及销毁对象的非static数据成员。
当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。
练习13.10
当我们销毁StrBlob时,StrBlob的shared_ptr成员的use_count会减一,当为零时销毁所指向内存的数据。
当我们销毁StrBlobPtr时,StrBlobPtr的weak_ptr成员use_count不会更改。
练习13.11
class HasPtr { public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0) {} HasPtr(HasPtr& hp) :ps(new std::string(*hp.ps)), i(hp.i) {} HasPtr& operator=(const HasPtr& hp) { ps = new std::string(*hp.ps); i = hp.i; return *this; } ~HasPtr() { delete ps; } private: std::string* ps; int i; };
练习13.12
发生了三次析构accum,item1,item2
练习13.13
struct X { X() { std::cout << "X()" << std::endl; } X(const X&) { std::cout << "X(const X&)" << std::endl; } X& operator= (const X& x); ~X() { std::cout << "~X()" << std::endl; } }; X& X::operator=(const X& x) { std::cout << "=X()" << std::endl; return* this; } static X xs;//调用默认构造函数 int main() { vector<X> xv;//不调用 X x;//调用默认构造函数 xv.push_back(x);//调用一个拷贝构造函数 xv.emplace_back(x);//调用一个拷贝构造函数,然后析构 X* xn = new X(x); X x1(x); delete xn; { X x3 = x1;//调用一个拷贝构造函数 }//离开定义域时析构 X x2 = x1; }//尚未析构的全部析构
练习13.14
由于b,c都是使用合成拷贝赋值运算符,所以a,b,c实际都是指向相同的mysn数据成员,1001,1001,1001
练习13.15
会改变,因为“=”赋值运算符赋给新对象,会产生拷贝初始化,会调用拷贝构造函数。新的输出结果为1004,1005,1006
练习13.16
修改为const numbered&后就不会在将对象作为实参传递给一个非引用类型的形参时进行拷贝初始化,1001,1002,1003
练习13.17
#ifndef NUMBERED_H_ #define NUMBERED_H_ #include <iostream> using std::cin; using std::cout; using std::endl; class numbered1 { public: numbered1() { mysn = ++initail_number; } ~numbered1(){} friend void f(numbered1 s); private: int mysn; static int initail_number; }; int numbered1::initail_number = 1000; void f(numbered1 s) { cout << s.mysn << endl; } class numbered2 { public: numbered2() { mysn = ++initail_number; } numbered2(const numbered2& n2){ mysn = ++initail_number; } ~numbered2() {} friend void f(numbered2 s); private: int mysn; static int initail_number; }; int numbered2::initail_number = 1000; void f(numbered2 s) { cout << s.mysn << endl; } class numbered3 { public: numbered3() { mysn = ++initail_number; } numbered3(const numbered3& n3) { mysn = ++initail_number; } friend void f(const numbered3& s); ~numbered3() {} private: int mysn; static int initail_number; }; int numbered3::initail_number = 1000; void f(const numbered3& s) { cout << s.mysn << endl; } #endif // !NUMBERED_H_
int main() { numbered1 a1, b1 = a1, c1 = b1; f(a1); f(b1); f(c1); numbered2 a2, b2 = a2, c2 = b2; f(a2); f(b2); f(c2); numbered3 a3, b3 = a3, c3 = b3; f(a3); f(b3); f(c3); }
练习13.18
#ifndef _EMPLOYEE_H_ #define _EMPLOYEE_H_ #include <string> using std::string; class employee { public: employee() = default; employee(string& s) :name(s), identity(++initail_number) {} ~employee() {} private: string name; unsigned identity; static unsigned initail_number; }; unsigned employee::initail_number = 1000; #endif // !_EMPLOYEE_H_
练习13.19
不需要定义自己的拷贝构造成员,因为在现实社会中你不需要有两个完全相同的employee成员
#ifndef _EMPLOYEE_H_ #define _EMPLOYEE_H_ #include <string> using std::string; class Employee { public: Employee() = default; Employee(string& s) :name(s), identity(++initail_number) {} Employee(Employee&) = delete; Employee& operator=(Employee&) = delete; ~Employee() {} private: string name; unsigned identity; static unsigned initail_number; }; unsigned Employee::initail_number = 1000; #endif // !_EMPLOYEE_H_
练习13.20
TextQuery和QueryResult的成员是用智能指针来管理动态内存,因此当我们拷贝、赋值或销毁类对象的时候引用计数会发生改变乃至被销毁
练习13.21
不需要,因为TextQuery和QueryResult的成员是用智能指针来管理动态内存,不需要定义一个自己版本的析构函数,因此不需要拷贝和赋值函数
class TextQuery { friend class QueryResult; public: TextQuery() :text(std::make_shared <StrBlob>()), query_words(std::make_shared<map<string, std::shared_ptr<set<size_t>>>>()) {} TextQuery(TextQuery&) = delete; TextQuery& operator=(TextQuery&) = delete; TextQuery(ifstream& ifs); QueryResult query(string s); ~TextQuery(); private: std::shared_ptr <StrBlob> text; std::shared_ptr<map<string, std::shared_ptr<set<size_t>>>> query_words; }; class QueryResult { public: friend ostream& print(ostream& os, const QueryResult qr); using QueryIterator = set<size_t>::iterator; public: QueryResult(string& w, std::shared_ptr<set<size_t>> n, std::shared_ptr<StrBlob> i) :word(w), nos(n), inputs(i) {} QueryResult(QueryResult&) = delete; QueryResult& operator=(QueryResult&) = delete; ~QueryResult(); QueryIterator begin() { return nos->begin(); } QueryIterator end() { return nos->end(); } std::shared_ptr <StrBlob> get_file() { return inputs; } private: string word; std::shared_ptr<set<size_t>> nos; std::shared_ptr<StrBlob> inputs; };
练习13.22
#ifndef _HASPTR_H_ #define _HASPTR_H_ #include <string> using std::string; class HasPtr { public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0) {} HasPtr(HasPtr& hp) :ps(new std::string(*hp.ps)), i(hp.i) {} HasPtr& operator=(const HasPtr& hp) { delete ps; ps = new std::string(*hp.ps); i = hp.i; return *this; } ~HasPtr() { delete ps; } private: std::string* ps; int i; }; #endif // !_HASPTR_H_
练习13.23
和书中代码相比我的代码不能防范自赋值操作的情况
练习13.24
未定义析构函数:无法释放构造函数分配的内存
未定义拷贝构造函数:无法分配自己的string副本
练习13.25
拷贝构造函数和拷贝赋值运算符:对象拷贝时动态分配内存而不是指向原来对象
不需要析构函数:智能指针能够在离开作用域时会自动销毁对象
练习13.26
StrBlob(const StrBlob& sb) { data = std::make_shared<std::vector<std::string>>(*sb.data); } StrBlob& operator=(const StrBlob& sb) { data= std::make_shared<std::vector<std::string>>(*sb.data); }
练习13.27
#ifndef _HASPTR_H_ #define _HASPTR_H_ #include <string> using std::string; class HasPtr { public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0),use(new size_t(1)) {} HasPtr(HasPtr& hp) :ps(hp.ps), i(hp.i), use(hp.use) { ++* use; } HasPtr& operator=(const HasPtr& hp) { ++* hp.use; if (-- * use == 0) { delete ps; delete use; } ps = hp.ps; i = hp.i; use = hp.use; return *this; } ~HasPtr() { if (-- * use == 0) { delete ps; delete use; } } private: std::string* ps; int i; size_t* use; }; #endif // !_HASPTR_H_
练习13.28
#ifndef _BINSTRTREE_H_ #define _BITSTRTREE_H_ #include <string> using std::string; class TreeNode { public: TreeNode() :value(string()), count(new int(1)), left(nullptr), right(nullptr){} TreeNode(TreeNode& tn) :value(tn.value), count(tn.count), left(tn.left), right(tn.right) { ++count; } TreeNode& operator=(TreeNode& tn); ~TreeNode(); private: std::string value; int* count; TreeNode* left; TreeNode* right; }; TreeNode& TreeNode::operator=(TreeNode& tn) { ++* tn.count; if (-- * count == 0) { delete count; count = nullptr; if (left) { delete left; left = nullptr; } if (right) { delete right; right = nullptr; } } left = tn.left; right = tn.right; count = tn.count; value = tn.value; return *this; } inline TreeNode::~TreeNode() { if (-- * count == 0) { if (left) { delete left; left = nullptr; } if (right) { delete right; right = nullptr; } delete count; count = nullptr; } } class BitStrTree { public: BitStrTree() :root(nullptr) {} BitStrTree(BitStrTree& bst) :root(new TreeNode(*bst.root)) { } BitStrTree& operator=(BitStrTree& tn); ~BitStrTree(); private: TreeNode* root; }; BitStrTree& BitStrTree::operator=(BitStrTree& tn) { auto temp = tn.root; if (root) { delete root; } root = temp; return *this; } inline BitStrTree::~BitStrTree() { if (root) { delete root; } } #endif // !_BINSTRTREE_H_
练习13.29
swap(HasPtr&,HasPtr&)中swap(lhs.ps,rhs.ps)调用的是std::swap(std::string,std::string),swap(lhs.i,rhs.i)调用的是std::swap(int,int),调用的是不同的函数,不会导致递归循环
练习13.30
#ifndef _HASPTR_H_ #define _HASPTR_H_ #include <iostream> #include <string> using std::string; class HasPtr { friend void swap(HasPtr&, HasPtr&); public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0),use(new size_t(1)) {} HasPtr(const HasPtr& hp) :ps(hp.ps), i(hp.i), use(hp.use) { ++* use; } HasPtr& operator=(const HasPtr hp) { ++* hp.use; if (-- * use == 0) { delete ps; delete use; } ps = hp.ps; i = hp.i; use = hp.use; return *this; } bool operator<(const HasPtr& hp) { if (ps<hp.ps||(ps==hp.ps&&i<hp.i)) { return true; } return false; } string& getPs() { return *ps; } ~HasPtr() { if (-- * use == 0) { delete ps; delete use; } } private: std::string* ps; int i; size_t* use; }; void swap(HasPtr& lhp, HasPtr& rhp) { using std::swap; std::cout << "swap" << std::endl; swap(lhp.ps, rhp.ps); swap(lhp.i, rhp.i); } #endif // !_HASPTR_H_
练习13.31
#ifndef _HASPTR_H_ #define _HASPTR_H_ #include <iostream> #include <string> using std::string; class HasPtr { friend void swap(HasPtr&, HasPtr&); public: HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0),use(new size_t(1)) {} HasPtr(const HasPtr& hp) :ps(hp.ps), i(hp.i), use(hp.use) { ++* use; } HasPtr& operator=(const HasPtr hp) { ++* hp.use; if (-- * use == 0) { delete ps; delete use; } ps = hp.ps; i = hp.i; use = hp.use; return *this; } bool operator<(const HasPtr& hp) { if (ps<hp.ps||(ps==hp.ps&&i<hp.i)) { return true; } return false; } string& getPs() { return *ps; } ~HasPtr() { if (-- * use == 0) { delete ps; delete use; } } private: std::string* ps; int i; size_t* use; }; void swap(HasPtr& lhp, HasPtr& rhp) { using std::swap; std::cout << "swap" << std::endl; swap(lhp.ps, rhp.ps); swap(lhp.i, rhp.i); } #endif // !_HASPTR_H_
int main() { HasPtr hp1("hello"); HasPtr hp2("world"); HasPtr hp3("Anna"); vector<HasPtr> hvec; hvec.push_back(hp1); hvec.push_back(hp2); hvec.push_back(hp3); std::sort(hvec.begin(), hvec.end()); for (auto i : hvec) { cout << i.getPs() << endl; } }
练习13.32
一般情况下特定的swap函数能避免不必要的内存分配。但是类指针本身就不会有不必要的内存分配,所以没有从中获益
练习13.33
不定义为Folder的原因是:如果是值传递,则会在传参的时候传递参数的副本,执行修改参数的操作,修改的是参数的副本。而且要为副本重新分配内存,浪费空间。
不定义为const Folder&的原因是:在save和remove中都要改变参数,因此不能定义为const类型。
练习13.34
#ifndef MESSAGE_H_ #define MESSAGE_H_ #include <string> #include <set> class Folder; class Message { friend class Folder; friend void swap(Message&, Message&); public: explicit Message(const std::string &str=""):contents(str){} Message(const Message&); Message& operator= (const Message&); ~Message(); void save(Folder&); void remove(Folder&); private: std::string contents; std::set<Folder*>folders; void add_to_Folders(const Message&); void remove_frome_Folders(); }; Message::Message(const Message&m):contents(m.contents),folders(m.folders) { add_to_Folders(m); } inline Message& Message::operator=(const Message& rhs) { remove_frome_Folders(); contents = rhs.contents; folders = rhs.folders; add_to_Folders(rhs); return*this; } Message::~Message() { remove_frome_Folders(); } inline void Message::save(Folder& f) { folders.insert(&f); f.addMsg(this); } inline void Message::remove(Folder& f) { folders.erase(&f); f.remMsg(this); } inline void Message::add_to_Folders(const Message&m) { for (auto f : m.folders) f->addMsg(this); } inline void Message::remove_frome_Folders() { for (auto f : folders) f->remMsg(this); } void swap(Message& lhs, Message& rhs) { using std::swap; for (auto f : lhs.folders) f->remMsg(&lhs); for (auto f : rhs.folders) f->remMsg(&rhs); swap(lhs.folders, rhs.folders); swap(lhs.contens, rhs.contens); for (auto f : lhs.folders) f->addMsg(&lhs); for (auto f : rhs.folders) f->addMsg(&rhs); } #endif // !MESSAGE_H_
练习13.35
没有将本消息添加到指向m的Folder中
练习13.36
class Folder { friend Message; public: Folder() {} Folder(const Folder&); Folder& operator= (const Folder&); ~Folder(); void addMsg(Message*); void remMsg(Message*); private: std::set<Message*>messages; }; void Folder::addMsg(Message* msg) { messages.insert(msg); } inline void Folder::remMsg(Message* msg) { messages.erase(msg); }
练习13.37
inline void Message::addFolder(Folder* folder) { folders.insert(folder); } inline void Message::remFolder(Folder* folder) { folders.erase(folder); }
练习13.38
使用动态分配的内存时,使用拷贝和交换是一种优雅的方式。但在 Message 类中,没有动态分配的内存。因此,使用是没有意义的,并且由于指向的指针,实现会变得更加复杂。
练习13.39
#ifndef STRVEC_H_ #define STRVEC_H_ #include<memory> #include<string> class StrVec { public: StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {} StrVec(const StrVec&); StrVec& operator=(const StrVec&); ~StrVec(); void push_back(const std::string&); size_t size() const { return first_free - elements; } size_t capacity() const { return cap - elements; } std::string* begin() const { return elements; } std::string* end() const { return first_free; } void reserve(size_t n); size_t capacity(); void resize(size_t n); void resize(size_t n, std::string t); private: static std::allocator<std::string> alloc; void chk_n_alloc() { if (size() == capacity())reallocate(); } std::pair<std::string*, std::string*> alloc_n_copy(const std::string*,const std::string*); void free(); void reallocate(); std::string* elements; std::string* first_free; std::string* cap; }; StrVec::StrVec(const StrVec& sv) { auto newdata = alloc_n_copy(sv.begin(), sv.end()); elements = sv.elements; first_free = cap = newdata.second; } inline StrVec& StrVec::operator=(const StrVec& sv) { auto data = alloc_n_copy(sv.begin(), sv.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } StrVec::~StrVec() { free(); } inline void StrVec::push_back(const std::string& s) { chk_n_alloc(); alloc.construct(first_free++, s); } inline void StrVec::reserve(size_t n) { if (n > capacity()) { auto newdata = alloc.allocate(n); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elements)); free(); elements = newdata; first_free = dest; cap = elements + n; } } inline size_t StrVec::capacity() { return cap - elements;; } inline void StrVec::resize(size_t n) { resize(n, std::string()); } inline void StrVec::resize(size_t n, std::string t) { if (n > size()) { if (n > capacity()) reserve(n * 2); for (size_t i = size(); i != n; ++i) alloc.construct(first_free++, t); } else if (n < size()) { while (first_free != elements + n) alloc.destroy(--first_free); } } inline std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string* b, const std::string* e) { auto data = alloc.allocate(e - b); return {data,uninitialized_copy(b,e,data)}; } inline void StrVec::free() { if (elements) { for (auto p = first_free; p != elements;) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } inline void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; auto newdata = alloc.allocate(newcapacity); auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elements)); free(); elements = newdata; first_free = dest; cap = elements + newcapacity; } #endif // !STRVEC_H_
练习13.40
inline StrVec::StrVec(std::initializer_list<std::string> ls) { auto newdata = alloc_n_copy(ls.begin(), ls.end()); elements = newdata.first; first_free = cap = newdata.second; }
练习13.41
后置递增运算符能对first_free指定的内存构造一个新元素,并移动first_free位置为最后一个实际元素之后的位置。如果使用前置递增运算符就会导致first_free先向后移动,再插入,此时first_free指向第二个位置,且此位置指向一个实际元素。
练习13.42
#include <fstream> using std::ifstream; using std::ofstream; #include <iostream> using std::cin; using std::cout; using std::endl; using std::ostream; #include <map> using std::map; using std::multimap; #include <set> using std::multiset; using std::set; #include <sstream> using std::istringstream; #include <string> using std::string; #include <unordered_map> using std::unordered_map; #include <vector> #include "StrVec.h" using std::vector; #ifndef TEXTQUERT_H_ #define TEXTQUERT_H_ class QueryResult; class TextQuery { friend class QueryResult; public: TextQuery() :text(std::make_shared <StrVec>()), query_words(std::make_shared<map<string, std::shared_ptr<set<size_t>>>>()) {} TextQuery(ifstream& ifs); QueryResult query(string s); ~TextQuery(); private: std::shared_ptr <StrVec> text; std::shared_ptr<map<string, std::shared_ptr<set<size_t>>>> query_words; }; class QueryResult { public: friend ostream& print(ostream& os, const QueryResult qr); using QueryIterator = set<size_t>::iterator; public: QueryResult(string& w, std::shared_ptr<set<size_t>> n, std::shared_ptr<StrVec> i) :word(w), nos(n), inputs(i) {} ~QueryResult(); QueryIterator begin() { return nos->begin(); } QueryIterator end() { return nos->end(); } std::shared_ptr <StrVec> get_file() { return inputs; } private: string word; std::shared_ptr<set<size_t>> nos; std::shared_ptr<StrVec> inputs; }; QueryResult::~QueryResult() { } inline TextQuery::TextQuery(ifstream& ifs) : text(std::make_shared<StrVec>()), query_words(std::make_shared<map<string, std::shared_ptr<set<size_t>>>>()) { size_t size=0; if (ifs) { for (string line; getline(ifs, line); ++size) { text->push_back(line); istringstream iss(line); size = text->size(); for (string text, word; iss >> text; word.clear()) { std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct); // use reference avoid count of shared_ptr add. auto& nos = (*query_words)[word]; if (!nos) nos.reset(new std::set<size_t>); nos->insert(size); } } } } inline QueryResult TextQuery::query(string s) { static std::shared_ptr<std::set<size_t>> nodate(new std::set<size_t>); auto found = query_words->find(s); if (found == query_words->end()) { cout << s + " is not in the text" << endl; return QueryResult(s, nodate, text); } else return QueryResult(s, found->second, text); } TextQuery::~TextQuery() { } ostream& print(ostream& os, const QueryResult qr) { os << qr.word << " occurs " << qr.nos->size() << " times" << endl; for (auto i : *qr.nos) { os << " (line " << i + 1 << ") " << qr.inputs->at(i) << std::endl; } return os; } #endif // !TEXTQUERT_H_
练习13.43
inline void StrVec::free() { if (elements) { for_each(elements, first_free, [this](string& p) {alloc.destroy(&p); }); alloc.deallocate(elements, cap - elements); } }
练习13.44
#ifndef _STRING_H_ #define _STRING_H_ #include <memory> #include <algorithm> class String { public: String() :String("") {} String(const String&); String(const char*); String(size_t,const char); String& operator=(String&); ~String(); size_t size() const { return first_free - elements; } char* begin() const { return elements; } char* end() const { return first_free; } const char& at(size_t pos) const { return *(elements + pos); } private: static std::allocator<char> alloc; std::pair<char*, char*> alloc_n_copy(const char*, const char*); void free(); char* elements; char* first_free; }; std::allocator<char> String::alloc; String::String(const String& s) { std::cout << "拷贝构造函数" << std::endl; auto newdata = alloc_n_copy(s.begin(), s.end()); elements = newdata.first; first_free = newdata.second; } inline String::String(const char* s) { const char* sl =s; while (*sl) ++sl; auto newdata = alloc_n_copy(s, sl); elements = newdata.first; first_free = newdata.second; } inline String::String(size_t n, const char t) { auto data = alloc.allocate(t); elements = data; first_free = std::uninitialized_fill_n(data, n, t); } inline String& String::operator=(String& sv) { std::cout << "拷贝赋值运算符" << std::endl; auto data = alloc_n_copy(sv.begin(), sv.end()); free(); elements = data.first; first_free = data.second; return *this; } String::~String() { free(); } inline std::pair<char*, char*> String::alloc_n_copy(const char* b, const char* e) { auto data = alloc.allocate(e - b); return { data,std::uninitialized_copy(b,e,data) }; } inline void String::free() { if (elements) { std::for_each(elements, first_free, [this](char& p) {alloc.destroy(&p); }); alloc.deallocate(elements, first_free - elements); } } #endif // !STRVEC_H_
练习13.45
左值引用:不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。返回左值的函数有赋值、下标、解引用和前置递增递减运算符。
右值引用:可以绑定到这类表达式,但是不能绑定到左值上。返回右值的函数有算数、关系、位以及后置递增递减运算符。
练习13.46
int f(); vector<int>vi(100); int&& r1 = f();//f()的返回值相当于一个常量,只能使用const的左值引用或者右值引用 int& r2 = vi[0];//下标,左值引用 int& r3 = r1;//此时r1相当于一个变量,左值引用 int&& r4 = vi[0] * f();//算术运算,右值引用
练习13.47
#ifndef _STRING_H_ #define _STRING_H_ #include <memory> #include <algorithm> class String { public: String() :String("") {} String(const String&); String(const char*); String(size_t,const char); String& operator=(String&); ~String(); size_t size() const { return first_free - elements; } char* begin() const { return elements; } char* end() const { return first_free; } const char& at(size_t pos) const { return *(elements + pos); } private: static std::allocator<char> alloc; std::pair<char*, char*> alloc_n_copy(const char*, const char*); void free(); char* elements; char* first_free; }; std::allocator<char> String::alloc; String::String(const String& s) { std::cout << "拷贝构造函数" << std::endl; auto newdata = alloc_n_copy(s.begin(), s.end()); elements = newdata.first; first_free = newdata.second; } inline String::String(const char* s) { const char* sl =s; while (*sl) ++sl; auto newdata = alloc_n_copy(s, sl); elements = newdata.first; first_free = newdata.second; } inline String::String(size_t n, const char t) { auto data = alloc.allocate(t); elements = data; first_free = std::uninitialized_fill_n(data, n, t); } inline String& String::operator=(String& sv) { std::cout << "拷贝赋值运算符" << std::endl; auto data = alloc_n_copy(sv.begin(), sv.end()); free(); elements = data.first; first_free = data.second; return *this; } String::~String() { free(); } inline std::pair<char*, char*> String::alloc_n_copy(const char* b, const char* e) { auto data = alloc.allocate(e - b); return { data,std::uninitialized_copy(b,e,data) }; } inline void String::free() { if (elements) { std::for_each(elements, first_free, [this](char& p) {alloc.destroy(&p); }); alloc.deallocate(elements, first_free - elements); } } #endif // !STRVEC_H_
练习13.48
String baz() { String ret("world"); return ret; } int main(int argc,char*argv[]) { char text[] = "world"; String s0; String s1("hello"); String s2(s0); String s3 = s1; String s4(text); s2 = s1; String s5 = baz(); vector<String> vs; cout << 1<<endl; vs.push_back(s1); cout << 2 << endl; vs.push_back(s2); cout << 3 << endl; vs.push_back(s3); cout << 4 << endl; vs.push_back(s4); cout << 5 << endl; vs.push_back(s5); }
1 拷贝构造函数 2 拷贝构造函数 拷贝构造函数 3 拷贝构造函数 拷贝构造函数 拷贝构造函数 4 拷贝构造函数 拷贝构造函数 拷贝构造函数 拷贝构造函数 5 拷贝构造函数 拷贝构造函数 拷贝构造函数 拷贝构造函数 拷贝构造函数
练习13.49
strVec
inline StrVec::StrVec(StrVec&& sv) noexcept :elements(sv.elements), first_free(sv.first_free), cap(sv.cap) { sv.elements = sv.first_free = sv.cap = nullptr; } inline StrVec& StrVec::operator=(StrVec&&rhs) { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; }
String
inline String::String(String&&s)noexcept:elements(s.elements),first_free(s.first_free) { s.elements = s.first_free = nullptr; } inline String& String::operator=(String&&rhs)noexcept { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; rhs.elements = rhs.first_free = nullptr; } }
Message
inline void Message::move_Folders(Message* m) { folders = std::move(m->folders); for (auto f : folders) { f->remMsg(m); f->addMsg(this); } m->folders.clear(); } inline Message::Message(Message&&m):contents(std::move(m.contents)) { move_Folders(&m); } inline Message& Message::operator=(Message&&rhs) { if (this != &rhs) { remove_frome_Folders(); contents=std::move(rhs.contents); move_Folders(&rhs); } return *this; }
练习13.50
String baz() { String ret("world"); return ret; } int main(int argc,char*argv[]) { char text[] = "world"; String s0; String s1("hello"); String s2(s0); String s3 = s1; String s4(text); s2 = s1; String s5 = baz(); vector<String> vs; cout << 1 << " " << vs.size() << " " << vs.capacity() << endl; vs.push_back(s1); cout << 2 << " " << vs.size() << " " << vs.capacity() << endl; vs.push_back(s2); cout << 3 << " " << vs.size() << " " << vs.capacity() << endl; vs.push_back(s3); cout << 4 << " " << vs.size() << " " << vs.capacity() << endl; vs.push_back(s4); cout << 5 << " " << vs.size() <<" "<<vs.capacity()<< endl; vs.push_back(s5); }
1 0 0 拷贝构造函数 2 1 1 拷贝构造函数 移动构造函数 3 2 2 拷贝构造函数 移动构造函数 移动构造函数 4 3 3 拷贝构造函数 移动构造函数 移动构造函数 移动构造函数 5 4 4 拷贝构造函数 移动构造函数 移动构造函数 移动构造函数 移动构造函数
我们可以观察到vs的编译器中capacity和size在前五个时的等大的因此每次push_back时都要创建一个元素容量增加一的vector,因此除了push_back时在容器末尾使用拷贝构造函数添加的元素外还需要转移容器中其他元素,此时会调用移动构造函数。
练习13.51
不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr,编译器知道要返回的对象将要被销毁,因此会执行一种特殊的"拷贝" -- 移动。最常见的就是从函数返回unique_ptr,因为返回的是右值。
练习13.52
hp = hp2,hp2是一个左值,rhs将使用拷贝构造函数来初始化,拷贝构造函数将分配一个新的string,并拷贝hp2指向的string。hp2本身不会发生变化。
hp = std::move(hp2),我们调用std::move将一个右值引用绑定到hp2上。在此情况下,拷贝构造函数和移动构造函数都是可行的。但是,由于实参是一个右值引用,移动构造函数是精确匹配的。移动构造函数从hp2拷贝指针,而不会分配内存。移动构造函数会修改hp2,使其处于无效状态。
练习13.53
因为operator=函数中,初始化形参会调用一次拷贝构造函数。
HasPtr& operator=(HasPtr& hp) { auto newps = new string(*hp.ps); delete ps; ps = newps; i = hp.i; return *this; } HasPtr& operator=(HasPtr&& hp)noexcept { if (this != &hp) { delete ps; ps = hp.ps; i = hp.i; hp.ps = nullptr; } return *this; }
练习13.54
operator不明确
int main(int argc,char*argv[]) { HasPtr a, b; b = std::move(a); }
练习13.55
void push_back(std::string&& t) { data->push_back(std::move(t)); }
练习13.56
因为此处的局部变量ret是左值 ,因此当我们调用ret.sorted()时,我们实际上不是像预期的那样调用成员函数Foo Foo::sorted() &&,而是调用Foo Foo::sorted() const &。因此,代码将陷入无限递归,并导致致命的堆栈溢出。
练习13.57
因为Foo(*this)返回的是一个右值,因此可以调用sorted的右值版本,返回排序后的Foo。
练习13.58
#include <vector> #include <algorithm> #ifndef _FOO_H_ #define _FOO_H_ class Foo { friend void print(Foo f); public: Foo(std::vector<int>& ivec) : data(ivec) {} Foo sorted()&&; Foo sorted()const&; using Comp = bool(const int&, const int&); Foo sorted(Comp*)&&; Foo sorted(Comp*)const&; private: std::vector<int> data; }; void print(Foo f) { for (const auto& item : f.data) cout << item << " "; } inline Foo Foo::sorted()&& { sort(data.begin(), data.end()); return *this; } Foo Foo::sorted() const& { Foo ret(*this); sort(ret.data.begin(), ret.data.end()); return ret; } inline Foo Foo::sorted(Comp* comp)&& { sort(data.begin(), data.end(), comp); return *this; } inline Foo Foo::sorted(Comp* comp) const& { Foo ret(*this); sort(ret.data.begin(), ret.data.end(), comp); return ret; } #endif // !_FOO_H_
int main(int argc,char*argv[]) { vector<int> a{ 1,5,10,8, 5 }; Foo f(a); print(f.sorted()); }