Nested و Local Classes — OOP في C++
الأصناف المتداخلة والمحلية
الصنف المتداخل (Nested Class)
صنف معرّف داخل صنف آخر. الاستخدام الأهم: إخفاء تفاصيل التنفيذ. عقدة القائمة المرتبطة لا معنى لها خارج القائمة:
#include <iostream>
#include <memory>
using namespace std;
class IntList {
struct Node { // private: لا أحد خارج IntList يراه
int value;
unique_ptr<Node> next;
explicit Node(int v) : value(v) {}
};
unique_ptr<Node> head;
size_t count = 0;
public:
void pushFront(int v) {
auto n = make_unique<Node>(v);
n->next = move(head);
head = move(n);
++count;
}
void print() const {
for (const Node* p = head.get(); p; p = p->next.get()) cout << p->value << ' ';
cout << '\n';
}
size_t size() const { return count; }
};
int main() {
IntList list;
list.pushFront(3); list.pushFront(2); list.pushFront(1);
list.print(); // 1 2 3
// IntList::Node n(5); ← خطأ: Node خاص
}قواعد الوصول
- الصنف المتداخل عضو في الصنف الخارجي، فيصل إلى أعضائه الخاصة (منذ C++11) — لكنه يحتاج كائناً منه، لأنه لا يملك this خاصاً بالخارجي.
- الصنف الخارجي لا يصل إلى الأعضاء الخاصة للمتداخل (لذلك جعلنا Node بنية struct بأعضاء public؛ فهي مخفية أصلاً لأنها private داخل IntList).
- إذا كان المتداخل public، يُستخدم من الخارج بالاسم الكامل: Outer::Inner.
أنواع متداخلة عامة
مكتبة STL تستخدم هذا كثيراً: vector<int>::iterator، map<K,V>::value_type. يمكنك فعل المثل:
class Order {
public:
enum class Status { Pending, Paid, Shipped }; // نوع متداخل عام
void setStatus(Status s) { status = s; }
Status getStatus() const { return status; }
private:
Status status = Status::Pending;
};
// Order o; o.setStatus(Order::Status::Paid);الصنف المحلي (Local Class)
صنف معرّف داخل دالة، مرئي فيها فقط. قيوده: لا أعضاء static بيانات، ولا يمكن أن يكون قالباً. كان يُستخدم لكائنات دالة صغيرة، أما اليوم فالـ lambda تؤدي الغرض بشكل أوضح:
#include <algorithm>
#include <vector>
using namespace std;
void sortDesc(vector<int>& v) {
struct Greater { bool operator()(int a, int b) const { return a > b; } }; // صنف محلي (أسلوب قديم)
sort(v.begin(), v.end(), Greater{});
sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // نفس الشيء بـ lambda
}متى تستخدم ماذا؟
| الحالة | الأداة |
|---|---|
| تفصيل تنفيذ يخص صنفاً واحداً (Node، Iterator) | صنف متداخل private |
| نوع مرتبط بالصنف يحتاجه المستخدم (Status) | نوع متداخل public |
| منطق صغير داخل دالة | lambda |
تمرين
أضف إلى IntList صنفاً متداخلاً عاماً Stats (min, max, sum) ودالة Stats stats() const تحسبها في مرور واحد.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class LinkedList {
private:
// Nested class - Node
class Node {
public:
int data;
Node* next;
Node(int d) : data(d), next(nullptr) {}
};
Node* head;
int size;
public:
// Public nested class - Iterator
class Iterator {
private:
Node* current;
public:
Iterator(Node* node) : current(node) {}
int& operator*() { return current->data; }
Iterator& operator++() {
if (current) current = current->next;
return *this;
}
bool operator!=(const Iterator& other) const {
return current != other.current;
}
};
LinkedList() : head(nullptr), size(0) {}
~LinkedList() {
while (head) {
Node* temp = head;
head = head->next;
delete temp;
}
}
void push_front(int data) {
Node* newNode = new Node(data);
newNode->next = head;
head = newNode;
size++;
}
void push_back(int data) {
Node* newNode = new Node(data);
if (!head) {
head = newNode;
} else {
Node* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
size++;
}
int getSize() const { return size; }
Iterator begin() { return Iterator(head); }
Iterator end() { return Iterator(nullptr); }
};
// مثال آخر: Menu System
class Restaurant {
public:
// Public nested class
class MenuItem {
private:
string name;
double price;
string category;
public:
MenuItem(const string& n, double p, const string& c)
: name(n), price(p), category(c) {}
string getName() const { return name; }
double getPrice() const { return price; }
string getCategory() const { return category; }
void display() const {
cout << " • " << name << " - $" << price << endl;
}
};
private:
string name;
vector<MenuItem> menu;
public:
Restaurant(const string& n) : name(n) {}
void addItem(const string& itemName, double price, const string& category) {
menu.push_back(MenuItem(itemName, price, category));
}
void displayMenu() const {
cout << "\n🍽️ قائمة " << name << endl;
cout << string(30, '=') << endl;
string currentCategory = "";
for (const auto& item : menu) {
if (item.getCategory() != currentCategory) {
currentCategory = item.getCategory();
cout << "\n" << currentCategory << ":" << endl;
}
item.display();
}
}
double calculateTotal(const vector<int>& itemIndexes) const {
double total = 0;
for (int idx : itemIndexes) {
if (idx >= 0 && idx < menu.size()) {
total += menu[idx].getPrice();
}
}
return total;
}
};
int main() {
// استخدام LinkedList مع Iterator
cout << "=== LinkedList ===" << endl;
LinkedList list;
list.push_back(10);
list.push_back(20);
list.push_back(30);
list.push_front(5);
cout << "العناصر: ";
for (LinkedList::Iterator it = list.begin(); it != list.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// Range-based for (يعمل مع begin/end)
cout << "باستخدام range-for: ";
for (int x : list) {
cout << x << " ";
}
cout << endl;
// استخدام Restaurant
cout << "\n=== Restaurant ===" << endl;
Restaurant restaurant("البيت العربي");
restaurant.addItem("حمص", 5.99, "المقبلات");
restaurant.addItem("فتوش", 6.99, "المقبلات");
restaurant.addItem("شاورما", 12.99, "الأطباق الرئيسية");
restaurant.addItem("كباب", 15.99, "الأطباق الرئيسية");
restaurant.addItem("كنافة", 7.99, "الحلويات");
restaurant.displayMenu();
// إنشاء MenuItem من الخارج
Restaurant::MenuItem customItem("طبق خاص", 20.00, "خاص");
cout << "\nطبق مخصص: " << customItem.getName() << endl;
return 0;
}اختبر فهمك