الحاويات الدينامكية في السي بلاس بلاس | C++ STL Components

الحاويات الديناميكية (STL Components) في C++

مفهوم STL في C++

STL هي إختصار لجملة Standard Template Library و هي عبارة عن مكتبة ضخمة تحتوي على دوال و كلاسات جاهزة تستخدم لتخزين مجموعة من القيم بشكل مرن جداً يجعل التعامل معها سهل للغاية من ناحية إضافة عناصر فيها, حذف عناصر منها, تحديث قيم عناصرها, ترتيب عناصرها و البحث فيها. و هي تتضمن الأشياء التالية:

  • حاويات (Containers).

  • خوارزميات (Algorithms).

  • دوال (Functions).

  • متواليات (Iterators).

في هذا الدرس سنتعرف على جميع أنواع الحاويات الموجودة في هذه المكتبة و ستتعلم كيفية التعامل معها بتفصيل ممل.

الحاويات الدينامكية في C++

الحاويات تنقسم لأربع فئات أساسية من ناحية الطريقة التي يتم فيها تخزين البيانات و من ثم إمكانية الوصول لها و كل فئة تحتوي على مجموعة كلاسات كالتالي.


1- مفهوم Sequence Containers في C++

الحاوية التي تندرج تحت هذا النوع يقصد بها أن الحاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم رقم Index و كأنها مصفوفة عادية.


2- مفهوم Container Adaptors في C++

الحاوية التي تندرج تحت هذا النوع يقصد بها أن الحاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع القدرة على الوصول إليها بحسب الترتيب الذي تم فيه إدخالها بأسلوب FIFO أو بأسلوب LIFO أو بحسب الأولوية المعطات لعناصرها.


3- مفهوم Associative Containers في C++

الحاوية التي تندرج تحت هذا النوع يقصد بها أن الحاوية تخزن العناصر التي نضيفها فيها بشكل جدول حيث أن كل عنصر نضيفه فيها يحتوي على مفتاح ( Key ) و قيمة ( Value ) مع الإشارة إلى أنها تخزن المفاتيح بترتيب بشكل أبجدي أو رقمي على حسب نوعها.


4- مفهوم Unordered Associative Containers في C++

الحاوية التي تندرج تحت هذا النوع يقصد بها أن الحاوية تخزن العناصر التي نضيفها فيها على شكل Buckets مما يجعل الوصول إليها سريع جداً.



الفرق بين المصفوفة العادية و الحاوية الدينامكية في C++

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

المصفوفة العادية نتعامل مع عناصرها فقط من خلال رقم Index العنصر أما في الحاويات الدينامكية فيوجد دوال عديدة نستخدمها للتعامل مع العناصر.

الكلاسات الموجودة في STL في C++

الجدول التالي يحتوي على جميع الكلاسات الموجودة في STL و التي تمثل حاويات دينامكية.

الكلاس مع تعريفه
array تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل مصفوفة متطورة مقارنةً بالمصفوفة العادية حيث أن التعامل معها مرن أكثر لأنك تستطيع معرفة عدد عناصرها و الحصول على قيمة أول و آخر عنصر فيها بكل سهولة.
المزيد »
vector يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم رقم Index.
إذاً يشبه المصفوفة العادية إلى حد ما و لكن الفرق الأساسي بينهما أن حجمه ليس ثابتاً حيث يمكنك إضافة العدد الذي تريده من العناصر فيه وقتما شئت.
المزيد »
forward_list تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم عنوان العنصر الذي يليه في الذاكرة حتى يتم الحفاظ على الترتيب الذي تم فيه تخزينهم.
المزيد »
list يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم عنوان العنصر الذي يسبقه و عنوان العنصر الذي يليه في الذاكرة حتى يتم الحفاظ على الترتيب الذي تم فيه تخزينهم و لتتمكن من التنقل بين العناصر بالإتجاهين; من الأول إلى الآخر و بالعكس.
المزيد »
stack يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل فوق بعضها البعض بأسلوب LIFO الذي يعني أن العنصر الذي يدخل أولاً يخرج أخيراً, و هذا يجعلك قادر فقط على التعامل مع العنصر الموجود في أعلاها.
المزيد »
queue يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض بأسلوب FIFO الذي يعني أن العنصر الذي يدخل أولاً يخرج أولاً, و هذا يجعلك قادر فقط على التعامل مع العنصر الموجود في آخرها.
المزيد »
priority_queue يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأكبر في البداية و العنصر الذي يملك القيمة الأصغر في النهاية, بالإضافة إلى أنه يسمح لك بالوصول لأعلى و أدنى قيمة فقط لأنها موجودة على أطراف الحاوية مع إمكانية حذف القيمة الأدنى منها فقط.
المزيد »
deque يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل مع إعطاء كل عنصر منهم رقم Index مما يجعلك قادر على الوصول إلى جميع عناصرها و إضافة عناصر جديدة في أي مكان فيها, و يميز هذه الحاوية بأنك قادر على التعامل مع العناصر الموجودة على أطرافها بشكل مباشر.
المزيد »
set يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأصغر في البداية و العنصر الذي يملك القيمة الأكبر في النهاية أو العكس, مع الإشارة إلى أنه لا يمكن تخزين قيم مكررة فيها.
المزيد »
multiset يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأصغر في البداية و العنصر الذي يملك القيمة الأكبر في النهاية أو العكس, مع الإشارة إلى أنه يمكن تخزين قيم مكررة فيها.
المزيد »
map يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بشكل تصاعدي على حسب قيم المفاتيح.
المزيد »
multimap يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بشكل تصاعدي على حسب قيم المفاتيح مع الإشارة إلى أنه يمكن وضع نفس المفتاح لأكثر من عنصر.
المزيد »
unordered_set تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه.
المزيد »
unordered_multiset تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه مع الإشارة إلى أنه يمكن تخزين قيم مكررة فيها.
المزيد »
unordered_map تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بالإعتماد على دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على مفتاح أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيها.
المزيد »
unordered_multimap تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بالإعتماد على دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على مفتاح أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيها مع الإشارة إلى أنه يمكن وضع نفس المفتاح لأكثر من عنصر.
المزيد »

الكلاس array في C++

تعريف الكلاس array في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل مصفوفة متطورة مقارنةً بالمصفوفة العادية حيث أن التعامل معها مرن أكثر لأنك تستطيع معرفة عدد عناصرها و الحصول على قيمة أول و آخر عنصر فيها بكل سهولة.

بعد أن يتم تحديد عدد العناصر التي يمكن إضافتها فيه, لا يمكن تغييره كما هو الحال في المصفوفات العادية.

لاستخدام الكلاس array يجب تضمين الملف #include<array> لأنه موجود فيه.


بناء الكلاس
                  template < class T, size_t N >
                  class array
                

إذاً عند إنشاء كائن من الكلاس array يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T و عدد العناصر الأقصى الذي قد نحتاج تخزينه فيه مكان الباراميتر .N

دوال الكلاس array في C++

الجدول التالي يحتوي على دوال الكلاس array الأكثر إستخداماً.

الدالة مع تعريفها
T at(size_t n) ترجع العنصر الموجود على الـ index الذي نمرره لها مكان الباراميتر n عند استدعائها.
ترمي الإستثناء out_of_range في حال كان الـ index المحدد فيها أصغر من 0 أو أكبر من عدد العناصر الموجودة.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير index غير موجود في الكائن.
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
T front() ترجع قيمة أول عنصر موجود في الكائن الذي قام باستدعائها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
T back() ترجع قيمة آخر عنصر موجود في الكائن الذي قام باستدعائها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
void fill(T val) تستخدم لإعطاء قيمة لكل عنصر موجود الكائن الذي قام باستدعائها.
مكان الباراميتر val يجب أن تمرر قيمة من نفس نوع البيانات التي يمكن للكائن في الأساس أن يخزّنها.
size_t size() تستخدم للحصول على عدد عناصر الكائن الذي قام باستدعائها.
القيمة التي ترجعها هي نفسها القيمة التي نمررها مكان الباراميتر الثاني الذي نمرره عند إنشاء الكائن و الذي يمثل عدد عناصره.

معلومة: الفرق بين الدالة size() و العامل sizeof هو أن هذا الأخير يرجع حجم العناصر في الذاكرة بالوحدة Byte و ليس عددها.
void swap(array& anotherArray) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherArray نمرر لها كائن من الكلاس array يملك نفس نوع و عدد عناصر الكائن الذي قام باستدعاء.

أمثلة شاملة حول التعامل مع الكلاس array في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من array مع تحديد أنه يمكن أن يحتوي على 5 عناصر نوعها int.
بعدها قمنا بوضع 0 كقيمة أولية لجميع عناصره و ثم عرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام العامل [] للوصول إلى العناصر و الدالة size() لمعرفة حجم المصفوفة.

المثال الأول
main.cpp
                    #include <iostream>
                    #include <array>

                    using namespace std;

                    int main()
                    {
                    // int يمكنه أن يحتوي على 5 قيم نوعها array هنا قمنا بتعريف كائن من الكلاس
                    array<int,5> myArray;

                    // هنا قمنا بوضع القيمة 0 كقيمة أولية لجميع عناصره
                    myArray.fill(0);

                    // إلى 5 myArray هنا قمنا بتغير قيمة أول عنصر في الكائن
                    myArray[0] = 5;

                    // إلى 7 myArray هنا قمنا بتغير قيمة ثالث عنصر في الكائن
                    myArray[2] = 7;

                    // إلى 9 myArray هنا قمنا بتغير قيمة آخر عنصر في الكائن
                    myArray[myArray.size()-1] = 9;

                    cout << "Array values = ";

                    // myArray هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<myArray.size(); i++)
                    {
                    cout << myArray[i] << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Array values = 5 0 7 0 9
                  


في المثال التالي قمنا بتعريف كائن من array أدخلنا فيه 5 عناصر نصية.
بعدها قمنا بطباعة عدد عناصره و أول و آخر قيمة فيه.

ملاحظة: قمنا باستخدام الدالة empty() لمعرفة ما إن كانت المصفوفة فارغة أم لا, الدالة front() للحصول على قيمة أول عنصر و الدالة back() للحصول على قيمة آخر عنصر.

المثال الثاني
main.cpp
                    #include <iostream>
                    #include <array>

                    using namespace std;

                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بوضع قيم أولية في عناصره string يمكنه أن يحتوي على 5 قيم نوعها array هنا قمنا بتعريف كائن من الكلاس
                    array<string,5> myArray = {"One", "Two", "Three", "Four", "Five"};

                    // size() باستخدام الدالة myArray هنا قمنا بطباعة عدد عناصر الكائن
                    cout << "Array size = " << myArray.size() << endl;

                    // و إن لم يكن فارغاً سيتم طباعة أول و آخر قيمة فيه empty() باستخدام الدالة myArray هنا قمنا بالتشييك على عدد عناصر الكائن
                    if (!myArray.empty())
                    {
                    cout << "Array first element value = " << myArray.front() << endl;
                    cout << "Array last element value = " << myArray.back();
                    }

                    return 0;
                    }

                  

سنحصل على النتيجة التالية عند التشغيل.

                    Array size = 5
                    Array first element value = One
                    Array last element value = Five
                  


في المثال التالي قمنا بتعريف كائنين من array في كل منهما 4 عناصر من النوع نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما و الدالة at() للوصول لقيم العناصر الموجودة فيهما.

المثال الثالث
main.cpp
                    #include <iostream>
                    #include <array>

                    using namespace std;

                    int main()
                    {
                    // مع إعطاؤهما قيم أولية int يحتوي كل منهما على 4 عناصر نوعها array هنا قمنا بتعريف كائنين من الكلاس
                    array<int,4> arr1 = {1, 2, 3, 4};
                    array<int,4> arr2 = {5, 6, 7, 8};

                    // arr2 مع قيم الكائن arr1 هنا قمنا بتبديل قيم الكائن
                    arr1.swap(arr2);

                    cout << "arr1 values = ";

                    // arr1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<arr1.size(); i++)
                    {
                    cout << arr1.at(i) << " ";
                    }

                    cout << "\narr2 values = ";

                    // arr2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<arr2.size(); i++)
                    {
                    cout << arr2.at(i) << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    arr1 values = 5 6 7 8
                    arr2 values = 1 2 3 4
                  
_______

الكلاس vector في C++

تعريف الكلاس vector في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم رقم Index.
إذاً يشبه المصفوفة العادية إلى حد ما و لكن الفرق الأساسي بينهما أن حجمه ليس ثابتاً حيث يمكنك إضافة العدد الذي تريده من العناصر فيه وقتما شئت.

لاستخدام الكلاس vector يجب تضمين الملف #include<vector> لأنه موجود فيه.


بناء الكلاس
                  template < class T, class Alloc = allocator<T> >
                  class vector
                

إذاً عند إنشاء كائن من الكلاس vector يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس vector في C++

الجدول التالي يحتوي على دوال الكلاس vector التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس vector التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
void resize(size_type n) تستخدم لحجز مساحة للعناصر التي تنوي تخزينها لاحقاً في الكائن الذي قام باستدعائها.
عند تمرير قيمة للبارميتر n يجب أن لا تكون أصغر من عدد العناصر الموجودة في الكائن, لأنك إذا فعلت ذلك سيتم حذف العناصر التي لم يعد الكائن قادر على تخزينها.
كما أنها ترمي الإستثناء bad_alloc في حال قمت بتمرير قيمة أكبر من عدد العناصر الأقصى التي يمكن تخزينها في الكائن.

معلومة: هذه الدالة قد تكون مفيدة في حال أردت حجز مساحة أوليّة للعناصر قبل البدء بإضافتهم بدل جعل المترجم يفعل ذلك كلما أضفت عنصر جديد.
هذه المعلومة غير مذكورة في المرجع الرسمي للغة C++ لذا قد تكون غير دقيقة.
size_t capacity() تستخدم للحصول على عدد العناصر التي تم حجز مساحة لها في الذاكرة من أجل الكائن الذي قام باستدعائها.

معلومة: الرقم الذي ترجعه هذه الدالة في العادة يكون أكبر بنسبة قليلة من الرقم الذي ترجعه الدالة size() و سبب ذلك أن المترجم يقوم بتخصيص مساحة زيادة للكائن حتى يستخدمها عندما يحتاجها.
void shrink_to_fit() تستخدم للتخلي عن أي مساحة زيادة مخصصة في الذاكرة للكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس vector التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
T at(size_t n) تستخدم للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر n نمرر لها رقم يمثل index العنصر الذي نريد الوصول إليه.
ترمي الإستثناء out_of_range في حال كانت قيمة n أصغر من 0 أو أكبر من عدد العناصر الموجودة.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير index غير موجود في الكائن.
T front() تستخدم للوصول لقيمة أول عنصر موجود في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
T back() تستخدم للوصول لقيمة آخر عنصر موجود في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
T* data() ترجع مؤشر لقيمة أول عنصر موجود في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس vector التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void assign(size_t n, const T& val) تستخدم لإضافة عدة عناصر في الكائن الذي قام باستدعائها و هي تحذف أي عناصر أخرى موجودة فيه في حال لم يكن فارغاً.
مكان الباراميتر n نمرر عدد العناصر التي نريد إضافتها في الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر القيمة التي نريد وضعها للعناصر التي سيتم إضافتها في الكائن الذي قام باستدعائها.
void push_back(T& val) تستخدم لإضافة عنصر جديد في آخر الكائن الذي قام باستدعائها.
void pop_back() تستخدم لحذف آخر عنصر موجود في الكائن الذي قام باستدعائها.
iterator insert(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند عنوان محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان المكان الذي سيتم إضافة العنصر عنده في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
void clear() تستخدم لحذف جميع العناصر الموجودة في الكائن الذي قام باستدعائها.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(vector& anotherVector) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherVector نمرر لها كائن من الكلاس vector يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

أمثلة شاملة حول التعامل مع الكلاس vector في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من vector مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 5 قيم فيه و من ثم عرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة push_back() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها, العامل [] للوصول إلى العناصر.

المثال الأول

main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // int يمكنه أن يحتوي على قيم نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<int> myVector;

                    // push_back() باستخدام الدالة myVector هنا قمنا بإضافة 5 عناصر في الكائن
                    myVector.push_back(1);
                    myVector.push_back(2);
                    myVector.push_back(3);
                    myVector.push_back(4);
                    myVector.push_back(5);

                    cout << "Vector values = ";

                    // myVector هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<myVector.size(); i++)
                    {
                    cout << myVector[i] << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector values = 1 2 3 4 5
                  


في المثال التالي قمنا بتعريف كائن من vector أدخلنا فيه 5 عناصر نصية.
بعدها قمنا بإضافة 5 قيم فيه و من ثم عرض عدد جميع عناصره, عدد العناصر التي قام المترجم بتخصيص مساحة لها في الذاكرة في حال أردنا إضافة عناصر جديدة, عدد جميع العناصر التي يمكن إضافتها فيه, بالإضافة لأول و آخر قيمة موجودة فيه.

ملاحظة: قمنا باستخدام الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة capacity() لمعرفة عدد العناصر المخصصة حالياً للكائن في الذاكرة حتى لو لم يحتاج إليها, الدالة max_size() لمعرفة أكبر عدد عناصر يمكن إضافتها فيه, الدالة empty() لمعرفة ما إن كانت المصفوفة فارغة أم لا, الدالة front() للحصول على قيمة أول عنصر و الدالة back() للحصول على قيمة آخر عنصر.

المثال الثاني
main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // string يمكنه أن يحتوي على 5 قيم نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<string> myVector;

                    // push_back() باستخدام الدالة myVector هنا قمنا بإضافة 5 عناصر في الكائن
                    myVector.push_back("One");
                    myVector.push_back("Two");
                    myVector.push_back("Three");
                    myVector.push_back("Four");
                    myVector.push_back("Five");

                    // size() باستخدام الدالة myVector هنا قمنا بطباعة عدد عناصر الكائن
                    cout << "Vector size = " << myVector.size() << endl;

                    // capacity() باستخدام الدالة myVector هنا قمنا بطباعة عدد العناصر التي تم تخصيص لها مساحة في الذاكرة حالياً من أجل الكائن
                    cout << "Vector capacity = " << myVector.capacity() << endl;

                    // max_size() باستخدام الدالة myVector هنا قمنا بطباعة عدد العناصر الأقصى التي يمكن تخزينها في الكائن
                    cout << "Vector max size = " << myVector.max_size() << endl;

                    // و إن لم يكن فارغاً سيتم طباعة أول و آخر قيمة فيه empty() باستخدام الدالة myVector هنا قمنا بالتشييك على عدد عناصر الكائن
                    if (!myVector.empty())
                    {
                    cout << "Vector first element value = " << myVector.front() << endl;
                    cout << "Vector last element value = " << myVector.back();
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector size = 5
                    Vector capacity = 8
                    Vector max size = 576460752303423487
                    Vector first element value = One
                    Vector last element value = Five
                  


في المثال التالي قمنا بتعريف كائن من vector مخصص لتخزين قيم نوعها int مع إضافة 9 قيم فيه عند تعريفه.
بعدها قمنا بحذف عدة عناصر منه بطرق مختلفة و من ثم طباعة قيم العناصر المتبقية.

ملاحظة: قمنا باستخدام الدالة pop_back() لحذف آخر عنصر من الكائن, الدالة erase() لحذف عنصر واحد, الدالة erase() مرة ثانية لحذف أكثر من عنصر في وقت واحد و الدالة begin() للحصول على عدّاد يمكن من خلاله تحديد مكان العناصر التي سيتم حذفها.

المثال الثالث
main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة 9 قيم فيه int يمكنه أن يحتوي على عناصر نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<int> myVector = {1, 2, 3, 4, 5, 6, 7, 8, 9};

                    // myVector هنا قمنا بحذف آخر عنصر موجود في الكائن
                    // {1, 2, 3, 4, 5, 6, 7, 8} أصبح فيه القيم التالية myVector إذاً الكائن
                    myVector.pop_back();

                    // myVector يساوي 2 في الكائن index هنا قمنا بحذف العنصر الذي يملك
                    // {1, 2, 4, 5, 6, 7, 8} أصبح فيه القيم التالية myVector إذاً الكائن
                    myVector.erase(myVector.begin() + 2);

                    // يساوي 1 وصولاً إلى index إبتداءاً من العنصر الذي يملك myDeque هنا قمنا بحذف العناصر الموجودة في الكائن
                    // {1, 6, 7, 8} أصبح فيه القيم التالية myVector يساوي 4. و بالتالي الكائن index ما قبل العنصر الذي يملك
                    myVector.erase(myVector.begin() + 1, myVector.begin() + 4);

                    cout << "Vector values = ";

                    // myVector هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<myVector.size(); i++)
                    {
                    cout << myVector[i] << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector values = 1 6 7 8
                  


في المثال التالي قمنا بتعريف كائن من vector مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بعرض جميع عناصره مرتين, مرة من أول عنصر لآخر عنصر, و مرة من آخر عنصر لأول عنصر.

ملاحظة: عند البدء من أول عنصر لآخر عنصر, قمنا باستخدام الدالة begin() للحصول على عدّاد يمكن من خلاله البدئ من العنصر الأول و الدالة end() للحصول على قيمة العدّاد التي من خلالها نعرف أننا وصلنا لآخر عنصر.
و عند البدء من آخر عنصر لأول عنصر, قمنا باستخدام الدالة rbegin() للحصول على عدّاد يمكن من خلاله البدئ من آخر عنصر و الدالة rend() للحصول على قيمة العدّاد التي من خلالها نعرف أننا وصلنا لأول عنصر بدون الحاجة لتغيير شكل الحلقة التي استخدمناها في المرة الأولى.

المثال الرابع
main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<int> myVector = {1, 2, 3, 4, 5};

                    cout << "Vector values from begin to end = ";

                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myVector هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(auto it=myVector.begin(); it<myVector.end(); it++)
                    {
                    cout << *it << " ";
                    }

                    cout << "\nVector values from end to begin = ";

                    // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myVector هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(auto it=myVector.rbegin(); it<myVector.rend(); it++)
                    {
                    cout << *it << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector values from begin to end = 1 2 3 4 5
                    Vector values from end to begin = 5 4 3 2 1
                  


في المثال التالي قمنا بتعريف كائن من vector مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 5 قيم فيه, و من ثم عرض عدد جميع عناصره و عدد العناصر التي قام المترجم بتخصيص مساحة لها في الذاكرة في حال أردنا إضافة عناصر جديدة.
بعدها قمنا بالتخلي عن أي مساحة خصصها المترجم بشكل إفتراضي للكائن حتى يخزن قيم فيها إن إحتاجها.
في الأخير قمنا بإعادة عرض المعلومات السابقة للتأكد ما إن تم التخلي عن المساحة التي يستخدمها الكائن أم لا.

ملاحظة: قمنا باستخدام الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة capacity() لمعرفة عدد العناصر المخصصة حالياً للكائن في الذاكرة حتى لو لم يحتاج إليها و الدالة shrink_to_fit() للتخلي عن أي مساحة في الذاكرة لم يحتاج إليها الكائن.

المثال الخامس
main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // int يمكنه أن يحتوي على قيم نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<int> myVector;

                    // push_back() باستخدام الدالة myVector هنا قمنا بإضافة 5 عناصر في الكائن
                    myVector.push_back(1);
                    myVector.push_back(2);
                    myVector.push_back(3);
                    myVector.push_back(4);
                    myVector.push_back(5);

                    // size() باستخدام الدالة myVector هنا قمنا بطباعة عدد عناصر الكائن
                    cout << "Vector size = " << myVector.size() << endl;

                    // capacity() باستخدام الدالة myVector هنا قمنا بطباعة عدد العناصر التي تم تخصيص لها مساحة في الذاكرة حالياً من أجل الكائن
                    cout << "Vector capacity = " << myVector.capacity() << endl;

                    // myVector هنا قمنا بالتخلي عن أي مساحة إضافية حجزها المترجم بشكل تلقائي للكائن
                    myVector.shrink_to_fit();

                    cout << "\nAfter shrinking the vector\n\n";

                    // size() باستخدام الدالة myVector هنا قمنا بإعادة طباعة عدد عناصر الكائن
                    cout << "Vector size = " << myVector.size() << endl;

                    // capacity() باستخدام الدالة myVector هنا قمنا بإعادة طباعة عدد العناصر التي تم تخصيص لها مساحة في الذاكرة حالياً من أجل الكائن
                    cout << "Vector capacity = " << myVector.capacity() << endl;

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector size = 5
                    Vector capacity = 8

                    After shrinking the vector

                    Vector size = 5
                    Vector capacity = 5
                  


في المثال التالي قمنا بتعريف كائن من vector مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 5 قيم فيه وراء بعضها.
بعدها قمنا بإضافة قيمة في مكان محدد فيه.
في الأخير قمنا بعرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة insert() لإضافة القيمة في مكان محدد في الكائن.

المثال السادس

main.cpp
                    #include <iostream>
                    #include <vector>

                    using namespace std;

                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها vector هنا قمنا بتعريف كائن من الكلاس
                    vector<int> myVector = {1, 2, 3, 4, 5};

                    // myVector رقم 2 بالنسبة للكائن index هنا قمنا بإضافة القيمة 8 في الـ
                    // {1, 2, 8, 3, 4, 5} أصبح فيه القيم التالية myVector إذاً الكائن
                    myVector.insert(myVector.begin() + 2, 8); 

                    cout << "Vector values = ";

                    // myVector هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for(size_t i=0; i<myVector.size(); i++)
                    {
                    cout << myVector[i] << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Vector values = 1 2 8 3 4 5
                  
_____

الكلاس forward_list في C++

تعريف الكلاس forward_list في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض مع إعطاء كل عنصر منهم عنوان العنصر الذي يليه في الذاكرة حتى يتم الحفاظ على الترتيب الذي تم فيه تخزينهم كالتالي.

  • الكائن يملك خانة أساسية يقال لها Head و هي تشير لعنوان أول عنصر موجود فيه.

  • العنصر الأخير لا يوجد أي عنصر بعده, لهذا يكون عنوان العنصر التالي بالنسبة له يساوي null للإشارة إلى أنه لا يوجد عنصر أصلاً بعده.

  • Tail تعني ذَيل و يقصد بها آخر عنصر موجود في الكائن.



لاستخدام الكلاس forward_list - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<forward_list> لأنه موجود فيه.


بناء الكلاس
                  template < class T, class Alloc = allocator<T> >
                  class forward_list;
                

إذاً عند إنشاء كائن من الكلاس forward_list يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس forward_list في C++

الجدول التالي يحتوي على دوال الكلاس forward_list التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator before_begin() ترجع كائن iterator يشير للمكان الذي يبدأ عنده تخزين عناصر الكائن في الذاكرة.
تستخدم هذه الدالة بشكل أساسي إذا أردت إضافة عنصر جديد قبل أول عنصر موجود في الكائن.

الجدول التالي يحتوي على دوال الكلاس forward_list التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
void resize(size_type n) تستخدم لحجز مساحة للعناصر التي تنوي تخزنيها لاحقاً في الكائن الذي قام باستدعائها.
عند تمرير قيمة للبارميتر n يجب أن لا تكون أصغر من عدد العناصر الموجودة في الكائن, لأنك إذا فعلت ذلك سيتم حذف العناصر التي لم يعد الكائن قادر على تخزينها.
كما أنها ترمي الإستثناء bad_alloc في حال قمت بتمرير قيمة أكبر من عدد العناصر الأقصى التي يمكن تخزينها في الكائن.

معلومة: هذه الدالة قد تكون مفيدة في حال أردت حجز مساحة أوليّة للعناصر قبل البدء بإضافتهم بدل جعل المترجم يفعل ذلك كلما أضفت عنصر جديد.
هذه المعلومة غير مذكورة في المرجع الرسمي للغة C++ لذا قد تكون غير دقيقة.

الجدول التالي يحتوي على دوال الكلاس forward_list التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
reference front() تستخدم لتغيير قيمة أول عنصر موجود في الكائن الذي قام باستدعائها أو للحصول على قيمته.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.

الجدول التالي يحتوي على دوال الكلاس forward_list التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void assign(initializer_list<T> aList) تستخدم لوضع مصفوفة من العناصر بدل العناصر الموجودة في الكائن الذي قام باستدعائها.
بشكل عام, يمكنك الإستفادة منها في حال أردت تخزين مجموعة من العناصر في الكائن بعد إنشائه.
مكان الباراميتر aList نمرر مصفوفة من العناصر التي تحتوي على قيم من نفس نوع الكائن الذي قام باستدعائها.
void emplace_front(T& val) تستخدم لإضافة عنصر في بداية الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في أول الكائن الذي قام باستدعائها.
void push_front(T& val) تستخدم لإضافة عنصر في بداية الكائن الذي قام باستدعائها تماماً كالدالة emplace_front().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في أول الكائن الذي قام باستدعائها.
iterator emplace_after(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند عنوان محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يمثل عنوان العنصر الذي سيتم إضافة العنصر الجديد بعده مباشرةً في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
iterator insert_after(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند رقم عنوان محدد - في الكائن الذي قام باستدعائها تماماً كالدالة emplace_after().
مكان الباراميتر position نمرر كائن أصله من Iterator يمثل عنوان العنصر الذي سيتم إضافة العنصر الجديد بعده مباشرةً في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنون العنصر الذي تم إضافته.
iterator erase_after(const_iterator position) تستخدم لحذف عنصر موجود بعد عنصر محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يمثل عنوان العنصر الذي سيتم حذف العنصر التالي الموجود بعده.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الموجود بعد العنصر الذي تم حذفه.
void clear() تستخدم لحذف جميع العناصر الموجودة في الكائن الذي قام باستدعائها.
void swap(forward_list& anotherList) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherList نمرر لها كائن من الكلاس forward_list يملك نفس نوع عناصر الكائن الذي قام باستدعائه.
void resize(size_type n) تستخدم لحجز مساحة للعناصر التي تنوي تخزينها لاحقاً في الكائن الذي قام باستدعائها.
عند تمرير قيمة للبارميتر n يجب أن لا تكون أصغر من عدد العناصر الموجودة في الكائن, لأنك إذا فعلت ذلك سيتم حذف العناصر التي لم يعد الكائن قادر على تخزينها.
معلومة: هذه الدالة قد تكون مفيدة في حال أردت حجز مساحة أوليّة للعناصر قبل البدء بإضافتهم بدل جعل المترجم يفعل ذلك كلما أضفت عنصر جديد.
هذه المعلومة غير مذكورة في المرجع الرسمي للغة C++ لذا قد تكون غير دقيقة.
iterator splice_after(const_iterator position, forward_list& anotherList) تستخدم لإضافة عناصر كائن من forward_list في مكان محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يمثل عنوان العنصر الذي سيتم إضافة عناصر الكائن anotherList إبتداءاً من عنده.
مكان الباراميتر anotherList نمرر الكائن الذي نريد نسخ عناصره و الذي يجب أن تكون عناصره من نفس نوع عناصر الكائن الذي قام باستدعائها.
void remove(const T& val) تستخدم لحذف جميع العناصر الموجودة في الكائن الذي قام باستدعائها في حال كانت قيمتها تساوي القيمة التي نمررها لها مكان الباراميتر val.
void remove_if(Predicate pred) تستخدم هذه الدالة لحذف عناصر من الكائن الذي قام باستدعائها في حال كانت قيمه تطابق الشرط الذي نقوم بتمريره لها مكان الباراميتر pred.
الباراميتر pred الذي يجب أن نمرره لها سيكون عبارة عن دالة ترجع true في حال كان يجب حذف العنصر و false في حال لم يكن يجب ذلك.
void unique() تستخدم لحذف القيم المكررة من الكائن الذي قام باستدعائها.
بمعنى آخر, تقوم بحذف أي قيمة تجدها متكررة فيه و تترك نسخة واحدة منها.
معلومة: عند استخدام هذه الدالة يجب أن تكون القيم المكررة مرتبة وراء بعضها البعض و لفعل هذا الأمر قم فقط باستدعاء الدالة sort() من الكائن قبل استدعاء هذه الدالة.
void merge(forward_list& anotherList) تستخدم لنقل عناصر كائن من forward_list في آخر الكائن الذي قام باستدعائها.
عند نقل عناصر الكائن anotherList إلى الكائن الذي قام باستدعائها يصبح فارغاً.
void sort() تستخدم لترتيب عناصر الكائن الذي قام باستدعائها من الأصغر إلى الأكبر.
void reverse() تستخدم لترتيب عناصر الكائن الذي قام باستدعائها بشكل عكسي.

أمثلة شاملة حول التعامل مع الكلاس forward_list في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من forward_list مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 5 قيم فيه بطرق مختلفة و من ثم عرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة assign() لإضافة مجموعة من العناصر فيه دفعة واحدة, الدالة push_front() لإضافة عنصر واحد في بدايته و الدالة emplace_front() لإضافة عنصر واحد في بدايته أيضاً. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
                    #include <iostream>
                    #include <forward_list>

                    using namespace std;

                    int main()
                    {
                    // int يمكنه أن يحتوي على قيم نوعها forward_list هنا قمنا بتعريف كائن من الكلاس
                    forward_list<int> myList;

                    // assign() باستخدام الدالة myList هنا قمنا بإضافة 3 عناصر أولية في الكائن
                    // {3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.assign({3, 4, 5});

                    // push_front() قيمته العدد 2 باستخدام الدالة myList هنا قمنا بإضافة عنصر في أول الكائن
                    // {2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.push_front(2);

                    // emplace_front() قيمته العدد 1 باستخدام الدالة myList هنا قمنا بإضافة عنصر في أول الكائن
                    // {1, 2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.emplace_front(1);

                    cout << "List values = ";

                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for (auto it = myList.begin(); it != myList.end(); ++it)
                    {
                    cout << *it << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    List values = 1 2 3 4 5
                  


في المثال التالي قمنا بتعريف دالة إسمها printList() عند استدعائها نمرر لها كائن من forward_list فتقوم بطباعة كل قيم عناصره حتى لا نقوم بتكرار نفس الكود كلما أردنا طباعة قيم عناصر الكائن.
بعدها قمنا بتعريف كائن من forward_list مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه كما هي, ثم ترتيبها من الأصغر إلى الأكبر و عرضها, ثم ترتيبها بشكل عكسي و عرضها.

ملاحظة: قمنا باستخدام الدالة sort() لترتيب عناصر الكائن من الأصغر إلى الأكبر و الدالة reverse() لعكس ترتيب العناصر.

المثال الثاني
main.cpp
                    #include <iostream>
                    #include <forward_list>

                    using namespace std;

                    // فتقوم بطباعة القيم الموجودة فيه forward_list عند استدعائها نمرر لها كائن من printList هنا قمنا بتعريف دالة إسمها
                    void printList(forward_list<int> &aList)
                    {
                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for (auto it = aList.begin(); it != aList.end(); ++it)
                    {
                    cout << *it << " ";
                    }
                    }

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها forward_list هنا قمنا بتعريف كائن من الكلاس
                    forward_list<int> myList = {3, 5, 1, 4, 2};

                    // كما أضفناها فيه myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
                    cout << "List values\n";
                    printList(myList);

                    // من الأصغر إلى الأكبر myList لترتيب قيم عناصر الكائن sort() هنا قمنا باستدعاء الدالة
                    myList.sort();

                    // بعد أن قمنا بترتيبها myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
                    cout << "\n\nAfter sorting list values\n";
                    printList(myList);

                    // myList لعكس ترتيب قيم عناصر الكائن sort() هنا قمنا باستدعاء الدالة
                    myList.reverse();

                    // بعد أن قمنا بترتيبها myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
                    cout << "\n\nAfter reverse list values\n";
                    printList(myList);

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    List values
                    3 5 1 4 2

                    After sorting list values
                    1 2 3 4 5

                    After reverse list values
                    5 4 3 2 1
                  


في المثال التالي قمنا بتعريف كائنين من forward_list في كل منهما 4 عناصر من النوع نوعها int.
بعدها قمنا بدمج الكائنين في كائن واحد حيث قمنا بنقل العناصر الموجودة في الكائن الثاني إلى الكائن الأول.
في النهاية قمنا بطباعة القيم التي أصبحت موجودة في الكائن الأول, و ما إن كان الكائن الثاني قد أصبح فارغاً أم لا.

ملاحظة: قمنا باستخدام الدالة merge() لنقل العناصر من كائن لآخر و الدالة empty() لمعرفة ما إن كان الكائن قد أصبح فارغاً أم لا.

المثال الثالث
main.cpp
                    #include <iostream>
                    #include <forward_list>

                    using namespace std;

                    int main()
                    {
                    // int يحتوي كل منهما على 4 عناصر نوعها forward_list هنا قمنا بتعريف كائنين من الكلاس
                    forward_list<int> list1 = {1, 2, 3, 4};
                    forward_list<int> list2 = {5, 6, 7, 8};

                    // list1 في آخر الكائن list2 هنا قمنا بنقل عناصر الكائن
                    // {1, 2, 3, 4, 5, 6, 7, 8} أصبح فيه القيم التالية list1 إذاً الكائن
                    list1.merge(list2);

                    cout << "First list values = ";

                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه list1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for (auto it = list1.begin(); it != list1.end(); ++it)
                    {
                    cout << *it << " ";
                    }

                    // قد أصبح فارغاً أم لا list2 هنا قمنا بطباعة ما إن كان الكائن
                    cout << "\nIs second list empty? " << ((list2.empty())? "Yes": "No");

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    First list values = 1 2 3 4 5 6 7 8
                    Is second list empty? Yes
                  


في المثال التالي قمنا بتعريف كائن من forward_list مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بإزالة العناصر الموجودة فيه و التي تملك قيم مكررة.
في الأخير قمنا بعرض القيم الموجودة فيه كما هي.

ملاحظة: قمنا باستخدام الدالة sort() لترتيب عناصر الكائن من الأصغر إلى الأكبر و الدالة unique() من بعدها لإبقاء نسخة واحدة فقط من العناصر التي تملك نفس القيمة.

المثال الرابع
main.cpp
                    #include <iostream>
                    #include <forward_list>

                    using namespace std;

                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها forward_list هنا قمنا بتعريف كائن من الكلاس
                    forward_list<int> myList = {1, 4, 2, 4, 5, 3, 1, 5};

                    // من الأصغر إلى الأكبر حتى يتم وضع القيم المكررة وراء بعضها myList هنا قمنا بترتيب عناصر الكائن
                    // {1, 1, 2, 3, 4, 4, 5, 5} أصبحت مرتبة كتالي myList إذاً عناصر الكائن
                    myList.sort();

                    // myList هنا قمنا بإبقاء نسخة واحدة من كل قيمة مكررة موجودة في الكائن
                    // {1, 2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.unique();

                    cout << "List values = ";

                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for (auto it = myList.begin(); it != myList.end(); ++it)
                    {
                    cout << *it << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    List values = 1 2 3 4 5
                  


في المثال التالي قمنا بتعريف كائن من forward_list مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بحذف أي عنصر يملك قيمة تساوي 0 و من ثم قمنا بحذف أي قيمة أكبر من 3.
في الأخير قمنا بعرض القيم الموجودة فيه كما هي.

ملاحظة: قمنا باستخدام الدالة remove() لحذف العناصر التي تملك قيمة محددة ألا و هي 0 و الدالة remove_if() لحذف العناصر في حال كانت قيمتها أكبر من 3.

المثال الخامس
main.cpp
                    #include <iostream>
                    #include <forward_list>

                    using namespace std;

                    // سنستخدمها لاحقاً عند التشييك على قيم العناصر التي يجب حذفها أم لا shouldBeRemoved هنا قمنا بتعريف دالة إسمها
                    bool shouldBeRemoved(const int& value)
                    {
                    // لإعلام المترجم أنه يجب حذف العنصر من الكائن الذي قام باستدعائها true هنا قلنا أن أي عنصر تكون قيمته أكبر من 3 سترجع الدالة
                    if (value > 3)
                    {
                    return true;
                    }

                    // لإعلام المترجم أنه يجب عدم حذف العنصر من الكائن الذي قام باستدعائها false في حال لم تكن قيمة العنصر أكبر من 3 سيتم إرجاع القيمة
                    return false;
                    }

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها forward_list هنا قمنا بتعريف كائن من الكلاس
                    forward_list<int> myList = {1, 0, 3, 2, 0, 0, 4, 2, 5, 0, 1, 5};

                    // هنا قمنا بحذف كل عنصر قيمته تساوي 0
                    // {1, 3, 2, 4, 2, 5, 1, 5} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.remove(0);

                    // لها shouldBeRemoved و تمرير إسم الدالة myList من الكائن remove_if() هنا قمنا باستدعاء الدالة
                    // shouldBeRemoved لإعلام المترجم أننا نريد التشييك على قيم العناصر التي نريد حذفها من خلال الدالة
                    // و هي من ستقرر ما إن كان يجب حذفه أم لا shouldBeRemoved سيتم تمريره للدالة myList إذاً كل عنصر  في الكائن
                    // {1, 3, 2, 2, 1} أصبح فيه القيم التالية myList إذاً الكائن
                    myList.remove_if(shouldBeRemoved);

                    cout << "List values = ";

                    // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
                    for (auto it = myList.begin(); it != myList.end(); ++it)
                    {
                    cout << *it << " ";
                    }

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    List values = 1 3 2 2 1
                  
_____

الكلاس list في C++

تعريف الكلاس list في C++

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

  • الكائن يملك خانة أساسية يقال لها Head و هي تشير لعنوان أول عنصر موجود فيه.

  • العنصر الأول لا يوجد أي عنصر قبله, لهذا يكون عنوان العنصر السابق بالنسبة له يساوي null للإشارة إلى أنه لا يوجد عنصر قبله.

  • العنصر الأخير لا يوجد أي عنصر بعده, لهذا يكون عنوان العنصر التالي بالنسبة له يساوي null للإشارة إلى أنه لا يوجد عنصر بعده.



لاستخدام الكلاس list - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<list> لأنه موجود فيه.


بناء الكلاس
      template < class T, class Alloc = allocator<T> >
      class list;
    

إذاً عند إنشاء كائن من الكلاس list يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس list في C++

الجدول التالي يحتوي على دوال الكلاس list التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس list التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
void resize(size_type n) تستخدم لحجز مساحة للعناصر التي تنوي تخزنيها لاحقاً في الكائن الذي قام باستدعائها.
عند تمرير قيمة للبارميتر n يجب أن لا تكون أصغر من عدد العناصر الموجودة في الكائن, لأنك إذا فعلت ذلك سيتم حذف العناصر التي لم يعد الكائن قادر على تخزينها.
كما أنها ترمي الإستثناء bad_alloc في حال قمت بتمرير قيمة أكبر من عدد العناصر الأقصى التي يمكن تخزينها في الكائن.

معلومة: هذه الدالة قد تكون مفيدة في حال أردت حجز مساحة أوليّة للعناصر قبل البدء بإضافتهم بدل جعل المترجم يفعل ذلك كلما أضفت عنصر جديد.
هذه المعلومة غير مذكورة في المرجع الرسمي للغة C++ لذا قد تكون غير دقيقة.

الجدول التالي يحتوي على دوال الكلاس list التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
reference front() تستخدم للوصول لقيمة أول عنصر موجود في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
reference back() تستخدم للوصول لقيمة آخر عنصر موجود في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.

الجدول التالي يحتوي على دوال الكلاس list التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void assign(initializer_list<T> aList) تستخدم لوضع مصفوفة من العناصر بدل العناصر الموجودة في الكائن الذي قام باستدعائها.
بشكل عام, يمكنك الإستفادة منها في حال أردت تخزين مجموعة من العناصر في الكائن بعد إنشائه.
مكان الباراميتر aList نمرر مصفوفة من العناصر التي تحتوي على قيم من نفس نوع الكائن الذي قام باستدعائها.
void emplace_front(T& val) تستخدم لإضافة عنصر في بداية الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في أول الكائن الذي قام باستدعائها.
void push_front(T& val) تستخدم لإضافة عنصر في بداية الكائن الذي قام باستدعائها تماماً كالدالة emplace_front().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في أول الكائن الذي قام باستدعائها.
void pop_front() تستخدم لحذف أول عنصر موجود في الكائن الذي قام باستدعائها.
void emplace_back(T& val) تستخدم لإضافة عنصر في آخر الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في آخر الكائن الذي قام باستدعائها.
void push_back(T& val) تستخدم لإضافة عنصر في آخر الكائن الذي قام باستدعائها تماماً كالدالة emplace_back().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته في أول الكائن الذي قام باستدعائها.
void pop_back() تستخدم لحذف آخر عنصر موجود في الكائن الذي قام باستدعائها.
iterator insert(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند عنوان محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان المكان الذي سيتم إضافة العنصر عنده في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(list& anotherList) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherList نمرر لها كائن من الكلاس list يملك نفس نوع عناصر الكائن الذي قام باستدعائها.
iterator splice(const_iterator position, list& anotherList) تستخدم لإضافة عناصر كائن من list في مكان محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يمثل عنوان العنصر الذي سيتم إضافة عناصر الكائن anotherList إبتداءاً من عنده.
مكان الباراميتر anotherList نمرر الكائن الذي نريد نسخ عناصره و الذي يجب أن تكون عناصره من نفس نوع عناصر الكائن الذي قام باستدعائها.
void remove(const T& val) تستخدم لحذف جميع العناصر الموجودة في الكائن الذي قام باستدعائها في حال كانت قيمتها تساوي القيمة التي نمررها لها مكان الباراميتر val.
void remove_if(Predicate pred) تستخدم هذه الدالة لحذف عناصر من الكائن الذي قام باستدعائها في حال كانت قيمه تطابق الشرط الذي نقوم بتمريره لها مكان الباراميتر pred.
الباراميتر pred الذي يجب أن نمرره لها سيكون عبارة عن دالة ترجع true في حال كان يجب حذف العنصر و false في حال لم يكن يجب ذلك.
void unique() تستخدم لحذف القيم المكررة من الكائن الذي قام باستدعائها.
بمعنى آخر, تقوم بحذف أي قيمة تجدها متكررة فيه و تترك نسخة واحدة منها.
معلومة: عند استخدام هذه الدالة يجب أن تكون القيم المكررة مرتبة وراء بعضها البعض و لفعل هذا الأمر قم فقط باستدعاء الدالة sort() من الكائن قبل استدعاء هذه الدالة.
void merge(list& anotherList) تستخدم لنقل عناصر كائن من list في آخر الكائن الذي قام باستدعائها.
عند نقل عناصر الكائن anotherList إلى الكائن الذي قام باستدعائها يصبح فارغاً.
void sort() تستخدم لترتيب عناصر الكائن الذي قام باستدعائها من الأصغر إلى الأكبر.
void reverse() تستخدم لترتيب عناصر الكائن الذي قام باستدعائها بشكل عكسي.

أمثلة شاملة حول التعامل مع الكلاس list في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من list مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 7 قيم فيه بطرق مختلفة و من ثم عرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة assign() لإضافة مجموعة من العناصر فيه دفعة واحدة, الدالة push_front() لإضافة عنصر واحد في بدايته و الدالة emplace_front() لإضافة عنصر واحد في بدايته أيضاً, الدالة push_back() لإضافة عنصر واحد في آخره و الدالة emplace_back() لإضافة عنصر واحد في آخره أيضاً. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        int main()
        {
        // int يمكنه أن يحتوي على قيم نوعها list هنا قمنا بتعريف كائن من الكلاس
        list<int> myList;

        // assign() باستخدام الدالة myList هنا قمنا بإضافة 3 عناصر أولية في الكائن
        // {3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.assign({3, 4, 5});

        // push_front() قيمته العدد 2 باستخدام الدالة myList هنا قمنا بإضافة عنصر في أول الكائن
        // {2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.push_front(2);

        // emplace_front() قيمته العدد 1 باستخدام الدالة myList هنا قمنا بإضافة عنصر في أول الكائن
        // {1, 2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.emplace_front(1);

        // push_back() قيمته العدد 6 باستخدام الدالة myList هنا قمنا بإضافة عنصر في آخر الكائن
        // {1, 2, 3, 4, 5, 6} أصبح فيه القيم التالية myList إذاً الكائن
        myList.push_back(6);

        // emplace_back() قيمته العدد 7 باستخدام الدالة myList هنا قمنا بإضافة عنصر في آخر الكائن
        // {1, 2, 3, 4, 5, 6, 7} أصبح فيه القيم التالية myList إذاً الكائن
        myList.emplace_back(7);

        cout << "List values = ";

        // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = myList.begin(); it != myList.end(); ++it)
        {
        cout << *it << " ";
        }

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        List values = 1 2 3 4 5 6 7
      


في المثال التالي قمنا بتعريف دالة إسمها printList() عند استدعائها نمرر لها كائن من list فتقوم بطباعة كل قيم عناصره حتى لا نقوم بتكرار نفس الكود كلما أردنا طباعة قيم عناصر الكائن.
بعدها قمنا بتعريف كائن من list مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه كما هي, ثم ترتيبها من الأصغر إلى الأكبر و عرضها, ثم ترتيبها بشكل عكسي و عرضها.

ملاحظة: قمنا باستخدام الدالة sort() لترتيب عناصر الكائن من الأصغر إلى الأكبر و الدالة reverse() لعكس ترتيب العناصر.

المثال الثاني
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        // فتقوم بطباعة القيم الموجودة فيه list عند استدعائها نمرر لها كائن من printList هنا قمنا بتعريف دالة إسمها
        void printList(list<int> &aList)
        {
        // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = aList.begin(); it != aList.end(); ++it)
        {
        cout << *it << " ";
        }
        }

        // main() هنا قمنا بتعريف الدالة
        int main()
        {
        // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها list هنا قمنا بتعريف كائن من الكلاس
        list<int> myList = {3, 5, 1, 4, 2};

        // كما أضفناها فيه myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
        cout << "List values\n";
        printList(myList);

        // من الأصغر إلى الأكبر myList لترتيب قيم عناصر الكائن sort() هنا قمنا باستدعاء الدالة
        myList.sort();

        // بعد أن قمنا بترتيبها myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
        cout << "\n\nAfter sorting list values\n";
        printList(myList);

        // myList لعكس ترتيب قيم عناصر الكائن sort() هنا قمنا باستدعاء الدالة
        myList.reverse();

        // بعد أن قمنا بترتيبها myList لعرض قيم عناصر الكائن printList() هنا قمنا باستدعاء الدالة
        cout << "\n\nAfter reverse list values\n";
        printList(myList);

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        List values
        3 5 1 4 2

        After sorting list values
        1 2 3 4 5

        After reverse list values
        5 4 3 2 1
      


في المثال التالي قمنا بتعريف كائنين من list في كل منهما 4 عناصر من النوع نوعها int.
بعدها قمنا بدمج الكائنين في كائن واحد حيث قمنا بنقل العناصر الموجودة في الكائن الثاني إلى الكائن الأول.
في النهاية قمنا بطباعة القيم التي أصبحت موجودة في الكائن الأول, و ما إن كان الكائن الثاني قد أصبح فارغاً أم لا.

ملاحظة: قمنا باستخدام الدالة merge() لنقل العناصر من كائن لآخر و الدالة empty() لمعرفة ما إن كان الكائن قد أصبح فارغاً أم لا.

المثال الثالث
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        int main()
        {
        // int يحتوي كل منهما على 4 عناصر نوعها list هنا قمنا بتعريف كائنين من الكلاس
        list<int> list1 = {1, 2, 3, 4};
        list<int> list2 = {5, 6, 7, 8};

        // list1 في آخر الكائن list2 هنا قمنا بنقل عناصر الكائن
        // {1, 2, 3, 4, 5, 6, 7, 8} أصبح فيه القيم التالية list1 إذاً الكائن
        list1.merge(list2);

        cout << "First list values = ";

        // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه list1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = list1.begin(); it != list1.end(); ++it)
        {
        cout << *it << " ";
        }

        // قد أصبح فارغاً أم لا list2 هنا قمنا بطباعة ما إن كان الكائن
        cout << "\nIs second list empty? " << ((list2.empty())? "Yes": "No");

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        First list values = 1 2 3 4 5 6 7 8
        Is second list empty? Yes
      


في المثال التالي قمنا بتعريف كائن من list مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بإزالة العناصر الموجودة فيه و التي تملك قيم مكررة.
في الأخير قمنا بعرض القيم الموجودة فيه كما هي.

ملاحظة: قمنا باستخدام الدالة sort() لترتيب عناصر الكائن من الأصغر إلى الأكبر و الدالة unique() من بعدها لإبقاء نسخة واحدة فقط من العناصر التي تملك نفس القيمة.

المثال الرابع
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        int main()
        {
        // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها list هنا قمنا بتعريف كائن من الكلاس
        list<int> myList = {1, 4, 2, 4, 5, 3, 1, 5};

        // من الأصغر إلى الأكبر حتى يتم وضع القيم المكررة وراء بعضها myList هنا قمنا بترتيب عناصر الكائن
        // {1, 1, 2, 3, 4, 4, 5, 5} أصبحت مرتبة كتالي myList إذاً عناصر الكائن
        myList.sort();

        // myList هنا قمنا بإبقاء نسخة واحدة من كل قيمة مكررة موجودة في الكائن
        // {1, 2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.unique();

        cout << "List values = ";

        // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = myList.begin(); it != myList.end(); ++it)
        {
        cout << *it << " ";
        }

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        List values = 1 2 3 4 5
      


في المثال التالي قمنا بتعريف كائن من list مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بحذف أي عنصر يملك قيمة تساوي 0 و من ثم قمنا بحذف أي قيمة أكبر من 3.
في الأخير قمنا بعرض القيم الموجودة فيه كما هي.

ملاحظة: قمنا باستخدام الدالة remove() لحذف العناصر التي تملك قيمة محددة ألا و هي 0 و الدالة remove_if() لحذف العناصر في حال كانت قيمتها أكبر من 3.

المثال الخامس
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        // سنستخدمها لاحقاً عند التشييك على قيم العناصر التي يجب حذفها أم لا shouldBeRemoved هنا قمنا بتعريف دالة إسمها
        bool shouldBeRemoved(const int& value)
        {
        // لإعلام المترجم أنه يجب حذف العنصر من الكائن الذي قام باستدعائها true هنا قلنا أن أي عنصر تكون قيمته أكبر من 3 سترجع الدالة
        if (value > 3)
        {
        return true;
        }

        // لإعلام المترجم أنه يجب عدم حذف العنصر من الكائن الذي قام باستدعائها false في حال لم تكن قيمة العنصر أكبر من 3 سيتم إرجاع القيمة
        return false;
        }

        // main() هنا قمنا بتعريف الدالة
        int main()
        {
        // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها list هنا قمنا بتعريف كائن من الكلاس
        list<int> myList = {1, 0, 3, 2, 0, 0, 4, 2, 5, 0, 1, 5};

        // هنا قمنا بحذف كل عنصر قيمته تساوي 0
        // {1, 3, 2, 4, 2, 5, 1, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.remove(0);

        // لها shouldBeRemoved و تمرير إسم الدالة myList من الكائن remove_if() هنا قمنا باستدعاء الدالة
        // shouldBeRemoved لإعلام المترجم أننا نريد التشييك على قيم العناصر التي نريد حذفها من خلال الدالة
        // و هي من ستقرر ما إن كان يجب حذفه أم لا shouldBeRemoved سيتم تمريره للدالة myList إذاً كل عنصر  في الكائن
        // {1, 3, 2, 2, 1} أصبح فيه القيم التالية myList إذاً الكائن
        myList.remove_if(shouldBeRemoved);

        cout << "List values = ";

        // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = myList.begin(); it != myList.end(); ++it)
        {
        cout << *it << " ";
        }

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        List values = 1 3 2 2 1
      




في المثال التالي قمنا بتعريف كائن من list مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بإزالة أول و آخر عنصر موجود فيه.
في الأخير قمنا بعرض القيم الموجودة فيه بشكل عكسي, أي من العنصر الأخير إلى العنصر الأول.

ملاحظة: قمنا باستخدام الدالة pop_front() لحذف عنصر من أول الكائن, الدالة pop_back() لحذف عنصر من آخر الكائن. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة rbegin() للحصول على مؤشر للعنصر الأخير لأننا سنبدأ من عنده و الدالة rend() للحصول على مؤشر للعنصر الأول لأننا سنتوقف عنده.

المثال السادس
main.cpp
        #include <iostream>
        #include <list>

        using namespace std;

        int main()
        {
        // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها list هنا قمنا بتعريف كائن من الكلاس
        list<int> myList = {1, 2, 3, 4, 5};

        // pop_front() بواسطة الدالة myList هنا قمنا بحذف العنصر الأول من الكائن
        // {2, 3, 4, 5} أصبح فيه القيم التالية myList إذاً الكائن
        myList.pop_front();

        // pop_back() بواسطة الدالة myList هنا قمنا بحذف العنصر الأخير من الكائن
        // {2, 3, 4} أصبح فيه القيم التالية myList إذاً الكائن
        myList.pop_back();

        cout << "List values in descending order = ";

        // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myList هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
        for (auto it = myList.rbegin(); it != myList.rend(); ++it)
        {
        cout << *it << " ";
        }

        return 0;
        }
      

سنحصل على النتيجة التالية عند التشغيل.

        List values in descending order = 4 3 2
      
____

الكلاس stack في C++

تعريف الكلاس stack في C++

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

طريقة تخزين العناصر في هذه الحاوية تعتمد أسلوب LIFO الذي هو اختصار لجملة Last In First out و التي تعني أن العنصر الأخير الذي يتم إضافته في الحاوية هو أول عنصر يمكن إخراجه منها كالتالي.


لاستخدام الكلاس stack - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<stack> لأنه موجود فيه.


بناء الكلاس
    template < class T, class Alloc = allocator<T> >
    class stack;
  

إذاً عند إنشاء كائن من الكلاس stack يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس stack في C++

الجدول التالي يحتوي على جميع دوال الكلاس stack.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
reference& top() تستخدم لتغيير قيمة العنصر الموجود في أعلى الكائن الذي قام باستدعائها أو للحصول على قيمته.
void push(T& val) تستخدم لإضافة عنصر جديد في أعلى الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void emplace(T& val) تستخدم لإضافة عنصر جديد في أعلى الكائن الذي قام باستدعائها تماماً كالدالة push().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void pop() تستخدم لحذف أعلى عنصر موجود في الكائن الذي قام باستدعائها.
void swap(stack& anotherStack) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherStack نمرر لها كائن من الكلاس stack يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

أمثلة شاملة حول التعامل مع الكلاس stack في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من stack مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع القيم الموجودة فيه من خلال حلقة تستمر في التنفيذ طالما أن الكائن غير فارغ; في كل دورة في الحلقة كنا نقوم بعرض قيمة العنصر الموجود في الأعلى و من ثم نحذفه حتى نتمكن من الوصول للعنصر الموجود بعده مباشرةً.

ملاحظة: قمنا باستخدام الدالة push() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة top() للحصول على قيمة العنصر الموجود في أعلى الكائن في كل مرة, الدالة pop() لحذف العنصر الموجود في أعلى الكائن و الدالة empty() لمعرفة ما إن كان الكائن يحتوي على عناصر بعد أم لا.

المثال الأول
main.cpp
      #include <iostream>
      #include <stack>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها stack هنا قمنا بتعريف كائن من الكلاس
      stack<int> myStack;

      // push() باستخدام الدالة myStack هنا قمنا بإضافة 5 عناصر في الكائن
      myStack.push(1);
      myStack.push(2);
      myStack.push(3);
      myStack.push(4);
      myStack.push(5);

      // size() باستخدام الدالة myStack هنا قمنا بطباعة عدد عناصر الكائن
      cout << "Stack size = " << myStack.size() << endl;

      cout << "Popping out stack elements:";

      // لا يزال يحتوي على عناصر myStack هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,myStack في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!myStack.empty())
      {
      cout << " " << myStack.top();
      myStack.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Stack size = 5
      Popping out stack elements: 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائنين من stack مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال الثاني
main.cpp
      #include <iostream>
      #include <stack>

      using namespace std;

      int main()
      {
      // int يمكنهما أن يحتويان على قيم نوعها stack هنا قمنا بتعريف كائنين من الكلاس
      stack<int> stack1;
      stack<int> stack2;

      // push() باستخدام الدالة stack1 هنا قمنا بإضافة 4 عناصر في الكائن
      stack1.push(1);
      stack1.push(2);
      stack1.push(3);
      stack1.push(4);

      // push() باستخدام الدالة stack2 هنا قمنا بإضافة 4 عناصر في الكائن
      stack2.push(5);
      stack2.push(6);
      stack2.push(7);
      stack2.push(8);

      // stack2 مع قيم الكائن stack1 هنا قمنا بتبديل قيم الكائن
      stack1.swap(stack2);

      cout << "Popping out stack1 elements:";

      // لا يزال يحتوي على عناصر stack1 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,stack1 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!stack1.empty())
      {
      cout << " " << stack1.top();
      stack1.pop();
      }

      cout << "\nPopping out stack2 elements:";

      // لا يزال يحتوي على عناصر stack2 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,stack2 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!stack2.empty())
      {
      cout << " " << stack2.top();
      stack2.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out stack1 elements: 8 7 6 5
      Popping out stack2 elements: 4 3 2 1
    
______

الكلاس queue في C++

تعريف الكلاس queue في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و كأنها موضوعة في نفق يمكن السير فيه باتجاه واحد, حيث أن العنصر يضاف فيها من جهة يمكن و من ثم يمكن إخراجه منها من الجهة المقابلة.

طريقة تخزين العناصر في هذه الحاوية تعتمد أسلوب FIFO الذي هو اختصار لجملة First In First out و التي تعني أن أول عنصر يتم إضافته في الحاوية هو أول عنصر يمكن إخراجه منها و كأنه يوجد جهة يتم منها إضافة العناصر و جهة يمكن إسترجاعها منها كالتالي.



لاستخدام الكلاس queue - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<queue> لأنه موجود فيه.


بناء الكلاس
    template < class T, class Alloc = allocator<T> >
    class queue;
  

إذاً عند إنشاء كائن من الكلاس queue يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس queue في C++

الجدول التالي يحتوي على جميع دوال الكلاس queue.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
reference& front() تستخدم لتغيير قيمة العنصر الأقدم في الكائن الذي قام باستدعائها أو للحصول على قيمته.
reference& back() تستخدم لتغيير قيمة العنصر الأحدث في الكائن الذي قام باستدعائها أو للحصول على قيمته.
void push(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة push().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void pop() تستخدم لحذف العنصر الأقدم من الكائن الذي قام باستدعائها.
void swap(queue& anotherQueue) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherQueue نمرر لها كائن من الكلاس queue يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

أمثلة شاملة حول التعامل مع الكلاس queue في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من queue مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة عدد العناصر التي قمنا بإضافتها, قيمة أقدم عنصر و قيمة أحدث عنصر.

ملاحظة: قمنا باستخدام الدالة push() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة front() للحصول على قيمة العنصر الأقدم في الكائن و الدالة back() للحصول على قيمة العنصر الأحدث فيه.

المثال الأول
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها queue هنا قمنا بتعريف كائن من الكلاس
      queue<int> myQueue;

      // push() باستخدام الدالة myQueue هنا قمنا بإضافة 5 عناصر في الكائن
      myQueue.push(1);
      myQueue.push(2);
      myQueue.push(3);
      myQueue.push(4);
      myQueue.push(5);

      // size() باستخدام الدالة myQueue هنا قمنا بطباعة عدد عناصر الكائن
      cout << "Stack size = " << myQueue.size() << endl;

      // هنا قمنا بطباعة القيمة العنصر الأقدم في الكائن, أي قيمة أول عنصر تم إضافته فيه
      cout << "Oldest value = " << myQueue.front() << endl;

      // هنا قمنا بطباعة القيمة العنسر الأحدث في الكائن, أي قيمة آخر عنصر تم إضافته فيه
      cout << "Newest value = " << myQueue.back();

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Stack size = 5
      Oldest value = 1
      Newest value = 5
    


في المثال التالي قمنا بتعريف كائن من queue مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم قمنا بعرض جميع القيم الموجودة فيه من خلال حلقة تستمر في التنفيذ طالما أن الكائن غير فارغ; في كل دورة في الحلقة كنا نقوم بعرض قيمة العنصر الأقدم فيه و من ثم نحذفه حتى نتمكن من الوصول للعنصر الموجود خلفه مباشرةً.

ملاحظة: قمنا باستخدام الدالة push() لإضافة العناصر, الدالة front() للحصول على قيمة العنصر الأقدم في الكائن في كل مرة, الدالة pop() لحذف العنصر الأقدم في الكائن و الدالة empty() لمعرفة ما إن كان الكائن يحتوي على عناصر بعد أم لا.

المثال الثاني
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها queue هنا قمنا بتعريف كائن من الكلاس
      queue<int> myQueue;

      // push() باستخدام الدالة myQueue هنا قمنا بإضافة 5 عناصر في الكائن
      myQueue.push(1);
      myQueue.push(2);
      myQueue.push(3);
      myQueue.push(4);
      myQueue.push(5);

      cout << "Popping out queue elements:";

      // لا يزال يحتوي على عناصر myQueue هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة في فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,myQueue في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!myQueue.empty())
      {
      cout << " " << myQueue.front();
      myQueue.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out queue elements: 1 2 3 4 5
    


في المثال التالي قمنا بتعريف كائنين من queue مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال الثالث
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنهما أن يحتويان على قيم نوعها queue هنا قمنا بتعريف كائنين من الكلاس
      queue<int> queue1;
      queue<int> queue2;

      // push() باستخدام الدالة queue1 هنا قمنا بإضافة 4 عناصر في الكائن
      queue1.push(1);
      queue1.push(2);
      queue1.push(3);
      queue1.push(4);

      // push() باستخدام الدالة queue2 هنا قمنا بإضافة 4 عناصر في الكائن
      queue2.push(5);
      queue2.push(6);
      queue2.push(7);
      queue2.push(8);

      // queue2 مع قيم الكائن queue1 هنا قمنا بتبديل قيم الكائن
      queue1.swap(queue2);

      cout << "Popping out queue1 elements:";

      // لا يزال يحتوي على عناصر queue1 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة في فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,queue1 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!queue1.empty())
      {
      cout << " " << queue1.front();
      queue1.pop();
      }

      cout << "\nPopping out queue2 elements:";

      // لا يزال يحتوي على عناصر queue2 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة في فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,queue2 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في أعلى الكائن
      while (!queue2.empty())
      {
      cout << " " << queue2.front();
      queue2.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out queue1 elements: 5 6 7 8
      Popping out queue2 elements: 1 2 3 4
    
_______

الكلاس priority_queue في C++

تعريف الكلاس priority_queue في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأكبر في البداية و العنصر الذي يملك القيمة الأصغر في النهاية, بالإضافة إلى أنه يسمح لك بالوصول لأعلى و أدنى قيمة فقط لأنها موجودة على أطراف الحاوية مع إمكانية حذف القيمة الأدنى منها فقط كالتالي.



الآن, عليك معرفة أنه بإمكانك تخصيص الطريقة التي يتم فيها ترتيب العناصر حتى تتناسب مع أنواع العناصر التي ستقوم بتخزينها لأنك على الأغلب لن تتعامل مع عناصر قيمتها int فقط. كمثال بسيط قد تكون العناصر التي تريد تخزينها عبارة كائنات من كلاس إسمه Product يمثل منتج, هنا يكون كل منتج يملك عدة معلومات مثل إسمه و سعره و تاريخ إنتاجه. في هذه الحالة يمكنك تخصيص الطريقة التي سيتم فيها ترتيب العناصر فمثلاً يمكنك جعل العناصر (التي تمثل منتجات) يتم ترتيبها على أساس أسعار المنتجات.


لاستخدام الكلاس priority_queue - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<queue> لأنه موجود فيه.


بناء الكلاس
    template < class T, class Alloc = allocator<T> >
    class priority_queue;
  

إذاً عند إنشاء كائن من الكلاس priority_queue يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس priority_queue في C++

الجدول التالي يحتوي على جميع دوال الكلاس priority_queue.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
reference& top() تستخدم لتغيير قيمة العنصر الموجود في آخر الكائن الذي قام باستدعائها أو للحصول على قيمته.
void push(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة push().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void pop() تستخدم لحذف آخر عنصر موجود في الكائن الذي قام باستدعائها.
void swap(priority_queue& anotherPriorityQueue) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherPriorityQueue نمرر لها كائن من الكلاس priority_queue يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

أمثلة شاملة حول التعامل مع الكلاس priority_queue في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من priority_queue مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع القيم الموجودة فيه من خلال حلقة تستمر في التنفيذ طالما أن الكائن غير فارغ; في كل دورة في الحلقة كنا نقوم بعرض قيمة العنصر الموجود في الأعلى و من ثم نحذفه حتى نتمكن من الوصول للعنصر الموجود بعده مباشرةً.

ملاحظة: قمنا باستخدام الدالة push() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة top() للحصول على قيمة العنصر الموجود في أعلى الكائن في كل مرة, الدالة pop() لحذف العنصر الموجود في أعلى الكائن و الدالة empty() لمعرفة ما إن كان الكائن يحتوي على عناصر بعد أم لا.

المثال الأول
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها priority_queue هنا قمنا بتعريف كائن من الكلاس
      priority_queue<int> pq;

      // push() باستخدام الدالة pq هنا قمنا بإضافة 5 عناصر في الكائن
      // {5, 4, 3, 2, 1} العناصر سيتم ترتيبها بشكل تلقائي من الأكبر إلى الأصغر كالتالي
      pq.push(3);
      pq.push(1);
      pq.push(2);
      pq.push(5);
      pq.push(4);

      // size() باستخدام الدالة pq هنا قمنا بطباعة عدد عناصر الكائن
      cout << "pq size = " << pq.size() << endl;

      cout << "Popping out pq elements:";

      // لا يزال يحتوي على عناصر pq هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,pq في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في آخر الكائن
      while (!pq.empty())
      {
      cout << " " << pq.top();
      pq.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      pq size = 5
      Popping out pq elements: 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من priority_queue مع تحديد أنه يمكن أن يحتوي على عناصر نوعها string.
إذاً في هذا الكائن سيكون كل كائن عبارة عن نص مما يعني أنه سيتم ترتيب أماكن العناصر على حسب عدد الأحرف الموجودة فيها. العنصر الذي يملك عدد أحرف أكبر سيتم وضعه بالأول.
بعدها قمنا بعرض جميع القيم الموجودة فيه بواسطة حلقة.

المثال الثاني
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // string يمكنه أن يحتوي على قيم نوعها priority_queue هنا قمنا بتعريف كائن من الكلاس
      priority_queue<string> pq;

      // push() باستخدام الدالة pq هنا قمنا بإضافة 5 عناصر في الكائن
      // {Strawberry, Blueberry, Banana, Apple} العناصر سيتم ترتيبها بشكل تلقائي من الأكبر إلى الأصغر كالتالي
      pq.push("Apple");
      pq.push("Banana");
      pq.push("Blueberry");
      pq.push("Strawberry");

      cout << "Popping out pq elements:";

      // لا يزال يحتوي على عناصر pq هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,pq في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في آخر الكائن
      while (!pq.empty())
      {
      cout << " " << pq.top();
      pq.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out pq elements: Strawberry Blueberry Banana Apple
    


في المثال التالي قمنا بتعريف كائنين من priority_queue مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال الثالث
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنهما أن يحتويان على قيم نوعها priority_queue هنا قمنا بتعريف كائنين من الكلاس
      priority_queue<int> pq1;
      priority_queue<int> pq2;

      // push() باستخدام الدالة pq1 هنا قمنا بإضافة 4 عناصر في الكائن
      // {4, 3, 2, 1} العناصر سيتم ترتيبها بشكل تلقائي من الأكبر إلى الأصغر كالتالي
      pq1.push(3);
      pq1.push(1);
      pq1.push(4);
      pq1.push(2);

      // push() باستخدام الدالة pq2 هنا قمنا بإضافة 4 عناصر في الكائن
      // {8, 7, 6, 5} العناصر سيتم ترتيبها بشكل تلقائي من الأكبر إلى الأصغر كالتالي
      pq2.push(5);
      pq2.push(6);
      pq2.push(8);
      pq2.push(7);

      // pq2 مع قيم الكائن pq1 هنا قمنا بتبديل قيم الكائن
      pq1.swap(pq2);

      cout << "Popping out pq1 elements:";

      // لا يزال يحتوي على عناصر pq1 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,pq1 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في آخر الكائن
      while (!pq1.empty())
      {
      cout << " " << pq1.top();
      pq1.pop();
      }

      cout << "\nPopping out pq2 elements:";

      // لا يزال يحتوي على عناصر pq2 هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,pq2 في كل دورة من دورات الحلقة سيتم طباعة قيمة العنصر الموجود في آخر الكائن
      while (!pq2.empty())
      {
      cout << " " << pq2.top();
      pq2.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out pq1 elements: 8 7 6 5
      Popping out pq2 elements: 4 3 2 1
    


في المثال التالي قمنا بتخصيص الطريقة التي سيقوم فيها الكائن الذي ننشئه من الكلاس priority_queue حتى يتناسب مع نوع العناصر التي سنقوم بتخزينها فيه.

في البداية قمنا بتعريف كلاس إسمه Book يمثل الشكل العام لأي كتاب و هو يحتوي على خاصيّتين و هما إسم الكتاب title و سعره price.

بعدها قمنا بإنشاء كلاس إسمه Compare سنستخدمه لنحدد الطريقة التي سيتم على أساسها ترتيب قيم العناصر.
في هذا الكلاس قمنا بإنشاء عامل ( Operator ) يملك باراميترين نوعهما Book لأن فكرة هذا العامل أنه يقارن أي عنصر جديد ( عبارة عن كائن من Book ) نقوم بإضافته مع أول عنصر موجود في الكائن. لذا الباراميتر الأول يعتبر العنصر الأول الموجود في الكائن و الباراميتر الثاني يعتبر العنصر الجديد الذي سيتم إضافته.
على هذا الأساس قمنا بتحديد أنه في حال كانت قيمة price في الباراميتر الأول أكبر من قيمة price في الباراميتر الثاني سيتم وضع العنصر الذي تم تمريره في الباراميتر الأول في بداية الكائن.

في الأخير قمنا بإنشاء ثلاث كائنات من الكلاس Book لأنها ستكون بمثابة العناصر التي سنقوم بتخزينها في الحاوية.
و من ثم قمنا بإنشاء كائن من priority_queue مع تحديد أنه سيتم تخزين عناصر نوعها Book فيه و أنه سيستخدم العامل الموجود في الكلاس Compare حتى يرتب العناصر التي نضيفها فيه و من بعدها أضفنا العناصر فيه, ثم قمنا بعرضها بواسطة حلقة.

المثال الرابع
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      // Book هنا قمنا بتعريف الكلاس
      class Book
      {
      public:
      // هنا قمنا بتعريف خصائص الكلاس
      string title;
      double price;

      // هنا قمنا بتعريف كونستكتور للكلاس حتى نستطيع تمرير قيم للخصائص بشكل مباشر عند إنشاء كائن منه
      Book(string title, double price)
      {
      this->title = title;
      this->price = price;
      }
      };

      // priority_queue و الذي سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Compare هنا قمنا بتعريف الكلاس
      class Compare
      {
      public:
      // price حتى يقارن قيمة priority_queue هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // الموجودة في أول عنصر فيها price في الكائن الذي سيتم إضافته في الحاوية مع قيمة
      bool operator() (Book b1, Book b2)
      {
      // الموجودة في العنصر الجديد أكبر من price هنا كأننا نعلم المترجم بأنه في حال كانت قيمة
      // الموجودة في العنصر الأول في الحاوية, فإننا نريد وضعها هي في البداية price قيمة
      return (b1.price > b2.price);
      }
      };

      // main() هنا قمنا بتعريف الدالة
      int main()
      {
      // بالإضافة إلى Book يمكنه أن يحتوي على كائنات من الكلاس priority_queue هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب الكائنات التي نضيفها فيه Compare أنه يعتمد على الكلاس 
      priority_queue<Book, vector<Book>, Compare> pq;

      // pq لأننا سنضعها كعناصر في الحاوية Book هنا قمنا بإنشاء 3 كائنات من الكلاس
      Book book1("C++ for beginner", 18);
      Book book2("HTML5 for dummies", 12);
      Book book3("Building Android apps", 25);

      // push() باستخدام الدالة pq في الكائن book3, book2 و book1 هنا قمنا بإضافة الكائنات
      // {book3, book1, book2} فيها كالتالي price العناصر سيتم ترتيبها من الأكبر إلى الأصغر على حسب قيمة الخاصية
      pq.push(book1);
      pq.push(book2);
      pq.push(book3);

      cout << "Popping out stored Books:\n";

      // لا يزال يحتوي على عناصر pq هنا قمنا بإنشاء حلقة تستمر في تنفيذ الأوامر الموجودة فيها طالما أن الكائن
      // بعدها سيتم حذفه مباشرةً ,pq في كل دورة من دورات الحلقة سيتم طباعة قيم العنصر الموجود في آخر الكائن
      while (!pq.empty())
      {
      cout << "- " << pq.top().title << " [" << pq.top().price << "$]\n";
      pq.pop();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Popping out stored Books:
      - HTML5 for dummies [12$]
      - C++ for beginner [18$]
      - Building Android apps [25$]
    
_____

الكلاس deque في C++

تعريف الكلاس deque في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل مع إعطاء كل عنصر منهم رقم Index مما يجعلك قادر على الوصول إلى جميع عناصرها و إضافة عناصر جديدة في أي مكان فيها, و يميز هذه الحاوية بأنك قادر على التعامل مع العناصر الموجودة على أطرافها بشكل مباشر كالتالي.



لاستخدام الكلاس deque - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<queue> لأنه موجود فيه.


بناء الكلاس
    template < class T, class Alloc = allocator<T> >
    class deque
  

إذاً عند إنشاء كائن من الكلاس deque يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس deque في C++

الجدول التالي يحتوي على دوال الكلاس deque التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الجهة اليسرى في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر في الجهة اليسرى و الوقوف عند آخر عنصر في الجهة اليمنى.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر بالنسبة للجهة اليسرى في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر في الجهة اليسرى و الوقوف عند آخر عنصر في الجهة اليمنى.
iterator rbegin() ترجع كائن iterator يشير لمكان أول عنصر في الجهة اليمنى في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من أول عنصر في الجهة اليمنى و الوقوف عند آخر عنصر في الجهة اليسرى.
iterator rend() ترجع كائن iterator يشير لمكان آخر عنصر بالنسبة للجهة اليمنى في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر في الجهة اليمنى و الوقوف عند آخر عنصر في الجهة اليسرى.

الجدول التالي يحتوي على دوال الكلاس deque التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
void resize(size_type n) تستخدم لحجز مساحة للعناصر التي تنوي تخزينها لاحقاً في الكائن الذي قام باستدعائها.
عند تمرير قيمة للبارميتر n يجب أن لا تكون أصغر من عدد العناصر الموجودة في الكائن, لأنك إذا فعلت ذلك سيتم حذف العناصر التي لم يعد الكائن قادر على تخزينها.
كما أنها ترمي الإستثناء bad_alloc في حال قمت بتمرير قيمة أكبر من عدد العناصر الأقصى التي يمكن تخزينها في الكائن.

معلومة: هذه الدالة قد تكون مفيدة في حال أردت حجز مساحة أوليّة للعناصر قبل البدء بإضافتهم بدل جعل المترجم يفعل ذلك كلما أضفت عنصر جديد.
هذه المعلومة غير مذكورة في المرجع الرسمي للغة C++ لذا قد تكون غير دقيقة.
void shrink_to_fit() تستخدم للتخلي عن أي مساحة زيادة مخصصة في الذاكرة للكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس deque التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
T at(size_t n) تستخدم للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر n نمرر لها رقم يمثل index العنصر الذي نريد الوصول إليه.
ترمي الإستثناء out_of_range في حال كانت قيمة n أصغر من 0 أو أكبر من عدد العناصر الموجودة.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير index غير موجود في الكائن.
T front() تستخدم للوصول لقيمة أول عنصر موجود في الجهة اليسرى في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
T back() تستخدم للوصول لقيمة أول عنصر موجود في الجهة اليمنى في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
قد تسبب مشكلة في حال كان الكائن لا يملك أي عنصر, لذلك يفضل التأكد من أن الكائن غير فارغ باستخدام الدالة empty() قبل استخدامها.
T* data() ترجع مؤشر لقيمة أول عنصر موجود في الجهة اليسرى في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس deque التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void assign(size_t n, const T& val) تستخدم لإضافة عدة عناصر في الكائن الذي قام باستدعائها و هي تحذف أي عناصر أخرى موجودة فيه في حال لم يكن فارغاً.
مكان الباراميتر n نمرر عدد العناصر التي نريد إضافتها في الكائن الذي قام باستدعائها.
مكان الباراميتر val نمرر القيمة التي نريد وضعها للعناصر التي سيتم إضافتها في الكائن الذي قام باستدعائها.
void push_back(T& val) تستخدم لإضافة عنصر جديد في آخر الكائن الذي قام باستدعائها, أي لإضافته في الجهة اليمنى.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void emplace_back(T& val) تستخدم لإضافة عنصر جديد في آخر الكائن الذي قام باستدعائها تماماً كالدالة push_back().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void push_front(T& val) تستخدم لإضافة عنصر جديد في أول الكائن الذي قام باستدعائها, أي لإضافته في الجهة اليسرى.
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void emplace_front(T& val) تستخدم لإضافة عنصر جديد في أول الكائن الذي قام باستدعائها تماماً كالدالة push_front().
مكان الباراميتر val نمرر العنصر الذي نريد إضافته.
void pop_back() تستخدم لحذف آخر عنصر موجود في الكائن الذي قام باستدعائها, أي لحذف أول عنصر من الجهة اليمنى.
void pop_front() تستخدم لحذف أول عنصر موجود في الكائن الذي قام باستدعائها, أي لحذف أول عنصر من الجهة اليسرى.
iterator insert(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند رقم Index محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان المكان الذي سيتم إضافة العنصر عنده في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
void clear() تستخدم لحذف جميع العناصر الموجودة في الكائن الذي قام باستدعائها.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(deque& anotherDeque) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherDeque نمرر لها كائن من الكلاس deque يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

أمثلة شاملة حول التعامل مع الكلاس deque في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من list مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 7 قيم فيه بطرق مختلفة و من ثم عرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة assign() لإضافة مجموعة من العناصر فيه دفعة واحدة, الدالة push_front() لإضافة عنصر واحد في بدايته و الدالة emplace_front() لإضافة عنصر واحد في بدايته أيضاً, الدالة push_back() لإضافة عنصر واحد في آخره و الدالة emplace_back() لإضافة عنصر واحد في آخره أيضاً.

المثال الأول
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<int> myDeque;

      // assign() باستخدام الدالة myDeque هنا قمنا بإضافة 3 عناصر أولية في الكائن
      // {3, 4, 5} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.assign({3, 4, 5});

      // push_front() قيمته العدد 2 باستخدام الدالة myDeque هنا قمنا بإضافة عنصر في أول الكائن
      // {2, 3, 4, 5} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.push_front(2);

      // emplace_front() قيمته العدد 1 باستخدام الدالة myDeque هنا قمنا بإضافة عنصر في أول الكائن
      // {1, 2, 3, 4, 5} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.emplace_front(1);

      // push_back() قيمته العدد 6 باستخدام الدالة myDeque هنا قمنا بإضافة عنصر في آخر الكائن
      // {1, 2, 3, 4, 5, 6} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.push_back(6);

      // emplace_back() قيمته العدد 7 باستخدام الدالة myDeque هنا قمنا بإضافة عنصر في آخر الكائن
      // {1, 2, 3, 4, 5, 6, 7} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.emplace_back(7);

      cout << "Deque values = ";

      // myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(size_t i=0; i<myDeque.size(); i++)
      {
      cout << myDeque[i] << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque values = 1 2 3 4 5 6 7
    


في المثال التالي قمنا بتعريف كائن من deque أدخلنا فيه 5 عناصر نصية.
بعدها قمنا بإضافة 5 قيم فيه و من ثم عرض عدد جميع عناصره, عدد جميع العناصر التي يمكن إضافتها فيه, بالإضافة لأول و آخر قيمة موجودة فيه.

ملاحظة: قمنا باستخدام الدالة size() لمعرفة عدد العناصر التي تم إضافتها, الدالة max_size() لمعرفة أكبر عدد عناصر يمكن إضافتها فيه, الدالة empty() لمعرفة ما إن كانت المصفوفة فارغة أم لا, الدالة front() للحصول على قيمة أول عنصر و الدالة back() للحصول على قيمة آخر عنصر.

المثال الثاني
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // string يمكنه أن يحتوي على 5 قيم نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<string> myDeque;

      // push_back() باستخدام الدالة myDeque هنا قمنا بإضافة 5 عناصر في الكائن
      myDeque.push_back("One");
      myDeque.push_back("Two");
      myDeque.push_back("Three");
      myDeque.push_back("Four");
      myDeque.push_back("Five");

      // size() باستخدام الدالة myDeque هنا قمنا بطباعة عدد عناصر الكائن
      cout << "Deque size = " << myDeque.size() << endl;

      // max_size() باستخدام الدالة myDeque هنا قمنا بطباعة عدد العناصر الأقصى التي يمكن تخزينها في الكائن
      cout << "Deque max size = " << myDeque.max_size() << endl;

      // و إن لم يكن فارغاً سيتم طباعة أول و آخر قيمة فيه empty() باستخدام الدالة myDeque هنا قمنا بالتشييك على عدد عناصر الكائن
      if (!myDeque.empty())
      {
      cout << "Deque first element value = " << myDeque.front() << endl;
      cout << "Deque last element value = " << myDeque.back();
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque size = 5
      Deque max size = 576460752303423487
      Deque first element value = One
      Deque last element value = Five
    


في المثال التالي قمنا بتعريف كائن من deque مخصص لتخزين قيم نوعها int مع إضافة 9 قيم فيه عند تعريفه.
بعدها قمنا بحذف عدة عناصر منه بطرق مختلفة و من ثم طباعة قيم العناصر المتبقية.

ملاحظة: قمنا باستخدام الدالة pop_back() لحذف آخر عنصر من الكائن, الدالة erase() لحذف عنصر واحد, الدالة erase() مرة ثانية لحذف أكثر من عنصر في وقت واحد و الدالة begin() للحصول على عدّاد يمكن من خلاله تحديد مكان العناصر التي سيتم حذفها.

المثال الثالث
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة 9 قيم فيه int يمكنه أن يحتوي على عناصر نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<int> myDeque = {1, 2, 3, 4, 5, 6, 7, 8, 9};

      // myDeque هنا قمنا بحذف آخر عنصر موجود في الكائن
      // {1, 2, 3, 4, 5, 6, 7, 8} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.pop_back();

      // myDeque يساوي 2 في الكائن index هنا قمنا بحذف العنصر الذي يملك
      // {1, 2, 4, 5, 6, 7, 8} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.erase(myDeque.begin() + 2);

      // يساوي 1 وصولاً إلى index إبتداءاً من العنصر الذي يملك myDeque هنا قمنا بحذف العناصر الموجودة في الكائن
      // {1, 6, 7, 8} أصبح فيه القيم التالية myDeque يساوي 4. و بالتالي الكائن index ما قبل العنصر الذي يملك
      myDeque.erase(myDeque.begin() + 1, myDeque.begin() + 4);

      cout << "Deque values = ";

      // myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(size_t i=0; i<myDeque.size(); i++)
      {
      cout << myDeque[i] << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque values = 1 6 7 8
    


في المثال التالي قمنا بتعريف كائن من deque مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بعرض جميع عناصرها مرتين, مرة من أول عنصر لآخر عنصر, و مرة من آخر عنصر لأول عنصر.

ملاحظة: عند البدء من أول عنصر لآخر عنصر, قمنا باستخدام الدالة begin() للحصول على عدّاد يمكن من خلاله البدئ من العنصر الأول و الدالة end() للحصول على قيمة العدّاد التي من خلالها نعرف أننا وصلنا لآخر عنصر.
و عند البدء من آخر عنصر لأول عنصر, قمنا باستخدام الدالة rbegin() للحصول على عدّاد يمكن من خلاله البدئ من آخر عنصر و الدالة rend() للحصول على قيمة العدّاد التي من خلالها نعرف أننا وصلنا لأول عنصر بدون الحاجة لتغيير شكل الحلقة التي استخدمناها في المرة الأولى.

المثال الرابع
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<int> myDeque = {1, 2, 3, 4, 5};

      cout << "Deque values from begin to end = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(auto it=myDeque.begin(); it<myDeque.end(); it++)
      {
      cout << *it << " ";
      }

      cout << "\nDeque values from end to begin = ";

      // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(auto it=myDeque.rbegin(); it<myDeque.rend(); it++)
      {
      cout << *it << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque values from begin to end = 1 2 3 4 5
      Deque values from end to begin = 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من deque مخصص لتخزين قيم نوعها int مع إضافة 5 قيم فيه عند تعريفه.
بعدها قمنا بتغيير قيم العناصر الموجودة على أطرافه, أي قيمة العنصر الأول و الأخير.
في الأخير قمنا بعرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة front() لتغيير قيمة العنصر الأول و الدالة back() لتغيير قيمة العنصر الأخير.

المثال الخامس
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<int> myDeque = {1, 2, 3, 4, 5};

      // إلى 11 myDeque هنا قمنا بتغيير قيمة العنصر الأول في الكائن
      myDeque.front() = 11;

      // إلى 55 myDeque هنا قمنا بتغيير قيمة العنصر الأخير في الكائن
      myDeque.back() = 55;

      cout << "Deque values = ";

      // myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(size_t i=0; i<myDeque.size(); i++)
      {
      cout << myDeque[i] << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque values = 11 2 3 4 55
    


في المثال التالي قمنا بتعريف كائن من deque مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة 5 قيم فيه وراء بعضها.
بعدها قمنا بإضافة قيمة في مكان محدد فيه.
في الأخير قمنا بعرض جميع قيمه باستخدام حلقة.

ملاحظة: قمنا باستخدام الدالة insert() لإضافة القيمة في مكان محدد في الكائن.

المثال السادس
main.cpp
      #include <iostream>
      #include <queue>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة 5 قيم فيه int يمكنه أن يحتوي على عناصر نوعها deque هنا قمنا بتعريف كائن من الكلاس
      deque<int> myDeque = {1, 2, 3, 4, 5};

      // myDeque رقم 2 بالنسبة للكائن index هنا قمنا بإضافة القيمة 8 في الـ
      // {1, 2, 8, 3, 4, 5} أصبح فيه القيم التالية myDeque إذاً الكائن
      myDeque.insert(myDeque.begin() + 2, 8); 

      cout << "Deque values = ";

      // myDeque هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for(size_t i=0; i<myDeque.size(); i++)
      {
      cout << myDeque[i] << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Deque values = 1 2 8 3 4 5
    
______

الكلاس set في C++

تعريف الكلاس set في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأصغر في البداية و العنصر الذي يملك القيمة الأكبر في النهاية أو العكس, مع الإشارة إلى أنه لا يمكن تخزين قيم مكررة فيها كالتالي.



الآن, عليك معرفة أن العناصر الموجودة في هذه الحاوية لا يمكن تعديل قيمها لأن ترتيب أماكن عناصرها يتم على أساس قيمها.
في حال أردت تعديل قيمة عنصر, فيجب أن تحذفه من الحاوية و تقوم بإجراء تعديل عليه و من بعدها تقوم بإضافته فيها من جديد.

كما أنك تستطيع تخصيص الطريقة التي يتم فيها ترتيب العناصر حتى تتناسب مع أنواع العناصر التي ستقوم بتخزينها لأنك على الأغلب لن تتعامل مع عناصر قيمتها int فقط. كمثال بسيط قد تكون العناصر التي تريد تخزينها عبارة كائنات من كلاس إسمه Product يمثل منتج, هنا يكون كل منتج يملك عدة معلومات مثل إسمه, رقم التعرفة الخاص به, سعره و تاريخ إنتاجه. في هذه الحالة يمكنك تخصيص الطريقة التي سيتم فيها ترتيب العناصر فمثلاً يمكنك جعل العناصر (التي تمثل منتجات) يتم ترتيبها على أساس رقم التعرفة الخاص بكل منتج.


لاستخدام الكلاس set - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<set> لأنه موجود فيه.


بناء الكلاس
    template < class T,                        // set::key_type/value_type
    class Compare = less<T>,        // set::key_compare/value_compare
    class Alloc = allocator<T>      // set::allocator_type
    > class set;
  

إذاً عند إنشاء كائن من الكلاس set يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس set في C++

الجدول التالي يحتوي على دوال الكلاس set التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس set التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس set التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
iterator insert(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند عنوان محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان المكان الذي سيتم إضافة العنصر عنده في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(set& anotherSet) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherSet نمرر لها كائن من الكلاس set يملك نفس نوع عناصر الكائن الذي قام باستدعائها.
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس set التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع القيمة 1 و في حال لم يتم إيجاد عنصر يملك نفس القيمة ترجع القيمة 0.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس set في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من set مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها set هنا قمنا بتعريف كائن من الكلاس
      set<int> mySet;

      // مع الإشارة إلى أننا تعمدنا إدخال نفس القيمة أكثر من مرة emplace() باستخدام الدالة mySet هنا حاولنا إضافة 7 عناصر في الكائن
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي من الأصغر إلى الأكبر كالتالي {5, 4, 3, 2, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      mySet.emplace(3);
      mySet.emplace(1);
      mySet.emplace(5);
      mySet.emplace(2);
      mySet.emplace(5);   // بما أنه يوجد القيمة 5 مسبقاً, لن يتم إضافتها من جديد
      mySet.emplace(4);
      mySet.emplace(1);   // بما أنه يوجد القيمة 1 مسبقاً, لن يتم إضافتها من جديد

      // size() باستخدام الدالة mySet هنا قمنا بطباعة عدد عناصر الكائن
      cout << "mySet size = " << mySet.size() << endl;

      cout << "mySet values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه mySet هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = mySet.begin(); it != mySet.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      mySet size = 5
      mySet values = 1 2 3 4 5
    


في المثال التالي قمنا بتعريف كائن من set مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة بشكل عكسي, أي من الآخر إلى الأول.

ملاحظة: قمنا باستخدام الدالة rbegin() للحصول على مؤشر للعنصر الأخير لأننا سنبدأ من عنده و الدالة rend() للحصول على مؤشر للعنصر الأول لأننا سنتوقف عنده.

المثال الثاني
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها set هنا قمنا بتعريف كائن من الكلاس
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي من الأصغر إلى الأكبر كالتالي {5, 4, 3, 2, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      set<int> mySet = {1, 4, 2, 4, 5, 3, 1, 5};

      cout << "mySet values =";

      // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه mySet هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = mySet.rbegin(); it != mySet.rend(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      mySet values = 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من set مخصص لتخزين قيم نوعها int بشكل تنازلي (أي من الأكبر إلى الأصغر) مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثالث

main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها set هنا قمنا بتعريف كائن من الكلاس
      // من أجل ترتيب القيم greater قيم هذا الكائن سيتم ترتيبها من الأكبر إلى الأصغر و سبب ذلك أننا جعلناه يعتمد على الكلاس
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي كالتالي {5, 4, 3, 2, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      set<int, greater<int>> mySet = {1, 4, 2, 4, 5, 3, 1, 5};

      cout << "mySet values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه mySet هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = mySet.begin(); it != mySet.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      mySet values = 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من set مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه لمعرفة ما إن كانت موجودة فيه أم لا.

ملاحظة: قمنا باستخدام الدالة count() للبحث عن القيمة في الكائن.

المثال الرابع
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها set هنا قمنا بتعريف كائن من الكلاس
      set<int> mySet = {1, 2, 3, 4, 5};

      // يوجد فيه عنصر قيمته تساوي 4 mySet هنا قمنا بطباعة ما إن كان الكائن
      if (mySet.count(4))
      {
      // إن كان فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "4 is exist in myset";
      }
      else
      {
      // إن لم يكن فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "4 is not exist in myset";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      4 is exist in myset
    


في المثال التالي قمنا بتعريف كائن من set مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن القيمة في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال الخامس
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها set هنا قمنا بتعريف كائن من الكلاس
      set<int> mySet = {1, 2, 3, 4, 5};

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه set<int>::iterator منا قمنا بتعريف كائن من 
      set<int>::iterator it;

      // it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن mySet هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة mySet.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها
      it = mySet.find(4);

      // فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها mySet.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != mySet.end())
      {
      // و من ثم قمنا بحذفه mySet هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن
      cout << "element with value '"<< *it << "' is removed from mySet\n";
      mySet.erase(it);
      }

      cout << "mySet values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه mySet هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = mySet.begin(); it != mySet.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      element with value '4' is removed from mySet
      mySet values = 1 2 3 5
    


في المثال التالي قمنا بتعريف كائنين من set مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال السادس
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها set هنا قمنا بتعريف كائنين من الكلاس
      set<int> set1 = {1, 2, 3, 4};
      set<int> set2 = {5, 6, 7, 8};

      // set2 مع قيم الكائن set1 هنا قمنا بتبديل قيم الكائن
      set1.swap(set2);

      cout << "set1 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه set1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = set1.begin(); it != set1.end(); ++it)
      {
      cout << *it << " ";
      }

      cout << "\nset2 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه set2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = set2.begin(); it != set2.end(); ++it)
      {
      cout << *it << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      set1 values = 5 6 7 8
      set2 values = 1 2 3 4
    


في المثال التالي قمنا بتخصيص الطريقة التي سيقوم فيها الكائن الذي ننشئه من الكلاس set حتى يتناسب مع نوع العناصر التي سنقوم بتخزينها فيه.

في البداية قمنا بتعريف كلاس إسمه Book يمثل الشكل العام لأي كتاب و هو يحتوي على خاصيّتين و هما إسم الكتاب title و رقم التعرفة id الخاص به.

بعدها قمنا بإنشاء كلاس إسمه Compare سنستخدمه لنحدد الطريقة التي سيتم على أساسها ترتيب قيم العناصر.
في هذا الكلاس قمنا بإنشاء عامل ( Operator ) يملك باراميترين نوعهما Book لأن فكرة هذا العامل أنه يقارن أي عنصر جديد ( عبارة عن كائن من Book ) نقوم بإضافته مع أول عنصر موجود في الكائن. لذا الباراميتر الأول يعتبر العنصر الأول الموجود في الكائن و الباراميتر الثاني يعتبر العنصر الجديد الذي سيتم إضافته.
على هذا الأساس قمنا بتحديد أنه في حال كانت قيمة id في الباراميتر الأول أصغر من قيمة id في الباراميتر الثاني سيتم وضع العنصر الذي تم تمريره في الباراميتر الأول في بداية الكائن.

في الأخير قمنا بإنشاء ثلاث كائنات من الكلاس Book لأنها ستكون بمثابة العناصر التي سنقوم بتخزينها في الحاوية.
و من ثم قمنا بإنشاء كائن من set مع تحديد أنه سيتم تخزين عناصر نوعها Book فيه و أنه سيستخدم العامل الموجود في الكلاس Compare حتى يرتب العناصر التي نضيفها فيه و من بعدها أضفنا العناصر فيه, ثم قمنا بعرضها بواسطة حلقة.

ملاحظة: قمنا باستخدام الحلقة Foreach عند عرض قيم العناصر.

المثال السابع
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      // Book هنا قمنا بتعريف الكلاس
      class Book
      {
      public:
      // هنا قمنا بتعريف خصائص الكلاس
      int id;
      string title;

      // هنا قمنا بتعريف كونستكتور للكلاس حتى نستطيع تمرير قيم للخصائص بشكل مباشر عند إنشاء كائن منه
      Book(int id, string title)
      {
      this->id = id;
      this->title = title;
      }
      };

      // set و الذي سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Compare هنا قمنا بتعريف الكلاس
      class Compare
      {
      public:
      // id حتى يقارن قيمة set هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // الموجودة في أول عنصر فيها id في الكائن الذي سيتم إضافته في الحاوية مع قيمة
      bool operator() (Book b1, Book b2)
      {
      // الموجودة في العنصر الجديد أصغر من id هنا كأننا نعلم المترجم بأنه في حال كانت قيمة
      // الموجودة في العنصر الأول في الحاوية, فإننا نريد وضعها هي في البداية id قيمة
      return (b1.id < b2.id);
      }
      };

      // main() هنا قمنا بتعريف الدالة
      int main()
      {
      // بالإضافة إلى Book يمكنه أن يحتوي على كائنات من الكلاس set هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب الكائنات التي نضيفها فيه Compare أنه يعتمد على الكلاس 
      set<Book, Compare> mySet;

      // mySet لأننا سنضعها كعناصر في الحاوية Book هنا قمنا بإنشاء 3 كائنات من الكلاس
      Book book1(2, "C++ for beginner");
      Book book2(1, "HTML5 for dummies");
      Book book3(3, "Building Android apps");

      // emplace() باستخدام الدالة mySet في الكائن book3, book2 و book1 هنا قمنا بإضافة الكائنات
      // {book2, book1, book3} فيها كالتالي id العناصر سيتم ترتيبها من الأسغر إلى الأكبر على حسب قيمة الخاصية
      mySet.emplace(book1);
      mySet.emplace(book2);
      mySet.emplace(book3);

      cout << "All books in mySet:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه mySet هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto const &e: mySet)
      {
      cout << e.id << " - " << e.title << endl;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      All books in mySet:
      1 - HTML5 for dummies
      2 - C++ for beginner
      3 - Building Android apps
    
_______

الكلاس multiset في C++

تعريف الكلاس multiset في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل متسلسل وراء بعضها البعض و بترتيب معين حيث يتم وضع العنصر الذي يملك القيمة الأصغر في البداية و العنصر الذي يملك القيمة الأكبر في النهاية أو العكس, مع الإشارة إلى أنه يمكن تخزين قيم مكررة فيها كالتالي.



الآن, عليك معرفة أن العناصر الموجودة في هذه الحاوية لا يمكن تعديل قيمها لأن ترتيب أماكن عناصرها يتم على أساس قيمها.
في حال أردت تعديل قيمة عنصر, فيجب أن تحذفه من الحاوية و تقوم بإجراء تعديل عليه و من بعدها تقوم بإضافته فيها من جديد.

كما أنك تستطيع تخصيص الطريقة التي يتم فيها ترتيب العناصر حتى تتناسب مع أنواع العناصر التي ستقوم بتخزينها لأنك على الأغلب لن تتعامل مع عناصر قيمتها int فقط. كمثال بسيط قد تكون العناصر التي تريد تخزينها عبارة كائنات من كلاس إسمه Product يمثل منتج, هنا يكون كل منتج يملك عدة معلومات مثل إسمه, رقم التعرفة الخاص به, سعره و تاريخ إنتاجه. في هذه الحالة يمكنك تخصيص الطريقة التي سيتم فيها ترتيب العناصر فمثلاً يمكنك جعل العناصر (التي تمثل منتجات) يتم ترتيبها على أساس رقم التعرفة الخاص بكل منتج.


لاستخدام الكلاس multiset - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<set> لأنه موجود فيه.


بناء الكلاس
    template < class T,                        // multiset::key_type/value_type
    class Compare = less<T>,        // multiset::key_compare/value_compare
    class Alloc = allocator<T>      // multiset::allocator_type
    > class multiset;
  

إذاً عند إنشاء كائن من الكلاس multiset يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر T.

دوال الكلاس multiset في C++

الجدول التالي يحتوي على دوال الكلاس multiset التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس multiset التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس multiset التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
iterator insert(const_iterator position, T& val) تستخدم لإضافة عنصر جديد في مكان محدد - أي عند عنوان محدد - في الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان المكان الذي سيتم إضافة العنصر عنده في الذاكرة.
مكان الباراميتر val نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع كائن أصله من Iterator يمثل عنوان العنصر الذي تم إضافته.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(multiset& anotherSet) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherSet نمرر لها كائن من الكلاس multiset يملك نفس نوع عناصر الكائن الذي قام باستدعائها.
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس multiset التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عدد العناصر التي تملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن أول عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس multiset في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من multiset مع تحديد أنه يمكن أن يحتوي على عناصر نوعها int.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // int يمكنه أن يحتوي على قيم نوعها multiset هنا قمنا بتعريف كائن من الكلاس
      multiset<int> myMultiset;

      // مع الإشارة إلى أننا تعمدنا إدخال نفس القيمة أكثر من مرة emplace() باستخدام الدالة myMultiset هنا قمنا بإضافة 7 عناصر في الكائن
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي من الأصغر إلى الأكبر كالتالي {5, 5, 4, 3, 2, 1, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      myMultiset.emplace(3);
      myMultiset.emplace(1);
      myMultiset.emplace(5);
      myMultiset.emplace(2);
      myMultiset.emplace(5);
      myMultiset.emplace(4);
      myMultiset.emplace(1);

      // size() باستخدام الدالة myMultiset هنا قمنا بطباعة عدد عناصر الكائن
      cout << "myMultiset size = " << myMultiset.size() << endl;

      cout << "myMultiset values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultiset هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultiset.begin(); it != myMultiset.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      myMultiset size = 7
      myMultiset values = 1 1 2 3 4 5 5
    


في المثال التالي قمنا بتعريف كائن من multiset مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة بشكل عكسي, أي من الآخر إلى الأول.

ملاحظة: قمنا باستخدام الدالة rbegin() للحصول على مؤشر للعنصر الأخير لأننا سنبدأ من عنده و الدالة rend() للحصول على مؤشر للعنصر الأول لأننا سنتوقف عنده.

المثال الثاني
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها multiset هنا قمنا بتعريف كائن من الكلاس
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي من الأصغر إلى الأكبر كالتالي {5, 4, 3, 2, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      multiset<int> myMultiset = {1, 4, 2, 5, 3};

      cout << "myMultiset values =";

      // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myMultiset هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultiset.rbegin(); it != myMultiset.rend(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      myMultiset values = 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من multiset مخصص لتخزين قيم نوعها int بشكل تنازلي (أي من الأكبر إلى الأصغر) مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثالث
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها multiset هنا قمنا بتعريف كائن من الكلاس
      // من أجل ترتيب القيم greater قيم هذا الكائن سيتم ترتيبها من الأكبر إلى الأصغر و سبب ذلك أننا جعلناه يعتمد على الكلاس
      // العناصر التي أضفناها سيتم ترتيبها بشكل تلقائي كالتالي {5, 4, 3, 2, 1} و ستلاحظ ذلك لاحقاً عند عرض قيمهم
      multiset<int, greater<int>> myMultiset = {1, 4, 2, 5, 3};

      cout << "myMultiset values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultiset هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultiset.begin(); it != myMultiset.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      myMultiset values = 5 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من multiset مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيم محددة فيه لمعرفة كم عنصر يملك هذه القيم.

ملاحظة: قمنا باستخدام الدالة count() لمعرفة كم مرة القيم مكررة في الكائن.

المثال الرابع
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها multiset هنا قمنا بتعريف كائن من الكلاس
      multiset<int> myMultiset = {1, 4, 2, 3, 4, 5, 2, 4, 7};

      // التي تملك القيمة 4 myMultiset هنا قمنا بطباعة عدد العناصر الموجودة في الكائن
      cout << "4 is exist " << myMultiset.count(4)  << " time(s)\n";

      // التي تملك القيمة 9 myMultiset هنا قمنا بطباعة عدد العناصر الموجودة في الكائن
      cout << "9 is exist " << myMultiset.count(9)  << " time(s)";

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      4 is exist 3 time(s)
      9 is exist 0 time(s)
    


في المثال التالي قمنا بتعريف كائن من multiset مخصص لتخزين قيم نوعها int مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن القيمة في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال الخامس
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه int يمكنه أن يحتوي على عناصر نوعها multiset هنا قمنا بتعريف كائن من الكلاس
      multiset<int> myMultiset = {1, 2, 3, 4, 5};

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه multiset<int>::iterator منا قمنا بتعريف كائن من 
      multiset<int>::iterator it;

      // it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن myMultiset هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة myMultiset.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها
      it = myMultiset.find(4);

      // فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها myMultiset.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != myMultiset.end())
      {
      // و من ثم قمنا بحذفه myMultiset هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن
      cout << "element with value '"<< *it << "' is removed from myMultiset\n";
      myMultiset.erase(it);
      }

      cout << "myMultiset values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultiset هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultiset.begin(); it != myMultiset.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      element with value '4' is removed from myMultiset
      myMultiset values = 1 2 3 5
    


في المثال التالي قمنا بتعريف كائنين من multiset مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال السادس
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها multiset هنا قمنا بتعريف كائنين من الكلاس
      multiset<int> multiset1 = {1, 2, 3, 4};
      multiset<int> multiset2 = {5, 6, 7, 8};

      // multiset2 مع قيم الكائن multiset1 هنا قمنا بتبديل قيم الكائن
      multiset1.swap(multiset2);

      cout << "multiset1 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه multiset1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = multiset1.begin(); it != multiset1.end(); ++it)
      {
      cout << *it << " ";
      }

      cout << "\nmultiset2 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه multiset2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = multiset2.begin(); it != multiset2.end(); ++it)
      {
      cout << *it << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      multiset1 values = 5 6 7 8
      multiset2 values = 1 2 3 4
    


في المثال التالي قمنا بتخصيص الطريقة التي سيقوم فيها الكائن الذي ننشئه من الكلاس multiset حتى يتناسب مع نوع العناصر التي سنقوم بتخزينها فيه.

في البداية قمنا بتعريف كلاس إسمه Book يمثل الشكل العام لأي كتاب و هو يحتوي على خاصيّتين و هما إسم الكتاب title و رقم التعرفة id الخاص به.

بعدها قمنا بإنشاء كلاس إسمه Compare سنستخدمه لنحدد الطريقة التي سيتم على أساسها ترتيب قيم العناصر.
في هذا الكلاس قمنا بإنشاء عامل ( Operator ) يملك باراميترين نوعهما Book لأن فكرة هذا العامل أنه يقارن أي عنصر جديد ( عبارة عن كائن من Book ) نقوم بإضافته مع أول عنصر موجود في الكائن. لذا الباراميتر الأول يعتبر العنصر الأول الموجود في الكائن و الباراميتر الثاني يعتبر العنصر الجديد الذي سيتم إضافته.
على هذا الأساس قمنا بتحديد أنه في حال كانت قيمة id في الباراميتر الأول أصغر من قيمة id في الباراميتر الثاني سيتم وضع العنصر الذي تم تمريره في الباراميتر الأول في بداية الكائن.

في الأخير قمنا بإنشاء ثلاث كائنات من الكلاس Book لأنها ستكون بمثابة العناصر التي سنقوم بتخزينها في الحاوية.
و من ثم قمنا بإنشاء كائن من multiset مع تحديد أنه سيتم تخزين عناصر نوعها Book فيه و أنه سيستخدم العامل الموجود في الكلاس Compare حتى يرتب العناصر التي نضيفها فيه و من بعدها أضفنا العناصر فيه, ثم قمنا بعرضها بواسطة حلقة.

ملاحظة: قمنا باستخدام الحلقة Foreach عند عرض قيم العناصر.

المثال السابع
main.cpp
      #include <iostream>
      #include <set>

      using namespace std;

      // Book هنا قمنا بتعريف الكلاس
      class Book
      {
      public:
      // هنا قمنا بتعريف خصائص الكلاس
      int id;
      string title;

      // هنا قمنا بتعريف كونستكتور للكلاس حتى نستطيع تمرير قيم للخصائص بشكل مباشر عند إنشاء كائن منه
      Book(int id, string title)
      {
      this->id = id;
      this->title = title;
      }
      };

      // multiset و الذي سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Compare هنا قمنا بتعريف الكلاس
      class Compare
      {
      public:
      // id حتى يقارن قيمة multiset هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // الموجودة في أول عنصر فيها id في الكائن الذي سيتم إضافته في الحاوية مع قيمة
      bool operator() (Book b1, Book b2)
      {
      // الموجودة في العنصر الجديد أصغر من id هنا كأننا نعلم المترجم بأنه في حال كانت قيمة
      // الموجودة في العنصر الأول في الحاوية, فإننا نريد وضعها هي في البداية id قيمة
      return (b1.id < b2.id);
      }
      };

      // main() هنا قمنا بتعريف الدالة
      int main()
      {
      // بالإضافة إلى Book يمكنه أن يحتوي على كائنات من الكلاس multiset هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب الكائنات التي نضيفها فيه Compare أنه يعتمد على الكلاس 
      multiset<Book, Compare> myMultiset;

      // myMultiset لأننا سنضعها كعناصر في الحاوية Book هنا قمنا بإنشاء 3 كائنات من الكلاس
      Book book1(2, "C++ for beginner");
      Book book2(1, "HTML5 for dummies");
      Book book3(3, "Building Android apps");

      // emplace() باستخدام الدالة myMultiset في الكائن book3, book2 و book1 هنا قمنا بإضافة الكائنات
      // {book2, book1, book3} فيها كالتالي id العناصر سيتم ترتيبها من الأسغر إلى الأكبر على حسب قيمة الخاصية
      myMultiset.emplace(book1);
      myMultiset.emplace(book2);
      myMultiset.emplace(book3);

      cout << "All books in myMultiset:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultiset هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto const &e: myMultiset)
      {
      cout << e.id << " - " << e.title << endl;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      All books in myMultiset:
      1 - HTML5 for dummies
      2 - C++ for beginner
      3 - Building Android apps
    
_______

الكلاس map في C++

تعريف الكلاس map في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بشكل تصاعدي على حسب قيم المفاتيح كالتالي.



القيمة المفتاحية لكل عنصر يمكن أن تكون من أي نوع تريده, و لكن في أغلب الأوقات ستكون المفاتيح عبارة عن أعداد أو كلمات.
لا تستطيع وضع نفس القيمة المفتاحية لأكثر من عنصر و هذا شيء منطقي لأن المفاتيح هي ما يميز العناصر عن بعضها.


لاستخدام الكلاس map - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<map> لأنه موجود فيه.


بناء الكلاس
    template < class Key,                                     // map::key_type
    class T,                                       // map::mapped_type
    class Compare = less<Key>,                     // map::key_compare
    class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
    > class map;
  

إذاً عند إنشاء كائن من الكلاس map يجب أن نمرر له نوع المفاتيح التي نريد تخزينها فيه مكان الباراميتر Key و نوع القيم التي نريد تخزينها فيه مكان الباراميتر T.

دوال الكلاس map في C++

الجدول التالي يحتوي على دوال الكلاس map التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس map التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس map التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
mapped_type& at(key_type& k) تستخدم للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
ترمي الإستثناء out_of_range في حال كانت قيمة k لا تمثل القيمة المفتاحية لأي عنصر موجود في الكائن.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير قيمة مفتاح غير موجود في الكائن.
operator[] (key_type& k) يستخدم العامل [] للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
في حال تمرير قيمة مفتاح غير موجود مكان الباراميتر k سيتم إضافة العنصر كعنصر جديد في الكائن.

الجدول التالي يحتوي على دوال الكلاس map التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
pair<K, V> insert({key, value}) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر key نمرر القيمة المفتاحية للعنصر الذي نريد إضافته.
مكان الباراميتر value نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
pair<K, V> emplace(Args&&... args) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة insert().
مكان الباراميتر Args نمرر القيمة المفتاحية للعنصر الذي نريد إضافته ثم نضع فاصلة و نمرر القيمة التي نريد وضعها فيه.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
size_t erase(key_type& k) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال المفتاح الخاص به.
مكان الباراميتر k نمرر مفتاح العنصر الذي نريد حذفه.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكانه فيه.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكان وجود هذه العناصر.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(map& anotherMap) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherMap نمرر لها كائن من الكلاس map يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس map التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع القيمة 1 و في حال لم يتم إيجاد عنصر يملك نفس القيمة ترجع القيمة 0.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس map في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن كود الإتصال الخاص بالبلد و إسم البلد.
بعدها قمنا بطباعة عدد العناصر التي قمنا بإضافتها.
في الأخير قمنا بعرض جميع مفاتيح و قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<string, string> myMap;

      // كل عنصر يحتوي على كود البلد كمفتاح و إسمه كقيمة ,myMap هنا قمنا بإضافة 12 عنصر في الكائن
      myMap.emplace("+961", "Lebanon");
      myMap.emplace("+962", "Jordan");
      myMap.emplace("+963", "Syria");
      myMap.emplace("+964", "Iraq");
      myMap.emplace("+965", "Kuwait");
      myMap.emplace("+966", "KSA");
      myMap.emplace("+967", "Yaman");
      myMap.emplace("+968", "Oman");
      myMap.emplace("+970", "Palestine");
      myMap.emplace("+212", "Morocco");
      myMap.emplace("+281", "Libya");
      myMap.emplace("+20",  "Egypt");

      // size() باستخدام الدالة myMap هنا قمنا بطباعة عدد عناصر الكائن
      cout << "myMap size = " << myMap.size() << "\n\n";

      cout << "myMap keys and values\n";
      cout << "---------------------\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMap.begin(); it != myMap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << "\t | " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      myMap size = 12

      myMap keys and values
      ---------------------
      +20      | Egypt
      +212     | Morocco
      +281     | Libya
      +961     | Lebanon
      +962     | Jordan
      +963     | Syria
      +964     | Iraq
      +965     | Kuwait
      +966     | KSA
      +967     | Yaman
      +968     | Oman
      +970     | Palestine
    


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثاني
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> myMap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMap.begin(); it != myMap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      1 - One
      2 - Two
      3 - Three
      4 - Four
      5 - Five
    


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بعرض القيم الموجودة فيه بشكل عكسي بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة rbegin() للحصول على مؤشر للعنصر الأخير لأننا سنبدأ من عنده و الدالة rend() للحصول على مؤشر للعنصر الأول لأننا سنتوقف عنده.

المثال الثالث
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> myMap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myMap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMap.rbegin(); it != myMap.rend(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      5 - Five
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بالبحث عن عنصر يملك مفتاح محدد لمعرفة ما إن كان يوجد عنصر يملك هذا المفتاح أم لا مع الإشارة إلى أنه في حال وجود عنصر يملك هذا المفتاح سيتم طباعة قيمته أيضاً.

ملاحظة: قمنا باستخدام الدالة count() للبحث عن عنصر يملك مفتاح محدد في الكائن و قمنا باستخدام الدالة at() لطباعة قيمة العنصر الذي يملك مفتاح محدد.

المثال الرابع
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> myMap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // يوجد فيه عنصر يملك مفتاح يساوي 4 أم لا myMap هنا قمنا بطباعة ما إن كان الكائن
      if (myMap.count(4))
      {
      // إن كان فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "Element with key '4' is exist and it's value = " << myMap.at(4);
      }
      else
      {
      // إن لم يكن فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "There is no element with key = 4";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '4' is exist and it's value = Four
    


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بحذف عنصر منه بالإعتماد على قيمة مفتاحه و من ثم طباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة erase() لحذف العنصر.

المثال الخامس
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> myMap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"}
      };

      // myMap هنا قمنا بحذف العنصر الذي يملك مفتاح يساوي 3 في الكائن
      myMap.erase(3);

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMap.begin(); it != myMap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      1 - One
      2 - Two
      4 - Four
      5 - Five
    


في المثال التالي قمنا بتعريف كائن من map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بالبحث عن عنصر يملك مفتاح محدد فيه و في حال وجوده سيتم حذف العنصر من الكائن.
في الأخير قمنا بطباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن العنصر الذي يملك المفتاح في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال السادس
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> myMap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"}
      };

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه map<int, string>::iterator منا قمنا بتعريف كائن من 
      map<int, string>::iterator it;

      // it عن عنصر يملك مفتاح يساوي 4, و بالتالي في حال وجود عنصر يملك هذا المفتاح سيتم تخزين عنوانه في الكائن myMap هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذا المفتاح myMap.end() في حال عدم وجود عنصر يملك مفتاح يساوي 4 سيتم تخزين القيمة التي ترجعها
      it = myMap.find(4);

      // فهذا يعني أنه تم إيجاد العنصر و بالتالي سيتم تنفيذ الكود الموضوع بالداخل myMap.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != myMap.end())
      {
      // و من ثم قمنا بحذفه myMap هنا قمنا بطباعة مفتاح العنصر الذي تم إيجاده في الكائن
      cout << "Element with key '"<< it->first << "' is removed from myMap\n\n";
      myMap.erase(it);
      }

      cout << "myMap keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMap.begin(); it != myMap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '4' is removed from myMap

      myMap keys and values:
      1 - One
      2 - Two
      3 - Three
      5 - Five
    


في المثال التالي قمنا بتعريف كائنين من map مع تحديد أن عناصرهما تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة قيم عناصرهما بواسطة حلقة للتأكد من أن التبديل قد تم بنجاح.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل عناصرهما.

المثال السابع
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن من الكلاس
      map<int, string> map1 = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"}
      };

      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها map هنا قمنا بتعريف كائن آخر من الكلاس
      map<int, string> map2 = {
      {5, "Five"},
      {6, "Six"},
      {7, "Seven"},
      {8, "Eight"}
      };

      // map2 مع قيم الكائن map1 هنا قمنا بتبديل قيم الكائن
      map1.swap(map2);

      cout << "map1 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه map1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = map1.begin(); it != map1.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      cout << "\nmap2 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه map2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = map2.begin(); it != map2.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      map1 keys and values:
      5 - Five
      6 - Six
      7 - Seven
      8 - Eight

      map2 keys and values:
      1 - One
      2 - Two
      3 - Three
      4 - Four
    
__________

الكلاس multimap في C++

تعريف الكلاس multimap في C++

يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بشكل تصاعدي على حسب قيم المفاتيح مع الإشارة إلى أنه يمكن وضع نفس المفتاح لأكثر من عنصر كالتالي.



القيمة المفتاحية لكل عنصر يمكن أن تكون من أي نوع تريده, و لكن في أغلب الأوقات ستكون المفاتيح عبارة عن أعداد أو كلمات.


معلومة تقنية

الفرق الوحيد بين الحاوية التي تنشئها من الكلاس map و الحاوية التي تنشئها من الكلاس multimap هو أن هذا الأخير يمكنه أن يستخدم نفس المفتاح لأكثر من عنصر.
الآن, بما أن مفاتيح العناصر في هذه الحاوية يمكن أن تكون مكررة فهذا يعني أنه لا يمكنك الإعتماد على قيم المفاتيح للتفرقة بين العناصر حيث أن المفتاح الواحد يمكن أن يرمز لعدة عناصر.


لاستخدام الكلاس multimap - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<map> لأنه موجود فيه.


بناء الكلاس
    template < class Key,                                     // multimap::key_type
    class T,                                       // multimap::multimapped_type
    class Compare = less<Key>,                     // multimap::key_compare
    class Alloc = allocator<pair<const Key,T> >    // multimap::allocator_type
    > class multimap;
  

إذاً عند إنشاء كائن من الكلاس multimap يجب أن نمرر له نوع المفاتيح التي نريد تخزينها فيه مكان الباراميتر Key و نوع القيم التي نريد تخزينها فيه مكان الباراميتر T.

دوال الكلاس multimap في C++

الجدول التالي يحتوي على دوال الكلاس multimap التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator rbegin() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.
iterator rend() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة rbegin() بداخل حلقة فإننا نستخدم معها الدالة rend() من أجل البدء من آخر عنصر و الوقوف عند أول عنصر.

الجدول التالي يحتوي على دوال الكلاس multimap التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس multimap التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
pair<K, V> insert({key, value}) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر key نمرر القيمة المفتاحية للعنصر الذي نريد إضافته.
مكان الباراميتر value نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
pair<K, V> emplace(Args&&... args) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة insert().
مكان الباراميتر Args نمرر القيمة المفتاحية للعنصر الذي نريد إضافته ثم نضع فاصلة و نمرر القيمة التي نريد وضعها فيه.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
size_t erase(key_type& k) تستخدم لحذف كل عنصر في الكائن الذي قام باستدعائها يملك المفتاح الذي نمرره لها.
مكان الباراميتر k نمرر المفتاح الذي نريد حذف كل عنصر يملكه.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكانه فيه.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكان وجود هذه العناصر.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(multimap& anotherMap) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherMap نمرر لها كائن من الكلاس multimap يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس multimap التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(const key_type& k) تستخدم لمعرفة عدد العناصر التي تملك مفتاح محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر k نمرر المفتاح الذي نريد معرفة عدد العناصر التي تملكه.
iterator find(const T& k) تستخدم للبحث في الكائن الذي قام باستدعائها عن مكان أول عنصر يملك مفتاح محدد.
مكان الباراميتر k نمرر المفتاح الذي نريد البحث عن أول عنصر يملكه.
في حال تم إيجاد عنصر يملك نفس المفتاح الذي تم تمريره لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس المفتاح الذي تم تمريره لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس multimap في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن كود الإتصال الخاص بالبلد و إسم البلد.
بعدها قمنا بطباعة عدد العناصر التي قمنا بإضافتها.
في الأخير قمنا بعرض جميع مفاتيح و قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<string, string> myMultimap;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,myMultimap هنا قمنا بإضافة 12 عنصر في الكائن
      myMultimap.emplace("Egypt", "Giza");
      myMultimap.emplace("Lebanon", "Beirut");
      myMultimap.emplace("Syria", "Sweida");
      myMultimap.emplace("Lebanon", "Tripolie");
      myMultimap.emplace("Syria", "Idlib");
      myMultimap.emplace("KSA", "Riyadh");
      myMultimap.emplace("Egypt", "Cairo");
      myMultimap.emplace("Lebanon", "Sidon");
      myMultimap.emplace("Syria", "Damascus");
      myMultimap.emplace("KSA", "Makkah");
      myMultimap.emplace("Syria", "Homs");
      myMultimap.emplace("Algeria", "Algiers");

      // size() باستخدام الدالة myMultimap هنا قمنا بطباعة عدد عناصر الكائن
      cout << "myMultimap size = " << myMultimap.size() << "\n\n";

      cout << "myMultimap keys and values\n";
      cout << "---------------------\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultimap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultimap.begin(); it != myMultimap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << "\t | " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      myMultimap size = 12

      myMultimap keys and values
      ---------------------
      Algeria  | Algiers
      Egypt    | Giza
      Egypt    | Cairo
      KSA      | Riyadh
      KSA      | Makkah
      Lebanon  | Beirut
      Lebanon  | Tripolie
      Lebanon  | Sidon
      Syria    | Sweida
      Syria    | Idlib
      Syria    | Damascus
      Syria    | Homs
    


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثاني
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<int, string> myMultimap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultimap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultimap.begin(); it != myMultimap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      1 - One
      2 - Two
      3 - Three
      4 - Four
      5 - Five
    


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بعرض القيم الموجودة فيه بشكل عكسي بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة rbegin() للحصول على مؤشر للعنصر الأخير لأننا سنبدأ من عنده و الدالة rend() للحصول على مؤشر للعنصر الأول لأننا سنتوقف عنده.

المثال الثالث
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<int, string> myMultimap = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من آخر عنصر وصولاً لأول عنصر فيه myMultimap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultimap.rbegin(); it != myMultimap.rend(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      5 - Five
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بالبحث عن عدد العناصر التي تملك مفتاح محدد و طباعة نتيجة البحث.

ملاحظة: قمنا باستخدام الدالة count() لمعرفة عدد العناصر التي تملك مفتاح محدد في الكائن.

المثال الرابع
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<string, string> myMultimap;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,myMultimap هنا قمنا بإضافة 12 عنصر في الكائن
      myMultimap.emplace("Egypt", "Giza");
      myMultimap.emplace("Lebanon", "Beirut");
      myMultimap.emplace("Syria", "Sweida");
      myMultimap.emplace("Lebanon", "Tripolie");
      myMultimap.emplace("Syria", "Idlib");
      myMultimap.emplace("KSA", "Riyadh");
      myMultimap.emplace("Egypt", "Cairo");
      myMultimap.emplace("Lebanon", "Sidon");
      myMultimap.emplace("Syria", "Damascus");
      myMultimap.emplace("KSA", "Makkah");
      myMultimap.emplace("Syria", "Homs");
      myMultimap.emplace("Algeria", "Algiers");

      // myMultimap في الكائن "Syria" هنا قمنا بطباعة عدد العناصر التي تملك قيمة تساوي
      cout << "Total number of elements with key 'Syria' = " << myMultimap.count("Syria");

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Total number of elements with key 'Syria' = 4
    


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بحذف جميع العناصر التي تملك مفاتيح محددة منه و من ثم طباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة erase() لحذف جميع العناصر التي تملك مفاتيح محددة.

المثال الخامس
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<string, string> myMultimap;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,myMultimap هنا قمنا بإضافة 12 عنصر في الكائن
      myMultimap.emplace("Egypt", "Giza");
      myMultimap.emplace("Lebanon", "Beirut");
      myMultimap.emplace("Syria", "Sweida");
      myMultimap.emplace("Lebanon", "Tripolie");
      myMultimap.emplace("Syria", "Idlib");
      myMultimap.emplace("KSA", "Riyadh");
      myMultimap.emplace("Egypt", "Cairo");
      myMultimap.emplace("Lebanon", "Sidon");
      myMultimap.emplace("Syria", "Damascus");
      myMultimap.emplace("KSA", "Makkah");
      myMultimap.emplace("Syria", "Homs");
      myMultimap.emplace("Algeria", "Algiers");

      // myMultimap من الكائن "Syria" و المتفاح "Lebanon" هنا قمنا بحذف جميع العناصر التي تملك المفتاح
      myMultimap.erase("Lebanon");
      myMultimap.erase("Syria");

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultimap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultimap.begin(); it != myMultimap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " -> " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Algeria -> Algiers
      Egypt -> Giza
      Egypt -> Cairo
      KSA -> Riyadh
      KSA -> Makkah
    


في المثال التالي قمنا بتعريف كائن من multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بالبحث عن أول عنصر يملك مفتاح محدد فيه و من ثم قمنا بحذفه.
في الأخير قمنا بطباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن أول عنصر يملك مفتاح محدد في الكائن و الدالة erase() لحذف العنصر منه.

المثال السادس
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<int, string> myMultimap = {
      {1, "Un"},
      {1, "One"},
      {2, "Deux"},
      {2, "Two"},
      {3, "Trois"},
      {3, "Three"}
      };

      // نجده يملك المفتاح الذي سنبحث عنه myMultimap لتخزين مكان أول عنصر في الكائن multimap<int, string>::iterator منا قمنا بتعريف كائن من 
      multimap<int, string>::iterator it;

      // it عن أول عنصر يملك مفتاح يساوي 2, و بالتالي في حال وجود عنصر يملك هذا المفتاح سيتم تخزين عنوانه في الكائن myMultimap هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذا المفتاح myMultimap.end() في حال عدم وجود عنصر يملك مفتاح يساوي 2 سيتم تخزين القيمة التي ترجعها
      it = myMultimap.find(2);

      // فهذا يعني أنه تم إيجاد العنصر و بالتالي سيتم تنفيذ الكود الموضوع بالداخل myMultimap.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != myMultimap.end())
      {
      // و من ثم قمنا بحذفه myMultimap هنا قمنا بطباعة مفتاح و قيمة العنصر الذي تم إيجاده في الكائن
      cout << "Element with key '"<< it->first << "' and value '" << it->second << "' is removed from myMultimap\n\n";
      myMultimap.erase(it);
      }

      cout << "myMultimap keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه myMultimap هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = myMultimap.begin(); it != myMultimap.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '2' and value 'Deux' is removed from myMultimap

      myMultimap keys and values:
      1 - Un
      1 - One
      2 - Two
      3 - Trois
      3 - Three
    


في المثال التالي قمنا بتعريف كائنين من multimap مع تحديد أن عناصرهما تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة قيم عناصرهما بواسطة حلقة للتأكد من أن التبديل قد تم بنجاح.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل عناصرهما.

المثال السابع
main.cpp
      #include <iostream>
      #include <map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن من الكلاس
      multimap<int, string> multimap1 = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"}
      };

      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها multimap هنا قمنا بتعريف كائن آخر من الكلاس
      multimap<int, string> multimap2 = {
      {5, "Five"},
      {6, "Six"},
      {7, "Seven"},
      {8, "Eight"}
      };

      // multimap2 مع قيم الكائن multimap1 هنا قمنا بتبديل قيم الكائن
      multimap1.swap(multimap2);

      cout << "multimap1 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه multimap1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = multimap1.begin(); it != multimap1.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      cout << "\nmultimap2 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه multimap2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = multimap2.begin(); it != multimap2.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      multimap1 keys and values:
      5 - Five
      6 - Six
      7 - Seven
      8 - Eight

      multimap2 keys and values:
      1 - One
      2 - Two
      3 - Three
      4 - Four
    
_____

الكلاس unordered_set في C++

تعريف الكلاس unordered_set في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه مع الإشارة إلى أنه لا يمكن تخزين قيم مكررة فيها.

بالمبدأ الدالة Hash() تقوم بإجراء عملية حسابية على قيم أي عنصر يتم إدخاله.
أي ناتج جديد غير مكرر ترجعه الدالة Hash() يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash() بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.



كما أنه بإمكانك تعريف الدالة طريقة عمل الدالة Hash() بنفسك حتى تحدد الطريقة التي سيتم على أساسها إنشاء Buckets و ترتيب العناصر ضمنهم.
على سبيل المثال إذا كنت تنوي إنشاء حاوية خاصة لتخزين الأعداد, يمكنك جعل الدالة Hash() تنشئ Buckets على حسب عدد الأرقام التي يتألف منها كل عدد. عندها سيتم ترتيب العناصر كالتالي:

  • العدد الذي يتألف من رقم واحد مثل 3 - 7 سيتم وضعهما في Bucket.

  • العدد الذي يتألف من رقمين واحد مثل 12 - 45 سيتم وضعهما في Bucket.

  • العدد الذي يتألف من ثلاثة أرقام مثل 100 - 273 سيتم وضعهما في Bucket و هكذا..



لاستخدام الكلاس unordered_set - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<unordered_set> لأنه موجود فيه.


بناء الكلاس
    template < class Key,                        // unordered_set::key_type/value_type
    class Hash = hash<Key>,           // unordered_set::hasher
    class Pred = equal_to<Key>,       // unordered_set::key_equal
    class Alloc = allocator<Key>      // unordered_set::allocator_type
    > class unordered_set;
  

إذاً عند إنشاء كائن من الكلاس unordered_set يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر Key.

دوال الكلاس unordered_set في C++

الجدول التالي يحتوي على دوال الكلاس unordered_set التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
local_iterator begin(size_t n) ترجع كائن iterator يشير لمكان أول عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد البدأ من أول عنصر موجود فيه.
local_iterator end(size_t n) ترجع كائن iterator يشير لمكان آخر عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد الوقوف عند آخر عنصر موجود فيه.

الجدول التالي يحتوي على دوال الكلاس unordered_set التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
size_t bucket_count() تستخدم للحصول على عدد الـ Buckets الموجودين في الكائن الذي قام باستدعائها.
size_t bucket_size(size_t n) تستخدم للحصول على عدد العناصر الموجودة في Bucket محددة في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر عدد يمثل Index الـ Bucket التي سيتم إرجاع عدد العناصر الموجودة فيه.
size_t max_bucket_count() تستخدم للحصول على أكبر عدد Buckets يمكن تخزينها في الكائن الذي قام باستدعائها.
size_t bucket(key_type& k) تستخدم للحصول على رقم Bucket محددة في الكائن الذي قام باستدعائها بالإعتماد على قيمة موجودة فيها.
مكان الباراميتر k نمرر قيمة العنصر الذي نريد معرفة في Bucket موجود.

الجدول التالي يحتوي على دوال الكلاس unordered_set التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(unordered_set& anotherSet) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherSet نمرر لها كائن من الكلاس unordered_set يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_set التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع القيمة 1 و في حال لم يتم إيجاد عنصر يملك نفس القيمة ترجع القيمة 0.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().
pair<iterator,iterator> equal_range(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن قائمة العناصر المكررة التي تملك نفس القيم التي تحددها لها.
مكان الباراميتر val نمرر القيمة التي نريد البحث عن كل العناصر التي تملكها.
ترجع كائن من الكلاس pair يحتوي على كل العناصر المكررة التي تم إيجادها.

أمثلة شاملة حول التعامل مع الكلاس unordered_set في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من unordered_set مع تحديد أنه يمكن أن يحتوي على عناصر نوعها string.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة كم Bucket تم إنشاؤها و إجمالي عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // string يمكنه أن يحتوي على قيم نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      unordered_set<string> us;

      // emplace() باستخدام الدالة us هنا قمنا بإضافة 7 عناصر في الكائن
      us.emplace("Mon");
      us.emplace("Tue");
      us.emplace("Wed");
      us.emplace("Thu");
      us.emplace("Fri");
      us.emplace("Sat");
      us.emplace("Sun");

      // bucket_count() باستخدام الدالة us تم إنشاؤها في الكائن Bucket هنا قمنا بطباعة كم
      cout << "us total buckets = " << us.bucket_count() << endl;

      // size() باستخدام الدالة us هنا قمنا بطباعة عدد عناصر الكائن
      cout << "us total elements = " << us.size() << endl;

      cout << "us values =";

      // us هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      // Bucket وصولاً لآخر عنصر موجود في آخر Bucket إبتداءاً من أول عنصر موجود في أول
      for (auto it = us.begin(); it != us.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      us total buckets = 17
      us total elements = 7
      us values = Sun Tue Wed Thu Fri Mon Sat
    


في المثال التالي قمنا بتعريف كائن من unordered_set مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size() لمعرفة عدد العناصر الموجودة في كل Bucket.

المثال الثاني
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = us.bucket_count();

      // us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
      for(auto it=us.begin(i); it!=us.end(i); ++it)
      {
      cout << " " << *it;
      }

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] elements: Fri
      bucket[1] elements:
      bucket[2] elements: Sat Mon
      bucket[3] elements:
      bucket[4] elements: Sun Wed
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements: Tue
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements: Thu
    


في المثال التالي قمنا بتعريف كائن من unordered_set مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض قيم العناصر الموجودة في كل Bucket قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول فيها لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير فيها لأننا سنتوقف عنده.

المثال الثالث
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = us.bucket_count();

      // us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] total elements = " << us.bucket_size(i);

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] total elements = 1
      bucket[1] total elements = 0
      bucket[2] total elements = 2
      bucket[3] total elements = 0
      bucket[4] total elements = 2
      bucket[5] total elements = 0
      bucket[6] total elements = 0
      bucket[7] total elements = 1
      bucket[8] total elements = 0
      bucket[9] total elements = 0
      bucket[10] total elements = 1
    


في المثال التالي قمنا بتعريف كائن من unordered_set مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه لمعرفة ما إن كانت موجودة فيه أم لا.

ملاحظة: قمنا باستخدام الدالة count() للبحث عن القيمة في الكائن.

المثال الرابع
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // يوجد فيه عنصر قيمته تساوي 4 us هنا قمنا بطباعة ما إن كان الكائن
      if (us.count("Wed"))
      {
      // إن كان فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "'Wed' is exist in us";
      }
      else
      {
      // إن لم يكن فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "'Wed' is not exist in us";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      'Wed' is exist in us
    


في المثال التالي قمنا بتعريف كائن من unordered_set مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن القيمة في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال الخامس
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه unordered_set<string>::iterator منا قمنا بتعريف كائن من 
      unordered_set<string>::iterator it;

      // it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن us هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة us.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها
      it = us.find("Wed");

      // فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها us.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != us.end())
      {
      // و من ثم قمنا بحذفه us هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن
      cout << "Element with value '"<< *it << "' is removed from us\n";
      us.erase(it);
      }

      cout << "us values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = us.begin(); it != us.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with value 'Wed' is removed from us
      us values = Fri Thu Sun Tue Sat Mon
    


في المثال التالي قمنا بتعريف كائنين من unordered_set مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال السادس
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها unordered_set هنا قمنا بتعريف كائنين من الكلاس
      unordered_set<int> us1 = {1, 2, 3, 4};
      unordered_set<int> us2 = {5, 6, 7, 8};

      // us2 مع قيم الكائن us1 هنا قمنا بتبديل قيم الكائن
      us1.swap(us2);

      cout << "us1 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = us1.begin(); it != us1.end(); ++it)
      {
      cout << *it << " ";
      }

      cout << "\nus2 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = us2.begin(); it != us2.end(); ++it)
      {
      cout << *it << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      us1 values = 8 7 6 5
      us2 values = 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من unordered_set مخصص لتخزين قيم نوعها int مع تحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets و إضافة عدة قيم فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets قمنا بإنشاء كلاس إسمه Hash و إعادة تعريف العامل () حتى يقارن قيمة أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع الأعداد التي تتألف من رقم واحد في Bucket, و الأعداد التي تتألف من رقم رقمين في Bucket, و الأعداد التي تتألف من ثلاثة أعداد في Bucket و هكذا..
في النهاية قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

المثال السابع
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      // unordered_set سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه
      class Hash
      {
      public:
      // Bucket حتى يقرر في أي unordered_set هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // val سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن القيمة التي يملكها العنصر سيتم تمريرها مكان الباراميتر
      size_t operator() (const int &val) const
      {
      // بشكل مؤقت لأننا نحتاج إجراء تعديل عليها val سيتم وضع قيمة num المتغير 
      // val سنستخدمه لحساب عدد الأرقام الموجودة في المتغير digits المتغير 
      int num = val;
      int digits = 1;

      // num و من ثم حذف أول رقم في المتغير digits هنا قمنا بإنشاء حلقة تقوم في كل دورة بإضافة واحد على قيمة المتغيرد
      // num تمثل عدد الأرقام الموجودة في العدد الذي كان يحتويه المتغير digits عندما تتوقف هذه الحلقة ستكون قيمة المتغير
      while (num != 0)
      {
      digits++;
      num = num / 10;
      }

      // سيتم إضافة العنصر Bucket لأنها التي ستحدد في أي digits في النهاية سيتم إرجاع قيمة المتغير
      return digits;
      }
      };

      int main()
      {
      // مع تحديد أنه يعتمد على الكلاس string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash
      unordered_set<int, Hash> us = {6, 34, 72, 3, 1, 123, 55, 100, 2020};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = us.bucket_count();

      // us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
      for(auto it=us.begin(i); it!=us.end(i); ++it)
      {
      cout << "  " << *it;
      }
      cout << endl;
      }

      return 0;
      }

    

سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأرقام التي يتألف منها كل عنصر.

      bucket[0] elements:
      bucket[1] elements:  1  3  6
      bucket[2] elements:  55  72  34
      bucket[3] elements:  100  123
      bucket[4] elements:  2020
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements:
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements:
    
_____

الكلاس unordered_multiset في C++

تعريف الكلاس unordered_multiset في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه مع الإشارة إلى أنه يمكن تخزين قيم مكررة فيها.

بالمبدأ الدالة Hash() تقوم بإجراء عملية حسابية على قيم أي عنصر يتم إدخاله.
أي ناتج جديد غير مكرر ترجعه الدالة Hash() يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash() بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.



كما أنه بإمكانك تعريف الدالة طريقة عمل الدالة Hash() بنفسك حتى تحدد الطريقة التي سيتم على أساسها إنشاء Buckets و ترتيب العناصر ضمنهم.
على سبيل المثال إذا كنت تنوي إنشاء حاوية خاصة لتخزين الأعداد, يمكنك جعل الدالة Hash() تنشئ Buckets على حسب عدد الأرقام التي يتألف منها كل عدد. عندها سيتم ترتيب العناصر كالتالي:

  • العدد الذي يتألف من رقم واحد مثل 3 - 7 سيتم وضعهما في Bucket.

  • العدد الذي يتألف من رقمين واحد مثل 12 - 45 سيتم وضعهما في Bucket.

  • العدد الذي يتألف من ثلاثة أرقام مثل 100 - 273 سيتم وضعهما في Bucket و هكذا..



معلومة تقنية

الفرق الوحيد بين الحاوية التي تنشئها من الكلاس unordered_set و الحاوية التي تنشئها من الكلاس unordered_multiset هو أن هذا الأخير يمكنه أن يحتوي على أكثر من عنصر عندهم نفس القيمة.
الآن, بما أن مفاتيح العناصر في هذه الحاوية يمكن أن تكون مكررة فهذا يعني أنه لا يمكنك الإعتماد على قيم المفاتيح للتفرقة بين العناصر حيث أن المفتاح الواحد يمكن أن يرمز لعدة عناصر.


لاستخدام الكلاس unordered_multiset - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<unordered_set> لأنه موجود فيه.


بناء الكلاس

    template < class Key,                        // unordered_multiset::key_type/value_type
    class Hash = hash<Key>,           // unordered_multiset::hasher
    class Pred = equal_to<Key>,       // unordered_multiset::key_equal
    class Alloc = allocator<Key>      // unordered_multiset::allocator_type
    > class unordered_multiset;
  

إذاً عند إنشاء كائن من الكلاس unordered_multiset يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر Key.

دوال الكلاس unordered_multiset في C++

الجدول التالي يحتوي على دوال الكلاس unordered_multiset التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
local_iterator begin(size_t n) ترجع كائن iterator يشير لمكان أول عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد البدأ من أول عنصر موجود فيه.
local_iterator end(size_t n) ترجع كائن iterator يشير لمكان آخر عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد الوقوف عند آخر عنصر موجود فيه.

الجدول التالي يحتوي على دوال الكلاس unordered_multiset التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.
size_t bucket_count() تستخدم للحصول على عدد الـ Buckets الموجودين في الكائن الذي قام باستدعائها.
size_t bucket_size(size_t n) تستخدم للحصول على عدد العناصر الموجودة في Bucket محددة في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر عدد يمثل Index الـ Bucket التي سيتم إرجاع عدد العناصر الموجودة فيه.
size_t max_bucket_count() تستخدم للحصول على أكبر عدد Buckets يمكن تخزينها في الكائن الذي قام باستدعائها.
size_t bucket(key_type& k) تستخدم للحصول على رقم Bucket محددة في الكائن الذي قام باستدعائها بالإعتماد على قيمة موجودة فيها.
مكان الباراميتر k نمرر قيمة العنصر الذي نريد معرفة في Bucket موجود.

الجدول التالي يحتوي على دوال الكلاس unordered_multiset التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
void emplace(T& val) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(unordered_multiset& anotherSet) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherSet نمرر لها كائن من الكلاس unordered_multiset يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_multiset التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عدد العناصر التي تملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن أول عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس unordered_multiset في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من unordered_multiset مع تحديد أنه يمكن أن يحتوي على عناصر نوعها string.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة كم Bucket تم إنشاؤها و إجمالي عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // string يمكنه أن يحتوي على قيم نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      unordered_multiset<string> ums;

      // emplace() باستخدام الدالة ums هنا قمنا بإضافة 9 عناصر في الكائن
      ums.emplace("Mon");
      ums.emplace("Tue");
      ums.emplace("Wed");
      ums.emplace("Thu");
      ums.emplace("Fri");
      ums.emplace("Sat");
      ums.emplace("Sun");
      ums.emplace("Thu");   // تعمدنا إعادة إدخال هذه القيمة مرة ثانية حتى تلاحظ أنه يمكن إدخال قيم مكررة
      ums.emplace("Fri");   // تعمدنا إعادة إدخال هذه القيمة مرة ثانية حتى تلاحظ أنه يمكن إدخال قيم مكررة

      // bucket_count() باستخدام الدالة ums تم إنشاؤها في الكائن Bucket هنا قمنا بطباعة كم
      cout << "ums total buckets = " << ums.bucket_count() << endl;

      // size() باستخدام الدالة ums هنا قمنا بطباعة عدد عناصر الكائن
      cout << "ums total elements = " << ums.size() << endl;

      cout << "ums values =";

      // ums هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      // Bucket وصولاً لآخر عنصر موجود في آخر Bucket إبتداءاً من أول عنصر موجود في أول
      for (auto it = ums.begin(); it != ums.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      ums total buckets = 17
      ums total elements = 9
      ums values = Sun Tue Wed Thu Thu Fri Fri Mon Sat
    


في المثال التالي قمنا بتعريف كائن من unordered_multiset مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size() لمعرفة عدد العناصر الموجودة في كل Bucket.

المثال الثاني

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = ums.bucket_count();

      // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
      for(auto it=ums.begin(i); it!=ums.end(i); ++it)
      {
      cout << " " << *it;
      }

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] elements: Fri
      bucket[1] elements:
      bucket[2] elements: Sat Mon
      bucket[3] elements:
      bucket[4] elements: Sun Wed
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements: Tue
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements: Thu
    


في المثال التالي قمنا بتعريف كائن من unordered_multiset مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض قيم العناصر الموجودة في كل Bucket قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول فيها لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير فيها لأننا سنتوقف عنده.

المثال الثالث

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = ums.bucket_count();

      // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] total elements = " << ums.bucket_size(i);

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] total elements = 1
      bucket[1] total elements = 0
      bucket[2] total elements = 2
      bucket[3] total elements = 0
      bucket[4] total elements = 2
      bucket[5] total elements = 0
      bucket[6] total elements = 0
      bucket[7] total elements = 1
      bucket[8] total elements = 0
      bucket[9] total elements = 0
      bucket[10] total elements = 1
    


في المثال التالي قمنا بتعريف كائن من unordered_multiset مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيم محددة فيه لمعرفة كم عنصر يملك هذه القيم.

ملاحظة: قمنا باستخدام الدالة count() لمعرفة كم مرة القيم مكررة في الكائن.

المثال الرابع

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      unordered_multiset<string> ums = {"Apple", "Orange", "Apple", "Tomato", "Banana", "Pineapple", "Blueberry"};

      // "Apple" التي تملك القيمة ums هنا قمنا بطباعة عدد العناصر الموجودة في الكائن
      cout << "'Apple' is exist " << ums.count("Apple")  << " time(s)\n";

      // "Carrot" التي تملك القيمة ums هنا قمنا بطباعة عدد العناصر الموجودة في الكائن
      cout << "'Carrot' is exist " << ums.count("Carrot")  << " time(s)";
      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      'Apple' is exist 2 time(s)
      'Carrot' is exist 0 time(s)
    


في المثال التالي قمنا بتعريف كائن من unordered_multiset مخصص لتخزين قيم نوعها string مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن أول عنصر يملك قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن القيمة في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال الخامس
main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      unordered_multiset<string> ums = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه unordered_multiset<string>::iterator منا قمنا بتعريف كائن من 
      unordered_multiset<string>::iterator it;

      // it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن ums هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة ums.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها
      it = ums.find("Wed");

      // فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها ums.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != ums.end())
      {
      // و من ثم قمنا بحذفه ums هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن
      cout << "First element with value '"<< *it << "' is removed from ums\n";
      ums.erase(it);
      }

      cout << "ums values =";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = ums.begin(); it != ums.end(); ++it)
      {
      cout << " " << *it;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      First element with value 'Wed' is removed from ums
      ums values = Fri Thu Sun Tue Sat Mon
    


في المثال التالي قمنا بتعريف كائنين من unordered_multiset مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل قيمهما.

المثال السادس

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها unordered_multiset هنا قمنا بتعريف كائنين من الكلاس
      unordered_multiset<int> ums1 = {1, 2, 3, 4};
      unordered_multiset<int> ums2 = {5, 6, 7, 8};

      // ums2 مع قيم الكائن ums1 هنا قمنا بتبديل قيم الكائن
      ums1.swap(ums2);

      cout << "ums1 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = ums1.begin(); it != ums1.end(); ++it)
      {
      cout << *it << " ";
      }

      cout << "\nums2 values = ";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه ums2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = ums2.begin(); it != ums2.end(); ++it)
      {
      cout << *it << " ";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      ums1 values = 8 7 6 5
      ums2 values = 4 3 2 1
    


في المثال التالي قمنا بتعريف كائن من unordered_multiset مخصص لتخزين قيم نوعها int مع تحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets و إضافة عدة قيم فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets قمنا بإنشاء كلاس إسمه Hash و إعادة تعريف العامل () حتى يقارن قيمة أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع الأعداد التي تتألف من رقم واحد في Bucket, و الأعداد التي تتألف من رقم رقمين في Bucket, و الأعداد التي تتألف من ثلاثة أعداد في Bucket و هكذا..
في النهاية قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

المثال السابع

main.cpp
      #include <iostream>
      #include <unordered_set>

      using namespace std;

      // unordered_multiset سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه
      class Hash
      {
      public:
      // Bucket حتى يقرر في أي unordered_multiset هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // val سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن القيمة التي يملكها العنصر سيتم تمريرها مكان الباراميتر
      size_t operator() (const int &val) const
      {
      // بشكل مؤقت لأننا نحتاج إجراء تعديل عليها val سيتم وضع قيمة num المتغير 
      // val سنستخدمه لحساب عدد الأرقام الموجودة في المتغير digits المتغير 
      int num = val;
      int digits = 1;

      // num و من ثم حذف أول رقم في المتغير digits هنا قمنا بإنشاء حلقة تقوم في كل دورة بإضافة واحد على قيمة المتغيرد
      // num تمثل عدد الأرقام الموجودة في العدد الذي كان يحتويه المتغير digits عندما تتوقف هذه الحلقة ستكون قيمة المتغير
      while (num != 0)
      {
      digits++;
      num = num / 10;
      }

      // سيتم إضافة العنصر Bucket لأنها التي ستحدد في digits في النهاية سيتم إرجاع قيمة المتغير
      return digits;
      }
      };

      int main()
      {
      // مع تحديد أنه يعتمد على الكلاس string يمكنه أن يحتوي على عناصر نوعها unordered_multiset هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash
      unordered_multiset<int, Hash> ums = {6, 34, 72, 3, 1, 123, 55, 100, 2020};

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = ums.bucket_count();

      // ums الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
      for(auto it=ums.begin(i); it!=ums.end(i); ++it)
      {
      cout << "  " << *it;
      }
      cout << endl;
      }

      return 0;
      }

    

سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأرقام التي يتألف منها كل عنصر.

      bucket[0] elements:
      bucket[1] elements:  1  3  6
      bucket[2] elements:  55  72  34
      bucket[3] elements:  100  123
      bucket[4] elements:  2020
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements:
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements:
    
________

الكلاس unordered_map

تعريف الكلاس unordered_map في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بالإعتماد على دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على مفتاح أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيها.

بالمبدأ الدالة Hash() تقوم بإجراء عملية حسابية على مفتاح أي عنصر يتم إدخاله.
أي ناتج ترجعه الدالة Hash() يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash() بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.



القيمة المفتاحية لكل عنصر يمكن أن تكون من أي نوع تريده, و لكن في أغلب الأوقات ستكون المفاتيح عبارة عن أعداد أو كلمات.
لا تستطيع وضع نفس القيمة المفتاحية لأكثر من عنصر و هذا شيء منطقي لأن المفاتيح هي ما يميز العناصر عن بعضها.


لاستخدام الكلاس unordered_map - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<unordered_map> لأنه موجود فيه.


بناء الكلاس

    template < class Key,                                    // unordered_map::key_type
    class T,                                      // unordered_map::mapped_type
    class Hash = hash<Key>,                       // unordered_map::hasher
    class Pred = equal_to<Key>,                   // unordered_map::key_equal
    class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
    > class unordered_map;
  

إذاً عند إنشاء كائن من الكلاس unordered_map يجب أن نمرر له نوع المفاتيح التي نريد تخزينها فيه مكان الباراميتر Key و نوع القيم التي نريد تخزينها فيه مكان الباراميتر T.

دوال الكلاس unordered_map في C++

الجدول التالي يحتوي على دوال الكلاس unordered_map التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
local_iterator begin(size_t n) ترجع كائن iterator يشير لمكان أول عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد البدأ من أول عنصر موجود فيه.
local_iterator end(size_t n) ترجع كائن iterator يشير لمكان آخر عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد الوقوف عند آخر عنصر موجود فيه.

الجدول التالي يحتوي على دوال الكلاس unordered_map التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_map التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
unordered_mapped_type& at(key_type& k) تستخدم للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
ترمي الإستثناء out_of_range في حال كانت قيمة k لا تمثل القيمة المفتاحية لأي عنصر موجود في الكائن.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير قيمة مفتاح غير موجود في الكائن.
operator[] (key_type& k) يستخدم العامل [] للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
في حال تمرير قيمة مفتاح غير موجود مكان الباراميتر k سيتم إضافة العنصر كعنصر جديد في الكائن.

الجدول التالي يحتوي على دوال الكلاس unordered_map التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
pair<K, V> insert({key, value}) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر key نمرر القيمة المفتاحية للعنصر الذي نريد إضافته.
مكان الباراميتر value نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
pair<K, V> emplace(Args&&... args) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة insert().
مكان الباراميتر Args نمرر القيمة المفتاحية للعنصر الذي نريد إضافته ثم نضع فاصلة و نمرر القيمة التي نريد وضعها فيه.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
size_t erase(key_type& k) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال المفتاح الخاص به.
مكان الباراميتر k نمرر مفتاح العنصر الذي نريد حذفه.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكانه فيه.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكان وجود هذه العناصر.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(unordered_map& anotherMap) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherMap نمرر لها كائن من الكلاس unordered_map يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_map التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع القيمة 1 و في حال لم يتم إيجاد عنصر يملك نفس القيمة ترجع القيمة 0.
iterator find(const T& val) تستخدم للبحث في الكائن الذي قام باستدعائها عن عنصر يملك قيمة محددة.
مكان الباراميتر val نمرر القيمة التي نريد البحث عنها.
في حال تم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس القيمة التي تم تمريرها لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس unordered_map في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن كود الإتصال الخاص بالبلد و إسم البلد.
بعدها قمنا بطباعة عدد العناصر التي قمنا بإضافتها.
في الأخير قمنا بعرض جميع مفاتيح و قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<string, string> um;

      // كل عنصر يحتوي على كود البلد كمفتاح و إسمه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("+961", "Lebanon");
      um.emplace("+962", "Jordan");
      um.emplace("+963", "Syria");
      um.emplace("+964", "Iraq");
      um.emplace("+965", "Kuwait");
      um.emplace("+966", "KSA");
      um.emplace("+967", "Yaman");
      um.emplace("+968", "Oman");
      um.emplace("+970", "Palestine");
      um.emplace("+212", "Morocco");
      um.emplace("+281", "Libya");
      um.emplace("+20",  "Egypt");

      // size() باستخدام الدالة um هنا قمنا بطباعة عدد عناصر الكائن
      cout << "um size = " << um.size() << "\n\n";

      cout << "um keys and values\n";
      cout << "---------------------\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << "\t | " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      um size = 12

      um keys and values
      ---------------------
      +20      | Egypt
      +281     | Libya
      +212     | Morocco
      +967     | Yaman
      +970     | Palestine
      +962     | Jordan
      +968     | Oman
      +961     | Lebanon
      +963     | Syria
      +966     | KSA
      +965     | Kuwait
      +964     | Iraq
    


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثاني

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<int, string> um = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      5 - Five
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بالبحث عن عنصر يملك مفتاح محدد لمعرفة ما إن كان يوجد عنصر يملك هذا المفتاح أم لا مع الإشارة إلى أنه في حال وجود عنصر يملك هذا المفتاح سيتم طباعة قيمته أيضاً.

ملاحظة: قمنا باستخدام الدالة count() للبحث عن عنصر يملك مفتاح محدد في الكائن و قمنا باستخدام الدالة at() لطباعة قيمة العنصر الذي يملك مفتاح محدد.

المثال الثالث

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<int, string> um = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // يوجد فيه عنصر يملك مفتاح يساوي 4 أم لا um هنا قمنا بطباعة ما إن كان الكائن
      if (um.count(4))
      {
      // إن كان فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "Element with key '4' is exist and it's value = " << um.at(4);
      }
      else
      {
      // إن لم يكن فيه سيتم تنفيذ أمر الطباعة التالي
      cout << "There is no element with key = 4";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '4' is exist and it's value = Four
    


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بحذف عنصر منه بالإعتماد على قيمة مفتاحه و من ثم طباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة erase() لحذف العنصر.

المثال الرابع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<int, string> um = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"}
      };

      // um هنا قمنا بحذف العنصر الذي يملك مفتاح يساوي 3 في الكائن
      um.erase(3);

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '4' is removed from um

      um keys and values:
      5 - Five
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بالبحث عن عنصر يملك مفتاح محدد فيه و في حال وجوده سيتم حذف العنصر من الكائن.
في الأخير قمنا بطباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن العنصر الذي يملك المفتاح في الكائن و الدالة erase() لحذف العنصر من الكائن.

المثال الخامس

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<int, string> um = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"}
      };

      // لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه unordered_map<int, string>::iterator منا قمنا بتعريف كائن من 
      unordered_map<int, string>::iterator it;

      // it عن عنصر يملك مفتاح يساوي 4, و بالتالي في حال وجود عنصر يملك هذا المفتاح سيتم تخزين عنوانه في الكائن um هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذا المفتاح um.end() في حال عدم وجود عنصر يملك مفتاح يساوي 4 سيتم تخزين القيمة التي ترجعها
      it = um.find(4);

      // فهذا يعني أنه تم إيجاد العنصر و بالتالي سيتم تنفيذ الكود الموضوع بالداخل um.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != um.end())
      {
      // و من ثم قمنا بحذفه um هنا قمنا بطباعة مفتاح العنصر الذي تم إيجاده في الكائن
      cout << "Element with key '"<< it->first << "' is removed from um\n\n";
      um.erase(it);
      }

      cout << "um keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '4' is removed from um

      um keys and values:
      1 - One
      2 - Two
      3 - Three
      5 - Five
    


في المثال التالي قمنا بتعريف كائنين من unordered_map مع تحديد أن عناصرهما تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة قيم عناصرهما بواسطة حلقة للتأكد من أن التبديل قد تم بنجاح.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل عناصرهما.

المثال السادس

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<int, string> um1 = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"}
      };

      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن آخر من الكلاس
      unordered_map<int, string> um2 = {
      {5, "Five"},
      {6, "Six"},
      {7, "Seven"},
      {8, "Eight"}
      };

      // um2 مع قيم الكائن um1 هنا قمنا بتبديل قيم الكائن
      um1.swap(um2);

      cout << "um1 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um1.begin(); it != um1.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      cout << "\num2 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um2.begin(); it != um2.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      um1 keys and values:
      8 - Eight
      7 - Seven
      6 - Six
      5 - Five

      um2 keys and values:
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من unordered_map مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن كود الإتصال الخاص بالبلد و إسم البلد.
في الأخير قمنا بعرض مفاتيح و قيم العناصر الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size() لمعرفة عدد العناصر الموجودة في كل Bucket.

المثال السابع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<string, string> um;

      // كل عنصر يحتوي على كود البلد كمفتاح و إسمه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("+961", "Lebanon");
      um.emplace("+962", "Jordan");
      um.emplace("+963", "Syria");
      um.emplace("+964", "Iraq");
      um.emplace("+965", "Kuwait");
      um.emplace("+966", "KSA");
      um.emplace("+967", "Yaman");
      um.emplace("+968", "Oman");
      um.emplace("+970", "Palestine");
      um.emplace("+212", "Morocco");
      um.emplace("+281", "Libya");
      um.emplace("+20",  "Egypt");

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة مفتاح و قيمة كل عصنر موجود فيها الحلقة التالية
      for(auto it=um.begin(i); it!=um.end(i); ++it)
      {
      cout << " [" << it->first << " : " << it->second << "]";
      }

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] elements:
      bucket[1] elements:
      bucket[2] elements:
      bucket[3] elements:
      bucket[4] elements: [+963 : Syria]
      bucket[5] elements: [+967 : Yaman]
      bucket[6] elements: [+966 : KSA] [+965 : Kuwait]
      bucket[7] elements: [+968 : Oman] [+961 : Lebanon]
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements: [+281 : Libya] [+212 : Morocco]
      bucket[11] elements: [+970 : Palestine] [+962 : Jordan]
      bucket[12] elements:
      bucket[13] elements: [+20 : Egypt]
      bucket[14] elements:
      bucket[15] elements: [+964 : Iraq]
      bucket[16] elements:
    


في المثال التالي قمنا بتعريف كائن من unordered_map مخصص لتخزين عناصر تتكون من مفاتيح نوعها string و قيم نوعها string أيضاً مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض عدد العناصر الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي.

المثال الثامن

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      unordered_map<string, string> um = {
      {"+961", "Lebanon"},
      {"+962", "Jordan"},
      {"+963", "Syria"},
      {"+964", "Iraq"},
      {"+965", "Kuwait"},
      {"+966", "KSA"},
      {"+967", "Yaman"},
      {"+968", "Oman"},
      {"+970", "Palestine"},
      {"+212", "Morocco"},
      {"+281", "Libya"},
      {"+20",  "Egypt"}
      };

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] total elements = " << um.bucket_size(i);

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] total elements = 1
      bucket[1] total elements = 0
      bucket[2] total elements = 1
      bucket[3] total elements = 2
      bucket[4] total elements = 0
      bucket[5] total elements = 0
      bucket[6] total elements = 3
      bucket[7] total elements = 1
      bucket[8] total elements = 0
      bucket[9] total elements = 1
      bucket[10] total elements = 1
      bucket[11] total elements = 0
      bucket[12] total elements = 2
    


في المثال التالي قمنا بتعريف كائن من unordered_map مخصص لتخزين مفاتيح نوعها string و قيم نوعها string أيضاً مع تحديد الطريقة التي سيتم على أساسها توزيع العناصر في Buckets و إضافة عدة عناصر فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع العناصر في Buckets قمنا بإنشاء كلاس إسمه Hash و إعادة تعريف العامل () حتى يقارن مفتاح أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع العناصر التي تملك مفتاح يتألف من حرف واحد في Bucket, و العناصر التي تملك مفتاح يتألف من حرفين في Bucket, و العناصر التي تملك مفتاح يتألف من ثلاثة أحرف في Bucket و هكذا..
في النهاية قمنا بعرض المفاتيح الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

المثال التاسع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      // unordered_map سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه
      class Hash
      {
      public:
      // Bucket حتى يقرر في أي unordered_map هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // key سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن المفتاح الذي يملكه العنصر سيتم تمريره مكان الباراميتر
      size_t operator() (const string &key) const
      {
      // سيتم إضافة العنصر Bucket في النهاية سيتم إرجاع عدد أحرف مفتاح العنصر لأنها التي ستحدد في أي
      return key.length();
      }
      };

      int main()
      {
      // مع تحديد أنه يعتمد string يمكنه أن يحتوي على عناصر مفاتيحها و قيمها نوعها unordered_map هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash على الكلاس
      unordered_map<string, string, Hash> um = {
      {"HTML", "Hyper Text Markup Language"},
      {"CSS", "Cascading Style Sheet"},
      {"CS", "Computer Science"},
      {"Algo", "Algorithms"},
      {"OOP", "Object Oriented Programming"},
      {"DB", "Database"},
      {"SQL", "Structured Query Language"}
      };

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة مفاتيح العناصر الموجودة فيها بواسطة الحلقة التالية
      for(auto it=um.begin(i); it!=um.end(i); ++it)
      {
      cout << "  " << it->first;
      }
      cout << endl;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأحرف التي يتألف منها كل عنصر.

      bucket[0] elements:
      bucket[1] elements:
      bucket[2] elements:  DB  CS
      bucket[3] elements:  SQL  OOP  CSS
      bucket[4] elements:  Algo  HTML
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements:
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements:
    
_____

الكلاس unordered_multimap

تعريف الكلاس unordered_multimap في C++

تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11 و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بشكل جدول يتألف من عمودين حيث يتكون كل عنصر من مفتاح ( Key ) يوضع في العمود الأول و قيمة ( Value ) توضع في العمود الثاني بالإضافة إلى أنها ترتب العناصر بالإعتماد على دالة مخصصة لذلك إسمها Hash() تقوم بالتشييك على مفتاح أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيها مع الإشارة إلى أنه يمكن وضع نفس المفتاح لأكثر من عنصر.

بالمبدأ الدالة Hash() تقوم بإجراء عملية حسابية على مفتاح أي عنصر يتم إدخاله.
أي ناتج ترجعه الدالة Hash() يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash() بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.



القيمة المفتاحية لكل عنصر يمكن أن تكون من أي نوع تريده, و لكن في أغلب الأوقات ستكون المفاتيح عبارة عن أعداد أو كلمات.


معلومة تقنية

الفرق الوحيد بين الحاوية التي تنشئها من الكلاس unordered_map و الحاوية التي تنشئها من الكلاس unordered_multimap هو أن هذا الأخير يمكنه أن يستخدم نفس المفتاح لأكثر من عنصر.
الآن, بما أن مفاتيح العناصر في هذه الحاوية يمكن أن تكون مكررة فهذا يعني أنه لا يمكنك الإعتماد على قيم المفاتيح للتفرقة بين العناصر حيث أن المفتاح الواحد يمكن أن يرمز لعدة عناصر.


لاستخدام الكلاس unordered_multimap - أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<unordered_multimap> لأنه موجود فيه.


بناء الكلاس

    template < class Key,                                    // unordered_multimap::key_type
    class T,                                      // unordered_multimap::mapped_type
    class Hash = hash<Key>,                       // unordered_multimap::hasher
    class Pred = equal_to<Key>,                   // unordered_multimap::key_equal
    class Alloc = allocator< pair<const Key,T> >  // unordered_multimap::allocator_type
    > class unordered_multimap;
  

إذاً عند إنشاء كائن من الكلاس unordered_multimap يجب أن نمرر له نوع المفاتيح التي نريد تخزينها فيه مكان الباراميتر Key و نوع القيم التي نريد تخزينها فيه مكان الباراميتر T.

دوال الكلاس unordered_multimap في C++

الجدول التالي يحتوي على دوال الكلاس unordered_multimap التي تستخدم للحصول على عداد يتيح المرور على عناصره.

الدالة مع تعريفها
iterator begin() ترجع كائن iterator يشير لمكان أول عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
iterator end() ترجع كائن iterator يشير لمكان آخر عنصر في الكائن الذي قام باستدعائها.
إذا كنا ننوي استخدام الدالة begin() بداخل حلقة فإننا نستخدم معها الدالة end() من أجل البدء من أول عنصر و الوقوف عند آخر عنصر.
local_iterator begin(size_t n) ترجع كائن iterator يشير لمكان أول عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد البدأ من أول عنصر موجود فيه.
local_iterator end(size_t n) ترجع كائن iterator يشير لمكان آخر عنصر بداخل Bucket محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر n نمرر رقم الـ Bucket الذي نريد الوقوف عند آخر عنصر موجود فيه.

الجدول التالي يحتوي على دوال الكلاس unordered_multimap التي تستخدم للحصول على عدد عناصره.

الدالة مع تعريفها
bool empty() تستخدم لمعرفة ما إن كان الكائن الذي قام باستدعائها فارغاً أم لا.
ترجع false في حال كان يوجد فيه عنصر أو أكثر, و ترجع true إن لم يكن كذلك.
size_t size() تستخدم للحصول على عدد العناصر الموجودة في الكائن الذي قام باستدعائها.
size_t max_size() تستخدم للحصول على أكبر عدد عناصر يمكن تخزينها في الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_multimap التي تستخدم للوصول لقيم عناصره.

الدالة مع تعريفها
unordered_multimapped_type& at(key_type& k) تستخدم للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
ترمي الإستثناء out_of_range في حال كانت قيمة k لا تمثل القيمة المفتاحية لأي عنصر موجود في الكائن.

معلومة: يمكنك استخدام العامل [] للوصول للعنصر و لكن عليك معرفة أن استخدام هذا العامل لا يرمي إستثناء في حال تمرير قيمة مفتاح غير موجود في الكائن.
operator[] (key_type& k) يستخدم العامل [] للوصول لقيمة عنصر محدد في الكائن الذي قام باستدعائها سواء لتغييرها أو للحصول عليها.
مكان الباراميتر k نمرر لها مفتاح العنصر الذي نريد الوصول إليه.
في حال تمرير قيمة مفتاح غير موجود مكان الباراميتر k سيتم إضافة العنصر كعنصر جديد في الكائن.

الجدول التالي يحتوي على دوال الكلاس unordered_multimap التي تستخدم للتحكم بعناصره.

الدالة مع تعريفها
pair<K, V> insert({key, value}) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها.
مكان الباراميتر key نمرر القيمة المفتاحية للعنصر الذي نريد إضافته.
مكان الباراميتر value نمرر القيمة التي نريد وضعها في العنصر الذي سيتم إضافته.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
pair<K, V> emplace(Args&&... args) تستخدم لإضافة عنصر جديد في الكائن الذي قام باستدعائها تماماً كالدالة insert().
مكان الباراميتر Args نمرر القيمة المفتاحية للعنصر الذي نريد إضافته ثم نضع فاصلة و نمرر القيمة التي نريد وضعها فيه.
كما أنها ترجع نسخة من العنصر الذي تم إضافته.
size_t erase(key_type& k) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال المفتاح الخاص به.
مكان الباراميتر k نمرر مفتاح العنصر الذي نريد حذفه.
iterator erase(const_iterator position) تستخدم لحذف عنصر محدد من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكانه فيه.
مكان الباراميتر position نمرر كائن أصله من Iterator يشير لعنوان العنصر الذي سيتم حذفه.
iterator erase(const_iterator first, const_iterator last) تستخدم لحذف مجموعة عناصر من الكائن الذي قام باستدعائها من خلال الإشارة إلى مكان وجود هذه العناصر.
مكان الباراميتر first نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم بدء الحذف من عنده.
مكان الباراميتر last نمرر كائن أصله من const_iterator يشير لعنوان العنصر الذي سيتم إيقاف الحذف عنده.
void swap(unordered_multimap& anotherMap) تستخدم لتبديل قيم عناصر الكائن الذي قام باستدعائها بقيم عناصر الكائن الذي نمرره لها.
مكان الباراميتر anotherMap نمرر لها كائن من الكلاس unordered_multimap يملك نفس نوع عناصر الكائن الذي قام باستدعائها.

الجدول التالي يحتوي على دوال الكلاس unordered_multimap التي تستخدم للبحث فيه.

الدالة مع تعريفها
size_t count(const key_type& k) تستخدم لمعرفة عدد العناصر التي تملك مفتاح محدد في الكائن الذي قام باستدعائها.
مكان الباراميتر k نمرر المفتاح الذي نريد معرفة عدد العناصر التي تملكه.
iterator find(const T& k) تستخدم للبحث في الكائن الذي قام باستدعائها عن مكان أول عنصر يملك مفتاح محدد.
مكان الباراميتر k نمرر المفتاح الذي نريد البحث عن أول عنصر يملكه.
في حال تم إيجاد عنصر يملك نفس المفتاح الذي تم تمريره لها ترجع كائن من Iterator يشير لعنوان العنصر في الذاكرة.
و في حال لم يتم إيجاد عنصر يملك نفس المفتاح الذي تم تمريره لها ترجع كائن من Iterator قيمته تساوي القيمة التي ترجعها الدالة end().

أمثلة شاملة حول التعامل مع الكلاس unordered_unordered_multimap في C++

في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن إسم البلد كمفتاح و إسم مدينة موجودة فيه كقيمة.
بعدها قمنا بطباعة عدد العناصر التي قمنا بإضافتها.
في الأخير قمنا بعرض جميع مفاتيح و قيم العناصر الموجودة فيه بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة emplace() لإضافة العناصر, الدالة size() لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin() للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end() للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.

المثال الأول

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<string, string> um;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("Egypt", "Giza");
      um.emplace("Lebanon", "Beirut");
      um.emplace("Syria", "Sweida");
      um.emplace("Lebanon", "Tripolie");
      um.emplace("Syria", "Idlib");
      um.emplace("KSA", "Riyadh");
      um.emplace("Egypt", "Cairo");
      um.emplace("Lebanon", "Sidon");
      um.emplace("Syria", "Damascus");
      um.emplace("KSA", "Makkah");
      um.emplace("Syria", "Homs");
      um.emplace("Algeria", "Algiers");

      // size() باستخدام الدالة um هنا قمنا بطباعة عدد عناصر الكائن
      cout << "um size = " << um.size() << "\n\n";

      cout << "um keys and values\n";
      cout << "---------------------\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << "\t | " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      um size = 12

      um keys and values
      ---------------------
      Algeria  | Algiers
      Lebanon  | Sidon
      Lebanon  | Tripolie
      Lebanon  | Beirut
      KSA      | Makkah
      KSA      | Riyadh
      Egypt    | Cairo
      Egypt    | Giza
      Syria    | Homs
      Syria    | Damascus
      Syria    | Idlib
      Syria    | Sweida
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن عدد و كمفتاح و إسمه باللغة الإنجليزية أو الفرنسية كقيمة.
بعدها قمنا بعرض القيم الموجودة فيه بواسطة حلقة.

المثال الثاني

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<int, string> um = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"},
      {5, "Five"},
      };

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      5 - Five
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن إسم البلد كمفتاح و إسم مدينة موجودة فيه كقيمة.
في الأخير قمنا بالبحث عن عدد العناصر التي تملك مفتاح محدد و طباعة نتيجة البحث.

ملاحظة: قمنا باستخدام الدالة count() لمعرفة عدد العناصر التي تملك مفتاح محدد في الكائن.

المثال الثالث

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<string, string> um;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("Egypt", "Giza");
      um.emplace("Lebanon", "Beirut");
      um.emplace("Syria", "Sweida");
      um.emplace("Lebanon", "Tripolie");
      um.emplace("Syria", "Idlib");
      um.emplace("KSA", "Riyadh");
      um.emplace("Egypt", "Cairo");
      um.emplace("Lebanon", "Sidon");
      um.emplace("Syria", "Damascus");
      um.emplace("KSA", "Makkah");
      um.emplace("Syria", "Homs");
      um.emplace("Algeria", "Algiers");

      // um في الكائن "Syria" هنا قمنا بطباعة عدد العناصر التي تملك قيمة تساوي
      cout << "Total number of elements with key 'Syria' = " << um.count("Syria");

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Total number of elements with key 'Syria' = 4
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن إسم البلد كمفتاح و إسم مدينة موجودة فيه كقيمة.
بعدها قمنا بحذف جميع العناصر التي تملك مفاتيح محددة منه و من ثم طباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة erase() لحذف جميع العناصر التي تملك مفاتيح محددة.

المثال الرابع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<string, string> um;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("Egypt", "Giza");
      um.emplace("Lebanon", "Beirut");
      um.emplace("Syria", "Sweida");
      um.emplace("Lebanon", "Tripolie");
      um.emplace("Syria", "Idlib");
      um.emplace("KSA", "Riyadh");
      um.emplace("Egypt", "Cairo");
      um.emplace("Lebanon", "Sidon");
      um.emplace("Syria", "Damascus");
      um.emplace("KSA", "Makkah");
      um.emplace("Syria", "Homs");
      um.emplace("Algeria", "Algiers");

      // um من الكائن "Syria" و المتفاح "Lebanon" هنا قمنا بحذف جميع العناصر التي تملك المفتاح
      um.erase("Lebanon");
      um.erase("Syria");

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " -> " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Algeria -> Algiers
      KSA -> Makkah
      KSA -> Riyadh
      Egypt -> Cairo
      Egypt -> Giza
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها int قيم و نوعها string.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن عدد و كمفتاح و إسمه باللغة الإنجليزية أو الفرنسية كقيمة.
بعدها قمنا بالبحث عن أول عنصر يملك مفتاح محدد فيه و من ثم قمنا بحذفه.
في الأخير قمنا بطباعة قيم جميع العناصر المتبقية بواسطة حلقة.

ملاحظة: قمنا باستخدام الدالة find() للبحث عن أول عنصر يملك مفتاح محدد في الكائن و الدالة erase() لحذف العنصر منه.

المثال الخامس

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<int, string> um = {
      {1, "Un"},
      {1, "One"},
      {2, "Deux"},
      {2, "Two"},
      {3, "Trois"},
      {3, "Three"}
      };

      // نجده يملك المفتاح الذي سنبحث عنه um لتخزين مكان أول عنصر في الكائن unordered_multimap<int, string>::iterator منا قمنا بتعريف كائن من 
      unordered_multimap<int, string>::iterator it;

      // it عن أول عنصر يملك مفتاح يساوي 2, و بالتالي في حال وجود عنصر يملك هذا المفتاح سيتم تخزين عنوانه في الكائن um هنا قمنا بالبحث في الكائن
      // فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذا المفتاح um.end() في حال عدم وجود عنصر يملك مفتاح يساوي 2 سيتم تخزين القيمة التي ترجعها
      it = um.find(2);

      // فهذا يعني أنه تم إيجاد العنصر و بالتالي سيتم تنفيذ الكود الموضوع بالداخل um.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
      if (it != um.end())
      {
      // و من ثم قمنا بحذفه um هنا قمنا بطباعة مفتاح و قيمة العنصر الذي تم إيجاده في الكائن
      cout << "Element with key '"<< it->first << "' and value '" << it->second << "' is removed from um\n\n";
      um.erase(it);
      }

      cout << "um keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه um هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = um.begin(); it != um.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      Element with key '2' and value 'Two' is removed from um

      um keys and values:
      3 - Three
      3 - Trois
      2 - Deux
      1 - One
      1 - Un
    


في المثال التالي قمنا بتعريف كائنين من unordered_multimap مع تحديد أن عناصرهما تتكون من مفاتيح نوعها int قيم و نوعها string مع إضافة عدة عناصر فيهما عند تعريفهم.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة مفاتيح و قيم عناصرهما بواسطة حلقة للتأكد من أن التبديل قد تم بنجاح.

ملاحظة: قمنا باستخدام الدالة swap() لتبديل عناصرهما.

المثال السادس

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<int, string> unordered_multimap1 = {
      {1, "One"},
      {2, "Two"},
      {3, "Three"},
      {4, "Four"}
      };

      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string و قيم نوعها int يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن آخر من الكلاس
      unordered_multimap<int, string> unordered_multimap2 = {
      {5, "Five"},
      {6, "Six"},
      {7, "Seven"},
      {8, "Eight"}
      };

      // unordered_multimap2 مع قيم الكائن unordered_multimap1 هنا قمنا بتبديل قيم الكائن
      unordered_multimap1.swap(unordered_multimap2);

      cout << "unordered_multimap1 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه unordered_multimap1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = unordered_multimap1.begin(); it != unordered_multimap1.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      cout << "\nunordered_multimap2 keys and values:\n";

      // إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه unordered_multimap2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيم عنصر جديد من العناصر الموجودة في الكائن
      for (auto it = unordered_multimap2.begin(); it != unordered_multimap2.end(); ++it)
      {
      // تمثل قيمة العمود الأول في العنصر و التي تعتبر المفتاح الخاص به first الخاصية
      // تمثل قيمة العمود الثاني في العنصر و التي تمثل قيمته second و الخاصية
      cout << it->first << " - " << it->second << "\n";
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      unordered_multimap1 keys and values:
      8 - Eight
      7 - Seven
      6 - Six
      5 - Five

      unordered_multimap2 keys and values:
      4 - Four
      3 - Three
      2 - Two
      1 - One
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مع تحديد أن عناصره تتكون من مفاتيح نوعها string قيم و نوعها string أيضاً.
بعدها قمنا بإضافة بعض العناصر فيه, كل عنصر عبارة منهم عبارة عن إسم البلد كمفتاح و إسم مدينة موجودة فيه كقيمة.
في الأخير قمنا بعرض مفاتيح و قيم العناصر الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size() لمعرفة عدد العناصر الموجودة في كل Bucket.

المثال السابع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // string و قيم نوعها string يمكنه أن يحتوي على مفاتيح نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      unordered_multimap<string, string> um;

      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      um.emplace("Egypt", "Giza");
      um.emplace("Lebanon", "Beirut");
      um.emplace("Syria", "Sweida");
      um.emplace("Lebanon", "Tripolie");
      um.emplace("Syria", "Idlib");
      um.emplace("KSA", "Riyadh");
      um.emplace("Egypt", "Cairo");
      um.emplace("Lebanon", "Sidon");
      um.emplace("Syria", "Damascus");
      um.emplace("KSA", "Makkah");
      um.emplace("Syria", "Homs");
      um.emplace("Algeria", "Algiers");

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة مفتاح و قيمة كل عصنر موجود فيها الحلقة التالية
      for(auto it=um.begin(i); it!=um.end(i); ++it)
      {
      cout << " [" << it->first << " : " << it->second << "]";
      }

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] elements: [KSA : Makkah] [KSA : Riyadh]
      bucket[1] elements:
      bucket[2] elements:
      bucket[3] elements: [Syria : Homs] [Syria : Damascus] [Syria : Idlib] [Syria : Sweida]
      bucket[4] elements:
      bucket[5] elements:
      bucket[6] elements:
      bucket[7] elements: [Lebanon : Sidon] [Lebanon : Tripolie] [Lebanon : Beirut]
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements:
      bucket[11] elements: [Egypt : Cairo] [Egypt : Giza]
      bucket[12] elements:
      bucket[13] elements:
      bucket[14] elements:
      bucket[15] elements: [Algeria : Algiers]
      bucket[16] elements:
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مخصص لتخزين عناصر تتكون من مفاتيح نوعها string و قيم نوعها string أيضاً مع إضافة عدة عناصر فيه عند تعريفه.
بعدها قمنا بعرض عدد العناصر الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

ملاحظة: قمنا باستخدام الدالة bucket_count() لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي.

المثال الثامن

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      int main()
      {
      // بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      // كل عنصر يحتوي على إسم البلد كمفتاح و إحدى مدنه كقيمة ,um هنا قمنا بإضافة 12 عنصر في الكائن
      unordered_multimap<string, string> um = {
      {"Egypt", "Giza"},
      {"Lebanon", "Beirut"},
      {"Syria", "Sweida"},
      {"Lebanon", "Tripolie"},
      {"Syria", "Idlib"},
      {"KSA", "Riyadh"},
      {"Egypt", "Cairo"},
      {"Lebanon", "Sidon"},
      {"Syria", "Damascus"},
      {"KSA", "Makkah"},
      {"Syria", "Homs"},
      {"Algeria", "Algiers"}
      };

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] total elements = " << um.bucket_size(i);

      cout << endl;
      }
      }
    

سنحصل على النتيجة التالية عند التشغيل.

      bucket[0] total elements = 0
      bucket[1] total elements = 0
      bucket[2] total elements = 3
      bucket[3] total elements = 0
      bucket[4] total elements = 0
      bucket[5] total elements = 6
      bucket[6] total elements = 1
      bucket[7] total elements = 0
      bucket[8] total elements = 0
      bucket[9] total elements = 0
      bucket[10] total elements = 2
      bucket[11] total elements = 0
      bucket[12] total elements = 0
    


في المثال التالي قمنا بتعريف كائن من unordered_multimap مخصص لتخزين مفاتيح نوعها string و قيم نوعها string أيضاً مع تحديد الطريقة التي سيتم على أساسها توزيع العناصر في Buckets و إضافة عدة عناصر فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع العناصر في Buckets قمنا بإنشاء كلاس إسمه Hash و إعادة تعريف العامل () حتى يقارن مفتاح أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع العناصر التي تملك مفتاح يتألف من حرف واحد في Bucket, و العناصر التي تملك مفتاح يتألف من حرفين في Bucket, و العناصر التي تملك مفتاح يتألف من ثلاثة أحرف في Bucket و هكذا..
في النهاية قمنا بعرض مفاتيح و قيم العناصر الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.

المثال التاسع

main.cpp
      #include <iostream>
      #include <unordered_map>

      using namespace std;

      // unordered_multimap سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه
      class Hash
      {
      public:
      // Bucket حتى يقرر في أي unordered_multimap هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
      // key سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن المفتاح الذي يملكه العنصر سيتم تمريره مكان الباراميتر
      size_t operator() (const string &key) const
      {
      // سيتم إضافة العنصر Bucket في النهاية سيتم إرجاع عدد أحرف مفتاح العنصر لأنها التي ستحدد في أي
      return key.length();
      }
      };

      int main()
      {
      // مع تحديد أنه يعتمد string يمكنه أن يحتوي على عناصر مفاتيحها و قيمها نوعها unordered_multimap هنا قمنا بتعريف كائن من الكلاس
      // من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash على الكلاس
      unordered_multimap<string, string, Hash> um = {
      {"Egypt", "Giza"},
      {"Lebanon", "Beirut"},
      {"Syria", "Sweida"},
      {"Lebanon", "Tripolie"},
      {"Syria", "Idlib"},
      {"KSA", "Riyadh"},
      {"Egypt", "Cairo"},
      {"Lebanon", "Sidon"},
      {"Syria", "Damascus"},
      {"KSA", "Makkah"},
      {"Syria", "Homs"},
      {"Algeria", "Algiers"}
      };

      // totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
      int totalBuckets = um.bucket_count();

      // um الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
      for(int i=0; i<totalBuckets; i++)
      {
      // Bucket في كل دورة سيتم طباعة رقم الـ
      cout << "bucket[" << i << "] elements:";

      // و من ثم طباعة مفتاح و قيمة كل عصنر موجود فيها الحلقة التالية
      for(auto it=um.begin(i); it!=um.end(i); ++it)
      {
      cout << " [" << it->first << " : " << it->second << "]";
      }

      cout << endl;
      }

      return 0;
      }
    

سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأحرف التي يتألف منها كل عنصر.

      bucket[0] elements:
      bucket[1] elements:
      bucket[2] elements:
      bucket[3] elements: [KSA : Makkah] [KSA : Riyadh]
      bucket[4] elements:
      bucket[5] elements: [Syria : Homs] [Syria : Damascus] [Syria : Idlib] [Syria : Sweida] [Egypt : Cairo] [Egypt : Giza]
      bucket[6] elements:
      bucket[7] elements: [Algeria : Algiers] [Lebanon : Sidon] [Lebanon : Tripolie] [Lebanon : Beirut]
      bucket[8] elements:
      bucket[9] elements:
      bucket[10] elements:
      bucket[11] elements:
      bucket[12] elements:
    
______
نلتقي في دروس قادمة
تعليقات