C++ Prime 第十三章 拷贝控制
2023-05-09~2023-05-13
13.1.1节练习
练习13.1:
拷贝构造函数是,第一个参数是自身类类型的引用。赋值操作、非引用方式的实参传递、非引用方式的类型返回
练习13.2:
拷贝构造的参数必须是引用类型,如果拷贝构造函数允许非引用的类类型作为参数,那么在传递实参时会无限触发非引用的类类型拷贝构造函数。
练习13.3:
依次拷贝StrBlob的非static的类型成员到一个新的StrBlob对象中。 依次拷贝StrBlobPtr的非static的类型成员到一个新的StrBlobPtr对象中(包括其中的指针,对与指针是直接复制操作,并不是拷贝一个新的指针指向的值的地址到该指针中)
练习13.4:
图中7个地方发生了拷贝构造
练习13.5:
#include<iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {};
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {};
void test()
{
cout << this->ps << endl;
}
private:
string* ps;
int i;
};
int main()
{
HasPtr s1;
s1.test();
HasPtr s2 = s1;
s2.test();
system("pause");
return 0;
}
13.1.2节练习
练习13.6:
拷贝赋值运算符是一个重载的operator=函数,对对象赋值时使用到拷贝赋值运算符,合成拷贝赋值运算符会将右侧运算对象的每个非static成员赋予左侧对应的成员,如果一个类未定义自己的拷贝赋值运算符,那么编译器会自动合成一个合成拷贝赋值运算符
练习13.7:
将右侧的StrBlob对象的每个非static成员赋予左侧的StrBlob对象的对应的成员
练习13.8:
#include<iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {};
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {};
HasPtr& operator= (const HasPtr & hp)
{
this->i = hp.i;
this->ps = hp.ps;
return *this;
};
void test()
{
cout << this->ps << endl;
}
private:
string* ps;
int i;
};
int main()
{
HasPtr s1;
HasPtr s2;
s1.test();
s2.test();
s1 = s2;
s1.test();
s2.test();
system("pause");
return 0;
}
13.1.3节练习
练习13.9:
析构函数执行构造函数相反的操作,即(销毁顺序按构造函数的逆序来销毁)销毁、释放构造函数生成的对象、内存,当一个类未定义自己的析构函数时,编译器会提供一个合成析构函数
练习13.10:
略
练习13.11:
#include<iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {};
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {};
HasPtr& operator= (const HasPtr & hp)
{
this->i = hp.i;
this->ps = hp.ps;
return *this;
};
~HasPtr()
{
cout << "释放内存..." << endl;
delete this->ps;
}
void test()
{
cout << this->ps << endl;
}
private:
string* ps;
int i;
};
void test()
{
HasPtr s1;
HasPtr s2;
}
int main()
{
test();
system("pause");
return 0;
}
练习13.12:
3次
练习13.13:
略
13.1.4节练习
练习13.14:
会打印3次一样的s.mysn
#include<iostream>
#include<string>
using namespace std;
string generateRandomString()
{
string digits;
for (int i = 0; i < 10; ++i)
{
digits += to_string(rand() % 10);
}
return digits;
}
class numbered
{
public:
numbered()
{
this->mysn = generateRandomString();
}
string mysn;
};
void f(numbered s)
{
cout << s.mysn << endl;;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
练习13.15:
不会改变输出结果
#include<iostream>
#include<string>
using namespace std;
string generateRandomString()
{
string digits;
for (int i = 0; i < 10; ++i)
{
digits += to_string(rand() % 10);
}
return digits;
}
class numbered
{
public:
numbered()
{
this->mysn = generateRandomString();
}
numbered(const numbered& n)
{
this->mysn = n.mysn;
}
string mysn;
};
void f(numbered s)
{
cout << s.mysn << endl;;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
练习13.16:
不会改变输出结果
#include<iostream>
#include<string>
using namespace std;
string generateRandomString()
{
string digits;
for (int i = 0; i < 10; ++i)
{
digits += to_string(rand() % 10);
}
return digits;
}
class numbered
{
public:
numbered()
{
this->mysn = generateRandomString();
}
numbered(const numbered& n)
{
this->mysn = n.mysn;
}
string mysn;
};
void f(const numbered& s)
{
cout << s.mysn << endl;;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
练习13.17:
如上诉
13.1.6节练习
练习13.18:
#include<iostream>
#include<string>
using namespace std;
class Employee
{
public:
Employee() : m_no(++Nos) {};
Employee(string name) : m_name(name), m_no(++Nos) {};
string m_name;
int m_no;
static int Nos;
};
int Employee::Nos = 0;
void f(const Employee& e)
{
cout << e.m_no << endl;;
}
int main()
{
Employee a, b, c;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
练习13.19:
不需要,因为证号是唯一的。但可以把拷贝控制成员设置成删除函数或者设置成私有来禁止编译器默认的合成拷贝构造函数和默认拷贝赋值函数
#include<iostream>
#include<string>
using namespace std;
class Employee
{
public:
Employee() : m_no(++Nos) {};
Employee(string name) : m_name(name), m_no(++Nos) {};
Employee(const Employee&) = delete;
Employee& operator=(const Employee&) = delete;
string m_name;
int m_no;
static int Nos;
};
int Employee::Nos = 0;
void f(const Employee& e)
{
cout << e.m_no << endl;;
}
练习13.20:
已TextQuery为例:
拷贝、赋值:拷贝(赋值)时调用默认合成拷贝构造(默认合成赋值函数)函数按照成员对象的代码顺序逐一复制成员导向到新对象中。
销毁:按照成员对象的代码逆序逐一销毁对象,(销毁对象就是通过该对象调用其析构函数)
练习13.21:
TextQuery的具体作用是通过处理输入的文件流生成存储对应的数据没有使用到拷贝控制的场景,我们可以通过删除函数或者将其设置为私有来禁止其拷贝 TextQuery在使用query时需要返回一个QueryResult的临时变量,所以QueryResult可以只将其拷贝复制函数设置成私有即可
#include<iostream>
#include<vector>
#include<memory>
#include<fstream>
#include<sstream>
#include<set>
#include<map>
using namespace std;
class TextQuery;
class StrBlob
{
public:
friend class TextQuery;
StrBlob() : data(make_shared<vector<string>>()) {};
StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {};
vector<string>::size_type size() const
{
return data->size();
}
bool empty() const { return data->empty(); };
void push_back(const string& s)
{
data->push_back(s);
}
string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
string& back()
{
check(0, "back on empty StrBlo");
return data->back();
}
void pop_back()
{
check(0, "pop_back on empty StrBlo");
data->pop_back();
}
string& const front() const
{
check(0, "const front on empty StrBlob");
return data->front();
}
string& const back() const
{
check(0, "const back on empty StrBlob");
return data->back();
}
private:
shared_ptr<vector<string>> data;
void check(vector<string>::size_type i, const string& msg) const
{
if (i >= data->size())
{
throw out_of_range(msg);
}
}
};
class QueryResult;
class TextQuery
{
public:
using line_no_type = vector<string>::size_type;
TextQuery(ifstream& infile)
{
// 创建vector<string>容器并使用智能指针管理
lines_vec = StrBlob();
string line;
string word;
int line_no = 0;
while (getline(infile, line))
{
// 分行保存到vector中
lines_vec.data->push_back(line);
istringstream ss(line);
while (ss >> word)
{
// 在单词与行号(set)的map映射中查找单词是否存在
// 如果存在,在该行号(set)中插入当前行
// 如果不存在,将这个行号(set)指针指向新创建的set
auto& line_no_set = this->word_lineno_map[word];
if (!line_no_set)
{
line_no_set.reset(new set<line_no_type>);
}
// set容器自带去重、排序
line_no_set->insert(line_no);
}
++line_no;
}
}
void test()
{
for (auto temp : *(this->lines_vec.data))
{
cout << temp << endl;
}
}
QueryResult query(const string& s);
private:
StrBlob lines_vec;
map<string, shared_ptr<set<line_no_type>>> word_lineno_map;
TextQuery(const TextQuery&) {};
TextQuery& operator=(const TextQuery&) {};
};
class QueryResult
{
using line_no_type = TextQuery::line_no_type;
friend ostream& print(ostream& out, const QueryResult& qr);
public:
QueryResult() = default;
QueryResult(const string& s, shared_ptr<vector<string>> l, shared_ptr<set<line_no_type>> l_no) :
word(s), lines(l), lines_no(l_no) {};
shared_ptr<vector<string>> get_file()
{
return this->lines;
}
set<line_no_type>::iterator cbegin() const
{
return (*this->lines_no).begin();
}
set<line_no_type>::iterator cend() const
{
return (*this->lines_no).end();
}
private:
shared_ptr<vector<string>> lines;
shared_ptr<set<line_no_type>> lines_no;
const string word;
QueryResult& operator=(const QueryResult&) {};
};
QueryResult TextQuery::query(const string& s)
{
auto ret = this->word_lineno_map.find(s);
if (ret != this->word_lineno_map.end())
{
return QueryResult(s, this->lines_vec.data, ret->second);
};
return QueryResult();
};
ostream& print(ostream& out, const QueryResult& qr)
{
if (qr.word == "")
{
out << "not found word" << endl;
return out;
}
out << qr.word << " occurs " << qr.lines_no->size() << " times " << endl;
for (auto line_no : *qr.lines_no)
{
cout << "\t" << "(line " << line_no << ")" << (*qr.lines)[line_no] << endl;
}
return out;
};
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
string word;
//tq.test();
while (cin >> word)
{
print(cout, tq.query(word));
}
}
int main()
{
ifstream infile("test_word.txt");
runQueries(infile);
system("pause");
return 0;
}
13.2节练习
练习13.22:
#include<iostream>
#include<string>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {};
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {};
HasPtr& operator= (const HasPtr& hp)
{
this->i = hp.i;
this->ps = new string(*hp.ps);
return *this;
};
~HasPtr()
{
cout << this << endl;
delete this->ps;
}
void set_string()
{
this->i = 123;
*this->ps = "abc";
}
void show_string()
{
cout << "i=";
cout << i << endl;
cout << "string=";
cout << *this->ps << endl;
}
private:
string* ps;
int i;
};
int main()
{
HasPtr hp1;
HasPtr hp2 = hp1;
hp1.set_string();
hp1.show_string();
// hp2不会被修改
hp2.show_string();
system("pause");
return 0;
}
13.2.1节练习
练习13.23:
#include<iostream>
#include<string>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {};
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {};
HasPtr& operator= (const HasPtr& hp)
{
this->i = hp.i;
// 如果自己赋值给自己hp1=hp1,即hp和this指向的是同一个对象
// 这种情况下如果先删除指针,会导致hp指向的对象也被删除,再进行赋值会产生异常
// 所以我们先通过解引用获取值再用来动态分配一块内存,就避免了异常
auto new_ps = new string(*hp.ps);
delete ps;
this->ps = new_ps;
return *this;
};
~HasPtr()
{
delete this->ps;
}
void set_string()
{
this->i = 123;
*this->ps = "abc";
}
void show_string()
{
cout << "i=";
cout << i << endl;
cout << "string=";
cout << *this->ps << endl;
}
private:
string* ps;
int i;
};
int main()
{
HasPtr hp1;
hp1 = hp1;
hp1.set_string();
hp1.show_string();
system("pause");
return 0;
}
练习13.24:
未定义析构函数导致内存泄露,未定义拷贝构造函数会导致通过值传递的方式初始化HasPtr对象时,两个对象中的ps指针地址相同,如果定义了析构函数,在释放内存时会出现重复释放同一块内存的情况,将会导致异常
练习13.25:
要符合类值版本,各个实例化对象之间的属性不能相互影响所以拷贝构造函数和拷贝赋值运算符触发时需要使用动态分配一个新的指针或者使用make_shared创建一个智能指针并赋值给strBlob对象中的智能指针成员,因为StrBlob和StrBlobPtr的成员对象都是智能指针和非动态分配的内存,所以无需去显式的释放内存自然就无需析构函数
练习13.26:
#include<iostream>
#include<vector>
#include<memory>
#include<fstream>
#include<sstream>
using namespace std;
class StrBlob
{
friend class StrBlobPtr;
public:
StrBlob() : data(make_shared<vector<string>>()) {};
StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {};
StrBlob(const StrBlob& s)
{
this->data = make_shared<vector<string>>(*s.data);
}
StrBlob& operator=(const StrBlob& s)
{
this->data = make_shared<vector<string>>(*s.data);
}
vector<string>::size_type size() const
{
return data->size();
}
bool empty() const { return data->empty(); };
void push_back(const string& s)
{
data->push_back(s);
}
string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
string& back()
{
check(0, "back on empty StrBlo");
return data->back();
}
void pop_back()
{
check(0, "pop_back on empty StrBlo");
data->pop_back();
}
string& const front() const
{
check(0, "const front on empty StrBlob");
return data->front();
}
string& const back() const
{
check(0, "const back on empty StrBlob");
return data->back();
}
StrBlobPtr begin();
StrBlobPtr end();
StrBlobPtr cbegin() const;
StrBlobPtr cend() const;
private:
shared_ptr<vector<string>> data;
void check(vector<string>::size_type i, const string& msg) const
{
if (i >= data->size())
{
throw out_of_range(msg);
}
}
};
class StrBlobPtr
{
public:
StrBlobPtr() : curr(0) {};
StrBlobPtr(StrBlob& s, size_t sz = 0) : wptr(s.data), curr(sz) {};
StrBlobPtr(const StrBlob& s, size_t sz = 0) : wptr(s.data), curr(sz) {};
string& deref() const
{
auto ret = check(curr, "increment past end of StrBlobPtr");
return (*ret)[curr];
}
StrBlobPtr& incr()
{
auto ret = check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr& redu()
{
auto ret = check(curr - 1, "increment past end of StrBlobPtr");
--curr;
return *this;
}
void set_StrBlob(vector<string> c)
{
auto ret = wptr.lock();
if (!ret)
{
throw runtime_error("wptr unbound");
}
for (string::size_type i = 0; i < (*ret).size(); ++i)
{
(*ret)[i] = to_string(i);
}
}
private:
size_t curr;
weak_ptr<vector<string>> wptr;
shared_ptr<vector<string>> check(size_t i, const string& msg) const
{
auto ret = wptr.lock();
if (!ret)
{
throw runtime_error("wptr unbound");
}
if (i > ret->size())
{
throw out_of_range(msg);
}
return ret;
}
};
StrBlobPtr StrBlob::begin()
{
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
StrBlobPtr StrBlob::cbegin() const
{
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::cend() const
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
int main()
{
const StrBlob s1 = { "100", "200", "300" };
int times = s1.size();
StrBlobPtr sptr1 = s1.cbegin();
//while (times > 0)
//{
// cout << sptr1.deref() << endl;
// sptr1.incr();
// --times;
//}
const StrBlob s2 = s1;
const StrBlob s3(s2);
StrBlobPtr sptr2 = s2.cbegin();
vector<string> v = { "300", "200", "100" };
sptr2.set_StrBlob(v);
StrBlobPtr sptr3 = s3.cbegin();
while (times > 0)
{
cout << sptr1.deref() << endl;
cout << sptr2.deref() << endl;
cout << sptr3.deref() << endl;
sptr1.incr();
sptr2.incr();
sptr3.incr();
--times;
}
system("pause");
return 0;
}
13.2.2节练习
练习13.27:
#include<iostream>
#include<vector>
#include<memory>
#include<fstream>
#include<sstream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) : ps(new string(s)), use(new string::size_type(1)), i(0) {};
HasPtr(const HasPtr& hp) : ps(hp.ps), i(hp.i), use(hp.use) { ++* use; };
HasPtr& operator=(const HasPtr& hp)
{
++*hp.use;
if (--*this->use == 0)
{
delete this->ps;
delete this->use;
}
this->use = hp.use;
this->ps = hp.ps;
this->i = hp.i;
return *this;
}
~HasPtr()
{
if (--*this->use == 0)
{
delete this->ps;
delete this->use;
}
}
void test()
{
cout << *this->use << endl;
}
private:
string* ps;
int i;
string::size_type* use;
};
void test(const HasPtr& hp)
{
HasPtr hp4 = hp;
hp4.test();
}
int main()
{
HasPtr hp1;
hp1 = hp1;
hp1.test();
HasPtr hp2 = hp1;
hp1.test();
HasPtr hp3(hp1);
hp1.test();
test(hp1);
hp1.test();
system("pause");
return 0;
}
练习13.28:
(a):
class TreeNode
{
public:
TreeNode(const string& val) : value(val), count(1), left(nullptr), right(nullptr) {};
TreeNode(const TreeNode& tr) : value(tr.value), count(tr.count), left(nullptr), right(nullptr)
{
if (tr.left)
{
this->left = new TreeNode(*tr.left);
}
if (tr.right)
{
this->right = new TreeNode(*tr.right);
}
}
TreeNode& operator=(const TreeNode& tr)
{
auto new_tr = new TreeNode(tr);
delete this->left;
delete this->right;
this->left = new_tr->left;
this->left = new_tr->left;
this->count = new_tr->count;
this->value = new_tr->value;
return *this;
}
~TreeNode()
{
delete left;
delete right;
}
private:
string value;
int count;
TreeNode* left;
TreeNode* right;
};
class BinStrTree
{
public:
BinStrTree() : root(nullptr) {};
BinStrTree(const BinStrTree& bst) : root(bst.root ? new TreeNode(*bst.root) : nullptr) {};
~BinStrTree() { delete root; };
private:
TreeNode* root;
};
13.3节练习
练习13.29:
swap(HasPtr&, HasPtr&)中调用的swap是标准库中的swap函数而不是调用自身所以不会发生递归循环
练习13.30:
全局函数test中调用swap时执行自定义的swap函数
#include<iostream>
#include<vector>
#include<memory>
#include<fstream>
#include<sstream>
using namespace std;
class HasPtr
{
public:
friend void swap(HasPtr& rhp, HasPtr& lhp);
HasPtr(const string& s = string()) : ps(new string(s)), use(new string::size_type(1)), i(0) {};
HasPtr(const HasPtr& hp) : ps(hp.ps), i(hp.i), use(hp.use) { ++* use; };
HasPtr& operator=(const HasPtr& hp)
{
++*hp.use;
if (--*this->use == 0)
{
delete this->ps;
delete this->use;
}
this->use = hp.use;
this->ps = hp.ps;
this->i = hp.i;
return *this;
}
~HasPtr()
{
if (--*this->use == 0)
{
delete this->ps;
delete this->use;
}
}
void test()
{
cout << *this->ps << endl;
}
private:
string* ps;
int i;
string::size_type* use;
};
void test()
{
HasPtr hp1("hp1");
HasPtr hp2("hp2");
swap(hp1, hp2);
hp1.test();
hp2.test();
}
void swap(HasPtr& rhp, HasPtr& lhp)
{
cout << "调用swap" << endl;
using std::swap;
swap(rhp.ps, lhp.ps);
swap(rhp.i, lhp.i);
}
int main()
{
test();
system("pause");
return 0;
}
练习13.31:
在windows11操作系统下使用visual2022,增加34个HasPtr对象时调用了自定义的swap函数
练习13.32:
类指针版的拷贝构造函数和赋值运算符都是直接赋值右侧对象的值到左侧对象中,与类值版本需要重新分配内存空间不同,所以类指针版本的HasPtr并不能从自定义的swap函数中受益
13.4节练习
练习13.33:
需要修改传入的实例化对象的属性,所以需要使用引用传递、使用值传递或者常量引用传递无法都无法达到该效果
练习13.34:
#include<iostream>
#include<vector>
#include<memory>
#include<set>
#include<algorithm>
using namespace std;
class Floder;
class Message
{
public:
friend void swap(Message& lmsg, Message& rmsg);
Message(const string msg = "") : content(msg) {};
Message(const Message& m) : content(m.content), floders(m.floders)
{
add_to_Folders(m);
}
Message& operator=(const Message& msg)
{
remove_from_Floders();
this->content = msg.content;
this->floders = msg.floders;
this->add_to_Folders(msg);
return *this;
}
~Message()
{
remove_from_Floders();
}
void save(Floder& f);
void remove(Floder& f);
private:
set<Floder*> floders;
string content;
void add_to_Folders(const Message& msg);
void remove_from_Floders();
};
class Floder
{
public:
friend void swap(Message& lmsg, Message& rmsg);
friend class Message;
private:
set<Message*> msgs;
void addMsg(Message* msg)
{
msgs.insert(msg);
}
void remMsg(Message* msg)
{
msgs.erase(msg);
}
};
void Message::save(Floder& f)
{
floders.insert(&f);
f.addMsg(this);
}
void Message::remove(Floder& f)
{
floders.erase(&f);
f.remMsg(this);
}
void Message::add_to_Folders(const Message& m)
{
for (auto f : m.floders)
{
f->addMsg(this);
}
}
void Message::remove_from_Floders()
{
for (auto f : floders)
{
f->remMsg(this);
}
}
void swap(Message& lmsg, Message& rmsg)
{
for (auto f : lmsg.floders)
{
f->remMsg(&lmsg);
}
for (auto f : rmsg.floders)
{
f->remMsg(&rmsg);
}
using std::swap;
swap(lmsg.content, rmsg.content);
swap(lmsg.floders, rmsg.floders);
for (auto f : lmsg.floders)
{
f->addMsg(&lmsg);
}
for (auto f : rmsg.floders)
{
f->addMsg(&rmsg);
}
}
void test()
{
Message msg1;
Message msg2 = msg1;
Message msg3(msg2);
}
int main()
{
test();
system("pause");
return 0;
}
练习13.35:
没有对Floders对象的set<Message*> msgs属性添加当前拷贝的Message对象
练习13.36:
见练习13.35
练习13.37:
见练习13.35 相较于拷贝和交换的方式来实现赋值运算符的方式复杂并且损耗性能,所以不采用
13.5节练习
练习13.39~40:
#include<iostream>
#include<vector>
#include<memory>
#include<set>
#include<algorithm>
#include<utility>
#include<initializer_list>
using namespace std;
class StrVec
{
public:
// 默认构造函数
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
// 拷贝构造函数
StrVec(const StrVec&);
// 拷贝赋值运算符
StrVec& operator=(const StrVec& lsv);
// 析构函数
~StrVec();
// 返回当前内存中存储的元素的数量
size_t size() const { return first_free - elements; };
// 返回当前总分配的内存可容纳元素的数量
size_t capacity() const { return cap - elements; };
// 为StrVec扩大内存容量
void reserve(size_t num);
// 从新分配StrVec的元素大小
void resize(size_t num, const string& s);
// 为StrVec添加元素
void push_back(const string& s);
// 返回首元素的迭代器
string* begin() const { return elements; };
// 返回实际元素尾后元素位置迭代器
string* end() const { return first_free; };
// 测试函数
void test();
private:
// 指向分配的内存中的首元素
string* elements;
// 指向实际元素的尾后元素
string* first_free;
// 指向分配的内存中的尾后元素
string* cap;
// 为StrVec分配使用的内存的静态成员
static allocator<string> alloc;
// 内存耗尽后,为StrVec分配新内存
void reallocate();
// 销毁元素并释放内存
void free();
// 保证StrVec有容纳一个新元素的空间,如果没有就调用reallocate分配更多内存来容纳新元素
void chk_n_alloc() { if (size() == capacity()) reallocate(); };
// 工具函数,拷贝构造函数、赋值运算符、析构函数所使用
pair<string*, string*> chk_n_copy(const string* b, const string* e);
// 工具函数,reallocate函数和reserve函数使用
void reallocate_memory(size_t num);
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
void StrVec::free()
{
if (elements)
{
for (auto p = first_free; p != elements;)
{
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
StrVec::~StrVec() { free(); };
pair<string*, string*> StrVec::chk_n_copy(const string* b, const string* e)
{
// 分配内存空间来保存给定范围内的string数据
auto data = alloc.allocate(e - b);
// 将string数据复制到分配的内存空间,并返回一个pair数据,first指向分配的内存空间首地址、second指向分配的内存空间的尾后地址
return { data, uninitialized_copy(b, e, data) };
}
StrVec::StrVec(const StrVec& sv)
{
auto new_data = chk_n_copy(sv.begin(), sv.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
StrVec& StrVec::operator=(const StrVec& lsv)
{
auto new_data = chk_n_copy(lsv.begin(), lsv.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::reallocate_memory(size_t num)
{
// 分配新内存
auto new_data = alloc.allocate(num);
// 指向新内存首元素
auto dest = new_data;
// 指向旧内存首元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
// 将每一个旧内存中的数据移动到到新内存中
alloc.construct(dest++, std::move(*elem++));
};
// 释放旧内存
free();
// 将指针指向新内存
first_free = dest;
elements = new_data;
cap = elements + num;
}
void StrVec::reallocate()
{
// 内存空间不够时将内存空间扩大成当前容量两倍大小
auto newcapacity = size() ? size() * 2 : 1;
reallocate_memory(newcapacity);
}
void StrVec::reserve(size_t num)
{
if (num <= capacity())
{
return ;
}
reallocate_memory(num);
}
void StrVec::resize(size_t num, const string& s = "")
{
if (num < size() && num >= 0)
{
auto end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
first_free = end_iter;
}
else if(num > size() && num <= capacity())
{
auto end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
first_free = end_iter;
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
auto end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
first_free = end_iter;
}
}
int main()
{
StrVec s;
//vector<string> s;
s.push_back("123455");
s.push_back("asd1");
s.push_back("asd");
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(1);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(2);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(20, "asd");
cout << s.size() << endl;
cout << s.capacity() << endl;
for (auto iter = s.begin(); iter != s.end(); ++iter)
{
cout << *iter << endl;
}
s = { "1", "2", "3", "4", "5" };
cout << s.size() << endl;
cout << s.capacity() << endl;
for (auto iter = s.begin(); iter != s.end(); ++iter)
{
cout << *iter << endl;
}
system("pause");
return 0;
}
练习13.41:
这题的题目是:为什么使用在自定义的StrVec的push_back中使用后置递增,而不是前置递增。(我的PDF文件上面的题目错了,找了实体书确认了一下哎,实体书本上的题目是正确)
我们传递的是first_free,first_free指向的是实际存储的元素的尾后元素,该元素是一块未构造的内存,我们应该在这个位置上构造数据,如果使用前置递增的话,会导致每次push_back的元素都间隔一个未构造内存元素
练习13.42:
#include<iostream>
#include<vector>
#include<memory>
#include<set>
#include<map>
#include<string>
#include<algorithm>
#include<utility>
#include<initializer_list>
#include<fstream>
#include<sstream>
using namespace std;
class StrVec
{
public:
// 默认构造函数
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
// 拷贝构造函数
StrVec(const StrVec&);
// 拷贝赋值运算符
StrVec& operator=(const StrVec& lsv);
// 析构函数
~StrVec();
// 返回当前内存中存储的元素的数量
size_t size() const { return first_free - elements; };
// 返回当前总分配的内存可容纳元素的数量
size_t capacity() const { return cap - elements; };
// 为StrVec扩大内存容量
void reserve(size_t num);
// 从新分配StrVec的元素大小
void resize(size_t num, const string& s);
// 为StrVec添加元素
void push_back(const string& s);
// 返回首元素的迭代器
string* begin() const { return elements; };
// 返回实际元素尾后元素位置迭代器
string* end() const { return first_free; };
// 测试函数
void test();
private:
// 指向分配的内存中的首元素
string* elements;
// 指向实际元素的尾后元素
string* first_free;
// 指向分配的内存中的尾后元素
string* cap;
// 为StrVec分配使用的内存的静态成员
static allocator<string> alloc;
// 内存耗尽后,为StrVec分配新内存
void reallocate();
// 销毁元素并释放内存
void free();
// 保证StrVec有容纳一个新元素的空间,如果没有就调用reallocate分配更多内存来容纳新元素
void chk_n_alloc() { if (size() == capacity()) reallocate(); };
// 工具函数,拷贝构造函数、赋值运算符、析构函数所使用
pair<string*, string*> chk_n_copy(const string* b, const string* e);
// 工具函数,reallocate函数和reserve函数使用
void reallocate_memory(size_t num);
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
void StrVec::free()
{
if (elements)
{
for (auto p = first_free; p != elements;)
{
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
StrVec::~StrVec() { free(); };
pair<string*, string*> StrVec::chk_n_copy(const string* b, const string* e)
{
// 分配内存空间来保存给定范围内的string数据
auto data = alloc.allocate(e - b);
// 将string数据复制到分配的内存空间,并返回一个pair数据,first指向分配的内存空间首地址、second指向分配的内存空间的尾后地址
return { data, uninitialized_copy(b, e, data) };
}
StrVec::StrVec(const StrVec& sv)
{
auto new_data = chk_n_copy(sv.begin(), sv.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
StrVec& StrVec::operator=(const StrVec& lsv)
{
auto new_data = chk_n_copy(lsv.begin(), lsv.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::reallocate_memory(size_t num)
{
// 分配新内存
auto new_data = alloc.allocate(num);
// 指向新内存首元素
auto dest = new_data;
// 指向旧内存首元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
// 将每一个旧内存中的数据移动到到新内存中
alloc.construct(dest++, std::move(*elem++));
};
// 释放旧内存
free();
// 将指针指向新内存
first_free = dest;
elements = new_data;
cap = elements + num;
}
void StrVec::reallocate()
{
// 内存空间不够时将内存空间扩大成当前容量两倍大小
auto newcapacity = size() ? size() * 2 : 1;
reallocate_memory(newcapacity);
}
void StrVec::reserve(size_t num)
{
if (num <= capacity())
{
return ;
}
reallocate_memory(num);
}
void StrVec::resize(size_t num, const string& s = "")
{
if (num < size() && num >= 0)
{
auto end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
first_free = end_iter;
}
else if(num > size() && num <= capacity())
{
auto end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
first_free = end_iter;
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
auto end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
first_free = end_iter;
}
}
class QueryResult;
class TextQuery
{
public:
using line_no_type = vector<string>::size_type;
TextQuery(ifstream& infile)
{
// 创建vector<string>容器并使用智能指针管理
lines_vec = make_shared<StrVec>();
string line;
string word;
int line_no = 0;
while (getline(infile, line))
{
// 分行保存到vector中
(*lines_vec).push_back(line);
istringstream ss(line);
while (ss >> word)
{
// 在单词与行号(set)的map映射中查找单词是否存在
// 如果存在,在该行号(set)中插入当前行
// 如果不存在,将这个行号(set)指针指向新创建的set
auto& line_no_set = this->word_lineno_map[word];
if (!line_no_set)
{
line_no_set.reset(new set<line_no_type>);
}
// set容器自带去重、排序
line_no_set->insert(line_no);
}
++line_no;
}
}
void test()
{
for (auto temp : *lines_vec)
{
cout << temp << endl;
}
}
QueryResult query(const string& s);
private:
shared_ptr<StrVec> lines_vec;
map<string, shared_ptr<set<line_no_type>>> word_lineno_map;
};
class QueryResult
{
using line_no_type = TextQuery::line_no_type;
friend ostream& print(ostream& out, const QueryResult& qr);
public:
QueryResult() = default;
QueryResult(const string& s, shared_ptr<StrVec> l, shared_ptr<set<line_no_type>> l_no) :
word(s), lines(l), lines_no(l_no) {};
private:
shared_ptr<StrVec> lines;
shared_ptr<set<line_no_type>> lines_no;
const string word;
};
QueryResult TextQuery::query(const string& s)
{
auto ret = this->word_lineno_map.find(s);
if (ret != this->word_lineno_map.end())
{
return QueryResult(s, lines_vec, ret->second);
};
return QueryResult();
};
ostream& print(ostream& out, const QueryResult& qr)
{
if (qr.word == "")
{
out << "not found word" << endl;
return out;
}
out << qr.word << " occurs " << qr.lines_no->size() << " times " << endl;
for (auto line_no : *qr.lines_no)
{
cout << "\t" << "(line " << line_no << ")" << *((*qr.lines).begin() + line_no) << endl;
}
return out;
};
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
string word;
//tq.test();
while (cin >> word)
{
print(cout, tq.query(word));
}
}
int main()
{
ifstream infile("test_word.txt");
runQueries(infile);
system("pause");
return 0;
}
练习13.43:
for_each对比for循环不需要手动递增迭代器比较方便
void StrVec::free()
{
if (elements)
{
// 使用for_each
for_each(elements, first_free,
[](const string& s) { alloc.destroy(&s); });
alloc.deallocate(elements, cap - elements);
//for (auto p = first_free; p != elements;)
//{
// alloc.destroy(--p);
//}
//alloc.deallocate(elements, cap - elements);
}
}
练习14.44:
#include<iostream>
#include<vector>
#include<memory>
#include<set>
#include<map>
#include<string>
#include<algorithm>
#include<utility>
#include<initializer_list>
#include<fstream>
#include<sstream>
using namespace std;
class String
{
/// <summary>
/// 属性:
/// 1、elements:指向字符数组的首地址
/// 2、first_free:指向实际存储字符的最后一个元素之后的地址
/// 3、cap:指向已分配内存的尾后地址
/// 4、alloc:allocator的静态成员对象(私有),用来分配内存、构造对象、销毁对象、回收内存
/// 方法:
/// 1、默认构造函数:
/// 参数默认为空,elements、first_free、cap初始化为空指针
/// 初始化allocator对象
/// 2、有参构造函数:
/// 2.1、参数是const char[]对象
/// 2.2、参数是initializer_list<char>&
/// 类似操作:开辟新内存,在内存中构造参数string的值,将相关指针指向新内存
/// 3、拷贝构造函数:
/// 开辟新内存,在内存中构造传入的const String&对象的值,将相关指针指向新内存
/// 4、赋值运算符:
/// 拷贝右侧对象数据到内存中,销毁旧内存数据,回收旧内存
/// 开辟新内存,在内存中构造传入的const String&对象的值,将相关指针指向新内存,返回当前对象
/// 5、析构函数:
/// 销毁存储的字符数据、回收内存
/// 6、size函数:返回当前对象的容量大小first_free-elements
/// 7、capacity函数:返回当前对象已分配总内存大小
/// 8、resize(num)函数:重新分配当前对象的容量
/// 8.1、num < size():销毁size()-num个字符对象
/// 8.2、num > size() && num < capacity():按给定的字符构造num-size()个对象
/// 8.3、num > size() && num > capacity():重新分配内存,并将旧字符数组move到新内存中
/// 将指针指向调整后的元素地址
/// 9、reserve(num)函数:当num > capacity()时分配新内存空间,并将旧内存中的字符数组move到新内存中,将相关指针指向新内存
/// 10、push_back函数:向容器添加新字符
/// 10.1、容器大小capacity() >= size() + 要添加字符的数量:在已分配的内存中构造字符
/// 10.2、容器大小capacity() < size() + 要添加字符的数量:分配新内存,并将旧内存中的字符数组move到新内存中,将相关指针指向新内存
/// 11、clear函数:销毁当前字符数组所有数据但不释放已分配的内存
/// 12、begin函数
/// 13、end函数
/// 14、工具函数:
/// 14.1、free函数:销毁内存中存储的所有数据,(为clear、析构、赋值运算符提供支持)
/// 14.2、chk_n_copy函数:从一对迭代器拷贝元素到新内存中并返回新内存的首、尾后指针
/// (为有参构造、拷贝构造、赋值运算符提供支持)
/// 14.3、reallocate_memory(num)函数:开辟容器大小为num的新空间,将旧内存中的数据移动到新内存中,将相关指针指向新内存
/// 14.4、chk_n_alloc函数:容器大小不足时,调用reallocate_memory(num),保证有内存空间构造新元素(为push_back函数提供支持)
/// (为reallocate函数、reserve函数、resize函数提供支持)
/// 先实现工具函数再实现具体功能函数
/// </summary>
public:
String() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
//2、有参构造函数:
//2.1、参数是const char[]对象
String(const char* p)
{
auto new_data = chk_n_copy(p);
elements = new_data.first;
first_free = cap = new_data.second;
}
//2.2、参数是initializer_list<char>&
String(const initializer_list<char>& c)
{
auto new_data = chk_n_copy(c.begin(), c.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
//3、拷贝构造函数
String(const String& s)
{
auto new_data = chk_n_copy(s.cbegin(), s.cend());
elements = new_data.first;
first_free = cap = new_data.second;
}
//4、赋值运算符
String& operator=(const String& s)
{
auto new_data = chk_n_copy(s.cbegin(), s.cend());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
//5、析构函数:
~String() { free(); };
char* elements;
char* first_free;
char* cap;
//6、size函数
size_t size() { return first_free - elements; };
//7、capacity函数
size_t capacity() { return cap - elements; };
//8、resize函数
void resize(size_t num, char c = '\0')
{
char* end_iter = nullptr;
if (size() > num)
{
end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
}
else if (num > size() && num <= capacity())
{
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, c);
}
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, c);
}
}
first_free = end_iter ? end_iter : first_free;
}
//9、reserve
void reserve(size_t num) { if (num > capacity()) reallocate_memory(num); };
//10、push_back函数
void push_back(char c)
{
chk_n_alloc();
alloc.construct(first_free++, c);
}
//11、clear函数
void clear()
{
for_each(elements, first_free,
[](const char& p) { alloc.destroy(&p);}
);
first_free = elements;
}
char* begin() const { return elements; };
char* end() const { return first_free; };
const char* cbegin() const { return elements; };
const char* cend() const { return first_free; };
void test();
private:
static allocator<char> alloc;
void free();
pair<char*, char*> chk_n_copy(const char* p);
pair<char*, char*> chk_n_copy(const char* b, const char* e);
void reallocate_memory(size_t num);
void chk_n_alloc();
};
// 静态对象类外初始化
allocator<char> String::alloc;
//14.1、free函数
inline void String::free()
{
if (elements)
{
for_each(elements, first_free,
[this](const char& c) { alloc.destroy(&c); }
);
alloc.deallocate(elements, cap - elements);
elements = nullptr;
first_free = nullptr;
cap = nullptr;
}
}
//14.2、chk_n_copy(const char* p))函数
inline pair<char*, char*> String::chk_n_copy(const char* p)
{
auto elem = p;
int str_length = 0;
while (*elem != '\0')
{
++str_length;
++elem;
}
auto new_data = alloc.allocate(str_length);
return { new_data, uninitialized_copy(p, elem, new_data) };
}
//14.2、chk_n_copy(const char* p))函数,重载版本
inline pair<char*, char*> String::chk_n_copy(const char* b, const char* e)
{
auto new_data = alloc.allocate(e - b);
return { new_data, uninitialized_copy(b, e, new_data) };
}
//14.3、reallocate_memory(num)函数
inline void String::reallocate_memory(size_t num)
{
auto new_data = alloc.allocate(num);
auto dest = new_data;
auto elem = elements;
for (size_t i = 0; i < size(); ++i)
{
alloc.construct(dest++, std::move(*elem++));
}
free();
elements = new_data;
first_free = dest;
cap = elements + num;
}
//14.4、chk_n_alloc函数
inline void String::chk_n_alloc()
{
if (size() == capacity())
{
size_t new_capacity = size() ? size() * 2 : 1;
reallocate_memory(new_capacity);
}
}
void String::test()
{
chk_n_alloc();
}
int main()
{
String s1;
s1 = String("abcdef");
String s3 = { 'a', 'b', 'c' };
s3.resize(1);
s3.resize(10, 'a');
//s3.clear();
cout << s3.size() << endl;
cout << s3.capacity() << endl;
if (s3.cend())
{
cout << "asdasdasd 123123123 " << endl;
}
auto iter = s3.elements;
for (auto temp : s3)
{
cout << temp <<endl;
}
system("pause");
return 0;
}
13.6.1节练习
练习13.45:
右值引用:引用对象即将销毁、引用对象没有其他使用者 左值引用:持久的保存对象,引用对象可能有其他的使用者
练习13.46:
int &&r1 = f();
int &&r2 = vi[0];
int &r3 = r1;
int &&r4 = vi[0] * f();
练习13.47~13.48:
13.6.2节练习
练习13.49:
String移动拷贝构造、移动赋值运算符:
// 添加一个移动构造函数
String(String&& s) noexcept :elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
}
String& operator=(String&& s)
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
return *this;
}
StrVec移动拷贝构造、移动赋值运算符
StrVec(StrVec&& s) noexcept : elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
}
StrVec& operator=(StrVec&& s)
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
Message移动拷贝构造、移动赋值运算符
// 添加一个移动赋值运算符
Message& operator=(Message&& m)
{
if (this != &m)
{
remove_from_Floders();
content = std::move(m.content);
move_floders(&m);
}
return *this;
}
Message& operator=(const Message& msg)
{
remove_from_Floders();
this->content = msg.content;
this->floders = msg.floders;
this->add_to_Folders(msg);
return *this;
}
void move_floders(Message* m)
{
floders = std::move(m->floders);
for (auto f : m->floders)
{
f->remMsg(m);
f->addMsg(this);
}
m->floders.clear();
}
练习13.50:
不会发生拷贝,只会发生移动
练习13.51:
可以理解为,unique_ptr虽然表示独占支持指针的所有权所以不允许拷贝,但是可以返回一个即将销毁的unique_ptr即为发生了移动构造,将指针的所有权转交给其他对象
练习13.52:
HasPtr hp;
HasPtr hp2;
hp = hp2;
// hp=hp2匹配了HasPtr& operator=(HasPtr rhs)这个赋值运算符
// 由于hp2是一个左值,触发拷贝构造函数-->HasPtr rhs(hp2)
// rhs构造完进入HasPtr& operator=(HasPtr rhs)这个赋值运算符这个函数流程
// 通过调用自定义的swap函数进入流程再调用std::swap函数交换rhs与this的指针和数据
// HasPtr& operator=(HasPtr rhs)函数流程结束,rhs这个临时对象销毁
hp = std::move(hp2);
// std::move(hp2)匹配了HasPtr& operator=(HasPtr rhs)这个赋值运算符
// 由于std::move(hp2)是一个右值引用,触发移动构造函数HasPtr(HasPtr &&p)-->HasPtr rhs(std::move(hp2))
// 由于触发的是移动构造函数,在构造rhs对象时免去了资源拷贝的性能浪费
// rhs构造完进入HasPtr& operator=(HasPtr rhs)这个赋值运算符这个函数流程
// 通过调用自定义的swap函数进入流程再调用std::swap函数交换rhs与this的指针和数据
// HasPtr& operator=(HasPtr rhs)函数流程结束,rhs这个临时对象销毁
练习13.53:
//移动构造函数
HasPtr(HasPtr&& hp) : ps(hp.ps), i(hp.i), use(hp.use) { hp.ps = nullptr; };
// 移动赋值运算符
HasPtr& operator=(HasPtr&& hp
if (--*use == 0)
{
delete ps;
delete use;
}
++* hp.use;
use = hp.use;
ps = hp.ps;
i = hp.i;
return *this;
}
练习13.54:
移动赋值运算符和拷贝并交换运算符参数列表存在二义性,程序无法运行。需要将拷贝移动并交换参数列表改为HasPtr& hp
13.6.3节练习
练习13.55:
void push_back(const string& s)
{
data->push_back(s);
}
void push_back(string&& s)
{
data->push_back(s);
}
练习13.56:
ret是一个左值,返回一个左值调用sorted会陷入死循环,无线递归调用sorted()函数
练习13.57:
Foo(* this)是一个右值对象,返回一个右值调用sorted排序函数的结果
练习13.58:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Foo
{
public:
Foo()
{
vector<int> temp = { 5, 9, 7, 6, 4, 8 };
data = temp;
}
Foo sorted() const&
{
cout << "左值版本" << endl;
return Foo(*this).sorted();
};
Foo sorted()&&
{
cout << "右值版本" << endl;
sort(data.begin(), data.end());
return *this;
}
void test()
{
for (auto temp : data)
{
cout << temp << endl;
}
}
private:
vector<int> data;
};
int main()
{
Foo f;
auto ret = f.sorted();
f.test();
ret.test();
system("pause");
return 0;
}
勇敢和愚蠢只有一剑之差