c++primer练习13.22-13.38

练习13.22

假定我们希望HasPtr的行为像一个值。即,对于对象所指向的string成员,每个对象都有一份自己的拷贝。编写拷贝构造函数,拷贝赋值函数

class HasPtr{
    public::
    HasPtr(HasPtr&b):ptr(new std::string(*b.ptr)),cur(b.cur){    }
    HasPtr &operator=(const HasPtr&a){delete ptr;ptr=new std::string(*a.ptr);cur=a.cur;}
    private::
    std::string*ptr;
    int cur;
    
}; 

练习13.23

理解上一题和本节代码之间的差异

::上一题代码不够全面,没有设计析构函数,由于new的ptr不能默认删除,得在析构函数里delete,并且拷贝赋值函数应该用临时值拷贝,防止自赋值的未定义行为,类值的行为表明每一个对象都拥有自己独一份的拷贝

练习13.24

如果本节未定义析构函数,会发生什么?如果为定义构造函数,将会发生什么?

::未定义析构函数,动态内存将不会回收,未定义构造函数,析构函数的delete将引起错误,删除非动态内存

练习13.25

假定希望定义StrBlob的类值版本,而且希望继续使用shared_ptr,这样我们的StrBlob类就仍能使用指向vector的weak_ptr了。你修改后的类将需要一个拷贝构造函数和一个拷贝赋值运算符,但不需要析构函数。解释为什么需要拷贝构造和赋值运算符,和为什么不用析构

::为了达到类值的效果,shared_ptr必须是值初始化的,而不是拷贝初始化,拥有独一份的值,赋值同理,shared_ptr指针可以自己管理内存,无需手动delete,默认析构即可

练习13.26

编写StrBlob类

class StrBlob{
        friend class StrBlobPtr;
    public:
        StrBlob(StrBlob&a):data(make_shared<vector<string>>(*a.data); )
        StrBlob &operator=(const StrBlob&a){auto d=a.data;data=d;return *this;        }
    
    
    private:
    std::shared_ptr<std::vector<std::string> > data; 
   
    void check(size_type i, const std::string &msg) const;
}; 

练习13.27

定义你自己的使用引用计数版本的HasPtr

class HasPtr{
    public:
    HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){    }
    HasPtr(HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use;    }
    HasPtr &operator=(const HasPtr&a);
    ~HasPtr();
    private:
    std::string*ptr;
    int i;
    size_t*use;
    
}; 
HasPtr&
HasPtr::operator=(const HasPtr&a)
{
    ++*a.use;
    if(--use==0)
    {delete ptr;
    delete use;
    }
    ptr=a.ptr;
    use=a.use;
    i=a.i;
    return *this;
}
HasPtr::~HasPtr()
{
    if(--*use==0)
    {
        delete ptr;
        delete use;
    }
}

练习13.28

给定下面的类,为其实现一个默认构造函数和必要的拷贝控制成员

class TreeNode {
public:
    TreeNode():value(string()),count(1),left(new TreeNode),right(new TreeNode){    }
    TreeNode(const string&s):value(s),count(1),left(new TreeNode),right(new TreeNode){    }
    TreeNode (const TreeNode a):value(a.value),count(a.count),left(a.left),right(a.right){    }
    TreeNode &operator=(const TreeNode&a);
    ~TreeNode();
    
private:

std::string value;

int count;

TreeNode *left;

TreeNode *right;

};
    TreeNode &TreeNode::operator=(const TreeNode&a)
{
    value(a.value);
    count(a.count);
    left(a.left);
    right(a.right);
    return *this;
}
class BinStrTree{
        friend class TreeNode;
    public:
        BinStrTree():root(new TreeNode){        }
        BinStrTree(const TreeNode&a):root(&a){        }            
        BinStrTree(const BinStrTree&a):root(a.root){++a->count;        }
        BinStrTree &operator=(const BinStrTree&a);
        ~BinStrTree();
        
        
    private:
        TreeNode*root;
        
};
BinStrTree&operator=(const BinStrTree&a)
{
    ++a->count;
    if(--*this.count==0)
    {
        delete root;
    }
    root=a;
}
BinStrTree::~BinStrTree()
{
    if(--*this.count==0)
    {
        delete root;
    }
}

练习13.29

解释(HasPtr&,HasPtr&)中对swap的调用不会导致递归循环

::参数可以区别开标准库和类swap

练习13.30

为你的类值版本的HasPtr编写swap函数,并测试它。为你的swap函数添加打印语句,指出函数什么时候会执行

#include <string>
#include <iostream>
#include <algorithm>
using std::string;
class HasPtr{
    friend void swap(HasPtr&,HasPtr&);
    public:
    HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){    }
    HasPtr(HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use;    }
    HasPtr &operator=(const HasPtr&a);
    ~HasPtr();
    string* called(){return ptr;}
    private:
    std::string*ptr;
    int i;
    size_t*use;
    
}; 
HasPtr&
HasPtr::operator=(const HasPtr&a)
{
    ++*a.use;
    if(--use==0)
    {delete ptr;
    delete use;
    }
    ptr=a.ptr;
    use=a.use;
    i=a.i;
    return *this;
}
HasPtr::~HasPtr()
{
    if(--*use==0)
    {
        delete ptr;
        delete use;
    }
}
inline void swap(HasPtr &a,HasPtr &b)
{
    using std::swap;
    swap(a.ptr,b.ptr);
    swap(a.i,b.i);
    std::cout<<"done"<<std::endl;
}
int main()
{

    HasPtr a("beauty"),b("day");
  swap(a,b);

    std::cout<<*a.called()<<std::endl;
}

函数只在调用交换HasPtr类对象时执行,函数内部的仍是标准库

练习13.31

为你的HasPtr类定义一个<运算符,并定义一个HasPtr的vector,为这个vector添加一些元素,并对它执行sort。注意何时会调用swap。

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using std::string;
class HasPtr{
    friend void swap(HasPtr&,HasPtr&);
    friend bool operator<(const HasPtr&a,const HasPtr &b);
    public:
    HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){    }
    HasPtr(const HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use;    }
    HasPtr &operator=(const HasPtr&a);
    ~HasPtr();
    string* called(){return ptr;}
//    bool operator<(HasPtr &a);
    private:
    std::string*ptr;
    int i;
    size_t*use;
    
}; 
HasPtr&
HasPtr::operator=(const HasPtr&a)
{
    ++*a.use;
    if(--use==0)
    {delete ptr;
    delete use;
    }
    ptr=a.ptr;
    use=a.use;
    i=a.i;
    return *this;
}
bool operator<(const HasPtr&a,const HasPtr &b)
{
    std::cout<<"do<"<<std::endl;
    return *a.ptr<*b.ptr;
}
HasPtr::~HasPtr()
{
    if(--*use==0)
    {
        delete ptr;
        delete use;
    }
}
inline void swap(HasPtr &a,HasPtr &b)
{
    using std::swap;
    swap(a.ptr,b.ptr);
    swap(a.i,b.i);
    std::cout<<"done"<<std::endl;
}
int main()
{

    HasPtr a("beauty"),b("day"),c("oh");
    std::vector<HasPtr> line{a,b,c};
    std::sort(line.begin(),line.end());
    std::cout<<*line.begin()->called()<<std::endl;    
}

元素多的时候会用自己定义的swap

练习13.32

类指针的HasPtr版本会从swap函数收益吗?如果会,得到了什么益处,如果不会,为什么

::不会收益,默认的swap就能交换指向,无需多此一举

练习13.33

为什么Message的成员save和remove的参数是一个Folder&?为什么我们不将参数定义为Folder或者const Folder&

::为了包含该message对象的Folder能删除或添加该指针

练习13.34

编写本节所描述的Message

class Message{
    friend class Folder;
    public:
        Message(const string&s=" "):content(s){        }
        Message(const Message&a):content(a.content),folders(a.folders){add_to_Folders(a);    }
        Message& operator=(const Message &a){remove_from_Folders();content=a.content;folders=a.folders;
        add_to_Folders(a);return *this;
        }
    
        void save(Folder&f)
        {
            folders.insert(&f);
            f.addMsg(this);    
        }
        void remove(Folder&f)
        {
            folders.erase(&f);
            f.remMsg(this);
        }
    
    
    private:
    string content;
    set<Folder*> folders;
    
    void add_to_Folders(const Message &m)
    {
        for(auto d:m.folders)
        d->addMsg(this);
    }
    void remove_from_Folders()
    {
        for(auto d:folders)
        d->remMsg(this);
    }
};

练习13.35

如果Message使用合成的拷贝控制成员,将会发生什么?

::Message的folders和Folder的Msg就没有意义了

练习13.36

设计并实现对应的Folder类。此类应该保存一个指向Folder中包含的Message的set

class Folder{
    friend class Message;
    public:
        void addMsg(Message*a){Msg.insert(a);}
        void remMsg(Message*a){Msg.erase(a);    }
    
        
    private:
        set<Message*> Msg;    
    
};

练习13.37

为Message添加成员,实现向folders添加删除

::看13.34

练习13.38

我们并未使用拷贝并交换方式来设计Message的赋值运算符,你认为其原因是什么?

::没有什么益处

 

posted @ 2022-08-15 16:30  yddl  阅读(67)  评论(0编辑  收藏  举报