Message and Folder Class Design

Quoted C++ Primer 5th Chapter 13.5 Classes That Management Dynamic Memory(P.519)

Introduce:

      As an example of a class that needs copy control in order to do some bookkeeping,we’ll sketch out two classes that might be used in a mail-handling application. These classes,Message and Folder, represent, respectively, email (or other kinds of)messages, and directories in which a message might appear. Each Message can appear in multiple Folders. However, there will be only one copy of the contents of any given Message. That way, if the contents of a Message are changed, those changes will appear when we view that Message from any of its Folders

Design:   

     To keep track of which Messages are in which Folders, each Message will store a set of pointers to the Folders in which it appears, and each Folder will contain a set of pointers to its Messages. Figure 13.1 illustrates this design


     Our Message class will provide save andremove operations to add or remove a Message from a specified Folder. To create a new Message, we will specify the contents of the message but no Folder. To put a Message in a particular Folder,we must call save.
     When we copy a Message, the copy and the original will be distinct Messages, but both Messages should appear in the same set of Folders. Thus, copying a Message will copy the contents and the set of Folder pointers. It must also add a pointer to the newly created Message to each of those Folders.
    When we destroy a Message, that Message no longer exists. Therefore, destroying a Message must remove pointers to that Message from the Folders that had contained that Message.
    When we assign one Message to another, we’ll replace the contents of the left-hand Message with those in the right-hand side. We must also update the set of Folders, removing the left-hand Message from its previous Folders and adding that Message to the Folders in which the right-hand Message appears.
    Looking at this list of operations, we can see that both the destructor and the copy-assignment operator have to remove this Message from the Folders that point to it.Similarly, both the copy constructor and the copy-assignment operator add a Message to a given list of Folders. We’ll define a pair of private utility functions
to do these tasks

    The Folder class will need analogous copy control members to add or remove itself from the Messages it stores.

Best Practices:The copy-assignment operator often does the same work as is needed in the copy constructor and destructor. In such cases, the common work should be put in private utility functions


The Message  and Folder Class

code quoted as follow:

https://github.com/Mooophy/Cpp-Primer/blob/master/ch13/README.md

#include <string>
#include <set>

class Folder;

class Message {
    friend void swap(Message &, Message &);
    friend class Folder;
public:
    explicit Message(const std::string &str = ""):contents(str) { }
    Message(const Message&);
    Message& operator=(const Message&);
    ~Message();
    void save(Folder&);
    void remove(Folder&);

    void print_debug();

private:
    std::string contents;
    std::set<Folder*> folders;

    void add_to_Folders(const Message&);
    void remove_from_Folders();

    void addFldr(Folder *f) { folders.insert(f); }
    void remFldr(Folder *f) { folders.erase(f); }
};

void swap(Message&, Message&);

class Folder {
    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<Message*> msgs;

    void add_to_Message(const Folder&);
    void remove_from_Message();

    void addMsg(Message *m) { msgs.insert(m); }
    void remMsg(Message *m) { msgs.erase(m); }
};

void swap(Folder &, Folder &);

#include "ex13_34_36_37.h"
#include <iostream>

void swap(Message &lhs, Message &rhs) 
{
    using std::swap;
    lhs.remove_from_Folders(); // Use existing member function to avoid duplicate code.
    rhs.remove_from_Folders(); // Use existing member function to avoid duplicate code.
    
    swap(lhs.folders, rhs.folders);
    swap(lhs.contents, rhs.contents);
    
    lhs.add_to_Folders(lhs); // Use existing member function to avoid duplicate code.
    rhs.add_to_Folders(rhs); // Use existing member function to avoid duplicate code.
}

// Message Implementation

void Message::save(Folder &f) 
{
    addFldr(&f); // Use existing member function to avoid duplicate code.
    f.addMsg(this);
}

void Message::remove(Folder &f) 
{
    remFldr(&f); // Use existing member function to avoid duplicate code.
    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);
}

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; 
}

// Folder Implementation

void swap(Folder &lhs, Folder &rhs) 
{
    using std::swap;
    lhs.remove_from_Message();
    rhs.remove_from_Message();

    swap(lhs.msgs, rhs.msgs);
    
    lhs.add_to_Message(lhs);
    rhs.add_to_Message(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_from_Message() 
{
    for (auto m : msgs)
        m->remFldr(this);
}

Folder::~Folder() 
{ 
    remove_from_Message(); 
}

Folder &Folder::operator=(const Folder &rhs) 
{
    remove_from_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;
}

We did not use copy and swap to define the Message assignment operator. Why do you suppose this is so?

@Mooophy The copy and swap is an elegant way when working with dynamicly allocated memory. In the Message class , nothing is allocated dynamically. Thus using this idiom makes no sense and will make it more complicated to implement due to the pointers that point back.

@pezy In this case, swap function is special. It will be clear two Message's folders , then swap members, and added themselves to each folders. But, Message assignment operator just clear itself, and copy the members, and added itself to each folders. The rhs don't need to clear and add to folders. So, if using copy and swap to define, it will be very inefficiency.


Add a move constructor and move-assignment operator 

#include <string>
#include <set>

#ifndef _MSC_VER
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif

class Folder;

class Message
{
    friend void swap(Message &, Message &);
    friend void swap(Folder &, Folder &);
    friend class Folder;
public:
    explicit Message(const std::string &str = "") : contents(str) { }
    Message(const Message&);
    Message& operator=(const Message&);
    Message(Message &&m); // need to update the Folders
    Message& operator=(Message&&); // need to update the Folders
    ~Message();

    void save(Folder&);
    void remove(Folder&);

    const std::string& msg() const { return contents; }
    void print_debug();

private:
    void add_to_Folders(const Message&);
    void remove_from_Folders();

    void addFldr(Folder *f) { folders.insert(f); }
    void remFldr(Folder *f) { folders.erase(f); }
    
    void move_Folders(Message*); // define function to do the common work

private:
    std::string contents;
    std::set<Folder*> folders;
};

void swap(Message&, Message&);

class Folder {
    friend void swap(Message&, Message&);
    friend void swap(Folder &, Folder &);
    friend class Message;
public:
    explicit Folder(const std::string &str = "") :name(str) { }
    Folder(const Folder &);
    Folder& operator=(const Folder &);
    Folder(Folder &&f); // need to update the Messages
    Folder& operator=(Folder &&); // need to update the Messages
    ~Folder();

    const std::string& fldr() const { return name; }
    void print_debug();

private:
    std::string name;
    std::set<Message*> msgs;

    void add_to_Message(const Folder&);
    void remove_from_Message();

    void addMsg(Message *m) { msgs.insert(m); }
    void remMsg(Message *m) { msgs.erase(m); }
    
    void move_Messages(Folder*); // define function to do the common work
};

void swap(Folder &, Folder &);

#endif


#include <iostream>

void swap(Message &lhs, Message &rhs)
{
    using std::swap;
    for (auto f : lhs.folders)
        f->remMsg(&lhs);
    std::cout << "Remove message from folders" << std::endl; // debug

    for (auto f : rhs.folders)
        f->remMsg(&rhs);
    std::cout << "Remove message from folders" << std::endl; // debug

    swap(lhs.folders, rhs.folders);
    swap(lhs.contents, rhs.contents);

    std::cout << "Message members swaped" << std::endl; // debug

    for (auto f : lhs.folders)
        f->addMsg(&lhs);
    std::cout << "Added message to folders" << std::endl; // debug

    for (auto f : rhs.folders)
        f->addMsg(&rhs);
    std::cout << "Added message to folders" << std::endl; // debug
}

Message::Message(const Message &m) : contents(m.contents), folders(m.folders)
{
    add_to_Folders(m);
}

Message::~Message()
{
    remove_from_Folders();
}

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);
    std::cout << "Added message to folders" << std::endl; // debug
}

void Message::remove_from_Folders()
{
    for (auto f : folders)
        f->remMsg(this);
    std::cout << "Remove message from folders" << std::endl; // debug
}

Message& Message::operator=(const Message &rhs)
{
    remove_from_Folders();
    contents = rhs.contents;
    folders = rhs.folders;
    std::cout << "Message members assgined" << std::endl; // debug
    add_to_Folders(rhs);
    return *this;
}

void Message::print_debug()
{
    std::cout << contents << ": ";
    for (auto f : folders)
        std::cout << f->fldr() << " ";
    std::cout << std::endl;
}

void Message::move_Folders(Message *m)
{
	folders = std::move(m->folders);
	for (auto f : folders)
	{
		f->remMsg(m);
		f->addMsg(this);
	}
	m->folders.clear();
}

Message::Message(Message &&m) : contents(std::move(m.contents))
{
    move_Folders(&m);
}

Message& Message::operator= (Message &&rhs)
{
    if (this != &rhs)
	{
		remove_from_Folders();
		contents = std::move(rhs.contents);
		move_Folders(&rhs);
	}
	std::cout << "Message members moved" << std::endl; // debug
	return *this;
}

// Folder Implementation

void swap(Folder &lhs, Folder &rhs)
{
    using std::swap;
    for (auto m : lhs.msgs)
        m->remFldr(&lhs);
    std::cout << "clear folder" << std::endl; // debug

    for (auto m : rhs.msgs)
        m->remFldr(&rhs);
    std::cout << "clear folder" << std::endl; // debug

    swap(lhs.name, rhs.name);
    swap(lhs.msgs, rhs.msgs);
    std::cout << "Folder members swaped" << std::endl; // debug

    for (auto m : lhs.msgs)
        m->addFldr(&lhs);
    std::cout << "Added messages to folder" << std::endl; // debug

    for (auto m : rhs.msgs)
        m->addFldr(&rhs);
    std::cout << "Added messages to folder" << std::endl; // debug
}

void Folder::add_to_Message(const Folder &f)
{
    for (auto m : f.msgs)
        m->addFldr(this);
    std::cout << "Added messages to folder" << std::endl; // debug
}

Folder::Folder(const Folder &f) : name(f.name), msgs(f.msgs)
{
    add_to_Message(f);
}

void Folder::remove_from_Message()
{
    for (auto m : msgs)
        m->remFldr(this);
    std::cout << "clear folder" << std::endl; // debug
}

Folder::~Folder()
{
    remove_from_Message();
}

Folder& Folder::operator =(const Folder &rhs)
{
    remove_from_Message();
    name = rhs.name;
    msgs = rhs.msgs;
    std::cout << "Folder members assigned" << std::endl; // debug
    add_to_Message(rhs);
    return *this;
}

void Folder::print_debug()
{
    std::cout << name << ": ";
    for (auto m : msgs)
        std::cout << m->msg() << " ";
    std::cout << std::endl;
}

void Folder::move_Messages(Folder *f)
{
	msgs = std::move(f->msgs);
	for (auto m : msgs)
	{
		m->remFldr(f);
		m->addFldr(this);
	}
	f->msgs.clear();
}

Folder::Folder(Folder&& f) : name(std::move(f.name))
{
	move_Messages(&f);
}

Folder& Folder::operator=(Folder &&rhs)
{
    if (this != &rhs)
	{
		remove_from_Message();
		name = std::move(rhs.name);
		move_Messages(&rhs);
	}
	std::cout << "Message members moved" << std::endl; // debug
	return *this;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值