مشروع 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;
}اختبر فهمك