In the previous unit, we covered structs and discussed how they are great for bundling multiple member variables into a single object that can be initialized and passed around as a unit. In other words, structs provide a convenient package for storing package and moving related data values.
Consider the following struct:
#include <iostream>
struct Date
{
int day{};
int month{};
int year{};
};
void printDate(const Date& date)
{
std::cout << date.day << '/' << date.month << '/' << date.year; // assume DMY format
}
int main()
{
Date date{ 19, 11, 23 }; // initialize using aggregate initialization
printDate(date); // can pass entire struct to function
return 0;
}In the above example, we create a Date object and then pass it to a function that prints the date. This program prints:
19/11/23The class invariant problem
Perhaps the biggest difficulty with structs is that they do not provide an effective way to document and enforce class invariants. we defined an invariant as , “a condition that must be true while some component is executing”.
Introduction to Classes
Just like structs, a class is a program-defined compound type that can have many member variables with different types.
Defining a class
Because a class is a program-defined data type, it must be defined before it can be used. Classes are defined similarly to structs, excepts we use the class keyword instead of struct. For example, here is a definition for a simple employee class:
class Employee
{
int m_id {};
int m_age {};
double m_wage {};
};Inner Classes
When building complex software systems, grouping related functionality logically is crucial for maintainability, readability, and scalability. One such organizational feature provided by C++ is the inner class, also known as a nested class.
What is an Inner Class in C++?
An inner class in C++ is a class defined inside the scope of another class. It's a way to logically associate one class with another, helping encapsulate behavior and reduce naming clutter.
class Outer {
public:
class Inner {
public:
void display() {
std::cout << "Inside Inner class" << std::endl;
}
};
void show() {
std::cout << "Inside Outer class" << std::endl;
}
};
How to Use an Inner Class
To use an inner class, you must refer it with the scope resolution operator ::.
int main() {
Outer::Inner obj; // Scope resolution
obj.display();
Outer o;
o.show();
return 0;
}
Characteristics of Inner Classes
| Feature | Behavior |
|---|---|
| 🔒 Access to Outer Class Members | ❌ Inner class cannot directly access outer class's non-static members. |
| 🧭 Scope | Inner class is scoped within the outer class. |
| 🔐 Access Modifiers | Inner class can be public, private, or protected. |
| 📛 Name Resolution | Must use Outer::Inner to refer to the class. |
| 👁 Friendship | Inner class can be declared as a friend to access private members of the outer class. |
Access Limitation: No Implicit Link to Outer Class
Inner classes don't automatically access the outer class's members:
class Outer {
int secret = 42;
public:
class Inner {
public:
void accessOuter() {
// std::cout << secret; ❌ Compiler error
}
};
};
To access secret, the inner class must use an object or pointer of the outer class.
class Outer {
int secret = 42;
public:
class Inner {
public:
void accessOuter(const Outer& outer) {
std::cout << "Secret: " << outer.secret << std::endl;
}
};
};
You can define and instantiate inner classes like this:
Outer::Inner innerObj;
innerObj.accessOuter(outerInstance);
Use Cases
1️⃣ Tree Data Structures
#include <iostream>
using namespace std;
class BinaryTree {
private:
// Inner class representing a node of the tree
class Node {
public:
int data;
Node* left;
Node* right;
Node(int value) : data(value), left(nullptr), right(nullptr) {}
};
Node* root;
// Helper function for recursive traversal
void inorderTraversal(Node* node) {
if (!node) return;
inorderTraversal(node->left);
cout << node->data << " ";
inorderTraversal(node->right);
}
public:
BinaryTree() : root(nullptr) {}
void insert(int value) {
root = insertRec(root, value);
}
void printInorder() {
inorderTraversal(root);
cout << endl;
}
private:
// Recursive helper for insertion
Node* insertRec(Node* node, int value) {
if (!node) return new Node(value);
if (value < node->data)
node->left = insertRec(node->left, value);
else
node->right = insertRec(node->right, value);
return node;
}
};
// Usage
int main() {
BinaryTree tree;
tree.insert(10);
tree.insert(5);
tree.insert(15);
cout << "Inorder Traversal: ";
tree.printInorder(); // Output: 5 10 15
return 0;
}
2️⃣ Linked List with Node as an Inner Class
#include <iostream>
using namespace std;
class LinkedList {
private:
// Inner class representing a node of the linked list
class Node {
public:
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};
Node* head;
public:
LinkedList() : head(nullptr) {}
void insertAtEnd(int value) {
Node* newNode = new Node(value);
if (!head) {
head = newNode;
return;
}
Node* temp = head;
while (temp->next) {
temp = temp->next;
}
temp->next = newNode;
}
void printList() const {
Node* temp = head;
while (temp) {
cout << temp->data << " -> ";
temp = temp->next;
}
cout << "NULL" << endl;
}
};
// Usage
int main() {
LinkedList list;
list.insertAtEnd(10);
list.insertAtEnd(20);
list.insertAtEnd(30);
cout << "Linked List: ";
list.printList(); // Output: 10 -> 20 -> 30 -> NULL
return 0;
}
3️⃣ Helper/Utility Classes
Sometimes you want a small class used only internally within another class. Inner classes make that possible without polluting the global namespace.
Static Nested Class
Anonymous Inner Class
Leave a comment
Your email address will not be published. Required fields are marked *


