ChajusSaib
ChajusSaib

Reputation: 167

Message assign operator not using swap

sorry if the title was a little off, didn't know what to use.

So in this book(C++ Primer 5th ed) I'm learning C++ from I got an question:

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

I really have no idea as both result in the same thing even when assigning the same object t itself.

main.cpp for both files:

#include <iostream>
#include "Folder.hpp"
#include "Message.hpp"

int main()
{
    Message m("Hello");
    Folder f1, f3;

    m.save(f1);

    Message m2;

    m2 = m;

    m2.save(f3);
    m2.save(f1);

    std::cout << f3.msgs.size() << std::endl;
    std::cout << f1.msgs.size() << std::endl;
    return 0;
}

swap function:

void swap(Message &lhs, Message &rhs)
{
    using std::swap;

    for (auto f : lhs.folders)
        f->remMsg(&lhs);
    for (auto f : rhs.folders)
        f->remMsg(&rhs);

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

    for (auto f : lhs.folders)
        f->addMsg(&lhs);
    for (auto f : rhs.folders)
        f->addMsg(&rhs);
}

Without using swap

operator=:

Message& Message::operator=(const Message &rhs)
{
    remove_from_Folders();
    contents = rhs.contents;
    folders = rhs.folders;
    add_to_Folders(rhs);
    return *this;
}

Changes by using swap:

Message.hpp Message& operator=(Message); // new declaration to match def

Message.cpp

Message& Message::operator=(Message rhs)
{
    swap(*this, rhs);
    return *this;
}

Output is

2
1

as expected for both versions.

I really have no idea what the upside of not using swap would be other then a little less work to be done.

Classes:

// Message.hpp

#ifndef MESSAGE_HPP
#define MESSAGE_HPP

#include <string>
#include <set>

class Folder;

class Message
{
        friend class Folder;
        friend void swap(Message&, Message&);
        friend std::ostream& print(std::ostream &os, const Message &m);
public:
        explicit Message(const std::string &str = "") : contents(str) { }

        ~Message();

        Message(const Message&);
        Message& operator=(const Message&);

        void save(Folder&);
        void remove(Folder&);
private:
        std::string contents;
        std::set<Folder*> folders;

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

std::ostream& print(std::ostream &, const Message &);

#endif






// Message.cpp

#include "Message.hpp"
#include "Folder.hpp"

void swap(Message &lhs, Message &rhs)
{
        using std::swap;

        for (auto f : lhs.folders)
                f->remMsg(&lhs);
        for (auto f : rhs.folders)
                f->remMsg(&rhs);

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

        for (auto f : lhs.folders)
                f->addMsg(&lhs);
        for (auto f : rhs.folders)
                f->addMsg(&rhs);
}

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

void Message::remove_from_Folders()
{
        for (auto f : folders)
                f->remMsg(this);
        folders.clear();
}


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

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

Message& Message::operator=(const Message &rhs)
{
        remove_from_Folders();
        contents = rhs.contents;
        folders = rhs.folders;
        add_to_Folders(rhs);
        return *this;
}


std::ostream& print(std::ostream &os, const Message &m)
{
        os << m.contents;
        return os;
}




// Folder.hpp

#ifndef FOLDER_HPP
#define FOLDER_HPP

#include <ostream>
#include <set>

class Message;

class Folder
{
        friend class Message;
        friend std::ostream& print(std::ostream &, Folder &);
public:
        ~Folder();

        void addMsg(Message *);
        void remMsg(Message *);
private:
        std::set<Message*> msgs;
};

std::ostream& print(std::ostream &, Folder &);

#endif




// Folder.cpp

#include "Folder.hpp"
#include "Message.hpp"

Folder::~Folder()
{
        for (auto &m : msgs)
        {
                m->remove(*this);
        }
}

void Folder::addMsg(Message *m)
{
        msgs.insert(m);
}

void Folder::remMsg(Message *m)
{
        msgs.erase(m);
}

std::ostream& print(std::ostream &os, Folder &f)
{
        for (const auto &m : f.msgs)
        {
                print(os, *m) << std::endl;
        }

        return os;
}

Cheers

Upvotes: 1

Views: 206

Answers (1)

ChajusSaib
ChajusSaib

Reputation: 167

So @KeithSmith posted a link to the answers and I got two from there. Here they are:

Exercise 13.38:

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 dynamically 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.

Basically Mooophy is saying that copy and swap is something used for dynamically allocated memory and the Message class has not dynamically allocated memory.

pezy is saying that swap is different that it swap adds both lhs and rhs to each folder and the assignment operator just adds lhs which is more efficient.

Just wanted to say thanks to KeithSmith for letting me know of the link.

Link: https://github.com/Mooophy/Cpp-Primer

Upvotes: 1

Related Questions