Kodax

مشروع C++ متكامل — C++ المتقدم

مشروع: نظام إدارة المهام

مشروع يجمع أدوات الدورة في برنامج سطر أوامر متكامل. اقرأ الكود الكامل في قسم المثال، ثم استخدم هذا الشرح كدليل.

المتطلبات

- إضافة المهام وحذفها وتحديث حالتها.

- البحث بكلمة، والعرض حسب الحالة.

- إحصائيات.

- حفظ المهام في ملف وتحميلها عند التشغيل.

أين استُخدم كل درس؟

| الجزء | المفهوم |

|---|---|

| enum class Priority/Status | أنواع آمنة بدل الأرقام السحرية |

| Task و TaskManager | التغليف: المدير وحده يعدّل القائمة |

| vector<unique_ptr<Task>> | الملكية الواضحة و RAII — لا delete يدوي |

| find_if، count_if، sort | خوارزميات STL بدل الحلقات |

| lambdas | معايير البحث والترتيب |

| ofstream / ifstream + stringstream | الحفظ والتحميل وتحليل الأسطر |

الإدخال الآمن في القائمة

حلقة القائمة تتوقف إذا فشلت قراءة الرقم (if (!(cin >> choice)) break;)، وتُرفض الأولوية خارج 0–3 قبل تحويلها بـ static_cast. بدون هذا الفحص، إدخال نص بدل رقم أو أولوية مثل 7 ينتج قيمة enum غير صالحة.

تصميم صيغة الملف

كل مهمة سطر واحد بحقول مفصولة بفاصل. اسأل نفسك: ماذا لو احتوى العنوان على الفاصل نفسه؟ (حلول: اختيار فاصل نادر مثل |، أو "هروب" الفاصل، أو صيغة JSON مع مكتبة).

مراجعة ذاتية

- لماذا unique_ptr وليس shared_ptr؟ (من يملك المهمة؟)

- أي دالة في TaskManager يجب أن تكون const؟ تحقق من الكود.

- ماذا يحدث للمهام في الذاكرة عند الخروج من البرنامج؟ ولماذا لا تحتاج مدمّراً مكتوباً؟

تحسينات مقترحة (تمارين)

1. تواريخ الاستحقاق: أضف تاريخاً لكل مهمة (std::chrono::year_month_day في C++20) واعرض المتأخرة.

2. الاستثناءات: اجعل updateStatus ترمي out_of_range لمعرف غير موجود والتقطها في القائمة.

3. optional: اجعل دالة findTask(int id) تعيد Task* أو optional وناقش الفرق.

4. التصنيفات: map<string, vector<int>> من التصنيف إلى معرفات المهام.

5. حفظ تلقائي في خيط: احفظ كل دقيقة في خيط خلفي مع mutex يحمي القائمة — وفكّر بكل نقطة يمكن أن يحدث فيها سباق.

بإكمال هذه الدورة تكون قد غطيت C++ الحديثة الأساسية. الخطوة التالية: بناء مشروع حقيقي بنظام بناء (CMake)، وكتابة اختبارات (GoogleTest أو Catch2)، والتعرّف على أدوات مثل -fsanitize=address.

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <functional>
using namespace std;

// ===== Enums =====
enum class Priority { LOW, MEDIUM, HIGH, URGENT };
enum class Status { TODO, IN_PROGRESS, DONE, CANCELLED };

string priorityToString(Priority p) {
    switch (p) {
        case Priority::LOW: return "منخفضة";
        case Priority::MEDIUM: return "متوسطة";
        case Priority::HIGH: return "عالية";
        case Priority::URGENT: return "عاجلة";
    }
    return "غير محدد";
}

string statusToString(Status s) {
    switch (s) {
        case Status::TODO: return "للتنفيذ";
        case Status::IN_PROGRESS: return "قيد التنفيذ";
        case Status::DONE: return "مكتمل";
        case Status::CANCELLED: return "ملغي";
    }
    return "غير محدد";
}

// ===== Task Class =====
class Task {
private:
    int id;
    string title;
    string description;
    Priority priority;
    Status status;
    string createdAt;
    static int nextId;

public:
    Task(const string& t, const string& desc, Priority p = Priority::MEDIUM)
        : id(nextId++), title(t), description(desc), 
          priority(p), status(Status::TODO) {
        auto now = chrono::system_clock::now();
        auto time = chrono::system_clock::to_time_t(now);
        createdAt = ctime(&time);
        createdAt.pop_back();  // إزالة \n
    }
    
    // Getters
    int getId() const { return id; }
    string getTitle() const { return title; }
    string getDescription() const { return description; }
    Priority getPriority() const { return priority; }
    Status getStatus() const { return status; }
    
    // Setters
    void setTitle(const string& t) { title = t; }
    void setDescription(const string& d) { description = d; }
    void setPriority(Priority p) { priority = p; }
    void setStatus(Status s) { status = s; }
    
    // Display
    void display() const {
        cout << "┌─────────────────────────────────" << endl;
        cout << "│ #" << id << " " << title << endl;
        cout << "│ " << description << endl;
        cout << "│ الأولوية: " << priorityToString(priority) 
             << " | الحالة: " << statusToString(status) << endl;
        cout << "│ تاريخ الإنشاء: " << createdAt << endl;
        cout << "└─────────────────────────────────" << endl;
    }
    
    // Serialization
    string serialize() const {
        stringstream ss;
        ss << id << "|" << title << "|" << description << "|"
           << static_cast<int>(priority) << "|" << static_cast<int>(status)
           << "|" << createdAt;
        return ss.str();
    }
    
    static unique_ptr<Task> deserialize(const string& line) {
        stringstream ss(line);
        string token;
        
        getline(ss, token, '|');
        int id = stoi(token);
        
        string title, desc, created;
        int pri, stat;
        
        getline(ss, title, '|');
        getline(ss, desc, '|');
        getline(ss, token, '|'); pri = stoi(token);
        getline(ss, token, '|'); stat = stoi(token);
        getline(ss, created, '|');
        
        auto task = make_unique<Task>(title, desc, static_cast<Priority>(pri));
        task->id = id;
        task->status = static_cast<Status>(stat);
        task->createdAt = created;
        
        if (id >= nextId) nextId = id + 1;
        
        return task;
    }
};

int Task::nextId = 1;

// ===== TaskManager Class =====
class TaskManager {
private:
    vector<unique_ptr<Task>> tasks;
    string filename;

public:
    TaskManager(const string& file = "tasks.txt") : filename(file) {
        load();
    }
    
    ~TaskManager() {
        save();
    }
    
    // CRUD Operations
    void addTask(const string& title, const string& desc, Priority p) {
        tasks.push_back(make_unique<Task>(title, desc, p));
        cout << "✅ تمت إضافة المهمة" << endl;
    }
    
    bool removeTask(int id) {
        auto it = find_if(tasks.begin(), tasks.end(),
            [id](const auto& t) { return t->getId() == id; });
        
        if (it != tasks.end()) {
            tasks.erase(it);
            cout << "✅ تم حذف المهمة" << endl;
            return true;
        }
        cout << "❌ المهمة غير موجودة" << endl;
        return false;
    }
    
    Task* findTask(int id) {
        auto it = find_if(tasks.begin(), tasks.end(),
            [id](const auto& t) { return t->getId() == id; });
        return it != tasks.end() ? it->get() : nullptr;
    }
    
    bool updateStatus(int id, Status newStatus) {
        if (Task* task = findTask(id)) {
            task->setStatus(newStatus);
            cout << "✅ تم تحديث الحالة" << endl;
            return true;
        }
        return false;
    }
    
    // Display
    void displayAll() const {
        if (tasks.empty()) {
            cout << "لا توجد مهام" << endl;
            return;
        }
        
        cout << "\n📋 قائمة المهام (" << tasks.size() << ")" << endl;
        for (const auto& task : tasks) {
            task->display();
        }
    }
    
    void displayFiltered(function<bool(const Task&)> filter) const {
        int count = 0;
        for (const auto& task : tasks) {
            if (filter(*task)) {
                task->display();
                count++;
            }
        }
        cout << "عدد النتائج: " << count << endl;
    }
    
    // Filtering
    void showByStatus(Status status) const {
        cout << "\n📋 المهام: " << statusToString(status) << endl;
        displayFiltered([status](const Task& t) {
            return t.getStatus() == status;
        });
    }
    
    void showByPriority(Priority priority) const {
        cout << "\n📋 المهام ذات الأولوية: " << priorityToString(priority) << endl;
        displayFiltered([priority](const Task& t) {
            return t.getPriority() == priority;
        });
    }
    
    void search(const string& keyword) const {
        cout << "\n🔍 نتائج البحث عن: " << keyword << endl;
        displayFiltered([&keyword](const Task& t) {
            return t.getTitle().find(keyword) != string::npos ||
                   t.getDescription().find(keyword) != string::npos;
        });
    }
    
    // Statistics
    void showStats() const {
        cout << "\n📊 الإحصائيات" << endl;
        cout << string(30, '=') << endl;
        
        int todo = 0, inProgress = 0, done = 0, cancelled = 0;
        int low = 0, medium = 0, high = 0, urgent = 0;
        
        for (const auto& task : tasks) {
            switch (task->getStatus()) {
                case Status::TODO: todo++; break;
                case Status::IN_PROGRESS: inProgress++; break;
                case Status::DONE: done++; break;
                case Status::CANCELLED: cancelled++; break;
            }
            
            switch (task->getPriority()) {
                case Priority::LOW: low++; break;
                case Priority::MEDIUM: medium++; break;
                case Priority::HIGH: high++; break;
                case Priority::URGENT: urgent++; break;
            }
        }
        
        cout << "الإجمالي: " << tasks.size() << endl;
        cout << "\nحسب الحالة:" << endl;
        cout << "  للتنفيذ: " << todo << endl;
        cout << "  قيد التنفيذ: " << inProgress << endl;
        cout << "  مكتمل: " << done << endl;
        cout << "  ملغي: " << cancelled << endl;
        
        cout << "\nحسب الأولوية:" << endl;
        cout << "  منخفضة: " << low << endl;
        cout << "  متوسطة: " << medium << endl;
        cout << "  عالية: " << high << endl;
        cout << "  عاجلة: " << urgent << endl;
        
        if (!tasks.empty()) {
            double completion = (double)done / tasks.size() * 100;
            cout << "\nنسبة الإنجاز: " << fixed << setprecision(1) 
                 << completion << "%" << endl;
        }
    }
    
    // File Operations
    void save() const {
        ofstream file(filename);
        for (const auto& task : tasks) {
            file << task->serialize() << endl;
        }
        cout << "💾 تم الحفظ" << endl;
    }
    
    void load() {
        ifstream file(filename);
        if (!file.is_open()) return;
        
        tasks.clear();
        string line;
        while (getline(file, line)) {
            if (!line.empty()) {
                tasks.push_back(Task::deserialize(line));
            }
        }
        cout << "📂 تم تحميل " << tasks.size() << " مهمة" << endl;
    }
};

// ===== Menu =====
void showMenu() {
    cout << "\n═══════════════════════════════" << endl;
    cout << "       📋 إدارة المهام" << endl;
    cout << "═══════════════════════════════" << endl;
    cout << "1. إضافة مهمة" << endl;
    cout << "2. عرض جميع المهام" << endl;
    cout << "3. تحديث حالة مهمة" << endl;
    cout << "4. حذف مهمة" << endl;
    cout << "5. البحث" << endl;
    cout << "6. فلترة حسب الحالة" << endl;
    cout << "7. الإحصائيات" << endl;
    cout << "0. خروج" << endl;
    cout << "═══════════════════════════════" << endl;
    cout << "اختيارك: ";
}

int main() {
    TaskManager manager;
    
    // إضافة مهام تجريبية
    if (manager.findTask(1) == nullptr) {
        manager.addTask("تعلم C++", "إكمال كورس البرمجة", Priority::HIGH);
        manager.addTask("مشروع التخرج", "البدء في التصميم", Priority::URGENT);
        manager.addTask("قراءة كتاب", "Clean Code", Priority::MEDIUM);
    }
    
    int choice;
    do {
        showMenu();
        if (!(cin >> choice)) break;  // نهاية الإدخال أو إدخال غير رقمي
        cin.ignore();
        
        switch (choice) {
            case 1: {
                string title, desc;
                int pri;
                cout << "العنوان: "; getline(cin, title);
                cout << "الوصف: "; getline(cin, desc);
                cout << "الأولوية (0=منخفضة, 1=متوسطة, 2=عالية, 3=عاجلة): ";
                cin >> pri;
                if (pri < 0 || pri > 3) { cout << "أولوية غير صالحة\n"; break; }
                manager.addTask(title, desc, static_cast<Priority>(pri));
                break;
            }
            case 2:
                manager.displayAll();
                break;
            case 3: {
                int id, stat;
                cout << "رقم المهمة: "; cin >> id;
                cout << "الحالة (0=للتنفيذ, 1=قيد التنفيذ, 2=مكتمل, 3=ملغي): ";
                cin >> stat;
                manager.updateStatus(id, static_cast<Status>(stat));
                break;
            }
            case 4: {
                int id;
                cout << "رقم المهمة للحذف: "; cin >> id;
                manager.removeTask(id);
                break;
            }
            case 5: {
                string keyword;
                cout << "كلمة البحث: "; getline(cin, keyword);
                manager.search(keyword);
                break;
            }
            case 6: {
                int stat;
                cout << "الحالة (0=للتنفيذ, 1=قيد التنفيذ, 2=مكتمل, 3=ملغي): ";
                cin >> stat;
                manager.showByStatus(static_cast<Status>(stat));
                break;
            }
            case 7:
                manager.showStats();
                break;
        }
    } while (choice != 0);
    
    cout << "👋 إلى اللقاء!" << endl;
    return 0;
}
اختبر فهمك