c++primer 13.33-13.44

2019-06-08  本文已影响0人  青吟乐

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的形式返回
    }


};
上一篇下一篇

猜你喜欢

热点阅读