Why is my program crashing when using multiple classes and pointers?

  • Context:
  • Thread starter Thread starter JonnyG
  • Start date Start date
  • Tags Tags
    Program
Click For Summary
SUMMARY

The program crashes due to dangling pointers caused by the destruction order of the Message and Folder objects. Specifically, when the Folder object is destroyed before the Message object, the pointers in the Message's folders set become invalid. To resolve this, the Folder destructor was modified to erase the Folder pointer from each Message's folders set, ensuring no dangling pointers remain. This solution was achieved by making Folder a friend of Message, allowing access to its private members.

PREREQUISITES
  • C++ programming language
  • Understanding of pointers and memory management
  • Familiarity with class relationships and destructors
  • Knowledge of the Standard Template Library (STL) set container
NEXT STEPS
  • Implement smart pointers in C++ for better memory management
  • Explore C++ friend classes and their implications on encapsulation
  • Learn about RAII (Resource Acquisition Is Initialization) principles
  • Investigate the use of std::shared_ptr and std::weak_ptr to manage shared ownership
USEFUL FOR

Software developers, particularly those working with C++ and object-oriented programming, who are dealing with complex class relationships and memory management issues.

JonnyG
Messages
233
Reaction score
45
Following my book, we are making a program with two classes: Message and Folder. The idea is that messages will be stored in a folder. The same message can be saved in multiple folders. In order to accomplish this, Message has a set of pointers to Folder that point to each of the folders that contains it. Folder has a set of pointers to Message that point to each of the messages it contains. Here is my code:

[CODE lang="cpp" title="Message.hpp"]#ifndef MESSAGE_HPP
#define MESSAGE_HPP
#include <string>
#include <set>
#include "Folder.hpp"

using std::string;
using std::set;

class Message
{

friend class Folder;
public:
explicit Message(const string &str) : contents(str) {}
Message(const Message&);
Message& operator=(const Message&);
~Message();

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

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

void addToFolders(const Message&);
void removeFromFolders();
void swap(Message&, Message&);
};

#endif
[/CODE]

[CODE title="Message.cpp"]#include "Message.hpp"

Message::Message(const Message &msg) : contents(msg.contents), folders(msg.folders) {
addToFolders(msg);
}

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

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

void Message::save(Folder &f) {
folders.insert(&f);
f.addMsg(this);
}

void Message::remove(Folder &f) {
folders.erase(&f);
f.remMsg(this);
}

void Message::addToFolders(const Message &msg) {
for (auto f : msg.folders)
f->addMsg(this);
}

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

void Message::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);
}[/CODE]

[CODE lang="cpp" title="Folder.hpp"]#ifndef FOLDER_HPP
#define FOLDER_HPP
#include <set>

using std::set;

class Message;
class Folder
{

public:
void addMsg(Message*);
void remMsg(Message*);

private:

set<Message*> containedMessages;
};

#endif
[/CODE]

[CODE lang="cpp" title="Folder.cpp"]#include "Folder.hpp"

void Folder::addMsg(Message *msg) {
containedMessages.insert(msg);
}

void Folder::remMsg(Message *msg) {
containedMessages.erase(msg);
}
[/CODE]

My test code that I run in my main.cpp file is (I've removed include directives and other statements for brevity):

[CODE lang="cpp" title="main.cpp"]int main() {
Message msg("testing");
Folder inbox;
msg.save(inbox);

}[/CODE]

This program crashes. Playing with the program, looking at my debugger, and using a memory checker program, it appears the problem is ultimately coming from my remMsg function in the Folder.cpp file. But I can't figure out exactly what's happening. My memory checker gives errors that say unaddressable memory and invalid heap argument, so I know it has something to do with memory getting deleted and dangling pointers, but I can't seem to fix the program.

Any tips?

Edit: I've suspected that when reaching the end of the main function, my Folder object may be getting deleted before my Message object, which leaves the set of pointers that point to my folder dangling (this set is a data member of my Message object). I would need to have access to the Message class members in my Folder destructor to account this possibility, but I cannot #include my Message header in my Folder header, because the Folder header is already #included in my Message header.
 
Last edited:
Technology news on Phys.org
JonnyG said:
Following my book, we are making a program with two classes: Message and Folder. The idea is that messages will be stored in a folder. The same message can be saved in multiple folders. In order to accomplish this, Message has a set of pointers to Folder that point to each of the folders that contains it. Folder has a set of pointers to Message that point to each of the messages it contains. Here is my code:

[CODE lang="cpp" title="Message.hpp"]#ifndef MESSAGE_HPP
#define MESSAGE_HPP
#include <string>
#include <set>
#include "Folder.hpp"

using std::string;
using std::set;

class Message
{

friend class Folder;
public:
explicit Message(const string &str) : contents(str) {}
Message(const Message&);
Message& operator=(const Message&);
~Message();

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

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

void addToFolders(const Message&);
void removeFromFolders();
void swap(Message&, Message&);
};

#endif
[/CODE]

[CODE title="Message.cpp"]#include "Message.hpp"

Message::Message(const Message &msg) : contents(msg.contents), folders(msg.folders) {
addToFolders(msg);
}

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

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

void Message::save(Folder &f) {
folders.insert(&f);
f.addMsg(this);
}

void Message::remove(Folder &f) {
folders.erase(&f);
f.remMsg(this);
}

void Message::addToFolders(const Message &msg) {
for (auto f : msg.folders)
f->addMsg(this);
}

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

void Message::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);
}[/CODE]

[CODE lang="cpp" title="Folder.hpp"]#ifndef FOLDER_HPP
#define FOLDER_HPP
#include <set>

using std::set;

class Message;
class Folder
{

public:
void addMsg(Message*);
void remMsg(Message*);

private:

set<Message*> containedMessages;
};

#endif
[/CODE]

[CODE lang="cpp" title="Folder.cpp"]#include "Folder.hpp"

void Folder::addMsg(Message *msg) {
containedMessages.insert(msg);
}

void Folder::remMsg(Message *msg) {
containedMessages.erase(msg);
}
[/CODE]

My test code that I run in my main.cpp file is (I've removed include directives and other statements for brevity):

[CODE lang="cpp" title="main.cpp"]int main() {
Message msg("testing");
Folder inbox;
msg.save(inbox);

}[/CODE]

This program crashes. Playing with the program, looking at my debugger, and using a memory checker program, it appears the problem is ultimately coming from my remMsg function in the Folder.cpp file. But I can't figure out exactly what's happening. My memory checker gives errors that say unaddressable memory and invalid heap argument, so I know it has something to do with memory getting deleted and dangling pointers, but I can't seem to fix the program.

Any tips?
It looks like in removeFromFolders, called from msg's destructor, it dereferences a pointer to inbox (->remMsg). But when the main function exits, inbox is destroyed before msg is, so when msg's destructor is called, the pointer to inbox is an invalid pointer. You can try printing something in their destructors to see what's happening more clearly.
 
Last edited:
  • Like
Likes   Reactions: JonnyG
Ah yes, your suggestion to print statements in the destructors have confirmed my suspicions. Thank you, Jarvis.

If you see my edit in my OP, I suspected this may have been the problem but I couldn't access the Message data members in the Folder class because Folder class already #included the Message class, so I couldn't #include the Message class in the Folder class.

What I did was create one header for both classes and I made Folder a friend of Message. This gave me access to the Message data members and allowed me to write this in the Folder destructor:

C++:
Folder::~Folder() {
    for (auto m : containedMessages)
        m->folders.erase(this);
}

This ensures that there will be no dangling pointers if Folder happens to be deleted first.

Now I am starting to understand what is meant by C++ makes it much harder to shoot yourself in the foot than C does, but when you do it blows off your entire leg.
 
  • Like
Likes   Reactions: Jarvis323
@Jarvis323

One more question if you don't mind. I notice that I have been changing the size of my set container with range-for loops by erasing and inserting objects. I was under the impression that this was a very bad idea because it could cause a big error. Any idea why my program is working perfectly fine?
 
JonnyG said:
@Jarvis323

One more question if you don't mind. I notice that I have been changing the size of my set container with range-for loops by erasing and inserting objects. I was under the impression that this was a very bad idea because it could cause a big error. Any idea why my program is working perfectly fine?
It looks like you're not adding or removing any objects to the container you're looping over, but just to the containers held by those objects. That should be fine.
 
  • Like
Likes   Reactions: JonnyG
Makes sense. Thank you!
 

Similar threads

  • · Replies 3 ·
Replies
3
Views
3K
  • · Replies 23 ·
Replies
23
Views
2K
Replies
4
Views
2K
  • · Replies 75 ·
3
Replies
75
Views
6K
  • · Replies 36 ·
2
Replies
36
Views
3K
  • · Replies 89 ·
3
Replies
89
Views
6K
  • · Replies 16 ·
Replies
16
Views
5K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 13 ·
Replies
13
Views
2K
Replies
3
Views
2K