Cutting my classes to the bare-minimum for readability:
#ifndef MESSAGEFOLDER
#define MESSAGEFOLDER
#include <string>
#include <set>
class Message;
class Folder{
public:
void addMsg(Message* m) { messages.insert(m); }
~Folder() { removeFromMessages(); }
private:
std::set<Message*> messages;
void removeFromMessages(); //removes its pointers from Messages
};
class Message{
friend class Folder;
public:
Message(const std::string &s = ""): contents(s) { }
Message(const Message& rhs): contents(rhs.contents), folders(rhs.folders) { addToFolders(); }
Message& save(Folder&);
~Message() { removeFromFolders(); }
private:
std::string contents;
std::set<Folder*> folders;
void addToFolders();
void removeFromFolders(); //removes its pointers from Folders
};
#endif // MESSAGEFOLDER
In MessageFolder.cpp
void Message::addToFolders(){
for(const auto& f : folders)
f->addMsg(this);
}
Message& Message::save(Folder& f){
folders.insert(&f);
f.addMsg(this);
return *this;
}
This code could cause some "problems" (albeit everything runs fine) when defining const
versions of messages
. The constructors for Message
do not assume const
of the this
pointer. So even though addToFolders
is a non-const
function the code
Message a("hello");
Folder f;
a.save(f);
const Message b(a);
would compile fine. There is an issue here because b
is a const
message but the copy-constructor sets b
's address (through addToFolders()
) to a Folder consisting of a set
of Message*
- the low-level const
is lost. Indeed if I were to then define a function in Folder
which changed the underlying messages I could change the contents
of the const Message b
seemingly with no compile error.
A solution would be to change the Folder
's set to set<const message*>
but this disallows me to change messages through the folders (which I do actually desire). How would I prevent const objects of message
being created or, even better, force that the this
pointer in the constructors is const
so that addToFolders()
would fail?
You can't prevent const
instances being constructed.
If you use this
in your constructor it is up to you to be suitably careful - you can explicitly const_cast
it to a const *
, for example.
Generally it works better if the management of the object is not handled within the object's own class. For example, you could restrict things so that the object can only be created via a folder
, and the folder
can then ensure it is handled correctly. (This is really just an example of separation of concerns.)