مشروع OOP متكامل — OOP في C++
مشروع: نظام إدارة مكتبة
حان وقت جمع كل ما تعلمته في برنامج واحد. اقرأ الكود الكامل في قسم المثال، ثم ارجع إلى هذا الشرح لتفهم قرارات التصميم.
المتطلبات
- إدارة الكتب والأعضاء.
- استعارة الكتب وإرجاعها مع منع استعارة كتاب مُعار.
- البحث والتقارير.
خريطة الأصناف
Person (مجرد) Book
├── Member (العنوان، المؤلف، متاح؟)
└── Librarian
Library: تملك الكتب والأعضاء (vector<unique_ptr<...>>)قرارات التصميم ولماذا
| القرار | الدرس | السبب |
|---|---|---|
| Person بدالة افتراضية خالصة | التجريد | لا يوجد "شخص عام" في المكتبة؛ كل شخص إما عضو أو موظف |
| مدمّر Person افتراضي | تعدد الأشكال | الأشخاص يُحذفون عبر مؤشر للأب |
| بيانات Book خاصة + دوال borrow/returnBook | التغليف | القاعدة "لا يُعار كتاب مُعار" محمية داخل Book |
| Library تملك الكائنات بـ unique_ptr | إدارة الموارد / Rule of Zero | لا delete يدوي ولا تسرب، ولا حاجة لمدمّر مكتوب |
| Member يخزن معرفات/مؤشرات غير مالكة للكتب | الملكية | المكتبة هي المالك الوحيد؛ العضو "يشير" فقط |
خطوات العمل التي ننصح بها في مشاريعك
1. اكتب قائمة الأسماء في المتطلبات: الأسماء المرشحة لتكون أصنافاً (كتاب، عضو، مكتبة).
2. اكتب الأفعال: دوال مرشحة (استعارة، إرجاع، بحث).
3. حدد من يملك من: الملكية الواضحة تمنع معظم أخطاء الذاكرة.
4. حدد القواعد لكل صنف واحمها بالتغليف.
5. ابنِ خطوة خطوة وشغّل بعد كل خطوة.
مراجعة ذاتية
قبل التحسينات، أجب دون النظر للكود:
- لماذا لا يمكن كتابة Person p("x");؟
- ماذا سيحدث لو كان مدمّر Person غير افتراضي؟
- لماذا لا يحتاج Library مُنشئ نسخ مكتوباً؟ وهل يمكن نسخ Library أصلاً؟ (تلميح: unique_ptr غير قابل للنسخ)
تحسينات مقترحة (تمارين)
1. حد الاستعارة: العضو يستعير 3 كتب كحد أقصى؛ احمِ القاعدة داخل Member.
2. تواريخ الإرجاع: أضف عدد أيام الاستعارة واحسب الغرامة عند التأخير.
3. أنواع مواد: اجعل Item صنفاً مجرداً يرث منه Book و Magazine (المجلات لا تُعار) — وطبّق تعدد الأشكال في canBorrow().
4. عامل الطباعة: أضف operator<< لـ Book و Member.
5. البحث: ابحث بجزء من العنوان مع تجاهل حالة الأحرف.
بعد هذا المشروع أنت جاهز لدورة C++ المتقدم: القوالب و STL والنقل والاستثناءات والتزامن.
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
using namespace std;
// ===== Base Classes =====
class Person {
protected:
int id;
string name;
string phone;
static int nextId;
public:
Person(const string& n, const string& p)
: id(nextId++), name(n), phone(p) {}
virtual void display() const = 0;
virtual ~Person() = default;
int getId() const { return id; }
string getName() const { return name; }
};
int Person::nextId = 1;
// ===== Book Class =====
class Book {
private:
string isbn;
string title;
string author;
bool available;
int borrowerId;
public:
Book(const string& i, const string& t, const string& a)
: isbn(i), title(t), author(a), available(true), borrowerId(0) {}
// Getters
string getISBN() const { return isbn; }
string getTitle() const { return title; }
string getAuthor() const { return author; }
bool isAvailable() const { return available; }
int getBorrowerId() const { return borrowerId; }
// Operations
void borrow(int memberId) {
available = false;
borrowerId = memberId;
}
void returnBook() {
available = true;
borrowerId = 0;
}
// Operator overloading
friend ostream& operator<<(ostream& os, const Book& b) {
os << "📖 " << b.title << " - " << b.author;
os << " [" << (b.available ? "متاح ✅" : "مُعار ❌") << "]";
return os;
}
};
// ===== Member Classes =====
class Member : public Person {
private:
vector<string> borrowedBooks; // ISBNs
static const int MAX_BOOKS = 3;
public:
Member(const string& n, const string& p) : Person(n, p) {}
bool canBorrow() const {
return borrowedBooks.size() < MAX_BOOKS;
}
void addBook(const string& isbn) {
borrowedBooks.push_back(isbn);
}
void removeBook(const string& isbn) {
auto it = find(borrowedBooks.begin(), borrowedBooks.end(), isbn);
if (it != borrowedBooks.end()) {
borrowedBooks.erase(it);
}
}
int getBorrowedCount() const { return borrowedBooks.size(); }
void display() const override {
cout << "👤 عضو #" << id << ": " << name << endl;
cout << " الهاتف: " << phone << endl;
cout << " الكتب المستعارة: " << borrowedBooks.size() << "/" << MAX_BOOKS << endl;
}
};
class Librarian : public Person {
private:
string employeeId;
public:
Librarian(const string& n, const string& p, const string& empId)
: Person(n, p), employeeId(empId) {}
void display() const override {
cout << "📚 أمين مكتبة #" << id << ": " << name << endl;
cout << " رقم الموظف: " << employeeId << endl;
}
};
// ===== Library System =====
class Library {
private:
string name;
vector<unique_ptr<Book>> books;
vector<unique_ptr<Member>> members;
vector<unique_ptr<Librarian>> staff;
public:
Library(const string& n) : name(n) {}
// إضافة كتاب
void addBook(const string& isbn, const string& title, const string& author) {
books.push_back(make_unique<Book>(isbn, title, author));
cout << "✅ تمت إضافة: " << title << endl;
}
// إضافة عضو
int addMember(const string& name, const string& phone) {
auto member = make_unique<Member>(name, phone);
int id = member->getId();
members.push_back(move(member));
cout << "✅ تسجيل عضو جديد: " << name << " (ID: " << id << ")" << endl;
return id;
}
// البحث عن كتاب
Book* findBook(const string& isbn) {
for (auto& book : books) {
if (book->getISBN() == isbn) {
return book.get();
}
}
return nullptr;
}
// البحث عن عضو
Member* findMember(int id) {
for (auto& member : members) {
if (member->getId() == id) {
return member.get();
}
}
return nullptr;
}
// استعارة كتاب
bool borrowBook(int memberId, const string& isbn) {
Member* member = findMember(memberId);
Book* book = findBook(isbn);
if (!member) {
cout << "❌ العضو غير موجود" << endl;
return false;
}
if (!book) {
cout << "❌ الكتاب غير موجود" << endl;
return false;
}
if (!book->isAvailable()) {
cout << "❌ الكتاب غير متاح" << endl;
return false;
}
if (!member->canBorrow()) {
cout << "❌ وصلت للحد الأقصى من الاستعارة" << endl;
return false;
}
book->borrow(memberId);
member->addBook(isbn);
cout << "✅ تم استعارة: " << book->getTitle() << endl;
return true;
}
// إرجاع كتاب
bool returnBook(int memberId, const string& isbn) {
Member* member = findMember(memberId);
Book* book = findBook(isbn);
if (!member || !book) {
cout << "❌ بيانات غير صحيحة" << endl;
return false;
}
book->returnBook();
member->removeBook(isbn);
cout << "✅ تم الإرجاع: " << book->getTitle() << endl;
return true;
}
// عرض جميع الكتب
void displayBooks() const {
cout << "\n📚 كتب " << name << endl;
cout << string(40, '=') << endl;
for (const auto& book : books) {
cout << *book << endl;
}
}
// عرض الأعضاء
void displayMembers() const {
cout << "\n👥 أعضاء المكتبة" << endl;
cout << string(40, '=') << endl;
for (const auto& member : members) {
member->display();
cout << endl;
}
}
// إحصائيات
void showStats() const {
int available = count_if(books.begin(), books.end(),
[](const auto& b) { return b->isAvailable(); });
cout << "\n📊 إحصائيات" << endl;
cout << "إجمالي الكتب: " << books.size() << endl;
cout << "الكتب المتاحة: " << available << endl;
cout << "الكتب المُعارة: " << (books.size() - available) << endl;
cout << "عدد الأعضاء: " << members.size() << endl;
}
};
int main() {
Library library("مكتبة المعرفة");
// إضافة كتب
cout << "=== إضافة كتب ===" << endl;
library.addBook("978-1", "تعلم C++", "أحمد محمد");
library.addBook("978-2", "هياكل البيانات", "سارة علي");
library.addBook("978-3", "الخوارزميات", "محمد خالد");
// إضافة أعضاء
cout << "\n=== تسجيل أعضاء ===" << endl;
int member1 = library.addMember("علي", "0501234567");
int member2 = library.addMember("فاطمة", "0509876543");
// عرض الكتب
library.displayBooks();
// استعارة
cout << "\n=== الاستعارة ===" << endl;
library.borrowBook(member1, "978-1");
library.borrowBook(member1, "978-2");
library.borrowBook(member2, "978-3");
// عرض بعد الاستعارة
library.displayBooks();
library.displayMembers();
// إرجاع
cout << "=== الإرجاع ===" << endl;
library.returnBook(member1, "978-1");
// الإحصائيات
library.showStats();
return 0;
}اختبر فهمك