本书为Primer C++ 中文第五版
练习题笔记
(13.1)
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则次构造函数是拷贝构造函数。拷贝构造函数的第一个参数必须是一个引用类型。拷贝构造函数通常不应该是explicit
使用场合:
类会自动定义合成拷贝构造函数,编译器从给定对象中依次将每个非static成员拷贝到正在创建的对象中
拷贝初始化,要求编译器将右侧运算对象拷贝到正在穿件的对象中,如果需要的话还要进行类型转换
在函数调用过程中,具有非引用类型的参数要进行拷贝初始化;具有非引用的返回类型,返回值会被用来初始化调用方的结果
(13.2)
拷贝构造函数的参数需要是引用类型,否则将会掉入无限调用拷贝构造函数的循环中
(13.3)
当拷贝一个StrBlob时,拷贝一个shared_ptr(调用shared_ptr的拷贝构造函数),shared_ptr计数会增加1
当拷贝一个StrBlobPtr时,拷贝一个weak_ptr(调用weak_ptr的拷贝构造函数),weak_ptr计数不会增加;curr直接拷贝
(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;//非引用的返回值
}
(13.5)
HasPtr::HasPtr(const HasPtr &hp) :
ps(new String(*(hp.ps))), i(hp.i) { }
(13.6)
拷贝赋值运算符接受一个与其所在类相同类型的参数,通常应该返回一个指向其左侧运算对象的引用。
如果类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符
(13.7)
当一个StrBlob赋值给另一个StrBlob时,会调用shared_ptr的拷贝赋值运算符
当一个StrBlobPtr赋值给另一个StrBlobPtr时,weak_ptr和curr会调用各自的拷贝赋值运算符
(13.8)
HasPtr::HasPtr& HasPtr::operator=(const HasPtr & hp) {
ps = new string(*(hp.ps));
i = hp.i;
return *this;
}
(13.9)
析构函数执行与构造函数相反的操作,释放对象使用的资源,并销毁对象的非static数据成员。没有返回值,也不结束参数,不能被重载,一个类对应唯一一个析构函数。
变量离开作用域时被销毁;对象被销毁时其成员被销毁;容器被销毁,其元素被销毁;动态分配的对象,使用delete时被销毁;
当指向一个对象的引用或指针离开作用域时,析构函数不会执行。
当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。
(13.10)
对于StrBlob,会销毁data,并使得shared_ptr计数减一,若变为0,则释放其对象
对于StrBlobPtr,会销毁weak_ptr和curr,因为是weak_ptr,所以计数不会改变
(13.11)
HasPtr::~HasPtr() {
delete ps;
}
(13.12)
bool fun(const Sales_data *trans, Sales_data accum)
{
Sales_data item1(*trans), item2(accum);
return item1.isbn() != item2.isbn();
}//离开作用域时,item1,item2以及accum都会被销毁
(13.13)
struct X {
X() {cout << "X()" << endl;}
X(const X&) {cout << "X(const X&)" << endl;}
X& operator=(const X&) {cout << "operator=(const X&)" << endl;}
~X() {cout << "~X()" << endl;}
};
void t1(X x) { }
void t2(X &x) { }
void test13() {
X x0;
cout << "拷贝构造函数" << endl;
X x1 = x0;
cout << "拷贝赋值运算符" << endl;
X x2;
x2 = x0;
cout << "调用非引用的参数" << endl;
t1(x0);
cout << "调用引用参数" << endl;
t2(x0);
cout << "析构函数" << endl;
}
(13.14)
因为b和c都会调用拷贝构造函数,但是拷贝构造函数是默认合成的,并没有为此生成唯一的序号,所以将会公用一个序号。所以代码输出都会是一样的。
(13.15)
输出结果会改变,但是由于函数的参数是非引用类型,在调用的时候会再次使用拷贝构造函数,和我们理想的结果不一样
(13.16)
将会是理想的效果,输出结果为原a,b,c的mysn
(13.17)
//14题的验证 可以重写numbered(const numbered&),然后调用test15即可。
//结果符合我们的预想
static int id = 0;
class numbered {
public:
int mysn;
numbered() {
mysn = id++;
}
numbered(const numbered&) {
mysn = id++;
}
};
void f(numbered s) {cout << s.mysn << endl;}
void f0(numbered &s) {cout << s.mysn << endl;}
void test15() {
numbered a, b = a, c = b;
f(a); f(b); f(c);
}
void test16() {
numbered a, b = a, c = b;
f0(a); f0(b); f0(c);
}
(13.18)
static int ID = 0;
class Employee {
public:
string name;
int isbn;
Employee() {
isbn = ID++;
}
Employee(string s) {
name = s;
isbn = ID++;
}
Employee(const Employee& e) {
name = e.name;
isbn = ID++;
}
Employee& operator=(const Employee& e) {
name = e.name;
isbn = e.isbn;
return *this;
}
};
(13.19)
需要定义拷贝控制成员:拷贝构造函数和拷贝赋值运算符,否则将不能保证雇员证号唯一
(13.20)
拷贝和赋值TextQuery对象时,file和wm会被拷贝,对于shared_ptr类型的file计数加1
删除TextQuery对象时,file和wm会被销毁,对于shared_ptr类型的file计数减1,若为0,其管理的内存被释放
拷贝和赋值QueryResult对象时,sought,lines,file会被拷贝,对于shared_ptr类型的lines和file计数会加1
删除QueryResult对象时,sought,lines,file会被销毁,对于shared_ptr类型的lines和file计数会减1,计数为0的时候其管理的内存会被销毁
(13.21)
不需要,因为对于动态管理的内存已经有智能指针实现,临时对象拷贝赋值和销毁也已经是实现了的。
(13.22)
class HasPtr{
private:
int num;
string *str = NULL;
public:
HasPtr() {}
HasPtr(int i, string s): num(i), str(new string(s)) { cout << "()" << endl; }
HasPtr(const HasPtr &p): num(p.num), str(new string(*(p.str))) { cout << "(const&)" << endl; }
HasPtr& operator=(const HasPtr &p) {
cout << "operator=" << endl;
num = p.num;
auto newstr = new string(*(p.str));
delete str;
str = newstr;
return *this;
}
~HasPtr() {delete str;}
void input() {
cout << num << " " << str << endl;
}
};
void test22() {
HasPtr hp0 = HasPtr(0, "zl");
HasPtr hp1 = hp0;
HasPtr hp2;
hp2 = hp0;
hp0.input();
hp1.input();
hp2.input();
}
(13.23)
赋值操作忘记删除当前的string,没有定义析构函数
(13.24)
析构函数未定义,导致内存不会被释放;
拷贝构造函数未定义,会导致两个对象会指向同一个string,其中一个被删除,则另一个指针空悬;
(13.25)
注意的地方就是拷贝构造函数和拷贝构造赋值运算符获得的对象不应该和原对象共享vector的内存,需要重新新建一个内存。
(13.26)
//类值版本的拷贝构造函数
StrBlob( const StrBlob &sb ) {
data = make_shared<vector<string>>(*sb.data);
}
//类值版本的拷贝赋值运算符
StrBlob& operator=( const StrBlob &sb ) {
auto newdata = make_shared<vector<string>>(*sb.data);
delete data;
data = newdata;
return *this;
}
(13.27)
class HasPtr{
private:
string *str = NULL;
int num;
int *use = NULL;
public:
HasPtr(const string &s = string()): str(new string(s)), num(0), use(new int(1)) { }
HasPtr(const HasPtr& hp) : str(hp.str), num(hp.num), use(p.use) {++*use;}
HasPtr& operator=(const HasPtr& hp) {
++*hp.use;
if(--*use == 0) {
delete str;
delete use;
}
str = hp.str;
num = hp.num;
use = hp.use;
return *this;
}
~HasPtr() {
if(--*use == 0) {
delete str;
delete use;
}
}
};
(13.28)
(a)
//参考HasPtr
class TreeNode {
public:
TreeNode() : value(string()), count(new int(1)), left(NULL), right(NULL){ }
TreeNode(const TreeNode& rhs)
: value(rhs.value), count(rhs.count), left(rhs.left), right(rhs.right) {
++*count;
}
TreeNode& operator=(const TreeNode& rhs) {
++*rhs.count;
if (--*count == 0) {
delete left;
delete right;
delete count;
}
value = rhs.value;
left = rhs.left;
right = rhs.right;
count = rhs.count;
return *this;
}
~TreeNode() {
if (--*count == 0) {
delete left;
delete right;
delete count;
}
}
private:
std::string value;
int* count;
TreeNode* left;
TreeNode* right;
};
(b)
class BinStrTree {
public:
BinStrTree() : root(new TreeNode()) {}
BinStrTree(const BinStrTree& bst) : root(new TreeNode(*bst.root)) {}
BinStrTree& operator=(const BinStrTree& bst) {
TreeNode* new_root = new TreeNode(*bst.root);
delete root;
root = new_root;
return *this;
}
~BinStrTree() { delete root; }
private:
TreeNode* root;
};
(13.29)
swap(HasPtr&, HasPtr&)中调用的swap是标准库的函数
(13.30)
class HasPtr{
//...
friend void swap(HasPtr &hp0, HasPtr &hp1) {
cout << "HasPtr swap" << endl;
using std::swap;
swap(hp0.str, hp1.str);
swap(hp0.num, hp1.num);
}
//...
};
void test30() {
HasPtr hp0 = HasPtr(0, "zl0");
HasPtr hp1 = HasPtr(1, "zl1");
hp0.input();
hp1.input();
swap(hp0, hp1);
hp0.input();
hp1.input();
}
(13.31)
当vector中的元素较少时,sort会采用插入排序法,不会调用swap函数,当元素数量增大到一定数量时,才会采用快速排序法,调用swap函数。
class HasPtr{
//...
bool operator<(const HasPtr &hp) {
return num < hp.num;
}
//...
};
void test31() {
HasPtr hp0 = HasPtr(0, "zl0");
HasPtr hp1 = HasPtr(1, "zl0");
HasPtr hp2 = HasPtr(2, "zl0");
cout << "init vector" << endl;
vector<HasPtr> vhp = {hp2, hp0, hp1};
cout << "sort begin()" << endl;
sort(vhp.begin(), vhp.end());
}
(13.32)
不会,因为HasPtr版本的swap是为了减少内存分配而不去交换临时对象,直接去交换两个指针。
(13.33)
用引用当参数可以避免使用拷贝构造函数,导致Message是添加删除到拷贝的Floder中;不用常量引用是因为sava和remove会对Floder进行增删操作
(13.34)
#include <iostream>
#include <string>
#include <set>
class Message {
friend class Folder;
friend void swap(Message&, Message&);
public:
explicit Message(const std::string &str = " "):
content(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_from_Folders();
};
class Folder {
public:
Folder() = default;
Floder(const Floder&);
~Floder();
Floder& operator=(const Floder&);
void print(ostream&);
private:
set<Message*> msgs;
void addFldr(Message *m) {msgs.insert(m);}
void remFldr(Message *m) {msgs.erase(m);}
void add_to_Messages(const Floder&);
void remove_from_Messages();
}
void Floder::add_to_Messages(const Floder &f) {
for(auto m : f.msgs)
m->addFldr(this);
}
void Floder::remove_from_Messages() {
for(auto m : f.msgs)
m->remFldr(this);
}
Floder::Floder(const Floder &f) : msgs(f.msgs) {
add_to_Messages(f);
}
Floder::~Floder() { remove_from_Messages(); }
Floder& Floder::operator=(const Floder& f) {
remove_from_Messages();
msgs = f.msgs;
add_to_Messages(f);
return *this;
}
void Floder::print(ostream &os) {
for(auto m : msgs) os << m->contents << endl;
os << endl;
}
void Message::save(Folder &f) {
folders.insert(&f);
f.addMsg(this);
}
void Message::remove(Folder &f) {
folders.erase(&f);
f.remMsg(this);
}
void Message::add_to_Folders(const Message &m) {
for(auto f : m.folders)
f->addMsg(this);
}
Message::Message(const Message &m):
contents(m.contents), folders(m.folders) {
add_to_Folders(m);
}
void Message::remove_from_Folders() {
for(auto f : folders)
f->remMsg(this);
}
Message::~Message() {
remove_from_Folders();
}
Message& Message::operator=(const Message &rhs) {
remove_from_Folders();
contents = rhs.contents;
folders = rhs.folders;
add_to_Folders(rhs);
return *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.contents, rhs.contents);
for(auto f : lhs.folders)
f->addMsg(&lhs);
for(auto f : rhs.folders)
f->addMsg(&rhs);
}
(13.35)
使用合成的拷贝构造函数,将不会把拷贝的Message副本添加到对于的Floder中
使用合成的拷贝赋值运算符,将不会把左边的Message原Floder记录删除,而且不会把右边的Message的Floder记录添加到左边去。
使用合成的析构函数,将不会删除Floder对应的Message信息
(13.36)
见34
(13.37)
见34
(13.38)
因为用swap方式实际赋值运算符,其参数是非引用的,尽管用swap可以提高其效能,但是用非引用的参数会用到拷贝构造函数。对于这道题,明显拷贝构造函数会造成更多的浪费。
(13.39)
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class StrVec {
public:
StrVec() : elements(NULL),first_free(NULL), cap(NULL) { }
StrVec(const StrVec&);
StrVec &operator=(const StrVec&);
~StrVec();
void push_back(const string&);
size_t size() const {return first_free - elements;}
size_t capacity() const {return cap - elements;}
string *begin() const {return elements;}
string *end() const {return first_free;}
private:
static allocator<string> alloc;
void chk_n_alloc() {
if(size() == capacity()) reallocate();
}
pair<string*, string*> alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string *elements; //数组首元素
string *first_free; //数组第一个空闲元素
string *cap; //数组尾后位置
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string &s) {
chk_n_alloc();
alloc.construct(first_free++, s);
}
pair<string*, string*> StrVec::alloc_n_copy(const string *b, const string *e) {
auto data = alloc.allocate(e - b);
return {data, uninitialized_copy(b, e, data)};
}
void StrVec::free() {
if(elements) {
for(auto p = first_free; p != elements;) {
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
StrVec::StrVec(const StrVec &s) {
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec() {free(); }
StrVec &StrVec::operator=(const StrVec &s) {
auto data = alloc_n_copy(s.begin(), s.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
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++,move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
(13.40)
StrVec( initializer_list<string> &ls) {
auto newdata = alloc_n_copy(ls.begin(), ls.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
(13.41)
前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。
(13.42)
把之前的vector<string>替换成StrVec
//TextQuery.h
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <set>
#include "StrVec.h"
using namespace std;
typedef size_t line_no;
class QueryResult;
class TextQuery {
public:
TextQuery(ifstream&);
QueryResult query(const string &) const;
private:
shared_ptr<StrVec> file;
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(string s, shared_ptr<set<line_no>> p,
shared_ptr<StrVec> f):
sought(s), lines(p), file(f){ }
private:
string sought;
shared_ptr<set<line_no>> lines;
shared_ptr<StrVec> file;
};
ostream &print(ostream & os, const QueryResult &qr) {
os << qr.sought << " occurs " << qr.lines->size() << " "
<< (((qr.lines->size()) > 1)? " times " : " time ") << endl;
for(auto num : *qr.lines) {
os << "\t(line " << num + 1 << ") "
<< *(qr.file->begin() + num) << endl;
}
return os;
}
TextQuery::TextQuery(ifstream &is) : file(new StrVec()) {
string text;
while(getline(is, text)) {
file->push_back(text);
int n = file->size() - 1;
istringstream line(text);
string word;
while(line >> word) {
auto &lines = wm[word];
if(!lines)
lines.reset(new set<line_no>);
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const string &sought) const {
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if(loc == wm.end())
return QueryResult(sought, nodata, file);
else
return QueryResult(sought, loc->second, file);
}
//main.cpp
#include "TextQuery.h"
int main() {
ifstream fin;
fin.open("test.txt");
TextQuery tq(fin);
string s;
while(cin>>s && s != "q") {
QueryResult qr = tq.query(s);
print(cout, qr);
}
}
(13.43)
void StrVec::free() {
for_each(elements, first_free, [](const string &s){alloc.destroy(&s);});
alloc.deallocate(elements, cap - elements);
}
很明显for_each内部已经封装了迭代器的增加,避免外面递增使用错误
(13.44)
#include <iostream>
#include <cstring>
#include <memory>
using namespace std;
class String {
public:
String(): sz(0), p(NULL){ }
//接受C风格字符串构造函数
String(const char *cp): sz(strlen(cp)), p(alloc.allocate(sz)) {
uninitialized_copy(cp, cp + sz, p);
}
String(const String &s): sz(s.sz), p(alloc.allocate(sz)) {
uninitialized_copy(s.p, s.p + sz, p);
}
String& operator=(const String &s) {
auto newp = alloc.allocate(s.sz);
uninitialized_copy(s.p, s.p + s.sz, newp);
if(p) alloc.deallocate(p, sz);
sz = s.sz;
p = newp;
return *this;
}
void input() {
auto newp = p;
for(size_t i = 0; i < sz; ++i, ++newp) {
cout << *newp;
}
cout << endl;
}
~String(){delete p;}
private:
static allocator<char> alloc;
size_t sz;
char *p;
};
allocator<char> String::alloc;
int main() {
String s("sssss");
s.input();
const char *p = "zlfight";
String s1(p);
s1.input();
s = s1;
s.input();
return 0;
}
(13.45)
赋值、下标、解引用和前置递增/递减运算符,返回左值
算术、关系、位以及后置递增/递减运算符,生成右值
左值持久,右值短暂
变量是左值,不能将一个右值引用直接绑定到一个变量上,即使这个变量是右值引用类型也不行
eg.
int &&rr1 = 42;
int &&rr2 = rr1; //error
调用move获得绑定到左值上的右值引用,定义在utility中;
int &&rr2 = move(rr1);//ok
当调用move之后,rr1将被销毁,除非赋予它新值,否则将不能使用该值。
(13.46)
int f(); //返回一个右值
vector<int> vi(100);
int&& r1 = f();
int& r2 = vi[0];//下标返回左值
int& r3 = r1;//r1是变量
int&& r4 = v[0] * f();//算术运算返回右值
(13.47)
#include <iostream>
#include <cstring>
#include <memory>
#include <vector>
using namespace std;
class String {
public:
String(): sz(0), p(NULL){ }
//接受C风格字符串构造函数
String(const char *cp): sz(strlen(cp)), p(alloc.allocate(sz)) {
uninitialized_copy(cp, cp + sz, p);
}
String(const String &s): sz(s.sz), p(alloc.allocate(sz)) {
cout << "调用拷贝构造" << endl;
uninitialized_copy(s.p, s.p + sz, p);
}
String& operator=(const String &s) {
cout << "调用拷贝赋值" << endl;
auto newp = alloc.allocate(s.sz);
uninitialized_copy(s.p, s.p + s.sz, newp);
if(p) alloc.deallocate(p, sz);
sz = s.sz;
p = newp;
return *this;
}
void input() {
auto newp = p;
for(size_t i = 0; i < sz; ++i, ++newp) {
cout << *newp;
}
cout << endl;
}
~String(){delete p;}
private:
static allocator<char> alloc;
size_t sz;
char *p;
};
allocator<char> String::alloc;
int main() {
vector<String> vs;
vs.reserve(16);
String s("test");
for(int i = 0; i < 10; ++i) {
cout << "vs.size(): " << vs.size() << ", vs.capacity(): " << vs.capacity() << endl;
cout << "push_back start()" << endl;
vs.push_back(s);
cout << "push_back end()" << endl;
}
return 0;
}
(13.48)
如果一开始没有给vector预分配空间,vector<> 空间分配是按2的幂次方递增的,在空间不足是会调用拷贝构造,把小的拷贝到大的上面去,所以对容器里面的元素也会调用拷贝构造(ps:因为没有定义移动构造函数,所以会调用拷贝构造函数),然后再调用push_back的拷贝构造函数。
但是如果一开始就给容器分配空间,每次push_back只会调用一次拷贝构造函数。
(13.49)
//StrVec
//移动构造函数
StrVec(StrVec &&s) noexcept : elements(s.elements), first_free(s.first_free), cap(s.cap) {
s.elements = s.first_free = s.cap = NULL;
}
//移动赋值
StrVec& operator=(StrVec &&s) noexcept {
if(this != &s) {
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = NULL;
}
return *this;
}
//String
//移动构造函数
String(String&& s) noexcept : sz(s.sz), p(s.p){
cout << "调用移动构造" << endl;
s.p = NULL;
}
//移动赋值运算符
String& operator=(String &&s) {
cout << "调用移动赋值" << endl;
if(this != &s) {
sz = s.sz;
p = s.p;
s.p = NULL;
}
return *this;
}
(13.50)
当vector内存不足,扩大内存的时候,会调用移动构造函数而不是拷贝构造函数
(13.51)
因为函数返回类型是值方式,所以返回的是右值,所以初始化或者赋值给一个unique_ptr时会调用它的移动构造函数或者移动赋值运算符接管此函数中unique_ptr变量的所有权。所以是合法的
(13.52)
hp = hp2;
hp2是一个左值,因此rhs将使用拷贝构造函数初始化;
hp = std::move(hp2);
调用std::move将一个右值引用绑定到hp2上,拷贝构造函数和移动构造函数都可行,但是实参是一个右值引用,移动构造函数精确匹配,rhs将使用移动构造函数;
(13.53)
因为赋值运算符尽管是使用swap交换指针,避免拷贝,但是rhs的生成是调用了拷贝构造函数了的。
HasPtr& operator=(const HasPtr &p) {
cout << "operator=" << endl;
num = p.num;
str = new string(*(p.str));
return *this;
}
HasPtr& operator=(HasPtr &&hp) {
if(this != &hp) {
num = hp.num;
str = p.str;
p.str = NULL;
}
return *this;
}
(13.54)
产生二义性错误
(13.55)
void push_back(string &&s) {
chk_n_alloc();
alloc.construct(first_free++, move(s));
}
(13.56)
因为ret是一个左值,左值调用的函数还是const &的函数,导致一直循环调用函数
(13.57)
因为Foo(*this)是一个右值,会调用&&的函数
(13.58)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Foo {
public:
Foo(vector<int> &v) : data(v) { }
Foo sorted() && {
cout << "&&" << endl;
sort(data.begin(), data.end());
return *this;
}
Foo sorted() const &{
cout << "const &" << endl;
//调用右值版本的sorted
return Foo(*this).sorted();
//Foo ret(*this);
//无限调用左值版本的sorted
//return ret.sorted();
//内部排序版本
//sort(ret.data.begin(), ret.data.end());
//return *this;
}
private:
vector<int> data;
};
int main() {
vector<int> tmp = {0,2,1,3};
Foo f0(tmp);
f0.sorted();
}