You are on page 1of 67

‫الفصــــــــــل ‪ .

3‬السلسلــــــــــة‪ ،‬املتجهــــــــــات واملصفوفــــــــــات‬


‫‪Chapter 3. Strings, Vectors, and Arrays‬‬

‫الـمحتويات‬
‫‪Contents‬‬

‫‪3.1 Namespace using Declarations‬‬ ‫‪ 3٫1‬إعالنات مساحـــــة اﻻسم ‪Using‬‬

‫‪3.2 Library string Type‬‬ ‫‪ 3٫2‬نـــــوع مكتبة السلسلـــــة‬

‫‪3.3 Library vector Type‬‬ ‫‪ 3٫3‬نـــــوع مكتبة املتجـــــه‬

‫‪3.4 Introducing Iterators‬‬ ‫‪ 3٫4‬مقدمـــــة عن املكـــــررات‬

‫‪3.5 Arrays‬‬ ‫‪ 3٫5‬املصفوفـــــات‬

‫‪3.6 Multidimensional Arrays‬‬ ‫‪ 3٫6‬املصفوفـــــات متعددة اﻷبعـــــاد‬

‫‪Chapter Summary‬‬ ‫ملخــــــص الفصــــــل‬

‫‪Defined Terms‬‬ ‫مصطلحــــــات معرفــــــة‬

‫إضافة إلى األنواع املدمجة اليت تم تناولها في الفصل الثاني ‪،‬‬


‫تعرف لغة يس‪ ++‬مكتبة غنية ألنواع بيانات مجردة‪.‬‬
‫ّ‬ ‫•‬
‫من بني أهم األنواع مكتبة السلسلة ‪ ،string‬اليت تدعم سالسل أحرف متغرية الطول ‪،‬‬ ‫•‬
‫تعرف مجموعات ذات أحجام متغرية‪.‬‬
‫ومكتبة املتجه ‪ ، vector‬واليت ّ‬ ‫•‬
‫ويرتبط بهما أنواع مصاحبة تُعرف باملكررات ‪ ،‬تُستخدم للوصول لألحرف في سلسلة أو عنارص في املتجه‪.‬‬ ‫•‬
‫تعرفها املكتبة عبارة عن تجريدات من نوع املصفوفة املدمجة األكرث بدائية‪.‬‬
‫أنواع السلسلة واملتجه اليت ّ‬ ‫•‬
‫يغطي هذا الفصل املصفوفات ‪ arrays‬وكذلك يقدم نوعي مكتبة املتجه والسالسل‪.‬‬ ‫•‬

‫معرفة مبارشة في لغة يس‪.++‬‬ ‫األنواع املدمجة اليت غطيناها في الفصل الثاني هي أنواع ّ‬
‫• تمثل هذه األنواع أقساما موجودة في معظم أجهزة الكمبيوتر ‪ ،‬مثل األرقام أو األحرف‪.‬‬
‫تعرف املكتبة القياسية عد ًدا من األنواع اإلضافية ذات املستوى األعلى واليت ال تنفذها أجهزة الكمبيوتر‬
‫ّ‬ ‫•‬
‫عاد ًة بشكل مبارش‪.‬‬

‫في هذا الفصل ‪ ،‬سوف نقدم اثنني من أهم أنواع املكتبة ‪ :‬السلسلة ‪ string‬واملتجه ‪.vector‬‬
‫السلسلة عبارة عن تسلسل متغري الطول من األحرف‪.‬‬ ‫•‬
‫أما املتجه فيحمل سلسلة متغرية الطول من كائنات نوع معني‪.‬‬ ‫•‬
‫أيضا نوع املصفوفة املدمجة‪.‬‬
‫سنغطي ً‬ ‫•‬
‫ومثل األنواع املدمجة األخرى ‪ ،‬تمثل املصفوفات أقسام العتاد الصلب‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬تكون املصفوفات أقل مالءمة لالستخدام من أنواع مكتبة السلسلة واملتجهات‪.‬‬ ‫•‬

‫املعرفة في املكتبة‪.‬‬
‫ّ‬ ‫وقبل البدء في استكشاف أنواع املكتبة ‪ ،‬سنلقي نظرة على آلية لتبسيط الوصول إلى األسماء‬
‫‪ . 3٫1‬إعالنات مساحـــــة اﻻسم ‪Using‬‬
‫‪3.1. Namespace using Declarations‬‬

‫حىت اآلن ‪ ،‬أشارت برامجنا رصاح ًة إلى أن كل اسم مكتبة نستخدمه موجود في مساحة االسم ‪.std‬‬
‫فعلى سبيل املثال ‪ ،‬للقراءة من املدخالت القياسية ‪ ،‬نكتب ‪.std::cin‬‬ ‫•‬
‫تستخدم هذه األسماء عامل النطاق (‪ ، )::‬والذي يخرب بأن املرتجم يجب أن يبحث في نطاق املعامل األيرس‬ ‫•‬
‫عن اسم املعامل األيمن‪.‬‬
‫وهكذا ‪ ،‬يخرب ‪ std::cin‬إننا نريد استخدام اسم ‪ cin‬من مساحة االسم ‪.std‬‬ ‫•‬

‫يمكن أن تكون اإلشارة إلى أسماء املكتبة باستخدام هذا الرتمزي مرهقة‪.‬‬
‫لحسن الحظ ‪ ،‬هناك طرق أسهل الستخدام أعضاء مساحة االسم‪.‬‬ ‫•‬
‫الطريقة األكرث أما ًنا هي إعالن ‪ .using‬تغطي الفقرة ‪ 18.2.2‬طريقة أخرى الستخدام األسماء من مساحة‬ ‫•‬
‫االسم‪.‬‬
‫يتيح لنا إعالن ‪ using‬استخدام اسم مساحة اسم دون تقييد االسم ببادئة‪: :‬اسم_مساحة_اسم ‪ .‬إعالن ‪using‬‬
‫يكون بصيغة‪:‬‬

‫;‪using namespace::name‬‬

‫بمجرد عمل إعالن ‪ ، using‬يمكننا الوصول إلى االسم مبارشة ‪:‬‬

‫>‪#include <iostream‬‬
‫‪//using declaration; when we use name cin, we get one from namespace std‬‬
‫;‪using std::cin‬‬
‫{ )(‪int main‬‬
‫;‪int i‬‬
‫;‪cin >> i‬‬ ‫‪// ok: cin synonym for std::cin‬‬
‫‪cout << i; // error: no using declaration; we must use full name‬‬
‫‪std::cout << i; // ok: explicitly use cout from namespace std‬‬
‫;‪return 0‬‬
‫}‬

‫مطلوب إعالن ‪ using‬منفصل لكل اسم‬


‫‪A Separate using Declaration Is Required for Each Name‬‬

‫يقدم كل إعالن ‪ using‬عضو مساحة اسم واحد‪.‬‬


‫يتيح لنا هذا السلوك أن نكون محددين بشأن األسماء اليت نستخدمها‪.‬‬ ‫•‬

‫على سبيل املثال ‪ ،‬سنعيد كتابة الربنامج من فقرة ‪ 1.2‬بإعالنات ‪ using‬ألسماء املكتبة اليت تستخدمها‪:‬‬

‫>‪#include <iostream‬‬
‫‪// using declarations for names from standard library‬‬
‫;‪using std::cin‬‬ ‫;‪using std::cout‬‬ ‫;‪using std::endl‬‬
‫{ )(‪int main‬‬
‫;‪cout << "Enter two numbers:" << endl‬‬
‫;‪int v1, v2‬‬
‫;‪cin >> v1 >> v2‬‬
‫‪cout << "The sum of " << v1 << " and " << v2‬‬
‫;‪<< " is " << v1 + v2 << endl‬‬
‫;‪return 0‬‬
‫}‬

‫إعالنات ‪ using‬لـ ‪ cin ، cout ، endl‬تعين أنه يمكننا استخدام هذه األسماء بدون البادئة ‪.std: :‬‬ ‫•‬
‫تذكر أن برامج يس‪ ++‬حرة الشكل ‪ ،‬لذلك يمكننا وضع كل إعالن ‪ using‬في سطر خاص به أو دمج عدة‬ ‫•‬
‫منها في سطر واحد‪.‬‬
‫أهم جزء هو أنه يجب أن يكون هناك إعالن ‪ using‬لكل اسم نستخدمه ‪ ،‬وأن ينتهي كل منها بفاصلة‬ ‫•‬
‫منقوطة‪.‬‬

‫ﻻ ينبغي أن تتضمن العناوين إعالنات ‪using‬‬

‫‪Headers Should Not Include using Declarations‬‬

‫عاد ًة ينبغي أﻻ يستخدم الكود داخل العناوين إعالنات ‪.using‬‬


‫والسبب هو أن محتويات ذلك العنوان يتم نسخها إلى داخل نص الربنامج املتضمن‪.‬‬ ‫•‬
‫فإذا كان عنوان ما يحتوي على إعالن ‪ ، using‬فسيحصل كل برنامج يتضمن هذا العنوان على نفس إعالن‬ ‫•‬
‫‪.using‬‬
‫نتيجة لذلك ‪ ،‬فقد يواجه برنامج ال يقصد استخدام اسم املكتبة املحددة تعارضات غري متوقعة في االسم‪.‬‬ ‫•‬

‫‪A Note to the Reader‬‬ ‫مالحظة إلى القراء‬

‫من اآلن فصاع ًدا ‪،‬‬


‫ستفرتض أمثلتنا أنه تم عمل إعالن ‪ using‬لألسماء اليت نستخدمها من املكتبة القياسية‪.‬‬ ‫•‬
‫وبالتالي ‪ ،‬سوف نشري إلى ‪ ، cin‬وليس ‪ ، std::cin‬في النص وفي أمثلة الكود‪.‬‬ ‫•‬
‫عالوة على ذلك ‪ ،‬وللحفاظ على أمثلة اﻷكواد قصرية ‪،‬‬ ‫•‬
‫لن نعرض إعالنات ‪ ، using‬ولن نعرض التوجيهات الرضورية ‪.#include‬‬ ‫•‬

‫يرسد الجدول ‪ A-1‬في امللحق ‪ A‬قائمة األسماء والعناوين املقابلة ألسماء املكتبة القياسية اليت نستخدمها في هذا‬
‫الكتاب التمهيدي‪.‬‬

‫تحذير‬

‫ينبغي أن يدرك القراء أن عليهم إضافة ‪ #include‬وإعالنات ‪ using‬املناسبة ألمثلتنا قبل تجميعها‪.‬‬

‫تمارين القسم ‪3.1‬‬


‫التمرين ‪:3.1‬‬
‫أعد كتابة التدريبات من الفقرة ‪ 1.4.1‬والفقرة ‪ 2.6.2‬باستخدام إعالنات ‪ using‬املناسبة‪.‬‬
‫‪ . 3٫2‬نـــــوع مكتبة السلسلـــــة‬
‫‪3.2. Library string Type‬‬

‫السلسلة ‪ string‬عبارة عن تسلسل متغري الطول من األحرف‪.‬‬


‫الستخدام نوع السلسلة ‪ ،‬يجب علينا تضمني عنوان السلسلة ‪.string‬‬ ‫•‬
‫ونظرًا ألنها جزء من املكتبة ‪ ،‬يتم تعريف السلسلة في مساحة االسم ‪.std‬‬ ‫•‬

‫تفرتض األمثلة لدينا الكود التالي‪:‬‬

‫>‪#include <string‬‬
‫;‪using std::string‬‬

‫شيوعا ؛ ستغطي الفقرة ‪ 9.5‬عمليات إضافية‪.‬‬


‫ً‬ ‫يصف هذا القسم عمليات السلسلة األكرث‬

‫ملحوظة‬

‫أيضا متطلبات كفاءة على املنفذات‪.‬‬


‫باإلضافة إلى تحديد العمليات اليت توفرها أنواع املكتبة ‪ ،‬يفرض املعيار ً‬
‫نتيجة لذلك ‪ ،‬تعد أنواع املكتبة فعالة بدرجة كافية لالستخدام العام‪.‬‬

‫‪3.2.1. Defining and Initializing strings‬‬ ‫‪ 3٫2٫1‬تعريف وتهيئة السالسل‬

‫تعرف كل فئة كيف يمكن تهيئة كائنات من نوعها‪.‬‬


‫يمكن للفئة تعريف عدة طرق مختلفة لتهيئة كائنات من نوعها‪.‬‬ ‫•‬
‫املهيِـئات اليت نوفرها ‪ ،‬أو من خالل أنواع تلك‬
‫يجب تميزي كل طريقة عن الطرق األخرى إما من خالل عدد ّ‬ ‫•‬
‫املهيِـئات‪.‬‬
‫ّ‬
‫شيوعا لتهيئة السالسل‪ .‬بعض األمثلة‪:‬‬
‫ً‬ ‫يرسد الجدول ‪ 3.1‬أكرث الطرق‬

‫;‪string s1‬‬ ‫‪// default initialization; s1 is the empty string‬‬


‫;‪string s2 = s1‬‬ ‫‪// s2 is a copy of s1‬‬
‫;"‪string s3 = "hiya‬‬ ‫‪// s3 is a copy of the string literal‬‬
‫;)'‪string s4(10, 'c‬‬ ‫‪// s4 is cccccccccc‬‬

‫الجدول ‪ .3.1‬طرق تهيئة سلسلة‬

‫‪string s1‬‬ ‫‪default initialization; s1 is the empty string‬‬

‫)‪string s2 (s1‬‬ ‫‪s2 copy of s1.‬‬

‫‪string s2 = s1‬‬ ‫‪Equivalent to s2 (s1). s2 copy of s1.‬‬

‫)”‪string s3 (“value‬‬ ‫‪s3 copy of string literal, not including null.‬‬

‫”‪string s3 = “value‬‬ ‫‪Equivalent s3(“value”), s3 copy of string literal.‬‬

‫)’‪String s4 (n, ‘c‬‬ ‫‪Initialize s4 with n copies of character ‘c’.‬‬

‫يمكننا بشكل افرتايض تهيئة سلسلة ‪ ،‬ما يؤدي إلى إنشاء سلسلة فارغة ؛ أي سلسلة بدون أحرف‪.‬‬ ‫•‬
‫عندما نوفر حرفية سلسلة ‪ ،‬يتم نسخ األحرف من قيم ذلك الحرفي ‪ -‬حىت وإن لم يتضمن الحرف الخالي في‬ ‫•‬
‫نهاية الحرفي ‪ -‬إلى السلسلة املنشأة حديثًا‪.‬‬
‫وعندما نوفر عد ًدا وحر ًفا ‪ ،‬فإن السلسلة تحتوي على عدة نسخ من الحرف املحدد‪.‬‬ ‫•‬

‫‪Direct and Copy Forms of Initialization‬‬ ‫أشكال التهيئة املبارشة وتهيئة النسخ‬

‫في الفقرة ‪ 2.2.1‬رأينا أن لغة يس‪ ++‬تملك عدة صيغ مختلفة من التهيئة‪.‬‬
‫باستخدام السالسل ‪ ،‬يمكننا البدء في فهم كيفية اختالف هذه النماذج عن بعضها‪.‬‬ ‫•‬
‫عندما نقوم بتهيئة متغري باستخدام = ‪ ،‬فإننا نطلب من املرتجم نسخ تهيئة الكائن عن طريق نسخ املُهئي‬ ‫•‬
‫على الجانب األيمن إلى الكائن الذي يتم إنشاؤه‪.‬‬
‫عدا ذلك ‪ ،‬عندما نحذف = ‪ ،‬نستخدم التهيئة املبارشة‪.‬‬ ‫•‬
‫عندما يكون لدينا ُمهئي واحد ‪ ،‬يمكننا استخدام إما شكل التهيئة املبارشة أو تهيئة النسخة‪.‬‬ ‫•‬
‫وعندما نقوم بتهيئة متغري على أكرث من قيمة واحدة ‪ ،‬كما هو الحال في تهيئة ‪ s4‬أعاله ‪،‬‬ ‫•‬
‫يجب أن نستخدم الشكل املبارش للتهيئة‪:‬‬ ‫•‬

‫;”‪string s5 = “hiya‬‬ ‫‪// copy initialization‬‬


‫;)”‪string s6(“hiya‬‬ ‫‪// direct initialization‬‬
‫;)’‪string s7(10, ‘c‬‬ ‫‪// direct initialization; s7 is cccccccccc‬‬

‫عندما نريد استخدام عدة قيم ‪ ،‬يمكننا استخدام صيغة التهيئة بشكل غري مبارش عن طريق إنشاء كائن (مؤقت) بشكل‬
‫رصيح لنسخه‪:‬‬

‫‪string s8 = string(10, ‘c’); // copy initialization; s8 is cccccccccc‬‬

‫يقوم ُمهئي ‪ s8‬بإنشاء سلسلة ‪ - string(10, ‘c’) -‬بالحجم املحدد وقيمة الحرف ثم ينسخ تلك القيمة إلى‬
‫‪ .s8‬سيبدو األمر كما لو كنا قد كتبنا‬

‫;)’‪string temp(10, ‘c‬‬ ‫‪// temp is cccccccccc‬‬


‫;‪string s8 = temp‬‬ ‫‪// copy temp into s8‬‬

‫وبالرغم من أن الكود املستخدم لتهيئة ‪ s8‬رشعي ‪ ،‬إال أنه أقل قابلية للقراءة وال يقدم أي مزية تعويضية على‬
‫الطريقة اليت هيأنا بها ‪.s7‬‬

‫‪3.2.2. Operations on strings‬‬ ‫‪ 3٫2٫2‬العمليات على السالسل‬

‫إلى جانب تحديد كيفية إنشاء الكائنات وتهيئتها ‪،‬‬


‫أيضا العمليات اليت يمكن أن تؤديها كائنات نوع الفئة‪.‬‬
‫تعرف الفئة ً‬ ‫•‬
‫فيمكن للفئة تعريف العمليات اليت يتم استدعاؤها عن طريق اسم ‪ ،‬مثل دالة ‪ isbn‬لفئتنا عنرص املبيعات‪.‬‬ ‫•‬
‫أيض ا تعريف ما تعنيه رموز عوامل مختلفة ‪ ،‬مثل << أو ‪ ، +‬عند تطبيقها على كائنات نوع‬
‫يمكن للفئة ً‬ ‫•‬
‫الفئة‪.‬‬
‫شيوعا‪.‬‬
‫ً‬ ‫يرسد الجدول ‪ 3.2‬عمليات السلسلة األكرث‬

‫الجدول ‪ .3.2‬عمليات السلسلة‬


os << s Write s onto output stream os. Return os.

Reads whitespace-separated string from is into s.


is >> s Return is.

getline(is, s) Reads a line of input from is into s. Return is.

s.empty() Returns true if s is empty. Otherwise return false.

s.size() Returns the number of character in s.

Returns a reference to the char at position n in s.


s[n] Position start at 0.

Returns a string that is the concatenation of s1 and


s1 + s2 s2.

s1 = s2 Replace character in s1 with a copy of s2.

s1 == s2 The string s1 and s2 are equal if they contain the


s1 != s2 same character. Equality is case-sensitive.

Comparisons are case-sensitive and use dictionary


<, <=, >, >= ordering.

Reading and Writing strings ‫سالسل القراءة والكتابة‬

، ‫كما رأينا في الفصل األول‬


، ‫ لقراءة وكتابة قيم األنواع املدمجة‬iostream ‫نستخدم مكتبة قناة املدخالت واملخرجات‬ •
.‫ وما إلى ذلك‬، double ‫ واملزدوج‬، int ‫مثل اﻷعداد الصحيحة‬ •

:‫ املخرجات لقراءة وكتابة السالسل‬/ ‫سنستخدم نفس عوامل املدخالت‬

// Note: #include and using declarations must added to compile this code
int main() {
string s; // empty string
cin >> s; // read a whitespace-separated string into s
cout << s << endl; // write s to output
return 0;
}

.s ‫يبدأ هذا الربنامج بتعريف سلسلة فارغة تسمى‬ •


.s ‫ ويخزن ما يقرأ في‬، ‫يقرأ السطر التالي املدخالت القياسية‬ •
‫يقرأ عامل املدخالت السلسلة ويتجاهل أي مسافة بيضاء أولية (مثالً املسافات واألسطر الجديدة والتاب)‪.‬‬ ‫•‬
‫ثم يقرأ األحرف حىت يتم العثور على حرف املسافة البيضاء التالي‪.‬‬ ‫•‬
‫لذا ‪ ،‬إن كانت املدخالت في هذا الربنامج هي ‪( ! Hello World‬الحظ املسافات األمامية والخلفية) ‪،‬‬ ‫•‬
‫فستكون املخرجات ‪ Hello‬بدون مسافات إضافية‪.‬‬ ‫•‬
‫ومثل عمليات املدخالت واملخرجات على األنواع املدمجة ‪ ،‬فإن عامل السلسلة يعيد معامله األيرس كنتيجة‪.‬‬ ‫•‬

‫بالتالي ‪ ،‬يمكننا ربط عدة عمليات قراءة أو كتابة‪:‬‬

‫;‪string s1, s2‬‬


‫;‪cin >> s1 >> s2‬‬ ‫‪// read first input into s1, second into s2‬‬
‫;‪cout << s1 << s2 << endl‬‬ ‫‪// write both strings‬‬

‫إذا أعطينا هذا اإلصدار من الربنامج نفس املدخالت ‪ ! Hello World ،‬فستكون مخرجاتنا "‪"! HelloWorld‬‬

‫‪Reading an Unknown Number of strings‬‬ ‫قراءة عدد غري معروف من السالسل‬

‫برنامجا يقرأ عد ًدا غري معروف من قيم عدد صحيح‪.‬‬


‫ً‬ ‫في الفقرة ‪ 1.4.3‬كتبنا‬
‫يمكننا كتابة برنامج مشابه يقرأ السالسل بدال ً من ذلك ‪:‬‬

‫{ )(‪int main‬‬
‫;‪string word‬‬
‫)‪while (cin >> word‬‬ ‫‪// read until end-of-file‬‬
‫‪cout << word << endl; // write each word followed by new line‬‬
‫;‪return 0‬‬
‫}‬

‫في هذا الربنامج ‪ ،‬نقرأ إلى سلسلة ‪ ،‬وليس عدد صحيح‪.‬‬ ‫•‬
‫عدا ذلك ‪ ،‬يتم تنفيذ رشط ‪ while‬بشكل مشابه لذلك املوجود في برنامجنا السابق‪.‬‬ ‫•‬
‫يخترب الرشط القناة بعد اكتمال القراءة‪.‬‬ ‫•‬
‫فإن كانت القناة صالح ًة ‪ -‬لم تصل لنهاية امللف أو تواجه مدخالً غري صالح ‪-‬‬ ‫•‬
‫فسيتم تنفيذ جملة ‪.while‬‬ ‫•‬
‫تطبع الجملة القيمة اليت نقرأها على املخرجات القياسية‪.‬‬ ‫•‬
‫وبمجرد أن نصل نهاية امللف (أو نواجه مدخالً غري صالح) ‪ ،‬فإننا نخرج من ‪.while‬‬ ‫•‬

‫‪Using getline to Read an Entire Line‬‬ ‫استخدام ‪ getline‬لقراءة سطر كامل‬

‫أحياناً ال نريد تجاهل املسافة البيضاء في مدخالتنا‪.‬‬


‫في مثل هذه الحاالت ‪ ،‬يمكننا استخدام دالة ‪ getline‬بدال ً من العامل <<‪.‬‬ ‫•‬
‫تأخذ الدالة ‪ getline‬قناة املدخالت وسلسلة‪.‬‬ ‫•‬
‫وتضمن وتخزّ ن ما قرأته ‪ -‬دون تضمني السطر‬
‫ّ‬ ‫تقرأ هذه الدالة القناة املعينة حىت السطر الجديد األول‬ ‫•‬
‫الجديد – إلى وسيطة السلسلة الخاصة‪.‬‬
‫بعد أن ترى ‪ getline‬سطرًا جدي ًدا ‪ ،‬حىت لو كان الحرف األول في املدخالت ‪ ،‬فإنها تتوقف عن القراءة‬ ‫•‬
‫وتعود‪.‬‬
‫إن كان الحرف األول في املدخالت سطرًا جدي ًدا ‪ ،‬فالسلسلة الناتجة هي سلسلة فارغة‪.‬‬ ‫•‬

‫مثل عامل املدخالت ‪،‬‬


‫تعيد ‪ getline‬وسيطة قناة املدخالت ‪ istream‬الخاصة بها‪.‬‬ ‫•‬
‫تماما كما يمكننا استخدام عامل املدخالت كرشط‪.‬‬
‫ً‬ ‫نتيجة لذلك ‪ ،‬يمكننا استخدام ‪ getline‬كرشط‬ ‫•‬

‫فمثالً ‪ ،‬يمكننا إعادة كتابة الربنامج السابق الذي يكتب كلمة واحدة كل سطر لكتابة سطر في املرة بدال ً من ذلك ‪:‬‬

‫{ )(‪int main‬‬
‫;‪string line‬‬
‫‪// read input line at time until end-of-file‬‬
‫))‪while (getline(cin, line‬‬
‫;‪cout << line << endl‬‬
‫;‪return 0‬‬
‫}‬

‫خاصا بنا‪.‬‬
‫ً‬ ‫ونظرًا ألن السطر ال يحتوي على سطر جديد ‪ ،‬يجب أن نكتب سطرًا‬ ‫•‬
‫كالعادة ‪ ،‬نستخدم ‪ endl‬إلنهاء السطر الحالي ومسح املخزن املؤقت‪.‬‬ ‫•‬

‫ملحوظة‬

‫يتم تجاهل السطر الجديد الذي يتسبب في إعادة ‪ getline‬؛ ال يتم تخزين السطر الجديد في السلسلة‪.‬‬

‫‪The string empty and size Operations‬‬ ‫عمليات الفراغ والحجم للسلسلة‬

‫تقوم الدالة ‪ empty‬بما يتوقعه املرء‪ :‬إعادة منطقي يشري إلى ما إذا كانت السلسلة فارغة‪.‬‬
‫ومثل العضو ‪ isbn‬في عنرص املبيعات ‪ ،‬الدالة ‪ empty‬هي دالة عضو في عنوان سلسلة‪.‬‬ ‫•‬
‫الستدعاء هذه الدالة ‪ ،‬نستخدم عامل النقطة لتحديد الكائن الذي نريد تشغيل الدالة ‪ empty‬عليه‪.‬‬ ‫•‬

‫يمكننا مراجعة الربنامج السابق لطباعة األسطر غري الفارغة فقط‪:‬‬

‫‪// read input a line at a time and discard blank lines‬‬


‫))‪while (getline(cin, line‬‬
‫))(‪if (!line.empty‬‬
‫;‪cout << line << endl‬‬

‫يستخدم الرشط عامل املنطق ‪( NOT‬العامل!)‪.‬‬ ‫•‬


‫يعيد هذا العامل معكوس القيمة املنطقية ملعامله‪.‬‬ ‫•‬
‫فارغا‪.‬‬
‫ً‬ ‫صحيحا إن لم يكن ‪string‬‬
‫ً‬ ‫في هذه الحالة ‪ ،‬يكون الرشط‬ ‫•‬
‫يعيد عضو الحجم ‪ size‬طول السلسلة (أي عدد األحرف املوجودة فيه)‪.‬‬ ‫•‬

‫يمكننا استخدام ‪ size‬لطباعة األسطر اليت يزيد طولها عن ‪ 80‬حر ًفا فقط ‪:‬‬

‫;‪string line‬‬
‫‪// read input line at time and print lines that longer than 80 characters‬‬
‫))‪while (getline(cin, line‬‬
‫)‪if (line.size() > 80‬‬
‫;‪cout << line << endl‬‬

‫‪The string::size_type Type‬‬ ‫نوع السلسلة نوع_الحجم‬

‫قد يكون من املنطقي أن نتوقع أن ‪ size‬تعيد عدداً صحيحاً أو ‪ -‬بالتفكري في الفقرة ‪ – 2.1.1‬بال_إشارة‪.‬‬
‫بدال ً من ذلك ‪ ،‬يقوم ‪ size‬بإعادة قيمة ‪.string::size_type‬‬ ‫•‬
‫هذا النوع سيتطلب القليل من الرشح‪.‬‬ ‫•‬

‫أنواعا مصاحبة متعددة‪.‬‬


‫ً‬ ‫تعرف فئة السلسلة ‪ -‬ومعظم أنواع املكتبة األخرى ‪-‬‬
‫ّ‬
‫• هذه األنواع املصاحبة تجعل من املمكن استخدام أنواع املكتبة بطريقة مستقلة عن اآللة‪.‬‬
‫النوع ‪ size_type‬أحد تلك األنواع املصاحبة‪.‬‬ ‫•‬
‫املعرف بواسطة السلسلة ‪ ،‬نستخدم عامل النطاق (‪ ).‬لإلخبار بإن االسم‬
‫ّ‬ ‫والستخدام ‪size_type‬‬ ‫•‬
‫‪ size_type‬معرف في فئة السلسلة‪.‬‬

‫وبالرغم من أننا ال نعرف النوع الدقيق لـ ‪، string::size_type‬‬


‫إال أننا نعلم أنها من نوع بال_إشارة وحجمها كبري بما يكفي الستيعاب حجم أي سلسلة‪.‬‬ ‫•‬
‫لذلك ينبغي على أي متغري يستخدم لتخزين نتيجة عائدة من عملية حجم سلسلة أن يكون من نوع‬ ‫•‬
‫‪.string::size_type‬‬
‫وألنه من املسلم به أن كتابة ‪ string::size_type‬يمكن أن يكون ممالً ؛‬ ‫•‬
‫فتحت املعيار الجديد‪ ،‬يمكننا أن نطلب من املرتجم توفري النوع املناسب ‪،‬‬ ‫•‬
‫وذلك باستخدام النوع التلقائي ‪ auto‬أو النوع‪-‬املعلن ‪: decltype‬‬ ‫•‬

‫‪auto len = line.size(); // len has type string::size_type‬‬

‫نوعا بال_إشارة ‪ ،‬فمن الرضوري أن نتذكر أن التعبريات اليت تمزج البيانات ذات‬
‫ونظرًا ألن ‪ size‬تعيد ً‬ ‫•‬
‫اﻹشارة مع الال_إشارة قد تكون لها نتائج مفاجئة‪.‬‬
‫تقريبا أن‬
‫ً‬ ‫فعلى سبيل املثال ‪ ،‬إذا كانت ‪ n‬عبارة عن عدد صحيح ‪ int‬تحتوي على قيمة سالبة ‪ ،‬فمن املؤكد‬ ‫•‬
‫‪ s.size() < n‬سيتم تقييمها صحيحة‪.‬‬
‫سينتج عن ذلك تقييم صحيح ألن القيمة السالبة في ‪ n‬ستتحول إلى قيمة كبرية بال_إشارة‪.‬‬ ‫•‬

‫تلميح‬

‫يمكنك تجنب املشاكل الناجمة عن التحويل بني الال_إشارة والعدد الصحيح ‪int‬‬
‫وذلك عن طريق عدم استخدام األعداد الصحيحة ‪ ints‬في تعبريات تستخدم ‪.)( size‬‬

‫‪Comparing strings‬‬ ‫مقارنة السالسل‬

‫تعرف فئة السلسلة العديد من العوامل اليت تقارن السالسل‪.‬‬


‫تعمل هذه العوامل من خالل مقارنة أحرف السالسل‪.‬‬ ‫•‬
‫املقارنات حساسة لحالة األحرف – ﻷن اإلصدارات الكبرية والصغرية من الحرف تعترب أحرفاً مختلفة‪.‬‬ ‫•‬
‫تخترب عوامل التساوي ( == و =! ) ما إذا كانت سلسلتان متساويتان أو غري متساويتني ‪ ،‬على التوالي‪.‬‬ ‫•‬
‫• فتكون السلسلتان متساويتان إن كانتا بنفس الطول وتحمالن نفس األحرف‪.‬‬
‫• العوامل العالئقية <‪ = >، >، =< ،‬تخترب ما إذا كانت إحدى السالسل أقل من أو أقل من أو تساوي أو أكرب‬
‫من أو أكرب من أو تساوي األخرى‪.‬‬
‫• تستخدم هذه العوامل نفس اسرتاتيجية القاموس (الحساس لحالة األحرف)‪:‬‬
‫‪ .1‬فإن كان للسلسلتني أطوال مختلفة وإن كان كل حرف في السلسلة األقرص مساو لحرف السلسلة املقابل‬
‫للسلسلة األطول ‪ ،‬فإن السلسلة األقرص تكون أقل من السلسلة األطول‪.‬‬
‫‪ .2‬أما إذا اختلف أي أحرف في املواضع املقابلة في السلسلتني ‪ ،‬فإن نتيجة مقارنة السلسلة هي نتيجة مقارنة‬
‫أول حرف تختلف عنده السلسلتان‪.‬‬
‫كمثال ‪ ،‬فكر في السالسل التالية‪:‬‬

‫‪string str‬‬ ‫;"‪= "Hello‬‬


‫;"‪string phrase = "Hello World‬‬
‫‪string slang‬‬ ‫;"‪= "Hiya‬‬

‫باستخدام القاعدة ‪ ، 1‬نرى أن ‪ str‬أقل من ‪.phrase‬‬ ‫•‬


‫أما من خالل تطبيق القاعدة ‪ ، 2‬نرى أن ‪ slang‬أكرب من كل من ‪ str‬و‪. phrase‬‬ ‫•‬

‫‪Assignment for strings‬‬ ‫التعيني للسالسل‬

‫بشكل عام ‪،‬‬


‫تسعى أنواع املكتبة إلى تسهيل استخدام نوع املكتبة كما هو الحال في استخدام نوع مدمج‪.‬‬ ‫•‬
‫وتحقيقا لهذه الغاية ‪ ،‬تدعم معظم أنواع املكتبة التعيني‪.‬‬ ‫•‬

‫في حالة السالسل ‪ ،‬يمكننا تعيني كائن سلسلة على آخر‪:‬‬

‫;‪string st1(10, 'c'), st2‬‬ ‫‪// st1 is cccccccccc; st2 empty string‬‬
‫;‪st1 = st2‬‬ ‫‪// assignment: replace contents of st1 with copy of st2‬‬
‫‪// both st1 and st2 now empty string‬‬

‫‪Adding Two strings‬‬ ‫جمع سلسلتني‬

‫يؤدي جمع سلسلتني إلى إنتاج سلسلة جديدة تمثل سلسلة الجهة اليرسى متبوعة بالسلسلة اليمىن‪.‬‬
‫أي عندما نستخدم عامل الجمع (‪ )+‬على السالسل ‪،‬‬ ‫•‬
‫تكون النتيجة سلسلة جديدة تكون أحرفها نسخة من تلك املوجودة في املعامل األيرس متبوع ًة بتلك‬ ‫•‬
‫املوجودة في املعامل األيمن‪.‬‬
‫يلحق عامل التعيني املركب (=‪ )+‬املعامل األيمن إلى السلسلة املوجودة على اليسار‪:‬‬

‫;"‪string s1 = "hello, ", s2 = "world\n‬‬


‫;‪string s3 = s1 + s2‬‬ ‫‪// s3 is hello, world\n‬‬
‫;‪s1 += s2‬‬ ‫‪// equivalent to s1 = s1 + s2‬‬
‫‪Adding Literals and strings‬‬ ‫جمع الحرفيات مع السالسل‬

‫كما رأينا في الفقرة ‪، 2.1.2‬‬


‫يمكننا استخدام نوع ما حيث يُتوقع وجود نوع آخر إذا وجد تحويل من النوع املحدد إلى النوع املتوقع‪.‬‬ ‫•‬
‫تتيح لنا مكتبة السلسلة تحويل كل من حرفيات األحرف وحرفيات سلسلة حرف إلى سالسل‪.‬‬ ‫•‬

‫ونظرًا ألنه يمكننا استخدام هذه الحرفيات حيث يُتوقع وجود سلسلة ‪ ،‬يمكننا إعادة كتابة الربنامج السابق على‬
‫النحو التالي ‪:‬‬

‫‪string s1 = "hello", s2 = "world"; // no punctuation in s1 or s2‬‬


‫;'‪string s3 = s1 + ", " + s2 + '\n‬‬

‫عندما نمزج سالسل مع سلسلة أو حرفيات حرف ‪ ،‬يجب أن يكون نوع سلسلة معامالً واحداً على األقل لكل عامل‬
‫جمع ‪: +‬‬

‫;" ‪string s4 = s1 + ",‬‬ ‫‪// ok: adding a string and a literal‬‬


‫;" ‪string s5 = "hello" + ",‬‬ ‫‪// error: no string operand‬‬
‫;"‪string s6 = s1 + ", " + "world‬‬ ‫‪// ok: each + has a string operand‬‬
‫;‪string s7 = "hello" + ", " + s2‬‬ ‫‪// error: can't add string literals‬‬

‫تتضمن عمليات تهيئة ‪ s4‬و ‪ s5‬عملية واحدة فقط لكل منهما ‪،‬‬ ‫•‬
‫لذلك من السهل معرفة ما إذا كانت التهيئة رشعية أم ال‪.‬‬ ‫•‬
‫معا تعابري املدخالت أو‬
‫قد تبدو بداية تهيئة ‪ s6‬مفاجئة ‪ ،‬لكنها تعمل بنفس الطريقة اليت نربط بها ً‬ ‫•‬
‫املخرجات‪.‬‬
‫فمجموعات التهيئة هذه تشبه ‪:‬‬

‫;"‪string s6 = (s1 + ", ") + "world‬‬

‫يعيد التعبري الفرعي ‪ "،" + s1‬سلسلة تشكل املعامل األيرس للعامل ‪ +‬الثاني‪.‬‬
‫يبدو األمر كما لو كنا قد كتبنا‬

‫;" ‪string tmp = s1 + ",‬‬ ‫‪// ok: + has a string operand‬‬


‫;"‪s6 = tmp + "world‬‬ ‫‪// ok: + has a string operand‬‬

‫من ناحية أخرى ‪ ،‬فإن تهيئة ‪ s7‬غري رشعية ‪ ،‬واليت يمكننا رؤيتها إذا قمنا بوضع أقواس في التعبري‪:‬‬

‫‪string s7 = ("hello" + ", ") + s2; // error: can't add string literals‬‬

‫اآلن يجب أن يكون من السهل مالحظة أن التعبري الفرعي األول يضيف حرفييت سلسلة‪.‬‬ ‫•‬
‫ال توجد طريقة للقيام بذلك ‪ ،‬وبالتالي فإن الجملة خاطئة‪.‬‬ ‫•‬

‫تحذير‬

‫ألسباب تاريخية ‪ ،‬وللتوافق مع لغة ‪ ، C‬ال تعترب حرفيات السلسلة سالسل املكتبة القياسية‪.‬‬
‫من املهم أن تتذكر أن هذه أنواع مختلفة عندما تستخدم حرفيات سلسلة وسالسل املكتبة‪.‬‬
‫قسم التدريبات ‪3.2.2‬‬
‫التمرين ‪:3.2‬‬
‫برنامجا لقراءة سطر مدخالت قياسية في املرة الواحدة‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬قم بتعديل برنامجك لقراءة كلمة في الوقت الواحد‪.‬‬

‫التمرين ‪:3.3‬‬
‫‪ -‬ارشح كيف يتم التعامل مع أحرف املسافات البيضاء في عامل مدخالت السلسلة وفي دالة ‪.getline‬‬

‫التمرين ‪:3.4‬‬
‫برنامجا يقرأ سلسلتني وقدم تقريراً ما إذا كانت السالسل متساوية‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬إن لم تكن ‪ ،‬فقدم تقريراً أيهما أكرب‪.‬‬
‫‪ -‬اآلن ‪ ،‬قم بتغيري الربنامج لتقرير ما إذا كانت السالسل لها نفس الطول ‪،‬‬
‫‪ -‬إذا لم تكن ‪ ،‬فقدم تقريراً بالسلسلة األطول‪.‬‬

‫التمرين ‪:3.5‬‬
‫برنامجا يقرأ سالسل من املدخالت القياسية ‪ ،‬ودمج ما يقرأ إلى سلسلة واحدة كبرية‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬اطبع السلسلة املدمجة‪.‬‬
‫‪ -‬بعد ذلك ‪ ،‬قم بتغيري الربنامج لفصل سالسل املدخالت املجاورة بمسافة‪.‬‬

‫‪ 3٫2٫3‬التعامل مع اﻷحرف في سلسلة‬


‫‪3.2.3. Dealing with the Characters in a string‬‬

‫غالبا ما نحتاج إلى التعامل مع األحرف الفردية في سلسلة‪.‬‬


‫ً‬
‫قد نرغب أن نتحقق لرنى ما إذا كانت سلسلة ما تحتوي على أي مسافة بيضاء ‪،‬‬ ‫•‬
‫أو لتغيري األحرف إلى أحرف صغرية ‪،‬‬ ‫•‬
‫أو ملعرفة ما إذا كان هناك حرف معني موجو ًدا ‪ ،‬وما إلى ذلك‪.‬‬ ‫•‬

‫يتضمن أحد أجزاء هذا النوع من املعالجة كيفية وصولنا إلى األحرف نفسها‪.‬‬
‫نحتاج أحيا ًنا إلى معالجة كل حرف‪.‬‬ ‫•‬
‫وفي أحيان أخرى نحتاج إلى معالجة حرف معني فقط ‪،‬‬ ‫•‬
‫أو التوقف عن املعالجة بمجرد استيفاء بعض الرشوط‪.‬‬ ‫•‬
‫ليتضح أن أفضل طريقة للتعامل مع هذه الحاالت تتضمن أقسام لغة ومكتبة مختلفة‪.‬‬ ‫•‬

‫الجزء اآلخر من معالجة األحرف هو معرفة و ‪ /‬أو تغيري خصائص الحرف‪.‬‬


‫يتم التعامل مع هذا الجزء من الدالة من خالل مجموعة من دوال املكتبة ‪ ،‬املوضحة في الجدول ‪. 3.3‬‬ ‫•‬
‫يتم تعريف هذه الدوال في العنوان ‪.cctype‬‬ ‫•‬

‫الجدول ‪ .3.3‬دوال ‪cctype‬‬

‫)‪isalnum(c‬‬ ‫‪true if c letter or digit.‬‬

‫)‪isalpha(c‬‬ ‫‪true if c letter.‬‬

‫)‪iscntrl(c‬‬ ‫‪true if c control character.‬‬


isdigit(c) true if c digit.

isgraph(c) true if c not space but printable.

islower(c) true if c lowercase letter.

true if c printable character


isprint(c)
(i.e. space character that has visible representation).

true if c punctuation character (i.e. character that not


ispunct(c)
control character, digit, letter or printable whitespace)

true if c whitespace
isspace(c)
(i.e. space, tab, vertical tab, return, newline or formfeed)

isupper(c) true if c uppercase letter.

isxdigit(c) true if c hexadecimal digit.

If c uppercase letter, returns its lowercase equivalent;


tolower(c)
otherwise return c unchanged.

If c lowercase letter, returns its uppercase equivalent;


toupper(c)
otherwise return c unchanged.

‫ من عناوين مكتبة يس‬++‫استخدم إصدارات يس‬ : ‫نصيحة‬


Use the C++ Versions of C Library Headers

.‫ مكتبة يس‬++‫ تتضمن مكتبة يس‬، ++‫خصيصا لـ يس‬


ً ‫باإلضافة إلى اﻷقسام املعرفة‬ •
. name.h :‫أسماء العناوين في يس تكون على شكل‬ •
c-name ‫ من هذه العناوين‬++‫يُطلق على إصدارات يس‬ •
.‫ ليشري إلى أن العنوان جزء من مكتبة يس‬c ‫ وتسبق االسم بالحرف‬h.‫فهي تزيل امتداد‬ •

، ctype.h ‫ تملك نفس محتويات‬cctype ‫ فإن‬، ‫ومن ثم‬ •


.++‫ولكن في نموذج مناسب لربامج يس‬ •
، std ‫ داخل مساحة االسم‬c-name ‫ يتم تعريف األسماء املعرفة في عناوين‬، ‫على وجه الخصوص‬ •
.‫ ليست كذلك‬h. ‫بينما تلك املعرفة في إصدارات‬ •

name.h ‫ من العناوين ﻻ إصدارات‬c-name ‫ إصدارات‬++‫ ينبغي أن تستخدم برامج يس‬، ‫في العادة‬ •
.std ‫بهذه الطريقة يتم العثور على األسماء من املكتبة القياسية باستمرار في مساحة االسم‬ •
‫ العبء على املربمج لتذكر أسماء املكتبة املوروثة من يس واليت هي فريدة من‬h. ‫يضع استخدام عناوين‬ •
.++‫نوعها لـ يس‬

‫ املستندة للنطاق‬for ‫معالجة كل حرف؟ استخدم‬


Processing Every Character? Use Range-Based for
‫إن أردنا القيام بيشء ما لكل حرف في سلسلة ‪،‬‬
‫فإن أفضل طريقة هي استخدام جملة قدمت بواسطة املعيار الجديد‪ :‬جملة ‪ for‬املستندة إلى النطاق‪.‬‬ ‫•‬
‫تتكرر هذه الجملة عرب العنارص في تسلسل معني وتنفذ بعض العمليات على كل قيمة في هذا التسلسل‪.‬‬ ‫•‬

‫النموذج النحوي لتلك الجملة هو‬

‫)تعبري ‪ : expression‬إعالن ‪For (declaration‬‬


‫جملة ‪statement‬‬

‫حيث التعبري عبارة عن كائن من نوع يمثل تسلساًل ‪،‬‬ ‫•‬


‫واإلعالن يعرف متغريا نستخدمه للوصول إلى العنارص األساسية في التسلسل‪.‬‬ ‫•‬
‫في كل تكرار ‪ ،‬يتم تهيئة املتغري في اإلعالن على قيمة العنرص التالي في التعبري‪.‬‬ ‫•‬
‫تمثل السلسلة تسلسل أحرف ‪ ،‬لذلك يمكننا استخدام سلسلة كتعبري في ‪ for‬للنطاق ‪.‬‬ ‫•‬

‫وكمثال بسيط ‪ ،‬يمكننا استخدام ‪ for‬للنطاق لطباعة كل حرف من سلسلة على سطر مخرجات خاص به ‪:‬‬

‫;)"‪string str("some string‬‬


‫‪// print characters in str one character to line‬‬
‫)‪for (auto c : str‬‬ ‫‪// for every char in str‬‬
‫;‪cout << c << endl‬‬ ‫‪// print current character followed by newline‬‬

‫تربط حلقة ‪ for‬املتغري ‪ c‬مع ‪.str‬‬ ‫•‬


‫نعرف بها أي متغري آخر‪.‬‬
‫نعرف متغري التحكم في الحلقة بنفس الطريقة اليت ّ‬
‫ّ‬ ‫•‬
‫في هذه الحالة ‪ ،‬نستخدم ‪ auto‬للسماح للمرتجم بتحديد نوع ‪ ، c‬والذي سيكون حرف ‪ char‬في هذه‬ ‫•‬
‫الحالة‪.‬‬
‫في كل تكرار ‪ ،‬يتم نسخ الحرف التالي في ‪ str‬إلى ‪.c‬‬ ‫•‬
‫وبالتالي ‪ ،‬يمكننا قراءة هذه الحلقة كأنها تخرب أن "لكل حرف ‪ c‬في السلسلة ‪ "،‬افعل شيًئ ا‪.‬‬ ‫•‬
‫متبوعا بسطر جديد‪.‬‬
‫ً‬ ‫"اليشء" في هذه الحالة هو طباعة الحرف‬ ‫•‬

‫كمثال أكرث تعقي ًدا إلى حد ما ‪ ،‬سنستخدم ‪ for‬النطاق ودالة ‪ ispunct‬لحساب عدد أحرف الرتقيم في سلسلة‪:‬‬

‫;)"!!!‪string s("Hello World‬‬


‫;‪// punct_cnt has same type that s.size returns‬‬
‫;‪decltype(s.size()) punct_cnt = 0‬‬
‫‪// count the number of punctuation characters in s‬‬
‫)‪for (auto c : s‬‬ ‫‪// for every char in s‬‬
‫))‪if (ispunct(c‬‬ ‫‪// if character is punctuation‬‬
‫;‪++punct_cnt‬‬ ‫‪// increment punctuation counter‬‬
‫;‪cout << punct_cnt << " punctuation characters in " << s << endl‬‬

‫مخرجات هذا الربنامج هي‬

‫!!!‪3 punctuation characters in Hello World‬‬

‫نستخدم هنا نوع‪-‬معلن ‪ decltype‬لإلعالن عن العداد الخاص بنا ‪.punct_cnt ،‬‬ ‫•‬
‫نوعه هو النوع الذي يتم إعادته عند استدعاء ‪ ، s.size‬وهو ‪.string::size_type‬‬ ‫•‬
‫نستخدم ‪ for‬النطاق ملعالجة كل حرف في السلسلة‪.‬‬ ‫•‬
‫هذه املرة نتحقق مما إذا كان كل حرف عبارة عن إحدى عالمات الرتقيم‪.‬‬ ‫•‬
‫إن كان األمر كذلك ‪ ،‬فإننا نستخدم عامل الزتايد ‪ ++‬إلضافة ‪ 1‬إلى العداد‪.‬‬ ‫•‬
‫عندما يكتمل النطاق ‪ ،‬نطبع النتيجة‪.‬‬ ‫•‬

‫استخدام ‪ for‬النطاق لتغيري اﻷحرف في سلسلة‬


‫‪Using a Range for to Change the Characters in a string‬‬

‫إن أردنا تغيري قيمة أحرف في سلسلة ‪ ،‬يجب أن نعرف متغري الحلقة كنوع مرجعي‪.‬‬
‫تذكر أن املرجع هو مجرد اسم آخر لكائن معني‪.‬‬ ‫•‬
‫مرجعا كمتغري تحكم لدينا ‪ ،‬فإن هذا املتغري مرتبط بكل عنرص في التسلسل بدوره‪.‬‬
‫ً‬ ‫فعندما نستخدم‬ ‫•‬
‫وباستخدام املرجع ‪ ،‬يمكننا تغيري الحرف الذي يرتبط به املرجع‪.‬‬ ‫•‬

‫لنفرتض أنه بدال ً من حساب عالمات الرتقيم ‪ ،‬أردنا تحويل سلسلة إلى حالة األحرف الكبرية‪.‬‬
‫فللقيام بذلك ‪ ،‬يمكننا استخدام دالة ‪ ، toupper‬واليت تأخذ حر ًفا وتعيد اإلصدار الكبري من ذلك الحرف‪.‬‬ ‫•‬

‫لتحويل السلسلة بأكملها ‪ ،‬نحتاج إلى استدعاء ‪ toupper‬على كل حرف وإعادة النتيجة إلى هذا الحرف‪:‬‬

‫;)"!!!‪string s("Hello World‬‬


‫‪// convert s to uppercase‬‬
‫)‪for (auto &c : s‬‬ ‫)‪// for every char in s (note: c reference‬‬
‫‪c = toupper(c); // c reference, so assignment changes char in s‬‬
‫;‪cout << s << endl‬‬

‫مخرجات هذا الكود هي‬

‫!!!‪HELLO WORLD‬‬

‫في كل تكرار ‪ ،‬يشري ‪ c‬إلى حرف ‪ s‬التالي‪.‬‬ ‫•‬


‫عندما نقوم بتعيني على ‪ ، c‬فإننا نغري الحرف األسايس في ‪.s‬‬ ‫•‬

‫لذلك ‪ ،‬عندما ننفذ‬

‫‪c = toupper(c); // c reference, so assignment changes char in s‬‬

‫فنحن بصدد تغيري قيمة الحرف الذي يرتبط به ‪.c‬‬ ‫•‬


‫عند اكتمال هذه الحلقة ‪ ،‬ستكون حالة األحرف في ‪ str‬كبرية‪.‬‬ ‫•‬

‫?‪Processing Only Some Characters‬‬ ‫معالجة بعض اﻷحرف فقط ؟‬

‫يعمل ‪ for‬النطاق بشكل جيد عندما نحتاج إلى معالجة كل حرف‪ .‬ومع ذلك ‪،‬‬
‫أحياناً نحتاج إلى الوصول إلى حرف فقط أو الوصول إلى عدة أحرف للتوصل إلى بعض الرشوط‪.‬‬ ‫•‬
‫فعلى سبيل املثال ‪ ،‬قد نرغب في كتابة الحرف األول فقط باألحرف الكبرية أو الكلمة األولى فقط في‬ ‫•‬
‫السلسلة‪.‬‬

‫هناك طريقتان للوصول إلى أحرف مفردة في سلسلة‪:‬‬


‫يمكننا استخدام رمز فهرسة أو يمكننا استخدام مكرر‪.‬‬ ‫•‬
‫سيكون لدينا املزيد لنقوله عن املكررات في الفقرة ‪ 3.4‬والفصل ‪.9‬‬ ‫•‬

‫يأخذ عامل الفهرسة (العامل [ ]) قيمة الدالة ‪string::size_type‬‬


‫حيث يشري إلى موضع الحرف الذي نريد الوصول إليه‪.‬‬ ‫•‬
‫يعيد العامل إشارة إلى حرف في املوضع املحدد‪.‬‬ ‫•‬

‫تبدأ فهرسة أحرف السالسل من الصفر ؛‬


‫فإن كانت ‪ s‬سلسلة تتكون من حرفني على األقل ‪،‬‬ ‫•‬
‫فإن ]‪ s[0‬هو الحرف األول ‪ ،‬و ]‪ s[1‬هو الحرف الثاني ‪ ،‬وآخر حرف سيكون ]‪.s[s.size()-1‬‬ ‫•‬

‫ملحوظة‬

‫يجب أن تكون القيم اليت نستخدمها لفهرسة سلسلة أكرب من أو تساوي الصفر و أصغر من ()‪.size‬‬
‫معرف‪.‬‬
‫َّ‬ ‫ستكون نتيجة استخدام فهرس خارج هذا النطاق سلوكاً غري‬
‫معرف‪.‬‬
‫َّ‬ ‫ضمنيا ‪ ،‬تعد فهرسة سلسلة فارغة سلوكاً غري‬
‫ً‬

‫يُشار إلى القيمة املوجودة في الفهرسة بـ"شفرة‪-‬فرعية ‪ "subscript‬أو "فهرس ‪."index‬‬ ‫•‬
‫يمكن أن يكون الفهرس الذي نقدمه أي تعبري ينتج عنه قيمة عدد صحيح‪ .‬مع ذلك ‪،‬‬ ‫•‬
‫إذا كان فهرسنا يحتوي على نوع ذي إشارة ‪ ،‬فسيتم تحويل قيمته إلى نوع بال_إشارة يمثله‬ ‫•‬
‫‪.string::size_type‬‬
‫يستخدم املثال التالي عامل الفهرسة لطباعة أول حرف في السلسلة‪:‬‬

‫))(‪if (!s.empty‬‬ ‫‪// make sure there's a character to print‬‬


‫;‪cout << s[0] << endl‬‬ ‫‪// print the first character in s‬‬

‫قبل الوصول إلى الحرف ‪ ،‬نتحقق من أن ‪ s‬ليست فارغ ًة‪.‬‬ ‫•‬


‫ففي أي وقت نستخدم فيه رمز فهرسة ‪ ،‬يجب أن نتأكد من وجود قيمة في الفهرس املحدد‪.‬‬ ‫•‬
‫فإن كانت ‪ s‬فارغة ‪ ،‬فإن ]‪ s[0‬تكون سلوكاً غري معرف‪.‬‬ ‫•‬

‫وطاملا أن السلسلة ليست ثابتة ‪ ،‬فيمكننا تعيني قيمة جديدة على الحرف الذي يعيده عامل الفهرسة‪.‬‬
‫فعلى سبيل املثال ‪ ،‬يمكننا استبدال أول حرف بحرف كبري على النحو التالي‪:‬‬

‫;)"‪string s("some string‬‬


‫))(‪if (!s.empty‬‬ ‫]‪// make sure there's character in s[0‬‬
‫;)]‪s[0] = toupper(s[0‬‬ ‫‪// assign new value to first character in s‬‬

‫مخرجات هذا الربنامج هي‬

‫‪Some string‬‬
‫‪Using a Subscript for Iteration‬‬ ‫استخدام فهرسة للتكرار‬

‫كمثال آخر ‪ ،‬سنقوم بتغيري أول كلمة في ‪ s‬إلى أحرف كبرية بالكامل‪:‬‬

‫‪// process characters in s til run out of characters or hit whitespace‬‬


‫;‪for (decltype(s.size()) index = 0‬‬
‫)‪index != s.size() && !isspace(s[index]); ++index‬‬
‫‪s[index] = toupper(s[index]); // capitalize the current character‬‬

‫يقوم هذا الربنامج بتوليد‬

‫‪SOME string‬‬

‫تستخدم حلقتنا ‪ for‬املتغري ‪ index‬لفهرسة ‪.s‬‬ ‫•‬


‫استخدمنا نوع‪-‬معلن إلعطاء ‪ index‬النوع املناسب‪.‬‬ ‫•‬
‫نقوم بتهيئة ‪ index‬على ‪ 0‬بحيث يبدأ أول تكرار من أول حرف في ‪.s‬‬ ‫•‬
‫في كل تكرار نقوم بزتايد ‪ index‬إللقاء نظرة على الحرف التالي في ‪.s‬‬ ‫•‬
‫في بنية الحلقة ‪ ،‬نكتب الحرف الحالي بأحرف كبرية‪.‬‬ ‫•‬

‫الجزء الجديد في هذه الحلقة هو رشط ‪.for‬‬


‫حيث يستخدم هذا الرشط عامل املنطق (&&)‪.‬‬ ‫•‬
‫صحيحا ‪ true‬و بخالف ذلك ينتج خطأ ‪.false‬‬ ‫ً‬ ‫ينتج عن هذا املعامل صحيح إذا كان كال املعاملني‬ ‫•‬
‫صحيحا‪.‬‬
‫ً‬ ‫الجزء املهم في هذا العامل هو أننا سنضمن أنه لن يقوم بتقييم معامله األيمن إال إن كان األيرس‬ ‫•‬
‫في هذه الحالة ‪ ،‬نضمن أننا لن نقوم بإدخال حروف ما لم نكن نعلم أن ‪ index‬في النطاق‪.‬‬ ‫•‬
‫أي أن ]‪ s[index‬لن يتم تنفيذه إﻻ إن كانت ‪ index‬ال تساوي ()‪. s.size‬‬ ‫•‬
‫ونظرًا ألن ‪ index‬لن تزيد أب ًدا عن قيمة ()‪، s.size‬‬ ‫•‬
‫دائما أقل من ()‪.s.size‬‬
‫ً‬ ‫فإننا نعلم أن ‪ index‬ستكون‬ ‫•‬

‫الفهارس غري مفحوصة ‪Subscripts are Unchecked‬‬ ‫احذر ‪:‬‬


‫عندما نستخدم رمز فهرسة ‪ ،‬يجب أن نتأكد من أن الفهرس ضمن النطاق‪.‬‬ ‫•‬
‫بمعىن ‪ ،‬يجب أن يكون الفهرس أكرب من أو يساوي الصفر ‪ 0‬و أصغر من حجم السلسلة‪.‬‬ ‫•‬
‫دائما في استخدام متغري من النوع‬
‫ً‬ ‫تتمثل إحدى طرق تبسيط اﻷكواد اليت تستخدم (الفهرسة)‬ ‫•‬
‫‪ size_type‬باعتباره رمز فهرسة‪.‬‬
‫ونظرًا ألنه نوع بال_ إشارة ‪ ،‬فإننا نضمن أال يكون الفهرس أقل من الصفر‪.‬‬ ‫•‬
‫عندما نستخدم قيمة ‪ size_type‬كقيمة فهرسة ‪ ،‬فإننا نحتاج فقط إلى‬ ‫•‬
‫التحقق من أن الفهرس أقل من القيمة املعادة بواسطة الحجم ()‪.size‬‬ ‫•‬

‫تحذير‬

‫املكتبة غري مطالبة بفحص قيمة الفهرسة‪ .‬نتيجة استخدام فهرس خارج النطاق سلوك غري معرف‬

‫‪Using a Subscript for Random Access‬‬ ‫استخدام فهرسة لوصول عشوائي‬


‫في املثال السابق ‪ ،‬قمنا بتقديم موضع واحد مفهرس في كل مرة لتكبري كل حرف في التسلسل‪.‬‬
‫أيضا حساب فهرسة وجلب الحرف املشري إليه مبارش ًة‪.‬‬
‫يمكننا ً‬ ‫•‬
‫فال حاجة للوصول إلى أحرف بالتسلسل‪.‬‬ ‫•‬

‫رقما بني ‪ 0‬و ‪ 15‬ونريد توليد التمثيل السدايس العرشي لهذا الرقم‪.‬‬
‫كمثال ‪ ،‬لنفرتض أن لدينا ً‬
‫عرشيا‪:‬‬
‫ً‬ ‫سداسيا‬
‫ً‬ ‫"رقما"‬
‫ً‬ ‫يمكننا القيام بذلك باستخدام سلسلة نصية تتم تهيئتها الستيعاب ‪16‬‬

‫‪const string hexdigits = "0123456789ABCDEF"; // possible hex digits‬‬


‫" ‪cout << "Enter a series of numbers between 0 and 15‬‬
‫" ‪<< " separated by spaces. Hit ENTER when finished :‬‬
‫;‪<< endl‬‬
‫;‪string result‬‬ ‫‪// will hold resulting hexify'd string‬‬
‫;‪string::size_type n‬‬ ‫‪// hold numbers from input‬‬
‫)‪while (cin >> n‬‬
‫))(‪if (n < hexdigits.size‬‬ ‫‪// ignore invalid input‬‬
‫;]‪result += hexdigits[n‬‬ ‫‪// fetch indicated hex digit‬‬
‫;‪cout << "Your hex number is: " << result << endl‬‬

‫إذا أعطينا هذا الربنامج املدخالت التالية ‪:‬‬

‫‪12 0 5 15 8 15‬‬

‫فسوف تكون املخرجات ‪:‬‬

‫‪Your hex number is: C05F8F‬‬

‫نبدأ بتهيئة ‪ hexdigits‬على حفظ األرقام السدايس عرشية من ‪ 0‬إلى ‪.F‬‬ ‫•‬
‫سنجعل هذه السلسلة ثابتة‪ const-‬ألننا ال نريد تغيري تلك القيم‪.‬‬ ‫•‬
‫داخل الحلقة ‪ ،‬نستخدم قيمة مدخالت ‪ n‬لفهرسة ‪ .hexdigits‬قيمة ]‪ hexdigits[n‬هي الحرف الذي‬ ‫•‬
‫يظهر في املوضع ‪ n‬باألرقام السداسية‪.‬‬
‫مثالً ‪ ،‬إذا كان ‪ n‬تساوي ‪ ، 15‬فإن النتيجة هي ‪ F‬؛ إذا كانت ‪ ، 12‬تكون النتيجة ‪ C‬؛ وهلم جرا‪.‬‬ ‫•‬
‫نلحق هذا الرقم بالنتيجة ‪ ،‬ونطبعه بمجرد قراءة كل املدخالت‪.‬‬ ‫•‬

‫عندما نستخدم رمز فهرسة ‪ ،‬يجب أن نفكر في كيفية علمنا أنه ضمن النطاق‪.‬‬
‫في هذا الربنامج ‪ ،‬يعد رمزنا ‪ n‬من نوع ‪ ، string::size_type‬وهو كما نعلم نوع بال_إشارة‪.‬‬ ‫•‬
‫ونتيجة لذلك ‪ ،‬نعلم أنه تم ضمان أن تكون قيمة ‪ n‬أكرب من أو تساوي ‪.0‬‬ ‫•‬
‫فكل ما تبقى هو أن نتحقق أنها أقل من حجم ‪ hexdigits‬قبل أن نستخدم ‪ n‬لفهرسة اﻷرقام‪.‬‬ ‫•‬

‫قسم التدريبات ‪3.2.3‬‬


‫التمرين ‪:3.6‬‬
‫استخدم ‪ for‬النطاق لتغيري كل األحرف في سلسلة إلى ‪.X‬‬

‫التمرين ‪:3.7‬‬
‫‪ -‬ماذا سيحدث إن قمت بتعريف متغري التحكم في الحلقة في التمرين السابق كنوع حرف ‪char‬؟‬
‫‪ -‬توقع النتائج ثم قم بتغيري برنامجك الستخدام حرف ملعرفة ما إذا كنت على حق‪.‬‬
‫التمرين ‪:3.8‬‬
‫‪ -‬أعد كتابة الربنامج في التمرين األول ‪ ،‬أوال ً باستخدام ‪ while‬ومرة أخرى باستخدام حلقة ‪ for‬التقليدية‪.‬‬
‫‪ -‬أي من الطرق الثالثة تفضل وملاذا؟‬

‫التمرين ‪:3.9‬‬
‫‪ -‬ماذا يفعل الربنامج التالي؟ هل الربنامج صحيح؟‬
‫‪ -‬إذا لم يكن كذلك ‪ ،‬فلماذا؟‬
‫;‪string s‬‬
‫;‪cout << s[0] << endl‬‬

‫التمرين ‪:3.10‬‬
‫برنامج ا يقرأ سلسلة من األحرف بما في ذلك عالمات الرتقيم ويكتب ما تمت قراءته لكن مع إزالة عالمات‬
‫ً‬ ‫‪ -‬اكتب‬
‫الرتقيم‪.‬‬

‫التمرين ‪:3.11‬‬
‫‪ -‬هل ‪ for‬النطاق التالية رشعية؟‬
‫‪ -‬إن كانت كذلك ‪ ،‬فما هو نوع ‪ C‬؟‬
‫;"!‪const string s = "Keep out‬‬
‫… *‪for (auto &c : s) { /‬‬ ‫} ‪*/‬‬
‫‪ . 3٫2‬نـــــوع مكتبة املتجـــــه‬
‫‪3.3. Library vector Type‬‬

‫املتجه ‪ vector‬عبارة عن مجموعة كائنات ‪ ،‬كل منها تملك نفس النوع‪.‬‬


‫يحتوي كل عنرص في املجموعة على فهرس مرتبط به ‪ ،‬يتيح لنا الوصول إلى ذلك العنرص‪.‬‬ ‫•‬
‫غالبا ما يُشار إلى املتجه كحاوية ألنه "يحوي" كائنات أخرى‪.‬‬
‫ً‬ ‫•‬
‫سيكون لدينا الكثري لنقوله عن الحاويات في الجزء الثاني‪.‬‬ ‫•‬

‫أيضا أنه تم إجراء إعالن ‪using‬‬


‫الستخدام متجه ‪ ،‬يجب علينا تضمني العنوان املناسب‪ .‬في أمثلتنا ‪ ،‬نفرتض ً‬
‫املناسب ‪:‬‬

‫>‪#include <vector‬‬
‫;‪using std::vector‬‬

‫املتجه عبارة عن قالب فئة ‪.class template‬‬


‫تحتوي يس‪ ++‬على قوالب فئة ودوال‪.‬‬ ‫•‬
‫تتطلب كتابة القالب فهماً عميقاً إلى حد ما لـ يس‪ .++‬في الواقع ‪،‬‬ ‫•‬
‫لن نرى كيفية إنشاء قوالبنا الخاصة حىت الفصل ‪!16‬‬ ‫•‬
‫لكن لحسن الحظ ‪ ،‬يمكننا استخدام القوالب دون معرفة كيفية كتابتها‪.‬‬ ‫•‬

‫القوالب ليست في حد ذاتها دوال أو فئات‪.‬‬


‫لكن يمكن اعتبارها بدال ً من ذلك تعليمات للمرتجم لتوليد فئات أو دوال‪.‬‬ ‫•‬
‫تسمى العملية اليت يستخدمها املرتجم إلنشاء فئات أو دوال من القوالب إنشاء مثيل ‪.instantiation‬‬ ‫•‬
‫قالبا ‪ ،‬نحدد نوع الفئة أو الدالة اليت نريد من املرتجم إنشاء مثيل لها‪.‬‬
‫ً‬ ‫عندما نستخدم‬ ‫•‬

‫بالنسبة لقالب فئة ‪،‬‬


‫نحدد أي فئة سيتم إنشاء مثيل لها من خالل توفري معلومات إضافية ‪ ،‬واليت تعتمد طبيعتها على القالب‪.‬‬ ‫•‬
‫دائما ‪:‬‬
‫ً‬ ‫إن الطريقة اليت نعرف بها املعلومات هي نفسها‬ ‫•‬
‫أن نوفرها داخل زوج من أقواس الزاوية بعد اسم النموذج‪.‬‬ ‫•‬

‫في حالة املتجه ‪ ،‬فإن املعلومات اإلضافية اليت نقدمها هي نوع الكائنات اليت سيحملها املتجه ‪:‬‬

‫;‪vector<int> ivec‬‬ ‫‪// ivec holds objects of type int‬‬


‫;‪vector<Sales_item> Sales_vec‬‬ ‫‪// holds Sales_items‬‬
‫;‪vector<vector<string>> file‬‬ ‫‪// vector whose elements are vectors‬‬

‫في هذا املثال ‪ ،‬يُنئش املرتجم ثالثة أنواع ممزية من قالب املتجه‪:‬‬ ‫•‬
‫متجه من نوع عدد صحيح <‪ >int‬ومتجه من نوع عنرص املبيعات <‪>Sales_item‬‬ ‫•‬
‫ومتجه من نوع <متجه من نوع سلسلة <‪.>string‬‬ ‫•‬

‫ملحوظة‬
‫املتجه عبارة عن قالب وليس نوع‪ .‬يجب أن تتضمن األنواع اليت يتم توليدها من املتجه نوع العنرص ‪،‬‬
‫على سبيل املثال ‪ ،‬متجه <‪.>int‬‬

‫يمكننا تعريف املتجهات لتحمل كائنات من أي نوع‪.‬‬


‫ونظرًا ألن املراجع ليست كائنات ‪ ،‬فال يمكننا الحصول على متجه من نوع مرجع‪ .‬مع ذلك ‪،‬‬ ‫•‬
‫يمكن أن يكون لدينا متجهات ملعظم األنواع املدمجة األخرى (غري املرجعية) ومعظم أنواع الفئات‪.‬‬ ‫•‬
‫على وجه الخصوص ‪ ،‬يمكن أن يكون لدينا متجهات تكون عنارصها في حد ذاتها متجهات‪.‬‬ ‫•‬

‫جدير بالذكر أن اإلصدارات السابقة من يس‪ ++‬استخدمت بناء جملة مختلفة قليالً لتعريف متجه تتكون عنارصه من‬
‫نوع متجهات (أو نوع قالب آخر)‪.‬‬
‫فقد كان علينا توفري مسافة بني قوس إغالق الزاوية للمتجه الخارجي ونوع عنرصه ؛ بحيث يكون __‬

‫> >‪vector<vector<int‬‬

‫بدال ً من ___‬

‫>>‪vector<vector<int‬‬

‫تحذير‬

‫قد تتطلب بعض املرتجمات تعريفات النمط القديم ملتجه من املتجهات ‪ ،‬مثالً ‪:‬‬
‫> >‪vector<vector<int‬‬

‫‪3.3.1. Defining and Initializing vectors‬‬ ‫‪ 3٫3٫1‬تعريف وتهيئة املتجهات‬

‫شيوعا‬
‫ً‬ ‫كحال أي نوع فئة ‪ ،‬يتحكم قالب املتجه في كيفية تعريف املتجهات وتهيئتها‪ .‬يرسد الجدول ‪ 3.4‬أكرث الطرق‬
‫لتعريف املتجهات‪.‬‬

‫الجدول ‪ .3.4‬طرق تهيئة متجه‬

‫‪Vector that holds objects of type.‬‬


‫‪vector<T> v1‬‬
‫‪Default initialization; v1 is empty.‬‬

‫)‪vector<T> v2(v1‬‬ ‫‪v2 has copy of each element in v1.‬‬

‫‪vector<T> v2 = v1‬‬ ‫‪Equivalent to v2(v1), v2 copy of elements in v1.‬‬

‫)‪vector<T> v3 (n, val‬‬ ‫‪v3 has n elements with value val.‬‬

‫)‪vector<T> v4 (n‬‬ ‫‪v4 has n copies of value-initialized object.‬‬

‫;‪v5 has as many elements as there initializers‬‬


‫}… ‪vector<T> v5 {a, b,c‬‬ ‫‪elements initialized by corresponding‬‬
‫‪initializers.‬‬

‫‪vector<T> v5 = {a, b,c …} Equivalent to vector<T> v5{a,b,c …}.‬‬

‫افرتاضيا ‪ ،‬بحيث يؤدي إلى إنشاء متجه فارغ من النوع املحدد‪:‬‬


‫ً‬ ‫يمكننا تهيئة متجه‬
‫‪vector<string> svec; // default initialization; svec has no elements‬‬

‫قد يبدو أن املتجه الفارغ سيكون ذا فائدة قليلة‪.‬‬ ‫•‬


‫لكن كما سرنى بعد قليل يمكننا (بكفاءة) إضافة عنارص إلى املتجه في وقت التشغيل‪ .‬في الواقع ‪،‬‬ ‫•‬
‫شيوعا الستخدام املتجهات هي تحديد متجه فارغ في البداية‬
‫ً‬ ‫تعترب الطريقة األكرث‬ ‫•‬
‫حيث تتم إضافة العنارص إليه عندما تصبح قيمها معروفة وقت التشغيل‪.‬‬ ‫•‬

‫نعرف املتجه‪.‬‬
‫أيضا توفري القيمة (القيم) األولية للعنرص (العنارص) عندما ّ‬
‫يمكننا ً‬
‫على سبيل املثال ‪ ،‬يمكننا نسخ عنارص من متجه آخر‪.‬‬ ‫•‬
‫عندما نقوم بنسخ متجه ‪ ،‬يكون كل عنرص في املتجه الجديد نسخة من العنرص املقابل في املتجه األصلي‪.‬‬ ‫•‬
‫ويجب أن يكون املتجهان من نفس النوع‪:‬‬ ‫•‬

‫;‪vector<int> ivec‬‬ ‫‪// initially empty‬‬


‫‪// give ivec some values‬‬
‫‪vector<int> ivec2‬‬ ‫;)‪(ivec‬‬ ‫‪// copy elements of ivec into ivec2‬‬
‫;‪vector<int> ivec3 = ivec‬‬ ‫‪// copy elements of ivec into ivec3‬‬
‫;)‪vector<string> svec(ivec2‬‬ ‫‪// error: svec holds strings, not ints‬‬

‫‪List Initializing a vector‬‬ ‫تهيئة املتجه قائمة‬

‫هناك طريقة أخرى لتوفري قيم العنارص ‪ ،‬وهي أنه بموجب املعيار الجديد ‪ ،‬يمكننا تهيئة متجه تهيئة‪-‬قائمة على‬
‫صفر أو أكرث من قيم العنارص األولية املحاطة بأقواس معقوفة ‪:‬‬

‫;}"‪vector<string> articles = {"a", "an", "the‬‬

‫يتكون املتجه الناتج من ثالثة عنارص ؛ األول يحمل السلسلة "‪ ، "a‬والثاني يحمل "‪ ، "an‬واألخري هو "‪."the‬‬

‫كما رأينا ‪ ،‬توفر يس‪ ++‬عدة أشكال من التهيئة ‪.‬‬


‫في كثري من الحاالت ‪ ،‬وليس كلها ‪ ،‬يمكننا استخدام هذه األشكال من التهيئة بالتبادل‪.‬‬ ‫•‬
‫مهما ‪:‬‬
‫حىت اآلن ‪ ،‬رأينا مثالني يكون فيهما شكل التهيئة ً‬ ‫•‬
‫عند استخدام نموذج تهيئة النسخة ( أي عندما نستخدم = ) ‪ ،‬حيث يمكننا توفري ُمهئي واحد فقط ؛‬ ‫•‬
‫وعندما نوفر ُمهيًئ ا داخل فئته‪ ،‬وهنا يجب علينا إما استخدام تهيئة النسخة أو استخدام األقواس املتعرجة‪.‬‬ ‫•‬
‫القيد الثالث هو أنه يمكننا توفري قائمة بقيم العنارص فقط باستخدام تهيئة القائمة اليت يتم فيها وضع‬ ‫•‬
‫املُهيئات في أقواس معقوفة‪.‬‬
‫ال يمكننا توفري تهيئة‪-‬قائمة باستخدام األقواس العادية‪:‬‬

‫;}"‪vector<string> v1{"a", "an", "the‬‬ ‫‪// list initialization‬‬


‫;)"‪vector<string> v2("a", "an", "the‬‬ ‫‪// error‬‬

‫‪Creating a Specified Number of Elements‬‬ ‫إنشاء عدد محدد من العنارص‬

‫أيضا تهيئة متجه من معدود وقيمة عنرص‪.‬‬


‫يمكننا ً‬
‫يحدد املعدود عدد العنارص اليت سيحملها املتجه ؛ توفر قيمة العنرص القيمة األولية لكل عنرص من هذه العنارص‪:‬‬
‫‪vector<int> ivec(10, -1); // ten int elements, each initialized to -1‬‬
‫"!‪vector<string> svec(10, "hi!"); // ten strings; each element is "hi‬‬

‫‪Value Initialization‬‬ ‫تهيئة القيمة‬

‫يمكننا عادة حذف القيمة وتوفري حجم فقط‪.‬‬


‫في هذه الحالة ‪ ،‬تُنئش لنا املكتبة ُمهئي عنرص ذي قيمة ُمهيئة‪.‬‬ ‫•‬
‫تُستخدم هذه القيمة اليت أنشأتها املكتبة لتهيئة كل عنرص في الحاوية‪.‬‬ ‫•‬
‫تعتمد قيمة ُمهئي العنرص على نوع العنارص املخزنة في املتجه‪.‬‬ ‫•‬
‫فإن كان املتجه يحتوي على عنارص نوع مدمج ‪ ،‬مثل عدد صحيح ‪ ، int‬فإن ُمهئي العنرص قيمته صفر‪.‬‬ ‫•‬
‫أما إن كانت العنارص من نوع فئة ‪ ،‬كسلسلة ‪ ،‬فإن ُمهئي العنرص نفسه يكون مهيأ بشكل افرتايض ‪:‬‬ ‫•‬

‫;)‪vector<int> ivec(10‬‬ ‫‪// ten elements, each initialized to 0‬‬


‫;)‪vector<string> svec(10‬‬ ‫‪// ten elements, each an empty string‬‬

‫هناك نوعان من القيود على هذا الشكل من التهيئة‪:‬‬ ‫•‬


‫رصيحا‪.‬‬
‫ً‬ ‫دائما ُمهيًئ ا‬
‫ً‬ ‫القيد األول هو أن بعض الفئات تتطلب أن نقدم‬ ‫•‬
‫افرتاضيا ‪،‬‬
‫ً‬ ‫فإن كان (متجهنا) يحتوي على كائنات من نوع ال يمكن تهيئتها‬ ‫•‬
‫فيجب علينا توفري القيمة األولية للعنرص ؛‬ ‫•‬
‫حيث ال يمكن إنشاء متجهات من هذه األنواع من خالل توفري الحجم فقط‪.‬‬ ‫•‬
‫أيضا ‪ ،‬يجب أن نستخدم الشكل‬
‫القيد الثاني هو أنه عندما نوفر عد ًدا من العنارص دون توفري قيمة أولية ً‬ ‫•‬
‫املبارش للتهيئة‪:‬‬

‫‪vector<int> vi = 10;//error: must use direct initialization to supply size‬‬

‫متجها بعرشة عنارص ذات قيمة ُمهيأة‪.‬‬


‫ً‬ ‫هنا نستخدم ‪ 10‬إلرشاد املتجه إلى كيفية إنشاء متجه — فنحن نريد‬ ‫•‬
‫نحن ال "ننسخ" ‪ 10‬في املتجه‪ .‬ومن ثم ‪ ،‬ال يمكننا استخدام تهيئة النسخة‪.‬‬ ‫•‬

‫سرنى املزيد حول كيفية عمل هذا التقييد ﻻحقاً‪.‬‬

‫?‪List Initializer or Element Count‬‬ ‫مهئي‪-‬قائمة أو عنرص معدود ؟‬

‫في حاالت قليلة ‪ ،‬يعتمد معىن التهيئة على ما إذا كنا نستخدم األقواس املتعرجة أو األقواس العادية لتمرير املُهئي‪.‬‬
‫• على سبيل املثال ‪ ،‬عندما نقوم بتهيئة متجه <‪ >int‬من قيمة ‪ int‬واحدة ‪،‬‬
‫فقد تمثل هذه القيمة حجم املتجه أو قد تكون قيمة عنرص‪.‬‬ ‫•‬
‫حجما وقيمة أولية ‪،‬‬
‫ً‬ ‫وباملثل ‪ ،‬إذا قدمنا قيميت ‪ int‬بالضبط ‪ ،‬فقد تكون هذه القيم‬ ‫•‬
‫قيما ملتجه ثنائي العنرص‪.‬‬
‫أو يمكن أن تكون ً‬ ‫•‬

‫نحدد املعىن الذي ننوي استخدامه سواء استخدمنا األقواس أو األقواس املتعرجة ‪:‬‬

‫;)‪vector<int> v1(10‬‬ ‫‪// v1 has ten elements with value 0‬‬


‫;}‪vector<int> v2{10‬‬ ‫‪// v2 has one element‬‬ ‫‪with value 10‬‬
‫;)‪vector<int> v3(10, 1‬‬ ‫‪// v3 has ten elements with value 1‬‬
‫;}‪vector<int> v4{10, 1‬‬ ‫‪// v4 has two elements with values 10 and 1‬‬

‫عندما نستخدم األقواس العادية ‪ ،‬فإننا نخرب بأن القيم اليت نوفرها يجب استخدامها لبناء الكائن‪.‬‬ ‫•‬
‫بالتالي ‪ ،‬يستخدم ‪ v1‬و ‪ُ v3‬مهيئهما لتحديد حجم املتجه ‪ ،‬وحجمه وقيم عنارصه ‪ ،‬على التوالي‪.‬‬ ‫•‬
‫وعندما نستخدم األقواس املتعرجة ‪ ، }...{ ،‬فإننا نخرب بأنه إن أمكن ‪ ،‬نريد إعداد تهيئة الكائن كقائمة‪.‬‬ ‫•‬
‫بمعىن ‪ ،‬إذا كانت هناك طريقة الستخدام القيم املوجودة داخل األقواس املتعرجة كقائمة من ُمهئي العنارص ‪،‬‬ ‫•‬
‫فستقوم الفئة بذلك‪.‬‬
‫وفقط إذا لم يكن من املمكن تهيئة الكائن كقائمة ‪ ،‬فسيتم النظر في الطرق األخرى لتهيئة الكائن‪.‬‬ ‫•‬
‫يمكن استخدام القيم اليت نوفرها عند تهيئة ‪ v2‬و ‪ v4‬كقيم للعنارص‪.‬‬ ‫•‬
‫تتم تهيئة هذه الكائنات كقائمة ؛ ﻷن املتجهات الناتجة لها عنرص واحد وعنرصين ‪ ،‬على التوالي‪.‬‬ ‫•‬

‫من ناحية أخرى ‪،‬‬


‫إذا استخدمنا األقواس املتعرجة ولم تكن هناك طريقة الستخدام املُهئي لتهيئة الكائن كقائمة ‪،‬‬ ‫•‬
‫فسيتم حينئذ استخدام هذه القيم لبناء الكائن‪.‬‬ ‫•‬
‫على سبيل املثال ‪ ،‬لتهيئة قائمة متجه من سالسل ‪ ،‬يجب علينا توفري القيم اليت يمكن استخدامها كسالسل‪.‬‬ ‫•‬

‫في هذه الحالة ‪ ،‬ال يوجد التباس حول ما إذا كان سيتم تهيئة قائمة العنارص أو إنشاء متجه بالحجم املحدد‪:‬‬

‫‪vector<string> v5{"hi"}; // list initialization: v5 has one element‬‬


‫‪vector<string> v6("hi"); //error: can't construct vector from string‬‬
‫‪literal‬‬
‫;}‪vector<string> v7{10‬‬ ‫‪// v7 has ten default-initialized elements‬‬
‫"‪vector<string> v8{10, "hi"}; // v8 has ten elements with value "hi‬‬

‫على الرغم من أننا استخدمنا األقواس في جميع هذه التعريفات باستثناء واحد ‪،‬‬ ‫•‬
‫إال أن املتجه ‪ v5‬فقط هي اليت تمت تهيئتها كقائمة‪.‬‬ ‫•‬
‫من أجل تهيئة متجه كقائمة ‪ ،‬يجب أن تتطابق القيم املوجودة داخل األقواس مع نوع العنرص‪.‬‬ ‫•‬
‫فال يمكننا استخدام ‪ int‬لتهيئة سلسلة ‪،‬‬ ‫•‬
‫ولذلك ال يمكن أن تكون ُمهيئات ‪ v7‬و ‪ v8‬مهيئات لعنرص‪.‬‬ ‫•‬
‫إن كانت تهيئة القائمة غري ممكنة ‪ ،‬يبحث املرتجم عن طرق أخرى لتهيئة الكائن على القيم املحددة‪.‬‬ ‫•‬

‫تمارين القسم ‪3.3.1‬‬


‫التمرين ‪: 3.12‬‬
‫‪ -‬أي من تعريفات املتجهات التالية خاطئة ‪ ،‬إن وجدت؟‬
‫‪ -‬بالنسبة لتلك الرشعية ‪ ،‬ارشح ما فعله التعريف‪.‬‬
‫‪ -‬بالنسبة لغري الرشعية ‪ ،‬ارشح سبب كونها غري رشعية‪.‬‬
‫;‪(a) vector<vector<int>> ivec‬‬
‫;‪(b) vector<string> svec = ivec‬‬
‫;)"‪(c) vector<string> svec(10, "null‬‬
‫التمرين ‪: 3.13‬‬
‫‪ -‬ما هو عدد العنارص املوجودة في كل من املتجهات التالية؟‬
‫‪ -‬وما هي قيم العنارص؟‬
‫;‪(a) vector<int> v1‬‬
‫)‪(b‬‬ ‫;)‪vector<int> v2(10‬‬
‫)‪(c‬‬ ‫;)‪vector<int> v3(10, 42‬‬
‫)‪(d‬‬ ‫;}‪vector<int> v4{10‬‬
‫)‪(e‬‬ ‫;}‪vector<int> v5{10, 42‬‬
‫)‪(f‬‬ ‫;}‪vector<string> v6{10‬‬
‫)‪(g‬‬ ‫;}"‪vector<string> v7{10, "hi‬‬

‫‪3.3.2. Adding Elements to a vector‬‬ ‫‪ 3٫3٫2‬إضافة عنارص إلى متجه‬

‫ال يمكن تهيئة عنارص املتجه مبارشة‬


‫إال إذا كان لدينا عدد قليل من قيم أولية معروفة ‪،‬‬ ‫•‬
‫أو أردنا عمل نسخة من متجه آخر ‪ ،‬أو أردنا تهيئة جميع العنارص على نفس القيمة‪.‬‬ ‫•‬
‫لكن الشائع عموما هو أن نقوم بإنشاء متجه ال نعرف عدد أو قيمة عنارصه اليت سنحتاجها ‪.‬‬ ‫•‬
‫وحىت لو كنا نعرف فعالً جميع القيم ‪ ،‬فإن كان لدينا عدد كبري من قيم عنارص أولية مختلفة ‪،‬‬ ‫•‬
‫فقد يكون من الصعب تحديدها عندما نقوم بإنشاء املتجه‪.‬‬ ‫•‬

‫كمثال‪ ،‬إذا احتجنا إلى متجه بقيم من ‪ 0‬إلى ‪، 9‬‬


‫فيمكننا بسهولة استخدام تهيئة قائمة‪.‬‬ ‫•‬
‫ولكن ماذا لو أردنا عنارص من ‪ 0‬إلى ‪ 99‬أو من ‪ 0‬إلى ‪ 999‬؟ ستكون تهيئة القائمة صعبة للغاية‪.‬‬ ‫•‬
‫في مثل هذه الحاالت ‪ ،‬من األفضل إنشاء متجه فارغ واستخدام عضو املتجه املسمى دفع_خلفي‬ ‫•‬
‫‪ push_back‬إلضافة عنارص في وقت التشغيل‪.‬‬
‫تأخذ عملية ‪ push_back‬قيمة و "تدفع" تلك القيمة كعنرص أخري جديد على "ظهر" املتجه‪.‬‬ ‫•‬

‫فمثال‪:‬‬

‫;‪vector<int> v2‬‬ ‫‪// empty vector‬‬


‫)‪for (int i = 0; i != 100; ++i‬‬
‫;)‪v2.push_back(i‬‬ ‫‪// append sequential integers to v2‬‬
‫‪// at end of loop v2 has 100 elements, values 0 . . . 99‬‬

‫نعرف ‪ v2‬على أنه فارغ‪.‬‬


‫على الرغم من أننا نعلم أنه سيكون لدينا في النهاية ‪ 100‬عنرص ‪ ،‬فإننا ّ‬ ‫•‬
‫سيضيف كل تكرار العدد الصحيح املتسلسل التالي كعنرص جديد في ‪.v2‬‬ ‫•‬
‫سنستخدم نفس األسلوب عند إنشاء متجه ال نعرف حىت وقت التشغيل عدد العنارص اليت ينبغي أن يحملها‪.‬‬ ‫•‬

‫فعلى سبيل املثال ‪ ،‬قد نقرأ املدخالت ‪ ،‬ونخزن القيم اليت نقرأها في املتجه‪:‬‬

‫‪// read words from standard input and store them as elements in vector‬‬
‫;‪string word‬‬
‫;‪vector<string> text‬‬ ‫‪// empty vector‬‬
‫{ )‪while (cin >> word‬‬
‫;)‪text.push_back(word‬‬ ‫‪// append word to text‬‬
‫}‬

‫مرة أخرى ‪ ،‬نبدأ بمتجه فارغ في البداية‪ .‬هذه املرة ‪ ،‬نقرأ ونخزن عد ًدا غري معروف من القيم في ‪.text‬‬
‫تنمو املتجهات بكفاءة ‪vectors Grow Efficiently‬‬ ‫مفهوم رئييس ‪:‬‬

‫يتطلب املعيار أن تطبيقات املتجه يمكن أن تضيف عنارص بكفاءة في وقت التشغيل‪.‬‬ ‫•‬
‫فغالبا ما يكون من غري الرضوري ‪ -‬وقد يؤدي إلى ضعف في األداء ‪-‬‬
‫ً‬ ‫ونظرًا ألن املتجهات تنمو بكفاءة ‪،‬‬ ‫•‬
‫تحديد متجه بحجم معني‪.‬‬
‫االستثناء من هذه القاعدة هو إن كانت جميع العنارص تحتاج بالفعل إلى نفس القيمة‪.‬‬ ‫•‬

‫أما إذا كانت هناك حاجة إلى قيم عنارص مختلفة ‪،‬‬ ‫•‬
‫فعادة ما يكون من األفضل تحديد متجه فارغ وإضافة عنارص عندما تصبح القيم اليت نحتاجها معروفة‬ ‫•‬
‫في وقت التشغيل‪.‬‬
‫عالوة على ذلك ‪ ،‬كما سرنى في فقرة ‪ ، 9.4‬يوفر املتجه إمكانيات تتيح لنا زيادة تحسني أداء وقت‬ ‫•‬
‫التشغيل عندما نضيف عنارص‪.‬‬
‫يختلف البدء بمتجه فارغ وإضافة عنارص في وقت التشغيل بشكل واضح عن كيفية استخدامنا‬ ‫•‬
‫للمصفوفات املدمجة في لغة يس وفي معظم اللغات األخرى‪.‬‬
‫خصوصاً ‪ ،‬إن كنت معتا ًدا على يس أو جافا ‪ ،‬قد تتوقع أن األفضل تحديد املتجه بحجمه املتوقع‪.‬‬ ‫•‬
‫لكن في الواقع ‪ ،‬العكس هو الصحيح في العادة‪.‬‬ ‫•‬

‫اﻵثار الربمجية ﻹضافة عنارص إلى متجه‬


‫‪Programming Implications of Adding Elements to a vector‬‬

‫حقيقة أنه يمكننا بسهولة وكفاءة إضافة عنارص إلى متجه يبسط إلى حد كبري العديد من مهام الربمجة‪.‬‬
‫الزتاما جدي ًدا على برامجنا‪:‬‬
‫ً‬ ‫ومع ذلك ‪ ،‬فإن هذه البساطة تفرض‬ ‫•‬
‫حيث يجب أن نتأكد من صحة أي حلقات نكتبها حىت لو غريت الحلقة حجم املتجه‪.‬‬ ‫•‬
‫وضوحا كلما تعلمنا املزيد عن‬
‫ً‬ ‫ستصبح اآلثار األخرى اليت تنجم عن الطبيعة الحيوية للمتجهات أكرث‬ ‫•‬
‫استخدامها‪.‬‬
‫ومع ذلك ‪ ،‬هناك تأثري واحد جدير باملالحظة بالفعل‪:‬‬ ‫•‬
‫ألسباب سنستكشفها في فقرة ‪ ، 5.4.3‬لن يمكننا استخدام ‪ for‬النطاق إذا كانت بنية الحلقة تضيف عنارص‬ ‫•‬
‫إلى املتجه‪.‬‬

‫تحذير‬

‫يجب أال تغري بنية ‪ for‬النطاق حجم التسلسل الذي يتم التكرار عليه‪.‬‬

‫تمارين القسم ‪3.3.2‬‬


‫التمرين ‪:3.14‬‬
‫برنامجا لقراءة تسلسل أعداد صحيحة من ‪ cin‬وتخزين هذه القيم في متجه‪.‬‬
‫ً‬ ‫اكتب‬
‫التمرين ‪:3.15‬‬
‫كرر الربنامج السابق ولكن لقراءة السالسل هذه املرة‪.‬‬

‫‪3.3.3. Other vector Operations‬‬ ‫‪ 3٫3٫3‬عمليات متجه أخرى‬


‫باإلضافة إلى ‪ ، push_back‬يوفر املتجه عد ًدا قليالً من العمليات األخرى ‪ ،‬معظمها يشبه العمليات املقابلة على‬
‫السالسل‪ .‬يرسد الجدول ‪ 3.5‬أهمها‪.‬‬

‫الجدول ‪ .3.5‬عمليات املتجه‬


‫)(‪v.empty‬‬ ‫‪Returns true if v is empty. Otherwise returns false.‬‬
‫)(‪v.size‬‬ ‫‪Returns number of elements in v.‬‬
‫)‪v.push_back(t‬‬ ‫‪Adds element with value t to end of v.‬‬
‫]‪v[n‬‬ ‫‪Returns reference to element at position n in v.‬‬
‫‪v1 = v2‬‬ ‫‪Replace elements in v1 with copy of element in v2.‬‬
‫‪Replace elements in v1 with copy of element in comma-‬‬
‫}… ‪v1 = {a, b,c‬‬
‫‪separated list.‬‬
‫‪v1 == v2‬‬ ‫‪v1 and v2 equal if they have same number of element and‬‬
‫‪v1 != v2‬‬ ‫‪each element in v1 equal to corresponding element in v2.‬‬
‫=> ‪<, <=, >,‬‬ ‫‪Have their normal meanings using dictionary ordering.‬‬

‫نتوصل إلى عنارص املتجه بنفس الطريقة اليت نتوصل بها إلى أحرف في سلسلة‪:‬‬ ‫•‬
‫من خالل موضعها في املتجه‪.‬‬ ‫•‬

‫على سبيل املثال ‪ ،‬يمكننا استخدام ‪ for‬النطاق ملعالجة جميع العنارص في املتجه‪:‬‬

‫;}‪vector<int> v{1,2,3,4,5,6,7,8,9‬‬
‫)‪for (auto &i : v‬‬ ‫)‪// for each element in v (note: i reference‬‬
‫;‪i *= i‬‬ ‫‪// square element value‬‬
‫)‪for (auto i : v‬‬ ‫‪// for each element in v‬‬
‫;" " << ‪cout << i‬‬ ‫‪// print element‬‬
‫;‪cout << endl‬‬

‫نعرف متغري التحكم ‪ i‬كمرجع ؛‬


‫في الحلقة األولى ‪ّ ،‬‬ ‫•‬
‫حىت نتمكن من استخدام ‪ i‬لتعيني قيم جديدة على العنارص في ‪.v‬‬ ‫•‬
‫نرتك لـ ‪ auto‬استنتاج نوع ‪.i‬‬ ‫•‬
‫تستخدم هذه الحلقة شكالً جدي ًدا من عامل التعيني املركب‪.‬‬ ‫•‬
‫فكما رأينا سابقا يضيف العامل =‪ +‬املعامل األيمن إلى اليسار ويخزن النتيجة في املعامل األيرس‪.‬‬ ‫•‬
‫يترصف العامل =* بشكل مشابه ‪ ،‬إال أنه يضاعف معامالت الجهة اليمىن واليرسى ‪ ،‬ويخزن النتيجة في‬ ‫•‬
‫املعامل األيرس‪.‬‬
‫‪ for‬النطاق الثانية لطباعة كل عنرص‪.‬‬ ‫•‬

‫تترصف األعضاء ()‪ empty‬و ()‪ size‬كما تترصف أعضاء السلسلة املقابلة ‪:‬‬
‫تعيد ‪ empty‬منطقيا ‪ ،‬يشري إلى ما إذا كان املتجه يحتوي على أي عنارص ‪،‬‬ ‫•‬
‫وتعيد ‪ size‬عدد العنارص في املتجه‪.‬‬ ‫•‬
‫املعرف بواسطة نوع املتجه املقابل‪.‬‬
‫ّ‬ ‫يقوم عضو الحجم ‪ size‬بإعادة قيمة من نوع ‪size_type‬‬ ‫•‬

‫ملحوظة‬
‫دائما على نوع عنرصه‪:‬‬
‫ً‬ ‫املعرف به‪ .‬يشتمل نوع املتجه‬
‫ّ‬ ‫الستخدام ‪ ، size_type‬يجب تسمية النوع‬
‫‪vector<int>::size_type‬‬ ‫‪// ok‬‬
‫‪vector::size_type‬‬ ‫‪// error‬‬

‫تملك عوامل املساواة والعالئق املنطقية نفس سلوك عمليات السلسلة املقابلة‪.‬‬
‫فيتساوى متجهان إن كان لديهما نفس عدد العنارص وكانت لعنارصهما املتقابلة نفس القيمة‪.‬‬ ‫•‬
‫تطبق العوامل العالئقية ترتيب القاموس‪:‬‬ ‫•‬
‫فإن كان للمتجهات أحجام مختلفة ‪ ،‬لكن عنارصها املشرتكة متساوية ‪،‬‬ ‫•‬
‫فإن املتجه الذي يحتوي على عدد أقل من العنارص يكون أقل من الذي يحتوي على عدد أكرب منها‪.‬‬ ‫•‬
‫أما إن كانت للعنارص قيم مختلفة ‪ ،‬فإن العالقة بني املتجهات تتحدد بالعالقة بني أولى العنارص املختلفة ‪.‬‬ ‫•‬

‫ال يمكننا مقارنة متجهني إال إن تمكنا من مقارنة العنارص فيهما‪.‬‬


‫تعرف بعض أنواع الفئات ‪ ،‬مثل السلسلة ‪ ،‬معىن عوامل املساواة والعالئقية‪.‬‬
‫ّ‬ ‫•‬
‫وبعضها اآلخر ‪ ،‬كفئتنا عنرص املبيعات ‪ ،‬ال تفعل ذلك‪.‬‬ ‫•‬
‫العمليات الوحيدة اليت تدعمها عنرص املبيعات هي تلك املدرجة في الفقرة ‪.1.5.1‬‬ ‫•‬
‫لم تتضمن تلك عمليات مساواة أو عالئقية‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬ال يمكننا مقارنة عنارص متجهني نوع عنرص مبيعات‪.‬‬ ‫•‬

‫‪Computing a vector Index‬‬ ‫حوسبة فهرسة متجه‬

‫يمكننا إحضار عنرص معني باستخدام عامل الفهرسة‪.‬‬


‫ومثل السالسل ‪ ،‬تبدأ فهرسة املتجه من الصفر ؛‬ ‫•‬
‫نوع الفهرس هو ‪ size_type‬املقابل ؛ و‪ -‬بافرتاض أن املتجه ليس ثاب ًتا ‪ -‬يمكننا الكتابة إلى العنرص‬ ‫•‬
‫املعاد بواسطة عامل الفهرسة‪.‬‬
‫إضافة إلى ذلك ‪ ،‬كما في الفقرة ‪ ، 3.2.3‬يمكننا حوسبة فهرس وجلب العنرص مبارش ًة في هذا املوضع‪.‬‬ ‫•‬

‫على سبيل املثال ‪ ،‬لنفرتض أن لدينا مجموعة من الدرجات ترتاوح من ‪ 0‬إلى ‪.100‬‬
‫نود حساب عدد الدرجات اليت تقع في مجموعات مختلفة عرشية‪.‬‬ ‫•‬
‫بني الصفر و ‪ 100‬هناك ‪ 101‬درجة ممكنة‪.‬‬ ‫•‬
‫يمكن تمثيل هذه الدرجات من خالل ‪ 11‬مجموعة‪:‬‬ ‫•‬
‫‪ 10‬مجموعات من ‪ 10‬درجات لكل منها باإلضافة إلى مجموعة واحدة للحصول على الدرجة املثالية ‪.100‬‬ ‫•‬
‫ستحسب املجموعة األولى درجات من ‪ 0‬إلى ‪ ، 9‬واملجموعة الثانية درجات من ‪ 10‬إلى ‪ ، 19‬وهكذا‪.‬‬ ‫•‬
‫تحسب املجموعة النهائية عدد النقاط اليت تم تحقيقها من ‪.100‬‬ ‫•‬

‫لتجميع الدرجات بهذه الطريقة ‪ ،‬إذا كانت مدخالتنا ‪:‬‬

‫‪42‬‬ ‫‪65‬‬ ‫‪95‬‬ ‫‪100‬‬ ‫‪39‬‬ ‫‪67‬‬ ‫‪95‬‬ ‫‪76‬‬ ‫‪88‬‬ ‫‪76‬‬ ‫‪83‬‬ ‫‪92‬‬ ‫‪76‬‬ ‫‪93‬‬

‫فيجب أن تكون املخرجات كالتالي‪:‬‬

‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪2‬‬ ‫‪4‬‬ ‫‪1‬‬

‫حيث تشري املخرجات إلى عدم وجود درجات أقل من ‪ ، 30‬ودرجة واحدة في الثالثينيات ‪،‬‬ ‫•‬
‫ودرجة واحدة في األربعينيات ‪ ،‬وال يشء في الخمسينيات ‪ ،‬واثنان في الستينيات ‪،‬‬ ‫•‬
‫وثالثة في السبعينيات ‪ ،‬واثنان في الثمانينيات ‪ ،‬وأربعة في التسعينيات ‪ ،‬ودرجة واحدة من ‪.100‬‬ ‫•‬

‫متجها يحتوي على ‪ 11‬عنرصًا لحفظ العدادات لكل مجموعة‪.‬‬


‫ً‬ ‫سنستخدم‬
‫يمكننا تحديد فهرس املجموعة لدرجة معينة بقسمة تلك الدرجة على ‪.10‬‬ ‫•‬
‫عندما نقسم عددين صحيحني ‪ ،‬نحصل على عدد صحيح يتم فيه اقتطاع الجزء الكرسي‪.‬‬ ‫•‬
‫على سبيل املثال ‪ 42 ،‬تقسيم‪ 10‬يساوي ‪ ، 4‬و ‪ 65‬تقسيم‪ 10‬يساوي ‪ 6‬و ‪ 100‬تقسيم‪ 10‬هو ‪.10‬‬ ‫•‬

‫وبمجرد أن نحوسب فهرس املجموعة ‪ ،‬يمكننا استخدامه لفهرسة املتجه وإحضار العداد الذي نريد زيادته‪:‬‬

‫‪// count number of grades by clusters of ten: 0-9, 10-19, … 90-99, 100‬‬
‫;)‪vector<unsigned> scores(11, 0‬‬ ‫‪// 11 buckets, all initially 0‬‬
‫;‪unsigned grade‬‬
‫{ )‪while (cin >> grade‬‬ ‫‪// read the grades‬‬
‫)‪if (grade <= 100‬‬ ‫‪// handle only valid grades‬‬
‫;]‪++scores[grade/10‬‬ ‫‪// increment counter for current cluster‬‬
‫}‬

‫نبدأ بتعريف متجه لحفظ عدادات املجموعات‪.‬‬ ‫•‬


‫في هذه الحالة ‪ ،‬نريد أن يكون لكل عنرص نفس القيمة ‪،‬‬ ‫•‬
‫لذلك نقوم بتخصيص كل العنارص الـ ‪ ، 11‬تمت تهيئة كل منها على صفر‪.‬‬ ‫•‬
‫رشط ‪ while‬يقرأ الدرجات‪.‬‬ ‫•‬
‫داخل الحلقة ‪ ،‬نتحقق من أن الدرجة اليت نقرأها صالحة القيمة (أي أقل من أو تساوي ‪.)100‬‬ ‫•‬
‫بافرتاض أن الدرجة صحيحة ‪ ،‬نقوم بزيادة العداد املناسب للدرجة‪.‬‬ ‫•‬

‫الجملة اليت يقوم بالزتايد هي مثال جيد لنوع الكود املقتضب املمزي لربامج يس‪.++‬‬

‫;]‪++scores[grade/10‬‬ ‫‪// increment counter for current cluster‬‬

‫فهذه الجملة مساوية ومكافئة لـ ‪:‬‬

‫‪auto ind‬‬ ‫;‪= grade/10‬‬ ‫‪// get bucket index‬‬


‫;‪scores[ind] = scores[ind]+1‬‬ ‫‪// increment count‬‬

‫نحوسب الفهرس بقسمة ‪ grade‬على ‪ 10‬ونستخدم نتيجة القسمة لفهرسة ‪.scores‬‬ ‫•‬
‫تجلب الدرجات املفهرسة العداد املناسب لهذه الدرجة‪.‬‬ ‫•‬
‫نقوم بزيادة قيمة هذا العنرص لإلشارة إلى حدوث درجة في النطاق املعطى‪.‬‬ ‫•‬

‫كما رأينا ‪ ،‬عندما نستخدم رمز فهرسة ‪ ،‬يجب أن نفكر في كيفية معرفتنا أن الفهارس تقع ضمن النطاق‪.‬‬
‫في هذا الربنامج ‪ ،‬نتحقق من أن املدخالت تكون درجة صالحة في نطاق بني ‪ 0‬و ‪.100‬‬ ‫•‬
‫وبالتالي ‪ ،‬فإننا نعلم أن الفهارس اليت يمكننا حسابها ترتاوح بني ‪ 0‬و ‪.10‬‬ ‫•‬
‫هذه الفهارس ستكون بني ‪ 0‬و ‪. scores.size()-1‬‬ ‫•‬

‫‪Subscripting Does Not Add Elements‬‬ ‫الفهرسة ﻻ تضيف عنارص‬


‫يعتقد املربمجون الجدد في يس‪ ++‬أحيا ًنا أن فهرسة متجه قد يضيف عنارص ؛ لكن هذا ال يحدث‪.‬‬
‫يعزتم الكود التالي إضافة عرشة عنارص إلى ‪: ivec‬‬

‫;‪vector<int> ivec‬‬ ‫‪// empty vector‬‬


‫)‪for (decltype(ivec.size()) ix = 0; ix != 10; ++ix‬‬
‫;‪ivec[ix] = ix‬‬ ‫‪// disaster: ivec has no elements‬‬

‫بكل حال ‪ ،‬هذا خطأ‪ ivec :‬متجه فارغ ؛ ال توجد عنارص لفهرستها!‬
‫وكما رأينا سابقاً ‪ ،‬فالطريقة الصحيحة لكتابة هذه الحلقة هي استخدام ‪: push_back‬‬

‫)‪for (decltype(ivec.size()) ix = 0; ix != 10; ++ix‬‬


‫;)‪ivec.push_back(ix‬‬ ‫‪// ok: adds new element with value ix‬‬

‫ﻻ تقم بفهرسة إﻻ عنارص تعلم بتواجدها!‬ ‫احذر ‪:‬‬


‫!‪Subscript Only Elements that are Known to Exist‬‬

‫من املهم للغاية أن نفهم أننا نستخدم عامل الفهرسة [ ] لجلب عنارص موجودة فعالً فقط‪ .‬فمثال‪:‬‬ ‫•‬
‫;‪vector<int> ivec‬‬ ‫‪// empty vector‬‬
‫;]‪cout << ivec[0‬‬ ‫!‪// error: ivec has no elements‬‬
‫‪vector<int> ivec2(10); // vector with ten elements‬‬
‫;]‪cout << ivec2[10‬‬ ‫‪// error: ivec2 has elements 0 … 9‬‬

‫يعد إدخال عنرص غري موجود خطًأ ‪ ،‬ولكنه خطأ من غري املحتمل أن يكتشفه املرتجم‪.‬‬ ‫•‬
‫بدال ً من ذلك ‪ ،‬القيمة اليت نحصل عليها في وقت التشغيل غري محددة‪.‬‬ ‫•‬
‫محاولة فهرسة عنارص غري موجودة ‪ ،‬لألسف ‪ ،‬خطأ برمجي شائع للغاية وخبيث‪.‬‬ ‫•‬
‫ما يسمى بأخطاء تجاوز سعة املخزن املؤقت هي نتيجة لعنارص فهرسة غري موجودة‪.‬‬ ‫•‬
‫شيوعا ملشاكل األمان في جهاز الكمبيوتر والتطبيقات األخرى‪.‬‬
‫ً‬ ‫مثل هذه األخطاء هي السبب األكرث‬ ‫•‬

‫تحذير‬

‫عامل الفهرسة على املتجه (والسلسلة) يقوم بجلب عنرص موجود ؛ لكن ال يضيف عنرصًا‪.‬‬

‫تلميح‬

‫تماما بـ ‪ for‬النطاق كلما أمكن ذلك‪.‬‬


‫ً‬ ‫هناك طريقة جيدة للتأكد من أن الفهرسة في النطاق هي تجنب الفهرسة‬

‫قسم التدريبات ‪3.3.3‬‬


‫التمرين ‪:3.16‬‬
‫برنامجا لطباعة حجم ومحتويات املتجهات من التمرين ‪.3.13‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬تحقق مما إذا كانت إجاباتك على هذا التمرين صحيحة‪.‬‬
‫‪ -‬إن لم تكن كذلك ‪ ،‬قم بإعادة دراسة ‪ 3.3.1‬حىت تفهم سبب كونك مخطًئ ا‪.‬‬
‫التمرين ‪:3.17‬‬
‫‪ -‬اقرأ سلسلة من الكلمات من ‪ cin‬وقم بتخزين القيم على شكل متجه‪.‬‬
‫‪ -‬بعد أن تقرأ كل الكلمات ‪ ،‬قم بمعالجة املتجه وقم بتغيري كل كلمة إلى أحرف كبرية‪.‬‬
‫‪ -‬اطبع العنارص املحولة ‪ ،‬ثماني كلمات إلى سطر‪.‬‬
‫التمرين ‪:3.18‬‬
‫‪ -‬هل الربنامج التالي رشعي؟‬
‫‪ -‬إذا لم يكن كذلك ‪ ،‬فكيف يمكنك إصالحه؟‬
‫;‪vector<int> ivec‬‬
‫;‪ivec[0] = 42‬‬
‫التمرين ‪:3.19‬‬
‫‪ -‬ضع قائمة بثالث طرق لتعريف املتجه وأعطه عرشة عنارص ‪ ،‬كل منها بالقيمة ‪.42‬‬
‫‪ -‬وضح ما إذا كانت هناك طريقة مفضلة للقيام بذلك وملاذا‪.‬‬
‫التمرين ‪:3.20‬‬
‫‪ -‬اقرأ مجموعة من األعداد الصحيحة في متجه‪.‬‬
‫‪ -‬اطبع مجموع كل زوج من العنارص املتجاورة‪.‬‬
‫متبوعا بمجموع العنرص الثاني والعنرص‬
‫ً‬ ‫‪ -‬قم بتغيري برنامجك بحيث يطبع مجموع العنرصين األول واألخري ‪،‬‬
‫الثاني إلى األخري ‪ ،‬وهكذا‪.‬‬
‫‪ . 3٫4‬مقـــــدمة عن املكـــــررات‬
‫‪3.4. Introducing Iterators‬‬

‫على الرغم من أنه يمكننا استخدام الفهرسة للوصول إلى أحرف سلسلة أو عنارص متجه ‪،‬‬
‫إال أن هناك آلية أكرث عمومية ‪ -‬تُعرف باسم املكررات ‪ -Iterators‬يمكننا استخدامها لنفس الغرض‪.‬‬ ‫•‬
‫وكما سرنى في الجزء الثاني تعرف املكتبة عدة أنواع أخرى من الحاويات إضافة إلى املتجهات‪.‬‬ ‫•‬
‫تحتوي كل حاويات املكتبة على مكررات ‪ ،‬لكن قلة منها فقط تدعم الفهرسة‪.‬‬ ‫•‬
‫من الناحية الفنية ‪ ،‬فإن السلسلة ليست نوع حاوية ‪ ،‬لكنها تدعم العديد من عمليات الحاوية‪.‬‬ ‫•‬
‫أيضا على مكررات مثل املتجهات‪.‬‬
‫وكما رأينا فالسلسلة مثل املتجه لها عامل فهرسة‪ .‬و تحتوي ً‬ ‫•‬

‫ومثل املؤرشات تمنحنا املكررات وصوال ً غري مبارش إلى كائن‪.‬‬


‫ففي حالة املكرر ‪ ،‬يكون هذا الكائن عنرصًا في حاوية أو حر ًفا في سلسلة‪.‬‬ ‫•‬
‫يمكننا استخدام مكرر لجلب عنرص وللمكررات عمليات لتتحرك من عنرص إلى آخر‪.‬‬ ‫•‬
‫صالحا أو غري صالح‪.‬‬
‫ً‬ ‫ومثل املؤرشات ‪ ،‬قد يكون مكرر ما‬ ‫•‬
‫فاملكرر الصالح يشري إلى عنرص أو يشري إلى موضع ما بعد آخر عنرص في الحاوية‪.‬‬ ‫•‬
‫تعترب جميع قيم املكرر األخرى غري صالحة‪.‬‬ ‫•‬

‫‪3.4.1. Using Iterators‬‬ ‫‪ 3٫4٫1‬استخدام املكررات‬

‫على عكس املؤرشات ‪ ،‬ال نستخدم عامل العنوان للحصول على مكرر‪.‬‬
‫لكن بدال ً من ذلك ‪ ،‬اﻷنواع اليت تملك مكررات تحوي أعضاء تعيد مكررات‪.‬‬ ‫•‬
‫على وجه الخصوص ‪ ،‬هذه األنواع تملك أعضاء باسم بداية ‪ begin‬ونهاية ‪.end‬‬ ‫•‬
‫مكررا يشري إلى أول عنرص(أو أول حرف) ‪ ،‬إن وجد ‪:‬‬
‫ً‬ ‫يعيد عضو بداية‬ ‫•‬

‫;‪// compiler determines type of b and e‬‬


‫‪// b denotes first element and e denotes one past last element in v‬‬
‫;)(‪auto b = v.begin(), e = v.end‬‬ ‫‪// b and e have same type‬‬

‫املكرر املعاد عن طريق نهاية هو مكرر مكانه "ما بعد نهاية" الحاوية املرتبطة (أو السلسلة)‪.‬‬ ‫•‬
‫يشري ذلك املكرر إلى عنرص غري موجود "ما بعد نهاية" الحاوية‪.‬‬ ‫•‬
‫فيتم استخدام نهاية كعالمة تشري إلى نهاية معالجتنا لجميع العنارص‪.‬‬ ‫•‬
‫وغالبا ما يُشار إلى املكرر املعاد من نهاية بمكرر ما بعد النهاية ‪the off-the-end iterator‬‬
‫ً‬ ‫•‬
‫أو اختصاراً "مكرر النهاية" “‪”.the end iterator‬‬ ‫•‬
‫فإن كانت الحاوية فارغة ‪ ،‬فستقوم بداية بإعادة نفس املكرر املعاد من نهاية‪.‬‬ ‫•‬

‫ملحوظة‬

‫إن كانت الحاوية فارغة ‪ ،‬فإن املكررات املعادة بواسطة ‪ begin‬و ‪ end‬متساوية ‪ -‬فكالهما مكررا ما بعد النهاية‪.‬‬
‫بشكل عام ‪ ،‬ال نعرف (أو نهتم) بالنوع الدقيق الذي يمتلكه املكرر‪.‬‬ ‫•‬
‫في هذا املثال ‪ ،‬استخدمنا ‪ auto‬لتعريف ‪ b‬و ‪. e‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬فإن لهذه املتغريات نوع ما يتم إعادته بواسطة عضوي ‪ begin‬و ‪ ، end‬على التوالي‪.‬‬ ‫•‬
‫سيكون لدينا املزيد لنقوله عن هذه األنواع ﻻحقا‪.‬‬ ‫•‬

‫‪Iterator Operations‬‬ ‫عمليات املكرر‬

‫تدعم املكررات عد ًدا قليالً من العمليات املدرجة في الجدول ‪.3.6‬‬


‫يمكننا مقارنة مكررين صالحني باستخدام == أو=!‪.‬‬ ‫•‬
‫فتكون املكررات متساوية إن كانت تشري لنفس العنرص أو كانا مكرران ملا بعد نهاية الحاوية نفسها‪.‬‬ ‫•‬
‫وإال فهي غري متساوية‪.‬‬ ‫•‬

‫الجدول ‪ .3.6‬عمليات مكرر الحاويات القياسية‬

‫‪*iter‬‬ ‫‪Returns reference to element denoted by iterator iter.‬‬

‫‪Dereference iter and fetches member named mem from‬‬


‫‪iter->mem‬‬ ‫‪underlying element.‬‬
‫‪Equivalent to (*iter).mem .‬‬

‫‪++iter‬‬ ‫‪Increments iter to refer next element in container.‬‬

‫‪--iter‬‬ ‫‪Decrements iter to refer previous element in container.‬‬

‫‪Compares two iterators for equality(inequality).‬‬


‫‪iter1 == iter2‬‬
‫‪Two iterators equal if they denote same element or‬‬
‫‪iter1 != iter2‬‬
‫‪if they off-the-end iterator for same container.‬‬

‫مثل املؤرشات ‪ ،‬يمكننا إلغاء املرجع عن مكرر للوصول إلى العنرص املشار إليه بواسطة مكرر‪.‬‬ ‫•‬
‫أيضا ‪ ،‬مثل املؤرشات ‪ ،‬قد نلغي املرجع فقط عن مكرر صالح يشري إلى عنرص‪.‬‬
‫ً‬ ‫•‬
‫إلغاء املرجع عن مكرر غري صالح أو مكرر ما بعد النهاية يعد سلوكاً غري معرف‪.‬‬ ‫•‬
‫فعلى سبيل املثال ‪ ،‬سنعيد كتابة الربنامج من الفقرة ‪ 3.2.3‬الذي استبدل أول حرف من سلسلة بحرف كبري‬ ‫•‬
‫وذلك باستخدام مكرر بدال ً من فهرسة ‪:‬‬ ‫•‬

‫;)"‪string s("some string‬‬


‫{ ))(‪if (s.begin() != s.end‬‬ ‫‪// make sure s not empty‬‬
‫;)(‪auto it = s.begin‬‬ ‫‪// it denotes first character in s‬‬
‫;)‪*it = toupper(*it‬‬ ‫‪// make that character uppercase‬‬
‫}‬

‫كما في برنامجنا األصلي ‪ ،‬نتحقق أوال ً من أن ‪ s‬ليست فارغة‪.‬‬ ‫•‬


‫في هذه الحالة ‪ ،‬نقوم بذلك عن طريق مقارنة املكررات املعادة بواسطة ‪ begin‬و ‪.end‬‬ ‫•‬
‫ستكون تلك املكررات متساوية إن كانت السلسلة فارغة‪.‬‬ ‫•‬
‫أما إن لم تكن متساوية‪ ،‬فهناك حرف واحد على األقل في ‪.s‬‬ ‫•‬
‫في داخل بنية ‪ ، if‬نحصل على مكرر أول حرف عن طريق تعيني مكرر معاد من ‪ begin‬على ‪.it‬‬ ‫•‬
‫نلغي املرجع عن املكرر لتمرير ذلك الحرف إلى ‪.toupper‬‬ ‫•‬
‫أيضا بإلغاء املرجع عن ‪ it‬على يسار التعيني من أجل تعيني الحرف املعاد من ‪ toupper‬على أول‬
‫نقوم ً‬ ‫•‬
‫حرف في ‪.s‬‬
‫وكما في برنامجنا األصلي ‪ ،‬ستكون املخرجات لهذه الحلقة هي‪:‬‬

‫‪Some string‬‬

‫‪Moving Iterators from One Element to Another‬‬ ‫تحريك املكررات من عنرص إلى آخر‬

‫تستخدم املكررات عامل الزتايد (‪ )++‬لالنتقال من عنرص إلى آخر‪.‬‬


‫منطقيا لزيادة عدد صحيح‪.‬‬
‫ً‬ ‫إن زيادة مكرر عملية مشابهة‬ ‫•‬
‫في حالة األعداد الصحيحة ‪ ،‬يكون التأثري هو "إضافة ‪ "1‬إلى قيمة العدد الصحيح‪.‬‬ ‫•‬
‫أما في حالة املكررات ‪ ،‬يكون التأثري هو "تقدم املكرر بموضع واحد"‪.‬‬ ‫•‬

‫ملحوظة‬

‫نظرًا ألن املكرر املعاد من ‪ end‬ال يشري لعنرص ‪ ،‬فال يسمح بزتايده وال إلغاء مرجع عنه‪.‬‬

‫باستخدام عامل الزتايد ‪ ،‬يمكننا إعادة كتابة برنامجنا الذي غرّي حالة أول كلمة في سلسلة لنستخدم املكررات ‪:‬‬

‫‪// process characters in s until run out of characters or hit whitespace‬‬


‫)‪for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it‬‬
‫;)‪*it = toupper(*it‬‬ ‫‪// capitalize current character‬‬

‫هذه الحلقة ‪ ،‬مثل تلك املوجودة في الفقرة ‪ ، 3.2.3‬تتكرر عرب األحرف في ‪، s‬‬ ‫•‬
‫وتتوقف عندما تواجه حرف مسافة بيضاء‪ .‬مع ذلك ‪،‬‬ ‫•‬
‫تصل هذه الحلقة إلى هذه األحرف باستخدام مكرر وليس بفهرسة‪.‬‬ ‫•‬
‫تبدأ الحلقة بتهيئة ‪ it‬على ‪ ، s.begin‬ما يعين أن ‪ it‬ستشري إلى أول حرف (إن وجد) في ‪.s‬‬ ‫•‬
‫يتحقق الرشط ما إذا كان ‪ it‬قد وصل إلى نهاية ‪ .s‬فإن لم يكن كذلك ‪،‬‬ ‫•‬
‫يقوم الرشط التالي بإلغاء املرجع عن ‪ it‬لتمرير الحرف الحالي إلى ‪ isspace‬ملعرفة ما إذا كنا قد انتهينا‪.‬‬ ‫•‬
‫في نهاية كل تكرار ‪ ،‬سننفذ ‪ ++it‬للتقدم في التكرار لنصل إلى الحرف التالي في ‪.s‬‬ ‫•‬
‫بنية هذه الحلقة ‪ ،‬هو نفس الجملة األخرية في‪ if‬السابقة‪.‬‬ ‫•‬
‫حيث نلغي املرجع عن ‪ it‬لتمرير الحرف الحالي إلى ‪ toupper‬وتعيني الحرف الكبري الناتج مرة أخرى‬ ‫•‬
‫على الحرف الذي يشري إلى ‪.it‬‬

‫الربمجة الشاملة ‪Generic Programming‬‬ ‫مفهوم رئييس‪:‬‬

‫قد يتفاجأ املربمجون الوافدون إلى يس‪ ++‬من يس أو جافا أننا استخدمنا =! بدال ً من > في حلقات ‪for‬‬ ‫•‬
‫كتلك املوجودة أعاله وفي الحلقة املوجودة في الصفحة ‪.94‬‬
‫يستخدم مربمجو يس‪ != ++‬كعادة‪ .‬يفعلون ذلك لنفس السبب الذي يجعلهم يستخدمون املكررات بدال ً‬ ‫•‬
‫من الفهرسة‪:‬‬
‫ينطبق أسلوب الكود هذا بشكل جيد على أنواع مختلفة من الحاويات اليت توفرها املكتبة‪.‬‬ ‫•‬

‫كما رأينا ‪ ،‬هناك عدد قليل من أنواع املكتبة ‪ ،‬من بينها املتجه والسلسلة ‪ ،‬تملك عامل فهرسة‪.‬‬ ‫•‬
‫وباملثل ‪ ،‬تحتوي جميع حاويات املكتبة على مكررات تعرف عاملي == و=!‪.‬‬ ‫•‬
‫معظم هذه املكررات ال تملك عامل املقارنة >‪.‬‬ ‫•‬
‫لكن ومن خالل استخدام املكررات و=! بشكل روتيين ‪ ،‬فال داعي للقلق بشأن النوع الدقيق للحاوية اليت‬ ‫•‬
‫نعالجها‪.‬‬

‫‪Iterator Types‬‬ ‫أنواع املكرر‬

‫كما أننا ال نعرف النوع الدقيق لعضو ‪ size_type‬للمتجه أو السلسلة ‪،‬‬


‫فال نعرف بشكل عام ‪ -‬ولسنا بحاجة إلى معرفة ‪ -‬النوع الدقيق للمكرر‪.‬‬ ‫•‬
‫بدال ً من ذلك ‪ ،‬ومثل ‪ ، size_type‬فأنواع املكتبة اليت تملك مكررات تقوم بتعريف أنواع تسمى املكرر‬ ‫•‬
‫واملكرر الثابت وتمثل أنواع املكرر الفعلية ‪:‬‬

‫‪vector<int>::iterator it; // it can read and write vector<int> elements‬‬


‫;‪string::iterator it2‬‬ ‫‪// it2 can read and write characters in string‬‬
‫‪vector<int>::const_iterator it3; // it3 can read but not write elements‬‬
‫;‪string::const_iterator it4‬‬ ‫‪// it4 can read but not write characters‬‬

‫يترصف املكرر الثابت كاملؤرش الثابت‪.‬‬


‫ومثل املؤرش الثابت ‪ ،‬يقرأ املكرر الثابت العنرص الذي يشري إلى ‪ it‬لكن ال يكتب عليه ؛‬ ‫•‬
‫بينما كائن نوع املكرر يمكنه القراءة والكتابة‪.‬‬ ‫•‬
‫إذا كان املتجه أو السلسلة ثابتة ‪ ،‬فقد نستخدم فقط نوع املكرر الثابت‪.‬‬ ‫•‬
‫لكن مع متجه أو سلسلة غري ثابتة ‪ ،‬يمكننا استخدام إما املكرر أو املُكرر الثابت‪.‬‬ ‫•‬

‫املكررات وأنواع املكرر ‪Iterators and Iterator Types‬‬ ‫مصطلحات‪:‬‬

‫يستخدم مصطلح مكرر لإلشارة إلى ثالثة كيانات مختلفة‪.‬‬ ‫•‬


‫فقد نعين مفهوم املكرر ‪ ،‬أو قد نشري إلى نوع املكرر املعرف بواسطة الحاوية ‪ ،‬أو قد نشري إلى كائن‬ ‫•‬
‫كمكرر‪.‬‬

‫من املهم أن نفهم أن هناك مجموعة من األنواع ذات الصلة من الناحية املفاهيمية‪.‬‬ ‫•‬
‫النوع عبارة عن مكرر إن كان يدعم مجموعة مشرتكة من اإلجراءات‪.‬‬ ‫•‬
‫تتيح لنا هذه اإلجراءات الوصول إلى عنرص في حاوية وتسمح لنا أن نتحرك من عنرص إلى آخر‪.‬‬ ‫•‬

‫مكررا ؛ نوع املكرر هذا يدعم اإلجراءات للمكرر (املفاهيمي)‪.‬‬


‫ً‬ ‫نوعا يسمى‬
‫تعرف كل فئة حاوية ً‬ ‫•‬

‫‪The begin and end Operations‬‬ ‫عمليات بداية ونهاية‬

‫يعتمد النوع املعاد بواسطة بداية ونهاية ‪ begin and end‬على ما إذا كان الكائن الذي يتم تشغيلهما عليه ثاب ًتا‪.‬‬
‫فإن كان الكائن ثاب ًتا ‪ ،‬فعندها ستقوم ‪ begin and end‬بإعادة مكرر ثابت ؛‬ ‫•‬
‫أما إن لم يكن الكائن ثاب ًتا ‪ ،‬فسيتم إعادة مكرر ‪:‬‬ ‫•‬
‫;‪vector<int> v‬‬
‫;‪const vector<int> cv‬‬
‫;)(‪auto it1 = v.begin‬‬ ‫‪// it1 has type vector<int>::iterator‬‬
‫;)(‪auto it2 = cv.begin‬‬ ‫‪// it2 has type vector<int>::const_iterator‬‬

‫غالبا ما يكون هذا السلوك االفرتايض ليس هو ما نريده‪.‬‬


‫ً‬ ‫•‬
‫فألسباب سنرشحها في الفقرة ‪ ، 6.2.3‬من األفضل عاد ًة استخدام نوع ثابت (مثل املكرر الثابت)‬ ‫•‬
‫وذلك عندما نحتاج إلى القراءة دون أن نحتاج إلى الكتابة على كائن‪.‬‬ ‫•‬
‫للسماح لنا بطلب نوع املكرر الثابت على وجه التحديد‪،‬‬ ‫•‬
‫قدم املعيار الجديد دالتني جديدتني باسم ‪ cbegin‬و ‪:cend‬‬ ‫•‬

‫;)(‪auto it3 = v.cbegin‬‬ ‫‪// it3 has type vector<int>::const_iterator‬‬

‫كأعضاء بداية ونهاية ‪ ،‬تقوم هذه األعضاء بإعادة مكررات إلى أول عنرص وما بعد آخر عنرص في الحاوية‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬وبغض النظر عما إذا كان املتجه (أو السلسلة) ثاب ًتا ‪ ،‬فإنهما تُعيدان مكررا ثابتا‪.‬‬ ‫•‬

‫‪Combining Dereference and Member Access‬‬ ‫الدمج بني إزالة املرجع والوصول للعضو‬

‫عندما نلغي مرجعا عن مكرر ‪ ،‬نصل إلى الكائن الذي يشري إليه املكرر‪.‬‬
‫فإن كان لذلك الكائن نوع فئة ‪ ،‬فقد نرغب في الوصول إلى عضو من هذا الكائن‪.‬‬ ‫•‬
‫فارغا‪.‬‬
‫ً‬ ‫على سبيل املثال ‪ ،‬قد يكون لدينا متجه لسالسل وقد نحتاج إلى معرفة ما إذا كان عنرص معني‬ ‫•‬

‫وبافرتاض أن ‪ it‬مكرر في ذلك املتجه ‪ ،‬يمكننا التحقق مما إذا كانت السلسلة اليت تشري إليها فارغة كما يلي ‪:‬‬

‫)(‪(*it).empty‬‬

‫ألسباب سنغطيها في الفقرة ‪ ، 4.1.2‬فإن األقواس املوجودة في الكود أعاله رضورية‪.‬‬ ‫•‬
‫تخرب األقواس أنه سيطبق عامل إلغاء املرجع عن ‪ it‬ويطبق عامل النقطة على نتيجة إلغاء املرجع عن ‪.it‬‬ ‫•‬
‫لكن بدون األقواس ‪ ،‬سيطبق عامل النقطة على ‪ ، it‬وليس على الكائن الناتج عنها ‪:‬‬ ‫•‬

‫‪// de-references it and calls member empty on resulting object‬‬


‫)(‪(*it).empty‬‬
‫‪// error: attempts to fetch member named empty from it‬‬
‫‪// but it iterator and has no member named empty‬‬
‫)(‪*it.empty‬‬

‫يتم تفسري التعبري الثاني على أنه طلب لجلب العضو ‪ empty‬من الكائن املسمى ‪.it‬‬ ‫•‬
‫بكل حال ‪ ،‬فـ ‪ it‬مكرر وليس له عضو يسمى ‪. empty‬‬ ‫•‬
‫ومن ثم ‪ ،‬فإن هذا التعبري خاطئ‪.‬‬ ‫•‬

‫لتبسيط عبارات مثل هذه ‪،‬‬


‫• تعرف اللغة عامل السهم (العامل <‪ .)-‬حيث يجمع بني إلغاء املرجع والوصول إلى األعضاء في عملية‬
‫واحدة‪.‬‬
‫وهذا يعين أن ‪ it->mem‬هو مرادف لـ‬ ‫•‬

‫‪(*it).mem‬‬

‫على سبيل املثال ‪،‬‬


‫متجها من نوع <‪ >string‬يسمى ‪ text‬يحتفظ بالبيانات من ملف نيص‪.‬‬ ‫ً‬ ‫لنفرتض أن لدينا‬ ‫•‬
‫كل عنرص في املتجه سيكون إما جملة أو سلسلة فارغة تمثل فاصل فقرة‪.‬‬ ‫•‬

‫فارغا ‪:‬‬
‫ً‬ ‫فإذا أردنا طباعة محتويات الفقرة األولى من النص ‪ ،‬فسنكتب حلقة تتكرر خالل ‪ text‬حىت نواجه عنرصًا‬

‫‪// print each line in text up to first blank line‬‬


‫)‪for (auto it = text.cbegin();it != text.cend() && !it->empty(); ++it‬‬
‫;‪cout << *it << endl‬‬

‫سنبدأ بتهيئة ‪ it‬لتشري إلى أول عنرص في ‪.text‬‬ ‫•‬


‫فارغا‪.‬‬
‫ً‬ ‫تستمر الحلقة حىت نقوم بمعالجة كل عنرص في ‪ text‬أو نجد عنرصًا‬ ‫•‬
‫فارغا ‪ ،‬فإننا نطبع العنرص الحالي‪.‬‬
‫ً‬ ‫وطاملا أن هناك عنارص ولم نر عنرصًا‬ ‫•‬
‫تجدر اإلشارة إلى أنه ونظرًا ألن الحلقة تقرأ العنارص من ‪ text‬وال تكتب عليها ‪ ،‬فقد استخدمنا ‪cbegin‬‬ ‫•‬
‫و ‪ cend‬للتحكم في املكرر‪.‬‬

‫‪Some vector Operations Invalidate Iterators‬‬ ‫بعض عمليات املتجه تبطل املكررات‬

‫آثارا لحقيقة أن املتجهات يمكن أن تنمو حيوياً‪.‬‬


‫ً‬ ‫في الفقرة ‪ 2.3.3‬الحظنا أن هناك‬
‫أيضا أن أحد هذه الدالالت هو أنه ال يمكننا إضافة عنارص إلى متجه داخل حلقة ‪ for‬النطاق‪.‬‬ ‫• الحظنا ً‬
‫• النتيجة األخرى هي أن أي عملية ‪ ،‬مثل ‪ ، push_back‬تغري حجم املتجه يحتمل أن تبطل كل املكررات في‬
‫هذا املتجه‪.‬‬
‫• سنستكشف كيف تصبح املكررات غري صالحة بمزيد من التفصيل في الفقرة ‪.9.3.6‬‬

‫تحذير‬

‫في الوقت الحالي ‪ ،‬من املهم إدراك أن الحلقات اليت تستخدم املكررات‬
‫يجب أال تضيف عنارص إلى الحاوية اليت تشري إليها املكررات‪.‬‬

‫قسم التدريبات ‪3.4.1‬‬


‫التمرين ‪:3.21‬‬
‫أعد التمرين األول من الفقرة ‪( 3.3.3‬ص ‪ )105‬باستخدام املكررات‪.‬‬
‫التمرين ‪:3.22‬‬
‫‪ -‬راجع الحلقة اليت طبعت الفقرة األولى في النص لتغيري العنارص في النص اليت تتوافق مع الفقرة األولى مع‬
‫األحرف الكبرية‪.‬‬
‫‪ -‬بعد تحديث النص ‪ ،‬اطبع محتوياته‪.‬‬
‫التمرين ‪:3.23‬‬
‫برنامجا إلنشاء متجه بعرشة عنارص ‪.int‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬باستخدام مكرر ‪ ،‬عنّي لكل عنرص قيمة تساوي ضعف قيمته الحالية‪.‬‬
.‫ اخترب الربنامج الخاص بك عن طريق طباعة املتجه‬-

3.4.2. Iterator Arithmetic ‫ حسابيات املكرر‬3٫4٫2

.‫يؤدي تزايد مكرر إلى تحريك املكرر عنرصًا واح ًدا كل مرة‬
.‫• تملك جميع حاويات املكتبة مكررات تدعم الزتايد‬
.‫ يمكننا استخدام == و=! ملقارنة مكررين صالحني في أي من أنواع حاويات املكتبة‬، ‫وباملثل‬ •
.‫كما تدعم املكررات للسلسلة واملتجه عمليات إضافية يمكنها التحريك عدة عنارص مكرر في املرة الواحدة‬ •
.‫كذلك تدعم جميع العوامل العالئقية‬ •

.3.7 ‫ وصفت في الجدول‬، ‫غالبا باسم حسابيات املكرر‬


ً ‫هذه العمليات يشار إليها‬

‫ العمليات املدعومة بمكررات املتجهات والسالسل‬.3.7 ‫الجدول‬

Adding (subtracting) integral value to (from) iterator


yields iterator that many elements forward (backward)
iter + n
within container.
iter – n
Resulting iterator must denote elements in, or one past
end of, same container.

Compound-assignment for iterator addition and


iter1 += n
subtracting. Assigns to iter1 value of adding n to, or
iter1 -= n
subtracting n from, iter1.

Subtracting two iterators yields number that when added


to right-hand iterator yields left-hand iterator.
iter1– iter2
Iterators must denote elements in, or one past end of,
same container.

Relational operators on iterators.


One iterator less than another if it refers to element
that appears in container before one referred to by
<, <=, >, >=
other iterator.
Iterators must denote elements in, or one past end of,
same container.

Arithmetic Operations on Iterators ‫العمليات الحسابية على املكررات‬

.ً‫يمكننا إضافة (أو طرح) قيمة صحيحة ومكررا‬


.‫• القيام بذلك يعيد مكرراً تم وضعه نحو األمام (أو الخلف) لعدة عنارص‬
‫ يجب أن تشري النتيجة إلى عنرص في نفس املتجه (أو‬، ‫ومكررا‬
ً ‫• فعندما نضيف أو نطرح قيمة صحيحة‬
. ‫السلسلة) أو تشري إلى ما بعد نهاية املتجه (أو السلسلة) املرتبط‬
:‫ يمكننا حساب مكرر ألقرب عنرص ملنتصف املتجه‬، ‫كمثال‬

// compute iterator to element closest to midpoint of vi


auto mid = vi.begin() + vi.size() / 2;
‫إن كان ‪ vi‬يحمل ‪ 20‬عنرصًا ‪ ،‬فإن ‪ vi.size()/2‬هو ‪.10‬‬ ‫•‬
‫في هذه الحالة ‪ ،‬قد نود ضبط ‪ mid‬لتساوي ‪. vi.begin()+10‬‬ ‫•‬
‫بتذكر أن الفهرسة تبدأ من ‪ ، 0‬فإن هذا العنرص مثيل ]‪ ، vi.[10‬عارش عنرص بعد األول‪.‬‬ ‫•‬

‫إضافة إلى مقارنة مكررين من أجل املساواة ‪،‬‬


‫يمكننا مقارنة مكررات املتجهات والسلسلة باستخدام العوامل العالئقية ( < ‪.) = > ، > ، =< ،‬‬ ‫•‬
‫يجب أن تكون املكررات صالحة ويجب أن تشري إلى عنارص في (أو ما بعد نهاية) نفس املتجه أو السلسلة‪.‬‬ ‫•‬

‫فمثالً ‪ ،‬وبافرتاض أن ‪ it‬مكرر في نفس املتجه مثل ‪ ، mid‬يمكننا التحقق ما إن كان يشري إلى عنرص قبل أو بعد‬
‫‪ mid‬كالتالي ‪:‬‬

‫)‪if (it < mid‬‬


‫‪// process elements in first half of vi‬‬

‫أيض ا طرح مكررين طاملا أنهما يشريان إلى عنارص في نفس املتجه أو السلسلة أو ما بعد نهايته‪.‬‬
‫يمكننا ً‬ ‫•‬
‫والنتيجة ستكون املسافة بني املكررات‪.‬‬ ‫•‬
‫نعين باملسافة هنا املقدار الذي يتعني علينا من خالله تغيري مكرر واحد للحصول على اآلخر‪.‬‬ ‫•‬
‫سمى ‪.difference_type‬‬
‫نوع النتيجة هو نوع عدد صحيح ذو إشارة يُ َّ‬ ‫•‬
‫يعرفان ‪.difference_type‬‬
‫كل من املتجه والسلسلة ّ‬ ‫•‬
‫هذا النوع ذو إشارة ‪ ،‬ألن الطرح قد يعطي نتيجة سلبية‪.‬‬ ‫•‬

‫‪Using Iterator Arithmetic‬‬ ‫استخدام حسابيات املكرر‬

‫الخوارزمية الكالسيكية اليت تستخدم حسابية املكرر هي البحث الثنائي‪.‬‬


‫يبحث البحث الثنائي عن قيمة معينة في تسلسل تم فرزه‪.‬‬ ‫•‬
‫حيث يعمل من خالل النظر إلى أقرب عنرص إلى منتصف التسلسل‪.‬‬ ‫•‬
‫فإذا كان ذلك العنرص هو العنرص الذي نريده ‪ ،‬فقد انتهينا‪.‬‬ ‫•‬
‫عدا ذلك ‪ ،‬إن كان ذلك العنرص أصغر من العنرص الذي نريده ‪،‬‬ ‫•‬
‫فإننا نواصل بحثنا من خالل النظر فقط إلى العنارص ما بعد العنرص املرفوض‪.‬‬ ‫•‬
‫إن كان العنرص األوسط أكرب من العنرص الذي نريده ‪ ،‬فإننا نستمر بالبحث فقط في النصف األول‪.‬‬ ‫•‬
‫متوسطا جدي ًدا في النطاق املصغر ونستمر في البحث حىت نجد العنرص أو تنفد العنارص‪.‬‬
‫ً‬ ‫نحسب عنرصًا‬ ‫•‬

‫يمكننا إجراء بحث ثنائي باستخدام املكررات على النحو التالي‪:‬‬

‫‪// text must be sorted‬‬


‫‪// beg and end will denote range we searching‬‬
‫;)(‪auto beg = text.begin(), end = text.end‬‬
‫;‪auto mid = text.begin() + (end - beg)/2‬‬ ‫‪// original midpoint‬‬
‫‪// while there still elements to look at and we haven't yet found sought‬‬
‫{ )‪while (mid != end && *mid != sought‬‬
‫)‪if (sought < *mid‬‬ ‫?‪// is element we want in first half‬‬
‫;‪end = mid‬‬ ‫‪// if so, adjust range to ignore second half‬‬
‫‪else‬‬ ‫‪// element we want in second half‬‬
‫;‪beg = mid + 1‬‬ ‫‪// start looking with element just after mid‬‬
‫;‪mid = beg + (end – beg)/2‬‬ ‫‪// new midpoint‬‬
‫}‬

‫نبدأ بتعريف ثالثة مكررات‪ beg :‬سيكون أول عنرص في النطاق ‪،‬‬ ‫•‬
‫و‪ end‬مكرر ما بعد العنرص األخري ‪ ،‬و‪ mid‬العنرص األقرب إلى الوسط‪.‬‬ ‫•‬
‫نقوم بتهيئة هذه املكررات لتشري إلى النطاق بأكمله في متجه <‪ >string‬يسمى ‪.text‬‬ ‫•‬

‫فارغا‪.‬‬
‫ً‬ ‫تتحقق الحلقة أوال ً من أن النطاق ليس‬
‫إن كان ‪ mid‬يساوي القيمة الحالية ل ‪ ، end‬فقد نفدت عنارص البحث‪.‬‬ ‫•‬
‫في هذه الحالة يفشل الرشط ونخرج من ‪.while‬‬ ‫•‬
‫خالف ذلك ‪ ،‬يشري ‪ mid‬إلى عنرص ما وسنتحقق ما إذا كان ‪ mid‬يشري إلى العنرص الذي نريده‪.‬‬ ‫•‬
‫إذا كان كذلك ‪ ،‬فقد انتهينا ونخرج من الحلقة‪.‬‬ ‫•‬

‫إن كان ال يزال لدينا عنارص يجب معالجتها ‪ ،‬فإن الكود املوجود داخل ‪ while‬يضبط النطاق عن طريق تحريك‬
‫‪ end‬أو ‪.beg‬‬
‫• إذا كان العنرص املشار إليه ب ‪ mid‬أكرب من ‪ ، sought‬فنحن نعلم أنه إذا كان ‪ sought‬في ‪، text‬‬
‫فسيظهر قبل العنرص الذي يُشار إليه ب ‪.mid‬‬
‫• لذلك ‪ ،‬يمكننا تجاهل العنارص بعد ‪ ، mid‬وهو ما نقوم به عن طريق ضبط ‪ mid‬على ‪.end‬‬
‫• إذا كانت ‪ *mid‬أصغر من ‪ ، sought‬فيجب أن يكون العنرص في نطاق العنارص بعد العنرص املشار إليه‬
‫ب ‪.mid‬‬
‫• في هذه الحالة ‪ ،‬نقوم بضبط النطاق بجعل ‪ beg‬يشري إلى العنرص بعد ‪ mid‬مبارشة‪.‬‬
‫نحن نعلم بالفعل أن ‪ mid‬ليس هو الذي نريده ‪ ،‬لذا يمكننا استبعاده من النطاق‪.‬‬ ‫•‬
‫مساويا ل ‪ end‬أو سيشري إلى العنرص الذي نبحث عنه‪.‬‬
‫ً‬ ‫في نهاية ‪ ، while‬سيكون ‪mid‬‬ ‫•‬
‫إذا كان ‪ mid‬يساوي ‪ ، end‬فهذا يعين أن العنرص لم يكن في ‪.text‬‬ ‫•‬

‫قسم التدريبات ‪3.4.2‬‬


‫التمرين ‪:3.24‬‬
‫أعد التمرين األخري من الفقرة ‪ 3.3.3‬باستخدام املكررات‪.‬‬
‫التمرين ‪:3.25‬‬
‫أعد كتابة برنامج تجميع الدرجات من الفقرة ‪ 3.3.3‬باستخدام املكررات بدال ً من الفهرسة‪.‬‬
‫التمرين ‪:3.26‬‬
‫في برنامج البحث الثنائي ‪ ،‬ملاذا كتبنا ؟‬
‫;‪mid = beg + (end - beg) / 2‬‬
‫بدال ً من ‪:‬‬
‫? ;‪mid = (beg + end) / 2‬‬
‫‪ . 3٫5‬املصفوفــــــات‬
‫‪3.5. Arrays‬‬

‫املصفوفة هي بنية بيانات مشابهة لنوع مكتبة املتجهات لكنها تقدم تباينا مختلفا في األداء واملرونة‪.‬‬
‫فهي كاملتجه عبارة عن حاوية لكائنات غري مسماة من نوع واحد يمكن الوصول إليها عن طريق الفهرسة‪.‬‬ ‫•‬
‫لكنها على عكس املتجه ‪ ،‬ذات حجم ثابت ؛ إذ ال يمكننا إضافة عنارص إليها‪.‬‬ ‫•‬
‫ونظرًا ألن حجمها ثابت ‪ ،‬فإنها تقدم أحيا ًنا أداء أفضل وقت التشغيل للتطبيقات املتخصصة‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬تأتي مزية وقت التشغيل تلك على حساب املرونة املفقودة‪.‬‬ ‫•‬

‫تلميح‬

‫إن كنت ال تعرف بالضبط عدد العنارص اليت تحتاجها ‪ ،‬فاستخدم املتجه‪.‬‬

‫‪ 3٫5٫1‬تعريف املصفوفات املدمجة وتهيئتها‬


‫‪3.5.1. Defining and Initializing Built-in Arrays‬‬

‫املصفوفات نوع مركب‪.‬‬


‫إعالن املصفوفة يكون على شكل [‪ ، a]d‬حيث ‪ a‬اسم يتم تعريفه و ‪ d‬هو بُعد وحجم املصفوفة‪.‬‬ ‫•‬
‫يحدد البعد عدد العنارص ويجب أن يكون أكرب من الصفر‪.‬‬ ‫•‬
‫عدد العنارص في املصفوفة هو جزء من نوعها‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬يجب أن يكون البعد معرو ًفا وقت الرتجمة ‪،‬‬ ‫•‬

‫ما يعين أن الحجم يجب أن يكون تعبريًا ثاب ًتا ‪:‬‬

‫;‪unsigned cnt = 42‬‬ ‫‪// not constant expression‬‬


‫;‪constexpr unsigned sz = 42‬‬ ‫‪// constant expression‬‬
‫;]‪int arr [10‬‬ ‫‪// array of ten ints‬‬
‫;]‪int *parr[sz‬‬ ‫‪// array of 42 pointers to int‬‬
‫;]‪string bad[cnt‬‬ ‫‪// error: cnt not constant expression‬‬
‫‪string strs[get_size()]; // ok if get_size is constexpr, error otherwise‬‬

‫افرتاضياً ‪ ،‬عنارص املصفوفة تهيأ بالتهيئة االفرتاضية‪.‬‬

‫تحذير‬

‫افرتاضيا من نوع مدمج تم تعريفها داخل دالة ستحمل‬


‫ً‬ ‫مثل متغريات النوع املدمج ‪ ،‬فإن مصفوفة ُمهيأة‬
‫قيما غري معرفة‪.‬‬

‫عندما نعرف مصفوفة ‪ ،‬يجب أن نحدد نوعها‪.‬‬ ‫•‬


‫فال يمكننا استخدام ‪ auto‬الستنتاج نوعها من تهيئة القائمة‪.‬‬ ‫•‬
‫تحتوي املصفوفات على كائنات كاملتجهات وبالتالي ‪ ،‬ال توجد مصفوفات من املراجع‪.‬‬ ‫•‬
‫‪Explicitly Initializing Array Elements‬‬ ‫تهيئة عنارص مصفوفة بشكل رصيح‬

‫يمكننا تهيئة عنارص املصفوفة تهيئة قائمة‪ .‬وعندما نقوم بذلك ‪ ،‬يمكننا حذف الحجم‪.‬‬
‫إذا حذفنا الحجم ‪ ،‬فإن املرتجم يستنتجها من عدد العنارص املهيئة‪.‬‬ ‫•‬
‫أما إذا حددنا حجماً ‪ ،‬فيجب أال يتجاوز عدد العنارص املهيئة الحجم املحدد‪.‬‬ ‫•‬
‫فإذا كان الحجم أكرب من عدد املهيئات ‪ ،‬يتم استخدام املهئي ألولى العنارص ويتم تهيئة أي عنارص متبقية ‪:‬‬ ‫•‬

‫;‪const unsigned sz = 3‬‬


‫;}‪int ia1[sz] = {0,1,2‬‬ ‫‪// array of three ints with values 0, 1, 2‬‬
‫;}‪int a2[] = {0, 1, 2‬‬ ‫‪// an array of dimension 3‬‬
‫;}‪int a3[5] = {0, 1, 2‬‬ ‫}‪// equivalent to a3[] = {0, 1, 2, 0, 0‬‬
‫;}‪int a5[2] = {0,1,2‬‬ ‫‪// error: too many initializers‬‬
‫;}"‪string a4[3] = {"hi", "bye‬‬ ‫}"" ‪// same as a4[] = {"hi", "bye",‬‬

‫‪Character Arrays Are Special‬‬ ‫مصفوفات الحرف خاصة‬

‫تحتوي مصفوفات األحرف على نموذج تهيئة إضافي ‪:‬‬


‫حيث يمكننا تهيئة تلك املصفوفات على حرفية سلسلة‪.‬‬ ‫•‬
‫عندما نستخدم هذا النموذج من التهيئة ‪ ،‬من املهم أن نتذكر أن حرفية السلسلة تنتهي بحرف خال‪.‬‬ ‫•‬
‫يتم نسخ هذا الحرف الخالي إلى داخل املصفوفة مع األحرف املوجودة في القيم ‪:‬‬ ‫•‬

‫;}'‪char a1[] = {'C', '+', '+‬‬ ‫‪// list initialization, no null‬‬


‫;}'‪char a2[] = {'C', '+', '+', '\0‬‬ ‫‪// list initialization,explicit null‬‬
‫;"‪char a3[] = "C++‬‬ ‫‪// null terminator added automatically‬‬
‫;"‪const char a4[6] = "Daniel‬‬ ‫!‪// error: no space for the null‬‬

‫إن أبعاد ‪ a1‬هي ثالث خانات ؛ بينما أبعاد ‪ a2‬و ‪ a3‬كالهما أربع خانات‪.‬‬
‫يعد تعريف ‪ a4‬خاطئاً‪ .‬فعلى الرغم من أن القيم الحرفية تحتوي فقط على ستة أحرف رصيحة ‪،‬‬ ‫•‬
‫يجب أن يكون حجم املصفوفة سبعة خانات على األقل ‪ -‬ستة لالحتفاظ بالحرفية وخانة للصفر‪.‬‬ ‫•‬

‫‪No Copy or Assignment‬‬ ‫ﻻ نسخ وﻻ تعيني‬

‫ال يمكننا تهيئة مصفوفة كنسخة من مصفوفة أخرى ‪ ،‬وال يجوز تعيني مصفوفة على أخرى‪:‬‬

‫;}‪int a[] = {0, 1, 2‬‬ ‫‪// array of three ints‬‬


‫;‪int a2[] = a‬‬ ‫‪// error: cannot initialize one array with another‬‬
‫;‪a2 = a‬‬ ‫‪// error: cannot assign one array to another‬‬

‫تحذير‬
‫قد تسمح بعض املرتجمات بتعيني املصفوفة كامتداد للمرتجم‪.‬‬ ‫•‬
‫من الجيد عاد ًة تجنب استخدام مزيات غري قياسية‪.‬‬ ‫•‬
‫فلن تعمل الربامج اليت تستخدم مثل هذه املزيات مع مرتجم مختلف‪.‬‬ ‫•‬
‫‪Understanding Complicated Array Declarations‬‬ ‫فهم إعالنات املصفوفة املعقدة‬

‫يمكن أن تحتوي املصفوفات على كائنات أي نوع كاملتجهات‪.‬‬


‫فعلى سبيل املثال ‪ ،‬يمكن أن يكون لدينا مصفوفة من املؤرشات‪.‬‬ ‫•‬
‫وألن املصفوفة عبارة عن كائن ‪ ،‬فبإمكاننا تعريف مؤرشات ومراجع ملصفوفات‪.‬‬ ‫•‬
‫بسيطا إلى حد ما ‪،‬‬
‫ً‬ ‫يعد تعريف املصفوفات اليت تحتوي على املؤرشات أمرًا‬ ‫•‬
‫لكن يعترب تعريف مؤرش أو مرجع ملصفوفة أكرث تعقي ًدا ‪:‬‬ ‫•‬

‫;]‪int *ptrs[10‬‬ ‫‪//ptrs array of ten pointers to int‬‬


‫;‪int &refs[10] = /* ? */‬‬ ‫‪//error: no arrays of references‬‬
‫;‪int (*Parray)[10] = &arr‬‬ ‫‪//Parray points to array of ten ints‬‬
‫;‪int (&arrRef)[10] = arr‬‬ ‫‪//arrRef refers to array of ten ints‬‬

‫افرتاضياً ‪ ،‬تربط ِّ‬


‫معدالت النوع اليمني إلى اليسار‪ .‬فقراءة تعريف ‪ ptrs‬من اليمني إلى اليسار أمر سهل‪:‬‬ ‫•‬
‫نرى أننا نقوم بتعريف مصفوفة بحجم ‪ ، 10‬تسمى ‪، ptrs‬‬ ‫•‬
‫وهي تحمل مؤرشات لعدد صحيح ‪.int‬‬ ‫•‬
‫أما قراءة تعريف ‪ Parray‬من اليمني إلى اليسار ليست مفيدة‪.‬‬ ‫•‬
‫فنظرًا ألن بُعد املصفوفة يتبع االسم الذي تم اإلعالن عنه ‪،‬‬ ‫•‬
‫فقد يكون من األسهل قراءة اإلعالن من الداخل إلى الخارج بدال ً من قراءته من اليمني إلى اليسار‪.‬‬ ‫•‬
‫القراءة من الداخل إلى الخارج تجعل فهم نوع ‪ Parray‬أسهل بكثري‪.‬‬ ‫•‬
‫نبدأ بمالحظة أن األقواس حول ‪ *Parray‬تعين أن ‪ Parray‬عبارة عن مؤرش‪.‬‬ ‫•‬
‫بالنظر إلى اليمني ‪ ،‬نرى أن ‪ Parray‬تشري إلى مصفوفة بحجم ‪.10‬‬ ‫•‬
‫بالنظر إلى اليسار ‪ ،‬نرى أن العنارص في تلك املصفوفة هي أعداد صحيحة‪.‬‬ ‫•‬
‫بالتالي ‪ ،‬فإن ‪ Parray‬مؤرش ملصفوفة من ‪ ١٠‬أعداد صحيحة‪.‬‬ ‫•‬
‫وباملثل ‪ ،‬يخرب (‪ )&arrRef‬أن ‪ arrRef‬مرجع‪.‬‬ ‫•‬
‫فالنوع الذي تشري إليه هو مصفوفة بحجم ‪.10‬‬ ‫•‬
‫تحتوي هذه املصفوفة على عنارص نوع عدد صحيح‪.‬‬ ‫•‬
‫بالطبع ‪ ،‬ال توجد قيود على عدد ُم ِّ‬
‫عدالت النوع اليت يمكن استخدامها‪:‬‬

‫‪int *(&arry)[10] = ptrs; // arry is reference to array of ten pointers‬‬

‫عند قراءة هذا اإلعالن من الداخل إلى الخارج ‪ ،‬نرى أن ‪ arry‬عبارة عن مرجع‪.‬‬ ‫•‬
‫بالنظر إلى اليمني ‪ ،‬نرى أن الكائن الذي يشري إليه ‪ arry‬هو مصفوفة بحجم ‪.10‬‬ ‫•‬
‫بالنظر إلى اليسار ‪ ،‬نرى أن نوع العنرص هو مؤرش لعدد صحيح ‪.‬‬ ‫•‬
‫وبالتالي ‪ ،‬فإن ‪ arry‬هي مرجع ملصفوفة من عرشة مؤرشات‪.‬‬ ‫•‬

‫تلميح‬

‫قد يكون من األسهل لفهم تعريفات املصفوفة البدء باسم املصفوفة وقراءتها من الداخل إلى الخارج‪.‬‬
‫تمارين القسم ‪3.5.1‬‬
‫التمرين ‪:3.27‬‬
‫‪ -‬بافرتاض أن ‪ txt_size‬دالة ال تأخذ أي وسيطات وتقوم بإعادة قيمة عدد صحيح ‪،‬‬
‫‪ -‬فأي التعريفات التالية تعترب غري رشعية؟ ارشح السبب‪.‬‬
‫;‪unsigned buf_size = 1024‬‬
‫;]‪(a) int ia[buf_size‬‬
‫;]‪(b) int ia[4 * 7 - 14‬‬
‫;])(‪(c) int ia[txt_size‬‬
‫;"‪(d) char st[11] = "fundamental‬‬
‫تمرين ‪:3.28‬‬
‫ما هي القيم املوجودة في املصفوفات التالية؟‬
‫;]‪string sa[10‬‬
‫;]‪int ia[10‬‬
‫{ )(‪int main‬‬
‫;]‪string sa2[10‬‬
‫;]‪int ia2[10‬‬
‫}‬
‫التمرين ‪:3.29‬‬
‫ضع قائمة ببعض عيوب استخدام املصفوفات بدال ً من املتجهات‪.‬‬

‫‪3.5.2. Accessing the Elements of an Array‬‬ ‫‪ 3٫5٫2‬الوصول لعنارص مصفوفة‬

‫مثل أنواع مكتبة املتجه والسالسل ‪،‬‬


‫يمكننا استخدام ‪ for‬النطاق أو عامل الفهرسة للوصول لعنارص مصفوفة‪.‬‬ ‫•‬
‫كالعادة‪ ،‬تبدأ الفهرسة من ‪.0‬‬ ‫•‬
‫فبالنسبة ملصفوفة من عرشة عنارص ‪ ،‬تكون الفهرسة من ‪ 0‬إلى ‪ ، 9‬وليس من ‪ 1‬إلى ‪.10‬‬ ‫•‬

‫عندما نستخدم متغريًا لفهرسة مصفوفة ‪،‬‬


‫ينبغي علينا عاد ًة تعريف هذا املتغري ليكون من النوع ‪.size_t‬‬ ‫•‬
‫فالنوع ‪ size_t‬هو نوع بال_إشارة خاص بالجهاز وتم ضمان حجمه ليكون كبريًا بما يكفي الحتواء حجم‬ ‫•‬
‫أي كائن في الذاكرة‪.‬‬
‫يتم تعريف نوع ‪ size_t‬في العنوان ‪ cstddef‬وهو إصدار يس‪ ++‬لعنوان ‪ stddef.h‬من مكتبة يس‪.‬‬ ‫•‬

‫باستثناء أن املصفوفات ذات حجم ثابت ‪ ،‬فإننا نستخدمها بطرق مشابهة لكيفية استخدامنا للمتجهات‪.‬‬
‫فمثالً ‪ ،‬يمكن إعادة تنفيذ برنامجنا للتقدير من فقرة ‪ 3٫3٫3‬الستخدام مصفوفة لتثبيت عدادات املجموعة ‪:‬‬

‫…‪// count number of grades by clusters of ten: 0-9, 10-19,‬‬ ‫‪90-99, 100‬‬
‫‪unsigned scores[11] = {}; // 11 buckets, all value initialized to 0‬‬
‫;‪unsigned grade‬‬
‫{ )‪while (cin >> grade‬‬
‫)‪if (grade <= 100‬‬
‫‪++scores[grade/10]; // increment counter for current cluster‬‬
‫}‬

‫االختالف الوحيد الواضح بني هذا الربنامج والربنامج املوجود في الصفحة ‪ 104‬هو إعالن ‪.scores‬‬ ‫•‬
‫فإعالن ‪ scores‬في هذا الربنامج عبارة عن مصفوفة من ‪ 11‬عنرصًا بال_إشارة‪.‬‬ ‫•‬
‫االختالف غري الواضح هو أن عامل الفهرسة في هذا الربنامج هو الذي تم تعريفه كجزء من اللغة‪.‬‬ ‫•‬
‫يمكن استخدام هذا العامل في معامالت نوع املصفوفة‪.‬‬ ‫•‬
‫تم تعريف عامل الفهرسة املستخدم في الربنامج في الصفحة ‪ 104‬بواسطة قالب مكتبة املتجه وينطبق على‬ ‫•‬
‫معامالت نوع متجه‪.‬‬
‫كما في حالة السلسلة أو املتجه ‪ ،‬من األفضل استخدام ‪ for‬النطاق عندما نريد عبور املصفوفة بأكملها‪.‬‬
‫على سبيل املثال ‪ ،‬يمكننا طباعة ‪ scores‬الناتجة على النحو التالي ‪:‬‬

‫)‪for (auto i : scores‬‬ ‫‪// for each counter in scores‬‬


‫;" " << ‪cout << i‬‬ ‫‪// print value of that counter‬‬
‫;‪cout << endl‬‬

‫يعرف النظام عدد العنارص في ‪ .scores‬فاستخدام ‪ for‬النطاق يعين أنه‬


‫ونظرًا ألن البعد جزء من نوع املصفوفة ‪ّ ،‬‬
‫ليس علينا إدارة العبور بأنفسنا‪.‬‬

‫‪Checking Subscript Values‬‬ ‫فحص قيم الفهرسة‬

‫مثل السلسلة واملتجه ‪ ،‬فإن األمر مرتوك للمربمج للتأكد من أن قيمة الفهرسة ضمن النطاق ‪-‬‬
‫أي أن قيمة الفهرس تساوي الصفر أو أكرب من الصفر وأقل من حجم املصفوفة‪.‬‬ ‫•‬
‫ال يشء يمنع الربنامج من تخطي حدود املصفوفة باستثناء االهتمام الدقيق بالتفاصيل واالختبار الشامل‬ ‫•‬
‫للكود‪.‬‬
‫من املمكن أن يتم تجميع الربامج وتنفيذها لكنها ستظل في خطأ قاتل‪.‬‬ ‫•‬

‫تحذير‬
‫شيوعا ملشاكل األمان هو أخطاء تجاوز سعة املخزن املؤقت‪.‬‬
‫ً‬ ‫املصدر األكرث‬ ‫•‬
‫تحدث مثل تلك األخطاء عندما يفشل برنامج فحص الفهرسة ‪،‬‬ ‫•‬
‫ويستخدم ذاكرة عن طريق الخطأ خارج نطاق مصفوفة أو هيكل بيانات مماثل‪.‬‬ ‫•‬

‫تمارين القسم ‪3.5.2‬‬


‫التمرين ‪:3.30‬‬
‫تعرف على أخطاء الفهرسة في الكود التالي‪:‬‬
‫;‪constexpr size_t array_size = 10‬‬
‫;]‪int ia[array_size‬‬
‫)‪for (size_t ix = 1; ix <= array_size; ++ix‬‬
‫;‪ia[ix] = ix‬‬
‫التمرين ‪:3.31‬‬
‫برنامجا لتعريف مصفوفة من عرشة أعداد صحيحة ‪.ints‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬امنح كل عنرص نفس القيمة مثل موضعه في املصفوفة‪.‬‬
‫التمرين ‪:3.32‬‬
‫‪ -‬انسخ املصفوفة اليت عرفتها في التمرين السابق إلى مصفوفة أخرى‪.‬‬
‫‪ -‬أعد كتابة الربنامج الخاص بك الستخدام املتجهات‪.‬‬
‫التمرين ‪:3.33‬‬
‫ماذا سيحدث إذا لم نقم بتهيئة مصفوفة ‪ scores‬في الربنامج في الصفحة ‪116‬؟‬
‫‪3.5.3. Pointers and Arrays‬‬ ‫‪ 3٫5٫2‬املؤرشات واملصفوفات‬

‫تتشابك املؤرشات واملصفوفات بشكل وثيق في يس‪.++‬‬


‫باﻷخص عندما نستخدم مصفوفة كما سرنى ‪ ،‬حيث يقوم املرتجم عاد ًة بتحويلها إلى مؤرش‪.‬‬ ‫•‬
‫في العادة ‪ ،‬نحصل على مؤرش لكائن باستخدام عامل العنوان‪.‬‬ ‫•‬
‫بشكل عام ‪ ،‬يمكن تطبيق عامل العنوان على أي كائن‪.‬‬ ‫•‬
‫العنارص في املصفوفة عبارة عن كائنات‪.‬‬ ‫•‬
‫فعندما نقوم بفهرسة مصفوفة ‪ ،‬تكون النتيجة هي كائن في ذلك املوقع في املصفوفة‪.‬‬ ‫•‬

‫وكحال أي كائن آخر ‪ ،‬يمكننا الحصول على مؤرش لعنرص مصفوفة من خالل أخذ عنوان هذا العنرص‪:‬‬

‫;}"‪string nums[] = {"one", "two", "three‬‬ ‫‪// array of strings‬‬


‫;]‪string *p = &nums[0‬‬ ‫‪// p points to first element in nums‬‬

‫تلقائيا‬
‫ً‬ ‫مع ذلك ‪ ،‬فإن املصفوفات لها خاصية خاصة ‪ -‬ففي معظم األماكن عندما نستخدم مصفوفة ‪ ،‬يقوم املرتجم‬
‫باستبدالها بمؤرش ألول عنرص ‪:‬‬

‫;‪string *p2 = nums‬‬ ‫]‪// equivalent to p2 = &nums[0‬‬

‫ملحوظة‬

‫في معظم التعبريات ‪ ،‬عندما نستخدم كائن نوع مصفوفة ‪ ،‬فإننا نستخدم مؤرشًا ﻷول عنرص في تلك املصفوفة‪.‬‬

‫هناك العديد من اآلثار املرتتبة على حقيقة أن العمليات على املصفوفات في الغالب تكون عمليات على مؤرشات‪.‬‬
‫معرف بـ ‪ auto‬فإن النوع املستنتج‬
‫كمهئي ملتغري ّ‬
‫• أحد تلك اآلثار الضمنية هو أنه عندما نستخدم مصفوفة ُ‬
‫سيكون مؤرشاً ‪ ،‬وليس مصفوفة ‪:‬‬

‫‪int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia array of ten ints‬‬


‫‪auto ia2(ia); // ia2 int* that points to first element in ia‬‬
‫;‪ia2 = 42‬‬ ‫‪// error: ia2 pointer, and we can't assign int to pointer‬‬

‫كمهئي ‪ ،‬فإن املرتجم يتعامل مع‬


‫وعلى الرغم من أن ‪ ia‬عبارة عن مصفوفة من عرشة ‪ ، ints‬فعندما نستخدم ‪ُ ia‬‬
‫هذه التهيئة كما لو كنا قد كتبنا‬

‫;)]‪auto ia2(&ia[0‬‬ ‫*‪// now it's clear that ia2 has type int‬‬

‫جدير بالذكر أن هذا التحويل ال يحدث عندما نستخدم النوع‪-‬املعلن ‪. decltype‬‬


‫فالنوع املعاد بواسطة النوع‪-‬املعلن لـ (‪ )ia‬سيكون مصفوفة من عرشة ‪: ints‬‬

‫‪// ia3 is an array of ten ints‬‬


‫;}‪decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9‬‬
‫;‪ia3 = p‬‬ ‫‪// error: can't assign int* to array‬‬
‫;‪ia3[4] = i‬‬ ‫‪// ok: assigns value of i to element in ia3‬‬

‫‪Pointers Are Iterators‬‬ ‫املؤرشات عبارة عن مكررات‬


‫املؤرشات اليت تتناول عنارص في مصفوفة تملك عمليات إضافية تتجاوز تلك اليت وصفناها في الفقرة ‪.2.3.2‬‬
‫باﻷخص ‪ ،‬تدعم مؤرشات عنارص مصفوفة نفس العمليات اليت تدعمها املكررات على متجهات أو سالسل‪.‬‬ ‫•‬

‫على سبيل املثال ‪ ،‬يمكننا استخدام عامل الزتايد لالنتقال من عنرص مصفوفة إلى التالي‪:‬‬

‫;}‪int arr[] = {0,1,2,3,4,5,6,7,8,9‬‬


‫;‪int *p = arr‬‬ ‫‪// p points to first element in arr‬‬
‫;‪++p‬‬ ‫]‪// p points to arr[1‬‬

‫ومثلما يمكننا استخدام املكررات الجتياز عنارص في متجه ‪ ،‬يمكننا استخدام املؤرشات الجتياز عنارص في مصفوفة‪.‬‬
‫لكن بالطبع للقيام بذلك ‪ ،‬نحتاج إلى الحصول على مؤرشات ﻷول عنرص والعنرص ما بعد األخري‪.‬‬ ‫•‬
‫كما رأينا للتو ‪ ،‬يمكننا الحصول على مؤرش ﻷول عنرص باستخدام املصفوفة نفسها أو من عنوان أول عنرص‪.‬‬ ‫•‬
‫ويمكننا الحصول على مؤرش ما بعد‪-‬النهاية باستخدام خاصية خاصة أخرى للمصفوفات‪.‬‬ ‫•‬

‫يمكننا أن نأخذ عنوان ما بعد آخر عنرص غري موجود في املصفوفة ‪:‬‬

‫;]‪int *e = &arr[10‬‬ ‫‪// pointer just past last element in arr‬‬

‫استخدمنا هنا عامل الفهرسة لفهرسة عنرص غري موجود ؛‬ ‫•‬


‫تحتوي ‪ arr‬على عرشة عنارص ‪ ،‬لذا فآخر عنرص في ‪ arr‬يقع في موضع الفهرس ‪.9‬‬ ‫•‬
‫اليشء الوحيد الذي يمكننا فعله بهذا العنرص هو أخذ عنوانه ‪ ،‬وهو ما نفعله لتهيئة ‪.e‬‬ ‫•‬
‫ومثل مكرر ما بعد النهاية‪ ،‬ال يشري مؤرش ما بعد آخر عنرص إلى عنرص‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬ال يجوز لنا إلغاء مرجع أو تزايد مؤرش لـما بعد آخر عنرص‪.‬‬ ‫•‬

‫باستخدام هذه املؤرشات ‪ ،‬يمكننا كتابة حلقة لطباعة العنارص في ‪ arr‬على النحو التالي‪:‬‬

‫)‪for (int *b = arr; b != e; ++b‬‬


‫‪cout << *b << endl; // print elements in arr‬‬

‫‪The Library begin and end Functions‬‬ ‫دوال مكتبة بداية ونهاية‬

‫على الرغم من أنه يمكننا حوسبة مؤرش ما بعد نهاية العنارص ‪ ،‬إال أن فعل ذلك قد يكون عرضة للخطأ‪.‬‬
‫لجعل استخدام املؤرشات أسهل وأكرث أما ًنا ‪،‬‬ ‫•‬
‫تشتمل املكتبة الجديدة على دالتني ‪ ،‬يطلق عليهما بداية ‪ begin‬ونهاية ‪.end‬‬ ‫•‬
‫تعمل هاتان الدالتان مثل أعضاء الحاوية املسماة بشكل مشابه‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬وﻷن املصفوفات ليست أنواع فئات ‪ ،‬فال تعترب هاتان دالتا أعضاء‪.‬‬ ‫•‬

‫بدال ً من ذلك ‪ ،‬تأخذ وسيطة تكون عبارة عن مصفوفة ‪:‬‬

‫‪int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia array of ten ints‬‬


‫;)‪int *beg = begin(ia‬‬ ‫‪//pointer to first element in ia‬‬
‫;)‪int *last = end(ia‬‬ ‫‪//pointer one past last element in ia‬‬

‫حيث تعيد بداية مؤرشًا ﻷول عنرص ‪ ،‬وتعيد نهاية مؤرشًا ملا بعد آخر عنرص في املصفوفة املحددة‪:‬‬
‫يتم تعريف هذه الدوال في العنوان ‪.iterator‬‬ ‫•‬
‫وباستخدام بداية ونهاية ‪ ،‬من السهل كتابة حلقة ملعالجة العنارص في مصفوفة‪.‬‬ ‫•‬

‫فمثالً ‪ ،‬وبافرتاض أن ‪ arr‬عبارة عن مصفوفة تحتوي على قيم عدد صحيح ‪ ،‬فقد نجد أول قيمة سالبة في ‪: arr‬‬

‫‪//pbeg points to first & pend points just past last element in arr‬‬
‫;)‪int *pbeg = begin(arr), *pend = end(arr‬‬
‫‪// find first negative element, stopping if we've seen all elements‬‬
‫)‪while (pbeg != pend && *pbeg >= 0‬‬
‫;‪++pbeg‬‬

‫نبدأ بتعريف مؤرشي عدد صحيح هما ‪ pbeg‬و ‪.pend‬‬ ‫•‬


‫نضع ‪ pbeg‬لتشري ﻷول عنرص و ‪ pend‬لتشري ملا بعد آخر عنرص في ‪.arr‬‬ ‫•‬
‫يستخدم رشط ‪ while‬املتغري ‪ pend‬ملعرفة ما إذا كان من اآلمن إلغاء مرجع عن ‪.pbeg‬‬ ‫•‬
‫سالبا‪.‬‬
‫ً‬ ‫إن كانت ‪ pbeg‬تشري إلى عنرص ما ‪ ،‬فإننا نلغي املرجع ونتحقق ما إذا كان العنرص األسايس‬ ‫•‬
‫إن كان األمر كذلك ‪ ،‬يفشل الرشط ونخرج من الحلقة‪.‬‬ ‫•‬
‫إن لم يكن األمر كذلك ‪ ،‬فإننا نزيد املؤرش لننظر إلى العنرص التالي‪.‬‬ ‫•‬

‫ملحوظة‬

‫يترصف مؤرش "ما بعد" نهاية املصفوفة املدمجة بنفس طريقة املكرر املعاد بعملية نهاية املتجه‪.‬‬
‫باﻷخص ‪ ،‬ال يجوز لنا إلغاء مرجع أو تزايد مؤرش ما بعد النهاية‪.‬‬

‫‪Pointer Arithmetic‬‬ ‫حسابيات املؤرش‬

‫يمكن للمؤرشات اليت تعالج عنارص مصفوفة استخدام جميع عمليات املكرر املدرجة في الجدول ‪ 3.6‬والجدول ‪. 3.7‬‬
‫• هذه العمليات ‪ -‬كالتعيني ‪ ،‬الزتايد ‪ ،‬املقارنات ‪ ،‬إضافة قيمة عدد صحيح ‪ ،‬طرح مؤرشين ‪ -‬لها نفس املعىن‬
‫عند تطبيقها على مؤرشات تشري لعنارص مصفوفة مدمجة مثل تطبيقها على مكررات‪.‬‬
‫• عندما نضيف (أو نطرح) قيمة صحيحة إلى (أو من) مؤرش ‪ ،‬تكون النتيجة مؤرشًا جدي ًدا‪.‬‬
‫يشري هذا املؤرش الجديد إلى العنرص الذي يتقدم (أو يسبق) املؤرش األصلي‪:‬‬ ‫•‬

‫;‪constexpr size_t sz = 5‬‬


‫;}‪int arr[sz] = {1,2,3,4,5‬‬
‫;‪int *ip = arr‬‬ ‫]‪// equivalent to int *ip = &arr[0‬‬
‫;‪int *ip2 = ip + 4‬‬ ‫‪// ip2 points to arr[4], last element in arr‬‬

‫بدءا من عنرص ‪ ip‬الحالي‪.‬‬


‫نتيجة إضافة ‪ 4‬إلى ‪ ip‬هي مؤرش يشري ملا بعد العنرص الرابع في املصفوفة ً‬
‫يجب أن تكون نتيجة إضافة قيمة صحيحة إلى مؤرش مؤرشًا لعنرص في نفس املصفوفة ‪ ،‬أو ملا بعد نهايتها مبارش ًة ‪:‬‬

‫;‪// ok: arr converted to pointer to its first element‬‬


‫‪// p points one past end of arr‬‬
‫;‪int *p = arr + sz‬‬ ‫!‪// use caution -- do not dereference‬‬
‫;‪// error: arr has only 5 elements‬‬
‫;‪int *p2 = arr + 10‬‬
‫‪// p2 has undefined value‬‬

‫عندما نضيف ‪ sz‬إلى ‪ ، arr‬يحول املرتجم ‪ arr‬إلى مؤرش ﻷول عنرص في ‪.arr‬‬ ‫•‬
‫عندما نضيف ‪ sz‬إلى هذا املؤرش ‪ ،‬نحصل على مؤرش يشري ملواضع ‪( sz‬أي ‪ 5‬مواضع) بعد األول‪.‬‬ ‫•‬
‫أي أنه يشري مرة واحدة إلى آخر عنرص في ‪.arr‬‬ ‫•‬
‫حوسبة مؤرش ألكرث من عنرص واحد ملا بعد آخر عنرص خطأ ‪،‬‬ ‫•‬
‫على الرغم من أنه من غري املرجح أن يكتشف املرتجم مثل هذه األخطاء‪.‬‬ ‫•‬

‫كحال املكررات ‪ ،‬فإن طرح مؤرشين يعطينا املسافة بينهما‪ .‬يجب أن تشري املؤرشات لعنارص في نفس املصفوفة ‪:‬‬

‫‪auto n = end(arr) - begin(arr); // n is 5, number of elements in arr‬‬

‫نتيجة طرح مؤرشين هو نوع مكتبة يسمى ‪.ptrdiff_t‬‬ ‫•‬


‫النوع ‪ ptrdiff_t‬شبيه بـ ‪ size_t‬عبارة عن نوع خاص بالجهاز يتم تعريفه في العنوان ‪.cstddef‬‬ ‫•‬
‫ونظرًا ألن الطرح قد ينتج عنه مسافة سالبة ‪ ،‬فإن ‪ ptrdiff_t‬هو نوع صحيح ذو إشارة‪.‬‬ ‫•‬

‫يمكننا استخدام العوامل العالئقية ملقارنة املؤرشات اليت تشري لعنارص من مصفوفة ‪ ،‬أو ملا بعد آخر عنرص فيها‪.‬‬
‫على سبيل املثال ‪ ،‬يمكننا عبور العنارص في ‪ arr‬على النحو التالي‪:‬‬

‫;‪int *b = arr, *e = arr + sz‬‬


‫{ )‪while (b < e‬‬
‫‪// use *b‬‬
‫;‪++b‬‬
‫}‬

‫ال يمكننا استخدام العوامل العالئقية على مؤرشات تشري لكائنني غري مرتبطني‪:‬‬

‫;‪int i = 0, sz = 42‬‬
‫;‪int *p = &i, *e = &sz‬‬
‫!‪// undefined: p and e unrelated; comparison meaningless‬‬
‫)‪while (p < e‬‬

‫على الرغم من أن حساب املؤرش قد يكون غامضا في هذه املرحلة ‪،‬‬ ‫•‬
‫أيضا للمؤرشات الفارغة وللمؤرشات اليت تشري لكائن ليس مصفوفة‪.‬‬
‫فمن الجدير بالذكر أن األداة صالحة ً‬ ‫•‬
‫في الحالة األخرية ‪ ،‬يجب أن تشري املؤرشات لنفس الكائن ‪ ،‬أو ما بعد ذلك الكائن‪.‬‬ ‫•‬
‫فارغا ‪ ،‬فيمكننا إضافة أو طرح تعبري ثابت ال يتجزأ تكون قيمته من ‪ 0‬إلى ‪.p‬‬
‫ً‬ ‫إذا كان ‪ p‬مؤرشًا‬ ‫•‬
‫أيضا طرح مؤرشين فارغني من بعضهما ‪ ،‬وفي هذه الحالة تكون النتيجة ‪.0‬‬
‫يمكننا ً‬ ‫•‬

‫التفاعل بني إزالة املرجع وحسابية املؤرش‬


‫‪Interaction between Dereference and Pointer Arithmetic‬‬

‫إن نتيجة إضافة قيمة صحيحة إلى مؤرش هو في حد ذاته مؤرش‪.‬‬


‫ولكن بافرتاض أن املؤرش الناتج يشري إلى عنرص ‪ ،‬يمكننا إلغاء املرجع عن املؤرش الناتج‪:‬‬
‫;}‪int ia[] = {0,2,4,6,8‬‬ ‫‪//array with 5 elements of type int‬‬
‫;)‪int last = *(ia + 4‬‬ ‫]‪//ok: initializes last to 8, value of ia[4‬‬

‫يقوم التعبري (‪ *)ia + 4‬بحساب عنوان أربعة عنارص بعد ‪ ia‬ويلغي املرجع عن املؤرش الناتج‪.‬‬
‫هذا التعبري يعادل كتابة ]‪.ia[4‬‬ ‫•‬
‫تذكر أنه الحظنا سابقاً أن األقواس مطلوبة في تعبريات تحتوي على عوامل مرجع وعوامل نقطة‪.‬‬ ‫•‬
‫وباملثل ‪ ،‬تعترب األقواس حول إضافة املؤرش رضورية‪.‬‬ ‫•‬

‫فكتابة ‪:‬‬

‫;‪last = *ia + 4‬‬ ‫‪//ok: last = 4, equivalent to ia[0] + 4‬‬

‫يعين إلغاء املرجع عن ‪ ia‬وإضافة ‪ 4‬إلى القيمة غري املرجعية‪ .‬سنغطي أسباب هذا السلوك في ‪.4.1.2‬‬

‫‪Subscripts and Pointers‬‬ ‫الفهرسة واملؤرشات‬

‫كما رأينا ‪ ،‬في معظم األماكن عندما نستخدم االسم ملصفوفة ‪،‬‬
‫فإننا نستخدم بالفعل مؤرشًا ﻷول عنرص في تلك املصفوفة‪.‬‬ ‫•‬
‫هناك مكان واحد حيث يقوم املرتجم بإجراء هذا التحويل هو عندما نقوم بفهرسة مصفوفة‪.‬‬ ‫•‬

‫بافرتاض ‪:‬‬

‫;}‪int ia[] = {0,2,4,6,8‬‬ ‫‪// array with 5 elements of type int‬‬

‫إذا كتبنا ]‪ ، ia[0‬فهذا تعبري يستخدم االسم ملصفوفة‪.‬‬


‫عندما نقوم بفهرسة مصفوفة ‪ ،‬فإننا نقوم بالفعل بفهرسة مؤرش لعنرص في تلك املصفوفة‪:‬‬

‫;]‪int i = ia[2‬‬ ‫‪//ia converted to pointer to first element in ia‬‬


‫‪// ia[2] fetches element to which (ia + 2) points‬‬
‫;‪int *p = ia‬‬ ‫‪// p points to first element in ia‬‬
‫;)‪i = *(p + 2‬‬ ‫]‪// equivalent to i = ia[2‬‬

‫يمكننا استخدام عامل الفهرسة على أي مؤرش ‪ ،‬طاملا أن هذا املؤرش يشري إلى عنرص (أو ما بعد آخر عنرص) فيها ‪:‬‬

‫;]‪int *p = &ia[2‬‬ ‫‪// p points to element indexed by 2‬‬


‫;]‪int j = p[1‬‬ ‫‪// p[1] equivalent to *(p + 1),‬‬
‫]‪// p[1] same element as ia[3‬‬
‫;]‪int k = p[-2‬‬ ‫]‪// p[-2] same element as ia[0‬‬

‫يشري هذا املثال األخري إلى اختالف مهم بني املصفوفات وأنواع مكتبة كاملتجه والسلسلة لها عوامل فهرسة‪.‬‬ ‫•‬
‫تفرض أنواع املكتبة على الفهرس املستخدم مع الكود أن يكون قيمة بال_إشارة‪.‬‬ ‫•‬
‫عامل الفهرسة املدمج ال يقوم بذلك‪.‬‬ ‫•‬
‫يمكن أن يكون الفهرس املستخدم مع عامل الفهرسة املدمج قيمة سالبة‪.‬‬ ‫•‬
‫بالطبع ‪ ،‬يجب أن يشري الناتج إلى عنرص في (أو ما بعد نهاية) املصفوفة اليت يشري إليها املؤرش األصلي‪.‬‬ ‫•‬
‫تحذير‬

‫على عكس فهرسة املتجه والسلسلة ‪ ،‬فإن فهرس عامل الفهرسة املدمج هو نوع مؤرش‪.‬‬

‫قسم التدريبات ‪3.5.3‬‬


‫التمرين ‪:3.34‬‬
‫‪ -‬بما أن ‪ p1‬و ‪ p2‬يشريان إلى عنارص في نفس املصفوفة ‪ ،‬فماذا يفعل الكود التالي؟‬
‫‪ -‬هل توجد قيم ‪ p1‬أو ‪ p2‬تجعل هذا الرمز غري رشعي؟‬
‫;‪p1 += p2 - p1‬‬
‫التمرين ‪:3.35‬‬
‫برنامجا لضبط العنارص في مصفوفة على صفر‪.‬‬
‫ً‬ ‫‪ -‬باستخدام املؤرشات ‪ ،‬اكتب‬
‫التمرين ‪:3.36‬‬
‫برنامجا ملقارنة مصفوفتني من أجل املساواة‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫مشابها ملقارنة متجهني‪.‬‬
‫ً‬ ‫برنامجا‬
‫ً‬ ‫‪ -‬اكتب‬

‫‪3.5.4. C-Style Character Strings‬‬ ‫‪ 3٫5٫4‬سالسل حرف نمط اليس‬

‫تحذير‬

‫بالرغم من دعم يس‪ ++‬لسالسل نمط اليس إال أنه ال ينبغي استخدامها في برامجها‪ .‬تعد تلك السالسل‬
‫غنيا لألخطاء بشكل مدهش وهي سبب جذري ملشكالت أمان عديدة‪ .‬كما أن استخدامها أصعب!‬ ‫مصدرا ً‬
‫ً‬

‫تُعد حرفيات سلسلة حرف مثياًل لبنية أكرث عمومية ترثها لغة يس‪ ++‬من سالسل أحرف أنماط لغة اليس‪ :‬سالسل‬
‫نمط اليس‪.‬‬

‫إن سالسل نمط اليس ليست نوعا‪ .‬بل هي اصطالح لكيفية تمثيل واستخدام سالسل األحرف‪.‬‬
‫• يتم تخزين السالسل اليت تتبع هذا االصطالح في مصفوفات أحرف تنتهي بقيمة خالية ‪null‬‬
‫‪. terminated‬‬
‫• نعين بقيمة خالية أن آخر حرف في السلسلة يُتبع بحرف خال‪.‬‬
‫وعادة نستخدم املؤرشات ملعالجة تلك السالسل‪.‬‬ ‫•‬

‫‪C Library String Functions‬‬ ‫دوال سلسلة مكتبة اليس‬

‫توفر مكتبة‪-‬يس القياسية مجموعة دوال مدرجة في الجدول ‪ ، 3.8‬واليت تعمل على سالسل نمط اليس‪.‬‬
‫يتم تعريف هذه الدوال في العنوان ‪ ، cstring‬وهو نسخة يس‪ ++‬من العنوان ‪.string.h‬‬

‫الجدول ‪ .3.8‬دوال سلسلة أحرف نمط اليس‬

‫)‪strlen(p‬‬ ‫‪Returns length of p, not counting null.‬‬

‫‪Compares p1 and p2 for equality. Returns 0 if p1==p2,‬‬


‫)‪strcmp(P1, p2‬‬
‫‪a positive value if p1>p2 & negative value if p1<p2.‬‬
‫)‪strcat(p1, p2‬‬ ‫‪Appends p2 to p1. Returns p1.‬‬

‫)‪strcpy(p1, p2‬‬ ‫‪Copies p2 into p1. Returns p1.‬‬

‫تحذير‬

‫ال تفحص الدوال الواردة في الجدول ‪ 3.8‬من معلمات السلسلة‪.‬‬

‫يجب أن يشري املؤرش(ــات) الذي تم تمريره(ــا) لهذه اإلجراءات ملصفوفة(ــات) منتهية بقيمة خالية ‪:‬‬

‫;}'‪char ca[] = {'C', '+', '+‬‬ ‫‪// not null terminated‬‬


‫;‪cout << strlen(ca) << endl‬‬ ‫‪// disaster: ca isn't null terminated‬‬

‫معرف‪.‬‬
‫في هذه الحالة ‪ ca ،‬عبارة عن مصفوفة من ‪ char‬ولكن ﻻ تنتهي بقيمة خالية‪ .‬النتيجة سلوك غري ّ‬ ‫•‬
‫التأثري األكرث احتماال لهذا االستدعاء هو استمرار ‪ strlen‬في البحث عرب الذاكرة اليت تلي ‪ ca‬حىت تواجه‬ ‫•‬
‫حر ًفا خالياً ‪.‬‬

‫مقارنة السالسل‬

‫تماما عن كيفية مقارنة مكتبة السالسل‪.‬‬


‫ً‬ ‫تتم مقارنة سلسلتني من نمط اليس بشكل مختلف‬
‫فعندما نقارن بني سلسلتني ملكتبة السلسلة ‪ ،‬فإننا نستخدم عوامل العالئقية أو املساواة العادية ‪:‬‬

‫;"‪string s1 = "A string example‬‬


‫;"‪string s2 = "A different string‬‬
‫)‪if (s1 < s2‬‬ ‫‪// false: s2 less than s1‬‬

‫معرفة بشكل مشابه سيقارن قيمة املؤرش ‪ ،‬وليس السالسل نفسها‪:‬‬


‫استخدام هذه العوامل على سالسل نمط يس ّ‬
‫;"‪const char ca1[] = "A string example‬‬
‫;"‪const char ca2[] = "A different string‬‬
‫)‪if (ca1 < ca2‬‬ ‫‪// undefined: compares two unrelated addresses‬‬

‫تذكر أننا عندما نستخدم مصفوفة ‪ ،‬فإننا نستخدم مؤرشًا يشري ﻷول عنرص في املصفوفة‪.‬‬ ‫•‬
‫فعليا قيمتني ثابتتني ملؤرش حرف *‪.‬‬
‫ً‬ ‫ومن ثم ‪ ،‬فإن هذا الرشط يقارن‬ ‫•‬
‫هذه املؤرشات ال تعالج نفس الكائن ‪ ،‬لذا فاملقارنة سلوك غري معرف‪.‬‬ ‫•‬

‫ملقارنة سالسل نمط اليس ‪ ،‬وبدال ً من قيم املؤرش ‪ ،‬يمكننا استدعاء ‪.strcmp‬‬
‫تعيد هذه الدالة صفراً إن كانت السالسل متساوية ‪ ،‬أو قيمة موجبة أو سالبة ‪،‬‬ ‫•‬
‫اعتما ًدا على ما إذا كانت أول سلسلة أكرب أو أصغر من الثانية ‪:‬‬ ‫•‬

‫‪if (strcmp(ca1, ca2) < 0) // same effect as string comparison s1 < s2‬‬

‫املستدعي هو املسؤول عن حجم سلسلة الوجهة‬


‫‪Caller Is Responsible for Size of a Destination String‬‬
‫أيضا ربط سالسل نمط اليس أو نسخها كثريًا عن نفس العمليات على سالسل املكتبة‪.‬‬
‫يختلف ً‬
‫فعلى سبيل املثال ‪ ،‬إذا أردنا جمع سلسلتني ‪ s1‬و ‪ s2‬املعرفتني أعاله ‪ ،‬فيمكننا القيام بذلك مبارشة ‪:‬‬

‫‪// initialize largeStr as concatenation of s1, space, and s2‬‬


‫;‪string largeStr = s1 + " " + s2‬‬

‫سيكون القيام بنفس اليشء مع مصفوفتينا ‪ ca1‬و ‪ ca2‬خطأ‪.‬‬ ‫•‬


‫يحاول التعبري ‪ ca1 + ca2‬جمع مؤرشين ‪ ،‬وهذا أمر غري رشعي وال معىن له‪.‬‬ ‫•‬

‫بدال من ذلك يمكننا استخدام ‪ strcat‬و ‪.strcpy‬‬


‫مع ذلك والستخدام هذه الدوال ‪ ،‬يجب علينا تمرير مصفوفة لتحتفظ بالسلسلة الناتجة‪.‬‬ ‫•‬
‫يجب أن تكون املصفوفة اليت نمررها كبرية بما يكفي الحتواء السلسلة اليت يتم إنشاؤها ‪ ،‬متضمنة الحرف‬ ‫•‬
‫الخالي في نهايتها‪.‬‬
‫الكود الذي نعرضه هنا محفوف باحتمالية حدوث خطأ جسيم‪ ،‬على الرغم من وجود نمط استخدام شائع ‪:‬‬

‫‪// disastrous if we miscalculated size of largeStr‬‬


‫;)‪strcpy(largeStr, ca1‬‬ ‫‪// copies ca1 into largeStr‬‬
‫;)" " ‪strcat(largeStr,‬‬ ‫‪// adds a space at end of largeStr‬‬
‫;)‪strcat(largeStr, ca2‬‬ ‫‪// concatenates ca2 onto largeStr‬‬

‫تكمن املشكلة في أنه يمكننا بسهولة أن نخطئ في تقدير الحجم املطلوب لـ ‪.largeStr‬‬ ‫•‬
‫عالوة على ذلك ‪ ،‬في أي وقت نقوم فيه بتغيري القيم اليت نريد تخزينها في ‪، largeStr‬‬ ‫•‬
‫علينا أن نتذكر التحقق جي ًدا من أننا حسبنا حجمها بشكل صحيح‪.‬‬ ‫•‬
‫لسوء الحظ ‪ ،‬يتم توزيع برامج مشابهة لهذا الرمز على نطاق واسع‪.‬‬ ‫•‬
‫وغالبا ما تؤدي إلى ترسيبات أمنية خطرية‪.‬‬
‫ً‬ ‫الربامج اليت تحتوي على مثل هذا الكود معرضة لألخطاء‬ ‫•‬

‫تلميح‬

‫بالنسبة ملعظم التطبيقات من األفضل استخدام سالسل املكتبة بدال ً من سالسل نمط اليس‪،‬‬
‫أيضا‪.‬‬
‫إضافة لكونها أكرث أما ًنا ً‬

‫قسم التدريبات ‪3.5.4‬‬


‫التمرين ‪:3.37‬‬
‫ماذا يفعل الربنامج التالي؟‬
‫;}'‪const char ca[] = {'h', 'e', 'l', 'l', 'o‬‬
‫;‪const char *cp = ca‬‬
‫{ )‪while (*cp‬‬
‫;‪cout << *cp << endl‬‬
‫;‪++cp‬‬
‫}‬
‫التمرين ‪:3.38‬‬
‫‪ -‬في هذا القسم ‪ ،‬الحظنا أن محاولة جمع مؤرشين ليس أمرًا غري رشعي فحسب ‪ ،‬بل ال معىن له‪.‬‬
‫‪ -‬ملاذا يكون جمع مؤرشين بال معىن؟‬
‫التمرين ‪:3.39‬‬
‫برنامجا ملقارنة سلسلتني‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫برنامجا ملقارنة قيم سلسليت أحرف نمط اليس‪.‬‬
‫ً‬ ‫‪ -‬اكتب اآلن‬

‫التمرين ‪:3.40‬‬
‫برنامجا لتعريف مصفوفتني أحرف تمت تهيئتهما من سلسلة حرفية‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬عرف اآلن مصفوفة حرف ثالثة لتحمل تجميع املصفوفتني ‪.‬‬
‫‪ -‬استخدم ‪ strcpy‬و ‪ strcat‬لنسخ املصفوفتني في الثالث ‪.‬‬

‫‪3.5.5. Interfacing to Older Code‬‬ ‫‪ 3٫5٫5‬التفاعل مع الكود األقدم‬

‫العديد من برامج يس‪ ++‬املكتبة القياسية وال تستخدم أنواع السالسل واملتجهات‪.‬‬ ‫تسبق‬
‫عالوة على ذلك ‪ ،‬تتفاعل العديد من برامج يس‪ ++‬مع برامج مكتوبة بلغة اليس أو لغات أخرى ال يمكنها‬ ‫•‬
‫استخدام مكتبة يس‪.++‬‬
‫ومن ثم ‪ ،‬قد يتعني على الربامج املكتوبة بلغة يس‪ ++‬الحديثة أن تتعامل مع أكواد استخدمت مصفوفات‬ ‫•‬
‫و‪/‬أو سالسل حرف نمط اليس‪.‬‬
‫توفر مكتبة يس‪ ++‬أقساماً لتجعل إدارة الواجهة أسهل‪.‬‬ ‫•‬

‫دمج مكتبة السالسل مع سالسل نمط اليس‬


‫‪Mixing Library strings and C-Style Strings‬‬

‫في الفقرة ‪ 3.2.1‬رأينا أنه يمكننا تهيئة سلسلة على حرفية سلسلة ‪:‬‬

‫;)"‪string s("Hello World‬‬ ‫‪// s holds Hello World‬‬

‫بشكل عام ‪ ،‬يمكننا استخدام مصفوفة أحرف منتهية بقيمة خالية في أي مكان يمكننا فيه استخدام حرفية سلسلة‪:‬‬
‫يمكننا استخدام مصفوفة أحرف منتهية بقيمة خالية لتهيئة سلسلة أو تعيينها‪.‬‬ ‫•‬
‫ويمكننا استخدام مصفوفة أحرف منتهية بقيمة خالية كمعامل واحد (لكن ليس كال املعاملني) إلى عامل تزايد‬ ‫•‬
‫السلسلة أو كمعامل أيمن في التعيني املركب (‪ )=+‬لسلسلة‪.‬‬

‫التوظيف العكيس ليس متوفراً ‪:‬‬


‫ال توجد طريقة مبارشة الستخدام مكتبة سلسلة عندما تكون سلسلة نمط اليس مطلوبة‪.‬‬ ‫•‬
‫فعلى سبيل املثال ‪ ،‬ال توجد طريقة لتهيئة مؤرش حرف على سلسلة‪.‬‬ ‫•‬
‫غالبا إلنجاز ما نريد‪:‬‬
‫ً‬ ‫ومع ذلك ‪ ،‬هناك دالة عضو سلسلة تسمى ‪ c_str‬واليت يمكننا استخدامها‬ ‫•‬

‫;‪char *str = s‬‬ ‫‪// error: can't initialize a char* from a string‬‬
‫;)(‪const char *str = s.c_str‬‬ ‫‪// ok‬‬

‫يشري االسم ‪ c_str‬إلى أن الدالة تقوم بإعادة سالسل أحرف نمط اليس‪.‬‬ ‫•‬
‫أي أنها تعيد مؤرشًا لبداية مصفوفة أحرف تنتهي بقيمة خالية تحتوي على نفس البيانات مثل األحرف‬ ‫•‬
‫املوجودة في السلسلة‪.‬‬
‫نوع املؤرش حرف ثابت ‪ ، * const char‬ما يمنعنا من تغيري محتويات املصفوفة‪.‬‬ ‫•‬
‫املصفوفة املعادة من ‪ c_str‬ليست مضمونة أن تكون صالحة إلى ما ﻻ نهاية‪.‬‬ ‫•‬
‫أي استخدام الحق لـ ‪ s‬قد يغري قيمة ‪ s‬يمكن أن يبطل هذه املصفوفة‪.‬‬ ‫•‬
‫تحذير‬

‫إذا احتاج الربنامج إلى وصول مستمر ملحتويات املصفوفة املعادة من ()‪ ، str‬فيجب على الربنامج‬
‫نسخ املصفوفة املعادة من ()‪.c_str‬‬

‫‪Using an Array to Initialize a vector‬‬ ‫استخدام مصفوفة لتهيئة متجه‬

‫في الفقرة ‪ 3.5.1‬الحظنا أنه ال يمكننا تهيئة مصفوفة مدمجة على مصفوفة أخرى‪ .‬وال يمكننا تهيئة مصفوفة على‬
‫متجه‪.‬‬
‫ومع ذلك ‪،‬‬
‫يمكننا استخدام مصفوفة لتهيئة متجه‪ .‬للقيام بذلك ‪ ،‬نحدد عنوان أول عنرص وما بعد أخر عنرص نرغب في نسخه ‪:‬‬

‫;}‪int int_arr[] = {0, 1, 2, 3, 4, 5‬‬


‫‪// ivec has six elements; each copy of corresponding element in int_arr‬‬
‫;))‪vector<int> ivec(begin(int_arr), end(int_arr‬‬

‫يشري املؤرشان املستخدمان في إنشاء ‪ ivec‬إلى نطاق القيم املراد استخدامها لتهيئة العنارص في ‪.ivec‬‬ ‫•‬
‫يشري املؤرش الثاني ملا بعد آخر عنرص يراد نسخه‪ .‬في هذه الحالة ‪،‬‬ ‫•‬
‫استخدمنا داليت ‪ begin‬و ‪ end‬للمكتبة لتمرير املؤرشات ﻷول عنرص وما بعد آخر عنرص في ‪.int_arr‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬سيكون لدى ‪ ivec‬ستة عنارص سيكون لكل منها نفس قيمة العنرص املقابل في ‪.int_arr‬‬ ‫•‬

‫يمكن أن يكون النطاق املحدد مجموعة فرعية من املصفوفة ‪:‬‬

‫]‪// copies three elements: int_arr[1], int_arr[2], int_arr[3‬‬


‫;)‪vector<int> subVec(int_arr + 1, int_arr + 4‬‬

‫تنئش هذه التهيئة ‪ subVec‬بثالثة عنارص‪ .‬قيمها هي نسخ من القيم من ]‪ int_arr[1‬وحىت ]‪.int_arr[3‬‬

‫نصيحة ‪ :‬استخدم أنواع املكتبة بدال ً من املصفوفات‬


‫‪Use Library Types Instead of Arrays‬‬

‫املؤرشات واملصفوفات عرضة للخطأ بشكل مدهش‪ .‬جزء من املشكلة يتعلق باملفاهيم ‪:‬‬ ‫•‬
‫تُستخدم املؤرشات في عمليات معالجة منخفضة املستوى حيث من السهل حصول أخطاء في معالجة‬ ‫•‬
‫امللفات‪.‬‬
‫تنشأ مشاكل أخرى بسبب بناء الجملة ‪ ،‬وال سيما صيغة اإلعالن املستخدمة مع املؤرشات‪.‬‬ ‫•‬

‫يجب أن تستخدم برامج يس‪ ++‬الحديثة املتجهات واملكررات بدال ً من املصفوفات واملؤرشات املدمجة ‪،‬‬ ‫•‬
‫وأن تستخدم السالسل بدال ً من سالسل أحرف مستندة إلى مصفوفة ذات نمط اليس‪.‬‬ ‫•‬

‫قسم التدريبات ‪3.5.5‬‬


‫التمرين ‪:3.41‬‬
‫برنامجا لتهيئة متجه على مصفوفة أعداد صحيحة ‪.ints‬‬
‫ً‬ ‫اكتب‬
‫التمرين ‪:3.42‬‬
‫برنامجا لنسخ متجه أعداد صحيحة ‪ ints‬في مصفوفة أعداد صحيحة ‪.ints‬‬
‫ً‬ ‫اكتب‬
‫‪ . 3٫6‬املصفوفـــــات متعددة األبعــــــاد‬
‫‪3.6. Multidimensional Arrays‬‬

‫باملعىن الدقيق للكلمة ‪ ،‬ال توجد مصفوفات متعددة األبعاد في يس‪.++‬‬


‫فما يشار إليها عاد ًة باملصفوفات متعددة األبعاد إنما هي في الواقع مصفوفات من مصفوفات‪.‬‬ ‫•‬
‫قد يكون من املفيد أن تضع هذه الحقيقة في االعتبار عند استخدام ما يبدو أنها مصفوفة متعددة األبعاد‪.‬‬ ‫•‬

‫نعرف املصفوفة اليت تكون عنارصها مصفوفات من خالل توفري بعدين‪ :‬بُعد املصفوفة نفسها وأبعاد عنارصها‪:‬‬

‫‪int ia[3][4]; // array size 3; each element is array of ints size 4‬‬

‫‪// array size 10; each element 20-element array whose elements arrays‬‬
‫‪// of 30 ints‬‬

‫‪int arr[10][20][30] = {0}; // initialize all elements to 0‬‬

‫كما رأينا في الفقرة ‪ ، 3.5.1‬يمكننا بسهولة فهم هذه التعريفات بقراءتها من الداخل إلى الخارج‪.‬‬ ‫•‬
‫سنبدأ باالسم الذي نعرفه (‪ )ia‬ونرى أن ‪ ia‬عبارة عن مصفوفة بحجم ‪.3‬‬ ‫•‬
‫أيضا بُعد‪.‬‬
‫باالستمرار في النظر إلى اليمني ‪ ،‬نرى أن عنارص ‪ ia‬لها ً‬ ‫•‬
‫وبالتالي ‪ ،‬فإن العنارص املوجودة في ‪ ia‬هي نفسها مصفوفات بحجم ‪.4‬‬ ‫•‬
‫بالنظر إلى اليسار ‪ ،‬نرى أن نوع هذه العنارص عبارة عن ‪.int‬‬ ‫•‬
‫إذن ‪ ia ،‬عبارة عن مصفوفة بحجم ‪ ، 3‬كل عنرص من عنارصه عبارة عن مصفوفة من أربعة أعداد صحيحة‬ ‫•‬
‫‪.ints‬‬

‫سنقرأ تعريف ‪ arr‬بنفس الطريقة‪.‬‬


‫أوال ً نرى أن ‪ arr‬عبارة عن مصفوفة بحجم ‪.10‬‬ ‫•‬
‫عنارص تلك املصفوفة هي نفسها مصفوفات بحجم ‪ .20‬فكل من هذه املصفوفات بها ‪ 30‬عنرص عدد صحيح ‪.‬‬ ‫•‬
‫ال يوجد حد لعدد الفهارس املستخدمة‪ .‬بمعىن أنه يمكن أن يكون لدينا مصفوفة عنارصها عبارة عن‬ ‫•‬
‫مصفوفات من عنارص تكون مصفوفات ‪ ،‬وما إلى ذلك‪.‬‬
‫في املصفوفة ثنائية األبعاد ‪ ،‬يُشار عاد ًة إلى البعد األول على أنه صف ‪ row‬والثاني عمود ‪.column‬‬

‫تهيئة عنارص مصفوفة متعددة اﻷبعاد‬


‫‪Initializing the Elements of a Multidimensional Array‬‬

‫كحال أي مصفوفة ‪ ،‬يمكننا تهيئة عنارص مصفوفة متعددة األبعاد من خالل توفري قائمة بني قوسني من املهيئات‪.‬‬
‫يمكن تهيئة املصفوفات متعددة األبعاد عن طريق تعريف القيم املوضوعة بني قوسني لكل صف‪:‬‬

‫{ = ]‪int ia[3][4‬‬ ‫‪// three elements; each element array of size 4‬‬
‫‪{0, 1, 2, 3},‬‬ ‫‪// initializers for row indexed by 0‬‬
‫‪{4, 5, 6, 7},‬‬ ‫‪// initializers for row indexed by 1‬‬
‫}‪{8, 9, 10, 11‬‬ ‫‪// initializers for row indexed by 2‬‬
‫;}‬
‫وضوحا إلى حد كبري ‪:‬‬
‫ً‬ ‫تعترب األقواس املتداخلة اختيارية‪ .‬التهيئة التالية مكافئة ‪ ،‬على الرغم من أنها أقل‬

‫‪// equivalent initialization without optional nested braces for each row‬‬
‫;}‪int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11‬‬

‫كحال املصفوفات أحادية البعد ‪ ،‬يمكن ترك العنارص خارج قائمة التهيئة‪.‬‬
‫يمكننا تهيئة أول عنرص فقط على كل صف على النحو التالي‪:‬‬

‫‪// explicitly initialize only element 0 in each row‬‬


‫;}} ‪int ia[3][4] = {{ 0 }, { 4 }, { 8‬‬

‫يتم تهيئة العنارص املتبقية بنفس طريقة تهيئة املصفوفات العادية أحادية البعد‪.‬‬
‫تماما‪ .‬فالكود التالي‪:‬‬
‫ً‬ ‫إذا تم حذف األقواس املتداخلة ‪ ،‬فستكون النتائج مختلفة‬

‫‪// explicitly initialize row 0; remaining elements are value initialized‬‬


‫;}‪int ix[3][4] = {0, 3, 6, 9‬‬

‫يقوم بتهيئة عنارص أول صف‪ .‬بينما تتم تهيئة العنارص املتبقية على ‪.0‬‬

‫‪Subscripting a Multidimensional Array‬‬ ‫فهرسة مصفوفة متعددة اﻷبعاد‬

‫كما هو حال أي مصفوفة ‪ ،‬يمكننا استخدام فهرسة للوصول إلى عنارص املصفوفة متعددة األبعاد‪.‬‬
‫للقيام بذلك ‪ ،‬نستخدم رمز فهرسة منفصاًل لكل بُعد‪.‬‬ ‫•‬
‫فإذا كان التعبري يوفر عد ًدا من الفهارس بقدر عدد األبعاد ‪ ،‬فإننا نحصل على عنرص من النوع املحدد‪.‬‬ ‫•‬

‫إذا قدمنا عدد فهارس أقل من األبعاد ‪ ،‬فإن النتيجة هي عنرص املصفوفة الداخلي في الفهرس املحدد ‪:‬‬

‫‪// assigns first element of arr to last element in last row of ia‬‬
‫;]‪ia[2][3] = arr[0][0][0‬‬
‫‪int (&row)[4] = ia[1]; //binds row to second four-element array in ia‬‬

‫في املثال األول نوفر مؤرشات لجميع األبعاد لكل من املصفوفتني‪.‬‬ ‫•‬
‫على الجانب األيرس ‪ ،‬يعيد ]‪ ia[2‬الصف األخري في ‪.ia‬‬ ‫•‬
‫إنه ال يقوم بجلب عنرص من تلك املصفوفة ولكنه يعيد املصفوفة نفسها‪.‬‬ ‫•‬
‫نقوم بتدوين هذه املصفوفة ‪ ،‬وإحضار العنرص [‪ ، ]3‬وهو العنرص األخري في تلك املصفوفة‪.‬‬ ‫•‬

‫وباملثل ‪ ،‬فإن املعامل األيمن له ثالثة أبعاد‪.‬‬


‫نقوم أوال ً بإحضار املصفوفة في الفهرس ‪ 0‬من املصفوفة الخارجية‪.‬‬ ‫•‬
‫نتيجة هذه العملية هي مصفوفة (متعددة األبعاد) بحجم ‪.20‬‬ ‫•‬
‫نأخذ أول عنرص من تلك املصفوفة املكونة من ‪ 20‬عنرصًا ‪ ،‬مما ينتج عنه مصفوفة بحجم ‪.30‬‬ ‫•‬
‫ثم نقوم بإحضار أول عنرص من تلك املصفوفة‪ .‬أما في املثال الثاني ‪ ،‬نعرف ‪ row‬كمرجع إلى مصفوفة من‬ ‫•‬
‫أربعة ‪.ints‬‬
‫نربط هذا املرجع إلى الصف الثاني في ‪.ia‬‬ ‫•‬

‫مثال آخر‪ :‬من الشائع استخدام زوج حلقات ‪ for‬متداخلة ملعالجة عنارص في مصفوفة متعددة األبعاد‪:‬‬
‫;‪constexpr size_t rowCnt = 3, colCnt = 4‬‬
‫;]‪int ia[rowCnt][colCnt‬‬ ‫‪// 12 uninitialized elements‬‬
‫‪// for each row‬‬
‫{ )‪for (size_t i = 0; i != rowCnt; ++i‬‬
‫‪// for each column within the row‬‬
‫{ )‪for (size_t j = 0; j != colCnt; ++j‬‬
‫‪// assign the element's positional index as its value‬‬
‫;‪ia[i][j] = i * colCnt + j‬‬
‫}‬
‫}‬

‫حلقات ‪ for‬الخارجية تعرب خالل كل عنرص من عنارص املصفوفة في ‪.ia‬‬ ‫•‬


‫أما حلقات ‪ for‬الداخلية فتعرب من خالل عنارص تلك املصفوفات الداخلية‪.‬‬ ‫•‬
‫في هذه الحالة ‪ ،‬قمنا بتعيني قيمة كل عنرص كمؤرش في املصفوفة الكلية‪.‬‬ ‫•‬

‫استخدام ‪ for‬النطاق مع املصفوفات متعددة اﻷبعاد‬


‫‪Using a Range for with Multidimensional Arrays‬‬

‫بموجب املعيار الجديد ‪ ،‬يمكننا تبسيط الحلقة السابقة باستخدام نطاق ‪:for‬‬

‫;‪size_t cnt = 0‬‬


‫)‪for (auto &row : ia‬‬ ‫‪// for every element in the outer array‬‬
‫{ )‪for (auto &col : row‬‬ ‫‪// for every element in the inner array‬‬
‫;‪col = cnt‬‬ ‫‪// give this element next value‬‬
‫;‪++cnt‬‬ ‫‪// increment cnt‬‬
‫}‬

‫تعطي هذه الحلقة عنارص ‪ ia‬نفس قيم الحلقة السابقة ‪ ،‬لكن هذه املرة نسمح للنظام بإدارة املؤرشات لنا‪.‬‬ ‫•‬
‫نريد تغيري قيمة العنارص ‪ ،‬لذلك نعلن متغريات التحكم لدينا ‪ row‬و‪ col‬مراجع‪.‬‬ ‫•‬
‫يكرر ‪ for‬األول عرب العنارص في ‪ .ia‬هذه العنارص عبارة عن مصفوفات بحجم ‪.4‬‬ ‫•‬
‫وبالتالي ‪ ،‬فإن نوع ‪ row‬هو مرجع ملصفوفة من أربعة ‪.ints‬‬ ‫•‬
‫يكرر ‪ for‬الثاني عرب إحدى تلك املصفوفات املكونة من ‪ 4‬عنارص‪.‬‬ ‫•‬
‫ومن ثم ‪ ،‬فإن العمود هو ‪ .&int‬وفي كل تكرار نعني قيمة ‪ cnt‬للعنرص التالي في ‪ ia‬و نزايد قيمة ‪.cnt‬‬ ‫•‬
‫في املثال السابق ‪ ،‬استخدمنا املراجع كمتغريات تحكم في الحلقة ألننا أردنا تغيري العنارص في املصفوفة‪.‬‬ ‫•‬
‫لكن‪ ،‬هناك سبب أعمق الستخدام املراجع‪.‬‬ ‫•‬

‫كمثال ‪ ،‬فكر في الحلقة التالية ‪:‬‬

‫)‪for (const auto &row : ia‬‬ ‫‪// for every element in the outer array‬‬
‫)‪for (auto col : row‬‬ ‫‪// for every element in the inner array‬‬
‫;‪cout << col << endl‬‬
‫نعرف متغري التحكم للحلقة الخارجية كمرجع‪.‬‬
‫ال تكتب هذه الحلقة على العنارص ‪ ،‬مع ذلك ما زلنا ّ‬
‫نقوم بذلك لتجنب تحويل املصفوفة العادية إلى مؤرش‪ .‬فلو أهملنا املرجع وكتبنا هذه الحلقات على النحو التالي‪:‬‬

‫)‪for (auto row : ia‬‬


‫)‪for (auto col : row‬‬

‫فلن يتم جمع برنامجنا‪.‬‬ ‫•‬


‫فكما في السابق‪ ،‬يكرر ‪ for‬األول عرب ‪ ، ia‬عنارصه عبارة عن مصفوفات بحجم ‪.4‬‬ ‫•‬
‫مرجعا ‪ ،‬فعندما يقوم املرتجم بتهيئة ‪، row‬‬
‫ً‬ ‫ونظرًا ألن ‪ row‬ليست‬ ‫•‬
‫فإنه سيحول كل عنرص مصفوفة (مثل أي كائن آخر من نوع مصفوفة) إلى مؤرش ﻷول عنرص للمصفوفة‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬في هذه الحلقة يكون نوع ‪ .* row int‬حلقة ‪ for‬الداخلية غري رشعية‪.‬‬ ‫•‬
‫على الرغم من نوايانا ‪ ،‬تحاول هذه الحلقة التكرار عرب ‪.* int‬‬ ‫•‬

‫ملحوظة‬

‫الستخدام مصفوفة متعددة األبعاد مع ‪ for‬النطاق‪ ،‬يجب أن يكون متغري التحكم في الحلقة للجميع‬
‫مرجعا ما عدا املصفوفة الداخلية‪.‬‬

‫‪Pointers and Multidimensional Arrays‬‬ ‫املؤرشات واملصفوفات متعددة اﻷبعاد‬

‫تلقائيا إلى مؤرش ألول‬


‫ً‬ ‫كما هو حال أي مصفوفة ‪ ،‬عندما نستخدم االسم ملصفوفة متعددة األبعاد ‪ ،‬يتم تحويلها‬
‫عنرص في املصفوفة‪.‬‬

‫ملحوظة‬

‫عندما تعرف مؤرش ًا ملصفوفة متعددة األبعاد ‪ ،‬تذكر أن تلك املصفوفة هي في الواقع مصفوفة من مصفوفات‪.‬‬

‫ونظرًا ألن املصفوفة متعددة األبعاد هي في الواقع مصفوفة من مصفوفات ‪ ،‬فإن نوع املؤرش الذي تتحول إليه‬
‫املصفوفة هو مؤرش ألول مصفوفة الداخلية ‪:‬‬

‫;]‪int ia[3][4‬‬ ‫‪//array of size 3; each element is array ints of size 4‬‬
‫;‪int(*p)[4] = ia‬‬ ‫‪// p points to array of four ints‬‬
‫;]‪p = &ia[2‬‬ ‫‪// p now points to last element in ia‬‬

‫بتطبيق اإلسرتاتيجية من الفقرة ‪ ، 3.5.1‬نبدأ باإلشارة إلى أن (‪ )*p‬تقول أن ‪ p‬عبارة عن مؤرش‪.‬‬ ‫•‬
‫بالنظر إلى اليمني ‪ ،‬نرى أن الكائن الذي تشري إليه ‪ p‬له بُعد بحجم ‪، 4‬‬ ‫•‬
‫وبالنظر إلى اليسار أن نوع العنرص هو ‪ .int‬ومن ثم ‪ ،‬فإن ‪ p‬مؤرش ملصفوفة من أربعة ‪.ints‬‬ ‫•‬

‫ملحوظة‬
‫األقواس في هذا اإلعالن رضورية‪:‬‬
‫;]‪int *ip[4‬‬ ‫‪// array of pointers to int‬‬
‫‪int (*ip)[4]; // pointer to an array of four ints‬‬

‫مع ظهور املعيار الجديد ‪،‬‬


‫يمكننا في كثري من األحيان تجنب االضطرار إلى كتابة نوع مؤرش في مصفوفة باستخدام ‪ auto‬أو ‪: decltype‬‬

‫‪// print value each element in ia, with each inner array on its own line‬‬
‫‪// p points to an array of four ints‬‬
‫{ )‪for (auto p = ia; p != ia + 3; ++p‬‬
‫;‪// q points to first element of array of four ints‬‬
‫‪// that is, q points to an int‬‬
‫)‪for (auto q = *p; q != *p + 4; ++q‬‬
‫;' ' << ‪cout << *q‬‬
‫;‪cout << endl‬‬
‫}‬

‫تبدأ حلقة ‪ for‬الخارجية بتهيئة ‪ p‬لتشري ﻷول مصفوفة في ‪.ia‬‬ ‫•‬


‫تستمر هذه الحلقة حىت ننتهي من معالجة الصفوف الثالثة في ‪.ia‬‬ ‫•‬
‫تزايد ‪ p ++‬لها تأثري تحريك ‪ p‬لتشري للصف التالي (أي العنرص التالي) في ‪.ia‬‬ ‫•‬

‫حلقة ‪ for‬الداخلية تطبع قيم املصفوفات الداخلية‪.‬‬


‫تبدأ بجعل ‪ q‬تشري ﻷول عنرص في املصفوفة الذي يشري إليه ‪ .p‬نتيجة ‪ *p‬هي مصفوفة من أربعة ‪.ints‬‬ ‫•‬
‫تلقائيا إلى مؤرش ألول عنرص‪.‬‬
‫ً‬ ‫كالعادة ‪ ،‬عندما نستخدم مصفوفة ‪ ،‬يتم تحويلها‬ ‫•‬
‫تعمل حلقة ‪ for‬الداخلية حىت ننتهي من معالجة كل عنرص في املصفوفة الداخلية‪.‬‬ ‫•‬
‫للحصول على مؤرش لنهاية املصفوفة الداخلية ‪ ،‬نقوم مرة أخرى بإلغاء املرجع عن ‪ p‬للحصول على مؤرش‬ ‫•‬
‫ﻷول عنرص في تلك املصفوفة‪.‬‬
‫ثم نضيف ‪ 4‬إلى هذا املؤرش ملعالجة العنارص األربعة في كل مصفوفة داخلية‪.‬‬ ‫•‬

‫بالطبع ‪ ،‬يمكننا كتابة هذه الحلقة بسهولة أكرب باستخدام داليت ‪ begin‬و‪ end‬للمكتبة‪:‬‬

‫‪// p points to first array in ia‬‬


‫{ )‪for (auto p = begin(ia); p != end(ia); ++p‬‬
‫‪// q points to first element in an inner array‬‬
‫)‪for (auto q = begin(*p); q != end(*p); ++q‬‬
‫‪cout << *q << ' ';// prints int value to which q points‬‬
‫;‪cout << endl‬‬
‫}‬

‫هنا ندع املكتبة تحدد مؤرش ‪ ، end‬ونستخدم ‪ auto‬لتجنب االضطرار إلى كتابة النوع املعاد من ‪.begin‬‬ ‫•‬
‫في الحلقة الخارجية ‪ ،‬هذا النوع هو مؤرش ملصفوفة من أربعة ‪.ints‬‬ ‫•‬
‫في الحلقة الداخلية ‪ ،‬هذا النوع هو مؤرش لـ ‪.int‬‬ ‫•‬
‫أسماء النوع املستعارة تبسط املؤرشات ملصفوفات متعددة اﻷبعاد‬
‫‪Type Aliases Simplify Pointers to Multidimensional Arrays‬‬

‫يمكن أن يسهل النوع املستعار قراءة وكتابة وفهم املؤرشات إلى مصفوفات متعددة األبعاد‪ .‬فمثال‪:‬‬

‫;‪using int_array = int[4]; // new style type alias declaration‬‬


‫;‪typedef int int_array[4]; // equivalent typedef declaration‬‬
‫‪// print value each element in ia, with each inner array on its own line‬‬
‫{ )‪for (int_array *p = ia; p != ia + 3; ++p‬‬
‫)‪for (int *q = *p; q != *p + 4; ++q‬‬
‫;' ' << ‪cout << *q‬‬
‫;‪cout << endl‬‬
‫}‬

‫نبدأ هنا بتعريف ‪ int_array‬كاسم لنوع "مصفوفة من أربعة ‪."ints‬‬ ‫•‬


‫ونستخدم اسم النوع هذا لتعريف متغري التحكم في الحلقة في حلقة ‪ for‬الخارجية‪.‬‬ ‫•‬

‫تمارين القسم ‪3.6‬‬


‫التمرين ‪:3.43‬‬
‫‪ -‬اكتب ثالثة إصدارات مختلفة من الربنامج لطباعة عنارص ‪.ia‬‬
‫‪ -‬يجب أن يستخدم اإلصدار األول نطاق ‪ for‬إلدارة املكرر ‪،‬‬
‫‪ -‬يستخدم اإلصداران اآلخران حلقة ‪ for‬عادية في حالة باستخدام الفهرسة وفي األخرى باستخدام املؤرشات‪.‬‬
‫‪ -‬في جميع الربامج الثالثة ‪ ،‬اكتب جميع األنواع مبارشة‪.‬‬
‫‪ -‬بمعىن ‪ ،‬ال تستخدم اسم نوع مستعار ‪ type alias‬أو ‪ auto‬أو ‪ decltype‬لتبسيط الشفرة‪.‬‬
‫التمرين ‪:3.44‬‬
‫أعد كتابة الربامج من التمارين السابقة باستخدام ‪ type alias‬كمتغريات تحكم في الحلقة‪.‬‬
‫التمرين ‪:3.45‬‬
‫أعد كتابة الربامج هذه املرة باستخدام ‪.auto‬‬
‫ملخــــــــــص الفصــــــــــل‬
‫‪Chapter Summary‬‬

‫من بني أهم أنواع املكتبة املتجه والسلسلة‪.‬‬


‫السلسلة عبارة عن تسلسل متغري الطول من األحرف ‪،‬‬ ‫•‬
‫واملتجه هو حاوية كائنات من نوع واحد‪.‬‬ ‫•‬

‫تسمح املكررات‬
‫بالوصول غري املبارش إلى الكائنات املخزنة في حاوية‪.‬‬ ‫•‬
‫وتُستخدم للوصول إلى العنارص والتنقل بينها في السالسل واملتجهات‪.‬‬ ‫•‬

‫توفر املصفوفات واملؤرشات لعنارص املصفوفة نظائر منخفضة املستوى ملكتبات املتجهات والسلسلة‪.‬‬
‫• بشكل عام ‪ ،‬ينبغي استخدام فئات املكتبة بشكل مفضل على البدائل منخفضة املستوى من املصفوفات‬
‫واملؤرشات املدمجة في اللغة‪.‬‬
‫مصطلحــــــــــات معــــــــــرفة‬
‫‪Defined Terms‬‬

‫أيضا ‪ ،‬دالة مكتبة قائمة بذاتها تأخذ مصفوفة‬


‫عضو سلسلة ومتجه يقوم بإعادة مكرر ﻷول عنرص‪ً .‬‬
‫‪begin‬‬
‫وتعيد مؤرشًا ﻷول عنرص في مصفوفة‪.‬‬

‫فهرسا خارج نطاق حاوية ‪ ،‬مثل سلسلة أو متجه أو‬


‫ً‬ ‫خطأ برمجة خطري ينتج عندما نستخدم‬
‫‪buffer overflow‬‬
‫مصفوفة‪.‬‬

‫مصفوفة أحرف خالية النهاية‪ .‬قيم حرفية سلسلة هي سالسل من نمط لغة اليس‪ .‬وهي بطبيعتها‬
‫‪C-style strings‬‬
‫عرضة للخطأ‪.‬‬

‫مخطط يمكن من خالله إنشاء أنواع فئات معينة‪ .‬الستخدام قالب فئة ‪ ،‬يجب علينا تحديد معلومات‬
‫‪class template‬‬
‫نعرف نوع العنرص‪ :‬متجه <‪ >int‬يحمل ‪.ints‬‬ ‫إضافية‪ .‬على سبيل املثال ‪ ،‬لتعريف متجه ‪ّ ،‬‬

‫‪compiler‬‬ ‫مزية تمت إضافتها إلى اللغة بواسطة مرتجم معني‪ .‬ال يمكن نقل الربامج اليت تعتمد على امتدادات‬
‫‪extension‬‬ ‫املرتجم بسهولة إلى برامج الرتجمة الربمجية األخرى‪.‬‬

‫‪container‬‬ ‫نوع تحتوي كائناته على مجموعة كائنات من نوع معني‪ .‬املتجه عبارة عن نوع حاوية‪.‬‬

‫‪copy‬‬ ‫نموذج تهيئة يستخدم = ‪ .‬الكائن الذي تم إنشاؤه حديثًا هو نسخة من املُهئي املحدد‪.‬‬
‫‪initialization‬‬

‫‪difference_type‬‬ ‫نوع رقم صحيح بإشارة يعرفه املتجه والسلسلة يمكنه االحتفاظ باملسافة بني أي مكررين‪.‬‬

‫‪direct‬‬ ‫نموذج التهيئة املبارشة الذي ال يتضمن =‬


‫‪initialization‬‬

‫عضو سلسلة ومتجه‪ .‬يقوم بإعادة قيمة منطقية ‪ ،‬واليت تكون صحيحة إذا كان الحجم صفرًا ‪ ،‬وإعادة‬
‫‪empty‬‬
‫قيمة خطأ في الحاالت األخرى‪.‬‬

‫أيضا ‪ ،‬دالة مكتبة قائمة بذاتها تأخذ مصفوفة‬


‫عضو سلسلة ومتجه يقوم بإعادة مكرر ما بعد النهاية‪ً .‬‬
‫‪end‬‬
‫وتعيد مؤرشًا ملا بعد آخر عنرص في مصفوفة‪.‬‬

‫دالة معرفة في عنوان سلسلة‪ .‬تأخذ قناة مدخالت وسلسلة‪ .‬تقوم بقراءة القناة حىت السطر الجديد‬
‫‪getline‬‬
‫التالي ‪ ،‬وتخزن ما قرأته في السلسلة ‪ ،‬وتعيد قناة املدخالت‪ .‬تتم قراءة السطر الجديد والتخلص منه‪.‬‬
‫قيمة فهرس مستخدمة في عامل الفهرسة لتشري إلى العنرص املراد اسرتداده من سلسلة أو متجه أو‬
‫‪index‬‬
‫مصفوفة‪.‬‬

‫‪instantiation‬‬ ‫عملية مرتجم إلنشاء مثيل حيث تقوم بإنشاء فئة قالب معينة أو دالة‪.‬‬

‫‪iterator‬‬ ‫نوع يستخدم للوصول إلى عنارص حاوية والتنقل بينها‪.‬‬

‫عمليات على متجه أو مكرر سلسلة‪ :‬إضافة أو طرح قيمة عدد صحيح وينتج عن املكرر العديد من‬
‫‪iterator‬‬ ‫العنارص قبل أو خلف املكرر األصلي‪ .‬ينتج عن طرح مكرر واحد من آخر املسافة بينهما‪ .‬يجب أن‬
‫‪arithmetic‬‬
‫تشري املكررات إلى عنارص في نفس الحاوية أو ما بعد آخرها‪.‬‬

‫‪null-terminated‬‬ ‫سلسلة يتبع حرفها األخري الحرف الخالي ('‪.)'0‬‬


‫‪string‬‬

‫‪off-the-end‬‬ ‫مكرر يتم إعادته بـ ‪ end‬يشري إلى عنرص غري موجود يقع بعد نهاية الحاوية‪.‬‬
‫‪iterator‬‬

‫‪pointer‬‬ ‫عمليات حسابية يمكن تطبيقها على مؤرشات‪ .‬تدعم مؤرشات املصفوفات نفس العمليات الحسابية‬
‫‪arithmetic‬‬ ‫للمكرر‪.‬‬

‫معرف في عنوان‪ cstddef‬وهو كبري بما يكفي‬


‫نوع صحيح بإشارة يعتمد على اآللة وهو ّ‬
‫‪ptrdiff_t‬‬
‫لالحتفاظ بالفرق بني مؤرشين في أكرب مصفوفة ممكنة‪.‬‬

‫‪push_back‬‬ ‫عضو متجه‪ .‬يقوم بإلحاق عنارص إلى الجزء الخلفي من املتجه‪.‬‬

‫‪range for‬‬ ‫جملة تحكم يتكرر عرب مجموعة محددة من القيم‪.‬‬

‫‪size‬‬ ‫عضو سلسلة ومتجه‪ .‬يعيد عدد األحرف أو العنارص ‪ ،‬على التوالي‪ .‬يعيد قيمة نوع ‪. size_type‬‬

‫معرف في عنوان‪ cstddef‬يكون كبريًا بما يكفي‬


‫نوع صحيح بال_إشارة يعتمد على اآللة وهو ّ‬
‫‪size_t‬‬
‫الحتواء حجم أكرب مصفوفة ممكنة‪.‬‬

‫اسم أنواع معرفة بواسطة فئات سلسلة ومتجهات قادرة على احتواء حجم أي سلسلة أو متجه ‪ ،‬على‬
‫‪size_type‬‬
‫تعرفها كنوع بال_إشارة‪.‬‬
‫التوالي‪ .‬فئات املكتبة اليت تحدد ‪( size_type‬نوع_املقاس) ّ‬

‫‪string‬‬ ‫نوع مكتبة تمثل سلسلة من األحرف‪.‬‬

‫‪using‬‬ ‫لجعل اسم من مساحة اسم قابل للوصول إليه مبارشة‪ Using namepsace::name .‬؛ يجعل‬
‫‪declarations‬‬ ‫االسم قابال للوصول إليه بدون البادئة‪.‬‬
‫تهيئة يتم فيها تهيئة أنواع مدمجة على الصفر وتهيئة أنواع فئات بواسطة ُمنشئة الفئة االفرتاضية‪.‬‬
‫‪value‬‬ ‫يمكن تهيئة كائنات نوع فئة فقط إذا كان للفئة ُمنشئة افرتاضية‪ .‬يستخدم لتهيئة عنارص الحاوية عند‬
‫‪initialization‬‬
‫تحديد حجم وليس ُمهئي عنرص‪ .‬تتم تهيئة العنارص كنسخة من هذه القيمة الناتجة عن املرتجم‪.‬‬

‫‪vector‬‬ ‫نوع مكتبة يحتوي على مجموعة من عنارص نوع محدد‪.‬‬

‫تعرف أنواع املكرر واملؤرشات عامل الزتايد "إلضافة واحد" عن طريق تحريك املكرر لإلشارة إلى‬
‫‪++ operator‬‬
‫العنرص التالي‪.‬‬

‫ينتج ]‪ obj[i‬عنرصا في املوضع ‪ i‬من كائن حاوية‪ .‬عدد الفهارس يبدأ من الصفر ‪ -‬العنرص األول‬
‫‪[ ] operator‬‬ ‫هو العنرص ‪ 0‬والعنرص األخري هو العنرص املفهرس بواسطة ‪ .obj.size()-1‬يقوم الفهرس‬
‫بإعادة كائن‪ .‬إذا كانت ‪ p‬عبارة عن مؤرش و ‪ n‬عدد صحيح ‪ ،‬فإن ]‪ p[n‬مرادف لـ * (‪.)p + n‬‬

‫‪-> operator‬‬ ‫عامل سهم‪ .‬يجمع بني عمليات إزالة املرجع وعوامل النقطة‪ a->b :‬هو مرادف لـ (* ‪.b. )a‬‬

‫‪<< operator‬‬ ‫عامل املخرجات في نوع مكتبة سلسلة‪ .‬عامل السلسلة يطبع األحرف في سلسلة‪.‬‬

‫عامل املدخالت في نوع مكتبة سلسلة‪ .‬يقرأ عامل السلسلة قطع األحرف املحددة بمسافات بيضاء ‪،‬‬
‫‪>> operator‬‬
‫ويخزن ما يقرأ في املعامل األيمن (سلسلة)‪.‬‬

‫‪! operator‬‬ ‫تعيد معكوس القيمة املنطقية ملعاملها‪ .‬تكون النتيجة صحيحة إن كان املعامل خطأ والعكس صحيح‪.‬‬

‫صحيحا‪ .‬يتم تقييم املعامل األيمن فقط إذا كان املعامل‬


‫ً‬ ‫تكون النتيجة صحيحة إذا كان كال املعاملني‬
‫‪&& operator‬‬
‫صحيحا‪.‬‬
‫ً‬ ‫األيرس‬

‫صحيحا‪.‬يتم تقييم املعامل اﻷيمن فقط إذا كان املعامل‬


‫ً‬ ‫النتائج صحيحة إذا كان أي من املعاملني‬
‫‪|| operator‬‬
‫األيرس خطأ‪.‬‬

You might also like