美文网首页
c++primer 13.33-13.44

c++primer 13.33-13.44

作者: 青吟乐 | 来源:发表于2019-06-08 00:02 被阅读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的形式返回
        }
    
    
    };
    

    相关文章

      网友评论

          本文标题:c++primer 13.33-13.44

          本文链接:https://www.haomeiwen.com/subject/pwcmxctx.html