← Back to list

Understanding the MemBot Project — Ownership, Move Semantics, and Graph Design

Udacity C++ Memory Management — Project Walkthrough (Part2)

Seulgie Han · 2026-01-16 22:59 · 2 claps · 2.8 min read
Open on Medium ↗
Wiki topics: BIZ · Business Strategy LNG · Linguistics & Language

Understanding the MemBot Project — Ownership, Move Semantics, and Graph Design

Udacity C++ Memory Management — Project Walkthrough (Part2)

In Part 1, we focused on what the project looks like. In Part 2, we focus on why it is designed this way. If you try to “just fill in the TODOs”, you will almost certainly:

  • break ownership rules
  • leak memory
  • or accidentally copy objects that must be moved

Let’s state the most important rule upfront:

At any point in time, every object must have exactly one clear owner.

This applies to:

  • GraphNodes
  • GraphEdges
  • ChatBot

Everything else uses non-owning raw pointers. Before touching code, we must clearly separate Ownership vs Access. Ownership means responsible for deleting the object, whereas Access means the availability to use the object / must NOT delete it. In this project:

  • GraphNode is owned by ChatLogic
  • GraphEdge is owned by GraphNode (parent side)
  • ChatBot is owned by Moves between GraphNodes

Everyone else only borrows pointers.

Understanding ChatBot

Understanding ChatBot ownership is crucial in this project. Ownership Structure:

  • The root GraphNode owns the ChatBot — Root Node creates and manages the ChatBot object
  • ChatLogic does NOT own the ChatBot — it only holds a pointer (_chatBot) to interact with it. Just has a pointer; does not delete or manage
  • Other GraphNodes only reference the ChatBot temporarily as it moves along the graph.

Why this design?

  1. There should always be only one ChatBot — no two nodes can own it at the same time.
  2. Move semantics allow the ChatBot to safely travel from one side to another.
  3. Clear ownershiproot node manages lifetime, so no double deletion occurs.

If ChatBot were copied instead of moved, two nodes could think they own the chatbot, and both would try to talk and control state. This violates single responsiblity and single ownership. Move semantics guarantee:

After moving, the source object is empty and safe.

How It Works in Code

// Root node creates ChatBot
ChatBot chatBot("../images/chatbot.png"); 
_chatBot->SetRootNode(rootNode);

// Moving the ChatBot to a new node
_currentNode->MoveChatbotToNewNode(newNode);
  • MoveChatbotToNewNode uses std::move(_chatbot)
  • Previous node _chatBot becomes nullptr
  • New node temporaily references the ChatBot but does not own it

ChatBot Movement Flow (Step-by-Step)

Let’s trace a typical user interaction.

Step 1: User sends a message

ChatLogic::SendMessageToChatBot("What is a pointer?");

Step 2: ChatBot processes input

ChatBot::ReceiveMessageFromUser(...)

Step 3: Current node transfers ChatBot

_currentNode->MoveChatbotToNewNode(newNode);

Step 4: Ownership transfer happens

newNode->moveChatbotHere(std::move(_chatBot));

std::move does NOT move by itself. It enables the move constructor.

Why Rule of Five Is Tested Here

ChatBot implements:

ChatBot(ChatBot &&source);
ChatBot &operator=(ChatBot &&source);

These functions transfer internal pointers, reset the source object, and avoid double references. The printed log messages exist so you can verify moves happen.

The ChatBot Does Not Own ChatLogic

This is subtle but crucial.

_chatLogic->SetChatbotHandle(this);

This creates a two-way connection. ChatBot knows ChatLogic and ChatLogic knows ChatBot. But neither owns the other. Why is this safe? Because both objects live for the duration of the program, and lifetime is controlled externally (main).

The main_cli.cpp is simple. As the CLI entry point, it only creates ChatLogic, load graph, and start loop. This is intentional. Memory complexity belongs inside the graph, not in main.

In GraphNode:

std::vector<GraphEdge*> _parentEdges;

This vector does NOT delete edges, only exists to answer questions like “How many parents does this node have?” — If child nodes owned incoming edges, that will lead to double deletion.

Key Takeaways

  • Owner: Root Node
  • References: ChatLogic and all other GraphNodes
  • Behavior: Only one ChatBot exists at a time, moving safely along the graph

Avoid to:

  • use shared_ptr everywhere
  • Copy ChatBot instead of moving
  • Let both nodes own the same edge
  • Delete objects manaually “just in case”

Before touching any TODO, answer these questions:

  1. Who owns this object?
  2. Who deletes it?
  3. Can it be copied safely?
  4. Should it be moved instead?
  5. Is this pointer owning or non-owning?

Final Ownership Diagram (Target Design)

ChatLogic
 ├── owns → GraphNode
 │           ├── owns → GraphEdge
 │           │          └── points to → GraphNode
 │           └── non-owning ← GraphEdge
 └── references → ChatBot

ChatBot
 ├── currentNode (non-owning)
 ├── rootNode (non-owning)
 └── chatLogic (non-owning)

Single ownership everywhere. Zero ambiguity.

In the next post, we will finally:

  • Fill in all TODOs
  • Convert ownership to unique_ptr where required
  • Implement safe move logic
  • Validate against the project rubric

At that point, this project stops being “confusing” and becomes mechanically logical.


메타데이터
post_id
8dcc259c657d
slug
understanding-the-membot-project-ownership-move-semantics-and-graph-design-8dcc259c657d
url
https://medium.com/@su-paris/understanding-the-membot-project-ownership-move-semantics-and-graph-design-8dcc259c657d
canonical_url
https://medium.com/@su-paris/understanding-the-membot-project-ownership-move-semantics-and-graph-design-8dcc259c657d
author_url
https://medium.com/@su-paris
status
ok
fetched_at
2026-07-13 08:54:12