13.33
Folder &f的原因是需要对Folder的对象数据(对象的folder)进行修改,需要使用引用,若是Folder参数,则是修改Folder对象的拷贝,若是const Folder &则不能进行属性的修改
13.34
#include<iostream>
#include<set>
using std::string;
using std::vector;
class Message{
public:
//folders被隐式的初始化为空集合
explicit Message(const std::string &str = ""){
contents = str;
}
//拷贝控制成员
Message(const Message&); //拷贝构造函数
Message& operator=(const Message&); //拷贝赋值运算符
~Message(); //析构函数
//从给定的Folder集合中添加或者删除本Message
void save(Folder&);
void remove(Folder&);
private:
string contents; //实际消息文本
std::set<Folder*> folders; //包含本Message的Folder
//拷贝构造函数,拷贝赋值运算符,和析构函数所使用的工具函数
//将本Message添加到指向参数的Folder中;
void add_to_Folder(const Message&);
//从folder中的每个Folder中删除本Message
void remove_form_Folders();
};
void Message::save(Folder &f){
folders.insert(&f); //将给定的Folder添加到我们的Folder列表中
f.addMsg(this); //将本Message添加到f的Message集合中
}
void Message::remove(Folder &f){
folders.erase(&f); //将给定的Folder从我们的Folder列表中删除
f.remMsg(this); //将本Message从f的Message集合中删除
}
//将本Message添加到指定M的Folder中
void Message::add_to_Folder(const Message &M){
for(auto f:M.folders){ //对每个包含M的folder
f->addMsg(this); //向该Folder添加一个指向本Message的指针
}
}
Message::Message(const Message &m){
contents = m.contents;
folders = m.folders;
add_to_Folder(m);//将本消息添加到指向m的Folder中
}
void Message::remove_form_Folders(){
for(auto c:folders){
c->remMsg(this);
}
}
Message::~Message(){
remove_form_Folders();
}
Message & Message::operator=(const Message & rhs){
//通过先删除指针再插入他们来实现自赋值的情况
remove_form_Folders(); //更新已有的Folder
contents = rhs.contents; //从rhs处拷贝消息内容
folders = rhs.folders; //从rhs处拷贝Folder指针
add_to_Folder(rhs); //将本Message添加到那些Folder中
return *this;
}
void swap(Message &lhs,Message &rhs){
using std::swap;
//将指针从它原来所在的floder删除
for(auto f : lhs.folders){
f->remMsag(&lhs);
}
for(auto f : rhs.folders){
f->remMsag(&rhs);
}
//交换content和Folder指针set
swap(lhs.folders,rhs.folders); //使用swap(set& set&)
swap(lhs.contents,rhs.contents); //swap(string,string)
//将每个message的指针添加到它的新Floder中
for(auto f:lhs.folders){
f->addMsg(&lhs);
}
for(auto f:rhs.folders){
f->addMsg(&rhs);
}
}
13.35
合成拷贝构造函数无法将本消息添加到folder列队,使用合成拷贝赋值运算符只会
contents = rhs.contents;
folders = rhs.folders;
不会先删除左侧对象的set中每个folder指向本对象的指针
也不会重新赋予右侧对象每个folder (赋值完成后也是左侧的folder)指向赋值后的左侧对象(即当前Message)的指针
合成析构函数会销毁当前message对象,所有指向它的指针变为危险指针但是自己定义的析构函数就会通过remMsg函数释放指针指向的内存并且处理指针
13.36
class Folder
{
public:
Folder();
~Folder();
Folder& operator=(const Folder&);
Folder(const Folder&);
void addMsg(Message *m)
{
Mset.insert(m);
}
void remMsg(Message *m)
{
Mset.erase(m);
}
private:
set<Message*> Mset;
};
13.37
增加如下两个方法
void addFldr(Folder* f) { folders.insert(f); }
void remFlddr(Folder* f) { folders.erase(f); }
13.38
拷贝交换适合动态分配内存的方法
13.39
具体请看我的注释
#ifndef STRVEC_H_INCLUDED
#define STRVEC_H_INCLUDED
#include<memory>
/**类似于vector内存分配策略的简化实现*/
class StrVec{
public:
StrVec(): elements(nullptr),first_free(nullptr),cap(nullptr){} //allocator成员进行默认初始化
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;
}
private:
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; //指向数组尾后位置的指针
void alloc_n_move(size_t n); //申请n长的内存块
void reserve(size_t n); //分配至少能容下n个元素的内存空间
void resize(size_t n); //分配至少能容下n个元素的内存空间,空位置值初始化
};
void StrVec::push_back(const std::string&s){
chk_n_alloc(); //确保有空间容纳新的元素
alloc.construct(first_free++,s); //在first_free指向的元素中构造s的副本
}
std::pair<std::string*,std::string*> StrVec::alloc_n_copy(const std::string *a , const std::string *e){
//分配空间保存给定范围中的元素
auto data = alloc.allocate(e-a);
//初始化并返回一个pair,该pair是有data和uninitialized_copy的返回值构成
auto llast = uninitialized_copy(a,e,data);
return {data,llast};
}
void StrVec::free(){
//不能传递给deallocate一个空的指针,如果elements为0,函数什么也不做
if(elements){
for(auto p = first_free;p!=elements;){
alloc.destroy(--p);//先destory销毁元素
}
alloc.deallocate(elements,cap - elements);//释放元素占用的内存
}
}
StrVec::StrVec(const StrVec &s){
//调用alloc_n_copy分配空间以容纳与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 &rhs){
//调用alloc_n_copy分配内存,大小与rhs中元素占用空间一样多
auto data = alloc_n_copy(rhs.begin(),rhs.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++,std::move(*elem++));
}
free(); //转移完成后旧释放旧内存空间
//更新我们的数据结构
elements = newdata;
first_free = dest;
cap = elements+newcapacity;
}
//13.39
void StrVec::alloc_n_move(size_t n)
{
//申请n长的内存块
auto newdata = alloc.allocate(n);
//该内存块起始点
auto dest = newdata;
//指示旧元素地址的起始
auto elem = elements;
//在新内存挨个使用移动构造函数构造进新内存
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();//构造完毕后释放旧内存
//更新当前StrVec属性
elements = newdata;
first_free = dest;
cap = elements + n;
}
void StrVec::reserve(size_t n)
{
if (n <= capacity()) return; //若是要求的内存长度小于当下长度,直接返回,什么也不做
alloc_n_move(n); //分配n个长度的空间
}
void StrVec::resize(size_t n){
if(n>size() ){//比当前大分两种情况,一种是比最大容量还要大,另一种是小于最大容量
if(n>capacity()){
reserve(2*n);
}
for(size_t i =size();i!=n;i++){
alloc.construct(first_free++," ");
}
}else if(n<size()){//比当前小就要从后往前一个个销毁
while(first_free!= elements+n){
alloc.destroy(first_free);
first_free--;
}
}
}
#endif // STRVEC_H_INCLUDED
11.40
在public中加上
StrVec(std::initializer_list<std::string> il);
实现
StrVec::StrVec(std::initializer_list<std::string> il){
auto newdata = alloc_n_copy(il.begin(),il.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
11.41
因为first_free已经是当前StrVec对象中最后一个元素之后的位置了,使用时需要先把这个位置的元素构造起来,再后移才行,如果前置,就是先空一个位置,在下一个位置构造string
11.42
略。。。
11.43
void StrVec::free(){
if(elements){
for_each(elements,first_free,[this](std::string &s){alloc.destroy(&s);});
alloc.deallocate(elements,cap - elements);//释放元素占用的内存
}
}
11.44
const_cast的作用:
一、常量指针被转化成非常量的指针,并且仍然指向原来的对象;
二、常量引用被转换成非常量的引用,并且仍然指向原来的对象;
三、const_cast一般用于修改底指针。如const char *p形式。
#include <string>
#include <algorithm>
#include <memory>
class String{
public:
String(); //默认构造函数
String(const char *s){
auto s1 = const_cast<char*> (s); //const_cast的作用就是解const
for(;*s1;s1++); //计算字符长度
alloc_n_copy(s,s1); //申请内存
}
String(const String&);
String & operator=(const String& );
~String(){
free();
}
void free(){
if(elements){//若elements不为空
std::for_each(elements,first_free,[this](char &c){alloc.destroy(&c);});
alloc.deallocate(elements,first_free-elements);
}
}
private:
std::allocator<char> alloc; //用于申请内存
char *elements; //首指针
char *first_free; //尾后指针
std::pair<char*,char*> alloc_n_copy(const char *a,const char *b){
auto first_address = alloc.allocate(b-a); //返回申请内存的首指针
auto last_f_address = std::uninitialized_copy(a,b,first_address);//返回构造后的尾后指针
return {first_address,last_f_address}; //以pair的形式返回
}
};
网友评论