为你的 HasPtr 类定义一个 < 运算符,并定义一个 HasPtr 的 vector。为这个 vector 添加一些元素,并对它执行 sort。注意何时会调用 swap。
#ifndef CP5_ex13_31_h
#define CP5_ex13_31_h
#include
#include
class HasPtr
{
public:
friend void swap(HasPtr&, HasPtr&);
friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);
HasPtr(const std::string &s = std::string())
: ps(new std::string(s)), i(0)
{}
HasPtr(const HasPtr &hp)
: ps(new std::string(*hp.ps)), i(hp.i)
{}
HasPtr& operator=(HasPtr tmp)
{
this->swap(tmp);
return *this;
}
~HasPtr()
{
delete ps;
}
void swap(HasPtr &rhs)
{
using std::swap;
swap(ps, rhs.ps);
swap(i, rhs.i);
std::cout << "call swap(HasPtr &rhs)" << std::endl;
}
void show() const
{
std::cout << *ps << std::endl;
}
private:
std::string *ps;
int i;
};
void swap(HasPtr& lhs, HasPtr& rhs)
{
lhs.swap(rhs);
}
bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
return *lhs.ps < *rhs.ps;
}
#endif
类指针的 HasPtr 版本会从 swap 函数收益吗?如果会,得到了什么益处?如果不是,为什么?
不会。类值的版本利用swap交换指针不用进行内存分配,因此得到了性能上的提升。类指针的版本本来就不用进行内存分配,所以不会得到性能提升。
为什么Message的成员save和remove的参数是一个 Folder&?为什么我们不能将参数定义为 Folder 或是 const Folder?
因为 save 和 remove 操作需要更新指定 Folder。
编写本节所描述的 Message。
#ifndef ex13_34_h
#define ex13_34_h
#include
#include
class Folder;
class Message
{
friend void swap(Message &, Message &);
friend void swap(Folder &, Folder &);
friend class Folder;
public:
explicit Message(const std::string& s = "") :contents(s) {}
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
void print_debug();
private:
std::string contents;
std::set folders;
void add_to_Folders(const Message&);
void remove_from_Folders();
void addFldr(Folder* f) { folders.insert(f); }
void remFlddr(Folder* f) { folders.erase(f); }
};
void swap(Message&, Message&);
class Folder
{
friend void swap(Message&, Message&);
friend void swap(Folder&, Folder&);
friend class Message;
public:
Folder() = default;
Folder(const Folder&);
Folder& operator=(const Folder&);
~Folder();
void print_debug();
private:
std::set msgs;
void add_to_Message(const Folder&);
void remove_to_Message();
void addMsg(Message* m) { msgs.insert(m); }
void remMsg(Message *m) { msgs.erase(m); }
};
void swap(Folder&, Folder&);
#endif // !ex13_34_h
#include "exercise13_34.h"
#include
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);
}
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);
folders.clear();
}
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 Message::print_debug()
{
std::cout << contents << std::endl;
}
void swap(Folder& lhs, Folder& rhs)
{
using std::swap;
for (auto m : lhs.msgs)
m->remFlddr(&lhs);
for (auto m : rhs.msgs)
m->remFlddr(&rhs);
swap(lhs.msgs, rhs.msgs);
for (auto m : lhs.msgs)
m->addFldr(&lhs);
for (auto m : rhs.msgs)
m->addFldr(&rhs);
}
void Folder::add_to_Message(const Folder& f)
{
for (auto m : f.msgs)
m->addFldr(this);
}
Folder::Folder(const Folder& f) :msgs(f.msgs)
{
add_to_Message(f);
}
void Folder::remove_to_Message()
{
for (auto m : msgs)
m->remFlddr(this);
msgs.clear();
}
Folder::~Folder()
{
remove_to_Message();
}
Folder& Folder::operator=(const Folder& rhs)
{
remove_to_Message();
msgs = rhs.msgs;
add_to_Message(rhs);
return *this;
}
void Folder::print_debug()
{
for (auto m : msgs)
std::cout << m->contents << " ";
std::cout << std::endl;
}
如果Message 使用合成的拷贝控制成员,将会发生什么?
在赋值后一些已存在的 Folders
将会与 Message
不同步。拷贝构造函数和拷贝赋值运算符要重新动态分配内存。因为 StrBlob 使用的是智能指针,当引用计数为0时会自动释放对象,因此不需要析构函数。
设计并实现对应的 Folder 类。此类应该保存一个指向 Folder 中包含 Message 的 set。
#ifndef ex13_34_h
#define ex13_34_h
#include
#include
class Folder;
class Message
{
friend void swap(Message &, Message &);
friend void swap(Folder &, Folder &);
friend class Folder;
public:
explicit Message(const std::string& s = "") :contents(s) {}
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
void print_debug();
private:
std::string contents;
std::set folders;
void add_to_Folders(const Message&);
void remove_from_Folders();
void addFldr(Folder* f) { folders.insert(f); }
void remFlddr(Folder* f) { folders.erase(f); }
};
void swap(Message&, Message&);
class Folder
{
friend void swap(Message&, Message&);
friend void swap(Folder&, Folder&);
friend class Message;
public:
Folder() = default;
Folder(const Folder&);
Folder& operator=(const Folder&);
~Folder();
void print_debug();
private:
std::set msgs;
void add_to_Message(const Folder&);
void remove_to_Message();
void addMsg(Message* m) { msgs.insert(m); }
void remMsg(Message *m) { msgs.erase(m); }
};
void swap(Folder&, Folder&);
#endif // !ex13_34_h
#include "exercise13_34.h"
#include
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);
}
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);
folders.clear();
}
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 Message::print_debug()
{
std::cout << contents << std::endl;
}
void swap(Folder& lhs, Folder& rhs)
{
using std::swap;
for (auto m : lhs.msgs)
m->remFlddr(&lhs);
for (auto m : rhs.msgs)
m->remFlddr(&rhs);
swap(lhs.msgs, rhs.msgs);
for (auto m : lhs.msgs)
m->addFldr(&lhs);
for (auto m : rhs.msgs)
m->addFldr(&rhs);
}
void Folder::add_to_Message(const Folder& f)
{
for (auto m : f.msgs)
m->addFldr(this);
}
Folder::Folder(const Folder& f) :msgs(f.msgs)
{
add_to_Message(f);
}
void Folder::remove_to_Message()
{
for (auto m : msgs)
m->remFlddr(this);
msgs.clear();
}
Folder::~Folder()
{
remove_to_Message();
}
Folder& Folder::operator=(const Folder& rhs)
{
remove_to_Message();
msgs = rhs.msgs;
add_to_Message(rhs);
return *this;
}
void Folder::print_debug()
{
for (auto m : msgs)
std::cout << m->contents << " ";
std::cout << std::endl;
}
为 Message 类添加成员,实现向 folders 添加和删除一个给定的 Folder*。这两个成员类似Folder 类的 addMsg 和 remMsg 操作。
#ifndef ex13_34_h
#define ex13_34_h
#include
#include
class Folder;
class Message
{
friend void swap(Message &, Message &);
friend void swap(Folder &, Folder &);
friend class Folder;
public:
explicit Message(const std::string& s = "") :contents(s) {}
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
void print_debug();
private:
std::string contents;
std::set folders;
void add_to_Folders(const Message&);
void remove_from_Folders();
void addFldr(Folder* f) { folders.insert(f); }
void remFlddr(Folder* f) { folders.erase(f); }
};
void swap(Message&, Message&);
class Folder
{
friend void swap(Message&, Message&);
friend void swap(Folder&, Folder&);
friend class Message;
public:
Folder() = default;
Folder(const Folder&);
Folder& operator=(const Folder&);
~Folder();
void print_debug();
private:
std::set msgs;
void add_to_Message(const Folder&);
void remove_to_Message();
void addMsg(Message* m) { msgs.insert(m); }
void remMsg(Message *m) { msgs.erase(m); }
};
void swap(Folder&, Folder&);
#endif // !ex13_34_h
#include "exercise13_34.h"
#include
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);
}
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);
folders.clear();
}
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 Message::print_debug()
{
std::cout << contents << std::endl;
}
void swap(Folder& lhs, Folder& rhs)
{
using std::swap;
for (auto m : lhs.msgs)
m->remFlddr(&lhs);
for (auto m : rhs.msgs)
m->remFlddr(&rhs);
swap(lhs.msgs, rhs.msgs);
for (auto m : lhs.msgs)
m->addFldr(&lhs);
for (auto m : rhs.msgs)
m->addFldr(&rhs);
}
void Folder::add_to_Message(const Folder& f)
{
for (auto m : f.msgs)
m->addFldr(this);
}
Folder::Folder(const Folder& f) :msgs(f.msgs)
{
add_to_Message(f);
}
void Folder::remove_to_Message()
{
for (auto m : msgs)
m->remFlddr(this);
msgs.clear();
}
Folder::~Folder()
{
remove_to_Message();
}
Folder& Folder::operator=(const Folder& rhs)
{
remove_to_Message();
msgs = rhs.msgs;
add_to_Message(rhs);
return *this;
}
void Folder::print_debug()
{
for (auto m : msgs)
std::cout << m->contents << " ";
std::cout << std::endl;
}
我们并未使用拷贝交换方式来设计 Message 的赋值运算符。你认为其原因是什么?
对于动态分配内存的例子来说,拷贝交换方式是一种简洁的设计。而这里的 Message 类并不需要动态分配内存,用拷贝交换方式只会增加实现的复杂度。
编写你自己版本的 StrVec,包括自己版本的 reserve、capacity 和 resize。
#ifndef ex13_39_h
#define ex13_39_h
#include
#include
class StrVec
{
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
StrVec(const StrVec&);
StrVec(std::initializer_list);//ex13.40
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; }
private:
std::pair alloc_n_copy(const std::string*, const std::string*);
void free();
void chk_n_alloc() { if (size() == capacity()) reallocate(); }
void reallocate();
void alloc_n_move(size_t new_cap);
void range_initialize(const std::string*, const std::string*);//ex13.40
private:
std::string *elements;
std::string *first_free;
std::string *cap;
std::allocator alloc;
};
#endif
#include "exercise13_39.h"
void StrVec::push_back(const std::string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
std::pair
StrVec::alloc_n_copy(const std::string* b, const std::string* e)
{
auto data = alloc.allocate(e - b);
return{ data, std::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& rhs)
{
auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
//ex13.40
StrVec::StrVec(std::initializer_list il)
{
range_initialize(il.begin(), il.end());
}
StrVec::~StrVec()
{
free();
}
StrVec& StrVec::operator=(const StrVec& rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::alloc_n_move(size_t new_cap)
{
auto newdata = alloc.allocate(new_cap);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + new_cap;
}
void StrVec::reallocate()
{
auto newcapacity = size() ? 2 * size() : 1;
alloc_n_move(newcapacity);
}
//ex13.40
void StrVec::range_initialize(const std::string* first, const std::string* last)
{
auto newdata = alloc_n_copy(first, last);
elements = newdata.first;
first_free = cap = newdata.second;
}
为你的 StrVec 类添加一个构造函数,它接受一个 initializer_list 参数。
#ifndef ex13_39_h
#define ex13_39_h
#include
#include
class StrVec
{
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
StrVec(const StrVec&);
StrVec(std::initializer_list);//ex13.40
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; }
private:
std::pair alloc_n_copy(const std::string*, const std::string*);
void free();
void chk_n_alloc() { if (size() == capacity()) reallocate(); }
void reallocate();
void alloc_n_move(size_t new_cap);
void range_initialize(const std::string*, const std::string*);//ex13.40
private:
std::string *elements;
std::string *first_free;
std::string *cap;
std::allocator alloc;
};
#endif
#include "exercise13_39.h"
void StrVec::push_back(const std::string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
std::pair
StrVec::alloc_n_copy(const std::string* b, const std::string* e)
{
auto data = alloc.allocate(e - b);
return{ data, std::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& rhs)
{
auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
//ex13.40
StrVec::StrVec(std::initializer_list il)
{
range_initialize(il.begin(), il.end());
}
StrVec::~StrVec()
{
free();
}
StrVec& StrVec::operator=(const StrVec& rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::alloc_n_move(size_t new_cap)
{
auto newdata = alloc.allocate(new_cap);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + new_cap;
}
void StrVec::reallocate()
{
auto newcapacity = size() ? 2 * size() : 1;
alloc_n_move(newcapacity);
}
//ex13.40
void StrVec::range_initialize(const std::string* first, const std::string* last)
{
auto newdata = alloc_n_copy(first, last);
elements = newdata.first;
first_free = cap = newdata.second;
}