C++: تمرين: قاعدة بيانات بسيطة

آخر تحديث: 2026-08-26

في الدرس 51، بنينا نظام دفتر عناوين.

الآن، سنبني ثاني مشروع تطبيقي شامل — قاعدة بيانات بسيطة.

هذا المشروع يستخدم العديد من ميزات C++ المتقدمة، مما يجعله اختبارًا رائعًا لما تعلمته.


1. متطلبات المشروع

(1) 1.1 المتطلبات الوظيفية

الميزة الوصف
CREATE TABLE إنشاء جدول
INSERT إدخال بيانات
SELECT استعلام بيانات
WHERE تصفية شرطية
فهرس تسريع الاستعلامات
معاملة ضمان ACID

(2) 1.2 تصميم مبسط

بما أن هذا مشروع تعليمي، سنصنع نسخة مبسطة:



2. تصميم بنية الجدول

(1) 2.1 تعريف العمود

▶ مثال 2: مثال على الكود (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <string>
#include <variant>
#include <vector>

// تعريف العمود
struct Column {
	std::string name;
	enum Type { INT, STRING } type;
};

// نوع القيمة (استخدم variant لتخزين أنواع مختلفة)
using Value = std::variant<int, std::string>;

// صف (صف واحد من البيانات)
using Row = std::vector<Value>;

// جدول
class Table {
private:
	std::string name;
	std::vector<Column> columns;
	std::vector<Row> rows;

public:
	Table(const std::string& name, const std::vector<Column>& columns)
	: name(name), columns(columns) {}
	
	// إدراج صف
	void insert(const Row& row) {
		if (row.size() != columns.size()) {
			std::cerr << "Column count mismatch" << std::endl;
			return;
		}
		rows.push_back(row);
	}
	
	// استعلام (مبسط: إرجاع جميع الصفوف)
	std::vector<Row> select() {
		return rows;
	}
	
	// عرض بنية الجدول
	void describe() const {
		std::cout << "Table name: " << name << std::endl;
		std::cout << "Columns: " << std::endl;
		for (const auto& col : columns) {
			std::cout << "  " << col.name << " (";
			if (col.type == Column::INT) {
				std::cout << "INT";
			} else {
				std::cout << "STRING";
			}
			std::cout << ")" << std::endl;
		}
	}
};

الخرج:

TEXT 📖 للعرض فقط
Table name:
Columns:
  (
INT
STRING
)


3. تنفيذ الاستعلام

(1) 3.1 عبارة WHERE

CPP
// دالة المسند (لـ WHERE)
using Predicate = std::function<bool(const Row&)>;

// استعلام بشرط
std::vector<Row> selectWhere(Predicate pred) {
	std::vector<Row> result;
	for (const auto& row : rows) {
		if (pred(row)) {
			result.push_back(row);
		}
	}
	return result;
}

// مثال: إيجاد صفوف حيث العمر > 18
auto predicate = [](const Row& row) {
	int age = std::get<int>(row[1]); // افترض أن العمود 2 هو العمر
	return age > 18;
};

auto result = table.selectWhere(predicate);


4. تنفيذ الفهرس

(1) 4.1 فهرس بسيط

TEXT 📖 للعرض فقط
#include <map>

// فهرس (مبسط: فهرسة أنواع int فقط)
class Index {
private:
	std::map<int, std::vector<int>> index; // القيمة → قائمة أرقام الصفوف

public:
	void build(const std::vector<Row>& rows, int colIndex) {
		index.clear();
		for (int i = 0; i < rows.size(); i++) {
			int key = std::get<int>(rows[i][colIndex]);
			index[key].push_back(i);
		}
	}
	
	std::vector<int> lookup(int key) {
		auto it = index.find(key);
		if (it != index.end()) {
			return it->second;
		}
		return {};
	}
};


5. دعم المعاملات

(1) 5.1 معاملة مبسطة

CPP
#include <stack>

class Transaction {
private:
	std::stack<std::vector<Row>> undoStack;

public:
	// بدء المعاملة (حفظ الحالة الحالية)
	void begin(Table& table) {
		undoStack.push(table.rows);
	}
	
	// تأكيد (تفريغ مكاكب التراجع)
	void commit() {
		while (!undoStack.empty()) {
			undoStack.pop();
		}
	}
	
	// تراجع (استعادة الحالة السابقة)
	void rollback(Table& table) {
		if (!undoStack.empty()) {
			table.rows = undoStack.top();
			undoStack.pop();
		}
	}
};


6. مثال كامل

▶ مثال 1: استخدام قاعدة البيانات البسيطة (الصعوبة ⭐⭐⭐)

TEXT 📖 للعرض فقط
int main() {
	// إنشاء جدول
	Table t("users", {{"name", Column::STRING}, {"age", Column::INT}});
	
	// إدخال بيانات
	t.insert({"Alice", 25});
	t.insert({"Bob", 30});
	t.insert({"Charlie", 20});
	
	// استعلام الكل
	auto rows = t.select();
	std::cout << "All users:" << std::endl;
	for (const auto& row : rows) {
		std::cout << std::get<std::string>(row[0]) << ", "
			<< std::get<int>(row[1]) << std::endl;
	}
	
	return 0;
}

الخرج:

TEXT 📖 للعرض فقط
All users:
, 


7. اتجاهات التوسيع

(1) 7.1 توسيعات الميزات

الميزة الصعوبة الوصف
الثبات ⭐⭐ حفظ في ملف
JOIN ⭐⭐⭐⭐ ربط جداول متعددة
محلل SQL ⭐⭐⭐⭐⭐ تحليل عبارات SQL
التحكم في التزامن ⭐⭐⭐⭐ أمان الخيوط


❓ تمارين

(1) تمرين أساسي (الصعوبة ⭐⭐)

أضف ميزة DELETE لقاعدة البيانات.

(2) تمرين متوسط (الصعوبة ⭐⭐⭐)

أضف ميزة UPDATE لقاعدة البيانات.

(3) تمرين متقدم (الصعوبة ⭐⭐⭐⭐)

نفذ محلل SQL بسيط يدعم SELECT * FROM table WHERE age > 18.


▶ مثال 3: تعريف بنية جدول قاعدة بيانات (الصعوبة ⭐)

CPP
#include <iostream>
#include <string>
#include <vector>
#include <variant>

// نوع القيمة (يدعم أنواع بيانات متعددة)
using Value = std::variant<int, std::string, double>;

// صف واحد من البيانات
struct Row {
    int id;
    Value name;
    Value age;
    Value score;

    void display() const {
        std::cout << "ID: " << id << std::endl;
        std::cout << "Name: " << std::get<std::string>(name) << std::endl;
        std::cout << "Age: " << std::get<int>(age) << std::endl;
        std::cout << "Score: " << std::get<double>(score) << std::endl;
    }
};

int main() {
    Row r = {1, std::string("Alice"), 20, 95.5};
    r.display();
    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
ID: 
Name: 
Age: 
Score: 

❓ أسئلة شائعة

س ما مدى سرعة البحث في map؟
ج map مُنفذ داخليًا بشجرة حمراء-سوداء، لذا البحث والإدراج والحذف كلها O(log n). لمليون سجل، أسوأ حالة تحتاج حوالي 20 مقارنة فقط.
س لماذا نستخدم CSV لتخزين الملفات؟
ج تنسيق CSV بسيط، قابل للقراءة البشرية، ويمكن فتحه مباشرة في Excel. JSON يدعم الهياكل المتداخلة لكنه أعقد قليلاً في التحليل. للإنتاج، يُوصى بـ SQLite.
س كيف نمنع حقن SQL؟
ج هذا تنفيذ بسيط. في بيئات الإنتاج الحقيقية، استخدم دائمًا استعلامات معاملية أو عبارات مُعدة مسبقًا. لا تقم أبدًا بدمج إدخال المستخدم مباشرة في سلاسل SQL.

📖 ملخص

نقطة المعرفة التطبيق
variant تخزين قيم أنواع مختلفة
vector<Row> تخزين بيانات الجدول
map تنفيذ الفهارس
كائنات الدوال تنفيذ مسندات WHERE

📝 تمارين

  1. أساسي (الصعوبة ⭐): شغّل برنامج قاعدة البيانات، أنشئ جدول "طلاب" (حقول: رقم الطالب، الاسم، العمر، الدرجة)، أدرج 3 سجلات، واستعلم عن جميع السجلات.

  2. متوسط (الصعوبة ⭐⭐): أضف ميزة "استعلام مرتب" — ادعم SELECT * FROM table ORDER BY age. تلميح: استخدم std::sort + تعبير لامدا للترتيب حسب حقل محدد.

  3. متقدم (الصعوبة ⭐⭐⭐): نفذ "ربط جداول متعددة" — أنشئ جدولين (طلاب، درجات)، ادعم SELECT students.name, grades.score FROM students JOIN grades ON students.id = grades.student_id. تحتاج لتصميم منطق JOIN ودمج النتائج.


الدرس التالي: ملخص الدورة وخارطة طريق التقدم (#53)

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%