<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; C++</title><link>https://academy.hsoub.com/programming/cpp/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; C++</description><language>ar</language><item><title>&#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x627;&#x644;&#x639;&#x634;&#x631;&#x629; &#x627;&#x644;&#x623;&#x643;&#x62B;&#x631; &#x634;&#x64A;&#x648;&#x639;&#x64B;&#x627; &#x641;&#x64A; &#x644;&#x63A;&#x629; ++C</title><link>https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D8%A9-%D8%A7%D9%84%D8%A3%D9%83%D8%AB%D8%B1-%D8%B4%D9%8A%D9%88%D8%B9%D9%8B%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r2441/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/C.png.311277e3a7f6f141876c0327ea15302d.png" /></p>
<p>
	هناك العديد من العقبات والأخطاء التي قد يتعرض لها مطورو لغة البرمجة ++C، يمكن لهذه العقبات أن تصعب عملية البرمجة وترفع تكاليفها، فتعلم قواعد البرمجة ووجود مهارات في لغات البرمجة الأخرى مثل جافا Java وسي شارب #C ليست كافية وحدها للاستفادة الكاملة من إمكانيات لغة ++C وتجنب الأخطاء.
</p>

<p>
	حيث يتطلب إتقان ++C عدة سنوات من الخبرة العملية لتجنب الوقوع في الأخطاء الدقيقة بسبب عدة عوامل متعلقة بطبيعة اللغة، وسنلقي الضوء في هذه المقالة على أهم الأخطاء التي يقع بها مطورو ++C سواء المبتدئون منهم أو الخبراء إن لم يكونوا حذرين في التعامل مع هذه اللغة.
</p>

<h2 id="newdelete">
	الخطأ 1: الاستخدام الخاطئ للمعاملين <code>new</code> و <code>delete </code>
</h2>

<p>
	بداية سنشرح مشكلة شائعة في <a href="https://academy.hsoub.com/programming/cpp/%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%81%D9%8A-c11-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%AA%D9%87%D8%A7-r1029/" rel="">إدارة الذاكرة في لغة ++C</a>، فمن الصعب جدًا -مهما حاولنا- تحرير كل الذاكرة المخصصة ديناميكيًا، حتى ولو نجحنا في ذلك ستبقى النتيجة غير آمنة وتصدر الاستثناءات أثناء التنفيذ.
</p>

<p>
	سنلقي الضوء هنا على المثال البسيط التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_6" style=""><span class="kwd">void</span><span class="pln"> </span><span class="typ">SomeMethod</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  </span><span class="typ">ClassA</span><span class="pln"> </span><span class="pun">*</span><span class="pln">a </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ClassA</span><span class="pun">;</span><span class="pln">
  </span><span class="typ">SomeOtherMethod</span><span class="pun">();</span><span class="pln">      </span><span class="com">// it can throw an exception</span><span class="pln">
  </span><span class="kwd">delete</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا أصدر هذا الكود البسيط استثناء فلن يتم حذف الكائن <code>a</code> أبدًا. ففي هذا الكود تنشئ الدالة <code>SomeMethod</code> كائن من الصنف ClassA باستخدام التخصيص الديناميكي للذاكرة <code>new ClassA</code>. بعد ذلك، تستدعي الدالة <code>SomeOtherMethod</code> والتي قد تتسبب في حدوث استثناء أثناء تنفيذها. فإذا وقع هذا الاستثناء قبل تنفيذ <code>delete a</code>، فلن يتم تحرير الكائن `` a من الذاكرة، مما يؤدي إلى ما يعرف <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%B3%D8%A7%D8%AF%D8%B3-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-management-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-c-r994/" rel="">بتسريب الذاكرة Memory Leak</a>.
</p>

<p>
	يقدم المثال التالي طريقة أقصر وأكثر أمانًا لتنفيذ هذه المهمة، حيث نستخدم هنا طريقة المؤشر الذكي <code>auto_ptr</code> الذي لم يعد موجودًا في النسخة 11 من<a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel=""> لغة البرمجة ++C</a> ولكنه لا يزال يستخدم على نطاق واسع في النسخ القديمة.
</p>

<p>
	ملاحظة: يمكن استبدال المؤشر <code>auto_ptr</code> في النسخة 11 من ++C بالمؤشر <code>unique_ptr</code> أو المؤشر <code>scoped_ptr</code> من خلال <a href="https://www.boost.org/" rel="external nofollow">المكتبة Boost</a> التي توفر بعض الأدوات المفيدة لإدارة الذاكرة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_8" style=""><span class="kwd">void</span><span class="pln"> </span><span class="typ">SomeMethod</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  std</span><span class="pun">::</span><span class="pln">auto_ptr</span><span class="pun">&lt;</span><span class="typ">ClassA</span><span class="pun">&gt;</span><span class="pln"> a</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ClassA</span><span class="pun">);</span><span class="pln"> </span><span class="com">// deprecated, please check the text</span><span class="pln">
  </span><span class="typ">SomeOtherMethod</span><span class="pun">();</span><span class="pln">      </span><span class="com">// it can throw an exception</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هنا سيحذف الكائن <code>a</code> مباشرة بمجرد انتهاء البرنامج من تنفيذ الكود الموجود بين قوسين.
</p>

<p>
	هذا المثال هو الأبسط للمشكلات التي يمكن تحدث في ++C، فهناك العديد من الأمثلة التي يجب فيها أن يتم الحذف في مكان آخر (مثال: دالة خارجية أو خيط thread آخر)، لذلك لا ينصح أبدًا باستخدام المعاملين <code>new</code> و<code>delete</code> مع بعضهما البعض وإنما يجب استخدام <a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-cpp-r913/" rel="">المؤشرات الذكية</a> بدلًا من ذلك.
</p>

<h2 id="virtualdestructor">
	الخطأ 2: عدم استخدام الهادم الوهمي Virtual Destructor
</h2>

<p>
	هذا من الأخطاء الأكثر شيوعًا التي تؤدي إلى استهلاك حجم الذاكرة داخل الأصناف المشتقة من أصناف أخرى، فعند إنشاء صنف أساسي base class وصنف مشتق derived class يحتوي على ذاكرة مخصصة، فمن الضروري أن تكون دالة الهدم في الصنف الأساسي وهمية virtual لأن ++C تعتمد على نوع المؤشر عند استدعاء الدوال، وبالتالي إذا حذفنا كائنًا من خلال مؤشر إلى صنف أساسي لا يحتوي على دالة هادم وهمية فلن تُستدعى دالة الهدم للصنف المشتق عند حذف الكائن. وهذا يؤدي إلى عدم تحرير الذاكرة بشكل صحيح وظهور مشكلة تسرب الذاكرة.
</p>

<p>
	هناك بعض الحالات التي لا يرغب فيها بوجود دالة الهدم الوهمية virtual destructor، على سبيل المثال عندما يكون الصنف لا يقبل الوراثة وحجمه وأداؤه في غاية الأهمية، حيث تضيف دالة الهدم الوهمية -أو أي دالة وهمية أخرى- بيانات إضافية لبنية الصنف بمعنى أنها تضيف مؤشر إلى جدول وهمي مما يؤدي إلى زيادة حجم الصنف class.
</p>

<p>
	ولما كان من الممكن توريث الأصناف في معظم الحالات -حتى وإن لم نكن نريد توريثها- لذا يفضل أن يضيف المبرمج دالة الهدم الوهمية عند التصريح عن الصنف class. من ناحية أخرى، في حال عدم رغبة المبرمج بإضافة أي دوال وهمية للكود لتعزيز أداء البرنامج فيتوجب عليه وضع ملاحظة عند التصريح عن الصنف تخبر باقي المطورين بأن هذا الصنف يجب أن لا يوسع عن طريق الوراثة منه. ومن أفضل الخيارات لتجنب الوقوع في هذه المشكلة هو استخدام <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">بيئة التطوير المتكاملة IDE</a> التي تدعم إنشاء دالة هدم افتراضية أثناء عملية إنشاء الصنف.
</p>

<p>
	هناك نقطة إضافية أخرى في هذا الموضوع وهي الأصناف والقوالب التي تكون موجودة ضمن المكتبة بشكل ضمني built -in فلا يمكن توريث هذه الأصناف كما أنها لا تحتوي على هادم وهمي افتراضي، على سبيل المثال إذا أنشأنا صنف سلسلة نصية string class فإنه سيرث تلقائيًا من الصنف <code>std::string</code> وهنا تكمن المشكلة ففي حال استخدم أحد المطورين هذا الصنف كمؤشر أو مرجع للصنف <code>std::string</code> فإن هذا سيؤدي إلى تسريب الذاكرة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_10" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">MyString</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string
</span><span class="pun">{</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">MyString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">*</span><span class="pln">s </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyString</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">delete</span><span class="pln"> s</span><span class="pun">;</span><span class="pln"> </span><span class="com">// May not invoke the destructor defined in MyString</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لتجنب الوقوع في مثل هذه المشكلات في لغة ++C، يجب على المطورين استخدام الوراثة الخاصة private inheritance والتي تعتبر الطريقة الأكثر أمانًا لإعادة استخدام الأصناف أو القوالب من المكتبة الأساسية القياسية.
</p>

<h2 id="deletesmartpointer">
	الخطأ 3: حذف مصفوفة باستخدام <code>delete</code> أو باستخدام المؤشر الذكي
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="161550" href="https://academy.hsoub.com/uploads/monthly_2024_11/Deleting-an-Array.png.f8de260395d898a98ef8657d011174e4.png" rel=""><img alt="Deleting-an-Array.png" class="ipsImage ipsImage_thumbnailed" data-fileid="161550" data-ratio="33.50" data-unique="zus6s1qfz" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_11/Deleting-an-Array.thumb.png.fb488f80611892f56eed9b7ae7ecad31.png"></a>
</p>

<p>
	ستحتاج في كثير من التطبيقات العملية إلى إنشاء مصفوفات مؤقتة بحجم متغير وهو ما يسمى المصفوفات المخصصة ديناميكيًا، ويتطلب التعامل مع هذه المصفوفات عناية خاصة لتحرير الذاكرة بعد الانتهاء من استخدامها.
</p>

<p>
	فمن المهم جدًا تحرير الذاكرة المخصصة لهذه المصفوفات بعد الانتهاء من استخدامها، ولكن المشكلة الكبيرة في لغة ++C هي أنها تتطلب عامل الحذف ذي الأقواس المربعة <code>delete[]</code> والذي ينساه المبرمج غالبًأ ، وعامل الحذف لن يقوم بحذف الذاكرة المخصصة للمصفوفة فحسب وإنما سيستدعي في البداية تابع الهدم destructor لجميع الكائنات أو العناصر ضمن المصفوفة.
</p>

<p>
	بالمقابل فإن استخدام أمر الحذف بدون أقواس مربعة <code>[]</code> أمر غير صحيح خاصة بالنسبة للأنواع الأساسية الأولية على الرغم من عدم وجود تابع هدم لهذه الأنواع، لذا يجب الانتباه لاستخدام الأقواس لضمان تحرير الذاكرة بشكل صحيح.
</p>

<p>
	وعند تخصيص مصفوفة ديناميكيًا، يتأكد المصرف compiler عادة من أن المؤشر يشير إلى أول عنصر في المصفوفة ويمكننا الوصول إلى باقي العناصر عن طريق التحرك من خلال هذا المؤشر.لكن لا يضمن كل مترجم وضع المؤشر على العنصر الأول من المصفوفة ولذلك فإن استخدام الكلمة المفتاحية <code>delete</code> بدون أقواس يمكن أن يؤدي إلى سلوك غير متوقع عند تحرير الذاكرة المخصصة للمصفوفة.
</p>

<p>
	إن استخدام المؤشرات الذكية مثل <code>auto_ptr</code> و<code>&lt;unique_ptr&lt;T</code> و <code>shared_ptr</code> أمر غير صحيح أيضًا فعندما يخرج المؤشر الذكي خارج المجال فإنه سيستدعي عامل الحذف بدون أقواس مما يؤدي إلى حدوث المشكلة نفسها التي شرحناها في الفقرة السابقة.
</p>

<p>
	عندما يكون استخدام المؤشر الذكي للمصفوفة أمرًا مطلوبًا، توجب على البرنامج استخدام الصنف <code>scoped_array</code> أو الصنف <code>shared_array</code> من المكتبة Boost أو الصنف <code>&lt;[]unique_ptr&lt;T</code> بالتحديد.
</p>

<p>
	إذا كنا غير محتاجين لاستخدام تقنية إدارة الذاكرة المسماة عد المراجع reference counting عند التعامل مع المصفوفات، فإن الحل الأفضل في هذه الحالة هو استخدام متجهات مكتبة القوالب المعيارية STL بدلًا عنها لأن تلك المتجهات لا تتعامل مع تحرير الذاكرة فحسب وإنما تحتوي على وظائف إضافية مهمة لا مجال لذكرها الآن.
</p>

<h2 id="">
	الخطأ 4: إعادة مرجع إلى كائن محلي داخل دالة
</h2>

<p>
	هناك خطأ شائع يرتكبه المطورون المبتدئون عند التعامل مع المراجع في دوال ++C، وهو إرجاع مرجع إلى كائن محلي أنشأه داخل الدالة. يحدث هذا الخطأ غالبًا من قبل المطورين المبتدئين عند محاولة تحسين الأداء عن طريق تجنب النسخ غير الضروري للكائنات وسنلقي الضوء عليه نظرًا لكمية التعليمات البرمجية القديمة التي تعاني من هذه المشكلة.
</p>

<p>
	لنأخذ المثال التالي والذي يريد المبرمج من خلاله إدخال بعض التحسينات على الكود وتجنب عمليات النسخ غير الضرورية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_12" style=""><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> </span><span class="typ">SumComplex</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Complex</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">…..</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SumComplex</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">);</span></pre>

<p>
	في هذه الحالة سيشير الكائن <code>sum</code> إلى الكائن المحلي <code>result</code> وستعيد الدالة الكائن <code>sum</code>، ولكن السؤال المطروح: أين سيتواجد الكائن <code>result</code> بعد تنفيذ الدالة <code>SumComplex</code>؟.
</p>

<p>
	الجواب هو أن الكائن <code>result</code> كان مخزنًا في المكدس، ولكن بعد تنفيذ الدالة وإرجاع القيمة فإن ذاكرة المكدس ستستخدم لتخزين الكائنات والمتحولات المحلية ضمن الدالة ومن ثم ستدمر كل هذه الكائنات، مما سيؤدي في النهاية إلى نتيجة غير متوقعة، وهذا ينطبق حتى على أنواع البيانات الأساسية.
</p>

<p>
	هناك حالات يمكنك فيها الاستفادة من تقنية تحسين القيمة المعادة Return Value Optimization أو اختصارًا RVO وهي تقنية يستخدمها المصرف compiler لمنع حدوث مشكلات تتعلق بأداء البرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_14" style=""><span class="typ">Complex</span><span class="pln"> </span><span class="typ">SumComplex</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Complex</span><span class="pun">&amp;</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Complex</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">real </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">.</span><span class="pln">real</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">imaginar </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">.</span><span class="pln">imaginar</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">Complex</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SumComplex</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">);</span></pre>

<p>
	بالنسبة للنسخ الحديثة من المصرفات compilers، عندما تتضمن عبارة إرجاع قيمة باني الكائن constructor فإن المصرف سيعمل على تحسين الكود لقائيًا لتجنب عمليات النسخ الزائدة. فبدلاً من إنشاء كائن مؤقت أولاً داخل الدالة ثم نسخه لاحقًا إلى المكان النهائي مثل المتغير sum في الكود أعلاه، يعمل المترجم على إنشاء الكائن النهائي مباشرة في موقعه المطلوب. هذا يعني أن sum سيبنى مباشرة باستخدام القيم المرجعة من الدالة دون الحاجة إلى كائن وسيط أو عملية نسخ إضافية مما يحسن الأداء ويوفر الذاكرة.
</p>

<h2 id="-1">
	الخطأ 5: استخدام مرجع لمورد أو مصدر محذوف
</h2>

<p>
	تحدث مثل هذه الأخطاء في لغة ++C بشكل كبير وخصوصًا في التطبيقات متعددة الخيوط multithread، وللتوضيح سنأخذ الكود التالي:
</p>

<p>
	الخيط رقم 1:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_16" style=""><span class="typ">Connection</span><span class="pun">&amp;</span><span class="pln"> connection</span><span class="pun">=</span><span class="pln"> connections</span><span class="pun">.</span><span class="typ">GetConnection</span><span class="pun">(</span><span class="pln">connectionId</span><span class="pun">);</span><span class="pln">
</span><span class="com">// ...</span></pre>

<p>
	الخيط رقم 2:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_18" style=""><span class="pln">connections</span><span class="pun">.</span><span class="typ">DeleteConnection</span><span class="pun">(</span><span class="pln">connectionId</span><span class="pun">);</span><span class="pln">
</span><span class="com">// …</span></pre>

<p>
	الخيط رقم 1:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_20" style=""><span class="pln">connection</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span></pre>

<p>
	في هذا المثال، ستكون نتيجة التنفيذ غير متوقعة في حال استخدم كلا الخيطين نفس المعرف connection ID، وغالبًا ما يجد المطور صعوبة في إيجاد سبب وقوع أخطاء انتهاك الوصول access violation هنا.
</p>

<p>
	ففي مثل هذه الحالات وعند وصول عدة خيوط إلى نفس المصدر، سيكون من المخاطرة الاحتفاظ بمؤشرات أو مراجع لهذا المورد لأنه يمكن لمؤشر آخر أن يحذفه، ولتجنب هذا علينا استخدام المؤشرات الذكية مع تقنية العد المرجعي والتي تعتبر الطريقة الأكثر أمانًا مثل المؤشر <code>shared_ptr</code> من المكتبة Boost، إذ يستخدم هذا النوع من المؤشرات العمليات الذرية atomic operations لزيادة أو إنقاص عداد المراجع reference counter مما يجعله آمنًا للاستخدام في بيئة متعددة الخيوط.
</p>

<p>
	<strong>ملاحظة</strong>: يتعقب عداد المراجع في البرمجة عدد المراجع لكائن معين في الذاكرة فكلما تم إنشاء مرجع جديد إلى الكائن يزداد بقيمة 1 وكلما تم حذف مرجع ينقص بقيمة 1.
</p>

<h2 id="-2">
	الخطأ 6: السماح برمي توابع الهدم لاستثناءات
</h2>

<p>
	ليس من الضروري أن يرمي تابع الهدم destructor استثناءً، وحتى لو كان ذلك ضروريًا فيمكن إيجاد عدة طرق أفضل للتعامل مع هذه الحالة. إذ لا تصدر توابع الهدم عادة الاستثناءات بشكل متعمد، وإنما يمكن تسجيل أو معرفة الحدث الذي تسبب في وقوع الاستثناء عند تدمير كائن معين في البرنامج للتعامل معه بشكل صحيح، وللتوضيح سنلقي نظرة على المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_22" style=""><span class="kwd">class</span><span class="pln"> A
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   A</span><span class="pun">(){}</span><span class="pln">
   </span><span class="pun">~</span><span class="pln">A</span><span class="pun">()</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
      writeToLog</span><span class="pun">();</span><span class="pln"> </span><span class="com">// could cause an exception to be thrown</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// …</span><span class="pln">

</span><span class="kwd">try</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   A a1</span><span class="pun">;</span><span class="pln">        
   A a2</span><span class="pun">;</span><span class="pln">        
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">exception</span><span class="pun">&amp;</span><span class="pln"> e</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"exception caught"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا حدث الاستثناء في المثال السابق مرتين في نفس الوقت كما في حالة تدمير كلا الكائنين فإن العبارة <code>catch</code> لن تنفذ ابدًا، والسبب في ذلك هو أنه عند وجود استثنائين يعملان على التوازي (في نفس الوقت) سواء كانا من نفس النوع أو من نوعين مختلفين فإن بيئة تشغيل اللغة ++C لن تكون قادرة على تحديد طريقة التعامل معهما، ونتيجة لذلك تستدعي بيئة التشغيل دالة الإنهاء <code>abort</code> مما يؤدي إلى تعطل البرنامج.
</p>

<p>
	تنص القاعدة العامة في هذه الحالة على عدم السماح برمي الاستثناءات من توابع الهدم في البرنامج حتى لو لم تبدو التعليمات البرمجية جذابة من الناحية الجمالية، فمن المهم هنا التعامل مع الاستثناءات المحتملة داخل تابع الهدم لمنعها من الانتشار بشكل أكبر
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_24" style=""><span class="kwd">try</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">    
      writeToLog</span><span class="pun">();</span><span class="pln"> </span><span class="com">// could cause an exception to be thrown</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln"> </span><span class="pun">{}</span></pre>

<h2 id="auto_ptr">
	الخطأ 7: استخدام المؤشر <code>auto_ptr</code> بشكل خاطئ
</h2>

<p>
	توقف دعم المؤشر أو القالب <code>auto_ptr</code> منذ صدور النسخة 11 من لغة البرمجة ++C وذلك لعدة أسباب (لا داعي لذكرها الآن)، ولكنه لا يزال يستخدم على نطاق واسع لأن معظم المشاريع المطورية سابقًا تعمل في النسخة 98++C.
</p>

<p>
	يتمتع القالب <code>auto_ptr</code> بميزة مهمة قد لا يعرفها الكثير من مطوري ++C، وهي أنه عند نسخ كائن من نوع <code>auto_ptr</code>، تنتقل ملكيته من الكائن الأصلي إلى الكائن الجديد. هذا يعني أن الكائن الأصلي يصبح فارغًا ولا يمكن الوصول إلى بياناته أو استخدامه بعد ذلك. إذا لم تستخدم هذا القالب بحذر وبطريقة احترافية، قد يتسبب ذلك في مشكلات كبيرة. للتوضيح ألقِ نظرة على الكود التالي:
</p>

<pre class="ipsCode">auto_ptr&lt;ClassA&gt; a(new ClassA); // deprecated, please check the text
auto_ptr&lt;ClassA&gt; b = a;
a-&gt;SomeMethod();    // will result in access violation error
</pre>

<p>
	سيؤدي الكود السابق إلى خطأ انتهاك الوصول، حيث يحتوي الكائن <code>b</code> فقط على مؤشر للكائن <code>Class A</code> بينما سيكون الكائن <code>a</code> فارغًا، وستؤدي محاولة الوصول إلى الكائن <code>a</code> إلى ظهور خطأ انتهاك الوصول.
</p>

<p>
	توجد عدة طرق لاستخدام الكائن <code>auto_ptr</code> بشكل خاطئ لذا يجب على المطور تذكر الأمور الأربعة التالية: 1.يجب عدم استخدام المؤشر <code>auto_ptr</code> ضمن حاويات مكتبة القوالب المعيارية STL، لأن نسخ الحاويات سيؤدي إلى وضع بيانات غير صحيحة في الحاويات الأصلية. بالإضافة إلى ذلك، قد تتسبب بعض خوارزميات مكتبة القوالب المعيارية في حدوث أخطاء في المؤشرات الخاصة بـ <code>auto_ptr</code>.
</p>

<ol start="2">
	<li>
		<p>
			عدم استخدام المؤشر <code>auto_prt</code> كوسيط دالة لأن هذا سيؤدي إلى نسخ القيمة وستصبح القيمة الأصلية الممررة إلى الوسيط خاطئة بعد استدعاء الدالة.
		</p>
	</li>
	<li>
		<p>
			إذا استخدم المبرمج المؤشر <code>auto_ptr</code> ضمن بيانات الصنف، يجب عليه التأكد من أن عملية النسخ داخل تابع البناء constructor تنجز بشكل صحيح وذلك إما من خلال إنشاء نسخة صحيحة داخل منشئ النسخة copy constructor وعامل الإسناد assignment operator أو عدم السماح بتطبيق هذه العمليات عن طريق جعلها <code>private</code> مما يمنع النسخ غير المقصود للبيانات.
		</p>
	</li>
	<li>
		<p>
			استخدام المؤشرات الذكية الأحدث من المؤشر <code>auto_ptr</code> إذا كان الأمر ممكنًا.
		</p>
	</li>
</ol>

<h2 id="-3">
	الخطأ 8: استخدام المؤشرات والمراجع غير الصالحة
</h2>

<p>
	هذا الخطأ شائع جدًا حتى أن يمكننا تأليف كتاب كامل حوله، حيث تحتوي كل حاوية من مكتبة القوالب المعيارية STL على بعض الحالات الخاصة التي تبطل عمل المؤشرات والمراجع، ومن المهم لكل مبرمج أن يكون على علم بهذه الحالات عند استخدام المؤشرات والمراجع.
</p>

<p>
	يمكن أن تحدث إحدى هذه المشكلات بشكل متكرر في بيئات التطوير التي تمتلك خاصية الخيوط المتعددة multiple threads، ولذلك من الضروري اعتماد آليات المزامنة synchronization mechanisms لمنع حدوث هذه المشكلة.
</p>

<p>
	سنلقي نظرة على الكود التالي الذي يستخدم متجه vector لتخزين السلاسل النصية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_26" style=""><span class="typ">vector</span><span class="str">&lt;string&gt;</span><span class="pln"> v</span><span class="pun">;</span><span class="pln">
v</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(“</span><span class="pln">string1</span><span class="pun">”);</span><span class="pln">
string</span><span class="pun">&amp;</span><span class="pln"> s1 </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">     </span><span class="com">// assign a reference to the 1st element</span><span class="pln">
</span><span class="typ">vector</span><span class="str">&lt;string&gt;</span><span class="pun">::</span><span class="typ">iterator</span><span class="pln"> iter </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">();</span><span class="pln">    </span><span class="com">// assign an iterator to the 1st element</span><span class="pln">
v</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(“</span><span class="pln">string2</span><span class="pun">”);</span><span class="pln">
cout </span><span class="pun">&lt;&lt;</span><span class="pln"> s1</span><span class="pun">;</span><span class="pln">     </span><span class="com">// access to a reference of the 1st element</span><span class="pln">
cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">iter</span><span class="pun">;</span><span class="pln">  </span><span class="com">// access to an iterator of the 1st element</span></pre>

<p>
	للوهلة الأولى ومن وجهة نظر منطقية يتبين لنا أن الكود صحيح ولا يوجد فيه مشكلات، ولكن عند إضافة عنصر ثاني إلى المتجه فسيحتاج لإعادة توضع الذاكرة وهذا يمكن أن يجعل كل من مؤشر ومرجع المتجه غير صالحين (يشيران إلى قيمة أخرى)، ونتيجة لذلك يمكن أن تتسبب محاولة الوصول لهم في السطرين الأخيرين من الكود بعد إضافة العنصر الجديد إلى حدوث خطأ انتهاك الوصول Access Violation لأن المؤشر والمرجع لم يعودا يشيران إلى الأماكن الصحيحة في الذاكرة.
</p>

<h2 id="passinganobjectbyvalue">
	الخطأ 9: تمرير كائن عن طريق القيمة
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="161549" href="https://academy.hsoub.com/uploads/monthly_2024_11/Passing-an-Object-by-Value.png.153af1aebe6a2ecfc07afe4fa437b5b6.png" rel=""><img alt="Passing-an-Object-by-Value.png" class="ipsImage ipsImage_thumbnailed" data-fileid="161549" data-ratio="19.83" data-unique="o598xonbm" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_11/Passing-an-Object-by-Value.thumb.png.2db05409b582820a4cb7fd7196db3f1e.png"></a>
</p>

<p>
	من المعروف لدى معظم المبرمجين أن عملية تمرير الكائن عن طريق القيمة  by value (وهي عبارة عن أخذ نسخة من الكائن وتمريرها إلى الدالة) فكرة سيئة فقد يكون لها تأثير سلبي على أداء البرنامج، ومع ذلك نجدهم يؤجلون معالجة هذا الموضوع إلى وقت لاحق لتجنب كتابة كود إضافي مما يؤدي تقليل فاعلية الكود المكتوب وجعله يتسبب في لمشكلات غير متوقعة
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_28" style=""><span class="kwd">class</span><span class="pln"> A
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="typ">GetName</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">return</span><span class="pln"> </span><span class="str">"A"</span><span class="pun">;}</span><span class="pln">
    </span><span class="pun">…</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> B</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> A
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="typ">GetName</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">return</span><span class="pln"> </span><span class="str">"B"</span><span class="pun">;}</span><span class="pln">
   </span><span class="pun">...</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> func1</span><span class="pun">(</span><span class="pln">A a</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   std</span><span class="pun">::</span><span class="pln">string name </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">.</span><span class="typ">GetName</span><span class="pun">();</span><span class="pln">
   </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

B b</span><span class="pun">;</span><span class="pln">
func1</span><span class="pun">(</span><span class="pln">b</span><span class="pun">);</span></pre>

<p>
	يصرّف هذا الكود بشكل صحيح وبدون مشكلات، ومع ذلك فإن الدالة <code>func1</code> ستنسخ جزء من الكائن <code>b</code> الذي ينتمي إلى الصنف <code>A</code> ضمن الكائن <code>a</code> عند استدعائها، وهذا ما يعرف باسم مشكلة التقطيع slicing problem.
</p>

<p>
	بالنتيجة سوف يستدعي البرنامج عند تنفيذه التابع من الصنف <code>A</code> بدلًا من الصنف <code>B</code>، ويعتبر هذا سلوكًا غير متوقع للشخص الذي يستدعي الدالة.
</p>

<p>
	تحدث مشكلة أخرى مشابهة عند محاولة التقاط catch الاستثناء كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_30" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">ExceptionA</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">exception</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ExceptionB</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ExceptionA</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">try</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   func2</span><span class="pun">();</span><span class="pln"> </span><span class="com">// can throw an ExceptionB exception</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">ExceptionA</span><span class="pln"> ex</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    writeToLog</span><span class="pun">(</span><span class="pln">ex</span><span class="pun">.</span><span class="typ">GetDescription</span><span class="pun">());</span><span class="pln"> 
    </span><span class="kwd">throw</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في المثال السابق، عندما تطلق الدالة <code>func2</code> استثناء من النوع <code>ExceptionB</code>، سيتلقط الاستثناء ويعالج في كتلة <code>try/catch</code>. ولكن بسبب مشكلة النسخ، ينسخ الاستثناء إلى كائن من النوع الأساسي <code>ExceptionA</code> بدلاً من النوع الفرعي <code>ExceptionB</code>. هذا يؤدي إلى فقدان المعلومات الخاصة بنوع الاستثناء الفرعي مما يتسبب في استدعاء تابع غير صحيح. وعند إعادة رمي الاستثناء باستخدام <code>throw</code>، سيرمى استثناء من النوع <code>ExceptionA</code> بدلاً من النوع الأصلي <code>ExceptionB</code>.
</p>

<p>
	النصيحة في نهاية هذه الفكرة هي يجب على المبرمج تمرير الكائنات حسب المرجع reference وليس حسب القيمة وأن يلتقط الاستثناءات بالمرجع <code>&amp;</code> وليس بالقيمة. هذا يحافظ على نوع الاستثناء الفرعي ويمنع المشاكل التي تحدث عند فقدان التفاصيل الخاصة بالاستثناء.
</p>

<h2 id="-4">
	الخطأ 10: استخدام التحويلات المعرفة من المستخدم عن طريق توابع البناء وعوامل التحويل
</h2>

<p>
	تشير التحويلات المخصصة أو المعرفة من قبل المستخدم في ++C إلى إمكانية تعريف تحويلات مخصصة بين أنواع البيانات المختلفة، يمكن إنشاء هذه التحويلات باستخدام توابع البناء constructors وعوامل التحويل conversion operators في الأصناف.
</p>

<p>
	وبشكل عام فإن هذا النوع من التحويلات يمنح المبرمج تحكمًا أكبر في طريقة تحويل الكائنات من أنواع مختلفة في كود ++C، وهو مفيد جدًا في بعض الأحيان لكنه قد يؤدي إلى تحويلات غير متوقعة يكون من الصعب تحديد موقعها. لنفترض أن أحدهم أنشأ مكتبة تحتوي على صنف <code>string</code> فيه دالتا بناء لإنشاء السلاسل النصية الأولى وسيطها عدد والثانية وسيطها محارف كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_32" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   </span><span class="typ">String</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
   </span><span class="typ">String</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">….</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستطيع من خلال التابع الأول إنشاء سلسلة نصية طولها n، ونستطيع من خلال التابع الثاني إنشاء سلسلة نصية تحتوي على الحروف المدخلة من المستخدم، ولكن تبدأ المشكلة عندما يكون لدينا كود مثل الكود التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5103_34" style=""><span class="typ">String</span><span class="pln"> s1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln">
</span><span class="typ">String</span><span class="pln"> s2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">‘</span><span class="pln">abc</span><span class="pun">’;</span></pre>

<p>
	في المثال الأول أعلاه، ستستدعى دالة البناء الأولى التي تنشئ سلسلة نصية <code>s1</code> حجمها هو 123 محرف وليس سلسلة تحتوي على الأحرف <code>123</code>، أما المثال الثاني فيحوي على علامات اقتباس مفردة بدلًا من علامات اقتباس مزدوجة والتي ستؤدي إلى استدعاء دالة البناء الأولى أيضًا وإنشاء سلسلة نصية ذات حجم كبير جدًا (لن يفهم 'abc' كسلسلة نصية).
</p>

<p>
	توضيح عند استخدام علامات الاقتباس الفردية يعامل كل حرف على أنه قيمة محرفية character literal، وتمثل قيمة كل حرف بناءً على ترميز الحرف وبالتالي ستحول <code>'abc'</code> إلى قيمة عددية باستخدام قيمة ASCII المقابلة لكل حرف في السلسلة وتدمج معًا لتكوين قيمة عددية كبيرة الحجم.
</p>

<p>
	تعتبر هذه الأمثلة بسيطة جدًا، وهناك العديد من الأمثلة الأكثر تعقيدًا التي تؤدي إلى الالتباس وحدوث نتائج غير متوقعة التي قد يكون من الصعب تحديدها أو التنبؤ بها.
</p>

<p>
	توجد قاعدتان بشكل عام لتجنب حدوث هذه المشكلات:
</p>

<ol>
	<li>
		تعريف التابع المنشأ باستخدام الكلمة المفتاحية <code>explicit</code> لمنع حدوث التحويلات الضمنية
	</li>
	<li>
		استخدام توابع تحويل صريحة بدلًا من استخدام عوامل التحويل، وهذا الأمر يتطلب جهد إضافي في كتابة الكود ولكنه أبسط في القراءة ويمكن أن يساعد في تجنب النتائج غير المتوقعة.
	</li>
</ol>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	تعتبر لغة ++C لغة قوية جدًا، وفي الحقيقة فإن العديد من التطبيقات التي نستخدمها يوميًا مبنية على لغة ++C. وبالرغم من أن لغة البرمجة ++C توفر مرونة كبيرة للمطورين من خلال ما توفره من ميزات متطورة وباعتبارها من لغات <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-oop/" rel="">البرمجة كائنية التوجه</a>، إلا أن هذه الميزات المتقدمة يمكن أن تسبب بعض الالتباس والإحباط لبعض المطورين إذا لم تستخدم بشكل صحيح واحترافي. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في <a href="https://academy.hsoub.com/questions/" rel="">قسم الأسئلة والأجوبة</a> في أكاديمية حسوب.<br>
	ترجمة وبتصرف للمقال <a href="https://www.toptal.com/c-plus-plus/top-10-common-c-plus-plus-developer-mistakes" rel="external nofollow">Top 10 Most Common C++ Mistakes That Developers Make </a>لكاتبه <a href="https://www.toptal.com/resume/vatroslav-bodrozic" rel="external nofollow">Vatroslav Bodrozic</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%AA%D8%B4%D8%AE%D9%8A%D8%B5%D9%87%D8%A7-r1191/" rel="">تحسين الشيفرات المكتوبة بلغة Cpp وتشخيصها</a><span style="display: none;"> </span>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%A3%D9%87%D9%85-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-r1186/" rel="">أنظمة التصريف المستخدمة لبناء البرامج المكتوبة بلغة ++C وأهم أخطاء عملية البناء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A8%D8%B3%D8%A7%D8%B7%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-r1870/" rel="">قواعد البرمجة ببساطة للمبتدئين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D8%A9-%D8%A7%D9%84%D8%A3%D9%83%D8%AB%D8%B1-%D8%B4%D9%8A%D9%88%D8%B9%D9%8B%D8%A7-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-php-r2434/" rel="">الأخطاء العشرة الأكثر شيوعًا في شيفرة PHP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D8%A9-%D8%A7%D9%84%D8%A3%D9%83%D8%AB%D8%B1-%D8%B4%D9%8A%D9%88%D8%B9%D9%8B%D8%A7-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-python-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r2429/" rel="">الأخطاء العشرة الأكثر شيوعًا في شيفرة بايثون Python</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2441</guid><pubDate>Tue, 05 Nov 2024 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x628;&#x64A;&#x646; &#x644;&#x63A;&#x629; C++&#x200E; &#x648; &#x644;&#x63A;&#x629; &#x62C;&#x627;&#x641;&#x627; Java</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%A8%D9%8A%D9%86-%D9%84%D8%BA%D8%A9-c%E2%80%8E-%D9%88-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r2321/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_05/----C_----Java.png.0ab7b0140180c4426ea7354cf28f0502.png" /></p>
<p>
	لا شك أن اختيار لغة البرمجة المناسبة لتطبيقاتك وبرامجك أمرٌ صعب، لاسيما عندما لا تملك خبرة عميقة بالخيارات المتاحة، وفي مقال اليوم نعقد لك مقارنة شاملة بين لغتي C++‎‎ وجافا ونستكشف أبرز الاختلافات الأساسية بينهما، وما هي أبرز العوامل التي عليك أخذها بعين الاعتبار عند اختيارك لإحديهما.
</p>

<p>
	هناك الكثير من المقالات التي تقارن بين المميزات التقنية للغتي C++‎‎ وجافا، ولكنها لا توضح لك بشكل دقيق ما هي الجوانب التي ستؤثر بها هذه الاختلافات، على سبيل المثال، لا تدعم لغة جافا الوراثة المتعددة بينما تدعمها لغة C++‎، حسنًا ماذا يعني ذلك لي كمطور؟ وهل هذا أمر جيد أم لا؟ لماذا يعتبر البعض بأن هذه ميزة في جافا، بينما يعتبرها آخرون مشكلة.
</p>

<p>
	دعنا نستكشف كافة الحالات التي يجب على المطورين فيها اختيار لغة C++‎‎ أو جافا أو ربما لغة أخرى في <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-r1827/" rel="">تطوير التطبيقات</a>، ولماذا يعد اختيار اللغة الصحيحة أمرًا مهمًا لنجاح مشروعك البرمجي.
</p>

<h2 id="c">
	الفروقات الأساسية بين C++‎‎ وجافا: بناء اللغة والنظام البيئي
</h2>

<p>
	أُُطلِقت <a href="https://academy.hsoub.com/programming/cpp/" rel="">لغة C++</a>‎‎ عام 1985 كواجهة أمامية <a href="https://academy.hsoub.com/programming/c/" rel="">للغة سي C</a> فهي تستخدم الكثير من مفاهيم وبنية لغة C، ولكنها أيضًا تضيف ميزات جديدة وتحسينات عليها، وهذا يشبه إلى حد ما طريقة ترجمة لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> إلى جافا سكريبت <a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript </a>. عادةً ما تقوم مترجمات C++‎‎ الحديثة بترجمة أكواد C++‎‎ إلى لغة الآلة الأصلية، وعلى الرغم من أن البعض يرى أن مترجمات C++‎‎ تقلل من قابلية التنقل بين المنصات، وتتطلب إعادة بناء البرامج للتكيف مع المعماريات الحديثة للحواسيب، إلا أن كود C++‎‎ يعمل على جميع منصات المعالجات تقريبًا.
</p>

<p>
	أما لغة جافا فقد صدرت لأول مرة في عام 1995، وهي تتميز بأنها لا تترجم مباشرةً إلى لغة الآلة الأصلية Native Machine Code، بل تترجم بدلاً من ذلك إلى لغة وسيطة تسمى شيفرة البايت bytecode، وهي لغة ذات تمثيل ثنائي مخصصة للعمل على آلة جافا الافتراضية <a href="https://academy.hsoub.com/programming/java/%D8%A2%D9%84%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-java-virtual-machine-r964/" rel="">JVM</a> أي بعبارة أخرى، يحتاج مترجم جافا الذي يترجم كود لغة جافا إلى كود يفهمه الحاسوب إلى ملف تشغيلي أصيل خاص بكل منصة أو نظام التشغيل كي يعمل بشكل صحيح. لذا، تختلف جافا عن بعض لغات البرمجة الأخرى من حيث أن كودها المصدري لا يتحول مباشرة إلى برنامج قابل للتشغيل بل يحول إلى كود البايت أولًا والذي يحتاج تنفيذه لبرنامج آخر يسمى آلة جافا الافتراضية.
</p>

<p>
	وتصنف كل من لغة C++‎‎ و جافا بأنها من عائلة اللغات الشبيهة بلغة سي C، حيث أنهما تشبهان لغة C بشكل عام في تراكيبها اللغوية وبنية التعليمات البرمجية، لكن يكمن الفرق الأبرز بينما في البيئة التشغيليلة الخاصة بكل لغة، حيث يمكن للغة C++‎‎ استدعاء المكتبات المستندة إلى C أو ++C، أو استدعاء واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel=""><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a></abbr> لنظام التشغيل بسهولة كبيرة، أما لغة جافا فهي ملاءمةً بشكل أفضل للمكتبات المعتمدة على لغة جافا، ويمكن الوصول إلى مكتبات C في جافا باستخدام واجهة برمجة التطبيقاتJava Native Interface أو اختصارًا JNI، لكن هذا قد يتسبب في وقوع بعض الأخطاء ويتطلب كتابة بعض أكواد C أو ++C، كما تتفاعل لغة C++‎‎ مع الأجهزة بسهولة أكبر من لغة جافا لكونها أقرب للعتاد وتعد أقرب إلى <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B3%D8%AA%D9%88%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة منخفضة المستوى</a>.
</p>

<h2 id="c-1">
	الفروقات التفصيلية بين لغة C++‎‎ وجافا: الأنواع المعمعة وإدارة الذاكرة وغيرها
</h2>

<p>
	يمكننا مقارنة لغة C++‎‎ بلغة جافا من جوانب عديدة، ففي بعض الحالات يكون قرار اختيار لغة C++‎‎ أو لغة جافا واضحًا وسهلًا، على سبيل المثال في حال أراد مطور ما <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%86%D8%AF%D8%B1%D9%88%D9%8A%D8%AF-r1802/" rel="">برمجة تطبيقات أصيلة لنظام التشغيل أندرويد</a> ففي الغالب سيختار لغة جافا لهذه المهمة، إلا في حال كان التطبيق المطلوب عبارة عن <a href="https://academy.hsoub.com/programming/game-development/%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%AA%D8%B7%D9%88%D8%B1%D9%87%D8%A7-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%88%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%AA%D9%87%D8%A7-r2290/" rel="">لعبة فيديو</a><span ipsnoautolink="true">،</span> ففي هذه الحالة سيختار لغة C++‎‎ أو لغة أخرى من <a href="https://academy.hsoub.com/programming/game-development/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8/" rel="">لغات برمجة الألعاب</a> تساعده على الحصول على رسوم متحركة أكثر انسيابيةً في الزمن الحقيقي، فغالبًا ما تسبب إدارة الذاكرة في جافا في حدوث تأخير غير محبب أثناء اللعب. أما بالنسبة لتطوير التطبيقات متعددة المنصات التي ليست ألعابًا فهي خارج نطاق المناقشة اليوم، إذ لا تعد لا لغة C++‎‎ ولا لغة جافا مثاليتين في هذه الحالة لأنهما لغتان تحتاجان لكتابة تعليمات برمجية طويلة جدًا لتطوير واجهة المستخدم الرسومية، ولا تساعد المطورين على برمجتها بكفاءة، وبالنسبة إلى التطبيقات عالية الأداء من الأفضل إنشاء وحدات C++‎‎ للقيام بالأعمال الثقيلة، واستخدام لغة أكثر إنتاجية لتطوير الواجهة الأمامية لهذه التطبيقات.
</p>

<p>
	وبما أن اختيار اللغة الأنسب قد لا يكون واضحًا بالنسبة لبعض المشاريع، لذلك دعونا نقارن بين اللغتين بتفصيل أكبر من خلال الجدول التالي:
</p>

<table>
	<thead>
		<tr>
			<th>
				الخاصية
			</th>
			<th>
				C++‎‎
			</th>
			<th>
				جافا
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				سهولة الاستخدام للمبتدئين
			</td>
			<td>
				لا
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				أداء زمن التشغيل
			</td>
			<td>
				ممتاز
			</td>
			<td>
				جيد
			</td>
		</tr>
		<tr>
			<td>
				التأخير Latency
			</td>
			<td>
				قابل للتنبؤ
			</td>
			<td>
				غير قابل للتنبؤ
			</td>
		</tr>
		<tr>
			<td>
				المؤشرات الذكية للعد المرجعي Reference-counting smart pointers
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				أسلوب جمع وتحرير القمامة أو البيانات المهملة في الذاكرة
			</td>
			<td>
				لا
			</td>
			<td>
				مطلوب
			</td>
		</tr>
		<tr>
			<td>
				تخصيص ذاكرة المكدس Stack memory allocation
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				الترجمة إلى ملف أصيل قابل للتنفيذ
			</td>
			<td>
				نعم
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				الترجمة إلى شيفرة البايت bytecode
			</td>
			<td>
				جافا Compilation to Java bytecode
			</td>
			<td>
				لا
			</td>
		</tr>
		<tr>
			<td>
				التفاعل المباشر مع واجهات برمجة تطبيقات نظام التشغيل منخفضة المستوى
			</td>
			<td>
				نعم
			</td>
			<td>
				يتطلب كود C
			</td>
		</tr>
		<tr>
			<td>
				التفاعل المباشر مع مكتبات C
			</td>
			<td>
				نعم
			</td>
			<td>
				يتطلب كود C
			</td>
		</tr>
		<tr>
			<td>
				التفاعل المباشر مع مكتبات جافا
			</td>
			<td>
				من خلال JNI
			</td>
			<td>
				نعم
			</td>
		</tr>
		<tr>
			<td>
				البناء وإدارة الحزم بشكل موحد
			</td>
			<td>
				لا
			</td>
			<td>
				Maven
			</td>
		</tr>
	</tbody>
</table>

<p>
	إضافة إلى الميزات التي قارنها في الجدول أعلاه، سنركز أيضًا على ميزات أخرى مثل البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> مثل الوراثة المتعددة multiple inheritance، والأنواع المعممة generics، والقوالب templates، والانعكاس reflection. فكلا اللغتين مثلًا تدعمان البرمجة كائنية التوجه مع فرق بسيط بينهما حيث تفرض لغة جافا استخدام نموذج البرمجة كائنية التوجه، بينما تدعم لغة C++‎‎ البرمجة كائنية التوجه إلى جانب نماذج برمجة أخرى.
</p>

<h2 id="">
	الوراثة المتعددة
</h2>

<p>
	الوراثة هي أحد مبادئ البرمجة كائنية التوجه وهي تمكن صنف ابن child class من وراثة الواصفات attributes والتوابع methods من صنف أب parent class. وأحد الأمثلة القياسية على مبدأ الوراثة هو صنف المستطيل Rectangle الذي يرث من صنف الشكل Shape الأكثر عمومية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_860_6" style=""><span class="com">// Note that we are in a C++‎‎ file</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// Position</span><span class="pln">

  </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">

  </span><span class="com">// The child class must override this pure virtual function</span><span class="pln">

  </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="pun">};</span><span class="pln">



</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// Width and height</span><span class="pln">

  </span><span class="typ">int</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> h</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">

  </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">();</span><span class="pln">

</span><span class="pun">};</span></pre>

<p>
	تحدث الوراثة المتعددة عندما يرث صنف ابن من عدة أصناف آباء بذات الوقت. فيما يلي مثال يستخدم صنف المستطيل Rectangle وصنف الشكل Shape وصنف إضافي Clickable:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_860_8" style=""><span class="com">// Not recommended</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{...};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{...};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Clickable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="typ">int</span><span class="pln"> xClick</span><span class="pun">,</span><span class="pln"> yClick</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">

  </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> click</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="pun">};</span><span class="pln">


</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ClickableRectangle</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Clickable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">void</span><span class="pln"> click</span><span class="pun">();</span><span class="pln">

</span><span class="pun">};</span></pre>

<p>
	لدينا في هذه الحالة صنفان أساسيان هما صنف الشكل <code>Shape</code> (الذي يمثل الصنف الأساسي أو الصنف الأب للمستطيل <code>Rectangle</code>) والصنف الأساسي <code>Clickable</code> والصنف <code>ClickableRectangle</code> الذي يرث من كل من الصنفين السابقين ويملك خصائص نوعي الكائنات التي يولدها كل صنف. تدعم لغة C++‎‎ الوراثة المتعددة، بينما لا تدعم لغة جافا هذه الميزة، وتعد ميزة الوراثة المتعددة مفيدة في بعض حالات المحددة مثل:
</p>

<ul>
	<li>
		إنشاء لغة تخصصية Domain-Specific Language أواختصارًا DSL للعمل في مجال معين ولحل مشكلة مخصصة.
	</li>
	<li>
		إجراء حسابات معقدة في وقت تصريف البرنامج كما في تطبيقات الزمن الحقيقي.
	</li>
	<li>
		تحسين سلامة المشروع البرمجي وضمان أمنه بطرق لا يمكن تحقيقها في لغة جافا.
	</li>
</ul>

<p>
	ومع ذلك، يُنصح عمومًا بعدم استخدام الوراثة المتعددة إلا عند الضرورة لكونها قد تعقد الكود البرمجي.
</p>

<h2>
	الأنواع المعممة والقوالب
</h2>

<p>
	تعمل الإصدارات المعممة <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-programming-r1406/" rel="">Generics</a> للأصناف مع أي نوع من أنواع البيانات وهي تسهل إعادة استخدام الكود البرمجي، وتدعم لغة جافا هذه الميزة عن طريق <a href="https://academy.hsoub.com/programming/go/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generics-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%AC%D9%88-go-r2206/" rel="">الأنواع المعممة</a> في حين تدعم لغة C++‎‎ هذه الميزة عن طريق القوالب <a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%84%D8%A8-templates-%D9%81%D9%8A-cpp-r971/" rel="">templates</a>، ولكن مرونة التعامل مع قوالب C++‎‎ تجعل عملية البرمجة أكثر أمنًا وقوة. حيث يمكن لمترجمات C++‎‎ إنشاء أصناف أو وظائف مخصصة جديدة في كل مرة تستخدم فيها أنواعًا مختلفة مع القالب، كما يمكن لقوالب C++‎‎ استدعاء وظائف أو دوال مخصصة بناءً على أنواع وسطاء الدالة، مما يسمح بتخصيص شيفرات مخصصة لأنواع بيانات معينة بدلًا من إنشاء إصدار واحد يناسب جميع أنواع البيانات، يطلق على هذه الميزة اسم تخصيص القالب template specialization.
</p>

<p>
	لا تملك لغة جافا ميزة مكافئة لهذه الميزة. بل على العكس تمامًا عند استخدام الأنواع المعممة generics في لغة جافا، تنشئ مترجمات لغة جافا كائنات عامة دون أنواع محددة من خلال عملية تسمى شطب النوع type erasure. وتجري عملية التحقق من النوع أثناء عملية الترجمة، ولكن لا يمكن للمبرمجين تعديل سلوك صنف أو تابع عام بناءً على نوع وسطائه.
</p>

<p>
	وكي تتمكن من فهم هذا الأمر بشكل أفضل، انظر سريعًا على الدالة العامة التالية <code>format</code>:<br>
	<code>std::string format(std::string fmt, T1 item1, T2 item2)</code><br>
	تستخدم هذه الدالة قالب <code>template&lt;class T1, class T2&gt;</code>‎‎ من مكتبة C++‎‎ تم إنشاؤها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_860_13" style=""><span class="pln">std</span><span class="pun">::</span><span class="pln">string firstParameter </span><span class="pun">=</span><span class="pln"> </span><span class="str">"A string"</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> secondParameter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln">
</span><span class="com">// Format printed output as an eight-character-wide string and a hexadecimal value</span><span class="pln">
format</span><span class="pun">(</span><span class="str">"%8s %x"</span><span class="pun">,</span><span class="pln"> firstParameter</span><span class="pun">,</span><span class="pln"> secondParameter</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Format printed output as two eight-character-wide strings</span><span class="pln">
format</span><span class="pun">(</span><span class="str">"%8s %8s"</span><span class="pun">,</span><span class="pln"> firstParameter</span><span class="pun">,</span><span class="pln"> secondParameter</span><span class="pun">);</span></pre>

<p>
	تنشأ الدالة العامة format في لغة C++‎‎ على النحو التالي (لاحظ أننا حددنا نوع وسطاء الدالة ليكون نص string ثم عدد صحيح int)
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_860_15" style=""><span class="pln">std</span><span class="pun">::</span><span class="pln">string format</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string fmt</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string item1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item2</span><span class="pun">)</span></pre>

<p>
	بينما تنشأ هذه الدالة في لغة جافا دون تحديد أنواع الكائنات <code>string</code> و <code>int</code> الخاصة بوسطاء الدالة<code>item1</code> و <code>item2</code> وفي هذه الحالة، يعرف القالب في لغة C++‎‎ أن آخر معامل مرسل هنا هو عدد صحيح int، وبالتالي يمكنه إجراء عملية تحويله لنص <code>std::to_string</code> المطلوب في الاستدعاء الثاني للدالة <code>format</code>.
</p>

<p>
	وبدون استخدام القوالب، قد تؤدي عبارة <code>printf</code> في لغة C++‎‎ والتي تحاول طباعة رقم على هيئة سلسلة نصية كما في الاستدعاء الثاني للدالة <code>format</code>في الكود السابق إلى سلوك غير محدد وقد تتسبب في تعطل التطبيق أو طباعة قيم غير صحيحة. من ناحية أخرى،ستكون الدالة في جافا قادرة على التعامل مع الرقم كنص في الاستدعاء الأول، ولكن لن تنسقه على هيئة قيمة ست عشرية مباشرةً.
</p>

<p>
	وعلى الرغم من بساطة هذا المثال، لكنه يوضح قدرة لغة C++‎‎ على تعريف واختيار قالب مخصص للتعامل مع أي كائن من الصنف بشكل ديناميكي دون الحاجة لتعديل الصنف أو تعديل كود الدالة format، ويمكنك الحصول على نفس النتيجة في لغة جافا باستخدام ميزة تسمى الانعكاس reflection كبديل على استخدام الأنواع المعممة generics، ولكن هذه الطريقة أقل مرونة وأكثر عرضة للأخطاء.
</p>

<h2 id="reflection">
	الانعكاس Reflection
</h2>

<p>
	تمكنك لغة البرمجة جافا من معرفة واستكشاف كافة تفاصيل بنية الكائن (في وقت التشغيل)، حيث يمكنك معرفة ما هي الأعضاء المتاحة في صنف أو نوع صنف معين. وتُسمى هذه الميزة الانعكاس Reflection لكونها تشبه رفع مرآة أمام الكائن لرؤية كل ما يوجد بداخله.
</p>

<p>
	في حين لا تمتلك لغة C++‎ ميزة الانعكاس بالكامل، ولكن تمكنك إصدارات C++‎‎ الحديثة من الحصول على معلومات عن نوع الصنف واكتشاف نوع صنف محدد في وقت تنفيذ الكود، لكنها غير قادرة على الوصول إلى معلومات أخرى مثل معلومات حول خصائص الصنف. ‎‎‎
</p>

<h2 id="-1">
	إدارة الذاكرة
</h2>

<p>
	تختلف لغتا جافا C++‎ كذلك في الأسلوب المتبع في إدارة الذاكرة والتي تتبع عادة أحد طريقتين رئيسيتين الأولى هي الطريقة اليدوية التي يتوجب فيها على المطورين تتبع الذاكرة وتحريرها يدويًا، والطريقة الثانية هي الطريقة الآلية ، حيث يقوم البرنامج بتتبع ومراقبة الكائنات التي لا تزال قيد الاستخدام في الذاكرة، فإذا لم يعد أي كائن قيد الاستخدام، سيعتبر قمامة ويمكن في هذه الحالة إعادة استخدام الذاكرة التي كان يحجزه ويستخدمها، ويسمى هذا الأسلوب في إدارة الذاكرة جمع القمامة أو كنس البيانات المهملة garbage collection.
</p>

<p>
	تستخدم لغة جافا هذا الأسلوب الذي يمكنها من إدارة الذاكرة بسهولة أكبر من الطريقة اليدوية، كما أنه يساعد في منع وقوع أخطاء تحرير الذاكرة التي تسهم غالبًا في حدوث ثغرات أمنية . من ناحية أخرى لا توفر لغة C++‎‎ طريقة إدارة الذاكرة الآلية بشكل مدمج لكنها تدعم نوعًا من أنواع كنس البيانات يطلق عليه اسم <a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-cpp-r913/" rel="">المؤشرات الذكية smart pointers</a> حيث تستخدم المؤشرات الذكية أسلوبًا يسمى عدّ المراجع وهي طريقة آمنة وفعالة في حال استخدامها بشكل صحيح، كما تقدم لغة C++‎‎ أيضًا مفهوم الهودام destructors التي تقوم بتنظيف أو تحرير الموارد المخصصة للكائن عن حذفه من الذاكرة.
</p>

<p>
	وتقدم لغة جافا تخصيص الذاكرة بالطريقة الآلية وفق أسلوب يسمى تخصيص الذاكرة المعتمد على الكومة heap allocation (باستخدام الدالة <code>new</code> أو <code>delete</code> أو الدالة <code>malloc</code> الأقدم التي أتت من لغة C) وتخصيص الذاكرة المعتمد على المكدس stack allocation. ويمكن القول بأن تخصيص الذاكرة المعتمد على المكدس أسرع وأكثر أمانًا من تخصيص الذاكرة المعتمد على الكومة، لأن المكدس هو بنية بيانات خطية في حين أن الكومة هو بينة بيانات شجرية، لذا فإن أسلوب ذاكرة المكدس أسهل بكثير من ناحية التعامل مع تخصيص وتحرير الذاكرة.
</p>

<p>
	وهناك أيضًا ميزة أخرى لصالح لغة C++‎‎ تتعلق بتخصيص الذاكرة بالاعتماد على المكدس وهي استخدام تقنية برمجية معروفة باسم Resource Acquisition Is Initialization أو اختصارًا RAII أي أن الحصول على المورد يجب أن يحدث عند إنشاء وتهيئة الكائن، وتربط الموارد مثل المراجع references بدورة حياة الكائن الذي يتحكم فيها، وتحذف أو تحرر في نهاية دورة حياة هذا الكائن.
</p>

<p>
	يُحرر مرجع المؤشر الذكي المشار إليه في أعلى الدالة تلقائيًا عند الخروج من الدالة. كما تُحرر الذاكرة المرتبطة به إذا كان هذا هو المرجع الأخير إلى المؤشر الذكي. وبهذه الطريقة تعمل المؤشرات الذكية في C++‎‎ دون الحاجة إلى تحرير المراجع يدويًا، وعلى الرغم من أن لغة جافا تتبع طريقة مشابهًا لهذا الأسلوب في تحرير الذاكرة، إلا أن الأمور تنجز بطريقة أصعب من تقنية تخصيص المورد عند إنشاء الكائن RAII المستخدمة في لغة C++‎‎ خاصةً إذا كنت بحاجة إلى إنشاء عدة موارد في نفس كتلة التعليمات البرمجية.
</p>

<h2 id="-2">
	الأداء في وقت التشغيل
</h2>

<p>
	يدل هذا المفهوم على مدى سرعة تشغيل البرنامج وفي هذا الصدد تملك لغة جافا أداء تشغيل جيد، ولكن تظل لغة C++‎‎ أعلى منها في الأداء لأن إدارة الذاكرة اليدوية أسرع من تقنية جمع القمامة. وبالرغم من ذلك يمكن أن تتفوق لغة جافا في الأداء على C++‎‎ وذلك في حالات خاصة معينة بسبب ميزة الترجمة اللحظية للغة Just-In-Time أو اختصارًا JIT
</p>

<p>
	وبشكل مخصص تُشغّل مكتبة الذاكرة القياسية في جافا جمع القمامة بشكل كبير، مقارنة بالتخصيص الأقل لذاكرة الكومة Heap في ++C لكن تبقى لغة جافا لغة سريعة نسبيًا ومقبولة الأداء في التطبيقات التي لا تكون فيها سرعة الاستجابة أمرًا أساسيًا مثل الألعاب أو التطبيقات التي تتطلب استجابة في الزمن الحقيقي.
</p>

<h2 id="-3">
	بناء وإدارة الحزم
</h2>

<p>
	يمكن التغاضي عن نقص أداء لغة جافا مقابل سهولة الاستخدام التي توفرها لا سيما في إدارة الحزم وطريقة بناء التطبيقات وإضافة التبعيات الخارجية إليها. حيث توفر لغة جافا أداة تسمى Maven تبسط العمل على المشاريع وتمكنك من إداراتها ببضع خطوات سهلة كما أنها تتكامل مع العديد من <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">بيئات التطوير المتكاملة </a>مثل IntelliJ IDEA.
</p>

<p>
	أما في لغة ++C فلا يوجد مستودع حزم قياسي ولا أسلوب موحد لبناء كود تطبيقات C++‎‎ فبعض المطورين يفضلون استخدام محرر فيجوال استوديو Visual Studio، بينما يستخدم آخرون أداة <a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%A3%D9%87%D9%85-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-r1186/" rel="">CMake</a> أو مجموعة أدوات مخصصة أخرى، كما أن بعض مكتبات C++‎‎ التجارية المتنوعة غير مفتوحة المصدر تكون متوفرة بتنسيق ثنائي، ولا يوجد أسلوب ثابت لدمج تلك المكتبات في التطبيقات، وقد تتسبب التغييرات في إعدادات البناء أو إصدارات المُصرّف مشكلات وأخطاء تحول دون عمل هذه المكتبات الثنائية بشكل صحيح.
</p>

<h2 id="-4">
	ملاءمة اللغة للمبتدئين
</h2>

<p>
	تعد لغة C++‎‎ صعبة الاستخدام وغير ملائمة للمبتدئين ليس فقط بسبب تعقيد بناء التطبيقات باستخدمها وصعوبة إدارة الحزم البرمجية، بل بسبب عوامل أخرى من بينها صعوبة تصحيح الأخطاء البرمجية، وصعوبة استخدام اللغة بطريقة آمنة ما لم تكن على دراية جيدة بلغة C ولغات التجميع وبطريقة عمل أجزاء الحاسوب منخفضة المستوى، فلغة C++‎‎ أداة قوية يمكن أن تنجز من خلالها الكثير، ولكنها لغة خطيرة إذا استخدمتها بطريقة خاطئة.
</p>

<p>
	من ناحية أخرى، تعد لغة جافا أسهل للمبتدئين مقارنةً بلغة C++‎‎ بسبب أسلوب إدارة الذاكرة السهل الذي تتبعه والذي شرحناه سابقًا، فلن يحتاج مطورو جافا للقلق بشأن تحرير ذاكرة الكائنات غير المستخدمة حيث يمكن للغة التعامل مع هذه المهمة تلقائيًا بكل سهولة.
</p>

<h2 id="cjava-1">
	هل أختار C++‎‎ أم Java؟
</h2>

<p>
	الآن بعد تعرفت على أهم الاختلافات والفروقات بين لغة C++‎‎ و Java لنعد للسؤال الأساسي هل أستخدم C++‎‎ أم جافا في تطوير تطبيقاتي؟ في الواقع لا يوجد جواب موحد على هذا السؤال يناسب الجميع حتى مع الفهم العميق لميزات كل من هاتين اللغتين.
</p>

<p>
	فقد يفضل مهندسو البرمجيات الذين لا يعرفون مفاهيم البرمجة على مستوى منخفض لغة البرمجة جافا عندما لا يكون أمامهم سوى خيارين فقط هما جافا أو C++‎، إلا في حال الحاجة لتطوير تطبيقات زمن حقيقي مثل الألعاب كما شرحنا من قبل. من ناحية أخرى، قد يختار المطورون الذين يسعون إلى تطوير خبراتهم البرمجية<br>
	لغة C++‎.
</p>

<p>
	ورغم ذلك قد تؤثر الفروق التقنية بين لغتي C++‎‎ و Java بشكل بسيط على قرار اختيار اللغة الأنسب، ويعتمد نوع اللغة بشكل أكبر على طبيعة التطبيقات أو المنتجات التي تريد تطويرها فقد تكون جافا هي الأنسب أو هي الأنسب أو ربما في النهاية برمجة ثالثة هي الأنسب.
</p>

<p>
	ولمساعدتك على الاختيار إليك الشكل التالي الذي يعد بمثابة دليل يساعدك على تحديد اللغة الأفضل حسب طبيعة التطبيق الذي تنوي تطويره.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="149283" href="https://academy.hsoub.com/uploads/monthly_2024_05/1155065370_.png.09f2b48c4f19af5b6252c9f3c174b1be.png" rel=""><img alt="دليل شامل لاختيار أفضل لغة لمختلف أنواع المشاريع" class="ipsImage ipsImage_thumbnailed" data-fileid="149283" data-ratio="76.71" data-unique="cf8do1cyk" style="width: 700px; height: auto;" width="700" src="https://academy.hsoub.com/uploads/monthly_2024_05/.thumb.png.5b42bb42fd26f2d98c8a2420ec5d3771.png"> </a>
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على أبرز الفروقات بين لغة C++‎‎ وجافا من عدة جوانب تتعلق بالإنتاجية وإدارة الذاكرة وطريقة عمل مصرّفات اللغة وغيرها من العوامل، ويمكن القول ختامًا أن لغة C++‎‎ تتفوق في الأداء على لغة جافا وتوفر إمكانية الوصول إلى ميزات منخفضة المستوى التي تفتقر إليها لغة جافا، في حين تتميز جافا بسهولة الاستخدام وتعتبر خيارًا أفضل للمبرمجين المبتدئين. وبالرغم من أن C++‎‎ و Java تنتميان إلى عائلة لغات سي C، إلا أنهما يحملان تصميمًا مستقلاً، ويبقى العامل الأهم في اختيار اللغة المناسبة هو أهداف تطبيقك وطبيعة المهام المطلوبة فيه.
</p>

<p>
	ترجمة بتصرف لمقال <a href="https://www.toptal.com/c-plus-plus/c-plus-plus-vs-java" rel="external nofollow">An In-depth Look at C++ vs. Java</a> للكاتب Timothy Mensch
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B9%D8%A7%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%88%D9%89-r2261/" rel="">دليلك الشامل إلى لغات البرمجة عالية المستوى</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%A3%D9%87%D9%85-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-r1186/" rel="">أنظمة التصريف المستخدمة لبناء البرامج المكتوبة بلغة Cpp وأهم أخطاء عملية البناء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-cpp-r974/" rel="">مواضيع متقدمة عن الأنواع والتعامل معها في Cpp</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D9%88%D8%A7%D9%86%D9%8A-%D9%88%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-object-initialization-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1109/" rel="">البواني وتهيئة الكائنات Object Initialization في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-cpp-r913/" rel="">المؤشرات الذكية (Smart Pointers) في Cpp</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2321</guid><pubDate>Sat, 18 May 2024 12:07:02 +0000</pubDate></item><item><title>&#x645;&#x648;&#x627;&#x636;&#x64A;&#x639; &#x645;&#x62A;&#x641;&#x631;&#x642;&#x629; &#x641;&#x64A; Cpp &#x62A;&#x635;&#x642;&#x644; &#x645;&#x646; &#x645;&#x647;&#x627;&#x631;&#x629; &#x627;&#x644;&#x645;&#x628;&#x631;&#x645;&#x62C; &#x648;&#x62E;&#x628;&#x631;&#x62A;&#x647;</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D9%85%D8%AA%D9%81%D8%B1%D9%82%D8%A9-%D9%81%D9%8A-cpp-%D8%AA%D8%B5%D9%82%D9%84-%D9%85%D9%86-%D9%85%D9%87%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D9%88%D8%AE%D8%A8%D8%B1%D8%AA%D9%87-r1192/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061ff5ee2e49_66----Cpp-----.png.538ed992fae1cf738c6776115ebdc887.png" /></p>

<h2>
	النطاقات Scopes
</h2>

<h3>
	المتغيرات العامة Global variables
</h3>

<p>
	إذا أردت التصريح عن نسخة واحدة من متغير ما، وكانت هذه النسخة متاحة للوصول في عدة ملفات مصدرية source files، فمن الممكن أن نجعلها في النطاق العام global scope باستخدام الكلمة المفتاحية <code>extern</code>، فهي تخبر المصرِّف بوجود تعريف لهذا المتغير في مكان ما داخل الشيفرة الحالية، لذا فمن الممكن استخدامه في أي مكان، كما ستُجرى جميع عمليات القراءة / الكتابة في نفس المكان من الذاكرة.
</p>

<p>
	يتألّف المثال التالي من عدّة ملفات مصدرية:
</p>

<p>
	الملف my_globals.h:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_7" style="">
<span class="com">#ifndef</span><span class="pln"> __MY_GLOBALS_H__
</span><span class="com">#define</span><span class="pln"> __MY_GLOBALS_H__
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> circle_radius</span><span class="pun">;</span><span class="pln"> </span><span class="com">//  سيُعرَّف في مكان ما circle_radius تخطر المصرّف بأنّ</span><span class="pln">
</span><span class="com">#endif</span></pre>

<p>
	الملف foo1.cpp:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_9" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"my_globals.h"</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> circle_radius </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln"> </span><span class="com">// extern  تعريف المتغير الخارجي</span></pre>

<p>
	الملف main.cpp:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_11" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"my_globals.h"</span><span class="pln">

</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"The radius is: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> circle_radius </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="str">'</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سينتج الخرج التالي:
</p>

<pre class="ipsCode">
The radius is: 123
</pre>

<h3>
	المتغيرات المضمنة Inline variables
</h3>

<p>
	يُسمح بتعريف مُتغير مُضمّن Inline variable في عدّة وحدات ترجمة دون انتهاك قاعدة التعريف الواحد. وفي حال تعريف متغيّر مُضمّن أكثر من مرة، فسيَدمِج الرابط linker كلّ تَعاريفه في كائن واحد في البرنامج النهائي.
</p>

<p>
	يمكن تعريف حقل ساكن static data member في صنف إن صُرِّح عنه بأنّه مُضمّن <code>‎inline‎</code>. على سبيل المثال، يمكن تعريف الصنف في المثال التالي في الترويسة.
</p>

<p>
	<strong>ملاحظة</strong>: قبل C++‎ 17، كان من الضروري توفير ملفّ <code>‎.cpp‎</code> لاحتواء تعريف <code>Foo::num_instances</code> كي نضمن ألا يُعرّف أكثر من مرّة واحدة، لكن في C++‎ 17، أصبحت مُختلف تعريفات المتغيّر المضمن <code>‎Foo::num_instances‎</code> تشير إلى نفس الكائن <code>‎int‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_14" style="">
<span class="com">// not thread-safe قد لا يكون متوافقا مع المسالك</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">++</span><span class="pln">num_instances</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}~</span><span class="typ">Foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">--</span><span class="pln">num_instances</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">inline</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> num_instances </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وفي حالّة خاصة، تكون الحقول الساكنة للتعابير الثابتة <code>constexpr‎</code> مضُمّنة بطريقة غير صريحة implicitly.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_16" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">MyString</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">MyString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">/* ... */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> max_size </span><span class="pun">=</span><span class="pln"> INT_MAX </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	صار التعريف <code>constexpr int MyString::max_size</code>ضروريًا في كل وحدات الترجمة في إصدار C++ 1.
</p>

<h3>
	نطاق كتلة بسيط
</h3>

<p>
	يبدأ نطاق متغيّر ما في كتلة <code>‎{ ... }‎</code> بعد التصريح وينتهي عند نهاية الكتلة، فإذا كانت هناك كتلة متشعّبة nested block، فتستطيع الكتلة الداخلية إخفاء نطاق المتغيّر الذي صُرِّح عنه في الكتلة الخارجية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_18" style="">
<span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln">
    </span><span class="com">//   ^</span><span class="pln">
    </span><span class="com">//   يبدأ هنا `x` نطاق</span><span class="pln">
    </span><span class="com">//</span><span class="pln">
</span><span class="pun">}</span><span class="pln">   </span><span class="com">// &lt;- ينتهي هنا `x` نطاق</span></pre>

<p>
	في حال التصريح عن متغير جديد داخل الكتلة المُتشعِّبة، وكان ذلك المتغير يحمل اسم متغير موجود في الكتلة الخارجية، فإنه يخفي المتغير الأول الذي في الكتلة الخارجية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_20" style="">
<span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">;</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x</span><span class="pun">;</span><span class="pln"> </span><span class="com">// → 200</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x</span><span class="pun">;</span><span class="pln"> </span><span class="com">// →  100</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	قاعدة التعريف الواحد ODR
</h2>

<h3>
	انتهاك قاعدة ODR عبر تحليل التحميل الزائد
</h3>

<p>
	يمكن انتهاك ODR إذا لم يشِر البحث عن الأسماء إلى نفس الكيان حتى في حال استخدام مقاطع tokens متماثلة للتعبير عن دالة مضمّنة معيّنة. لاحظ الدالة <code>‎func‎</code> في المثال التالي:
</p>

<ul>
<li>
		header.h
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_22" style="">
<span class="kwd">void</span><span class="pln"> overloaded</span><span class="pun">(</span><span class="typ">int</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">inline</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    overloaded</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		foo.cpp ، تشير <code>overloaded</code> إلى <code>(void overloaded(char</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_24" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"header.h"</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    func</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		bar.cpp، تشير <code>overloaded</code> إلى <code>(void overloaded(char</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_26" style="">
<span class="kwd">void</span><span class="pln"> overloaded</span><span class="pun">(</span><span class="kwd">char</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أخرى “include” قد تأتي من عبارات تضمين</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">"header.h"</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    func</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لدينا انتهاك هنا لقاعدة ODR، إذ يشير المقطع <code>‎overloaded‎</code> إلى عدّة كيانات مختلفة في نفس وحدة الترجمة.
</p>

<h3>
	مضاعفة الدوال المعرفة Multiply defined function
</h3>

<p>
	إنّ أهم نتيجة لقاعدة التعريف الواحد هي أنّ الدوال غير المضمّنة ذات الارتباط الخارجي non-inline functions with external linkage يجب ألا تُعرَّف إلا مرّة واحدة في البرنامج، لكن يمكن التصريح عنها عدّة مرات. لذا لا ينبغي تعريف مثل هذه الدوال في الترويسات، إذ يمكن إدراج الترويسة عدّة مرات من قبل عدة وحدات ترجمة مختلفة.
</p>

<ul>
<li>
		<code>foo.h‎</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_28" style="">
<span class="com">#ifndef</span><span class="pln"> FOO_H
</span><span class="com">#define</span><span class="pln"> FOO_H
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"foo"</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">();</span><span class="pln">
</span><span class="com">#endif</span></pre>

<ul>
<li>
		<code>foo.cpp</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_30" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"foo.h"</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"bar"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<code>main.cpp‎</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_32" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"foo.h"</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">();</span><span class="pln">
    bar</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذا البرنامج، عُرِّفت الدالّة الأبسط <code>‎foo‎</code> في الترويسة <code>‎foo.h‎</code>، والتي ضُمِّنت مرّتين: مرّة من قبل <code>‎foo.cpp‎</code> ومرّة أخرى من قبل <code>‎main.cpp‎</code>، ولذلك تحتوي كل وحدة ترجمة على تعريف <code>‎foo‎</code> الخاص بها.
</p>

<p>
	لاحظ أنّ دروع التضمين include guards في <code>‎foo.h‎</code> لا تمنع حدوث ذلك، إذ أنّ الملفين <code>‎foo.cpp‎</code> و <code>‎main.cpp‎</code> يشتملان على <code>‎foo.h‎</code> بشكل منفصل، وعلى الأرجح سينتج عن محاولة إنشاء هذا البرنامج خطأٌ في وقت الربط link-time error بسبب تعريف <code>‎foo‎</code> أكثر من مرّة.
</p>

<p>
	ويجب التصريح عن الدوال في الترويسات وتعريفها في ملفات <code>‎.cpp‎</code> المقابلة لتجنّب مثل هذه الأخطاء، مع بعض الاستثناءات.
</p>

<h3>
	الدوال المضمنة Inline functions
</h3>

<p>
	يمكن تعريف دالّة مُضمّنة <code>‎inline‎</code> في عدّة وحدات ترجمة بشرط أن تكون جميع تَعاريفها متطابقة، ويجب أن تُعرّف أيضًا في كل وحدة ترجمة تُستخدم فيها، لذا يجب تعريف الدوال المضمّنة في الترويسات، ولا توجد حاجة إلى ذكرها في ملف التنفيذ implementation file.
</p>

<p>
	سيتصرّف البرنامج كما لو كان هناك تعريف واحد للدالّة.
</p>

<ul>
<li>
		<code>foo.h‎</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_34" style="">
<span class="com">#ifndef</span><span class="pln"> FOO_H
</span><span class="com">#define</span><span class="pln"> FOO_H
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">inline</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"foo"</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">();</span><span class="pln">
</span><span class="com">#endif</span></pre>

<ul>
<li>
		<code>foo.cpp</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_36" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"foo.h"</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// تعريفات أخرى أكثر تعقيدا</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<code>main.cpp‎</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_38" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">"foo.h"</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">();</span><span class="pln">
    bar</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذا المثال، عُرِّفت الدالّة <code>‎foo‎</code> بصورة مُضمّنة inline في الترويسة، أمّا الدالّة الأكثر تعقيدًا <code>‎bar‎</code> فهي غير مُضمنة، وقد عُرِّفت في ملف التقديم. وتحتوي كل من وحدتي الترجمة <code>foo.cpp‎</code> و <code>‎main.cpp</code> على تعريفات لـ <code>‎foo‎</code>، وهذا لن يسبّب مشكلة لأنّ <code>‎foo‎</code> مضمّنة.
</p>

<p>
	تكون الدوالّ المُعرَّفة داخل تعريف صنف ما -والتي قد تكون دوالًا تابعة أو صديقة- مضمنة بشكل غير صريح، لذا يمكن تعريف الدوال التابعة للفصل داخل تعريف الصنف، رغم أنّه يمكن إدراج التعاريف في وحدات ترجمة متعددة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_40" style="">
<span class="com">// in foo.h</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"bar"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> baz</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// in foo.cpp</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Foo</span><span class="pun">::</span><span class="pln">baz</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// التعريف</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عُرِّفت الدالّة <code>‎Foo::baz‎</code> خارج السطر، وعليه تكون غير مُضمّنة، ويجب ألا تُعُرِّف في الترويسة.
</p>

<h2>
	البحث باسم الوسيط Argument Dependent Name Lookup
</h2>

<p>
	عند البحث عن الدوال، تُجمع "الأصناف المرتبطة" associated classes، و"فضاءات الأسماء المرتبطة" associated namespaces التي تحقق أحد الشروط التالية، وفقًا لنوع الوسيط <code>‎T‎</code>.
</p>

<p>
	عند البحث عن الدوال فإننا نجدها عبر جمع "الأصناف المرتبطة" associated classes أولًا، وكذلك "فضاءات الأسماء المرتبطة" associated namespaces، التي تحقق شرطًا أو أكثر من الشروط أدناه وفقًا لنوع الوسيط <code>‎T‎</code>، لكن سنعرض قبل هذا القواعد الخاصة بأسماء الأصناف والتعداد وقوالب أسماء الأصناف:
</p>

<ul>
<li>
		إذا كان <code>‎T‎</code> صنفًا مُتشعِّبا nested class، أو عضوَ تعدادٍ member enumeration، فسيُضاف الصنف المحيطة به إلى مجموعة البحث.
	</li>
	<li>
		إذا كان <code>‎T‎</code> تعدادًا -قد يكون أيضًا عضوًا من صنف-، يُضاف أعمق فضاء اسم - innermost namespace - داخله إلى مجموعة البحث.
	</li>
	<li>
		إذا كان <code>‎T‎</code> صنفًا -قد يكون مُتشعِّبًا أيضًا-، تُضاف جميع أصنافه الأساسية، وكذلك الصنف نفسه، وأعمق فضاءات الاسم في جميع الأصناف المرتبطة.
	</li>
	<li>
		إذا كان <code>‎T‎</code> من النوع <code>‎ClassTemplate&lt;TemplateArguments&gt;‎</code> -وهو صنف أيضًا-، تُضاف الأصناف وفضاءات الاسم المرتبطة بوسائط نوع القالب، وفضاء الاسم الخاص بوسائط قالب القالب template template argument، والصنف المحيط بوسائط قالب القالب، إذا كان وسيط القالبِ template argument قالبَ عضوٍ member template.
	</li>
</ul>
<p>
	هناك بعض القواعد التي تخصّ الأنواع المضمّنة أيضًا:
</p>

<ul>
<li>
		إذا كان <code>‎T‎</code> مؤشّرًا يشير إلى نوع <code>‎U‎</code>، أو مصفوفةً مؤلّفة من عناصر من النوع <code>‎U‎</code>، تُضاف أصناف وفضاءات الأسماء المرتبطة بـ <code>‎U‎</code> إلى مجموعة البحث. في المثال: <code>void (*fptr)(A);f(fptr);‎</code>، تُضاف فضاءات الأسماء والأصناف المرتبطة بـ <code>‎void(A)‎</code> (انظر القاعدة التالية).
	</li>
	<li>
		إذا كان <code>‎T‎</code> نوع دالّة، تُضاف الأصناف وفضاءات الأسماء المرتبطة بأنواع المُعاملات والقيمة المعادة. في المثال: <code>void(A)</code>‎، تُضاف فضاءات الأسماء والأصناف المرتبطة بـ <code>A</code>.
	</li>
	<li>
		إذا كان <code>‎T‎</code> مؤشّرًا يشير إلى عضو، تُضاف الأصناف وفضاءات الأسماء المرتبطة بنوع ذلك العضو (قد تنطبق على كل من مؤشّرات التوابع ومؤشّرات الحقول). في مثال <code>‎‎B A::*p; void (A::*pf)(B); f(p);f(pf);‎</code>؛ تُضاف فضاءات الأسماء والأصناف المرتبطة بـ <code>A‎</code> و <code>B‎</code> و<code>void(B)‎</code>، والتي تطبّق القواعد أعلاه التي تخصّ أنواع الدوال.
	</li>
</ul>
<p>
	يُعثَر على جميع الدوال والقوالب داخل جميع فضاءات الأسماء المرتبطة من خلال البحث باسم الوسيط، أو <strong>البحث المعتمد على الوسيط</strong> argument dependent lookup. ويُعثَر على الدوال الصديقة friend functions لنطاق الأسماء المُصرَّح عنها في الأصناف المرتبطة، والتي عادة ما تكون غير مرئية. بالمقابل، تُتجَاهل الموجّهات directives. وتكون جميع الاستدعاءات في المثال التالي صالحة حتى دون الحاجة إلى تأهيل <code>‎f‎</code> بفضاء الاسم عند الاستدعاء.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_42" style="">
<span class="kwd">namespace</span><span class="pln"> A </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">struct</span><span class="pln"> Z </span><span class="pun">{};</span><span class="pln">
    </span><span class="kwd">namespace</span><span class="pln"> I </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">void</span><span class="pln"> g</span><span class="pun">(</span><span class="pln">Z</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> I</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">struct</span><span class="pln"> X </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">struct</span><span class="pln"> Y </span><span class="pun">{};</span><span class="pln">
        </span><span class="kwd">friend</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">Y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">X p</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> X </span><span class="pun">&gt;</span><span class="pln"> p</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// أمثلة على الاستدعاءات</span><span class="pln">
f</span><span class="pun">(</span><span class="pln">A</span><span class="pun">::</span><span class="pln">X</span><span class="pun">());</span><span class="pln">
f</span><span class="pun">(</span><span class="pln">A</span><span class="pun">::</span><span class="pln">X</span><span class="pun">::</span><span class="pln">Y</span><span class="pun">());</span><span class="pln">
f</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">make_shared </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">::</span><span class="pln">X </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">());</span><span class="pln">
g</span><span class="pun">(</span><span class="pln">A</span><span class="pun">::</span><span class="pln">Z</span><span class="pun">());</span><span class="pln"> </span><span class="com">// "using namespace I;"غير صالح، يُتجاهل</span></pre>

<h2>
	حزم المعاملات Parameter packs
</h2>

<h3>
	قالب بحزمة معاملات template with a parameter pack
</h3>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_44" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">...</span><span class="typ">Types</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Tuple</span><span class="pln"> </span><span class="pun">{};</span></pre>

<p>
	حزمة المُعاملات parameter pack هي مُعاملُ قالبٍ template parameter يقبل وسيط قالب أو أكثر، أو لا يقبل على الإطلاق، ويكون القالب متغايرًا variadic إذا كان يحتوي على حزمة مُعاملات واحدة على الأقل.
</p>

<h3>
	توسيع حزمة معاملات
</h3>

<p>
	يُوسَّع نمط <code>‎parameter_pack ...‎</code> إلى قائمة من البدائل لحزمة <code>‎parameter_pack‎</code> مفصولة بالفاصلة الأجنبية <code>,</code>، وذلك مع كل معامل من معاملاتها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_46" style="">
<span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="com">// أساس التكرار</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> variadic_printer</span><span class="pun">(</span><span class="pln">T last_argument</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> last_argument</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> T</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">...</span><span class="typ">Args</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> variadic_printer</span><span class="pun">(</span><span class="pln">T first_argument</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Args</span><span class="pun">...</span><span class="pln"> other_arguments</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> first_argument </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"\n"</span><span class="pun">;</span><span class="pln">
    variadic_printer</span><span class="pun">(</span><span class="pln">other_arguments</span><span class="pun">...);</span><span class="pln"> </span><span class="com">// توسيع حزمة المعاملات</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند استدعاء الشيفرة أعلاه مع <code>‎variadic_printer(1, 2, 3, "hello");‎</code>، ستطبع:
</p>

<pre class="ipsCode">
1
2
3
hello
</pre>

<h2>
	فواصل الأرقام Digit separators
</h2>

<p>
	من الصعب قراءة القيم العددية المؤلّفة من من أرقام كثيرة، فمثلًا:
</p>

<ul>
<li>
		حاول نُطق 7237498123
	</li>
	<li>
		قارن بين 237498123 وبين 237499123 من حيث التساوي.
	</li>
	<li>
		حدّد العدد الأكبر من بين 237499123 و 20249472.
	</li>
</ul>
<p>
	وقد عرَّف الإصدار ‎C‎++ 14 علامةَ الاقتباس <code>‎'‎</code> على أساس فاصلة للأرقام، بحيث تُستخدَم داخل الأعداد والقيم مصنَّفة النوع المُعرَّفة من قبل المستخدِم user-defined literals لتسهيل قراءة الأعداد الكبيرة. انظر:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_48" style="">
<span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> decn </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="str">'000'</span><span class="lit">000</span><span class="str">'</span><span class="lit">000ll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> hexn </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0xFFFF</span><span class="str">'</span><span class="typ">FFFFll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> octn </span><span class="pun">=</span><span class="pln"> </span><span class="lit">00</span><span class="str">'23'</span><span class="lit">00ll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> binn </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0b1010</span><span class="str">'0011ll;</span></pre>

<p>
	تُتجَاهل علامة الاقتباس المُفردة عند تحديد القيمة. مثلًا:
</p>

<ul>
<li>
		القيم مصنَّفة النوع التالية: <code>‎1048576‎</code> و <code>‎1'048'576‎</code> و <code>‎0X100000‎</code> و <code>‎0x10'0000‎</code> و <code>‎0'004'000'000‎</code> كلها لها نفس القيمة.
	</li>
	<li>
		القيم مصنفة النوع التالية <code>‎1.602'176'565e-19‎</code> و <code>‎1.602176565e-19‎</code> لهما نفس القيمة.
	</li>
</ul>
<p>
	ولا توجد أهمية تُذكر لموضع علامات الاقتباس المُفردة، فمثلًا، كل التعابير التالية متكافئة:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_50" style="">
<span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> a1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123456789ll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> a2 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="str">'456'</span><span class="lit">789ll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> a3 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">12</span><span class="str">'34'</span><span class="lit">56</span><span class="str">'78'</span><span class="lit">9ll</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> a4 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">12345</span><span class="str">'6789ll;</span></pre>

<p>
	كذلك يُسمح باستخدام الفاصلة في القيم مصنَّفة النوع والمُعرّفة من المستخدم:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_52" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">seconds tiempo </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="str">'674'</span><span class="lit">456s</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">5</span><span class="str">'300h;</span></pre>

<h2>
	موازنة مع إصدارات C++‎ المختلفة
</h2>

<p>
	اعلم أنه يمكن تنفيذ تكرار حلقي على حاوية التسلسل <code>c</code> في لغة ++C باستخدام الفهارس على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_54" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">size_t</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> </span><span class="pun">++</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> c</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	هذه الصيغة -رغم بساطتها- عرضة لكثير من الأخطاء الشائعة، مثل استخدام عامل مقارنة خاطئ، أو متغيّر فهرسة غير صحيح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_56" style="">
<span class="kwd">for</span><span class="pun">(</span><span class="typ">size_t</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> </span><span class="pun">++</span><span class="pln">j</span><span class="pun">)</span><span class="pln"> c</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
                     </span><span class="pun">^~~~~~~~~~~~~~^</span></pre>

<p>
	كذلك يمكن التكرار على الحاويات باستخدام المُكرّرات iterators، مع عيوب مماثلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_58" style="">
<span class="kwd">for</span><span class="pun">(</span><span class="typ">iterator</span><span class="pln"> it </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">();</span><span class="pln"> it </span><span class="pun">!=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"> </span><span class="pun">++</span><span class="pln">it</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">it</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	وقد قدّمت C++‎ 11 حلقة <code>for</code> النطاقية، إضافة إلى الكلمة المفتاحية <code>‎auto‎</code>، واللّتان سمحتا بكتابة شيفرات مثل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_60" style="">
<span class="kwd">for</span><span class="pun">(</span><span class="kwd">auto</span><span class="pun">&amp;</span><span class="pln"> x </span><span class="pun">:</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	وهنا، يكون المُعاملان الوحيدان هما الحاوية <code>‎c‎</code>، ومتغيّر <code>‎x‎</code> لتخزين القيمة الحالية. هذا يمنع الأخطاء المشار إليها سابقا. ووفقًا لمعيار C++‎ 11، فإنّ التنفيذ الأساسي underlying implementation يكافئ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_62" style="">
<span class="kwd">for</span><span class="pun">(</span><span class="kwd">auto</span><span class="pln"> begin </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> end </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"> begin </span><span class="pun">!=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">begin</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في مثل هذا التنفيذ فإن التعبير <code>‎auto begin = c.begin(), end = c.end();‎</code> يجبر <code>‎begin‎</code> و <code>‎end‎</code> على أن يكونا من نفس النوع، بينما لا تُزاد قيمة <code>‎end‎</code> ولا تحصَّل dereferenced، ومن ثم فإنّ حلقة <code>for</code> النطاقية لا تعمل إلا على الحاويات المُعرَّفة بواسطة زوج مُكرّر/ مُكرّر iterator/iterator. وقد خفّف معيارُ C++‎ 17 هذا القيد عن طريق تعديل التنفيذ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8624_64" style="">
<span class="kwd">auto</span><span class="pln"> begin </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> end </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(;</span><span class="pln"> begin </span><span class="pun">!=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">begin</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هنا يُسمح بأن تكون <code>‎begin‎</code> و <code>‎end‎</code> من نوعين مختلفين طالما أنه يمكن مقارنتهما للتحقّق من أنّهما غير متساويين. هذا يسمح بالتكرار على العديد من الحاويات مثل الحاويات المعرّفة عبر زوج مُكرّر/حارس iterator/sentinel.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617035098="1" data-ss1617035474="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصول
</p>

<ul>
<li>
		Chapter 64: Inline variables
	</li>
	<li>
		Chapter 117: Scopes
	</li>
	<li>
		Chapter 120: One Definition Rule (ODR)‎
	</li>
	<li>
		Chapter 122: Argument Dependent Name Lookup
	</li>
	<li>
		Chapter 129: Parameter packs
	</li>
	<li>
		Chapter 135: Digit separators
	</li>
	<li>
		Chapter 137: Side by Side Comparisons of classic C++ examples solved via C++ vs C++11 vs C++14 vs C++1
	</li>
</ul>
<p>
	من كتاب <a data-ss1617035098="1" data-ss1617035474="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1192</guid><pubDate>Fri, 07 May 2021 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62D;&#x633;&#x64A;&#x646; &#x627;&#x644;&#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x643;&#x62A;&#x648;&#x628;&#x629; &#x628;&#x644;&#x63A;&#x629; Cpp &#x648;&#x62A;&#x634;&#x62E;&#x64A;&#x635;&#x647;&#x627;</title><link>https://academy.hsoub.com/programming/cpp/%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%AA%D8%B4%D8%AE%D9%8A%D8%B5%D9%87%D8%A7-r1191/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061fd087de04_65-----Cpp-.png.7a9a7fa437baf0f8e0b9f34e194cc7e4.png" /></p>

<p>
	تُعرف اللغتان C وC++‎ بانّ أداءهما عال جدًّا - ويُعزى ذلك في الغالب إلى إمكانية التخصيص المكثّف للشيفرة، إذ يُسمح للمستخدم بتحسين الأداء عبر اختيار بنية الشيفرة وكيفية تنفيذها. وإن أردت تحسين الشيفرة فمن المهم أن تفهمها وتعرف كيفية استخدامها.
</p>

<p>
	وتشمل بعض أخطاء التحسين الشائعة ما يلي:
</p>

<ul>
<li>
		<strong>التحسين السابق لأوانه</strong>: قد يسوء أداء الشيفرات المعقّدة بعد التحسين ممّا يضيع الوقت ويقلل الكفاءة. يجب أن تكون الأولوية هي كتابة شيفرة صحيحة وقابلة للصيانة، وليس شيفرة مُحسَّنة.
	</li>
	<li>
		<strong>التحسين في الحالات غير المناسبة</strong>: إضافة تحميل زائد لأجل حالة احتمال حدوثها 1% لا يستحقّ أن تبطئ لأجله 99% من الحالات الأخرى.
	</li>
	<li>
		<strong>التحسين المصغّر Micro-optimization</strong>: تقوم المُصرِّفات بهذا الأمر بفعالية كبيرة، كما أن التحسين المصغَّر يمكن أن يضرّ بقدرة المُصرِّفات على زيادة تحسين الشيفرة.
	</li>
</ul>
<p>
	وبعض أهداف التحسين النموذجية ما يلي:
</p>

<ul>
<li>
		تقليل كمية العمل المطلوبة.
	</li>
	<li>
		استخدام خوارزميات / بنيات أكثر فاعلية.
	</li>
	<li>
		استغلال العتاد بشكل أفضل.
	</li>
</ul>
<p>
	لكن قد تكون لعمليات التحسين تأثيرات جانبية سلبية، مثل:
</p>

<ul>
<li>
		الاستخدام المكثّف للذاكرة.
	</li>
	<li>
		تعقيد الشيفرة وصعوبة قراءتها أو صيانتها.
	</li>
	<li>
		تعقيد الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> وتصميمات الشيفرة.
	</li>
</ul>
<p>
	على أي حال، غالبًا ما يُعدِّل المُصرِّفُ البرنامجَ عند التصريف لتحسين أدائه، وهذا مسموح به وفقًا لقاعدة "كما لو" as-if rule. والتي تسمح بإجراء التغييرات والتحويلات التي لا تغيّر السلوك الملاحظ للبرنامج.
</p>

<h2>
	تحسين الصنف الأساسي الفارغ Empty Base Class Optimization
</h2>

<p>
	لا يمكن أن يشغل كائن ما أقل من بايت واحد، وإلّا لكانت أعضاء المصفوفات التي تضم عناصر من نفس النوع تحتل العنوان نفسه. لهذا، تكون <code>‎sizeof(T)&gt;=1‎</code> صحيحة دائما. وكذلك لا يمكن أن يكون الصنف المشتق أصغر من صنفه الأساسي base class. لكن إن كان الصنف الأساسي فارغًا، فلن يُضاف حجمه بالضرورة إلى الصنف المشتق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_7" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Base</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Derived</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Base</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وليس من الضروري هنا تخصيص بايت للصنف <code>‎Base‎</code> داخل <code>‎Derived‎</code> حتى يحتلّ كل كائن -بغض النظر عن نوعه- عنوانًا مميّزًا. وفي حال إجراء تحسين للصنف الأساسي الفارغ -ولم تكن هناك حاجة إلى الحشو padding-، فستكون العبارة <code>‎sizeof(Derived) ==sizeof(int)‎</code> صحيحة، أي أنّه لن يُجرى أيّ تخصيص إضافي للصنف الفارغ. هذا ممكن حتّى مع الأصناف الأساسية base classes المتعددة، إذ لا يمكن لعدّة أًصناف أساسية في ++C أن يكون لها نفس النوع، وعليه فلن تنشأ مشكلة من ذلك.
</p>

<p>
	لاحظ أنّه لا يمكن إجراء ذلك إلا إذا كان نوع العضو الأول من <code>‎Derived‎</code> مختلفًا عن كلّ الأصناف الأساسية، وهذا يشمل كل الأصناف الأساسية المشتركة بشكل مباشر أو غير مباشر. وإذا كان له نفس نوع أحد أصنافه الأساسية -أو كانت هناك صنف أساسية مشترك)- فيلزم على الأقل تخصيص بايت واحد لضمان ألّا يحتلّ كائنان مختلفان من نفس النوع العنوان نفسه.
</p>

<h2>
	التحسين عن طريق تقليل الشيفرة المنفذة
</h2>

<p>
	الأسلوب الأوضح للتحسين هو تقليل الشيفرة المنفّذة، فعادة ما يسرِّع التنفيذَ دون تغيير التعقيد الزمني للشيفرة. لكن رغم أنّ هذه الطريقة تسرّع البرنامج، فلن تظهر فائدتها إلّا في الشيفرات التي تُستدعى كثيرًا.
</p>

<p>
	هذه بعض الأمثلة على تقليل الشيفرات المنفّذة:
</p>

<h3>
	إزالة الشيفرات عديمة الفائدة
</h3>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_9" style="">
<span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> A </span><span class="pun">*</span><span class="pln">a</span><span class="pun">);</span><span class="pln"> </span><span class="com">// دالة ما</span><span class="pln">
</span><span class="com">// تخصيص غير ضروري للذاكرة + إلغاء تخصيص النسخة</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> a1 </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_unique</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;();</span><span class="pln">
func</span><span class="pun">(</span><span class="pln">a1</span><span class="pun">.</span><span class="pln">get</span><span class="pun">());</span><span class="pln">
</span><span class="com">// استخدام المكدّس يمنع</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> a2 </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">{};</span><span class="pln">
func</span><span class="pun">(&amp;</span><span class="pln">a2</span><span class="pun">);</span></pre>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	منذ الإصدار C++‎ 14، يُسمح للمُصرِّفات بتحسين هذه الشيفرة لإزالة تخصيص الذاكرة، ومطابقة التحرير matching deallocation.
</p>

<h3>
	تنفيذ الشيفرة مرة واحدة فقط
</h3>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_11" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">map</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">unique_ptr</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;&gt;</span><span class="pln"> lookup</span><span class="pun">;</span><span class="pln">
</span><span class="com">// إدارج/بحث بطيئ</span><span class="pln">
</span><span class="com">// داخل هذه الدالة، سنمر على القاموس مرّتين لأجل البحث عن العنصر، ومرّة ثالثة إن لم يكن موجودا</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> A </span><span class="pun">*</span><span class="pln">lazyLookupSlow</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">key</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">lookup</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">key</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> lookup</span><span class="pun">.</span><span class="pln">cend</span><span class="pun">())</span><span class="pln">
        lookup</span><span class="pun">.</span><span class="pln">emplace_back</span><span class="pun">(</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_unique</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;());</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> lookup</span><span class="pun">[</span><span class="pln">key</span><span class="pun">].</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// داخل هذه الدالة، سيحصل نفس التأثير الذي يحدث في النسخة الأبطأ لكن مع مضاعفة السرعة</span><span class="pln">
</span><span class="com">// لأنّنا سنمر على القاموس مرة واحدة فقط</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> A </span><span class="pun">*</span><span class="pln">lazyLookupSlow</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">key</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> lookup</span><span class="pun">[</span><span class="pln">key</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">value</span><span class="pun">)</span><span class="pln">
        value </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_unique</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن استخدام أسلوب مشابه للتحسين عبر تنفيذ implement إصدار مستقر من <code>‎unique‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_13" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> stableUnique</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">set</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> checkUnique</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s </span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يُعاد الإدراج إن كان ناجحا، لذلك يمكن أن نعرف ما إذا كان العنصر موجودًا أم لا </span><span class="pln">
        </span><span class="com">// هذا يمنع الإدراج، والذي سيمر على القاموس مرّة لكل عنصر فريد</span><span class="pln">
</span><span class="com">// على عناصر مكرّرة v يُكسِبنا هذا حوالي نصف الوقت إن لم تحتو</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">checkUnique</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
            result</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">        
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	منع عمليات إعادة التخصيص والنسخ أو النقل عديمة الفائدة
</h3>

<p>
	منعنا في المثال السابق عمليّات البحث في <code>std::set</code>، بيْد أنّ هذا غير كافٍ، فما يزال المتجه <code>‎std::vector‎</code> يحتوي على خوارزمية مكلّفة ونامية ستحتاج إلى نقل ذاكرتها، يمكن منع هذا عبر حجز الحجم المناسب.
</p>

<p>
	في المثال التالي، نضمن عدم حدوث أي نسخ أو نقل في المتجه لأن سعته تساوي الحد الأقصى للأعداد التي سندرجها، وإن افترضنا عدم حدوث أي تخصيص من الحجم صفر، وأن تخصيص كتلة كبيرة من الذاكرة يأخذ نفس الوقت الذي يأخذه تخصيص حجم صغير فهذا لن يبطئ البرنامج.
</p>

<p>
	<strong>ملاحظة</strong>: يمكن أن تنتبه المصرفات لهذا الأمر وتحذف شيفرة التحقق من الشيفرة المولَّدة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_15" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> stableUnique</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">

    result</span><span class="pun">.</span><span class="pln">reserve</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">());</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">set</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&gt;</span><span class="pln"> checkUnique</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s </span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// انظر المثال أعلاه</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">checkUnique</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
            result</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	استخدام حاويات أكفأ
</h2>

<p>
	استخدام بنيات البيانات الصحيحة في الوقت المناسب يمكن أن يحسّن التعقيد الزمني للشيفرة، انظر المثال التالي حيث يساوي تعقيد <code>stableUnique</code> هنا <code>(N log(N</code>، حيث <code>N</code> أكبر من عدد العناصر في <code>v</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_17" style="">
<span class="com">// log(N) &gt; insert complexity of std::set</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> stableUnique</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">set</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> checkUnique</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s </span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// انظر فقرة التحسين عبر تقليل الشيفرة المنفّذة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">checkUnique</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
            result</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا تخفيض تعقيد التقديم إلى <code>N</code> عبر استعمال حاوية تستخدم تنفيذًا مختلفًا لتخزين عناصرها، مثل قاموس hash بدلًا من شجرة tree. وهناك أثر جانبي إضافي أيضًا، وهو تقليل استدعاء مُعامل المقارنة على السلاسل النصية، لأنّنا لن نحتاج إلى استدعائها إلّا في حال كانت ستُدرج السلسلة النصية في نفس المجموعة. انظر المثال التالي حيث يكون تعقيد <code>stableUnique</code> مساويًا لـ <code>(N log (N</code>، حيث <code>N</code> أكبر من عدد العناصر في <code>v</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_19" style="">
<span class="com">// أكبر من 1 std::unordered_set تعقيد الإدراج في</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> stableUnique</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">unordered_set</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&gt;</span><span class="pln"> checkUnique</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s </span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// انظر فقرة التحسين عبر تقليل الشيفرة المنفّذة</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">checkUnique</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span><span class="pln">second</span><span class="pun">)</span><span class="pln">
            result</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	تحسين الكائنات الصغيرة Small Object Optimization
</h2>

<p>
	تحسين الكائنات الصغيرة هي تقنية تُستخدم في بنيات البيانات منخفضة المستوى، مثل <code>‎std::string‎</code> (يشار إليها أحيانًا باسم تحسين السلاسل النصية القصيرة/الصغيرة)، ويُقصد بها استخدام مساحة مكدّس كمخزن مؤقت buﬀer بدلًا من استخدام ذاكرة مخصّصة عندما يكون المحتوى صغيرًا بدرجة كافية لتسَعَه المساحة المحجوزة. وسنحاول منع تخصيص مساحة في الكومة heap، لكنّ ثمن هذا هو إضافة حمل إضافي overhead على الذاكرة، وإجراء بعض الحسابات الإضافية.
</p>

<p>
	تعتمد فوائد هذه التقنية على نوع الاستخدام، ويمكن أن تضرّ بالأداء في حال استخدامها بشكل غير صحيح. هذا مثال على ذلك، الطريقة التالية ساذجة لكتابة سلسلة نصية باستخدام هذا التحسين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_21" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;cstring&gt;</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> string final
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> SMALL_BUFFER_SIZE </span><span class="pun">=</span><span class="pln"> </span><span class="lit">16</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">bool</span><span class="pln"> _isAllocated</span><span class="pun">{</span><span class="kwd">false</span><span class="pun">};</span><span class="pln"> </span><span class="com">///&lt; تذكّر إذا كنّا قد خصّصنا الذاكرة</span><span class="pln">
    </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">_buffer</span><span class="pun">{</span><span class="kwd">nullptr</span><span class="pun">};</span><span class="pln"> </span><span class="com">///&lt; مؤشر يشير إلى المخزن المؤقت الذي نستخدمه </span><span class="pln">
    </span><span class="kwd">char</span><span class="pln"> _smallBuffer</span><span class="pun">[</span><span class="pln">SMALL_BUFFER_SIZE</span><span class="pun">]=</span><span class="pln"> </span><span class="pun">{</span><span class="str">'\0'</span><span class="pun">};</span><span class="pln"> 
</span><span class="com">///&lt;  SMALL OBJECT مساحة المكدّس المُستخدمة لأجل الكائن الصغير</span><span class="pln">
OPTIMIZATION
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
    </span><span class="pun">~</span><span class="pln">string</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_isAllocated</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">delete</span><span class="pln"> </span><span class="pun">[]</span><span class="pln"> _buffer</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">explicit</span><span class="pln"> string</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">cStyleString</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">auto</span><span class="pln"> stringSize </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">strlen</span><span class="pun">(</span><span class="pln">cStyleString</span><span class="pun">);</span><span class="pln">
        _isAllocated </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stringSize </span><span class="pun">&gt;</span><span class="pln"> SMALL_BUFFER_SIZE</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_isAllocated</span><span class="pun">)</span><span class="pln">
            _buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">[</span><span class="pln">stringSize</span><span class="pun">];</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
            _buffer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">_smallBuffer</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">strcpy</span><span class="pun">(</span><span class="pln">_buffer</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">cStyleString</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    string</span><span class="pun">(</span><span class="pln">string </span><span class="pun">&amp;&amp;</span><span class="pln">rhs</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">:</span><span class="pln"> _isAllocated</span><span class="pun">(</span><span class="pln">rhs</span><span class="pun">.</span><span class="pln">_isAllocated</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">,</span><span class="pln"> _buffer</span><span class="pun">(</span><span class="pln">rhs</span><span class="pun">.</span><span class="pln">_buffer</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">,</span><span class="pln"> _smallBuffer</span><span class="pun">(</span><span class="pln">rhs</span><span class="pun">.</span><span class="pln">_smallBuffer</span><span class="pun">)</span><span class="pln"> </span><span class="com">//&lt;  غير ضروري عند التخصيص</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_isAllocated</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// منع الحذف المزدوج للذاكرة</span><span class="pln">
            rhs</span><span class="pun">.</span><span class="pln">_buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// نسخ البيانات</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">strcpy</span><span class="pun">(</span><span class="pln">_smallBuffer</span><span class="pun">,</span><span class="pln"> rhs</span><span class="pun">.</span><span class="pln">_smallBuffer</span><span class="pun">);</span><span class="pln">
            _buffer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">_smallBuffer</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// حَذفنا التوابع الأخرى، مثل منشئ النسخ ومعامل الإسناد، لأجل تحسين القراءة والوضوح</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	كما ترى في الشيفرة أعلاه، فقد أُضيفت بعض التعقيدات الإضافية لمنع تنفيذ بعض عمليات <code>‎new‎</code> و <code>‎delete‎</code>. وأصبح لدى الصنف مساحة أكبر للذاكرة، وقد لا يستخدَمها إلا في بضع حالات فقط.
</p>

<p>
	غالبًا ما سيُحاول ترميزَ القيمة البوليانية <code>‎_isAllocated‎</code> داخل المؤشّر <code>‎_buffer‎</code> عبر معالجة البتّات من أجل تقليل حجم النُسخة (intel 64 bit: يمكن أن يقلّل الحجم بمقدار 8 بايت)، لكن هذا التحسين غير ممكن إلا إن كانت قواعد المحاذاة الخاصّة بالمنصة معروفة.
</p>

<h3>
	حالات الاستخدام
</h3>

<p>
	نظرًا لأنّ هذا التحسين يضيف الكثير من التعقيد، فليس من المستحسن استخدامه في كل الأصناف. وستُصادفه غالبًا في بنيات البيانات منخفضة المستوى شائعة الاستخدام.
</p>

<p>
	قد تجد بعض الاستخدامات في <code>‎std::basic_string&lt;&gt;‎</code> و <code>‎std::function&lt;&gt;‎</code> في تطبيقات implementations المكتبة القياسية الشائعة في C++‎ 11.
</p>

<p>
	وبما أن هذا التحسين لا يمنع تخصيصات الذاكرة إلا عندما تكون البيانات المُخزّنة أصغر من القيمة المضافة، فستظهر فائدته بالخصوص في الأصناف التي تُستخدم غالبًا مع البيانات الصغيرة. لكن هناك عيب فيه وهو الحاجة إلى جهد إضافي عند تحريك المخزن المؤقت، وهذا يجعل عمليّات النقل أكثر كلفة موازنة بالحالات التي لا يُستخدم المخزن المؤقّت فيها، خاصة عندما يحتوي المخزن المؤقّت على نوع بيانات غير قديم non-POD type.
</p>

<h2>
	توسيع التضمين والتضمين
</h2>

<p>
	توسيع التضمين Inline expansion، أو التضمين inlining وحسب، هي تقنية تحسين يستخدمها المُصرِّف تقوم على تعويض استدعاء دالة بمتن تلك الدالّة. هذا يقلّل من الحمل الزائد overhead لاستدعاء الدوالّ، ولكن على حساب الذاكرة، إذ أنّ الدالّة قد تُكرّر عدّة مرات.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_23" style="">
<span class="com">// :المصدر</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> process</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> foo</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> process</span><span class="pun">(</span><span class="pln">a</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// :البرنامج بعد التضمين</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> foo</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> a</span><span class="pun">;</span><span class="pln"> </span><span class="com">// foo() في process() نسخ متن</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُستخدم تقنية التضمين غالبًا مع الدوال الصغيرة، حيث يكون الحمل الزائد لاستدعاء الدالّة صغيرًا مقارنةً بحجم جسم الدالّة.
</p>

<h2>
	تحسين الصنف الأساسي الفارغ Empty base optimization
</h2>

<p>
	يجب أن لا يقلّ حجم أي كائن أو عضو من كائن فرعي عن القيمة 1، حتى لو كان ذلك النوع فارغًا (أي حتى لو لم يحتوٍ الصنف <code>‎class‎</code> أو البنية <code>‎struct‎</code> على أعضاء بيانات غير ساكنة)، وذلك لأجل ضمان أن تكون عناوين الكائنات المختلفة التي تنتمي لنفس النوع مختلفة دائمًا.
</p>

<p>
	بالمقابل، فإنّ القيود على الكائنات الفرعية من الصنف الأساسي base class subobjects أقل بكثير، ويمكن تحسينها بشكل كامل انطلاقًا من مخطط الكائن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3738_25" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;cassert&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Base</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"> </span><span class="com">// صنف فارغ</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Derived1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Base</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// حجم أيّ كائن من صنف فارغ يساوي 1 على الأقل</span><span class="pln">
    assert</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Base</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// تطبيق تحسين الصنف الفارغ</span><span class="pln">
    assert</span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">Derived1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="typ">int</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُستخدم تحسين الصنف الفارغ عادةً في أصناف المكتبة القياسية الواعية بتخصيص كل من الذاكرة:
</p>

<ul>
<li>
		<code>std::vector‎</code>.
	</li>
	<li>
		<code>std::function</code>.
	</li>
	<li>
		<code>std::shared_ptr</code>.
	</li>
</ul>
<p>
	أو غيرها، وذلك لتجنّب شغل أي مساحة تخزين إضافية من قبل عضو مُخصِّص allocator member إذا كان ذلك المُخصّص عديم الحالة stateless. ويمكن تحقيق ذلك عن طريق تخزين أحد أعضاء البيانات المطلوبة، مثل: مؤشّرات <code>‎begin‎</code> و <code>‎end‎</code>، أو <code>‎capacity‎</code> الخاصّة بالمتجهات.
</p>

<h2>
	التشخيص Proﬁling
</h2>

<h3>
	التشخيص باستخدام gcc وgprof
</h3>

<p>
	يتيح لك برنامج التشخيص gprof الخاصّ بـ GNU gprof تشخيص شيفرتك. ولأجل لاستخدامه، عليك إنجاز الخطوات التالية:
</p>

<ol>
<li>
		ابنِ التطبيق مع الإعدادات المناسبة لتوليد معلومات التشخيص.
	</li>
	<li>
		قم بتوليد معلومات التشخيص عن طريق تشغيل التطبيق المبني.
	</li>
	<li>
		اعرض معلومات التشخيص المُولّدة باستخدام أداة gprof.
	</li>
</ol>
<p>
	أضف راية <code>‎-pg‎</code> لأجل بناء التطبيق مع إعدادات توليد معلومات التشخيص المناسبة، انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_9" style="">
<span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">pg </span><span class="pun">*.</span><span class="pln">cpp </span><span class="pun">-</span><span class="pln">o app</span></pre>

<p>
	أو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_11" style="">
<span class="pln">$ gcc </span><span class="pun">-</span><span class="pln">O2 </span><span class="pun">-</span><span class="pln">pg </span><span class="pun">*.</span><span class="pln">cpp </span><span class="pun">-</span><span class="pln">o app</span></pre>

<p>
	وهكذا دواليك. بمجرد إنشاء التطبيق (نفترض أنّ اسمه <code>‎app‎</code>)، نفِّذه كالمعتاد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_13" style="">
<span class="pln">$ </span><span class="pun">./</span><span class="pln">app</span></pre>

<p>
	من المفروض أن ينتج هذا الأمرُ ملفًّا يُسمّى <code>‎gmon.out‎</code>. إن أردت رؤية نتائج التشخيص، نفّذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_15" style="">
<span class="pln">$ gprof app gmon</span><span class="pun">.</span><span class="pln">out</span></pre>

<p>
	(لاحظ أننا وقرنا كلًّا من التطبيق وكذلك الخرج الناتج). ويمكنك توجيه الخرج أو إعادة توجيهه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_17" style="">
<span class="pln">$ gprof app gmon</span><span class="pun">.</span><span class="pln">out </span><span class="pun">|</span><span class="pln"> less</span></pre>

<p>
	يجب أن تكون نتيجة الأمر الأخير مطبوعة على شكل جدول، ستمثّل صفوفهُ الدوالَّ، أمّا أعمدته فتشير إلى عدد الاستدعاءات، وإجمالي الوقت المستغرق، والوقت الذاتي المستغرق - self time spent - (أي الوقت المستغرق داخل الدالة ما عدا استدعاءات الدوال الداخلية).
</p>

<h3>
	إنشاء مخططات الاستدعاءات callgraph بواسطة gperf2dot
</h3>

<p>
	بالنسبة للتطبيقات الكبيرة، فقد يصعب فهم تنسيق التشخيص المُستَوي ﬂat execution proﬁles، لهذا تنتج العديد من أدوات التشخيص رسومًا تخطيطية لتسهيل قراءتها.
</p>

<p>
	تحوّل أداة <a data-ss1617034501="1" data-ss1617034896="1" href="https://github.com/jrfonseca/gprof2dot" rel="external nofollow">gperf2dot</a> خرْج النص الذي تنتجه العديد من المُشخِّصات، مثل: Linux perf، وcallgrind، وoproﬁle إلى رسم تخطيطي، يمكنك استخدام هذه الأداة عن طريق تشغيل المشخّص profiler الذي تعمل به مثل <code>‎gprof‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_19" style="">
<span class="com"># التصريف مع رايات التشخيص</span><span class="pln">
g</span><span class="pun">++</span><span class="pln"> </span><span class="pun">*.</span><span class="pln">cpp </span><span class="pun">-</span><span class="pln">pg
</span><span class="com"># توليد بيانات التشخيص</span><span class="pln">
</span><span class="pun">./</span><span class="pln">main
</span><span class="com"># ترجمة بيانات التشخيص إلى نص، وإنشاء صورة</span><span class="pln">
gprof </span><span class="pun">./</span><span class="pln">main </span><span class="pun">|</span><span class="pln"> gprof2dot </span><span class="pun">-</span><span class="pln">s </span><span class="pun">|</span><span class="pln"> dot </span><span class="pun">-</span><span class="typ">Tpng</span><span class="pln"> </span><span class="pun">-</span><span class="pln">o output</span><span class="pun">.</span><span class="pln">png</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61066" data-ss1617034501="1" data-ss1617034896="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/QDQA0.png.d0c13297b6f99656a0422b41d5312956.png" rel=""><img alt="QDQA0.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61066" data-unique="v105hdmjl" src="https://academy.hsoub.com/uploads/monthly_2021_03/QDQA0.thumb.png.78cce5ce73294b5d31544a3b8c1bdf22.png"></a>
</p>

<h3>
	تشخيص استخدام وحدة المعالجة المركزية بواسطة gcc وGoogle Perf
</h3>

<p>
	توفّر <a data-ss1617034501="1" data-ss1617034896="1" href="https://github.com/gperftools/gperftools" rel="external nofollow">أدوات Google Perf</a> برنامج تشخيص لوحدة المعالجة المركزية CPU، مع واجهة استخدام سهلة نسبيًا.
</p>

<p>
	لاستخدامها عليك:
</p>

<ol>
<li>
		تثبيت أدوات Google Perf .
	</li>
	<li>
		تصريف الشيفرة كالمعتاد.
	</li>
	<li>
		إضافة مكتبة التشخيص <code>‎libprofiler‎</code> إلى مسار تحميل المكتبات في وقت التشغيل.
	</li>
	<li>
		استخدم <code>‎pprof‎</code> لتوليد تشخيص نصّي، أو رسم تخطيطي. مثلّا:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_21" style="">
<span class="pln">g</span><span class="pun">++</span><span class="pln"> </span><span class="pun">-</span><span class="pln">O3 </span><span class="pun">-</span><span class="pln">std</span><span class="pun">=</span><span class="pln">c</span><span class="pun">++</span><span class="lit">11</span><span class="pln"> main</span><span class="pun">.</span><span class="pln">cpp </span><span class="pun">-</span><span class="pln">o main
</span><span class="com"># تنفيذ المُشخِّص</span><span class="pln">
LD_PRELOAD</span><span class="pun">=/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">local</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">libprofiler</span><span class="pun">.</span><span class="pln">so CPUPROFILE</span><span class="pun">=</span><span class="pln">main</span><span class="pun">.</span><span class="pln">prof CPUPROFILE_FREQUENCY</span><span class="pun">=</span><span class="lit">100000</span><span class="pln"> </span><span class="pun">./</span><span class="pln">main</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		تشير <code>‎CPUPROFILE‎</code> إلى ملف بيانات التشخيص، و
	</li>
	<li>
		تشير <code>‎CPUPROFILE_FREQUENCY‎</code> إلى تردّد عيّنات المشخّص proﬁler sampling frequency. إن أردت إجراء المعالجة اللاحقة post-process لبيانات التشخيص، فاستخدم <code>‎pprof‎</code>، ويمكنك إنشاء تشخيص استدعاءات ﬂat call proﬁle على هيئة نصّ:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_6" style="">
<span class="pln">$ pprof </span><span class="pun">--</span><span class="pln">text </span><span class="pun">./</span><span class="pln">main main</span><span class="pun">.</span><span class="pln">prof
PROFILE</span><span class="pun">:</span><span class="pln"> interrupts</span><span class="pun">/</span><span class="pln">evictions</span><span class="pun">/</span><span class="pln">bytes </span><span class="pun">=</span><span class="pln"> </span><span class="lit">67</span><span class="pun">/</span><span class="lit">15</span><span class="pun">/</span><span class="lit">2016</span><span class="pln">
pprof </span><span class="pun">--</span><span class="pln">text </span><span class="pun">--</span><span class="pln">lines </span><span class="pun">./</span><span class="pln">main main</span><span class="pun">.</span><span class="pln">prof
</span><span class="typ">Using</span><span class="pln"> local file </span><span class="pun">./</span><span class="pln">main</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Using</span><span class="pln"> local file main</span><span class="pun">.</span><span class="pln">prof</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Total</span><span class="pun">:</span><span class="pln"> </span><span class="lit">67</span><span class="pln"> samples
</span><span class="lit">22</span><span class="pln"> </span><span class="lit">32.8</span><span class="pun">%</span><span class="pln"> </span><span class="lit">32.8</span><span class="pun">%</span><span class="pln"> </span><span class="lit">67</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> longRunningFoo </span><span class="pun">??:</span><span class="lit">0</span><span class="pln">
</span><span class="lit">20</span><span class="pln"> </span><span class="lit">29.9</span><span class="pun">%</span><span class="pln"> </span><span class="lit">62.7</span><span class="pun">%</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="lit">29.9</span><span class="pun">%</span><span class="pln"> __memmove_ssse3_back
</span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">string</span><span class="pun">/../</span><span class="pln">sysdeps</span><span class="pun">/</span><span class="pln">x86_64</span><span class="pun">/</span><span class="pln">multiarch</span><span class="pun">/</span><span class="pln">memcpy</span><span class="pun">-</span><span class="pln">ssse3</span><span class="pun">-</span><span class="pln">back</span><span class="pun">.</span><span class="pln">S</span><span class="pun">:</span><span class="lit">1627</span><span class="pln">
</span><span class="lit">4</span><span class="pln"> </span><span class="lit">6.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">68.7</span><span class="pun">%</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="lit">6.0</span><span class="pun">%</span><span class="pln"> __memmove_ssse3_back
</span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">string</span><span class="pun">/../</span><span class="pln">sysdeps</span><span class="pun">/</span><span class="pln">x86_64</span><span class="pun">/</span><span class="pln">multiarch</span><span class="pun">/</span><span class="pln">memcpy</span><span class="pun">-</span><span class="pln">ssse3</span><span class="pun">-</span><span class="pln">back</span><span class="pun">.</span><span class="pln">S</span><span class="pun">:</span><span class="lit">1619</span><span class="pln">
</span><span class="lit">3</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">73.1</span><span class="pun">%</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">%</span><span class="pln"> __random_r </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random_r</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">388</span><span class="pln">
</span><span class="lit">3</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">77.6</span><span class="pun">%</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">%</span><span class="pln"> __random_r </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random_r</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">401</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">80.6</span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> __munmap
</span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">misc</span><span class="pun">/../</span><span class="pln">sysdeps</span><span class="pun">/</span><span class="pln">unix</span><span class="pun">/</span><span class="pln">syscall</span><span class="pun">-</span><span class="kwd">template</span><span class="pun">.</span><span class="pln">S</span><span class="pun">:</span><span class="lit">81</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">83.6</span><span class="pun">%</span><span class="pln"> </span><span class="lit">12</span><span class="pln"> </span><span class="lit">17.9</span><span class="pun">%</span><span class="pln"> __random </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">298</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">86.6</span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> __random_r </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random_r</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">385</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">89.6</span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="lit">3.0</span><span class="pun">%</span><span class="pln"> rand </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">rand</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">26</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">91.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __memmove_ssse3_back
</span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">string</span><span class="pun">/../</span><span class="pln">sysdeps</span><span class="pun">/</span><span class="pln">x86_64</span><span class="pun">/</span><span class="pln">multiarch</span><span class="pun">/</span><span class="pln">memcpy</span><span class="pun">-</span><span class="pln">ssse3</span><span class="pun">-</span><span class="pln">back</span><span class="pun">.</span><span class="pln">S</span><span class="pun">:</span><span class="lit">1617</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">92.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __memmove_ssse3_back
</span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">string</span><span class="pun">/../</span><span class="pln">sysdeps</span><span class="pun">/</span><span class="pln">x86_64</span><span class="pun">/</span><span class="pln">multiarch</span><span class="pun">/</span><span class="pln">memcpy</span><span class="pun">-</span><span class="pln">ssse3</span><span class="pun">-</span><span class="pln">back</span><span class="pun">.</span><span class="pln">S</span><span class="pun">:</span><span class="lit">1623</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">94.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __random </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">293</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">95.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __random </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">296</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">97.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __random_r </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random_r</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">371</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">98.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> __random_r </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">random_r</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">381</span><span class="pln">
</span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1.5</span><span class="pun">%</span><span class="pln"> rand </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">rand</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">28</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">67</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> __libc_start_main </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">csu</span><span class="pun">/</span><span class="pln">libcstart</span><span class="pun">.</span><span class="pln">
c</span><span class="pun">:</span><span class="lit">287</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">67</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> _start </span><span class="pun">??:</span><span class="lit">0</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">67</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> main </span><span class="pun">??:</span><span class="lit">0</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">14</span><span class="pln"> </span><span class="lit">20.9</span><span class="pun">%</span><span class="pln"> rand </span><span class="pun">/</span><span class="pln">build</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">3GlaMS</span><span class="pun">/</span><span class="pln">eglibc</span><span class="pun">-</span><span class="lit">2.19</span><span class="pun">/</span><span class="pln">stdlib</span><span class="pun">/</span><span class="pln">rand</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">27</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">100.0</span><span class="pun">%</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">40.3</span><span class="pun">%</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">::</span><span class="pln">_M_emplace_back_aux </span><span class="pun">??:</span><span class="lit">0</span></pre>

<p>
	أو يمكنك إنشاء رسم تخطيطي في ملف pdf باستخدام الأمر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2598_23" style="">
<span class="pln">pprof </span><span class="pun">--</span><span class="pln">pdf </span><span class="pun">./</span><span class="pln">main main</span><span class="pun">.</span><span class="pln">prof </span><span class="pun">&gt;</span><span class="pln"> out</span><span class="pun">.</span><span class="pln">pdf</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617034501="1" data-ss1617034896="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصول:
</p>

<ul>
<li>
		 Chapter 143: Optimization in C++‎ 
	</li>
	<li>
		Chapter 144: Optimization
	</li>
	<li>
		Chapter 145: Profiling
	</li>
</ul>
<p>
	من كتاب <a data-ss1617034501="1" data-ss1617034896="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1191</guid><pubDate>Tue, 04 May 2021 13:02:00 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x648;&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x62A;&#x646;&#x642;&#x64A;&#x62D; &#x627;&#x644;&#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x648;&#x62A;&#x635;&#x62D;&#x64A;&#x62D; &#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D9%88%D8%AA%D8%B5%D8%AD%D9%8A%D8%AD-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-cpp-r1190/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061fb0a10955_64---------Cpp.png.726b9e77d29192689120784b2b5f6794.png" /></p>

<p>
	يقضي مطوّرو C++‎ الكثير من وقتهم في تنقيح الأخطاء (debugging)، ويهدف هذا المقال إلى مساعدتك في هذه المهمة وإعطائك بعض التقنيات المفيدة، لكن لا تتوقع قائمة شاملة للمشاكل وحلولها التي تدعمها هذه الأدوات.
</p>

<h2>
	اختبار الوحدات في C++‎
</h2>

<p>
	يسعى اختبار الوحدات (Unit testing) إلى التحقق من صحّة وسلامة وحدات الشيفرة (units of code)، ويشير مصطلح "وحدات الشيفرة" في C++‎ غالبًا إلى الأصناف أو الدوال أو مجموعات مؤلّفة منهما.
</p>

<p>
	ويُجرى اختبار الوحدات غالبًا باستخدام "أُطر اختبار" متخصصة أو "مكتبات اختبار"، والتي تستخدِم غالبًا صياغات أو أنماط غير اعتيادية، وسنراجع في هذا الموضوع بعض استراتيجيات ومكتبات وأطر اختبار الوحدات.
</p>

<h3>
	اختبار جوجل
</h3>

<p>
	<a data-ss1617033982="1" data-ss1617034287="1" href="https://github.com/google/googletest" rel="external nofollow">اختبار جوجل</a> (Google Test) هو إطار لاختبار C++‎ من Google، ويتطلّب استخدامُه بناء مكتبة <code>‎gtest‎</code> وربطها بإطار الاختبار الخاص بك.
</p>

<p>
	مثال بسيط، سنقسم المثال بداعي الشرح …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_7" style="">
<span class="com">// main.cpp</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;gtest/gtest.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span></pre>

<p>
	أنشئت حالات اختبار جوجل بواسطة وحدات ماكرو خاصة بالمعالج الأولي لـ ++C، وسنوفر المعامِليْن <code>test name</code> و <code>test suite</code>، نتابع …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_9" style="">
<span class="pln">TEST</span><span class="pun">(</span><span class="pln">module_name</span><span class="pun">,</span><span class="pln"> test_name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Hello world!"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span></pre>

<p>
	كذلك توفر Google Test وحدات ماكرو لأجل التوكيدات (assertions):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_11" style="">
<span class="pln">    ASSERT_EQ</span><span class="pun">(</span><span class="lit">1</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن تشغيل اختبار جوجل يدويًا من دالة <code>()main</code> أو ربطها بمكتبة <code>gtest_main</code> لدالة <code>()main</code> معدَّة سلفًا لقبول حالات اختبار جوجل، …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_13" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">**</span><span class="pln"> argv</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">::</span><span class="pln">testing</span><span class="pun">::</span><span class="typ">InitGoogleTest</span><span class="pun">(&amp;</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> RUN_ALL_TESTS</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// أمر البناء: g++ main.cpp -lgtest</span></pre>

<h3>
	catch
</h3>

<p>
	<a data-ss1617033982="1" data-ss1617034287="1" href="https://github.com/catchorg/Catch2" rel="external nofollow">Catch</a> هي مكتبة ترويسة (header only library) تسمح لك باستخدام كل من نمطي الاختبار TDD و BDD.
</p>

<p>
	<a data-ss1617033982="1" data-ss1617034287="1" href="https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md" rel="external nofollow">الشيفرة التالية مأخوذة من صفحة توثيق Catch</a>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_15" style="">
<span class="pln">SCENARIO</span><span class="pun">(</span><span class="str">"vectors can be sized and resized"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"[vector]"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    GIVEN</span><span class="pun">(</span><span class="str">"A vector with some items"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> v</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">

        REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
        REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">capacity</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">

        WHEN</span><span class="pun">(</span><span class="str">"the size is increased"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            v</span><span class="pun">.</span><span class="pln">resize</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">

            THEN</span><span class="pun">(</span><span class="str">"the size and capacity change"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">capacity</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        WHEN</span><span class="pun">(</span><span class="str">"the size is reduced"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            v</span><span class="pun">.</span><span class="pln">resize</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">

            THEN</span><span class="pun">(</span><span class="str">"the size changes but not capacity"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">capacity</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        WHEN</span><span class="pun">(</span><span class="str">"more capacity is reserved"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            v</span><span class="pun">.</span><span class="pln">reserve</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">

            THEN</span><span class="pun">(</span><span class="str">"the capacity changes but not the size"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">capacity</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        WHEN</span><span class="pun">(</span><span class="str">"less capacity is reserved"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            v</span><span class="pun">.</span><span class="pln">reserve</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">

            THEN</span><span class="pun">(</span><span class="str">"neither size nor capacity are changed"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
                REQUIRE</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">capacity</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيُبلَغ عن هذه الاختبارات على النحو التالي عند التشغيل:
</p>

<pre class="ipsCode">
Scenario: vectors can be sized and resized Given: A vector with some items When: more capacity is reserved Then: the capacity changes but not the size
</pre>

<h2>
	التحليل الساكن Static analysis
</h2>

<p>
	التحليل الساكن طريقة لتفحّص الشيفرة للبحث عن الأنماط التي ترتبط عادة بالأخطاء المشهورة وتدل عليها، هذه التقنية تستغرق وقتًا أقل من مراجعة الشيفرة، لكن تقتصر عمليات التحقق التي تقوم بها على الحالات المُبرمجة في الأداة. وتتضمّن عمليات مثل التحقّق استخدام الفاصلة المنقوطة بشكل غير صحيح بعد عبارة <code>‎(if (var);)‎</code> مثلًا، وخوارزميات الشُّعَب (graph algorithms) المتقدمة التي تحدد ما إذا كان المتغيّر مُهيَّأً أم لا.
</p>

<h3>
	تحذيرات المُصرِّف Compiler warnings
</h3>

<p>
	من السهل تمكين التحليل الساكن، فهناك إصدارات مُبسّطة مُضمّنة سلفًا في مصرّفك:
</p>

<ul>
<li>
		<a data-ss1617033982="1" data-ss1617034287="1" href="https://clang.llvm.org/docs/DiagnosticsReference.html" rel="external nofollow"><code>clang++ -Wall -Weverything -Werror ...‎</code></a>
	</li>
	<li>
		<a data-ss1617033982="1" data-ss1617034287="1" href="https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html" rel="external nofollow"><code>g++ -Wall -Weverything -Werror ...‎</code></a>
	</li>
	<li>
		<a data-ss1617033982="1" data-ss1617034287="1" href="https://docs.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level?view=vs-2019" rel="external nofollow"><code>cl.exe /W4 /WX ...‎</code></a>
	</li>
</ul>
<p>
	إذا مكّنتَ هذه الخيارات، ستلاحظ أنّ كل مُصرِّف سيعثر على أخطاء لا تنتبه إليها المصرّفات الأخرى، وستحصل على أخطاء على تقنيات قد تكون صالحة في سياقات محدّدة. فمثلًا، قد تكون <code>‎while (staticAtomicBool);‎</code> مقبولة حتى لو لم تكن <code>‎while (localBool);‎</code> كذلك.
</p>

<p>
	لذلك، على عكس مراجعة الشيفرة، فأنت تستخدم أداة تفهم شيفرتك، وتنبّهك إلى الكثير من الأخطاء، وأحيانًا قد لا تتفق معك. قد تضطر إلى وقف التحذير محليًا في هذه الحالة نظرًا لأنّ الخيارات المذكورة أعلاه قد تُمكِّن جميع التحذيرات، بما في ذلك التحذيرات التي لا تريدها (مثلًا، لماذا يجب أن تكون شيفرتك متوافقة مع C++‎ 98؟). في هذه الحالة، يمكنك تعطيل تحذير بعينه:
</p>

<ul>
<li>
		<p>
			<code>clang++ -Wall -Weverything -Werror -Wno-errortoaccept ...‎</code>
		</p>
	</li>
	<li>
		<p>
			‎‎<code>‎‎g++ -Wall -Weverything -Werror -Wno-errortoaccept ...‎</code>‎
		</p>
	</li>
	<li>
		<p>
			‎‎<code>‎‎cl.exe /W4 /WX /wd&lt;no of warning&gt;...‎</code>‎
		</p>

		<p>
			صحيح أنّ تحذيرات المصرّف ستساعدك أثناء التطوير، إلّا أنّها تُبطئ عملية التصريف، لهذا قد لا ترغب في تمكينها افتراضيًا في كل مرة، فإما أن تشغّل تحذيرات المصرّف افتراضيًا، أو يمكنك تمكين التكامل المستمر (continuous integration) باستخدام عمليات تحقق أكثر كلفة (أو كليهما).
		</p>
	</li>
</ul>
<h3>
	أدوات خارجية
</h3>

<p>
	إذا قرّرت العمل بالتكامل المستمر (continuous integration)، فإنّ استخدام الأدوات الأخرى ليست ذات فائدة. وتحتوي أداة مثل clang-tidy على قائمة من عمليّات التحقق التي تغطي مجموعة واسعة من المشاكل، منها:
</p>

<ul>
<li>
		الأخطاء الفعلية
	</li>
	<li>
		منع التشريح (Prevention of slicing)
	</li>
	<li>
		توكيدات (Asserts) مع آثار جانبية
	</li>
	<li>
		التحقّق من إمكانية القراءة
	</li>
	<li>
		إزاحات بادئة (indentation) غير صحيحة
	</li>
	<li>
		التحقق من تسمية المُعرِّف (Check identifier naming)
	</li>
	<li>
		التحقّق من التحديث (Modernization checks)
	</li>
	<li>
		استخدام <code>make_unique()‎</code>
	</li>
	<li>
		استخدام nullptr *التحقّق من الأداء
	</li>
	<li>
		إيجاد النسخ غير الضرورية
	</li>
	<li>
		البحث عن خوارزميات الاستدعاء غير الفعّالة
	</li>
</ul>
<p>
	قد لا تكون القائمة كبيرة، إذ أنّ Clang تحتوي على الكثير من التحذيرات، إلّا أنها ستجعل شيفرتك أكثر أمانًا.
</p>

<h3>
	أدوات أخرى
</h3>

<p>
	توجد أدوات أخرى مماثلة بنفس الغرض، مثل:
</p>

<ul>
<li>
		<a data-ss1617033982="1" data-ss1617034287="1" href="https://blogs.msdn.microsoft.com/hkamel/2013/10/24/visual-studio-2013-static-code-analysis-in-depth-what-when-and-how/" rel="external nofollow">محلل visual studio الساكن</a> كأداة خارجية
	</li>
	<li>
		<a data-ss1617033982="1" data-ss1617034287="1" href="https://blogs.kde.org/2015/11/15/new-cqt-code-checks-clazy-static-analyzer" rel="external nofollow">clazy</a>، إضافة خاصّة بمصرّف Clang للتحقق من شيفرات Qt
	</li>
</ul>
<h3>
	الخلاصة
</h3>

<p>
	توجد الكثير من أدوات التحليل الساكن لـ C++‎، سواء المضمّنة في المُصرِّف، أو الأدوات الخارجية، ولا تأخذ تجربتها الكثير من الوقت عند الاكتفاء بالعمل بالإعدادات السهلة، وستساعدك على رصد أخطاء قد لا تنتبه لها عند مراجعة الشيفرة.
</p>

<h2>
	تحليل Segfault مع GDB
</h2>

<p>
	سنستخدم نفس الشيفرة التي استخدمناها في المثال أعلاه.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_17" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> fail</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p1</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p2</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p3 </span><span class="pun">=</span><span class="pln"> p1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p2 </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fail</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أولًا، سنحاول تصريفها:
</p>

<pre class="ipsCode">
g++ -g -o main main.cpp
</pre>

<p>
	ثم نشغّلها بواسطة gdb:
</p>

<pre class="ipsCode">
gdb ./main
</pre>

<p>
	الآن، سنكون في صدفة gdb. اكتب الأمر run.
</p>

<pre class="ipsCode">
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/opencog/code-snippets/stackoverflow/a.out
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400850 in fail () at debugging_with_gdb.cc:11
11 std::cout &lt;&lt; *p2 &lt;&lt; std::endl;
</pre>

<p>
	لاحظ أنّ خطأ التجزئة (segmentation fault) قد حدث في السطر 11، لذا فالمتغيّر الوحيد المُستخدم في هذا السطر هو المؤشّر p2. يمكن التحقّق من محتواه بطباعة ما يلي:
</p>

<pre class="ipsCode">
(gdb) print p2
$1 = (int *) 0x0
</pre>

<p>
	نلاحظ أنّ p2 هُيِّئ عند القيمة 0×0، والتي تكافئ NULL. في هذا السطر، حاولنا تحصيل (dereference) مؤشّر فارغ، لذا علينا إصلاح هذا الخلل.
</p>

<h2>
	تنظيف الشيفرات
</h2>

<p>
	يبدأ التنقيح بفهم الشيفرة التي تحاول تنقيحها. انظر المثال التالي عن شيفرة سيّئة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_19" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> vectorToSort</span><span class="pun">;</span><span class="pln">
    vectorToSort</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
    vectorToSort</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">13</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">52</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        vectorToSort</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">i </span><span class="pun">*</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">/// مُحسّنة لاستخدامها في ترتيب المتجهات الصغيرة</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">end</span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> vectorToSort</span><span class="pun">)</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> value </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">' '</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذه الشيفرة أفضل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6494_21" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> createSemiRandomData</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
    data</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
    data</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">13</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">52</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> </span><span class="pun">--</span><span class="pln">i</span><span class="pun">)</span><span class="pln">
        vectorToSort</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">i </span><span class="pun">*</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
    </span><span class="com">/// مُحسّنة لاستخدامها في ترتيب المتجهات الصغيرة</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> sortVector</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> vectorToSort</span><span class="pun">.</span><span class="pln">end</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> printVector</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="str">&lt;int&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">auto</span><span class="pln"> i</span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> i </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">' '</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> vectorToSort </span><span class="pun">=</span><span class="pln"> createSemiRandomData</span><span class="pun">();</span><span class="pln">
    sortVector</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">ref</span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">));</span><span class="pln">
    printVector</span><span class="pun">(</span><span class="pln">vectorToSort</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بغض النظر عن نمط التشفير الذي تفضله، سيساعدك الثّبات على نفس النمط على فهم شيفرتك، ويمكن رصد فرصتين لتحسين قراءة الشيفرة بالنظر إلى الشيفرة أعلاه، وكذلك تسهيل تنقيحها:
</p>

<h3>
	استخدام دوال منفصلة للإجراءات المنفصلة
</h3>

<p>
	يتيح لك استخدام دوال منفصلة تخطّي بعض الدوال في المنقّح إذا لم تكن مهتمًّا بتفاصيلها. قد لا تكون مهتمًا بتفاصيل إنشاء البيانات أو طباعتها في هذه الحالة الخاصّة، وقد ينصبّ اهتمامك على عملية الترتيب وحَسب.
</p>

<p>
	هناك ميزة أخرى، وهي أنّ الشيفرة التي عليك قراءتها وحفظها ستكون أقصر عند تفحّص الشيفرة، إذ ستحتاج الآن إلى قراءة 3 أسطر فقط من الشيفرة في دالة <code>‎main()‎</code> لفهمها، بدلًا من قراءة الدالّة بأكملها.
</p>

<p>
	والميزة الثالثة هي أنّه نظرًا لكون الشيفرةً المُتحقَّق منها أقصر، فإن عينك ستتدرب الآن على رصد الأخطاء في غضون ثوانٍ.
</p>

<h3>
	استخدام تنسيقات وإنشاءات متّسقة
</h3>

<p>
	استخدامُ التنسيقات والإنشاءات المتّسقة ينظّم الشيفرةَ ويسهّل تحليلها، وهناك نقاشات كثيرة حول طريقة التنسيق "الأمثل". لكن بغضّ النظر فالمهمّ أن تلتزم بنمط برمجة واحد في شيفرتك، فذلك سيحسّن قراءتها ويسهّل التركيز على محتوى الشيفرة.
</p>

<p>
	ويوصى باستخدام أداة مخصّصة لتنسيق الشيفرة نظرًا لأنّ هذه المهمّة قد تستغرق وقتًا طويلاً، وتدعم معظم بيئات التطوير المتكاملة (IDEs) هذه الميزة بشكل أو بآخر.
</p>

<p>
	قد تلاحظ أنّ نمط البرمجة لا يقتصر على استخدام المسافات الفارغة والسطور الجديدة، فلم نعد نمزج بين النمط الحر (free-style) والدوال التابعة لبدء / إنهاء الحاويات (<code>‎v.begin()‎</code> و <code>‎std::end(v)‎</code>).
</p>

<h3>
	تسليط الضوء على الأجزاء الهامّة من شيفرتك
</h3>

<p>
	بغض النظر عن النمط الذي تستخدمه، فإنّ الشيفرة أعلاه تحتوي على عدد من الملاحظات التي قد تعطيك تلميحًا حول الأمور المهمّة في الشيفرة:
</p>

<ul>
<li>
		تعليق يتضمّن الكلمة "مُحسَّن" ، في إشارة إلى استخدام بعض التقنيات الجذابة.
	</li>
	<li>
		بعض تعليمات <code>return</code> المبكّرة في <code>‎sortVector()‎</code> تشير إلى أنّنا نريد فعل شيء خاصّ.
	</li>
	<li>
		<code>‎std::ref()‎</code> - تشير إلى أنّ شيئًا ما يحدث في <code>‎sortVector()‎</code>.
	</li>
</ul>
<h3>
	الخلاصة
</h3>

<p>
	الشيفرات النظيفة أسهل في الفهم وتقلل الوقت اللازم لتنقيحهَا، فقد يتمكّن مُراجِع الشيفرة من رصد الأخطاء منذ الوهلة الأولى في المثال الثاني، بينما قد تكون الأخطاء أخفَى في المثال الأوّل. (<strong>ملاحظة</strong> الخطأ موجود في: عملية الموازنة مع العدد <code>‎2‎</code>.)
</p>

<p>
	هذا الدرس جزء من <a data-ss1617033982="1" data-ss1617034287="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 142: C++ Debugging and Debugprevention Tools &amp; Techniques والفصل Chapter 141: Unit Testing in C++‎ من كتاب <a data-ss1617033982="1" data-ss1617034287="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1190</guid><pubDate>Mon, 29 Mar 2021 16:12:01 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x62A;&#x635;&#x631;&#x64A;&#x641; &#x648;&#x628;&#x646;&#x627;&#x621; &#x627;&#x644;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x627;&#x644;&#x645;&#x643;&#x62A;&#x648;&#x628;&#x629; &#x628;&#x644;&#x63A;&#x629; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D9%88%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-r1189/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061f4f56e070_63------Cpp-.png.187528abcb81ef505041a0f91d330be5.png" /></p>

<p>
	ينبغي تصريف البرامج المكتوبة بلغة C++‎ قبل أن تتمكن تلك البرامج من العمل، وستجد مجموعة كبيرة ومتنوّعة من برامج التصريف أو المصرِّفات (compilers) المتاحة والمناسبة لنظام التشغيل الذي تعمل به.
</p>

<h2>
	التصريف بواسطة GCC
</h2>

<p>
	التصريف دون تحسين مفيد في المراحل الأولى من التطوير والتنقيح، على الرغم من أنّ خيار <code>‎-Og‎</code> موصىً به في الإصدارات الحديثة من GCC، وبناء عليه فيمكن تصريف وربط ملف تنفيذي وغير محسَّن على افتراض أن هناك ملفًا مصدريًا واحدًا يسمى <code>main.cpp</code>، وذلك على النحو التالي:
</p>

<pre class="ipsCode">
g++ - o app - Wall main.cpp - O0
</pre>

<p>
	ولإنتاج ملف مُحسّن قابل للتنفيذ لاستخدامه في الإنتاج، استخدم أحد خيارات <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html" rel="external nofollow"><code>‎-O‎</code></a> (راجع: ‎،<code>-O1‎</code>، <code>‎-O2‎</code>، <code>‎-O3‎</code>، <code>‎-Os‎</code>، <code>‎-‎‎Ofast</code>‎):
</p>

<pre class="ipsCode">
g++ - o app - Wall - O2 main.cpp
</pre>

<p>
	في حال حذف الخيار <code>‎-O</code>، سيُستخدَم <code>‎-O0</code> الذي يعني إلغاء التحسينات، كخيار افتراضي، واعلم أن تحديد <code>‎-O</code> بدون رقم يجعله مساويًا لـ <code>‎-O1</code>.
</p>

<p>
	وكخيار بديل، يمكن أن نستخدم رايات التحسين (optimization flags) من مجموعات <code>‎O‎</code> -أو تحسينات تجريبية أخرى- مباشرةً. انظر المثال التالي حيث يبني البرنامج مع التحسين <code>‎-O2‎</code>، بالإضافة إلى راية واحدة من مستوى التحسين <code>‎-O3‎</code>:
</p>

<pre class="ipsCode">
g++ -o app -Wall -O2 -ftree-partial-pre main.cpp
</pre>

<p>
	لإنتاج ملفّ تنفيذي مُحسّن لأجل منصة معيّنة -لاستخدامه على جهاز له نفس المعمارية-، استخدم:
</p>

<pre class="ipsCode">
g++ -o app -Wall -O2 -march=native main.cpp
</pre>

<p>
	سوف ينتُج عن الشيفرَتين أعلاه ملفٌّ ثنائي (binary ﬁle) يمكن تشغيله باستخدام <code>‎.\app.exe‎</code> على Windows، أو باستخدام <code>‎./app‎</code> على Linux و Mac OS، كما يمكن أيضًا تخطي الراية <code>‎-o‎</code>، وفي هذه الحالة سينشئ GCC الملفّ التنفيذي الافتراضي <code>‎a.exe‎</code> على Windows و <code>‎a.out‎</code> على الأنظمة الشبيهة بيونكس (Unix-like).
</p>

<p>
	لأجل تصريف ملف دون ربطه، استخدم الخيار <code>‎-c‎</code>:
</p>

<pre class="ipsCode">
g++ -o file.o -Wall -c file.cpp
</pre>

<p>
	سينتج عن هذا ملفٌّ يحمل الاسم <code>‎file.o‎</code>، والذي يمكن ربطه لاحقًا بملفّات أخرى لإنتاج ملف ثنائي:
</p>

<pre class="ipsCode">
g++ -o app file.o otherfile.o
</pre>

<p>
	انظر <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="gcc.gnu.org" rel="">gcc.gnu.org</a> للمزيد من التفاصيل حول خيارات التحسين، خاصة <code>‎-Og‎</code>، وهو التحسين مع التركيز على تجربة التنقيح (debugging)، الذي يوصى باستخدامه في دورات تحرير-تصريف-تنقيح القياسية - standard edit-compile-debug cycle)، وكذلك <code>‎-Ofast‎</code> الذي يشمل جميع التحسينات، بما في ذلك تلك التي تتجاهل الامتثال الصارم للمعايير.
</p>

<p>
	تُمكِّنك راية <code>‎-Wall‎</code> من إطلاق تحذيرات من بعض الأخطاء الشائعة، وينبغي أن تُستخدم دائما، ويوصى باستخدام <code>‎-Wextra‎</code> لتحسين جودة الشيفرة، وكذلك رايات التحذير الأخرى التي لا تُمكَّن تلقائيًا من قِبل <code>‎-Wall‎</code> و <code>‎-‎‎Wextra‎</code>.
</p>

<p>
	إذا كانت الشيفرة تتوقع معيارًا محدّدًا للغة C++‎، فيمكنك تحديد المعيار الذي تريد استخدامه عن طريق تضمين راية <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html#index-std-112" rel="external nofollow"><code>‎-std=‎</code></a>. وتتوافق القيم المدعومة مع سنة الإصدار النهائي لمعيار ISO C++‎، واعتبارا من GCC 6.1.0 فإنّ القيم الصالحة لـ <code>‎std=‎</code> هي: <code>‎c++98‎</code> / <code>‎c++03‎</code>، <code>‎c++11‎</code>، <code>‎c++14‎</code>، و<code>‎c++17‎</code> / <code>‎c++1z‎</code>. لاحظ أن القيم المفصولة بشرطةٍ مائلة <code>/</code> متكافئة.
</p>

<pre class="ipsCode">
g++ -std=c++11 &lt;file&gt;
</pre>

<p>
	يتضمّن GCC بعض الإضافات (Extensions) (extensions) الخاصة بالمُصرِّف، والتي تُعطَّل عندما تتعارض مع معيار قياسي محدّد من قبل راية <code>‎-std=‎</code>، فإذا أردت التصريف مع كل الإضافات (Extensions) المُمَكَّنة، فاستخدام القيمة <code>‎gnu++XX‎</code>، حيث تمثّل <code>‎XX‎</code> أيًّا من السنوات المذكورة أعلاه.
</p>

<p>
	ويُستخدَم المعيار الافتراضي في حالة عدم تعريف أيّ منها، وبالنسبة لإصدارات GCC التي تسبق 6.1.0 فإنّ الإعداد الافتراضي هو <code>‎</code>-‎std‎ = ‎gnu‎++03، أما في GCC 6.1.0 والإصدارات الأحدث فإن الإعداد الافتراضي هو <code>‎-std=gnu++14‎</code>.
</p>

<p>
	لاحظ أنّه نظرا لوجود بعض الأخطاء (bugs) في GCC، فيجب أن تكون راية <code>pthread-</code> حاضرة في التصريف والربط إن أردت أن يدعم GCC معيار الخيوط (threading) الذي أُدخل في C++‎11، مثل <code>‎std::thread‎</code> و <code>‎std::wait_for‎</code>. قد لا ينتج عن حذفها عند استخدام الخيوط أيّ تحذيرات، ولكن قد تحصل على نتائج غير صحيحة في بعض المنصّات.
</p>

<h3>
	الربط بالمكتبات Linking with libraries
</h3>

<p>
	استخدم خيار <code>‎-l‎</code> لتمرير اسم المكتبة:
</p>

<pre class="ipsCode">
g++ main.cpp -lpcre2-8
# 8bit code units (UTF-8) في وحدات الشيفرات ثمانية البتّات PCRE2 هي مكتبة pcre2-8 
</pre>

<p>
	إذا لم تكن المكتبة موجودة في مسار المكتبة القياسية، فأضف المسار باستخدام <code>‎-L‎</code>:
</p>

<pre class="ipsCode">
g++ main.cpp -L/my/custom/path/ -lmylib
</pre>

<p>
	يمكن ربط عدّة مكتبات معًا:
</p>

<pre class="ipsCode">
g++ main.cpp -lmylib1 -lmylib2 -lmylib3
</pre>

<p>
	إذا كانت إحدى المكتبات تعتمد على مكتبة أخرى، فضع المكتبة المعتِمدة قبل المكتبة المستقلة:
</p>

<pre class="ipsCode">
g++ main.cpp -lchild-lib -lbase-lib
</pre>

<p>
	أو اترك الرابط (linker) يتكفّل بتحديد الترتيب عبر الخيارات <code>‎--start-group‎</code> و <code>‎--end-group‎</code> (<strong>ملاحظة</strong> هذا له تكلفة كبيرة على الأداء):
</p>

<pre class="ipsCode">
g++ main.cpp -Wl,--start-group -lbase-lib -lchild-lib -Wl,--end-group
</pre>

<h2>
	التصريف باستخدام Visual Studio (واجهة رسومية)
</h2>

<ol>
<li>
		نزِّل <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://visualstudio.microsoft.com/" rel="external nofollow">Visual Studio Community 2015</a> وثبِّته.
	</li>
	<li>
		افتح Visual Studio Community .
	</li>
	<li>
		انقر على File، ثم NewK ثم Project.
	</li>
</ol>
<p style="text-align: center;">
	<img alt="bFNzb.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61051" data-unique="ikyfxl82s" src="https://academy.hsoub.com/uploads/monthly_2021_03/bFNzb.png.5d7caa8026df0f807fcea229e7426255.png"></p>

<ol start="4">
<li>
		انقر على Templates ثم ++Visual C ثم Win32 Console Application، ثم ضع اسمًا للمشروع وليكن MyFirstProgram.
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61055" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/kYTy1.png.f01bbee7b1e75e3d91a6f0b130268cc9.png" rel=""><img alt="kYTy1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61055" data-unique="1n31mbdd3" src="https://academy.hsoub.com/uploads/monthly_2021_03/kYTy1.thumb.png.107e787d344cc0160ab603348e792252.png"></a>
</p>

<ol start="5">
<li>
		انقر على Ok.
	</li>
	<li>
		انقر على "Next " في النافذة التالية.
	</li>
</ol>
<p style="text-align: center;">
	<img alt="Rebpz.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61058" data-unique="9nc0v75d6" src="https://academy.hsoub.com/uploads/monthly_2021_03/Rebpz.png.45b42ea77feafe8b2e16be37cec5dad6.png"></p>

<ol start="7">
<li>
		اختر الخانة <code>‎Empty project‎</code> تحت الخيارات الإضافية (Additional options) ثم انقر على "Finish":
	</li>
</ol>
<p style="text-align: center;">
	<img alt="P0P5J.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61056" data-unique="yrmww9q3f" src="https://academy.hsoub.com/uploads/monthly_2021_03/P0P5J.png.7eb78f41d1df17d73584c91bd02223c8.png"></p>

<ol start="8">
<li>
		انقر بالزر الأيمن فوق مجلد ملف المصدر ثم ‎Add، ثم New Item:
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61052" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/DLwEd.png.176b51fb070b8c1e387e56074a1fddb1.png" rel=""><img alt="DLwEd.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61052" data-unique="sz57ailvn" src="https://academy.hsoub.com/uploads/monthly_2021_03/DLwEd.thumb.png.d161bf682c81ce98b3960607e9ec78bb.png"></a>
</p>

<ol start="9">
<li>
		حدّد ملف C++‎ وقم بتسمية main.cpp، ثم انقر فوق Add:
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61063" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/zQaws.png.bb5ef4e8e3b285b4a6f4baf1d5110d29.png" rel=""><img alt="zQaws.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61063" data-unique="nc3v4q3f3" src="https://academy.hsoub.com/uploads/monthly_2021_03/zQaws.thumb.png.4b55e06985257aec0cce07a3c62a4b2e.png"></a>
</p>

<ol start="10">
<li>
		انسخ والصق الشيفرة التالية في الملف الجديد main.cpp:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_7" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Hello World!\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ينبغي أن تبدو بيئة العمل لديك كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61059" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/vTBkv.png.6a0f87287d2584af1f2061731d52f9bf.png" rel=""><img alt="vTBkv.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61059" data-unique="q406ipzf2" src="https://academy.hsoub.com/uploads/monthly_2021_03/vTBkv.thumb.png.d87ceb6f2581167ef831dd99e34fe705.png"></a>
</p>

<ol start="11">
<li>
		انقر على Debug ثم Start Without Debugging، (أو اضغط على ctrl + F5):
	</li>
</ol>
<p style="text-align: center;">
	<img alt="B3twO.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61050" data-unique="cdc2glq4w" src="https://academy.hsoub.com/uploads/monthly_2021_03/B3twO.png.94afe6f7367cfc0c2b017158d09ce232.png"></p>

<ol start="12">
<li>
		يجب أن تحصل على الخرج التالي في سطر الأوامر:
	</li>
</ol>
<p style="text-align: center;">
	<img alt="1AwnS.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61049" data-unique="i5fo1558p" src="https://academy.hsoub.com/uploads/monthly_2021_03/1AwnS.png.18f3c3edbb4b1d4ec36024e7f92d29ef.png"></p>

<h2>
	مُصرِّفات الشبكة Online Compilers
</h2>

<p>
	توفّر العديد من المواقع مصرّفات C++‎ يمكن الوصول إليها عبر شبكة الإنترنت، وتختلف مزايا وإمكانيات مصرّفات الشبكة من موقع إلى آخر، ولكنها عادة ما تسمح بالقيام بما يلي:
</p>

<ul>
<li>
		لصق الشيفرة في نموذج في المتصفّح.
	</li>
	<li>
		تحديد بعض خيارات المصرّف، وتصريف الشيفرة.
	</li>
	<li>
		الحصول على خرج المصرّف و/أو البرنامج.
	</li>
</ul>
<p>
	عادةً ما تكون المُصرِّفات الشبكية مقيّدة إذ أنّها تتيح لأيّ شخص تشغيل المصرّف وتنفيذ شيفرات عشوائية على الخادم، وهذا قد يكون مصدر خطر في حال تنفيذ شيفرات ضارة.
</p>

<p>
	قد تكون مصرّفات الشبكة مفيدة للأغراض التالية:
</p>

<ul>
<li>
		تشغيل مقتطف صغير من شيفرة جهاز يفتقر إلى مصرّف C++‎ (مثل الهواتف الذكية والأجهزة اللوحية وما إلى ذلك).
	</li>
	<li>
		التحقّق من أنّ الشيفرة تُصرَّف بنجاح في مختلف المُصرِّفات، وتعمل بنفس الطريقة بغض النظر عن المُصرِّف الذي صُرِّفت فيه.
	</li>
	<li>
		تعلُّم أو تعليم أساسيات C++‎.
	</li>
	<li>
		تعلّم ميزات C++‎ الحديثة (C++‎ 14 و C++‎ 17 في المستقبل القريب) إذا لم يكن لديك مصرّف C++‎ حديث على جهازك.
	</li>
	<li>
		رصد الأخطاء التي يمكن أن تكون موجودة في المصرّف الذي تعمل به بمقارنته بمجموعة كبيرة من المصرّفات الأخرى.
	</li>
	<li>
		التحقق ممّا إذا كانت الإصدارات اللاحقة من المصرّف الذي تعمل قد صُحِّحت في حال لم تتوفر تلك الإصدارات على جهازك.
	</li>
	<li>
		حل المشاكل والتمارين عبر الشبكة.
	</li>
</ul>
<p>
	بالمقابل، <strong>لا ينبغي</strong> استخدام مُصرِّفات الشبكة لأجل:
</p>

<ul>
<li>
		<p>
			تطوير برامج كاملة (ولو كانت صغيرة) باستخدام C++‎، وفي العادةً فإن مصرّفات الشبكة لا تسمح بالارتباط مع مكتبات خارجية، أو تنزيل أدوات البناء.
		</p>
	</li>
	<li>
		<p>
			إجراء حسابات مكثفة. موارد الخادم الحسابية محدودة، لذلك سيوقَف أيّ برنامج يقدّمه المستخدم بعد بضع ثوانٍ من بدء تنفيذه، فوقت التنفيذ المسموح به يكفي عادة للاختبار والتعلم فقط.
		</p>
	</li>
	<li>
		<p>
			قرصنة ومهاجمة الخادم الذي يعمل عليه المُصرِّف، أو أيّ جهة خارجية مُستضافة على الشبكة.
		</p>

		<p>
			أمثلة على المصرفات الشبكية:
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://codepad.org/" rel="external nofollow">codepad.org</a>: مُصرِّف مع إمكانية مشاركة الشيفرة، وتحرير الشيفرة بعد التصريف، لكن التحذيرات و الأخطاء لا تعمل بشكل جيد.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://coliru.stacked-crooked.com/" rel="external nofollow">coliru.stacked-crooked.com</a>: مصرّف يمكّنك من أن تحدّد سطر الأوامر، ويتيح لك اختيار أحد المُصرِّفين GCC أو Clang.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://cpp.sh/" rel="external nofollow">cpp.sh</a>: مُصرِّف يدعم C++‎ 14، لا يسمح لك بتحرير سطر أوامر المصرّف، ولكن يوفّر بعض الخيارات عبر عناصر التحكم في واجهة المستخدم الرسومية.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://gcc.godbolt.org/" rel="external nofollow">gcc.godbolt.org</a>: يوفر قائمة واسعة من إصدارات المُصرِّف، والمعماريات، وهو مفيد للغاية لمن يحتاج إلى التحقّق من عمل الشيفرة في عدّة مُصرِّفات مختلفة. والمصرفات التالية هي بعض المصرّفات المتاحة: GCC و Clang و MSVC ومصرّف Intel و ELLCC و Zapcc، مع توفّر واحد أو أكثر من هذه المصرّفات للمعماريّات ARM و ARMv8 (مثلARM64) و Atmel AVR و MIPS و MIPS64 و MSP430 و PowerPC و x86 و x64. أيضًا، يمكن تعديل وسائط سطر الأوامر الخاص بالمُصرِّف.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://ideone.com/" rel="external nofollow">ideone.com</a>: يُستخدم على نطاق واسع لتوضيح سلوك الشيفرة، ويوفر كلًّا من GCC و Clang، لكنّه لا يسمح بتحرير سطر أوامر المصرّف.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://melpon.org/wandbox" rel="external nofollow">melpon.org/wandbox</a>: يدعم العديد من إصدارات Clang و GNU / GCC.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://onlinegdb.com/" rel="external nofollow">onlinegdb.com</a>: بيئة تطوير متكاملة لكن محدودة، تتضمّن محررًا ومُصرِّفًا (gcc) ومنقّحًا (gdb).
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://rextester.com/" rel="external nofollow">rextester.com</a>: يوفر المُصرِّفات Clang و GCC و Visual Studio لكل من C و C++‎ (إضافة إلى مُصرِّفات خاصّة بلغات أخرى)، ومكتبة Boost.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://tutorialspoint.com/compile_cpp11_online.php" rel="external nofollow">tutorialspoint.com/compile_cpp11_online.php</a>: صدفة UNIX متكاملة، مع مصرّف GCC، ومستكشف مشاريع سهل الاستخدام.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://webcompiler.cloudapp.net/" rel="external nofollow">webcompiler.cloudapp.net</a>: مُصرِّف لبرنامج Visual Studio 2015، مُقدّم من قبل Microsoft كجزء من RiSE4fun.
		</p>
	</li>
</ul>
<h2>
	التصريف باستخدام Visual C++‎ (سطر الأوامر)
</h2>

<p>
	بالنسبة للمبرمجين الذين اعتادوا على العمل بالمصرّفَين GCC أو Clang، والذين يودّون الانتقال إلى Visual Studio، أو المبرمجين الذين يفضّلون العمل بسطر الأوامر بشكل عام، يمكن استخدام مصرّف Visual C++‎ من سطر الأوامر إلى جانب بيئة تطوير متكاملة (IDE).
</p>

<p>
	وإذا كنت ترغب في تصريف شيفرتك من سطر الأوامر في Visual Studio، فسيكون عليك إعداد بيئة سطر الأوامر، عن طريق فتح:
</p>

<p style="text-align: center;">
	 <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://docs.microsoft.com/en-us/cpp/build/setting-the-path-and-environment-variables-for-command-line-builds?redirectedfrom=MSDN&amp;view=vs-2019#Anchor_0" rel="external nofollow">Visual Studio Command Prompt/Developer Command Prompt/x86 Native Tools Command Prompt/x64 Native Tools Command Prompt </a>
</p>

<p>
	أو شيئٍا من هذا القبيل (يختلف الأمر حسب إصدار Visual Studio الذي تعمل به)، أو في موجّه الأوامر، من خلال الانتقال إلى المجلّد الفرعي <code>‎VC‎</code> في مجلّد التثبيت الخاصّ بالمصرّف (يكون عادةً <code>‎\Program Files (x86)\Microsoft Visual Studio x\VC‎</code>، حيث يمثّل <code>‎x‎</code> رقم الإصدار (مثل <code>‎10.0‎</code> لعام 2010، أو <code>‎14.0‎</code> لعام 2015)، ثمّ تنفيذ الملفّ <code>‎VCVARSALL‎</code> مع مُعامل سطر الأوامر المُحدّد <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://docs.microsoft.com/en-us/cpp/build/setting-the-path-and-environment-variables-for-command-line-builds?redirectedfrom=MSDN&amp;view=vs-2019#Anchor_1" rel="external nofollow">هنا</a>.
</p>

<p>
	لاحظ أنّه على عكس GCC، فإنّ Visual Studio لا يوفّر واجهة للرابط (<code>‎link.exe‎</code>) عبر المصرّف (<code>‎cl.exe‎</code>)، ولكنّها توفّر الرابط (linker) كبرنامج منفصل، ويستدعيه االمُصرِّف عند الإنهاء.
</p>

<p>
	ويمكن استخدام <code>‎cl.exe‎</code> و <code>‎link.exe‎</code> بشكل منفصل مع عدّة ملفّات وخيارات، أو إخبار <code>‎cl‎</code> بتمرير الملفّات والخيارات إلى <code>‎link‎</code> إذا كانت المهمّتان ستُنجزان معًا، وستُترجم خيارات الربط المُحدّدة لـ <code>‎cl‎</code> إلى خيارات خاصة بـ <code>‎link‎</code>، وتُمرّر الملفات غير المُعالجة بواسطة <code>‎cl‎</code> مباشرة إلى الرابط <code>‎link‎</code>.
</p>

<p>
	انظر <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://docs.microsoft.com/en-us/cpp/build/reference/linker-options?redirectedfrom=MSDN&amp;view=vs-2019" rel="external nofollow">هذه الصفحة للمزيد عن وسائط <code>‎link‎</code></a>.
</p>

<p>
	لاحظ أنّ الوسائط التي تخصّ <code>‎cl‎</code> حسّاسة لحالة الأحرف، وذلك على خلاف وسائط <code>‎link‎</code>، وأن بعض الأمثلة التالية تستخدم المتغيّر "current directory" الخاصّ بصدفة (shell) ويندوز، <code>‎%cd%‎</code>، عند تحديد أسماء المسار المطلق (absolute path names)، ويُوسَّع هذا المتغيّر إلى مجلّد العمل الحالي (current working directory).
</p>

<p>
	وفي سطر الأوامر، سيكون هو المجلّد الذي كنت تستخدمه عند تشغيل <code>‎cl‎</code>، وهو محدَّد في موجّه الأوامر - command prompt - افتراضيًا (مثلًا، في موجّه الأوامر <code>‎C:\src&gt;‎</code>، فإنّ <code>‎%cd%‎</code> سيكون <code>‎C:\src\‎</code>).
</p>

<p>
	يمكنك تصريف وربط ملف تنفيذي غير مُحسَّن (unoptimised executable) بافتراض أنّ هناك ملفّا مصدريًا واحدًا يُسمّى <code>‎main.cpp‎</code> في المجلد الحالي، وهذا مفيد في مرحلة التطوير الأولي والتنقيح، عبر استخدام أيٍّ ممّا يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_12" style="">
<span class="pln">cl main</span><span class="pun">.</span><span class="pln">cpp
</span><span class="com">// "main.obj" إنشاء ملف</span><span class="pln">
</span><span class="com">// "main.obj" إجراء الربط مع</span><span class="pln">
</span><span class="com">// "main.exe" إنشاء الملف القابل للتنفيذ</span><span class="pln">

cl </span><span class="pun">/</span><span class="typ">Od</span><span class="pln"> main</span><span class="pun">.</span><span class="pln">cpp</span></pre>

<p>
	في المثال السابق، سيتصرف <code>cl /Od main.cpp</code> كسابقه، لكن يكون "Od/" هو خيار "Optimisation: disabled"، ويكون الخيار الافتراضي عند عدم تحديد أي من خيارات "O/".
</p>

<p>
	بافتراض أنّ هناك ملفًّا مصدريًا إضافيًا " niam.cpp " في نفس المجلّد، فستنشئ الشيفرة التالية الملفين "main.obj" و "niam.obj"، ثم يجري الربط معهما، ومن ثم ينشئ الملف التنفيذي "main.exe":
</p>

<pre class="ipsCode">
cl main.cpp niam.cpp
</pre>

<p>
	يمكنك أيضًا استخدام محارف البدل (wildcards)، ستنشئ الشيفرة التالية ملف كائن "main.obj" إضافة إلى ملف كائن لكل ملف cpp. في المجلد التالي: "%cd%\src" ثم تُجري الربط مع "main.obj" وكل ملف كائن مُنشأ، وستكون كل ملفات الكائن في المجلد الحالي، ثم تولِّد ملف main.exe.
</p>

<pre class="ipsCode">
cl main.cpp src\* .cpp
</pre>

<p>
	لإعادة تسمية الملف القابل للتنفيذ أو نقله، استخدم أحد الخيارات التالية:
</p>

<pre class="ipsCode">
cl /o name main.cpp
// "name.exe" تولد ملف قابل للتنفيذ
cl /o folder\ main.cpp
// "%cd%\folder" في المجلد "main.exe" إنشاء ملف
cl /o folder\name main.cpp
// "%cd%\folder" في المجلد "main.exe" إنشاء ملف
cl /Fename main.cpp
// "/o name" مثل
cl /Fename main.cpp
// "/o folder\" مثل
cl /Fefolder\name main.cpp
//  "/o folder\name" مثل
</pre>

<p>
	يمرِّر كلٌّ من <code>‎/o‎</code> و <code>‎/Fe‎</code> معاملاتِهما (دعنا نسميها <code>‎o-param‎</code>) إلى <code>‎link‎</code> على شكل <code>‎/OUT:o-param‎</code>، مع إلحاق الامتداد المناسب (بشكل عام <code>‎.exe‎</code> أو <code>‎.dll‎</code>) لتسمية <code>‎o-param‎</code> بما يُناسب، في حين أنّ لـ <code>‎/o‎</code> و <code>‎/Fe‎</code> نفس الوظيفية - على حدّ علمي - إلّا أنّ الأخير هو المفضل في Visual Studio.
</p>

<p>
	لقد أصبحت <code>‎/o‎</code> مهمَلة، ويبدو أنّها تًقدّم بشكل أساسي للمبرمجين الذين اعتادوا العمل بالمصرّفَين GCC و Clang. لاحظ أنّه على الرغم من أنّ المسافة الفارغة بين <code>‎/o‎</code> واسم المجلد المحدّد اختيارية، إلا أنه لا يمكن وضع مسافة بيضاء بين <code>‎/Fe‎</code> واسم المجلد المحدّد.
</p>

<p>
	بالمثل، يمكن إنشاء ملف تنفيذي مُحسّن (لاستخدامه في مرحلة الإنتاج) على النحو التالي: سنحسِّن حجم الملف التنفيذي في السطر الأول لكن قد يكون هذا على حساب سرعة التنفيذ، أما في السطر الثاني فيحسِّن سرعة البرنامج، لكن ربما يجعل هذا حجم الملف أكبر.
</p>

<pre class="ipsCode">
cl /O1 main.cpp
cl /O2 main.cpp
</pre>

<p>
	هنا ننشئ ملفات خاصة لتحسين البرنامج، مما يسمح لسطر الأوامر أن يأخذ كل وحدات الترجمة في حسبانه خلال عملية التحسين، وسيمرر خيار توليد شيفرة الرابط-الوقت (Link-Time Code Generation) أو LTCG/، إلى الرابط LINK، ليخبره باستدعاء سطر الأوامر خلال مرحلة الربط لإجراء تحسينات إضافية. وينبغي ربط كائن الملف المُنشأ مع LTCG/ في حال عدم إجراء الربط في ذلك الوقت، كما يمكن استخدامها مع خيارات التحسين الأخرى في سطر الأوامر، …
</p>

<pre class="ipsCode">
cl /GL main.cpp other.cpp
</pre>

<p>
	أخيرًا، لإنتاج ملف تنفيذي مُحسَّن لأجل منصّة معيّنة (لاستخدامه في الإنتاج على جهاز ذي معمارية معيّنة)، فعليك اختيار موجّه الأوامر المناسب، أو مُعامل <code>‎VCVARSALL‎</code> المناسب للمنصّة المُستهدفة. ويجب أن يرصُد الرابط (<code>‎link‎</code>) المنصّة المطلوبة من ملفّات الكائن؛ وإلّا، فعليك استخدام الخيار <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://docs.microsoft.com/en-us/cpp/build/reference/machine-specify-target-platform?redirectedfrom=MSDN&amp;view=vs-2019" rel="external nofollow"><code>‎/MACHINE‎</code></a> لتعريف المنصّة المُستهدفة بشكل صريح. مثال: إذا أردت التصريف للتنفيذ على معمارية 64 بت، وتعذر على الرابط LINK أي يرى المنصة المستهدفة:
</p>

<pre class="ipsCode">
cl main.cpp / link / machine: X64
</pre>

<p>
	سينتج أيًا مما سبق ملفًا تنفيذيًا يحمل الاسم المحدّد من قِبل <code>‎/o‎</code> أو <code>‎/Fe‎</code>، وفي حالة عدم توفير أيّ منهما، سيحمل اسمًا مطابقًا للملف المصدري الأول أو ملفِّ الكائن المحدّد للمُصرِّف.
</p>

<pre class="ipsCode">
cl a.cpp b.cpp c.cpp
// "a.exe" إنشاء
cl d.obj a.cpp q.cpp
// "d.exe" إنشاء
cl y.lib n.cpp o.obj
// "n.exe" إنشاء
cl / o yo zp.obj pz.cpp
// "yo.exe" إنشاء
</pre>

<p>
	لتصريف الملفّات دون ربط:
</p>

<pre class="ipsCode">
cl / c main.cpp
// "main.obj" إنشاء ملف
</pre>

<p>
	تخبر الشيفرةُ السابقة سطرَ الأوامر <code>‎cl‎</code> بالخروج دون استدعاء <code>‎link‎</code>، وتنتج ملفّ كائن يمكن ربطه لاحقًا بالملفّات الأخرى لإنتاج ملف ثنائي. في الشيفرة التالية، ينشئ السطر الأول ملف الكائن niam.obj، ويجري الربط مع niam.obj و main.obj، ثم ينشئ الملف التنفيذي main.exe. ويجري السطر الثاني ربطًا مع niam.obj و main.obj، ثم ينشئ الملف التنفيذي main.exe.
</p>

<pre class="ipsCode">
cl main.obj niam.cpp

link main.obj niam.obj
</pre>

<p>
	هناك مُعاملات أخرى مهمّة لسطر الأوامر، ومن المفيد جدًا معرفتها، منها ما يلي:
</p>

<ul>
<li>
		<p>
			<code>cl /EHsc main.cpp</code>: يشير EHsc إلى أنه لن تُمسك إلا اعتراضات ++C القياسية، وأن دوال C الخارجية لن ترفع اعتراضات (exceptions). يوصى بهذا المعامل لمن يريد كتابة شيفرة محمولة ومتعددة المنصات.
		</p>
	</li>
	<li>
		<p>
			<code>cl /clr main.cpp</code>: يشير clr/ إلى أن الشيفرة يجب أن تُصرَّف لاستخدام اللغة المشتركة لوقت التشغيل، وهي آلة وهمية خاصة بإطار عمل NET.، وتسمح باستخدام لغة C++/CLI الخاصة بميكروسوفت إضافة إلى سطر أوامر ++C، وتنشئ ملفًا تنفيذيًا يتطلب NET. ليعمل.
		</p>
	</li>
	<li>
		<p>
			<code>cl /Za main.cpp</code>: يشير Za/ إلى وجوب تعطيل إضافات (Extensions) ميكروسوفت، وأن الشيفرة يجب أن تُصرَّف وفق مواصفات ISO للغة ++C حصرًا، وهذا ضروري لضمان محمولية البرنامج.
		</p>
	</li>
	<li>
		<p>
			<code>cl /Zi main.cpp</code>: يولد Zi/ ملف قاعدة بيانات PDB للبرنامج من أجل استخدامه عند تنقيح برنامج ما دون التأثير على مواصفات التحسين، ويُمرَّر خيار DEBUG/ إلى الرابط LINK.
		</p>
	</li>
	<li>
		<p>
			<code>cl /LD main.cpp</code>: يخبر LD/ سطرَ الأوامر أن يضبط LINK كي يولد ملف DLL بدلًا من ملف تنفيذي، وسينتج الرابط ملف DLL إضافة إلى ملفي EXP و LIB لاستخدامهما عند الربط. مرر ملف LIB المرتبط بملف DLL إلى سطر الأوامر أو الرابط عند تصريف تلك البرامج، وذلك لاستخدام الأخير في برامج أخرى.
		</p>
	</li>
	<li>
		<p>
			<code>cl main.cpp /link /LINKER_OPTION</code>: يمرر link/ كل ما بعده إلى الرابط مباشرة دون تحليل. استبدل LINKER_OPTION/ بخيارات الرابط التي تريد.
		</p>
	</li>
</ul>
<p>
	بالنّسبة للذين لديهم خبرة في التعامل مع اليونكسات وأشباهها، و/أو GCC / Clang و <code>‎cl‎</code> و <code>‎link‎</code>، وأدوات سطر أوامر Visual Studio الأخرى، يمكنهم أن يقبلوا المُعاملات المُحدّدة باستخدام الواصلة <code>-</code> (مثل <code>‎-c‎</code>) بدلًا من الشرطة المائلة (مثل <code>‎/c‎</code>). إضافة إلى ذلك، يتعرّف نظام ويندوز على الشرطة المائلة العكسية <code>\</code> (backslash) أو الأمامية <code>/</code> (slash) ويعدّهما فواصل صالحة للمسارات، لذلك يمكن استخدام المسارات التي على نمط يونكس وما شابهه.
</p>

<p>
	يسهّل هذا تحويل أسطر أوامر المصرّف من <code>‎g++‎</code> أو <code>‎clang++‎</code> إلى <code>‎cl‎</code>، أو العكس، بالحد الأدنى من التغييرات.
</p>

<pre class="ipsCode">
g++ - o app src / main.cpp
cl - o app src / main.cpp
</pre>

<p>
	ستحتاج إلى البحث عن أوامر مكافئة في توثيق المصرّف الذي تستخدمه و/أو في مواقع توثيق أخرى، وذلك عند محاولة نقل سطور الأوامر التي تستخدم خيارات معقّدة لـ <code>‎g++‎</code> أو <code>‎clang++‎</code>.
</p>

<p>
	وإن احتجت إلى استخدام ميزات لغة معيّنة في شيفرتك، فيلزم استخدام إصدار محدّد من MSVC. ومن الممكن في <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://blogs.msdn.microsoft.com/vcblog/2016/06/07/standards-version-switches-in-the-compiler/" rel="external nofollow">Visual C++‎ 2015 التحديث 3</a> اختيار إصدار المعيار الذي تريد اعتماده عند التصريف عبر الراية <code>‎/std‎</code>، والقيمُ الممكنةُ هي <code>‎/std:c++14‎</code> و <code>‎/std:c++latest‎</code> (ستتبعُهما <code>‎/std:c++17‎</code> قريبًا).
</p>

<p>
	<strong>ملاحظة</strong> في الإصدارات القديمة من هذا المُصرِّف، كانت بعض رايات المزايا متوفرة، بيْد أنّها كانت في الغالب تُستخدم لمعاينة الميزات الجديدة.
</p>

<h2>
	التصريف باستخدام Clang
</h2>

<p>
	بما أن الواجهة الأمامية لمصرّف Clang مُصمّمة لتكون متوافقة مع GCC، فإنّ معظم البرامج التي يمكن تصريفها عبر GCC ستُصرَّف كذلك عند استبدال <code>‎clang++‎</code> بـ<code>‎g++‎</code> في برامج البناء النصية (build scripts)، وفي حال عدم إعطاء <code>‎-std=version‎</code>، فسيُستخدَم الإصدار gnu11.
</p>

<p>
	يمكن لمستخدمي ويندوز الذين اعتادوا على MSVC استبدال <code>‎clang-cl.exe‎</code> بـ <code>‎cl.exe‎</code>. واعلم أن clang يحاول بشكل افتراضي أن يكون متوافقًا مع أحدث إصدار مُثبّت من MSVC. ويمكن استخدام clang-cl عبر تغيير <code>‎Platform toolset‎</code> في خاصّيات المشروع، وذلك عند التصريف باستخدام visual studio.
</p>

<p>
	وفي كلتا الحالتين، سيكون clang متوافقًا عبر واجهته الأمامية وحسب، لكنه سيحاول أيضًا بناء ملفات ثنائية متوافقة. كذلك يجب على مستخدمي clang-cl أن ينتبهوا إلى أنّ التوافق مع MSVC ليس كاملًا.
</p>

<p>
	لاستخدام clang أو clang-cl، يمكن استخدام التثبيت الافتراضي على توزيعات محدّدة من Linux، أو تلك المُجمَّعة في بيئات التطوير المتكاملة - IDEs - (مثل XCode على Mac). أما بالنسبة للإصدارات الأخرى من هذا المُصرِّف، أو في المنصّات التي لم يُثبّت عليها، فيمكن تنزيله من <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="releases.llvm.org/download.html" rel="">صفحة التنزيل الرسمية</a>.
</p>

<p>
	إذا كنت تستخدم CMake لبناء شيفرتك، فيمكنك تبديل االمُصرِّف عادة عن طريق تعيين متغيّرَي البيئة <code>‎CC‎</code> و <code>‎CXX‎</code> على النحو التالي:
</p>

<pre class="ipsCode">
mkdir build
cd build
CC=clang CXX=clang++ cmake ..
cmake --build .
</pre>

<h2>
	عملية التصريف في C++‎
</h2>

<p>
	بعد تطوير برنامج بلغة C++‎، فإنّ الخطوة التالية هي تصريف ذلك البرنامج قبل تشغيله. والتصريف (compiling) هو العملية التي تحوّل البرنامج المكتوب بلغة قابلة للقراءة البشرية، مثل C و C++‎ وغيرهما، إلى شيفرة الآلة، وهي شيفرة يمكن أن تُفهمها وحدة المعالجة المركزية مباشرة. على سبيل المثال، إذا كان لديك ملفّ مصدَري مكتوب بلغة C++‎ باسم prog.cpp، وقمت بتنفيذ الأمر compile …
</p>

<pre class="ipsCode">
g++ -Wall -ansi -o prog prog.cpp
</pre>

<p>
	ستحدث 4 مراحل رئيسية عند إنشاء ملف تنفيذي من الملف المصدري.
</p>

<ol>
<li>
		يأخذ معالج C++‎ الأولي ملفّ الشيفرة المصدرية ويتعامل مع الترويسات (include#)، ووحدات الماكرو (‎#define‎) وموجّهات المعالج الأخرى.
	</li>
	<li>
		تُصرّف شيفرة C++‎ المصدرية المُوسّعة التي تم إنتاجها من قبل معالج C++‎ الأوّلي إلى لغة التجميع (assembly) المناسبة للمنصّة.
	</li>
	<li>
		تُصرَّف الشيفرة المجمّعة التي تم إنتاجها بواسطة المصرّف إلى كائن شيفرة (object code) مناسب للمنصة.
	</li>
	<li>
		يُربط ملفّ كائن الشيفرة المُنتج من قبل المجمّع مع ملفّات كائنات الشيفرة الخاصة بمكتبات الدوال المُستخدمة، وذلك لإنتاج مكتبة أو ملف قابل للتنفيذ.
	</li>
</ol>
<h3>
	المعالجة الأولية Preprocessing
</h3>

<p>
	يُعالج المعالج الأولي تعليمات المعالج، مثل ‎#include و ‎#deﬁne، وهو غير واعٍ بصيغة C++‎، لذا يجب توخّي الحذر عند استخدامه.
</p>

<p>
	يعمل المعالج الأوّلي على ملفّ ++C مصدري واحد عن طريق استبدال محتوى الملفّات المناسبة (والتي تكون عادة مجرّد تصريحات) بتعليمات ‎#include، ويستبدل وحدات الماكرو (‎#deﬁne)، ويختار عدة أجزاء من النص بحسب تعليمات ‎#if و ‎#ifdef و ‎#fndef.
</p>

<p>
	يعمل المعالج الأوّلي على مجموعة من وحدات المعالجة الأوّلية (preprocessing tokens)، ويُعُرِّف استبدال الماكرو على أنّه استبدال مقاطع بمقاطع أخرى (يتيح المُعامل ## دمجَ شيفرَتين إذا كان ذلك مناسبًا).
</p>

<p>
	بعد كل هذا، يُنتج المعالج الأوّلي خرجًا واحدًا يمثّل مجرى من المقاطع الناتجة عن التحويلات الموضّحة أعلاه، ويضيف أيضًا بعض العلامات/الوسوم الخاصة التي تخبر المُصرِّف بالسطر الذي جاء منه كل مقطع، حتّى يتمكّن من صياغة رسائل الخطأ المناسبة.
</p>

<p>
	قد تنتج بعض الأخطاء في هذه المرحلة عند استخدام التعليمتين #if و ‎#error. ونستطيع إيقاف العملية في مرحلة المعالجة الأولية باستخدام راية المصرّف أدناه:
</p>

<pre class="ipsCode">
g++ -E prog.cpp
</pre>

<h3>
	التصريف
</h3>

<p>
	تُجرى خطوة التصريف على كل خرج من مخرجات المعالج الأوّلي (Preprocessor)، ويحلّل االمُصرِّف شيفرة C++‎ المصدرية الخالصة - دون أي تعليمات للمعالج الأولي الآن- ثمّ يحوّلها إلى شيفرة المجمّع، ثم يستدعي الواجهة الخلفية الأساسية (underlying backend)، وهي المجمّع في سلسلة الأدوات، التي تجمّع تلك الشيفرة وتحوّلها إلى شيفرة الآلة، وتنتج ملفًّا ثنائيًا وفق تنسيق معيّن (مثل ELF أو COFF أو a.out …).
</p>

<p>
	تحتوي ملفات الكائنات على الشيفرة المُصرَّفة (في شكل ثنائي) للرموز المُعرَّفة في المدخلات، ويشار إلى الرموز الموجودة في ملف الكائن بالاسم. وتستطيع ملفات الكائنات أن تشير إلى الرموز التي لم تُعرِّف بعد، كأن تستخدم تصريحًا بدون توفير تعريف له، فلا يمانع المُصرِّف ذلك، وسينتج ملفَّ الكائن بلا مشاكل طالما أنّ الشيفرة المصدرية مصاغة بشكل جيّد.
</p>

<p>
	تسمح لك المصرّفات عادةً بإيقاف عملية التصريف في هذه المرحلة، وهو أمر مفيد للغاية إذ يُمكِّنك من تصريف كل ملفّ مصدري بشكل منفصل، وعليه فلن تكون بحاجة إلى إعادة تصريف كل شيء إن لم تكن غيرت إلا ملفًّا واحدًا فقط.
</p>

<p>
	يمكن وضع ملفات الكائنات الناتجة في أرشيفات خاصة تسمّى المكتبات الساكنة (static libraries)، لتسهيل استخدامها لاحقًا، ويُبلَّغ في هذه المرحلة عن أخطاء المصرّف "المعتادة"، مثل أخطاء الصيغة، أو أخطاء فشل تحليل التحميل الزائد.
</p>

<p>
	لإيقاف العملية بعد خطوة التصريف، يمكن استخدام الخيار <code>‎-S</code>:
</p>

<pre class="ipsCode">
g++ -Wall -ansi -S prog.cpp
</pre>

<h3>
	التجميع Assembling
</h3>

<p>
	ينشئ المجمّع شيفرة الكائن، وقد ترى على نظام UNIX ملفّات ذات لاحقة ‎.o (أو .OBJ على MSDOS) للدلالة إلى ملفات كائنات الشيفرَة (object code files).
</p>

<p>
	في هذه المرحلة، يحوّل المجمّع تلك الملفّات من شيفرة مُجمَّعة (assembly code) إلى تعليمات على مستوى الآلة، كما سيكون الملفُّ المُنشأ كائنَ شيفرة قابلة للنقل (relocatable object code)، ومن ثم ستنشئ مرحلةُ التصريف برنامجَ الكائن القابل للنقل، ويمكن استخدام هذا البرنامج في مواضع أخرى دون الحاجة إلى إعادة تصريفه مرّة أخرى.
</p>

<p>
	لإيقاف العملية بعد خطوة التجميع، استخدم الخيار<code>‎</code>-c:
</p>

<pre class="ipsCode">
g++ -Wall -ansi -c prog.cpp
</pre>

<h3>
	الربط Linking
</h3>

<p>
	الرابط (Linker) هو الذي ينتج مخرجات التصريف النهائية من كائنات الملفّات التي أنتجها المجمّع (Assembler)، وقد تكون المُخرجات إمّا مكتبة مشتركة (أو ديناميكية، رغم تشابه الأسماء، إلا أنّها تختلف عن المكتبات الساكنة المذكورة آنفًا)، أو يمكن أن تكون ملفّا تنفيذيًا.
</p>

<p>
	ويربط جميع الكائنات عن طريق استبدال العناوين الصحيحة بالمراجع التي تشير إلى رموز غير مُعرَّفة (undeﬁned symbols). يمكن تعريف تلك الرموز في ملفّات كائنات أخرى أو في مكتبات أخرى، وإذا عُرِّفت في مكتبات غير المكتبة القياسية، فعليك إخبار الرابط بذلك.
</p>

<p>
	وتكون الأخطاء الأكثر شيوعًا في هذه المرحلة هي التعاريف المفقودة أو التعاريف المكرّرة، والأولى تعني أنّه إمّا أنّ التعاريف غير موجودة (أي أنّها غير مكتوبة)، أو أنّ ملفات الكائنات أو المكتبات التي عُرِّفت فيها لم تُعط للرابط، أمّا الأخطاء الأخيرة فهي واضحة: إذ أنّها تعني أنّ نفس الرمز قد عُرِّف في ملفّين أو مكتبتين مختلفتين.
</p>

<h2>
	التصريف عبر Code::Blocks (واجهة رسومية)
</h2>

<ol>
<li>
		<p>
			نزِّل <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="http://www.codeblocks.org/downloads/binaries" rel="external nofollow">Code::Blocks</a> وثبِّته، وإذا كنت تستخدم ويندوز فاحرص على اختيار الملفّ الذي يحتوي اسمُه على <code>‎mingw‎</code>، إذ لا تثبّت الملفّات الأخرى أي مُصرِّفات.
		</p>
	</li>
	<li>
		<p>
			افتح Code::Blocks وانقر على "Create a new project":
		</p>
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61061" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/z4Oll.png.0689c4576ff6aebe819a29b9e2d119ea.png" rel=""><img alt="z4Oll.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61061" data-unique="nvc8qj44r" src="https://academy.hsoub.com/uploads/monthly_2021_03/z4Oll.thumb.png.1a0cd8cd2cf1d537497dcdbb524fc6f8.png"></a>
</p>

<ol start="3">
<li>
		<p>
			اختر "Console application" وانقر على "Go":
		</p>
	</li>
</ol>
<p style="text-align: center;">
	<img alt="0wBAn.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61048" data-unique="yn6j5gm65" src="https://academy.hsoub.com/uploads/monthly_2021_03/0wBAn.png.c0b2917ce4fbe8e0d129ccebe956dcdd.png"></p>

<ol start="4">
<li>
		<p>
			انقر على"Next"، واختر "C++‎"، انقر على "Next"، اختر اسمًا لمشروعك، واختر مجلدًا لحفظه، ثمّ انقر على "Next" ثمّ على "Finish".
		</p>
	</li>
	<li>
		<p>
			يمكنك الآن تعديل وتصريف شيفرتك، ستجد شيفرة افتراضية تطبع السلسلة النصية "Hello world!" في سطر الأوامر. اضغط على أحد الأزرار الثلاثة compile/run في شريط الأدواتل تصريف و/أو تشغيل البرنامج:
		</p>
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61060" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/wCmdw.png.5067a468bdb78902a6af3ec037e2747c.png" rel=""><img alt="wCmdw.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61060" data-unique="4xivylli0" src="https://academy.hsoub.com/uploads/monthly_2021_03/wCmdw.thumb.png.e14368cb97ca6b57b4d389ca0fb1af26.png"></a>
</p>

<p>
	للتصريف دون تشغيل، اضغط على <img alt="gOkY9.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61054" data-unique="6ool3menl" src="https://academy.hsoub.com/uploads/monthly_2021_03/gOkY9.png.d55cd46497aab6832d58d64b98bf9d03.png">، وللتشغيل دون التصريف مرّة أخرى، اضغط على <img alt="eLjbt.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61053" data-unique="jm6gqym1k" src="https://academy.hsoub.com/uploads/monthly_2021_03/eLjbt.png.9231574cda9bc96d1d2a4f0a1a2d038f.png">، وللتصريف ثمّ التشغيل اضغط على <img alt="Zgyi8.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61062" data-unique="9smvfyals" src="https://academy.hsoub.com/uploads/monthly_2021_03/Zgyi8.png.702ab07195cac9b82672d547659d92bf.png">.
</p>

<p>
	تصريف وتشغيل البرنامج الافتراضي "مرحبا العالم!" سيُنتِج الخرج التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61057" data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/qbVy8.png.56dfc3acaf5bf52f23148f413839112f.png" rel=""><img alt="qbVy8.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61057" data-unique="4wvtm1hox" src="https://academy.hsoub.com/uploads/monthly_2021_03/qbVy8.thumb.png.26cf5b64fa68660fb83e5b0bcd6a057d.png"></a>
</p>

<h2>
	مواصفات الربط Linkage speciﬁcations
</h2>

<p>
	تخبر مواصفات الربط (linkage speciﬁcation) المُصرِّفَ بأن يصرِّف التصريحات بطريقة تسمح بربطها بالتصريحات المكتوبة بلغة أخرى، مثل C.
</p>

<h3>
	معالج الإشارات الخاصّ بأنظمة التشغيل المشابهة ليونيكس
</h3>

<p>
	بما أن معالج الإشارة (signal handler) يُستدعى عن طريق النواة (kernel) باستخدام صيغة الاستدعاء في لغة C، فيجب أن نطلب من االمُصرِّف أن يستخدم تلك الصيغة عند تصريف الدالّة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_14" style="">
<span class="kwd">volatile</span><span class="pln"> </span><span class="typ">sig_atomic_t</span><span class="pln"> death_signal </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="str">"C"</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> cleanup</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> signum</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    death_signal </span><span class="pun">=</span><span class="pln"> signum</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    bind</span><span class="pun">(...);</span><span class="pln">
    listen</span><span class="pun">(...);</span><span class="pln">
    signal</span><span class="pun">(</span><span class="pln">SIGTERM</span><span class="pun">,</span><span class="pln"> cleanup</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> fd </span><span class="pun">=</span><span class="pln"> accept</span><span class="pun">(...))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fd </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> errno </span><span class="pun">==</span><span class="pln"> EINTR </span><span class="pun">&amp;&amp;</span><span class="pln"> death_signal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        printf</span><span class="pun">(</span><span class="str">"Caught signal %d; shutting down\n"</span><span class="pun">,</span><span class="pln"> death_signal</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إنشاء مكتبة بلغة C متوافقة مع C++‎
</h3>

<p>
	يمكن في العادةً تضمين ترويسات مكتبة C في برامج C++‎، لأنّ معظم التصريحات صالحة في كلا اللغتين. على سبيل المثال، انظر ملف <code>‎foo.h‎</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_16" style="">
<span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> bar</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">Foo</span><span class="pun">;</span><span class="pln">
</span><span class="typ">Foo</span><span class="pln"> make_foo</span><span class="pun">(</span><span class="typ">int</span><span class="pun">);</span></pre>

<p>
	سيصرَّف تعريف <code>‎make_foo‎</code> بشكل منفصل ويُوزّع مع الترويسة على هيئة كائن.
</p>

<p>
	يمكن لبرنامج C++‎ أن يُضمِّن ملف الترويسة <code>‎foo.h‎</code> عبر التعبير <code>‎#include &lt;foo.h&gt;‎</code>، لكن لن يستطيع المُصرِّف أن يعرف أنّ الدالّة <code>‎make_foo‎</code> قد عُرِّفت وفق صيغة مكتوبة بلغة C، وسيحاول البحث عنه باسم آخر، وسيفشل في إيجاده.
</p>

<p>
	وحتى لو استطاع إيجاد تعريف <code>‎make_foo‎</code> في المكتبة، فليست كل المنصّات تستخدم نفس صيغات الاستدعاء في C و C++‎، وسوف يستخدم مصرّف C++‎ صيغة الاستدعاء الخاصة بـ C++‎ عند استدعاء <code>‎make_foo‎</code>، وهذا من شأنه أن يتسبّب في حدوث خطأ تجزئة (segmentation fault) في حال كان يُتوقّع أن تُستدعى <code>‎make_foo‎</code> وفق صيغة الاستدعاء في C.
</p>

<p>
	يمكن حلّ هذه المشكلة عبر تغليف (wrap) كافة التصريحات في ترويسة في كتلة <code>‎extern "C"‎</code>، انظر ..
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_18" style="">
<span class="com">#ifdef</span><span class="pln"> __cplusplus
</span><span class="kwd">extern</span><span class="pln"> </span><span class="str">"C"</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="com">#endif</span><span class="pln">
</span><span class="kwd">typedef</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> bar</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Foo</span><span class="pun">;</span><span class="pln">
</span><span class="typ">Foo</span><span class="pln"> make_foo</span><span class="pun">(</span><span class="typ">int</span><span class="pun">);</span><span class="pln">
</span><span class="com">#ifdef</span><span class="pln"> __cplusplus
</span><span class="pun">}</span><span class="pln"> </span><span class="com">/* "extern C" نهاية الكتلة */</span><span class="pln">
</span><span class="com">#endif</span></pre>

<p>
	والآن، عند تضمين <code>‎foo.h‎</code> من برنامج C، سيظهر كتصريح عادي، أمّا عند تضمين <code>‎foo.h‎</code> من برنامج مكتوب بلغة C++‎، فسيكون <code>‎make_foo‎</code> داخل كتلة <code>‎extern "C"‎</code>، وسيعرف المصرّفُ كيف يبحث عن الأسماء، وسيستخدم صيغة الاستدعاء في C.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 138: Compiling and Building والفصل Chapter 134: Linkage specifications من كتاب <a data-ss1617032410="1" data-ss1617033597="1" data-ss1617033755="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1189</guid><pubDate>Tue, 27 Apr 2021 13:03:04 +0000</pubDate></item><item><title>&#x645;&#x62D;&#x62F;&#x62F;&#x627;&#x62A; &#x623;&#x635;&#x646;&#x627;&#x641; &#x627;&#x644;&#x62A;&#x62E;&#x632;&#x64A;&#x646; (Storage class speci&#xFB01;ers) &#x648;&#x623;&#x645;&#x62B;&#x644;&#x629; &#x639;&#x644;&#x649; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645;&#x647;&#x627; &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-storage-class-speci%EF%AC%81ers-%D9%88%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%81%D9%8A-cpp-r1188/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061f2d0a26ac_62----(Storage-class-speciers)--Cpp.png.c799bf4955568df924ba16c9853645d7.png" /></p>

<p>
	محدّدات أصناف التخزين هي كلمات مفتاحية يمكن استخدامها في التصريحات، ولا تؤثر على نوع التصريح لكنها تعدّل الطريقة التي تُخزّن بها الكيانات.
</p>

<h2>
	extern
</h2>

<p>
	محدّدُ صنف التخزين <code>‎extern‎</code> يستطيع التصريحَ بإحدى الطرق الثلاث التالية، وذلك وفقًا للسياق، فمثلًا:
</p>

<ol>
<li>
		يمكن استخدامه للتصريح عن متغيّر دون تعريفه، يُستخدَم عادة في ملفات الترويسات الخاصة بالمتغيّرات التي تُعرّف في ملف تنفيذ منفصل.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_7" style="">
<span class="com">// نطاق عام</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln"> </span><span class="com">// بالقيمة الافتراضية x تعريف: سيهيأ</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y</span><span class="pun">;</span><span class="pln"> </span><span class="com">// معرَّف في مكان آخر، غالبًا في وحدة ترجمة أخرى y :تصريح.</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln"> </span><span class="com">// أيّ تأثير هنا "extern" تعريف: ليس للكلمة المفتاحية</span></pre>

<ol start="2">
<li>
		إعطاء ارتباط خارجي (external linkage) لمتغيّر في نطاق فضاء الاسم، حتى لو كانت الكلمتان المفتاحيتان <code>‎const‎</code> و <code>‎constexpr‎</code> تتسبّبان في إنشاء ارتباط داخلي.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_9" style="">
<span class="com">// النطاق العام</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> w </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln"> </span><span class="com">// C وخارجي في ،C++ ارتباط داخلي في</span><span class="pln">
</span><span class="kwd">static</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln"> </span><span class="com">// C++ و C ارتباط داخلي في كل من</span><span class="pln">
</span><span class="kwd">extern</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln"> </span><span class="com">// C++ و C ارتباط خارجي في كل من</span><span class="pln">
</span><span class="kwd">namespace</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">extern</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ارتباط داخلي لأنّه في فضاء اسم غير مُسمّى</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="3">
<li>
		إعادة التصريح (redeclaring) عن متغيّر في نطاق الكتلة إن صُرِّح عنه مسبقًا عبر الارتباط (linkage). أما خلاف ذلك، سيُصرّح عن متغيّر جديد عبر الارتباط، وسيكون عضوًا في أقرب فضاء اسم محيط.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_11" style="">
<span class="com">// النطاق العام</span><span class="pln">
</span><span class="kwd">namespace</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">struct</span><span class="pln"> C </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln"> </span><span class="com">// x إعادة التصريح عن</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// طباعة 1 وليس 2</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> g</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">extern</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y</span><span class="pun">;</span><span class="pln"> </span><span class="com">// العامّة المُعرّفة في موضع آخر y ارتباط خارجي، ويشير إلى قيمة y لدى</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن أيضًا التصريح عن الدالّة على أنها <code>‎extern‎</code>، لكنّ هذا ليس له أيّ تأثير وإنّما يُستخدم في العادةً كتلميح للقارئ بأنّ الدالّة المُصرَّح عنها هنا ستعُرَّف في وحدة ترجمة أخرى. ففي المثال التالي، يكون <code>()void f</code> تصريحًا لاحقًا يعني أن <code>f</code> ستُعرَّف في وحدة الترجمة هذه، أما <code>()extern void g</code> ليس تصريحًا لاحقًا، مما يعني أن <code>g</code> تُعرَّف في وحدة ترجمة أخرى، انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_13" style="">
<span class="kwd">void</span><span class="pln"> f</span><span class="pun">();</span><span class="pln"> 
</span><span class="kwd">extern</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> g</span><span class="pun">();</span><span class="pln"> </span></pre>

<p>
	في الشيفرة أعلاه، في حال التصريح عن <code>‎f‎</code> مع <code>‎extern‎</code> والتصريح عن <code>‎g‎</code> بدون <code>‎extern‎</code>، لن يؤثر ذلك على صحة أو دلالات البرنامج، ولكن من المحتمل أن يربك القارئ.
</p>

<h2>
	register
</h2>

<p>
	<strong>الإصدار &lt; C++‎ 17</strong>
</p>

<p>
	<strong><c></c>++‎&gt;</strong>
</p>

<p>
	<code>register</code> هو محدّد صنف تخزين يخبر االمُصرِّف أنّ المتغيّر سيُستخدَم بكثرة، وتأتي الكلمة "register" من حقيقة أن المُصرِّف قد يختار تخزين هذا المتغيّر في سجل وحدة المعالجة المركزية (CPU register) لتسريع الوصول إليه. وقد أُهمِلت بدءًا من الإصدار C++‎ 11.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_15" style="">
<span class="kwd">register</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">100</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    f</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> g </span><span class="pun">=</span><span class="pln"> i </span><span class="pun">*</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
    i </span><span class="pun">+=</span><span class="pln"> h</span><span class="pun">(</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُمكن تعريف كلٍّ من المتغيّرات المحلية ومُعامِلات الدوالّ بالكلمة المفتاحية <code>‎register‎</code>، ولا تضع C++‎ قيودًا على ما يمكن فعله بالمتغيّرات المعرّفة بـ <code>‎register‎</code>، على عكس لغة C. فكثلًا، يجوز أخذ عنوان متغيّر مصرّح عنه بـ <code>‎register‎</code>، لكنّ هذا قد يمنع المُصرِّف من تخزين مثل هذا المتغيّر في السجل (register).
</p>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	الكلمة المفتاحية <code>‎register‎</code> غير مستخدمة ومحفوظة، ولا ينبغي استخدامها.
</p>

<h2>
	static
</h2>

<p>
	لدى محدّد التخزين <code>‎static‎</code> ثلاث معانٍ مختلفة.
</p>

<ol>
<li>
		يعطي ارتباطًا داخليًا لمتغيّر أو دالّة مُصرَّح عنها في نطاق فضاء الاسم.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_17" style="">
<span class="com">// دالة داخلية، لا يمكن الارتباط بها</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> semiperimeter</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a </span><span class="pun">+</span><span class="pln"> b </span><span class="pun">+</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2.0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// التصدير إلى العميل</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> area</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> s </span><span class="pun">=</span><span class="pln"> semiperimeter</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> sqrt</span><span class="pun">(</span><span class="pln">s </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">s </span><span class="pun">-</span><span class="pln"> a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">s </span><span class="pun">-</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">s </span><span class="pun">-</span><span class="pln"> c</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="2">
<li>
		تصرّح بأنّ المتغيّر له مدة تخزين ساكنة - static storage duration - (ما لم تكن خيطًا محليًا <code>‎thread_local‎</code>)، ومتغيرات نطاق فضاء الاسم تكون ساكنة ضمنيًا، وتُهيّأ المتغيّرات المحلية الساكنة مرّة واحدة فقط، إذ يمرّ التحكّم في المرّة الأولى عبر تعريفها، ولا تُدمَّر بعد الخروج من نطاقها.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_19" style="">
<span class="kwd">void</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"f has been called "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">count </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" times so far\n"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="3">
<li>
		عند تطبيقها على تصريح عن عضو صنف (class member)، فإنّها تجعل ذلك العضو ساكنًا.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_21" style="">
<span class="kwd">struct</span><span class="pln"> S </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> S</span><span class="pun">*</span><span class="pln"> create</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> S</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    S</span><span class="pun">*</span><span class="pln"> s </span><span class="pun">=</span><span class="pln"> S</span><span class="pun">::</span><span class="pln">create</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّه إن كان العضو ساكنًا، ستَنطبق النقطتان 2 و 3 في نفس الوقت، إذ تضع الكلمة المفتاحية <code>‎static‎</code> عضو الصنف في عضو بيانات ساكن (static data member)، كما تجعله أيضًا في متغيّر ذي مدة تخزين ساكنة (static storage duration).
</p>

<h2>
	auto
</h2>

<p>
	<strong>الإصدار ≤ C++‎ 03</strong>
</p>

<p>
	يصرّح هذا المحدّد بأنّ المتغيّر لديه مدة تخزين آلية (automatic storage duration)، وهو غير ضروري نظرًا لأنّ مدة التخزين التلقائي هي الإعداد الافتراضي في نطاق الكتلة، كما لا يُسمح باستخدام المحدّد <code>auto</code> في نطاق فضاء الاسم.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_23" style="">
<span class="kwd">void</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> z</span><span class="pun">;</span><span class="pln"> </span></pre>

<p>
	في المثال السابق، <code>auto int x</code> تكافئ <code>int x</code>، ولا تجوز <code>auto y</code> في ++C لكنها جائزة في C89، كذلك لا تجوز <code>auto int z</code>، إذ لا يمكن أن تكون متغيرات نطاق فضاء الاسم آلية.
</p>

<p>
	تغيّر معنى <code>‎auto‎</code> في الإصدار C++‎ 11 بشكل كامل، ولم تعد محدّدًا للتخزين، وإنّما صارت تُستخدم في استنتاج الأنواع.
</p>

<h2>
	mutable
</h2>

<p>
	<code>mutable</code> هو محدّدٌ يمكن تطبيقه على تصريح عضو بيانات من صنف غير ساكن (non-static) وغير مرجعي (non-reference)، والعضو القابل للتغيير في صنفٍ لا يكون غيرَ ثابتٍ حتى لو كان الكائن ثابتًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_25" style="">
<span class="kwd">class</span><span class="pln"> C </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">mutable</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> times_accessed</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        C</span><span class="pun">():</span><span class="pln"> x</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> times_accessed</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> get_x</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">++</span><span class="pln">times_accessed</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا بأس: يمكن للدوال التابعة الثابتة أن تعدّل أعضاء البيانات غير الثابتة</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> set_x</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">++</span><span class="pln">times_accessed</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	أضيف معنى ثانٍ للكلمة المفتاحية <code>‎mutable‎</code> في C++‎ 11، فعندما تتبع قائمة المُعاملات الخاصة بتعبير لامدا، فإنها تقمع المؤهّل الثباتي <code>‎const‎</code> الضمني الموجود في مُعامل استدعاء لامدا (lambda's function call operator). ولذلك يمكن لتعابير لامدا القابلة للتغيير (mutable) أنّ تعدّل قيم الكيانات التي حصلت عليها عن طريق النسخ.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8188_27" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> my_iota</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> start</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> count</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">generate</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(),</span><span class="pln">
        </span><span class="pun">[</span><span class="pln">start</span><span class="pun">]()</span><span class="pln"> </span><span class="kwd">mutable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> start</span><span class="pun">++;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّ <code>‎mutable‎</code> لا تُعدُّ محدّدَ صنف تخزين إذا استخدِمَت بهذه الطريقة لتشكيل تعابير لامدا تقبل للتغيير (أي mutable).
</p>

<p>
	هذا الدرس جزء من <a data-ss1617031883="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 133: Storage class specifiers من كتاب <a data-ss1617031883="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1188</guid><pubDate>Mon, 29 Mar 2021 15:34:39 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x645;&#x648;&#x627;&#x631;&#x62F; (Resources) &#x648;&#x62A;&#x62E;&#x635;&#x64A;&#x635;&#x647;&#x627; &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D8%B1%D8%AF-resources-%D9%88%D8%AA%D8%AE%D8%B5%D9%8A%D8%B5%D9%87%D8%A7-%D9%81%D9%8A-cpp-r1187/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061db3ebd93a_61-----(Resources)---Cpp.png.ed6ef4fa497e6bdd6a346e62f4123336.png" /></p>

<p>
	إدارة الموارد هي إحدى أصعب الأشياء في C و C++‎، لكن C++‎ توفّر لنا العديد من الطرق التي نصمم بها إدارة الموارد في برامجنا، وسنحاول في هذا المقال أن نشرح بعض هذه الطرق.
</p>

<h2>
	تقنية RAII: تخصيص الموارد يكافئ التهيئة
</h2>

<p>
	تقنية RAII وتدعى <strong>اكتساب أو تخصيص الموارد هي تهيئة</strong> (Resource Acquisition Is Initialization) هو مصطلح شائع في إدارة الموارد، ويستخدم المؤشّرات الذكية (smart pointer) لإدارة الموارد في حالة الذاكرة الديناميكية، وتُمنح الموارد المكتسبة ملكية مؤشر ذكي أو مدير موارد مكافئ مباشرة عند استخدام أسلوب RAII. ولا يمكن الوصول إلى المورد إلا من خلال ذلك المدير، مما يمكِّن المدير من تتبّع مختلف العمليات الجارية على المورد.
</p>

<p>
	على سبيل المثال، تحرِّر الكائنات من النوع <code>‎std::auto_ptr‎</code> مواردها تلقائيًا عندما تخرج عن النطاق، أو عندما تُحذف بطريقة أخرى.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_7" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;memory&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        auto_ptr ap</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">(</span><span class="lit">5</span><span class="pun">));</span><span class="pln"> 
        cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ap </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 5</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> 
</span><span class="pun">}</span></pre>

<p>
	في المثال السابق، كان المورد هو الذاكرة الديناميكية، ودُمِّر <code>auto-ptr</code> ثم حُرِّر مورده تلقائيًا.
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	كانت المشكلة الرئيسية في كائنات <code>std::auto_ptr</code> أنّها لا يمكن أن تُنسخ دون نقل الملكية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_9" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;memory&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    auto_ptr ap1</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">(</span><span class="lit">5</span><span class="pun">));</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ap1 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 5</span><span class="pln">
    auto_ptr ap2</span><span class="pun">(</span><span class="pln">ap1</span><span class="pun">);</span><span class="pln"> 
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ap2 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 5</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> ap1 </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تفسير الشيفرة السابقة:
</p>

<ul>
<li>
		<code>(auto_ptr ap2(ap1</code>: تنسخ <code>ap2</code> من <code>ap1</code> وتنتقل الملكية إلى <code>ap2</code>.
	</li>
	<li>
		<code>cout &lt;&lt; ap1 == nullptr &lt;&lt; endl</code>: تطبع القيمة 1، ويخسر <code>ap1</code> ملكيته للمورد.
	</li>
</ul>
<p>
	وبسبب دلالات النسخ الغريبة تلك فإنّ هناك قيودًا على استخدام <code>‎std::auto_ptr‎</code>، مثل أنها لا يمكن أن تُستخدم في الحاويات، وذلك لمنع حذف الذاكرة مرتين: فإذا كان لدينا كائنين من النوع <code>‎auto_ptrs‎</code> يملكان نفس المورد، سيحاول كلا الكائنين تحريره عند تدميرهما.
</p>

<p>
	ومن المهم منع تحرير المورد الذي سبق تحريره من قبل، إذ سيؤدي ذلك التحرير الثاني إلى حدوث مشاكل، لكن بأي حال فإن <code>std::shared_ptr</code> لديه طريقة لتجنب ذلك، مع عدم نقل الملكية أثناء النسخ.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_11" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;memory&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    shared_ptr sp2</span><span class="pun">;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        shared_ptr sp1</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">(</span><span class="lit">5</span><span class="pun">));</span><span class="pln">    </span><span class="com">// sp1 إعطاء الملكيّة إلى</span><span class="pln">
        cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sp1 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl        </span><span class="com">// تطبع 5</span><span class="pln">
        sp2 </span><span class="pun">=</span><span class="pln"> sp1</span><span class="pun">;</span><span class="pln">            </span><span class="com">// يملك كلاهما المورد ،sp1 من sp2 نسخ</span><span class="pln">
        cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sp1 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">        </span><span class="com">// تطبع 5</span><span class="pln">
        cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sp2 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">        </span><span class="com">// تطبع 5</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">                </span><span class="com">// الملكية الحصرية للمورد sp2  عن النطاق وتدميره، أصبح لـ sp1 خروج</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">sp2 </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">                </span><span class="com">// عن النطاق، وتحرير المورد sp2 خروج</span></pre>

<h3>
	القفل Locking
</h3>

<p>
	هذا مثال عن قفل سيّء:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_13" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">mutex mtx</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> bad_lock_example</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    mtx</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        foo</span><span class="pun">();</span><span class="pln">
        bar</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">baz</span><span class="pun">())</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            mtx</span><span class="pun">.</span><span class="pln">unlock</span><span class="pun">();</span><span class="pln">    </span><span class="com">// ينبغي فتح القفل عند كل نقطة خروج</span><span class="pln">
            </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        quux</span><span class="pun">();</span><span class="pln">
        mtx</span><span class="pun">.</span><span class="pln">unlock</span><span class="pun">();</span><span class="pln">        </span><span class="com">// يحدث فتح القفل العادي هنا</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        mtx</span><span class="pun">.</span><span class="pln">unlock</span><span class="pun">();</span><span class="pln">        </span><span class="com">// ينبغي فرض فتح القفل في حال طرح اعتراض</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pun">;</span><span class="pln">                </span><span class="com">// والسماح للاعتراض بالاستمرار</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تلك طريقة خاطئة لتنفيذ عمليتي القفل والفتح لكائنات المزامنة (mutex)، ولا بدّ أن يتحقّق المُبرمِجُ من أنّ جميع التدفّقات (flows) الناتجة عن إنهاء الدالّة تؤدّي إلَى استدعاء <code>‎unlock()‎</code>، وذلك للتأكد أنّ فتح القفل باستخدام <code>‎unlock()‎</code> سيحرّر الكائن المزامنة الصحيح. وهذه عمليات هشة كما وضحنا أعلاه، لأنّها تتطّلب من المطوّرين متابعة النمط يدويًا. ويمكن حلّ هذه المشكلة باستخدام صنف مُصمّم خصّيصًا لتنفيذ تقنية RAII:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_15" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">mutex mtx</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> good_lock_example</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">lock_guard</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">mutex </span><span class="pun">&gt;</span><span class="pln"> lk</span><span class="pun">(</span><span class="pln">mtx</span><span class="pun">);</span><span class="pln">    </span><span class="com">// المنشئ يقفل.</span><span class="pln">
    </span><span class="com">// المدمِّر يفتح!</span><span class="pln">
    </span><span class="com">// تضمن اللغة استدعاء المدمر.</span><span class="pln">
    foo</span><span class="pun">();</span><span class="pln">
    bar</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">baz</span><span class="pun">())</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    quux</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<code>‎lock_guard‎</code> هو قالب صنف بسيط للغاية يستدعي <code>‎lock()‎</code> ويمرّر إليه الوسيط المُمرّر إلى مُنشئه ويحتفظ بمرجع إلى ذلك الوسيط، ثمّ يستدعي <code>‎unlock()‎</code> على الوسيط في مدمّره، ممّا يعني ضمان فتح قفل <code>mutex</code> عند خروج <code>‎lock_guard‎</code> عن النطاق. ولا يهم سبب الخروج عن النطاق سواءً كان ذلك بسبب اعتراض أو عودة مبكّرة، ذلك أن جميع الحالات سيتمّ التعامل معها، وسيفتح القفل بشكل صحيح بغض النظر عن سير البرنامج.
</p>

<h3>
	ScopeSuccess
</h3>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	نستطيع باستخدام <code>‎int std::uncaught_exceptions()‎</code> أن ننفذ إجراءً لم يكن لينفَّذ إلّا في حالة النجاح (عدم رفع اعتراض في النطاق). وقد كانت <code>‎bool std::uncaught_exception()‎</code> فيما سبق تسمح برصد العمليّات الجارية لفكّ المكدّس (stack unwinding). انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_17" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;exception&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> F</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ScopeSuccess</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        F f</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> uncaughtExceptionCount </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">uncaught_exceptions</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">explicit</span><span class="pln"> </span><span class="typ">ScopeSuccess</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> F </span><span class="pun">&amp;</span><span class="pln">f</span><span class="pun">):</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

        </span><span class="typ">ScopeSuccess</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ScopeSuccess</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">ScopeSuccess</span><span class="pln"> </span><span class="pun">&amp;</span><span class="kwd">operator</span><span class="pun">=(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ScopeSuccess</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="com">// f() might throw, as it can be caught normally.</span><span class="pln">
        </span><span class="pun">~</span><span class="typ">ScopeSuccess</span><span class="pun">()</span><span class="pln"> noexcept</span><span class="pun">(</span><span class="pln">noexcept</span><span class="pun">(</span><span class="pln">f</span><span class="pun">()))</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">uncaughtExceptionCount </span><span class="pun">==</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">uncaught_exceptions</span><span class="pun">())</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                f</span><span class="pun">();</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">Foo</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">try</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ScopeSuccess</span><span class="pln"> logSuccess
            </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">[]()</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Success 1\n"</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">};</span><span class="pln">
            </span><span class="com">// نجاح النطاق</span><span class="pln">
            </span><span class="com">// أثناء فكّ المكدّس Foo حتى في حال تدمير</span><span class="pln">
            </span><span class="com">// 0 &lt; std::uncaught_exceptions() </span><span class="pln">
            </span><span class="com">// std::uncaught_exception() == true</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

        </span><span class="kwd">try</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ScopeSuccess</span><span class="pln"> logSuccess
            </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">[]()</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Success 2\n"</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">};</span></pre>

<p>
	تزيد القيمة المعادة من <code>std::uncaught_exceptions</code>، …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_19" style="">
<span class="pln">            </span><span class="kwd">throw</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">runtime_error</span><span class="pun">(</span><span class="str">"Failed"</span><span class="pun">);</span><span class="pln">  
        </span><span class="pun">}</span></pre>

<p>
	وتنقص القيمة المعادة من <code>std::uncaught_exceptions</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_21" style="">
<span class="pln">        </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Foo</span><span class="pln"> foo</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">runtime_error</span><span class="pun">(</span><span class="str">"Failed"</span><span class="pun">);</span><span class="pln">    </span><span class="com">// std::uncaught_exceptions() == 1</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// std::uncaught_exceptions() == 0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيكون الخرج:
</p>

<pre class="ipsCode">
Success 1
</pre>

<h3>
	ScopeFail ‏(C++‎ 17)
</h3>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	يمكننا تنفيذ إجراء معيّن لا يُنفَّذ إلّا عند الفشل (رفع اعتراض في النطاق) بفضل <code>‎int std::uncaught_exceptions()‎</code>، وكانت <code>‎bool std::uncaught_exception()‎</code> تسمح سابقًا برصد إن كانت هناك عملية جارية لفك المكدّس.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_23" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;exception&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> F</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ScopeFail</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
            F f</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> uncaughtExceptionCount </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">uncaught_exceptions</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">explicit</span><span class="pln"> </span><span class="typ">ScopeFail</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> F </span><span class="pun">&amp;</span><span class="pln">f</span><span class="pun">):</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

        </span><span class="typ">ScopeFail</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ScopeFail</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">ScopeFail</span><span class="pln"> </span><span class="pun">&amp;</span><span class="kwd">operator</span><span class="pun">=(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ScopeFail</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span></pre>

<p>
	يجب ألا ترفع <code>()f</code> وإلا فستستدعى <code>std::terminate</code>، نتابع المثال …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_25" style="">
<span class="pln">        </span><span class="pun">~</span><span class="typ">ScopeFail</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">uncaughtExceptionCount </span><span class="pun">!=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">uncaught_exceptions</span><span class="pun">())</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                f</span><span class="pun">();</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">Foo</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">try</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ScopeFail</span><span class="pln"> logFailure
            </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">[]()</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Fail 1\n"</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">};</span><span class="pln">
            </span><span class="com">// نجاح النطاق</span><span class="pln">
            </span><span class="com">// أثناء فكّ المكدّس Foo حتى في حال تدمير</span><span class="pln">
            </span><span class="com">// 0 &lt; std::uncaught_exceptions() في حال</span><span class="pln">
            </span><span class="com">// std::uncaught_exception() == true أو سابقا</span><span class="pln">

        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

        </span><span class="kwd">try</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ScopeFail</span><span class="pln"> logFailure
            </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">[]()</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Failure 2\n"</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">};</span></pre>

<p>
	تزيد القيمة المعادة من <code>std::uncaught_exceptions</code> ….
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_27" style="">
<span class="pln">            </span><span class="kwd">throw</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">runtime_error</span><span class="pun">(</span><span class="str">"Failed"</span><span class="pun">);</span><span class="pln"> 
        </span><span class="pun">}</span></pre>

<p>
	تقل القيمة المعادة من <code>std::uncaught_exceptions</code> ….
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_29" style="">
<span class="pln">        </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">

        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Foo</span><span class="pln"> foo</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">runtime_error</span><span class="pun">(</span><span class="str">"Failed"</span><span class="pun">);</span><span class="pln">    </span><span class="com">// std::uncaught_exceptions() == 1</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// std::uncaught_exceptions() == 0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيكون الخرج:
</p>

<pre class="ipsCode">
Failure 2
</pre>

<h3>
	Finally/ScopeExit
</h3>

<p>
	إذا لم ترد كتابة أصناف خاصّة للتعامل مع بعض الموارد، فاكتب صنفًا عامًا على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_31" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Finally</span><span class="pln"> final
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">explicit</span><span class="pln"> </span><span class="typ">Finally</span><span class="pun">(</span><span class="typ">Function</span><span class="pln"> f</span><span class="pun">):</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">move</span><span class="pun">(</span><span class="pln">f</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">Finally</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            f</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">    </span><span class="com">// (1) انظر أدناه</span><span class="pln">
        </span><span class="typ">Finally</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">Finally</span><span class="pun">(</span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;</span><span class="kwd">operator</span><span class="pun">=(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;</span><span class="kwd">operator</span><span class="pun">=(</span><span class="typ">Finally</span><span class="pln"> </span><span class="pun">&amp;&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">private</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Function</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="com">// عندما يخرج الكائن المُعاد عن النطاق f تنفيذ الدالة</span><span class="pln">
</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> onExit</span><span class="pun">(</span><span class="typ">Function</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> f</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Finally</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="typ">decay_t</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">&gt;&gt;</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">forward</span><span class="pun">&lt;</span><span class="typ">Function</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	وهذا مثال على استخدام ذلك الصنف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_33" style="">
<span class="kwd">void</span><span class="pln"> foo</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="str">&lt;int&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    v</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> autoRollBackChange </span><span class="pun">=</span><span class="pln"> onExit</span><span class="pun">([</span><span class="pln"> </span><span class="pun">&amp;]()</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        v</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    </span><span class="com">// ... `foo(v, i + 1)` شيفرة تكرارية</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة (1)</strong>: يجب أخذ الملاحظات التالية حول تعريف المدمّر في الاعتبار عند التعامل مع الاعتراضات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_37" style="">
<span class="typ">Finally</span><span class="pun">()</span><span class="pln"> noexcept </span><span class="pun">{</span><span class="pln"> f</span><span class="pun">();</span><span class="pln"> </span><span class="pun">}:</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">terminate </span><span class="com">// تُستدعى في حال رفع اعتراض</span><span class="pln">
</span><span class="typ">Finally</span><span class="pun">()</span><span class="pln"> noexcept</span><span class="pun">(</span><span class="pln">noexcept</span><span class="pun">(</span><span class="pln">f</span><span class="pun">()))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> f</span><span class="pun">();</span><span class="pln"> </span><span class="pun">}</span><span class="pln">  </span><span class="com">// إلّا في حال رفع اعتراض أثناء فك المكدّس terminate() لا تُستدعى</span><span class="pln">
</span><span class="typ">Finally</span><span class="pun">()</span><span class="pln"> noexcept </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> f</span><span class="pun">();</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(...)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* ignore exception (might log it) */</span><span class="pun">}</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
              </span><span class="com">// لا تُستدعى std::terminate، لكن لا نستطيع معالجة الخطأ (حتى في حالة عدم فك المكدّس</span></pre>

<h2>
	كائنات المزامنة وأمان الخيوط Mutexes &amp; Thread Safety
</h2>

<p>
	قد تحدث مشكلة إن حاولت عدّة خيوط الوصول إلى نفس المورد، فلنفترض مثلًا أنّ لدينا خيطًا يضيف العدد 1 إلى متغيّر. فأوّل شيء يفعله هو أنّه يقرأ المتغيّر، ثم يضيف واحدًا إليه، ثم يعيد تخزينه مرّة أخرى. لنفترض الآن أنّنا هيّأنا ذلك المتغيّر عند القيمة 1، ثم أنشأنا نُسختين من ذلك الخيط، فبعد أن ينتهي كلا الخيطين، نتوقّع أن تكون قيمة ذلك المتغيّر هي 3، لكن هناك بعض المشاكل التي قد تحدث هنا، سنفصلها في الجدول التالي:
</p>

<table>
<thead><tr>
<th>
				الخيط 1
			</th>
			<th>
				الخيط 2
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				المرحلة 1
			</td>
			<td>
				قراءة 1 من المتغير
			</td>
		</tr>
<tr>
<td>
				المرحلة 2
			</td>
			<td>
				قراءة 1 من المتغير
			</td>
		</tr>
<tr>
<td>
				المرحلة 3
			</td>
			<td>
				إضافة 1 إلى 1 للحصول على 2
			</td>
		</tr>
<tr>
<td>
				المرحلة 4
			</td>
			<td>
				إضافة 1 إلى 1 للحصول على 2
			</td>
		</tr>
<tr>
<td>
				المرحلة 5
			</td>
			<td>
				تخزين 2 في المتغير
			</td>
		</tr>
<tr>
<td>
				المرحلة 6
			</td>
			<td>
				تخزين 2 في المتغير
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	في نهاية العملية، تُخزّن القيمة 2 في المتغيّر بدلًا من 3، ذلك أن الخيط 2 يقرأ المتغيّر قبل أن يُحدِّث الخيط 1 ذلك المتغيّر. ما الحلّ إذن؟ يكون الحل في كائنات المزامنة … .
</p>

<p>
	كائن المزامنة (mutex) هو كائن لإدارة الموارد، ومُصمّم لحل هذا النوع من المشاكل. فعندما يحاول خيط ما الوصول إلى مورد، فإنّه يستحوذ على كائن المزامنة لذلك المورد (resource's mutex). ويحرّر ذلك (releases) الخيطُ كائن المزامنة بمجرّد الانتهاء من العمل على المورد.
</p>

<p>
	وعند استحواذ خيط على كائن مزامنةٍ فإنّ كلّ الاستدعاءات للاستحواذ على ذلك الكائن لن تعود إلى أن يُحرَّر.
</p>

<p>
	لفهم ذلك بشكل أفضل، فكّر في كائن المزامنة كطابور انتظار في متجر كبير، إذ تنتقل الخيوط في الطابور لمحاولة الاستحواذ على كائن المزامنة، ثم تنتظر الخيوط التي تسبقُها حتى تنتهي، ثم تستخدم المورد، ثم تخرج عن الطابور عن طريق تحرير كائن المزامنة. فلو حَاول الجميع الوصول إلى المورد في نفس الوقت ستحدث فوضى كبيرة.
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	المكتبة <code>std::mutex</code>هي تطبيق كائن المزامنة في C++‎ 11.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2216_35" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;thread&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;mutex&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">void</span><span class="pln"> add_1</span><span class="pun">(</span><span class="typ">int</span><span class="pun">&amp;</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> mutex</span><span class="pun">&amp;</span><span class="pln"> m</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// الدالة التي ستُنفّذ في الخيط</span><span class="pln">
    m</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">();</span><span class="pln">
    i </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    m</span><span class="pun">.</span><span class="pln">unlock</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln">
    var </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    mutex m</span><span class="pun">;</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> var </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 1</span><span class="pln">

    thread t1</span><span class="pun">(</span><span class="pln">add_1</span><span class="pun">,</span><span class="pln"> var</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">);</span><span class="pln"> </span><span class="com">// إنشاء خيط مع وسائط</span><span class="pln">
    thread t2</span><span class="pun">(</span><span class="pln">add_1</span><span class="pun">,</span><span class="pln"> var</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">);</span><span class="pln"> </span><span class="com">// إنشاء خيط آخر</span><span class="pln">
    t1</span><span class="pun">.</span><span class="pln">join</span><span class="pun">();</span><span class="pln"> t2</span><span class="pun">.</span><span class="pln">join</span><span class="pun">();</span><span class="pln"> </span><span class="com">// انتظار انتهاء الخيطين</span><span class="pln">

    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> var </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 3</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617025849="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 83: RAII: Resource Acquisition Is Initialization والفصل Chapter 132: Resource Management من كتاب <a data-ss1617025849="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1187</guid><pubDate>Mon, 29 Mar 2021 15:25:18 +0000</pubDate></item><item><title>&#x623;&#x646;&#x638;&#x645;&#x629; &#x627;&#x644;&#x62A;&#x635;&#x631;&#x64A;&#x641; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x629; &#x644;&#x628;&#x646;&#x627;&#x621; &#x627;&#x644;&#x628;&#x631;&#x627;&#x645;&#x62C; &#x627;&#x644;&#x645;&#x643;&#x62A;&#x648;&#x628;&#x629; &#x628;&#x644;&#x63A;&#x629; Cpp &#x648;&#x623;&#x647;&#x645; &#x623;&#x62E;&#x637;&#x627;&#x621; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x627;&#x644;&#x628;&#x646;&#x627;&#x621;</title><link>https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%A3%D9%87%D9%85-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-r1186/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061d94320c48_60--------Cpp----.png.4b07e0895030eaec3147be3bef7308b3.png" /></p>

<p>
	لغة C++‎، على غرار C، لها تاريخ طويل ومتنوّع بخصوص سير العمل التصريفي وعمليات البناء. واليوم، لدى لغة C++‎ العديد من أنظمة البناء التي تُستخدَم لتصريف البرامج لعدة منصات أحيانًا داخل نظام بناء واحد. وسننظر في هذا الدرس في بعض أنظمة البناء ونحلّلها.
</p>

<h2>
	إنشاء بيئة بناء باستخدام CMake
</h2>

<p>
	ينشئ CMake بيئات البناء للمُصرِّفات أو بيئات التطوير المتكاملة (IDE) انطلاقًا من تعريف مشروع واحد، وسوف توضّح الأمثلة التالية كيفية إضافة ملف CMake إلى برنامج "Hello World" متعدّد المنصات.
</p>

<p>
	تأخذ ملفات CMake دائمًا الاسم "CMakeLists.txt"، ويجب أن تكون موجودة في مجلّد الجذر لكل مشروع، وربما في المجلدات الفرعية أيضا.
</p>

<p>
	هذا مثال على ملف CMakeLists.txt:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_7" style="">
<span class="pln">cmake_minimum_required</span><span class="pun">(</span><span class="pln">VERSION </span><span class="lit">2.4</span><span class="pun">)</span><span class="pln">
project</span><span class="pun">(</span><span class="typ">HelloWorld</span><span class="pun">)</span><span class="pln">
add_executable</span><span class="pun">(</span><span class="typ">HelloWorld</span><span class="pln"> main</span><span class="pun">.</span><span class="pln">cpp</span><span class="pun">)</span></pre>

<p>
	يمكنك رؤية البرنامج يعمل مباشرة <a data-ss1617025341="1" href="http://coliru.stacked-crooked.com/a/616a0939bdd91b8b" rel="external nofollow">من هنا</a>.
</p>

<p>
	هذا الملفُّ يخبر أداةَ CMake باسم المشروع، وإصدار الملف المتوقع، وبعض التعليمات الضرورية لتوليد ملف تنفيذي (executable) يُسمّى "HelloWorld"، والذي يتطلب ملف <code>‎main.cpp‎</code>.
</p>

<p>
	في المثال التالي: أنشئ بيئة بناء للمُصرِّف أو بيئة تطوير متكاملة من سطر الأوامر:
</p>

<pre class="ipsCode">
&gt; cmake .
</pre>

<p>
	ابن التطبيق بما يلي:
</p>

<pre class="ipsCode">
&gt; cmake --build .
</pre>

<p>
	تولّد هذه الشيفرة بيئة البناء الافتراضية للنظام وفق نظام التشغيل والأدوات المُثبّتة، حافظ على الشيفرة المصدرية نظيفة من آثار البناء (build artifacts) باستخدام نمط تصميم "خارج المصدر" (out-of-source):
</p>

<pre class="ipsCode">
&gt; mkdir build
&gt; cd build
&gt; cmake ..
&gt; cmake --build .
</pre>

<p>
	يمكن لـ CMake أيضًا تجريد (abstract) الأوامر الأساسية للصدفة (shell) الخاصة بالمنصة من المثال السابق:
</p>

<pre class="ipsCode">
&gt; cmake -E make_directory build
&gt; cmake -E chdir build cmake ..
&gt; cmake --build build
</pre>

<p>
	يتضمّن CMake مولدّات لعدد من أدوات البناء الشائعة وبيئات التطوير المتكاملة، ولإنشاء ملفات makeﬁles لأجل <a data-ss1617025341="1" href="https://docs.microsoft.com/ar-sa/cpp/build/reference/nmake-reference?redirectedfrom=MSDN&amp;view=vs-2019" rel="external nofollow">nmake الخاصة بـ Visual Studio</a>:
</p>

<pre class="ipsCode">
&gt; cmake -G "NMake Makefiles" ..
&gt; nmake
</pre>

<h2>
	التصريف باستخدام GNU
</h2>

<p>
	GNU Make هو برنامج مخصّص لأتمتة تنفيذ أوامر الصدفة، وينتمي لعائلة برامج Make، ويتمتع Make بشعبية كبيرة لدى مستخدمي أنظمة التشغيل الشبيهة بيونكس وتلك الموافقة لمعايير POSIX، بما في ذلك الأنظمة المشتقة من نواة لينُكس ونظام Mac OSX ونظام BSD، ويتميّز GNU Make بأنّه مرتبط بمشروع جنو الذي يرتبط بدوره بأنظمة التشغيل المبنية على جنو\لينكس الشهير.
</p>

<p>
	هناك إصدارات أخرى من GNU Make متوافقة مع أنظمة تشغيل أخرى، مثل ويندوز و Mac OS X، وهذا البرنامج مستقر للغاية وله أثر تاريخي يبقيه مشهورًا، لهذا يُدرَّس GNU Make إلى جانب لغتي C و C++‎.
</p>

<h3>
	القواعد الأساسية
</h3>

<p>
	يجب أن تنشئ ملف Makefile في مجلّد المشروع لأجل التصريف بواسطة make. هذا مثال بسيط على ملف Makeﬁle: سنقسم الملف أثناء سرد المثال للشرح …. <strong>Makeﬁle</strong>
</p>

<pre class="ipsCode">
# إعداد بعض المتغيرات لاستخدامها مع الأمر
# g++ أولا، سنحدد المصرف
CXX=g++

# وغيرها g++ ثم نصرّف مع التنبيهات الموصى بها في
CXXFLAGS=-Wall -Wextra -pedantic

# هذا هو ملف الخرج
EXE=app

SRCS=main.cpp
</pre>

<p>
	يُستدَعى هذا الهدف عند استدعاء <code>make</code> في سطر الأوامر، وتقول <code>(EXE)$</code> الموجودة على اليمين أن الهدف <code>all</code> يعتمد على الهدف <code>(EXE)$</code>. لاحظ أنه بما أن هذا هو الهدف الأول، فسيكون الهدف الافتراضي إن استُدعيَت <code>make</code> بدون هدف، نتابع المثال …
</p>

<pre class="ipsCode">
all: $(EXE)

# هذا يكافئ
# app: $(SRCS)
</pre>

<p>
	(SRCS)$ يمكن فصلها، مما يعني أن هذا الهدف سيعتمد على كل الملفات. لاحظ أن هذا الهدف له متن تابع (method body)، وهو الجزء المزاح بمسافة جدول واحدة (tab) وليس أربع مسافات فارغة. وسينفذ <code>make</code> الأمر التالي عند بناء هذا الهدف:
</p>

<pre class="ipsCode">
g++ -Wall -Wextra -pedantic -o app main.cpp
</pre>

<p>
	والذي يعني تصريف <code>main.cpp</code> مع التحذيرات، والإخراج إلى الملف <code>app/.</code>، نتابع …
</p>

<pre class="ipsCode">
$(EXE): $(SRCS)
@$(CXX) $(CXXFLAGS) -o $@ $(SRCS)
</pre>

<p>
	هذا الهدف ينبغي له أن يعكس الهدف <code>all</code>، وإن استدعيْتَ <code>make</code> مع وسيط مثل <code>make clean</code> فسيُستَدعى الهدف <code>gets</code> الموافق له، …
</p>

<pre class="ipsCode">
clean:
@rm -f $(EXE)
</pre>

<p>
	<strong>ملاحظة: </strong>تأكد من أن الإزاحات البادئة قد تمت بزر الجدول (tab)، ولا تستخدم 4 مسافات بيضاء من زر Space. وإلا فسيُطلق الخطأ <code>‎Makefile:10: *** missing separator. Stop.‎</code>.
</p>

<p>
	لتنفيذ هذه الشيفرة من سطر الأوامر، عليك كتابة ما يلي:
</p>

<pre class="ipsCode">
$ cd ~/Path/to/project
$ make
$ ls
app main.cpp Makefile
$ ./app
Hello World!
$ make clean
$ ls
main.cpp Makefile
</pre>

<h3>
	عمليات البناء التزايدية التزايدي Incremental builds
</h3>

<p>
	تظهر فائدة make عندما تكثر الملفات، فماذا لو عدّلت الملف <strong>a.cpp</strong> دون الملف <strong>b.cpp</strong>؟ لن يكون من الحكمة إذًا أن تعيد تصريف b.cpp، لأنّ ذلك سيُهدر الوقت. في المثال أدناه، لنفرض أن لدينا الهيكل التالي لمجلد:
</p>

<pre class="ipsCode">
.
+-- src
|   +-- a.cpp
|   +-- a.hpp
|   +-- b.cpp
|   +-- b.hpp
+-- Makefile
</pre>

<p>
	فسيكون هذا ملف <strong>Makeﬁle</strong> جيد:
</p>

<pre class="ipsCode">
CXX=g++
CXXFLAGS=-Wall -Wextra -pedantic
EXE=app
SRCS_GLOB=src/*.cpp
SRCS=$(wildcard $(SRCS_GLOB))
OBJS=$(SRCS:.cpp=.o)
all: $(EXE)
$(EXE): $(OBJS)
@$(CXX) -o $@ $(OBJS)
depend: .depend
.depend: $(SRCS)
@-rm -f ./.depend
@$(CXX) $(CXXFLAGS) -MM $^&gt;&gt;./.depend
clean:
-rm -f $(EXE)
-rm $(OBJS)
-rm *~
-rm .depend
include .depend
</pre>

<p>
	مرّة أخرى، لاحظ إزاحات الجدول البادئة. سيضمن ملفّ Makeﬁle الجديد ألّا تصرّف إلّا الملفات التي عُدِّلت، وهذا سيقلّل وقت التصريف.
</p>

<h3>
	التوثيق
</h3>

<p>
	للحصول على المزيد من المعلومات عن make، راجع <a data-ss1617025341="1" href="https://www.gnu.org/software/make/manual/" rel="external nofollow">التوثيق الرسمي -بالإنجليزية- من مؤسسة البرمجيات الحرة</a> وإجابة dmckee التفصيلية على <a data-ss1617025341="1" href="https://stackoverflow.com/questions/2481269/how-to-make-a-simple-c-makefile/2481326#2481326" rel="external nofollow">موقع stackoverﬂow</a>.
</p>

<h2>
	البناء بواسطة SCons
</h2>

<p>
	يمكنك بناء شيفرة "Hello World" متعدّدة المنصات باستخدام أداة <a data-ss1617025341="1" href="https://scons.org/" rel="external nofollow">Scons</a>، وهي أداة بناء برامج تستخدم لغة <a data-ss1617025341="1" href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a>.
</p>

<p>
	أولاً، عليك إنشاء ملف يُسمّى <code>‎SConstruct‎</code> -سيبحث SCons عن هذا الملف افتراضيًا-، وينبغي أن يكون الملف -للآن- في مجلّد مع الملف <code>‎hello.cpp‎</code>. اكتب السطر التالي في الملف الجديد:
</p>

<pre class="ipsCode">
Program('hello.cpp')
</pre>

<p>
	الآن، من سطر الأوامر، نفّذ الأمر <code>‎scons‎</code>، ينبغي أن ترى شيئًا من هذا القبيل:
</p>

<pre class="ipsCode">
$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o hello.o -c hello.cpp
g++ -o hello hello.o
scons: done building targets.
</pre>

<p>
	رغم أن التفاصيل ستختلف وفقًا لنظام التشغيل والمُصرِّف المُستخدم. سيساعدك الصنفان <code>‎Environment‎</code> و <code>‎Glob‎</code> على إعداد ما يجب بناؤه. على سبيل المثال، سيبني الملف <code>‎SConstruct‎</code> الملف التنفيذي <code>hello</code> باستخدام جميع ملفات <code>cpp</code> في مجلد <code>src</code>، كما أن <code>CPPPATH</code> الخاص به سيكون <code>‎/usr/include/boost‎</code>، كما يحدّد معيار C++‎ 11، انظر:
</p>

<pre class="ipsCode">
env=Environment(CPPPATH='/usr/include/boost/',
CPPDEFINES=[],
LIBS=[],
SCONS_CXX_STANDARD="c++11"
)
env.Program('hello', Glob('src/*.cpp'))
</pre>

<h2>
	Autotools
</h2>

<p>
	هي مجموعة من البرامج التي تُستخدَم لإنشاء نظام بناء جنو (GNU Build System) لأجل حزمة برامج معيّنة، وهي حزمة من الأدوات تعمل معًا لإنتاج العديد من موارد البناء، مثل ملفات Makeﬁle -لتُستخدَم مع برنامج GNU Make، لهذا تُعدُّ هذه الحزمة المولّد المعياري لأنظمة البناء. فيما يلي بعض برامج Autotools الأساسية:
</p>

<ul>
<li>
		Autoconf.
	</li>
	<li>
		Automake (لا تخلط بينها وبين <code>‎make‎</code>).
	</li>
</ul>
<p>
	والهدف من حزمة Autotools بشكل عام، هو توليد برنامج نصّي متوافق مع يونيكس (Unix-compatible script)، إضافة إلى ملفّ Makeﬁle للسماح للأمر التالي بإنجاز عملية البناء -وكذلك تثبيت- معظم الحُزم:
</p>

<pre class="ipsCode">
./configure &amp;&amp; make &amp;&amp; make install
</pre>

<p>
	وترتبط حزمة Autotools على هذا النحو أيضًا ببعض مدراء الحزم (package managers)، خاصة تلك المرتبطة بأنظمة التشغيل التي تتوافق مع POSIX.
</p>

<h2>
	Ninja
</h2>

<p>
	يوصف نظام بناء Ninja في موقعه بأنه <a data-ss1617025341="1" href="https://ninja-build.org/" rel="external nofollow">"نظام بناء صغير يركّز على السرعة"</a>، وصُمِّم هذا النظام لكي تُبنى ملفاته بواسطة مولدات ملفات بناء النظام (build system ﬁle generators)، ويعتمد أسلوبًا منخفض المستوى (low-level) لبناء الأنظمة، وذلك على عكس مدراء أنظمة البناء عالية المستوى مثل CMake أو Meson.
</p>

<p>
	كُتِب نظام Ninja أساسًا بلغة C++‎ و Python، وقد أُنشِئ كبديل لنظام البناء SCons في مشروع Chromium.
</p>

<h2>
	NMAKE (أداة صيانة برامج Microsoft)
</h2>

<p>
	NMAKE هي أداة سطر أوامر طوّرتها ميكروسوفت لاستخدامها مع Visual Studio و / أو أدوات سطر أوامر Visual C++‎. ويُنظر إليها على أنها نظام بناء يندرج ضمن مجموعة عائلة Make لأنظمة البناء، ولكنّه يتميّز ببعض الميزات الخاصّة التي تميّزه عن برامج Make الشبيهة بيونكس الأخرى، مثل دعم صياغة مسارات ملفات Windows (والتي تختلف عن مسارات الملفات في يونيكس).
</p>

<h2>
	أخطاء المصرف الشائعة (GCC)
</h2>

<h3>
	مرجعّ غير معرَّف إلى "***"
</h3>

<p>
	تحدث أخطاء الرابط (linker) عندما يعجز عن العثور عن رمز مُستخدم، ويحدث هذا غالبًا عند عدم ربط إحدى المكتبات المستخدمة.
</p>

<ul>
<li>
		qmake:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_10" style="">
<span class="pln">LIBS </span><span class="pun">+=</span><span class="pln"> nameOfLib</span></pre>

<ul>
<li>
		cmake:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_12" style="">
<span class="pln">TARGET_LINK_LIBRARIES</span><span class="pun">(</span><span class="pln">target nameOfLib</span><span class="pun">)</span></pre>

<ul>
<li>
		استدعاء g++‎:
	</li>
</ul>
<pre class="ipsCode">
g++ -o main main.cpp -Llibrary/dir -lnameOfLib
</pre>

<p>
	قد ينسى المرء أيضًا تصريف وربط جميع ملفّات <code>‎.cpp‎</code> المستخدمة (تحدّد functionModule.cpp الدالّة المطلوبة):
</p>

<pre class="ipsCode">
g++ -o binName main.o functionsModule.o
</pre>

<h3>
	error: '***' was not declared in this scope
</h3>

<p>
	يحدث هذا الخطأ في حال استخدام كائن غير معروف.
</p>

<h3>
	أخطاء متعلقة بالمتغيّرات
</h3>

<p>
	لن تُصرّف الشيفرة التالية، لأن المتغير <code>i</code> غير موجود في نطاق الدالة <code>main</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_14" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> i </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> 
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الحل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_16" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> i </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	أخطاء متعلقة بالدوال
</h3>

<p>
	يحدث هذا الخطأ غالبًا في حال عدم تضمين الترويسة المطلوبة (على سبيل المثال استخدام <code>‎std::cout‎</code> دون <code>‎#include &lt;iostream&gt;‎</code>). ففي المثال التالي، لن تُصرّف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_18" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    doCompile</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> doCompile</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"No!"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الحلّ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_20" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> doCompile</span><span class="pun">();</span><span class="pln"> </span><span class="com">// تصريح لاحق للدالة</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    doCompile</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> doCompile</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"No!"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_22" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> doCompile</span><span class="pun">()</span><span class="pln"> </span><span class="com">// تعريف الدالة قبل استخدامها</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"No!"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    doCompile</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: يفسر المُصرِّف الشيفرة من الأعلى إلى الأسفل (للتبسيط فقط)، لذا يجب التصريح عن كل شيء (أو تعريفه) قبل استخدامه.
</p>

<h3>
	fatal error: ***: No such ﬁle or directory
</h3>

<p>
	يحدث هذا الخطأ عندما يعجز المُصرِّف عن العثور عن ملفّ معيّن (ملف مصدري يستخدم <code>‎#include "someFile.hpp"‎</code>).
</p>

<ul>
<li>
		qmake:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_24" style="">
<span class="pln">INCLUDEPATH </span><span class="pun">+=</span><span class="pln"> dir </span><span class="pun">/</span><span class="pln"> </span><span class="typ">Of</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="typ">File</span></pre>

<ul>
<li>
		cmake:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_26" style="">
<span class="pln">include_directories</span><span class="pun">(</span><span class="pln">dir</span><span class="pun">/</span><span class="typ">Of</span><span class="pun">/</span><span class="typ">File</span><span class="pun">)</span></pre>

<ul>
<li>
		استدعاء g++‎:
	</li>
</ul>
<pre class="ipsCode">
g++ -o main main.cpp -Idir/Of/File
</pre>

<h2>
	عدم التوافقية مع لغة C
</h2>

<p>
	سنتحدّث في هذا القسم عن شيفرات C غير المتوافقة مع لغة C++‎ والتي ستعطل مصرّفاتها‎.
</p>

<h3>
	الكلمات المفتاحية المحجوزة
</h3>

<p>
	هناك كلمات مفتاحية لها غرض خاص في C++‎، فالشيفرة التالية مثلًا جائزة في C، لكن غير جائزة في C++‎.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_29" style="">
<span class="typ">int</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span></pre>

<p>
	وإصلاح هذه الأخطاء سهل، فكل ما عليك هو إعادة تسمية المتغيّر.
</p>

<h3>
	المؤشّرات ذات النوعية الضعيفة Weakly Typed Pointers
</h3>

<p>
	يمكن تحويل المؤشّرات في C إلى النوع <code>‎void*‎</code>، أما في C++‎ فستحتاج إلى تحويل صريح explicit cast. انظر المثال التالي حيث تكون الشيفرة غير جائزة في C++‎، ولكن جائزة في C:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_31" style="">
<span class="kwd">void</span><span class="pun">*</span><span class="pln"> ptr</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pun">*</span><span class="pln"> intptr </span><span class="pun">=</span><span class="pln"> ptr</span><span class="pun">;</span></pre>

<p>
	إضافة تحويل صريح (explicit cast) ستحل المشكلة لكن قد يسبّب مشاكل أخرى.
</p>

<h3>
	goto أو switch
</h3>

<p>
	لا يجوز في ++C أن تتخطي التهيئة باستخدام <code>‎goto‎</code> أو <code>‎switch‎</code>. انظر المثال التالي حيث تكون الشيفرة صحيحة في C، وغير صحيحة في C++‎:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9050_33" style="">
<span class="kwd">goto</span><span class="pln"> foo</span><span class="pun">;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> skipped </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
foo</span><span class="pun">;</span></pre>

<p>
	قد يتطلب إصلاح هذه الأخطاء إعادة تصميم البرنامج.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617025341="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصول
</p>

<ul>
<li>
		Chapter 130: Build Systems
	</li>
	<li>
		Chapter 139: Common compile/linker errors (GCC)‎
	</li>
	<li>
		Chapter 136: C incompatibilities
	</li>
</ul>
<p>
	من كتاب <a data-ss1617025341="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1186</guid><pubDate>Mon, 29 Mar 2021 13:45:58 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x635;&#x62D;&#x629; &#x627;&#x644;&#x62B;&#x628;&#x627;&#x62A;&#x64A;&#x629; (Const Correctness) &#x641;&#x64A; &#x643;&#x62A;&#x627;&#x628;&#x629; &#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%B5%D8%AD%D8%A9-%D8%A7%D9%84%D8%AB%D8%A8%D8%A7%D8%AA%D9%8A%D8%A9-const-correctness-%D9%81%D9%8A-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-cpp-r1185/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061d6f38927e_59----(Const-Correctness)----Cpp.png.6933e19bdad0c8a704099fc33f6ea6b9.png" /></p>

<p>
	الصحة الثباتية (Const Correctness) أسلوب لتصميم الشيفرات، يعتمد على فكرة أنّه لا ينبغي أن تُتاح إمكانية تعديل نسخة معيّنة -على سبيل المثال الحق في الكتابة- إلّا للشيفرَات التي تحتاج إلى تعديل تلك النسخة، وبالمقابل، فإنّ أيّ شيفرة لا تحتاج إلى تعديل نُسخة ما لن تملك القدرة على تعديلها، أي سيكون لها حق القراءة فقط.
</p>

<p>
	تصون هذه المقاربة النُسخة من أن تُعدَّل عن غير قصد، لأنّ ذلك قد يجعل الشيفرة غير موثوقة، كما يوضّح للمبرمجين الذي يقرؤون الشيفرة ما إذا كانت الشيفرة تهدف إلى تغيير حالة النُسخة أم لا. وتسمح أيضًا بالتعامل مع النسخ ككائنات ثابتة (<code>‎const‎</code>) عندما لا تكون هناك حاجة إلى تعديلها، أو تعريفها ككائنات ثابتة إذا لم تكن بحاجة إلى تغييرها بعد التهيئة، وذلك دون التأثير على وظيفتها.
</p>

<p>
	يتم ذلك عن طريق إعطاء التوابعِ مؤهلات (const CV-qualifiers)، وعن طريق وسم معاملات المؤشّرات / المراجع بالكلمة المفتاحية <code>‎const‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_7" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstCorrectClass</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> getX</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="com">// الدالة ثابتة: أي أنها لن تعدل النسخة</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> setX</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        x </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="com">// غير ثابتة: أي أنها ستعدل النسخة</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// المعامل ثابت، أي أنّه لن يُعدّل</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> const_correct_reader</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstCorrectClass</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getX</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// المعامل غير ثابت: سيُعدَّل</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> const_correct_writer</span><span class="pun">(</span><span class="typ">ConstCorrectClass</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">setX</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstCorrectClass</span><span class="pln"> invariant</span><span class="pun">;</span><span class="pln"> </span><span class="com">// النسخة ثابتة: لن تُعدّل</span><span class="pln">
</span><span class="typ">ConstCorrectClass</span><span class="pln"> variant</span><span class="pun">;</span><span class="pln"> </span><span class="com">// النسخة غير ثابتة: يمكن أن تُعدَّل</span><span class="pln">
</span><span class="com">// …</span><span class="pln">

</span><span class="com">// صحيح: استدعاء دالة ثابتة غير مُعدِّلة على نسخة ثابتة</span><span class="pln">
const_correct_reader</span><span class="pun">(</span><span class="pln">invariant</span><span class="pun">);</span><span class="pln"> 

</span><span class="com">// صحيح: استدعاء دالة ثابتة غير مُعدِّلة على نسخة قابة للتعديل</span><span class="pln">
const_correct_reader</span><span class="pun">(</span><span class="pln">variant</span><span class="pun">);</span><span class="pln"> 

</span><span class="com">// صحيح: استدعاء دالة ثابتة مُعدِّلة على نسخة قابلة للتعديل</span><span class="pln">
const_correct_writer</span><span class="pun">(</span><span class="pln">variant</span><span class="pun">);</span><span class="pln"> 

</span><span class="com">// خطأ: استدعاء دالة ثابتة مُعدِّلة على نسخة ثابتة</span><span class="pln">
const_correct_writer</span><span class="pun">(</span><span class="pln">invariant</span><span class="pun">);</span><span class="pln"> </span></pre>

<p>
	نظرًا لطبيعة الصحة الثباتية، فإنّها تبدأ بتوابع الصنف، ثمّ تستمر إلى الخارج، وسيطلق المصرِّف خطأً إذا حاولت استدعاء تابع غير ثابت من نُسخة ثابتة، أو من نُسخة غير ثابتة تُعامل كنسخة ثابتة.
</p>

<h2>
	الصحة الثباتية في تصميم الأصناف
</h2>

<p>
	جميع الدوال التابعة في صنف صحيح ثباتيًا (const-correct class)، التي لا تُغيّر الحالة المنطقية، يكون مؤشّر <code>‎this‎</code> الخاص بها ثابتًا، للدلالة على أنّها لن تُعدّل الكائن، لكن هذا لا يمنع أن تكون هناك حقول متغيرة - <code>‎mutable‎</code> - والتي يمكن تعديلها حتى لو كانت الكائنات التي تنتمي إليها ثابتة.
</p>

<p>
	عندما تعيد دالةٌ تأهيلها ثابت (<code>‎const‎</code>) مرجعًا، فينبغي أن يكون ذلك المرجع ثابتًا. يتيح هذا لها إمكانية أن تُستدعى على كلٍّ من النُسخ الثابتة، وكذلك النسخ غير المؤهّلة (non-cv-qualiﬁed)، إذ ستكون <code>‎const T*‎</code> قادرة على الارتباط بـ <code>‎T*‎</code> أو <code>‎const T*‎</code>. هذا يسمح بدوره للدوالّ بأن تصرّح عن المُعاملات المُمرّة بالمرجع (passed-by-reference) على أنّها ثابتة عندما لا تحتاج إلى تعديل، وذلك دون التأثير على وظيفتها.
</p>

<p>
	علاوة على ذلك، في الأصناف الصحيحة ثباتيًا، تكون جميع مُعاملات الدالة المُمرّة بالمرجع صحيحة ثباتيًا، إذ لن يمكن تعديلها إلّا عندما تحتاج الدالّة إلى تعديلها صراحة.
</p>

<p>
	أولاً، لننظر إلى مؤهِّلات للمؤشّر <code>‎this‎</code> : لنفرض أن لدينا صنف <code>Field</code>، له الدالة التابعة <code>;(void insert_value(int</code>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_9" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Field</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">ConstIncorrect</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">// يعدِّل</span><span class="pln">
   </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> getField</span><span class="pun">();</span><span class="pln"> </span><span class="com">// قد يعدِّل، كما تُكشَف الأعضاء كمراجع غير ثابتة وذلك لإتاحة التعديل غير </span><span class="pln">
                          </span><span class="com">//  المباشر</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> setField</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">// يعدِّل</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">);</span><span class="pln"> </span><span class="com">// قد يعدِّل</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> doNothing</span><span class="pun">();</span><span class="pln"> </span><span class="com">//  قد يعدِّل</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">ConstIncorrect</span><span class="pun">::</span><span class="typ">ConstIncorrect</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">):</span><span class="pln"> fld</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln"> </span><span class="com">//  يعدِّل</span><span class="pln">
</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pun">::</span><span class="pln">getField</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="com">// لا يعدِّل</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pun">::</span><span class="pln">setField</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fld </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="com">// يعدِّل</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pun">::</span><span class="pln">doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">//  يعدِّل</span><span class="pln">
    fld</span><span class="pun">.</span><span class="pln">insert_value</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pun">::</span><span class="pln">doNothing</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln"> </span><span class="com">// لا يعدِّل</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Field</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">ConstCorrectCVQ</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  يعدِّل</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> getField</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا يعدِّل، تُكشف الأعضاء كمراجع ثابتة لمنع التعديل غير المباشر</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> setField</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  يعدِّل</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  يعدِّل</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> doNothing</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا يعدِّل</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">ConstCorrectCVQ</span><span class="pun">::</span><span class="typ">ConstCorrectCVQ</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">):</span><span class="pln"> fld</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pun">::</span><span class="pln">getField</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pun">::</span><span class="pln">setField</span><span class="pun">(</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fld </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pun">::</span><span class="pln">doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fld</span><span class="pun">.</span><span class="pln">insert_value</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pun">::</span><span class="pln">doNothing</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	هذا لن يعمل إذا لا يمكن استدعاء الدوال التابعة على نُسخ <code>ConstIncorrect</code>، نتابع …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_11" style="">
<span class="kwd">void</span><span class="pln"> const_correct_func</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstIncorrect</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Field</span><span class="pln"> f </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getField</span><span class="pun">();</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">do_nothing</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما هذا فيعمل إذ يمكن استدعاء <code>()doNothing</code> و <code>()getField</code> على نسخ <code>ConstCorrectCVQ</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_13" style="">
<span class="kwd">void</span><span class="pln"> const_correct_func</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstCorrectCVQ</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Field</span><span class="pln"> f </span><span class="pun">=</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getField</span><span class="pun">();</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">do_nothing</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا بعد ذلك دمج هذا مع معاملات الدوال الصحيحة ثباتيا (‎Const Correct Function Parameters‎)، وسيجعل هذا الصنفَ صحيحًا ثباتيًا بشكل كامل.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_16" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstCorrect</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Field</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
      </span><span class="typ">ConstCorrect</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تعدّل النسخة، ولكن لا تعدّل المعامل</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> getField</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا تعدِّل، وتكشف العضو كمرجع ثابت لمنع التعديل غير المباشر</span><span class="pln">
      </span><span class="kwd">void</span><span class="pln"> setField</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تعدّل النسخة، ولكن لا تعدّل المعامل</span><span class="pln">
      </span><span class="kwd">void</span><span class="pln"> doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">);</span><span class="pln"> </span><span class="com">// passed by value تعدّل، لكن لا تعدّل المعامل - ممرر بالقيمة</span><span class="pln">
      </span><span class="kwd">void</span><span class="pln"> doNothing</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا تعدّل </span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">ConstCorrect</span><span class="pun">::</span><span class="typ">ConstCorrect</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> fld</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> </span><span class="typ">ConstCorrect</span><span class="pun">::</span><span class="pln">getField</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> fld</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrect</span><span class="pun">::</span><span class="pln">setField</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Field</span><span class="pun">&amp;</span><span class="pln"> f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> fld </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrect</span><span class="pun">::</span><span class="pln">doSomething</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fld</span><span class="pun">.</span><span class="pln">insert_value</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstCorrect</span><span class="pun">::</span><span class="pln">doNothing</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	يمكن أيضًا دمج هذا مع <a data-ss1617024748="1" href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%A7%D9%84%D8%B2%D8%A7%D8%A6%D8%AF-overloading-%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D9%87-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%87-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-cpp-r934/" rel="">التحميل الزائد</a> على أساس الصحة الثباتية إذا أردنا سلوك ما عندما تكون النُسخة ثابتة، وسلوكًا آخر مختلفًا في الحالات الأخرى. وإحدى الاستخدامات الشائعة لهذا هي الحاويات (constainers)، إذ توفّر دوال وصول (accessors) لا تسمح بالتعديل إلّا إن كانت الحاوية نفسها غير ثابتة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_19" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstCorrectContainer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> arr</span><span class="pun">[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
      </span><span class="com">// معامل تسجيل يوفر إمكانية القراءة إن كانت النسخة ثابتة، أو إمكانية القراءة/الكتابة خلاف ذلك</span><span class="pln">
      </span><span class="typ">int</span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">[](</span><span class="typ">size_t</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">[](</span><span class="typ">size_t</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يُستخدم هذا كثيرًا في المكتبة القياسية، إذ توفّر معظم الحاويات تحميلات زائدة لأخذ الثباتية في الحسبان.
</p>

<h2>
	معاملات الدوال الصحيحة ثباتيًا
</h2>

<p>
	إذا كانت دالّة ما صحيحة ثباتيًا، فإنّ جميع المُعاملات المُمرَّرة إليها بالمرجع ستكون ثابتة، ما لم تعدّلها الدالّة بشكل مباشر أو غير مباشر. يفيد هذا في منع المبرمج من إجراء تعديل غير مقصود، ويسمح للدالّة بأن تقبل النُسخ الثابتة، وكذلك النسخ غير المؤهّلة، وتجعل مؤشّر النسخة <code>‎this‎</code> من النوع <code>‎const T*‎</code> عندما تُستدعى دالة تابعة ما، حيث <code>‎T‎</code> يمثّل هنا نوع الصنف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_21" style="">
<span class="kwd">struct</span><span class="pln"> </span><span class="typ">Example</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> func</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> const_incorrect_function</span><span class="pun">(</span><span class="typ">Example</span><span class="pun">&amp;</span><span class="pln"> one</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Example</span><span class="pun">*</span><span class="pln"> two</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    one</span><span class="pun">.</span><span class="pln">func</span><span class="pun">();</span><span class="pln">
    two</span><span class="pun">-&gt;</span><span class="pln">func</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> const_correct_function</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Example</span><span class="pun">&amp;</span><span class="pln"> one</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Example</span><span class="pun">*</span><span class="pln"> two</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    one</span><span class="pun">.</span><span class="pln">func</span><span class="pun">();</span><span class="pln">
    two</span><span class="pun">-&gt;</span><span class="pln">func</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Example</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
    const_incorrect_function</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">b</span><span class="pun">);</span><span class="pln">
    const_correct_function</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">b</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا هو خرج البرنامج
</p>

<pre class="ipsCode">
3
3
5
5
</pre>

<p>
	تمسك الدوال الصحيحة ثباتيًا الكثير من الأخطاء التي قد لا تمسكها الدوال غير الصحيحة ثباتيًا كما هو مبيّن أدناه، وذلك رغم أنّ تأثيرات هذا السلوك أقل وضوحًا من التأثيرات الناجمة عن تصميم الأصناف الصحيحة ثباتيًا التي رأيناها من قبل، في أنّ الدوال الصحيحة ثباتيا والأصناف غير الصحيحة ثباتيًا تتسبّب في حدوث أخطاء تصريفية، في حين أنّ الأصناف الصحيحة ثباتيًا والدوال غير الصحيحة ثباتيًا تُصرَّف بلا مشاكل.
</p>

<p>
	لاحظ أنّ الدوالّ غير الصحيحة ثباتيًا تتسبب في حدوث أخطاء تصريفية في حالة تمرير نُسخة ثابتة إليها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_23" style="">
<span class="com">// قراءة القيمة من المتجهة ثم حساب وإعادة قيمة</span><span class="pln">
</span><span class="com">// إمساك القيمة المعادة لأجل تسريع البرنامج</span><span class="pln">
</span><span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">typename</span><span class="pln"> T</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> T</span><span class="pun">&amp;</span><span class="pln"> bad_func</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;&amp;</span><span class="pln"> v</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Helper</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;&amp;</span><span class="pln"> h</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إمساك القيم لإستخدامها مستقبلا</span><span class="pln">
    </span><span class="com">// بعد حساب القيمة المعادة، تُخزّن ويُسجل فهرسها</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> vals </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> v_ind </span><span class="pun">=</span><span class="pln"> h</span><span class="pun">.</span><span class="pln">get_index</span><span class="pun">();</span><span class="pln">            </span><span class="com">// v الفهرس الحالي في</span></pre>

<p>
	سيكون السطر التالي مساويًا لـ <code>1-</code> إن كان فهرس المخزن المؤقت غير مسجلًا …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_25" style="">
<span class="pln">  </span><span class="typ">int</span><span class="pln"> vals_ind </span><span class="pun">=</span><span class="pln"> h</span><span class="pun">.</span><span class="pln">get_cache_index</span><span class="pun">(</span><span class="pln">v_ind</span><span class="pun">);</span><span class="pln">        
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vals</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vals_ind </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vals_ind </span><span class="pun">&lt;</span><span class="pln"> vals</span><span class="pun">.</span><span class="pln">size</span><span class="pun">())</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">!(</span><span class="pln">h</span><span class="pun">.</span><span class="pln">needs_recalc</span><span class="pun">()))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> vals</span><span class="pun">[</span><span class="pln">h</span><span class="pun">.</span><span class="pln">get_cache_index</span><span class="pun">(</span><span class="pln">v_ind</span><span class="pun">)];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
    T temp </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">[</span><span class="pln">v_ind</span><span class="pun">];</span><span class="pln">
    temp </span><span class="pun">-=</span><span class="pln"> h</span><span class="pun">.</span><span class="pln">poll_device</span><span class="pun">();</span><span class="pln">
    temp </span><span class="pun">*=</span><span class="pln"> h</span><span class="pun">.</span><span class="pln">obtain_random</span><span class="pun">();</span><span class="pln">
    temp </span><span class="pun">+=</span><span class="pln"> h</span><span class="pun">.</span><span class="pln">do_tedious_calculation</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">[</span><span class="pln">h</span><span class="pun">.</span><span class="pln">get_last_handled_index</span><span class="pun">()]);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vals_ind </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        vals</span><span class="pun">[</span><span class="pln">vals_ind</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        v</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لا ينبغي الدخول إلى القيم</span><span class="pln">
        vals_ind </span><span class="pun">=</span><span class="pln"> vals</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        h</span><span class="pun">.</span><span class="pln">register_index</span><span class="pun">(</span><span class="pln">v_ind</span><span class="pun">,</span><span class="pln"> vals_ind</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> vals</span><span class="pun">[</span><span class="pln">vals_ind</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// النسخة الصحيحة ثباتيا تماثل النسخة أعلاه، لذا سنتجاوز معظمها</span><span class="pln">
</span><span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">typename</span><span class="pln"> T</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> T</span><span class="pun">&amp;</span><span class="pln"> good_func</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;&amp;</span><span class="pln"> v</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Helper</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;&amp;</span><span class="pln"> h</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">vals_ind </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    vals</span><span class="pun">[</span><span class="pln">vals_ind</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        v</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">);</span><span class="pln">        </span><span class="com">// Error: discards qualifiers.</span><span class="pln">
        vals_ind </span><span class="pun">=</span><span class="pln"> vals</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        h</span><span class="pun">.</span><span class="pln">register_index</span><span class="pun">(</span><span class="pln">v_ind</span><span class="pun">,</span><span class="pln"> vals_ind</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> vals</span><span class="pun">[</span><span class="pln">vals_ind</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	الصحة الثباتية كأداة للتوثيق
</h2>

<p>
	من فوائد مفهوم الصحة الثباتية (const correctness) هي أنّه يمكن استخدامها لتوثيق الشيفرات، عبر توفير ضمانات وتوجيهات للمبرمجين والمستخدمين الآخرين. هذه الضمانات يفرضها المُصرِّف بسبب الثباتية، مع غيابٍ للثباتية يوحي أن الشيفرة لا توفرها.
</p>

<h3>
	التوابع المؤهّلة ثباتيًا
</h3>

<ul>
<li>
		يمكن أن نفترض أنّ المبرمج الذي كتب الشيفرة يقصد أنّ الدوال التابعة الثابتة ستقرأ النسخة، و:
	</li>
	<li>
		لن تعدّل الحالة المنطقية للنُسخة التي استُدعيت عليها. نتيجة لذلك، فلن تعدّل أيّ حقل من حقول النُسخة التي استُدعيت عليها، باستثناء المتغيّرات القابلة للتغيير <code>‎mutable‎</code>.
	</li>
	<li>
		لن تستدعي دوال أخرى من شأنّها تعديل أيّ حقل من حقول النُسخة، باستثناء المتغيّرات القابلة للتغيير <code>‎mutable‎</code>.
	</li>
	<li>
		بالمقابل، يمكن افتراض أنّ أيّ دالة تابعة غير ثابتة سوف تسعى إلى تعديل النُسخة، و:
	</li>
	<li>
		قد تعدّل الحالة المنطقية أو لا.
	</li>
	<li>
		قد تستدعي دوال أخرى يمكن أن تعدّل الحالة المنطقية أو لا.
	</li>
</ul>
<p>
	يمكن استخدام هذا لأجل بناء افتراضات حول حالة الكائن بعد استدعاء دالة تابعة معيّنة عليه دون الحاجة إلى رؤية تعريف تلك الدالّة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_27" style="">
<span class="com">// ConstMemberFunctions.h</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">mutable</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> cache</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">mutable</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> state_changed</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
    </span><span class="com">// يعدّل المنشئ الحالة المنطقية، لذا لا ضرورة لأيّ افتراضات</span><span class="pln">
    </span><span class="typ">ConstMemberFunctions</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span></pre>

<p>
	يمكن أن نفترض أن الدالة التالية لن تعدل الحالة المنطقية، ولن تستدعي <code>()set_val</code>، ويمكن كذلك أن تستدعي <code>()bad_func</code> أو <code>()squared_calc</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_29" style="">
<span class="pln">    </span><span class="typ">int</span><span class="pln"> calc</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span></pre>

<p>
	يمكن أن نفترض أن الدالة التالية لن تعدل الحالة المنطقية، ولن تستدعي <code>()set_val</code>، ويمكن كذلك أن تستدعي <code>()bad_func</code> أو <code>()calc</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_31" style="">
<span class="pln">    </span><span class="typ">int</span><span class="pln"> squared_calc</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span></pre>

<p>
	يمكن أن نفترض أن الدالة التالية لن تعدل الحالة المنطقية، ولن تستدعي <code>()set_val</code>، ويمكن كذلك أن تستدعي <code>()calc</code> أو <code>()squared_calc</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_33" style="">
<span class="pln">    </span><span class="kwd">void</span><span class="pln"> bad_func</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span></pre>

<p>
	يمكن أن نفترض أن الدالة التالية لن تعدل الحالة المنطقية، ولن تستدعي <code>()set_val</code>، ويمكن كذلك أن تستدعي <code>()bad_func</code> أو <code>()squared_calc</code> أو <code>()calc</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_35" style="">
<span class="pln">    </span><span class="kwd">void</span><span class="pln"> set_val</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وفقًا لقواعد الصحة الثباتية، ستُفرض هذه الافتراضات في الواقع بواسطة المُصرِّف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_37" style="">
<span class="com">// ConstMemberFunctions.cpp</span><span class="pln">
</span><span class="typ">ConstMemberFunctions</span><span class="pun">::</span><span class="typ">ConstMemberFunctions</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v </span><span class="com">/* = 0*/</span><span class="pun">)</span><span class="pln">
</span><span class="pun">:</span><span class="pln"> cache</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> val</span><span class="pun">(</span><span class="pln">v</span><span class="pun">),</span><span class="pln"> state_changed</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="com">// لقد كان افتراضنا صحيحا</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">::</span><span class="pln">calc</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">state_changed</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        cache </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">
        state_changed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> cache</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// لقد كان افتراضنا صحيحا</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">::</span><span class="pln">squared_calc</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> calc</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> calc</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// لقد كان افتراضنا غير صحيح</span><span class="pln">
</span><span class="com">// للمؤهّلات `this` فشل تصريف الدالة بسبب فقدان</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">::</span><span class="pln">bad_func</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    set_val</span><span class="pun">(</span><span class="lit">863</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// لقد كان افتراضنا صحيحا</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">::</span><span class="pln">set_val</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">v </span><span class="pun">!=</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        val </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">;</span><span class="pln">
        state_changed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	مُعاملات الدالة الثابتة
</h3>

<ul>
<li>
		يمكن أن نفترض أنّ الدوالّ التي تقبل مُعاملًا واحدًا ثابتًا أو أكثر تهدف إلى قراءة تلك المُعاملات، و:
	</li>
	<li>
		أنّها لن تعدّل تلك المُعاملات، أو تستدعي عليها تابعًا يمكن أن يُعدّلها.
	</li>
	<li>
		لن تمرّر تلك المُعاملات إلى أيّ دالّة أخرى من شأنها تعديلها و/أو استدعاء أي توابع أخرى من شأنها تعديلها.
	</li>
	<li>
		بالمقابل، يمكن افتراض أنّ أي دالّة تقبل مُعاملًا واحدًا غير ثابت أو أكثر ستسعى لتعديل تلك المُعاملات، و:
	</li>
	<li>
		قد تعدِّل أو لا تعدّل تلك المُعاملات، أو قد تستدعي عليها توابع يمكن أن تعدّلها.
	</li>
	<li>
		قد تمرّر أو قد لا تمرّر تلك المُعاملات إلى دوال أخرى يمكن أن تعدّلها و / أو تستدعي عليها توابع قد تعدّلها.
	</li>
</ul>
<p>
	يمكن استخدام هذا الأمر لأجل بناء افتراضات حول الحالة التي ستكون عليها المُعاملات بعد تمريرها إلى دالّة معيّنة، حتى دون النظر في تعريف تلك الدالّة.
</p>

<p>
	فيما يلي، يمكن أن نفترض أن <code>c</code> لم تُعدَّل وأن <code>()c.set_val</code> لم تُستدعى ولم تمرَّر إلى <code>()non_qualified_function_parameter</code>، وإذا مُرِّرَت إلى <code>()one_const_one_not</code> فستكون أول المعامِلات …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_39" style="">
<span class="com">// function_parameter.h</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> const_function_parameter</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">);</span></pre>

<p>
	يمكن أن نفترض أن <code>c</code> عُدِّلَت و/أو أن <code>()c.set_val</code> استدعيَت، وقد تكون مرِّرَت إلى أي من تلك الدوال، وإن مُرِّرَت إلى <code>()one_const_one_not</code> فقد تكون أيًا من المعامِلات …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_41" style="">
<span class="kwd">void</span><span class="pln"> non_qualified_function_parameter</span><span class="pun">(</span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">);</span></pre>

<p>
	نستطيع افتراض أن: <code>l</code> لم تُعدَّل، وأن <code>()l.set_val</code> لن تُستدعى. <code>l</code> قد تُمرَّر أو لا إلى <code>()const_function_parameter</code>. تم تعديل <code>r</code> و/أو قد تستدعى <code>()r.set_val</code>. قد تُمرَّر <code>r</code> أو لا إلى أي من الدوال السابقة. نتابع …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_43" style="">
<span class="kwd">void</span><span class="pln"> one_const_one_not</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> l</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> r</span><span class="pun">);</span></pre>

<p>
	يمكن أن نفترض أن <code>c</code> لم تُعدَّل وأن <code>()c.set_val</code> لم تُستدعى، وأنما لم تُمرَّر إلى <code>()non_qualified_function_parameter</code>، وإن مُرِّرَت إلى <code>()one_const_one_not</code> فقد تكون أيًا من المعامِلات …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_47" style="">
<span class="kwd">void</span><span class="pln"> bad_parameter</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">);</span></pre>

<p>
	وفقًا لقواعد الصحة الثباتية، ستُفرض هذه الافتراضات من قِبل المُصرِّف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_49" style="">
<span class="com">// function_parameter.cpp</span><span class="pln">
</span><span class="com">// افتراضنا كان صحيحا</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> const_function_parameter</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"With the current value, the output is: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">calc</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">'\n'</span><span class="pln">
        </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"If squared, it's: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">squared_calc</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// افتراضنا كان صحيحا</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> non_qualified_function_parameter</span><span class="pun">(</span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">set_val</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"For the value 42, the output is: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">calc</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">'\n'</span><span class="pln">
        </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"If squared, it's: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">squared_calc</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// افتراضنا كان صحيحا</span><span class="pln">
</span><span class="com">// لاحظ أنّ الصحة الثباتية لا  تحصِّن التغليف من أن يُكسر، وإنما تمنع الحق في الكتابة</span><span class="pln">
</span><span class="com">// إلا عند الحاجة إليها</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> one_const_one_not</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> l</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> r</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Machiavelli</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> unimportant</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">bool</span><span class="pln"> state_changed</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
    </span><span class="kwd">reinterpret_cast</span><span class="pun">&lt;</span><span class="typ">Machiavelli</span><span class="pun">&amp;&gt;(</span><span class="pln">r</span><span class="pun">).</span><span class="pln">val </span><span class="pun">=</span><span class="pln"> l</span><span class="pun">.</span><span class="pln">calc</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">reinterpret_cast</span><span class="pun">&lt;</span><span class="typ">Machiavelli</span><span class="pun">&amp;&gt;(</span><span class="pln">r</span><span class="pun">).</span><span class="pln">state_changed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    const_function_parameter</span><span class="pun">(</span><span class="pln">l</span><span class="pun">);</span><span class="pln">
    const_function_parameter</span><span class="pun">(</span><span class="pln">r</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتراضنا فيما يلي كان خطأ، وتفشل الدالة في التصريف لأن <code>this</code> فقد مؤهلاتٍ في <code>()c.set_val</code>، …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_51" style="">
<span class="kwd">void</span><span class="pln"> bad_parameter</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">&amp;</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">set_val</span><span class="pun">(</span><span class="lit">18</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	رغم أنّه يمكن التحايل على الصحة الثباتية وكسر الضمانات التي تقدّمها، إلا أنّه ينبغي توخّي الحذر عند فعل ذلك، ويجب أن يكون المبرمج على علم بما يفعل، فمن المحتمل أن يتسبّب في حدوث سلوك غير معرَّف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9239_53" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DealBreaker</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">DealBreaker</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
        </span><span class="com">// اسم غير مسموح به، لكنه ثابت</span><span class="pln">
        </span><span class="kwd">void</span><span class="pln"> no_guarantees</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">DealBreaker</span><span class="pun">::</span><span class="typ">DealBreaker</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v </span><span class="com">/* = 0 */</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">ConstMemberFunctions</span><span class="pun">(</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="com">// افتراضنا كان خاطئا</span><span class="pln">
</span><span class="com">// الصحة الثباتية، وتجعل المصرف يعتقد أننا على علم بتبِعات ما نفعل const_cast تحذف</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">DealBreaker</span><span class="pun">::</span><span class="pln">no_guarantees</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const_cast</span><span class="pun">&lt;</span><span class="typ">DealBreaker</span><span class="pun">*&gt;(</span><span class="kwd">this</span><span class="pun">)-&gt;</span><span class="pln">set_val</span><span class="pun">(</span><span class="lit">823</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">DealBreaker</span><span class="pln"> d</span><span class="pun">(</span><span class="lit">50</span><span class="pun">);</span><span class="pln">
d</span><span class="pun">.</span><span class="pln">no_guarantees</span><span class="pun">();</span><span class="pln"> </span><span class="com">// ثابتة، وقد تُعدّل d سلوك غير معرَّف، إذ أنّ</span></pre>

<p>
	على أي حال، ولأن هذا يتطلب من المبرمج أن يخبر المصرِّف أنه ينوي تجاهل الثباتية (Constness)، ولعدم التماثل (Consistency) بين المصرِّفات، فمن الآمن افتراض أن الشيفرة الصحيحة ثباتيًا <strong>لن</strong> تتجاهل الصحة الثباتية أو التماثل بين المصرِّفات إلا إن حُدِّد خلاف ذلك.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617024748="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 128: Const Correctness من كتاب <a data-ss1617024748="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1185</guid><pubDate>Mon, 29 Mar 2021 13:36:42 +0000</pubDate></item><item><title>&#x623;&#x645;&#x62B;&#x644;&#x629; &#x639;&#x644;&#x649; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x62E;&#x627;&#x62F;&#x645; &#x639;&#x645;&#x64A;&#x644; (Client server) &#x628;&#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644; &#x644;&#x63A;&#x629; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AE%D8%A7%D8%AF%D9%85-%D8%B9%D9%85%D9%8A%D9%84-client-server-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D9%84%D8%BA%D8%A9-cpp-r1184/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061d47eb1de8_58-----(Client-server)---Cpp.png.418df8d85f34b8d76b8a1cbe11ee8ec9.png" /></p>

<p>
	سوف نستعرض في هذا الدرس بعض الأمثلة على كيفية التعامل مع خادم العميل (Client server).
</p>

<h2>
	مثال Hello TCP Client
</h2>

<p>
	هذا البرنامج مكمّل لبرنامج Hello TCP Server، ويمكنك تشغيل أي منهما للتحقق من صلاحيتهما. انظر شيفرة المثال فيما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_7" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;cstring&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;arpa/inet.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;netdb.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/socket.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/types.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;unistd.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	سنأخذ هنا عنوان IP ورقم بوابة كوسائط لبرنامجنا، نتابع …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_9" style="">
<span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">argc </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Run program as 'program &lt;ipaddress&gt; &lt;port&gt;'\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ipAddress </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">portNum </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
    addrinfo hints</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;</span><span class="pln">
    memset</span><span class="pun">(&amp;</span><span class="pln">hints</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">hints</span><span class="pun">));</span><span class="pln">
    hints</span><span class="pun">.</span><span class="pln">ai_family </span><span class="pun">=</span><span class="pln"> AF_UNSPEC</span><span class="pun">;</span><span class="pln">
    hints</span><span class="pun">.</span><span class="pln">ai_socktype </span><span class="pun">=</span><span class="pln"> SOCK_STREAM</span><span class="pun">;</span><span class="pln">
    hints</span><span class="pun">.</span><span class="pln">ai_flags </span><span class="pun">=</span><span class="pln"> AI_PASSIVE</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> gAddRes </span><span class="pun">=</span><span class="pln"> getaddrinfo</span><span class="pun">(</span><span class="pln">ipAddress</span><span class="pun">,</span><span class="pln"> portNum</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">hints</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">p</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">gAddRes </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> gai_strerror</span><span class="pun">(</span><span class="pln">gAddRes</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"No addresses found\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	ينشئ استدعاء <code>()socket</code> قناة socket جديدة ويعيد الواصف الخاص بها …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_11" style="">
<span class="pln">   </span><span class="typ">int</span><span class="pln"> sockFD </span><span class="pun">=</span><span class="pln"> socket</span><span class="pun">(</span><span class="pln">p</span><span class="pun">-&gt;</span><span class="pln">ai_family</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_socktype</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_protocol</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sockFD </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while creating socket\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">4</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	لاحظ عدم وجود استدعاء <code>()bind</code> كما في برنامج Hello TCP Server، فذلك غير ضروري رغم أنك تستطيع استدعاءها، لأنه لا يلزم العميل أن يكون له رقم منفذ (port number) ثابت، لذا فإن الاستدعاء التالي سيربطه برقم عشوائي. سيحاول استدعاء <code>()connect</code> أن ينشئ اتصال TCP بالخادم المحدد …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_13" style="">
<span class="pln">    </span><span class="typ">int</span><span class="pln"> connectR </span><span class="pun">=</span><span class="pln"> connect</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">,</span><span class="pln"> p </span><span class="pun">-&gt;</span><span class="pln"> ai_addr</span><span class="pun">,</span><span class="pln"> p </span><span class="pun">-&gt;</span><span class="pln"> ai_addrlen</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">connectR </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        close</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">);</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while connecting socket\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string reply</span><span class="pun">(</span><span class="lit">15</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">);</span></pre>

<p>
	هنا يحاول استدعاء <code>()recv</code> الحصول على إجابة من الخادم، لكن الإجابة قد تحتاج إلى عدة استدعاءات لـ <code>()recv</code> قبل أن تُستقبَل بالكامل، سنحاول حل هذا لاحقًا …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_15" style="">
<span class="pln">    </span><span class="kwd">auto</span><span class="pln"> bytes_recv </span><span class="pun">=</span><span class="pln"> recv</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">reply</span><span class="pun">.</span><span class="pln">front</span><span class="pun">(),</span><span class="pln"> reply</span><span class="pun">.</span><span class="pln">size</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">bytes_recv </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while receiving bytes\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">6</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"\nClient recieved: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> reply </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    close</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">);</span><span class="pln">
    freeaddrinfo</span><span class="pun">(</span><span class="pln">p</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	مثال Hello TCP Server
</h2>

<p>
	نقترح أن تلقي نظرة سريعة على كتاب <a data-ss1617024119="1" href="https://beej.us/guide/bgnet/" rel="external nofollow">Beej's Guide to Network Programming</a>، والذي سيفسر لك معظم المفاهيم المُستخدَمة هنا.
</p>

<p>
	سننشئ خادم TCP بسيطًا هنا يعرض الجملة "Hello World" لكل الاستدعاءات الواردة، ثم يُغلقها. كما أنّ الخادم سيتواصل مع العملاء تكراريًا (iteratively)، أي عميلًا واحدًا في كل مرّة. سنشغّل الخادم عبر منفذ محدّد، لذلك سنأخذ رقم المنفذ كوسيط. انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_17" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;cstring&gt;</span><span class="pln">    </span><span class="com">// sizeof()</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string&gt;</span></pre>

<p>
	الترويسات التالية من أجل <code>()getaddrinfo</code> و <code>()socket</code> والدوال الصديقة …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_19" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;arpa/inet.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;netdb.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/socket.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/types.h&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;unistd.h&gt;</span><span class="pln">    </span><span class="com">// close()</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">argv</span><span class="pun">[])</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	هنا نتحقق مما إذا كان رقم المنفذ قد أُعطي أم لا …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_21" style="">
<span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">argc </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Run program as 'program &lt;port&gt;'\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">auto</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">portNum </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span></pre>

<p>
	في السطر التالي، عدد الاتصالات المسموح بها في طابور الوارد …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_23" style="">
<span class="pln">    </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> backLog </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span></pre>

<p>
	نحتاج إلى مؤشرين، <code>res</code> سيأخذ القيمة و<code>p</code> سيكرر عليها …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_25" style="">
<span class="pln">    addrinfo hints</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">res</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">p</span><span class="pun">;</span><span class="pln">
    memset</span><span class="pun">(</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">hints</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">hints</span><span class="pun">));</span><span class="pln">
    </span><span class="com">// راجع الكتاب لمزيد من التوضيحات</span><span class="pln">
    hints</span><span class="pun">.</span><span class="pln">ai_family </span><span class="pun">=</span><span class="pln"> AF_UNSPEC</span><span class="pun">;</span><span class="pln"> </span><span class="com">// التي ستستخدم IP لا تحدّد بعدُ نسخة</span></pre>

<p>
	في السطر التالي، يشير <code>SOCK_STREAM</code> إلى <code>TCP</code> …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_27" style="">
<span class="pln">   hints</span><span class="pun">.</span><span class="pln">ai_socktype </span><span class="pun">=</span><span class="pln"> SOCK_STREAM</span><span class="pun">;</span><span class="pln">
    hints</span><span class="pun">.</span><span class="pln">ai_flags </span><span class="pun">=</span><span class="pln"> AI_PASSIVE</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> gAddRes </span><span class="pun">=</span><span class="pln"> getaddrinfo</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">,</span><span class="pln"> portNum</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">hints</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">res</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">gAddRes </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> gai_strerror</span><span class="pun">(</span><span class="pln">gAddRes</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Detecting addresses"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> numOfAddr </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	يحرص طول ipv6 على إمكانية تخزين عنواني ipv4/6 في هذا المتغير، وبما أن <code>()getaddrinfo</code> قد أعطتنا قائمة من العناوين، فسنكرر عليها ونسأل المستخدم أن يختار أحدها لربطها بالبرنامج …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_29" style="">
<span class="pln">   </span><span class="kwd">char</span><span class="pln"> ipStr</span><span class="pun">[</span><span class="pln">INET6_ADDRSTRLEN</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">=</span><span class="pln"> res</span><span class="pun">;</span><span class="pln"> p </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln"> p </span><span class="pun">=</span><span class="pln"> p </span><span class="pun">-&gt;</span><span class="pln"> ai_next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">addr</span><span class="pun">;</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">string ipVer</span><span class="pun">;</span><span class="pln">
        </span><span class="com">// ipv4 إن كان العنوان من النوع</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">-&gt;</span><span class="pln"> ai_family </span><span class="pun">==</span><span class="pln"> AF_INET</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            ipVer </span><span class="pun">=</span><span class="pln"> </span><span class="str">"IPv4"</span><span class="pun">;</span><span class="pln">
            sockaddr_in </span><span class="pun">*</span><span class="pln">ipv4 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">reinterpret_cast</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> sockaddr_in </span><span class="pun">*&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">-&gt;</span><span class="pln"> ai_addr</span><span class="pun">);</span><span class="pln">
            addr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;(</span><span class="pln">ipv4 </span><span class="pun">-&gt;</span><span class="pln"> sin_addr</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">++</span><span class="pln">numOfAddr</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="com">// ipv6 إن كان العنوان من النوع</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            ipVer </span><span class="pun">=</span><span class="pln"> </span><span class="str">"IPv6"</span><span class="pun">;</span><span class="pln">
            sockaddr_in6 </span><span class="pun">*</span><span class="pln">ipv6 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">reinterpret_cast</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> sockaddr_in6 </span><span class="pun">*</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p </span><span class="pun">-&gt;</span><span class="pln"> ai_addr</span><span class="pun">);</span><span class="pln">
            addr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;(</span><span class="pln">ipv6 </span><span class="pun">-&gt;</span><span class="pln"> sin6_addr</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">++</span><span class="pln">numOfAddr</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="com">// من الشكل الثنائي إلى الشكل النصي IPv4/6 تحويل عناوين</span><span class="pln">
        inet_ntop</span><span class="pun">(</span><span class="pln">p </span><span class="pun">-&gt;</span><span class="pln"> ai_family</span><span class="pun">,</span><span class="pln"> addr</span><span class="pun">,</span><span class="pln"> ipStr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">ipStr</span><span class="pun">));</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"("</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> numOfAddr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">") "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> ipVer </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" : "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> ipStr </span><span class="pun">&lt;&lt;</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// في حال عدم العثور على أيّ عنوان</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">numOfAddr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Found no host address to use\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// اسأل المستخدم أن يختار عنوانا</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Enter the number of host address to bind with: "</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> choice </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">bool</span><span class="pln"> madeChoice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cin </span><span class="pun">&gt;&gt;</span><span class="pln"> choice</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">choice </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">numOfAddr </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> choice </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            madeChoice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Wrong choice, try again!"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln">
            madeChoice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">madeChoice</span><span class="pun">);</span><span class="pln">
    p </span><span class="pun">=</span><span class="pln"> res</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// كواصف socketFD لننشئ قناة جديدة، ستُعاد</span><span class="pln">
    </span><span class="com">//  تعيد هذه الاستدعاءات في العادة القيمة -1 في حال وقوع خطأ ما</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> sockFD </span><span class="pun">=</span><span class="pln"> socket</span><span class="pun">(</span><span class="pln">p</span><span class="pun">-&gt;</span><span class="pln">ai_family</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_socktype</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_protocol</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sockFD </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while creating socket\n"</span><span class="pun">;</span><span class="pln">
        freeaddrinfo</span><span class="pun">(</span><span class="pln">res</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">4</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// لنربط العنوان بالقناة التي أنشأناها للتو</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> bindR </span><span class="pun">=</span><span class="pln"> bind</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_addr</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">-&gt;</span><span class="pln">ai_addrlen</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">bindR </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while binding socket\n"</span><span class="pun">;</span><span class="pln">

        </span><span class="com">// في حال وقوع خطأ، احرص على إغلاق القناة وتحرير الموارد</span><span class="pln">
        close</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">);</span><span class="pln">
        freeaddrinfo</span><span class="pun">(</span><span class="pln">res</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// وأخيرا، ابدأ بالإنصات إلى الاتصالات الواردة عبر قناتنا</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> listenR </span><span class="pun">=</span><span class="pln"> listen</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">,</span><span class="pln"> backLog</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">listenR </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while Listening on socket\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="com">// في حال وقوع خطأ، احرص على إغلاق القناة وتحرير الموارد</span><span class="pln">
        close</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">);</span><span class="pln">
        freeaddrinfo</span><span class="pun">(</span><span class="pln">res</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">6</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// البنية كبيرة كفاية لتحمل عنوان العميل</span><span class="pln">
    sockaddr_storage client_addr</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">socklen_t</span><span class="pln"> client_addr_size </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="pln">client_addr</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string response </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello World"</span><span class="pun">;</span></pre>

<p>
	فيما يلي حلقة لا نهائية للتواصل مع الاتصالات الواردة، وستتعامل معها بالتتابع أي واحدة في كل مرة. كذلك فيما يلي من الأمثلة سنستدعي <code>()fork</code> لكل اتصال مع العملاء …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_31" style="">
<span class="pln">    </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يعيد واصف قناة جديد accept استدعاء</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> newFD
            </span><span class="pun">=</span><span class="pln"> accept</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sockaddr </span><span class="pun">*)</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">client_addr</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">client_addr_size</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newFD </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error while Accepting on socket\n"</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span></pre>

<p>
	فيما يلي، يعيد استدعاء <code>send</code> البيانات التي مرَّرتها أنت كمعامِل ثاني، وطولها كمعامل ثالث، ويعيد عدد البِتَّات المرسلة فعليًا …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_33" style="">
<span class="pln">        </span><span class="kwd">auto</span><span class="pln"> bytes_sent </span><span class="pun">=</span><span class="pln"> send</span><span class="pun">(</span><span class="pln">newFD</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">(),</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">length</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
        close</span><span class="pun">(</span><span class="pln">newFD</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    close</span><span class="pun">(</span><span class="pln">sockFD</span><span class="pun">);</span><span class="pln">
    freeaddrinfo</span><span class="pun">(</span><span class="pln">res</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيعمل البرنامج على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7295_35" style="">
<span class="typ">Detecting</span><span class="pln"> addresses
</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="typ">IPv4</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.0</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln">
</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="typ">IPv6</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="pun">::</span><span class="pln">
</span><span class="typ">Enter</span><span class="pln"> the number of host address to bind with</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617024119="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 127: Client server examples من كتاب <a data-ss1617024119="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1184</guid><pubDate>Fri, 09 Apr 2021 13:02:01 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x648;&#x62F; (Recursion) &#x648;&#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x642;&#x627;&#x628;&#x644;&#x629; &#x644;&#x644;&#x627;&#x633;&#x62A;&#x62F;&#x639;&#x627;&#x621; (Callable Objects) &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF-recursion-%D9%88%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D9%84%D9%84%D8%A7%D8%B3%D8%AA%D8%AF%D8%B9%D8%A7%D8%A1-callable-objects-%D9%81%D9%8A-cpp-r1183/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061d2e11e776_57---(Recursion)----(Callable-Objects)--Cpp.png.ff70e464764b5241fbd4c13154b8e516.png" /></p>

<h2>
	تطبيقات على التعاود
</h2>

<p>
	يمكن استعمال التعاود في الكثير من التطبيقات المفيدة إذ يساعدنا على تبسيط الشيفرة ويعطيها قوة وفعالية على عكس لو لم نعتمد على مفهوم التعاود وإليك بعض هذه التطبيقات مع شيفراتها.
</p>

<h3>
	حساب تسلسلات فيبوناتشي Fibonnaci sequence
</h3>

<p>
	هذه هي الطريقة الأبسط لاستخدام التكرارية للحصول على العنصر رقم N من سلسلة Fibonnaci:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_7" style="">
<span class="typ">int</span><span class="pln"> get_term_fib</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> get_term_fib</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> get_term_fib</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكن المشكلة في هذه الطريقة أنّها غير مناسبة للأعداد الكبيرة، فكلما زادت قيمة <code>‎n‎</code>، زاد عدد استدعاءات الدالة بشكل أسي (exponentially). يمكن الاستعاضة عن هذه الطريقة بالتكرارية المُذيّلة (tail recursion) على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_9" style="">
<span class="typ">int</span><span class="pln"> get_term_fib</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> prev </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> curr </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> prev</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> curr</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> get_term_fib</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> curr</span><span class="pun">,</span><span class="pln"> prev </span><span class="pun">+</span><span class="pln"> curr</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيحسُب كل استدعاء للدالّة الحدّ التالي في تسلسل Fibonnaci، وعليه فإنّ عدد استدعاءات الدالّة يزيد خطيًا مع <code>‎n‎</code>.
</p>

<h3>
	التكرارية مع التذكّر Recursion with memoization
</h3>

<p>
	قد تصبح الدوال التكرارية مكلفة للغاية، ويمكن جعلها أسرع بكثير عن طريق تخزين القيم التي سبق حسابها، إذا كانت دوالًّا خالصة -أي دوالًّا تُعيد دائمًا نفس القيمة عند استدعائها مع نفس الوسائط، ولا تعتمد على الحالة الخارجية ولا تعدّلها-، لكن هذا سيكون على حساب استخدام مزيد من الذاكرة.
</p>

<p>
	يوضّح المثال التالي كيفية حساب تسلسل فيبوناتشي باستخدام التكرارية التذكيرية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_11" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;map&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">map</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> values</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> n</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">map</span><span class="pun">&lt;</span><span class="typ">int</span><span class="pun">,</span><span class="typ">int</span><span class="pun">&gt;::</span><span class="typ">iterator</span><span class="pln"> iter </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">n</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">iter </span><span class="pun">==</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">end</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> values</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> iter</span><span class="pun">-&gt;</span><span class="pln">second</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّه على الرغم من استخدام صيغة التكرارية البسيطة، إلّا أنّ تعقيد الدالّة يساوي O(n)‎ في الاستدعاء الأول، وسيكون التعقيد ثابتًا في الاستدعاءات اللاحقة مع نفس القيمة، أي O(1)‎. واعلم أنّ هذا التنفيذ ليس متعدّد المداخل (reentrant)، كما لا يسمح بالتخلص من القيم المُخزّنة. هناك تقديم بديل يعتمد على السماح بتمرير قاموس كوسيط إضافي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_13" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;map&gt;</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">map</span><span class="pun">&lt;</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">&gt;</span><span class="pln"> values</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n</span><span class="pun">==</span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> n</span><span class="pun">==</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> n</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">map</span><span class="pun">&lt;</span><span class="typ">int</span><span class="pun">,</span><span class="typ">int</span><span class="pun">&gt;::</span><span class="typ">iterator</span><span class="pln"> iter </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">n</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">iter </span><span class="pun">==</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">end</span><span class="pun">())</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> values</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> fibonacci</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> iter</span><span class="pun">-&gt;</span><span class="pln">second</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذا المثال، يُطلَب من المُستدعي أن يحفظ القاموس الذي يحتوي القيم المُخزّنة. ميزة هذا أنّ الدالّة أصبحت الآن متعدّدة المداخل (reentrant)، وأنّه المستدعي يستطيع إزالة القيم التي لم تعد مطلوبة، وهذا يحسّن استخدام الذاكرة. بالمقابل، يعيب هذه الطريقة أنّها تكسر التغليف (breaks encapsulation). إذ يمكن للمُستدعي تغيير الخرج عن طريق ملء القاموس بقيم غير صحيحة.
</p>

<h2>
	الكائنات القابلة للاستدعاء Callable Objects
</h2>

<p>
	الكائنات القابلة للاستدعاء هي هياكل C++‎ يمكن استخدامها كدوال، وتدخل فيها كل الأشياء التي يمكنك تمريرها إلى دالة المكتبة القياسية في C++‎ 17‏ <code>invoke()‎</code> أو التي يمكن استخدامها في مُنشئ <code>std::function</code>، وهذا يشمل: مؤشّرات الدوال، والأصناف التي تحتوي على <code>operator()‎</code>، والأصناف ذات التحويلات الضمنية، ومراجع الدوال، ومؤشّرات الدوال التابعة ومؤشّرات الحقول (Pointers to member data)، وتعابير لامبدا.
</p>

<p>
	تُستخدَم الكائنات القابلة للاستدعاء في العديد من خوارزميات مكتبة القوالب القياسية STL كدوال شرطية (predicate).
</p>

<h3>
	مؤشّرات الدوال Function Pointers
</h3>

<p>
	تعدّ مؤشّرات الدوال الطريقة الأبسط لتمرير الدوال، ويمكن استخدامها أيضًا في لغة C، وإن أردت استخدامها ككائنات قابلة للاستدعاء، فيمكنك تعريف مؤشّر الدالّة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_15" style="">
<span class="kwd">typedef</span><span class="pln"> returnType</span><span class="pun">(*</span><span class="pln">name</span><span class="pun">)(</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln"> </span><span class="com">// All</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> returnType</span><span class="pun">(*)(</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln"> </span><span class="com">// &lt;= C++11</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">add_pointer</span><span class="pun">&lt;</span><span class="pln">returnType</span><span class="pun">(</span><span class="pln">arguments</span><span class="pun">)&gt;::</span><span class="pln">type</span><span class="pun">;</span><span class="pln"> </span><span class="com">// &lt;= C++11</span><span class="pln">
</span><span class="kwd">using</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">add_pointer_t</span><span class="pun">&lt;</span><span class="pln">returnType</span><span class="pun">(</span><span class="pln">arguments</span><span class="pun">)&gt;;</span><span class="pln"> </span><span class="com">// &lt;= C++14</span></pre>

<p>
	يستخدم المثال التالي مؤشّر دالة لكتابة دالة مخصّصة لترتيب المتجهات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_17" style="">
<span class="kwd">using</span><span class="pln"> </span><span class="typ">LessThanFunctionPtr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">add_pointer_t</span><span class="pun">&lt;</span><span class="kwd">bool</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">)&gt;;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> sortVectorInt</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="str">&lt;int&gt;</span><span class="pun">&amp;</span><span class="pln">v</span><span class="pun">,</span><span class="pln"> </span><span class="typ">LessThanFunctionPtr</span><span class="pln"> lessThan</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">lessThan</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">front</span><span class="pun">(),</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">back</span><span class="pun">()))</span><span class="pln"> </span><span class="com">// استدعاء مؤشر الدالة</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">swap</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">front</span><span class="pun">(),</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">back</span><span class="pun">());</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">v</span><span class="pun">,</span><span class="pln"> lessThan</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">bool</span><span class="pln"> lessThanInt</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> lhs</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> rhs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> lhs </span><span class="pun">&lt;</span><span class="pln"> rhs</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
sortVectorInt</span><span class="pun">(</span><span class="pln">vectorOfInt</span><span class="pun">,</span><span class="pln"> lessThanInt</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تمرير المؤشر إلى دالة حرّة</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">GreaterThanInt</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> cmp</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> lhs</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> rhs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> lhs </span><span class="pun">&gt;</span><span class="pln"> rhs</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
sortVectorInt</span><span class="pun">(</span><span class="pln">vectorOfInt</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">GreaterThanInt</span><span class="pun">::</span><span class="pln">cmp</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تمرير المؤشر إلى دالة تابعة ساكن</span></pre>

<p>
	كان بإمكاننا استدعاء مؤشّر الدالّة بدلًا من ذلك، بإحدى الطرق التالية :
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_19" style="">
<span class="pun">(*</span><span class="pln">lessThan</span><span class="pun">)(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">front</span><span class="pun">(),</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">back</span><span class="pun">())</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">invoke</span><span class="pun">(</span><span class="pln">lessThan</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">front</span><span class="pun">(),</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">back</span><span class="pun">())</span><span class="pln"> </span><span class="com">// &lt;= C++17</span></pre>

<h3>
	الأصناف ذات التابع operator()‎‏ (الكائنات الداليّة Functors)
</h3>

<p>
	يمكن استخدام كل الأصناف التي تحمّل <code>‎operator()‎</code> تحميلًا زائدًا ككائنات دوال، ويمكن كتابة هذه الأصناف يدويًا (يشار إليها غالبًا باسم الكائنات الدالية - functors)، أو إنشاؤها تلقائيًا بواسطة المُصرِّف عن طريق كتابة تعابير لامدا (منذ C++‎ 11 وما بعده).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_21" style="">
<span class="kwd">struct</span><span class="pln"> </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> age</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// كائن دالي، يعثر على الشخص باسمه</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">FindPersonByName</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">FindPersonByName</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> _name</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="com">// التابع المزاد تحميله والذي سيُستدعى</span><span class="pln">
    </span><span class="kwd">bool</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">()(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">person</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> person</span><span class="pun">.</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> _name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">string _name</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="typ">Person</span><span class="pun">&gt;</span><span class="pln"> v</span><span class="pun">;</span><span class="pln"> </span><span class="com">// نفترض أنّه يحتوي البيانات</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="typ">Person</span><span class="pun">&gt;::</span><span class="typ">iterator</span><span class="pln"> iFind </span><span class="pun">=</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">find_if</span><span class="pun">(</span><span class="pln">v</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> v</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(),</span><span class="pln"> </span><span class="typ">FindPersonByName</span><span class="pun">(</span><span class="str">"Foobar"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// ...</span></pre>

<p>
	بحكم أنّ للكائنات الدالية هويّتها الخاصة، فلا يمكن وضعها في التعريفات النوعية typedef، ويجب قبولها عبر وسيط القالب. يمكن أن يكون تعريف <code>‎std::find_if‎</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9336_23" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> </span><span class="typ">Iterator</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> </span><span class="typ">Predicate</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
</span><span class="typ">Iterator</span><span class="pln"> find_if</span><span class="pun">(</span><span class="typ">Iterator</span><span class="pln"> begin</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Iterator</span><span class="pln"> end</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Predicate</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">predicate</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Iterator</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> begin</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">!=</span><span class="pln"> end</span><span class="pun">,</span><span class="pln"> </span><span class="pun">++</span><span class="pln">i</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">predicate</span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="pln">i</span><span class="pun">))</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> end</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يمكن استدعاء الدالة الشرطية عبر استدعاء: <code>‎std::invoke(predicate, *i)‎</code>، وذلك منذ الإصدار C++ 17.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617023710="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 124: Recursion in C++‎ والفصل Chapter 126: Callable Objects من كتاب <a data-ss1617023710="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1183</guid><pubDate>Mon, 29 Mar 2021 13:17:25 +0000</pubDate></item><item><title>&#x623;&#x647;&#x645; &#x627;&#x644;&#x633;&#x645;&#x627;&#x62A; (Attributes) &#x648;&#x645;&#x648;&#x627;&#x636;&#x639; &#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644;&#x647;&#x627; &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A3%D9%87%D9%85-%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-attributes-%D9%88%D9%85%D9%88%D8%A7%D8%B6%D8%B9-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D9%87%D8%A7-%D9%81%D9%8A-cpp-r1182/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6061d1c29aa04_56---(Attributes)----Cpp.png.33a33e2e6fc89ec016880763f732259c.png" /></p>

<h2>
	السمة [[fallthrough]]
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	عند إنهاء تعليمة <code>case</code> بكلمة <code>switch</code> ستنفَّذ الشيفرة التي تليها، وإن أردت منع هذا السلوك، فاستخدم تعليمة ‎‎<code>break‎</code>‎. يُسمّى هذا السلوك بـ "السقطة" (fallthrough)، وقد ينجم عنه أخطاء غير مقصودة، لذا تطلق العديد من المُصرِّفات والمحلّلات الساكنة (static analyzers) تحذيرًا منه.
</p>

<p>
	وقد أُدخلت سمة قياسية (standard attribute) في C++ 17 من أجل الإشارة إلى أنّ التحذير ليس ضرويًا إذا كانت السقطة مقصودة في الشيفرة، ويمكن للمصرّفات إعطاء تحذيرات بأمان إذا أنهيت <code>case</code> بالكلمةُ المفتاحيةُ <code>‎break‎</code> أو <code>‎[[fallthrough]]‎</code>، وكانت مؤلفة من تعليمة واحد على الأقل.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_7" style="">
<span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">input</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2011</span><span class="pun">:</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2014</span><span class="pun">:</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2017</span><span class="pun">:</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Using modern C++"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">[[</span><span class="pln">fallthrough</span><span class="pun">]];</span><span class="pln"> </span><span class="com">// &gt; لا يوجد تحذير</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1998</span><span class="pun">:</span><span class="pln">
</span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2003</span><span class="pun">:</span><span class="pln">
    standard </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	راجع <a data-ss1617023403="1" href="https://isocpp.org/files/papers/P0188R0.pdf" rel="external nofollow">هذا الاقتراح</a> لمزيد من الأمثلة حول كيفية استخدام <code>‎[[fallthrough]]‎</code>.
</p>

<h2>
	السمة [[nodiscard]]
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	يمكن استخدام السمة <code>‎[[nodiscard]]‎</code> للإشارة إلى أنّه لا ينبغي تجاهل القيمة المُعادة من دالة عند استدعاء دالة، ويجب على االمُصرِّف في حال تجاهلها إعطاء تحذير بهذا الشأن. يمكن إضافة هذه السمة إلى:
</p>

<ul>
<li>
		تعريف دالّة
	</li>
	<li>
		نوع
	</li>
</ul>
<p>
	ينتج عن إضافة السمة إلى نوع ما نفسُ السلوكِ الناتج عن إضافة السمة إلى كل الدوالّ التي تُعيد ذلك النوع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_9" style="">
<span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">typename</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">[[</span><span class="pln">nodiscard</span><span class="pun">]]</span><span class="pln"> </span><span class="typ">Finally</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="typ">decay_t</span><span class="pun">&lt;</span><span class="typ">Function</span><span class="pun">&gt;&gt;</span><span class="pln"> onExit</span><span class="pun">(</span><span class="typ">Function</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">f</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> f</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    assert</span><span class="pun">(</span><span class="pln">i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">            </span><span class="com">// لجعل التعليقات واضحة</span><span class="pln">
    </span><span class="pun">++</span><span class="pln">i</span><span class="pun">;</span><span class="pln">                    </span><span class="com">// i == 1</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> exit1 </span><span class="pun">=</span><span class="pln"> onExit</span><span class="pun">([&amp;</span><span class="pln">i</span><span class="pun">]{</span><span class="pln"> </span><span class="pun">--</span><span class="pln">i</span><span class="pun">;</span><span class="pln"> </span><span class="pun">});</span><span class="pln">    </span><span class="com">//  f() التخفيض بـ 1 عند الخروج من</span><span class="pln">
    </span><span class="pun">++</span><span class="pln">i</span><span class="pun">;</span><span class="pln">                    </span><span class="com">// i == 2</span><span class="pln">
    onExit</span><span class="pun">([&amp;</span><span class="pln">i</span><span class="pun">]{</span><span class="pln"> </span><span class="pun">--</span><span class="pln">i</span><span class="pun">;</span><span class="pln"> </span><span class="pun">});</span><span class="pln">            </span><span class="com">// خطأ: محاولة التخفيض بـ 1 مباشرة</span><span class="pln">
</span><span class="com">// يُتوقع أن يصدر المصرف تنبيها</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> i </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">end</span><span class="pun">;</span><span class="pln">    </span><span class="com">// القيمة المتوقعة 2، لكن القيمة الحقيقية هي 1</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	راجع <a data-ss1617023403="1" href="www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0068r0.pdf" rel="">هذا الاقتراح</a> لمزيد من الأمثلة حول كيفية استخدام <code>‎[[nodiscard]]‎</code>.
</p>

<h2>
	السمة [[deprecated]] والسمة [[deprecated("reason")‎]]
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	قدَّمت C++‎ 14 طريقة قياسية لإهمال (deprecating) الدوال عبر السمات. يمكن استخدام <code>‎[[deprecated]]‎</code> للإشارة إلى إهمال الدالّة. ويسمح التعبير <code>‎[[deprecated("reason")]]‎</code>بتحديد سبب للإهمال يمكن أن نجعل المُصرِّف يظهِره.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_11" style="">
<span class="kwd">void</span><span class="pln"> function</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">unique_ptr</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">a</span><span class="pun">);</span><span class="pln">
</span><span class="com">// توفير رسالة محدّدة تساعد المبرمجين الآخرين على تصحيح شيفراتهم</span><span class="pln">
</span><span class="pun">[[</span><span class="pln">deprecated</span><span class="pun">(</span><span class="str">"Use the variant with unique_ptr instead, this function will be removed in the next release"</span><span class="pun">)]]</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> function</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">auto_ptr</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
</span><span class="com">// في حال عدم وجود رسالة، سيُطلق تنبيه عام عند الاستدعاء</span><span class="pln">
</span><span class="pun">[[</span><span class="pln">deprecated</span><span class="pun">]]</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> function</span><span class="pun">(</span><span class="pln">A </span><span class="pun">*</span><span class="pln">a</span><span class="pun">);</span></pre>

<p>
	يُمكن تطبيق هذه السمة على:
</p>

<ul>
<li>
		تصريح صنف.
	</li>
	<li>
		اسم typedef.
	</li>
	<li>
		متغيّر.
	</li>
	<li>
		حقل غير ساكن (non-static data member).
	</li>
	<li>
		دالة.
	</li>
	<li>
		تعداد (enum).
	</li>
	<li>
		تخصيص قالب (template specialization).
	</li>
</ul>
<p>
	(المرجع: <a data-ss1617023403="1" href="www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf" rel="">c++14 standard draft: 7.6.5 Deprecated attribute</a>)
</p>

<h2>
	السمة [[maybe_unused]]
</h2>

<p>
	تُستعمل السمة <code>‎[[maybe_unused]]‎</code> للدلالة على احتمال عدم استخدام منطق بعينه داخل الشيفرة، وترتبط غالبًا بشروط المعالج الأَولي (preprocessor). ويمكن استخدام هذه السمة لمنع هذا السلوك من خلال الإشارة إلى أنّ هذا الأمر مقصود، نظرًا لأنّ المُصرِّفات قد تبعث تحذيرات بشأن المتغيّرات غير المستخدمة.
</p>

<p>
	وتُعد القيم المعادة التي تشير إلى نجاح عملية التنقيح من الأمثلة التطبيقية للمتغيّرات التي تُستخدم أثناء عمليات تنقيح بنيات الشيفرة (debug builds)، ولكن لا تبقى إليها حاجة في مرحلة الإنتاج. ويجب التحقق من شرط النجاح أثناء تنقيح أخطاء البُنيات، رغم أن تلك التحققات ستُحذف في مرحلة الإنتاج لعدم الحاجة إليها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_13" style="">
<span class="pun">[[</span><span class="pln">maybe_unused</span><span class="pun">]]</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> mapInsertResult </span><span class="pun">=</span><span class="pln"> configuration</span><span class="pun">.</span><span class="pln">emplace</span><span class="pun">(</span><span class="str">"LicenseInfo"</span><span class="pun">,</span><span class="pln">
stringifiedLicenseInfo</span><span class="pun">);</span><span class="pln">
assert</span><span class="pun">(</span><span class="pln">mapInsertResult</span><span class="pun">.</span><span class="pln">second</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لا تُستدعى إلا خلال الإطلاق، لذا لا ينبغي أن تكون في القاموس</span></pre>

<p>
	هناك مثال أكثر تعقيدًا، وهي الدوال المساعدة التي توضع في فضاء اسم غير مُسمّى (unnamed namespace)، وتلك الدوال إن لم تُستخدَم أثناء التصريف، فقد يطلق االمُصرِّف تحذيرًا بهذا الشأن، لذا قد ترغب في حمايتها باستخدام نفس وسوم المعالج الأولي التي تُستخدَم مع المُستدعي (caller)، لكن قد يكون هذا معقدًا، لهذا يُعدُّ استخدام السمة <code>‎[[maybe_unused]]‎</code> بديلاً أسهل في الصيانة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_15" style="">
<span class="kwd">namespace</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">[[</span><span class="pln">maybe_unused</span><span class="pun">]]</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string createWindowsConfigFilePath</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">relativePath</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// TODO: …  BSD, MAC أعد استخدام هذا على</span><span class="pln">
    </span><span class="pun">[[</span><span class="pln">maybe_unused</span><span class="pun">]]</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string createLinuxConfigFilePath</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">relativePath</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">string createConfigFilePath</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&amp;</span><span class="pln">relativePath</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="com">#if OS == "WINDOWS"</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> createWindowsConfigFilePath</span><span class="pun">(</span><span class="pln">relativePath</span><span class="pun">);</span><span class="pln">
</span><span class="com">#elif</span><span class="pln"> OS </span><span class="pun">==</span><span class="pln"> </span><span class="str">"LINUX"</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> createLinuxConfigFilePath</span><span class="pun">(</span><span class="pln">relativePath</span><span class="pun">);</span><span class="pln">
</span><span class="com">#else</span><span class="pln">
</span><span class="com">#error</span><span class="pln"> </span><span class="str">"OS is not yet supported"</span><span class="pln">
</span><span class="com">#endif</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	راجع <a data-ss1617023403="1" href="https://isocpp.org/files/papers/P0212R0.pdf" rel="external nofollow">هذا الاقتراح</a> لمزيد من الأمثلة حول كيفية استخدام <code>‎[[maybe_unused]]‎</code>.
</p>

<h2>
	السمة [[noreturn]]
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	قدّمت C++‎ 11 سمةَ <code>‎[[noreturn]]‎</code> التي يمكن استخدامها مع دالّة للإشارة إلى أنّ تلك الدالّة لا تُعاد إلى المُستدعي، سواء عبر تعليمة <code>return</code>، أو عند الوصول إلى نهاية متنها -من المهم الإشارة إلى أن هذا لا ينطبق على الدوال الفارغة <code>‎void‎</code>، نظرًا لأنّها تعود إلى المُستدعي، ولكن لا تعيد أيّ قيمة-. قد تنتهي مثل هذه الدوالّ عبر استدعاء <code>‎std::terminate‎</code> أو <code>‎std::exit‎</code>، أو عبر رفع اعتراض (throwing an exception). تجدر الإشارة أيضًا إلى أنّ مثل هذه الدوالّ يمكن أن تُعاد عبر تنفيذ <code>‎longjmp‎</code>.
</p>

<p>
	على سبيل المثال، الدالّة أدناه إمّا أن ترفع اعتراضًا أو تستدعي <code>‎std::terminate‎</code>، لذلك فهي مرشح جيد لاستخدام <code>‎[[noreturn]]‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_17" style="">
<span class="pun">[[</span><span class="pln">noreturn</span><span class="pun">]]</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> ownAssertFailureHandler</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cerr </span><span class="pun">&lt;&lt;</span><span class="pln"> message </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">THROW_EXCEPTION_ON_ASSERT</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="typ">AssertException</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">move</span><span class="pun">(</span><span class="pln">message</span><span class="pun">));</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">terminate</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يسمح هذا السلوك للمُصرِّف بإنهاء الدوال التي لا تحتوي على تعليمة <code>return</code> إذا كان يعلم أنّ الشيفرة لن تُنفَّذ. هنا، لن يحتاج المُصرِّف إلى إضافة الشيفرة الموجودة أسفل ذلك الاستدعاء نظرًا لأنّ استدعاء <code>‎ownAssertFailureHandler‎</code> في الشيفرة أدناه لن يعود أبدًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_19" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> createSequence</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">end </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> sequence</span><span class="pun">;</span><span class="pln">
        sequence</span><span class="pun">.</span><span class="pln">reserve</span><span class="pun">(</span><span class="pln">end </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">i</span><span class="pun">)</span><span class="pln">
            sequence</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> sequence</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    ownAssertFailureHandler</span><span class="pun">(</span><span class="str">"Negative number passed to createSequence()"</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// return std::vector&lt;int&gt;{}; //&lt; Not needed because of [[noreturn]]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيكون السلوك غير معرَّف إذا كانت الدالّة ستعاد، ونتيجة لذلك لا يُسمح بما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_21" style="">
<span class="pun">[[</span><span class="pln">noreturn</span><span class="pun">]]</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> assertPositive</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">number </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln">
        ownAssertFailureHandler</span><span class="pun">(</span><span class="str">"Positive number expected"</span><span class="pln">s</span><span class="pun">);</span><span class="pln"> </span><span class="com">//&lt; [[noreturn]]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّ <code>‎[[noreturn]]‎</code> تُستخدم في الغالب في الدوال الفارغة، لكنها ليست ضرورية، وهذا يسمح باستخدام الدوال في البرمجة العامة (generic programming):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1920_23" style="">
<span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">InconsistencyHandler</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> fortyTwoDivideBy</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
        i </span><span class="pun">=</span><span class="pln"> </span><span class="typ">InconsistencyHandler</span><span class="pun">::</span><span class="pln">correct</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">42.</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">InconsistencyThrower</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="pun">[[</span><span class="pln">noreturn</span><span class="pun">]]</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> correct</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> ownAssertFailureHandler</span><span class="pun">(</span><span class="str">"Unknown inconsistency"</span><span class="pln">s</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">InconsistencyChangeToOne</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> correct</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> fortyTwo </span><span class="pun">=</span><span class="pln"> fortyTwoDivideBy</span><span class="pun">&lt;</span><span class="typ">InconsistencyChangeToOne</span><span class="pun">&gt;(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> unreachable </span><span class="pun">=</span><span class="pln"> fortyTwoDivideBy</span><span class="pun">&lt;</span><span class="typ">InconsistencyThrower</span><span class="pun">&gt;(</span><span class="lit">0</span><span class="pun">);</span></pre>

<p>
	تحتوي دوال المكتبة القياسية التالية على هذه السمات:
</p>

<ul>
<li>
		<code>std::abort</code>
	</li>
	<li>
		<code>std::exit</code>
	</li>
	<li>
		<code>std::quick_exit</code>
	</li>
	<li>
		<code>std::unexpected</code>
	</li>
	<li>
		<code>std::terminate</code>
	</li>
	<li>
		<code>std::rethrow_exception</code>
	</li>
	<li>
		<code>std::throw_with_nested</code>
	</li>
	<li>
		<code>std::nested_exception::rethrow_nested</code>
	</li>
</ul>
<p>
	هذا الدرس جزء من <a data-ss1617023403="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 123: Attributes من كتاب <a data-ss1617023403="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1182</guid><pubDate>Mon, 29 Mar 2021 13:11:50 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x628;&#x64A;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x62B;&#x627;&#x628;&#x62A;&#x629; constexpr &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D8%AA%D8%B9%D8%A8%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D8%A7%D8%A8%D8%AA%D8%A9-constexpr-%D9%81%D9%8A-cpp-r1030/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/55.png.2d2a548e4377a46f3594b48d925f6e87.png" /></p>

<p>
	<code>‎constexpr‎</code> هي كلمة مفتاحية يمكن استخدامها مع متغيّر لجعل قيمته تعبيرًا ثابتًا (constant expression)، أو دالةً لأجل استخدامها في التعبيرات الثابتة، أو (منذ C++‎ 17) تعليمة <code>if</code> حتى يُصرَّف فرع واحد فقط من فروعها.
</p>

<h2>
	المصادقة عبر الدالة static_assert
</h2>

<p>
	تقتضي المصادقات (Assertations) وجوب التحقق من شرط معيّن، وإطلاق خطأ إن كانت خطأً (false)، ويحدث هذا في وقت التصريف بالنسبة لـ <code>‎static_assert()‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_7" style="">
<span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">typename</span><span class="pln"> T</span><span class="pun">&gt;</span><span class="pln">
T mul10</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> T t</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static_assert</span><span class="pun">(</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">is_integral</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;::</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> </span><span class="str">"mul10() only works for integral types"</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">t </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">t </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	المعاملات التي تقبلها الدالة <code>‎static_assert()‎</code>:
</p>

<table>
<thead><tr>
<th>
				المعامِل
			</th>
			<th>
				التفاصيل
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>bool_constexpr</code>
			</td>
			<td>
				التعبير المراد التحقق منه
			</td>
		</tr>
<tr>
<td>
				<code>message</code>
			</td>
			<td>
				الرسالة المُراد طباعتها عندما تساوي <code>bool_constexpr</code> القيمة <code>false</code>
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	المعامل الأوّل إلزامي ويمثّل الشرط، وهو تعبير منطقي ثابت <code>constexpr</code>. كذلك يمكن أن تقبل الدالة أيضًا مُعاملًا ثانيًا، والذي يمثّل الرسالة، وهي سلسلة نصية مجردة. صار المعامِل الثاني اختياريًا ابتداءً من C++‎ 17، أما قبل ذلك كان إلزاميًا.
</p>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_9" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> T </span><span class="pun">&gt;</span><span class="pln">
    T mul10</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> T t</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">static_assert</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">is_integral </span><span class="pun">&lt;</span><span class="pln"> T </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">::</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">t </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">t </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	تُستخدم المصادقات في حال:
</p>

<ul>
<li>
		لزِم التحقق في وقت التصريف من نوع معيّن في تعبير ثابت constexpr.
	</li>
	<li>
		احتاجت دالّة القالب إلى التحقق من خاصّيات معيّنة من النوع المُمرّر إليها.
	</li>
	<li>
		إذا أردت كتابة حالات اختبارية لما يلي:
	</li>
	<li>
		الدوال الوصفية للقوالب template metafunctions
	</li>
	<li>
		دوال التعبيرات الثابتة constexpr functions
	</li>
	<li>
		شيفرة وصفية جامعة macro metaprogramming
	</li>
	<li>
		إن كانت بعض التعريفات مطلوبة (على سبيل المثال، إصدار C++‎)
	</li>
	<li>
		نقل الشيفرات القديمة (Porting legacy code)، والمصادقة (assertation) على <code>‎sizeof(T)‎</code> (على سبيل المثال، ‎32-bit int)
	</li>
	<li>
		إن كانت بعض ميزات المصرّف مطلوبة لعمل البرنامج (التحزيم - packing - أو تحسين الأصناف الأساسية الفارغة، وما إلى ذلك)
	</li>
</ul>
<p>
	لاحظ أنّ <code>‎static_assert()‎</code> لا تشارك في قاعدة SFINAE: إذا كانت التحميلات الزائدة/ التخصيصات الإضافية ممكنة، فلا ينبغي استخدامها بدلًا من تقنيات قوالب البرمجة الوصفية - template metaprogramming - (مثل <code>‎std::enable_if&lt;&gt;‎</code>)، وقد تُستخدَم -مع لزوم التحقق منها- في شيفرة القالب في حال إيجاد ([التحميل الزائد](رابط الفصل 35) / التخصيص) المتوقع، وفي مثل هذه الحالات قد تُوفِّر رسالة خطأ أو أكثر تكون أوضح مما لو كنا اعتمدنا على قاعدة SFINAE.
</p>

<h2>
	متغيّرات التعبيرات الثابتة (constexpr variables)
</h2>

<p>
	إن صُرِّخ عن متغيّر بالكلمة المفتاحية <code>‎constexpr‎</code>، فسيكون ثابتًا (<code>‎const‎</code>) ضمنيًا، وسيكون من الممكن استخدام قيمته كتعبير ثابت.
</p>

<h3>
	المقارنة مع <code>define</code>
</h3>

<p>
	يمكن استخدام <code>‎constexpr‎</code> كبديل آمن نوعيًا (type-safe) للتعبيرات التي تعتمد على <code>‎#define‎</code> في وقت التصريف، وعند استخدام <code>‎constexpr‎</code>، سيُستبدَل التعبير المُقيَّم في وقت التصريف بنتيجته. انظر المثال التالي:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_11" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> N </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> N</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سينتج عن المثال أعلاه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_13" style="">
<span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span></pre>

<p>
	ستختلف الشيفرة الجامعة التي تعتمد على المعالج الأولى في وقت التصريف (pre-processor based compile-time macro)، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_15" style="">
<span class="com">#define</span><span class="pln"> N </span><span class="lit">10</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> N</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا سيُنتِج الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_17" style="">
<span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span></pre>

<p>
	والذي سيُحوَّل إلى <code>cout &lt;&lt; 10 + 2</code> لكن سيكون على المُصرِّف أن يقوم بالمزيد من العمل، كما قد تحدث مشكلة في حال لم تستخدم بشكل صحيح.
</p>

<p>
	على سبيل المثال (مع <code>‎#define‎</code>):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_21" style="">
<span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> N </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span></pre>

<p>
	سينتج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_19" style="">
<span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> </span><span class="com">// 14</span></pre>

<p>
	سيعيد التقييم الأولي (pre-evaluated) القيمة <code>‎24‎</code> للتعبير الثابت <code>‎constexpr‎</code>، كما هو مُتوقّع.
</p>

<h3>
	مقارنة مع const
</h3>

<p>
	تحتاج المتغيرات الثابتة (<code>‎const‎</code>) إلى ذاكرة لتخزينها، وذلك على خلاف التعبيرات الثابتة <code>‎constexpr‎</code>، وتنتج التعبيرات الثابتة <code>‎constexpr‎</code> قيمًا ثابتة في وقت التصريف وغير قابلة للتغيير. قد يقال أيضًا أنّ القيمة الثابتة (<code>‎const‎</code>) هي أيضًا غير قابلة للتغيير، لكن انظر المثال التالي لتوضيح الفرق بينهما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_23" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size2 </span><span class="pun">=</span><span class="pln"> abs</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> arr_one</span><span class="pun">[</span><span class="pln">size1</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> arr_two</span><span class="pun">[</span><span class="pln">size2</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستفشل التعليمة الثانية في معظم المُصرِّفات -رغم أنها قد تعمل في GCC-، إذ يجب أن يكون حجم أيّ مصفوفة تعبيرًا ثابتًا (أي ينتُج عنه قيمة في وقت التصريف). وكما ترى في الشيفرة أعلاه، فقد أُسنِد إلى المتغيّر الثاني <code>‎size2‎</code> قيمة ستُحدَّد في وقت التشغيل (runtime) رغم أنّها تساوي <code>‎10‎</code>، إلا أنّ المُصرِّف لا يعدُّها قيمة تصريفية (تصريفية، من وقت التصريف، compile-time).
</p>

<p>
	هذا يعني أنّ <code>‎const‎</code> قد تكون أو لا تكون ثابتة تصريفية حقيقية، ولا تستطيع أن تضمن لقيمة ثابتة <code>‎const‎</code> معيّنة أن تكون تصريفيةً، ولك أن تستخدم <code>‎#define‎</code> رغم أنها لا تخلو من بعض المشاكل. وعليه، يمكنك استخدام الحلّ التالي:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_25" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">size</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب تقييم التعابير الثابتة <code>‎constexpr‎</code> إلى قيم تصريفية، لذا لا يمكن استخدام ما يلي …
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_27" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size </span><span class="pun">=</span><span class="pln"> abs</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	… ما لم تكن الدالة (<code>‎abs‎</code>) نفسها تعيد تعبيرًا ثابتًا. يجوز تهيئة جميع الأنواع الأساسية باستخدام <code>‎constexpr‎</code>.
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_29" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="typ">FailFatal</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> PI </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.14</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> site </span><span class="pun">=</span><span class="pln"> </span><span class="str">"StackOverflow"</span><span class="pun">;</span></pre>

<p>
	يمكنك أيضًا استخدام <code>‎auto‎</code> كما في المثال التالي:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_31" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> domain </span><span class="pun">=</span><span class="pln"> </span><span class="str">".COM"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// const char * const domain = ".COM"</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> PI </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.14</span><span class="pun">;</span><span class="pln"> </span><span class="com">// constexpr double</span></pre>

<h2>
	تعليمة if الساكنة
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	يمكن استخدام عبارة <code>‎if constexpr‎</code> للتحكّم في تصريف الشيفرة، لكن يجب أن يكون الشرط تعبيرًا ثابتًا. ستُتجَاهل الفروع غير المُختارة، ولن تُستنسَخ التعليمات التي تمّ تجاهلها داخل القالب. مثلًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_33" style="">
<span class="kwd">template</span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pln"> T</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="typ">Rest</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> g</span><span class="pun">(</span><span class="pln">T </span><span class="pun">&amp;&amp;</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rest</span><span class="pln"> </span><span class="pun">&amp;&amp;...</span><span class="pln">rs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
</span><span class="com">// ... p معالجة</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">sizeof</span><span class="pun">...(</span><span class="pln">rs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
g</span><span class="pun">(</span><span class="pln">rs</span><span class="pun">...);</span><span class="pln"> </span><span class="com">// لا تقم بالتهيئة باستخدام قائمة وسائط فارغة</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا يلزم تعريف المتغيّرات والدوال التي استُخدَمت قيمتها (odr-used) حصرًا داخل العبارات المُتجاهلَة (discarded statements)، كما لا تُستخدَم عبارات <code>‎return‎</code> المُتجاهلة في استنتاج النوع المعاد من الدالّة.
</p>

<p>
	وتختلف العبارة <code>‎if‎ ‎constexpr‎</code> عن شيفرات التصريف الشرطية <code>‎</code>#ifdef. #ifdef، إذ تعتمد حصرًا على الشروط التي يمكن تقييمها في وقت المعالجة الأولية. فلا يمكن استخدام <code>‎#ifdef‎</code> للتحكم في تصريف الشيفرة بناءً على قيمة مُعامل القالب، لكن من ناحية أخرى، لا يمكن استخدام <code>‎if constexpr‎</code> لتجاهل الشيفرات ذات الصياغة غير الصحيحة، وذلك على خلاف <code>‎#ifdef‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_35" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="kwd">constexpr</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foobar</span><span class="pun">;</span><span class="pln"> </span><span class="com">// error; foobar has not been declared</span><span class="pln">
    std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> v</span><span class="pun">(</span><span class="str">"hello, world"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// error; no matching constructor</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	دوال التعبيرات الثابتة (constexpr functions)
</h2>

<p>
	ستكون الدوالّ المُصرَّح عنها بالكلمة المفتاحية <code>‎constexpr‎</code> مُضمّنة (inline) ضمنيًا، وسينتج عن استدعائها تعابير ثابتة، فسيعاد تعبير ثابت إذا كانت الوسائط المُمرّرة إلى الدالة التالية تعابير ثابتة أيضًا:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_37" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن استخدام نتيجة استدعاء الدالّة كمصفوفة مربوطة (array bound) أو وسيط قالب، كما يمكن استخدامها لتهيئة متغيّر تعبير ثابت (constexpr variable):
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_39" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> S </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Array</span><span class="pun">[</span><span class="pln">S</span><span class="pun">];</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Array2</span><span class="pun">[</span><span class="typ">Sum</span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">)];</span><span class="pln"> </span><span class="com">// مصفوفة مؤلفة من 50 عنصرا في وقت التصريف</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّك إذا أزلت الكلمة المفتاحية <code>‎constexpr‎</code> من تعريف النوع المُعاد الخاص بالدالّة، فلن يعمل الإسناد إلى <code>‎S‎</code>، لأنّ <code>‎S‎</code> متغيّر تعبير ثابت ويجب أن تُسند إليه قيمة تصريفية. وبالمثل، لن يكون حجم المصفوفة تعبيرًا ثابتًا إذا لم تكن <code>‎Sum‎</code> دالةَ تعبير ثابت. كذلك تستطيع استخدام دوال التعبيرات الثابتة (‎<code>‎constexpr‎</code> functions) كما لو كانت دوال عادية:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_41" style="">
<span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> abs</span><span class="pun">(-</span><span class="lit">20</span><span class="pun">));</span></pre>

<p>
	لن تكون <code>‎Sum‎</code> دالّة تعبير ثابت الآن، وستُصرَّف كدالة عادية، وستأخذ وسائط متغيّرة (غير ثابتة)، وتعيد قيمة غير ثابتة، لذا لا تحتاج إلى كتابة دالّتين. هذا يعني أيضًا أنّه إذا حاولت إسناد هذا الاستدعاء إلى متغيّر غير ثابت، فلن ينجح التصريف:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_43" style="">
<span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">auto</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> abs</span><span class="pun">(-</span><span class="lit">20</span><span class="pun">));</span></pre>

<p>
	وذلك لأنه لا ينبغي أن يُسنَد إلى تعبير ثابت إلّا ثابتة تصريفية (compile-time constant). بالمقابل فإن استدعاء الدالة أعلاه يجعل <code>‎Sum‎</code> تعبيرًا غير ثابت (القيمة اليمينية غير ثابتة، على خلاف القيمة اليسارية التي صُرَّح عنها كتعبير ثابت).
</p>

<p>
	<strong>يجب</strong> أيضًا أن تُعيد دالة التعبير الثابت ثابتةً تصريفية (compile-time constant). في المثال التالي، لن تُصرَّف الشيفرة:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_45" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a1 </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لأنّ <code>‎a1‎</code> متغيّر غير ثابت، ويمنع الدالّة من أن تكون دالة تعبير ثابت <code>‎constexpr‎</code> حقيقية، ولن تنجح محاولة جعلها تعبيرًا ثابتًا وإسناد قيمة <code>a</code> لها - نظرًا لأنّ قيمة <code>a</code> (المُعامل الوارد - incoming parameter) ما تزال غير معروفة بعد:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_47" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> a1 </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ</span><span class="pln">
        </span><span class="pun">..</span></pre>

<p>
	وكذلك لن تُصرَّف الشيفرة التالية:
</p>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_49" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln"> </span><span class="com">// abs(a) + abs(b) أو</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبما أن <code>‎abs(a)‎</code> ليست تعبيرًا ثابتًا -ولن تعمل <code>‎abs(10)‎</code>، إذ لن تعيد <code>‎abs‎</code> قيمة من النوع <code>‎constexpr int‎</code>- فماذا عن الشيفرة التالية؟: <strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8298_51" style="">
<span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Abs</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> v </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> v </span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="pln">v</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> a</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Abs</span><span class="pun">(</span><span class="pln">a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لقد صمّمنا الدالّة <code>‎Abs‎</code> وجعلناها دالة تعبير ثابت، كما أنّ جسم <code>‎Abs‎</code> لن يخرق أيّ قاعدة. كذلك تعطي نتيجة تقييم التعبير هي تعبير ثابت <code>‎constexpr‎</code>، وذلك في موضع الاستدعاء (داخل <code>‎Sum‎</code>). ومن ثم يكون استدعاء <code>Sum(-10, 20)‎‎</code> تعبيرًا ثابتًا في وقت التصريف (compile-time constexpr) ينتج عنه القيمة <code>‎30‎</code>.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617022782="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصلين Chapter 118: static_assert و Chapter 119: constexpr من كتاب <a data-ss1617022782="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1030</guid><pubDate>Tue, 03 Nov 2020 13:07:00 +0000</pubDate></item><item><title>&#x646;&#x645;&#x627;&#x630;&#x62C; &#x627;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; &#x641;&#x64A; C++11 &#x648;&#x625;&#x62F;&#x627;&#x631;&#x62A;&#x647;&#x627;</title><link>https://academy.hsoub.com/programming/cpp/%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%81%D9%8A-c11-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%AA%D9%87%D8%A7-r1029/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/54.png.8de56dbdec4dc46c501f0f8d08cb4474.png" /></p>

<h2>
	نماذج الذاكرة
</h2>

<p>
	إن حاوَلَت عدّة خيوط الوصول إلى نفس الموضع من الذاكرة، فستدخل في تسابق على البيانات (data race) إذا تسبب عملية واحدة على الأقل من العمليات المُنفّذة في تعديل البيانات -تُعرف باسم عمليات التخزين store operation-، وتتسبّب سباقات البيانات تلك في سلوك غير معرَّف. ولكي نتجنبها، امنع الخيوط من تنفيذ عمليات متضاربة (Conflicting) بشكل متزامن.
</p>

<p>
	يمكن استخدام أساسيات التزامن (مثل كائنات التزامن - mutex - وما شابه) لتأمين عمليات الوصول المتضاربة، وقد قدّم الإصدار C++‎ 11 نموذج ذاكرة (Memory Model) جديد، هذا النموذج قدّم طريقتين محمولتين جديدتين لمزامنة الوصول إلى الذاكرة في بيئة متعددة الخيوط، وهما: العمليات الذرية (atomic operations) والأسوار (fences).
</p>

<h3>
	العمليات الذرية
</h3>

<p>
	أصبح من الممكن الآن القراءة من موضع معيّن من الذاكرة أو الكتابة فيها باستخدام التحميل الذري (atomic load) وعمليات التخزين الذرية (atomic store)، والتي تُغلَّف في صنف القالب <code>‎std::atomic&lt;t&gt;‎</code> من باب التيسير، ويغلّف هذا الصنف قيمةً من النوع <code>‎t‎</code>، لكن تحميلها وتخزينها إلى الكائن يكون ذريًّا.
</p>

<p>
	وهذا القالب ليس متاحًا لجميع الأنواع بل هو متعلق بالتنفيذ، لكن في العادةً يكون متاحًا لمعظم (أو جميع) الأنواع العددية الصحيحة وأنواع المؤشّرات بحيث تكون الأنواع ‎<code>std::atomic&lt;unsigned&gt;‎</code>‎ و <code>std::atomic&lt;std::vector&lt;foo&gt; *&gt;‎</code> متاحة، على خلاف ‎‎<code>std::atomic&lt;std::pair&lt;bool,char&gt;‎&gt;‎‎</code>‎.
</p>

<p>
	تتميّز العمليات الذرية بالخاصّيات التالية:
</p>

<ul>
<li>
		<p>
			يمكن إجراء جميع العمليات الذرية بشكل متزامن في عدّة خيوط دون الخوف من التسبب في سلوك غير معرَّف.
		</p>
	</li>
	<li>
		<p>
			سيرى التحميل الذري (atomic load) القيمة الأولية التي بُنِي الكائن الذري عليها، أو القيمة المكتوبة فيه عبر عملية التخزين الذرية.
		</p>
	</li>
	<li>
		<p>
			تُرتَّب عمليات التخزين الذرية (Atomic stores) في كائن ذري معيّن بنفس الطريقة في جميع الخيوط، وإذا رأى خيطٌ قيمةَ عملية تخزين ذرية ما من قبل، فإنّ عمليات التحميل الذري اللاحقة سترى إما القيمة نفسها أو القيمة المُخزّنة بواسطة عملية التخزين الذرية اللاحقة.
		</p>
	</li>
	<li>
		<p>
			تسمح عمليات القراءة-التعديل-الكتابة (read-modify-write) الذرية بالتحميل الذري والتخزين الذري دون حدوث أي تخزين ذرّي آخر بينهما. على سبيل المثال، يمكن للمرء أن يزيد العدّادَ ذريًّا (atomically increment) عبر عدة خيوط تلقائيًا، ولن تُفقد أيّ زيادة حتى لو كان هناك تنافر (contention) بين الخيوط.
		</p>
	</li>
	<li>
		<p>
			تتلقى العمليات الذرية مُعاملًا اختياريًا من نوع <code>‎std::memory_order‎</code>، يُعرِّف الخاصّيات الإضافية للعملية بخصوص مواضع الذاكرة الأخرى.
		</p>
	</li>
</ul>
<table>
<thead><tr>
<th>
				<code>std::memory_order</code>
			</th>
			<th>
				الشرح
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>std::memory_order_relaxed</code>
			</td>
			<td>
				لا قيود إضافية
			</td>
		</tr>
<tr>
<td>
				<code>std::memory_order_release</code>-<code>std::memory_order_acquire‎</code>
			</td>
			<td>
				إذا رأت <code>‎load-acquire‎</code> القيمة المُخزّنة بواسطة <code>‎store-release‎</code> فإنّ التخزينات المتسلسلة (stores sequenced) قبل <code>‎store-release‎</code> ستحدث قبل التحميلات المتسلسلة (loads sequenced) بعد اكتساب التحميل (load acquire).
			</td>
		</tr>
<tr>
<td>
				<code>std::memory_order_consume</code>
			</td>
			<td>
				مثل <code>‎memory_order_acquire‎</code> ولكن تعمل مع الأحمال غير المستقلة (dependent loads) وحسب
			</td>
		</tr>
<tr>
<td>
				<code>std::memory_order_acq_rel</code>
			</td>
			<td>
				تجمع <code>‎load-acquire‎</code> و <code>‎store-release‎</code>
			</td>
		</tr>
<tr>
<td>
				<code>std::memory_order_seq_cst</code>
			</td>
			<td>
				تناسق تسلسلي (sequential consistency)
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	تسمح وسوم ترتيب الذاكرة أعلاه بثلاث نماذج من ترتيب الذاكرة:
</p>

<ul>
<li>
		الاتساق المتسلسل sequential consistency.
	</li>
	<li>
		الترتيب المتراخي Relaxed Ordering.
	</li>
	<li>
		ترتيب اكتساب-تحرير release-acquire واكتساب-استهلاك release-consume.
	</li>
</ul>
<h3>
	الاتساق المتسلسل (Sequential Consistency)
</h3>

<p>
	إذا لم يعُرِّف ترتيب الذاكرة الخاص بعملية ذرية معيّنة فسيتمّ اللجوء إلى الترتيب الافتراضي، والذي هو الاتساق المتسلسل. يمكن أيضًا اختيار هذا الوضع صراحة عبر الوسْم <code>‎std::memory_order_seq_cst‎</code>.
</p>

<p>
	وفي هذا الترتيب، لا يمكن لأيّ عملية على الذاكرة أن تتجاوز العملية الذرية، وستحدث جميع عمليات الذاكرة التسلسلية السابقة للعملية الذرية قبل العملية الذرية، كما ستحدث العملية الذرية قبل جميع عمليات الذاكرة المتسلسلة اللاحقة. هذا هو الوضع الأسهل والأوضح، لكنه قد يؤدّي إلى إضعاف الأداء ويمنع كل تحسينات المصرّف التي قد تحاول إعادة ترتيب العمليات التي تأتي بعد العملية الذرية.
</p>

<h3>
	الترتيب المتراخي (Relaxed Ordering)
</h3>

<p>
	الترتيب المتراخي للذاكرة هو نقيض ترتيب الاتساق المتسلسل، ويمكن تعريفه باستخدام الوسم <code>std::memory_order_relaxed</code>. لا تفرض العمليات الذرية المتراخية أيّ قيود على عمليات الذاكرة الأخرى، ولا يبقى تأثير سوى أنّ العملية بحد ذاتها ستبقى ذرية.
</p>

<h3>
	ترتيب التحرير-الاكتساب (Release-Acquire Ordering)
</h3>

<p>
	يمكن وسم عملية تخزين ذرية باستخدام <code>‎std::memory_order_release‎</code>، كما يمكن وسم عملية تحميل ذري باستخدام <code>‎std::memory_order_acquire‎</code>. وتُسمّى العملية الأولى تخزين-تحرير (ذري) (‎(atomic) store-release) في حين تسمّى العملية الثانية تحميل-اكتساب (ذري) (‎(atomic) load-acquire‎).
</p>

<p>
	وعندما ترى عمليةُ "التحميل-الاكتساب" القيمةَ المكتوبة من قبل عملية (التخزين-التحرير) فسيحدث ما يلي: ستصبح جميع عمليات التخزين المُسلسلة التي تسبق عملية (التخزين-التحرير) مرئيّة لعمليات التحميل المُسلسلة بعد عملية (التحميل-الاكتساب).
</p>

<p>
	يمكن أن تحصل عمليات (القراءة-التعديل-الكتابة) الذرية أيضًا على الوسم التراكمي <code>‎std::memory_order_acq_rel‎</code>. هذا يجعل الجزء المتعلّق بالتحميل الذري من العملية عبارة عن عملية تحميل-اكتساب ذرّية، بينما يصبح الجزء المتعلق بالتخزين الذري عبارة عن عملية تخزين-تحرير ذرّية.
</p>

<p>
	لا يُسمح للمُصرِّف بنقل عمليات التخزين الموجودة بعد عملية تخزين-تحرير ذرّية ما، كما لا يُسمح له أيضًا بنقل عمليات التحميل التي تسبق عملية تحميل-اكتساب ذرية (أو تحميل-استهلاك).
</p>

<p>
	لاحظ أيضًا أنّه لا توجد عملية تحميل-تحرير ذرّية (atomic load-release)، ولا عملية تخزين-اكتساب ذرّية (atomic store-acquire)، وأيّ محاولة لإنشاء مثل هذه العمليات سينجم عنها عمليات متراخية (relaxed operations).
</p>

<h3>
	ترتيب تحرير-استهلاك (Release-Consume Ordering)
</h3>

<p>
	عمليات تحرير-استهلاك تشبه عمليات تحرير-اكتساب، لكن هذه المرّة يوسَم الحمل الذري باستخدام <code>std::memory_order_consume</code> لكي يصبح عملية تحميل-استهلاك (ذرية) - ‎(atomic) load-consume operation. هذا الوضع يشبه عملية تحرير-اكتساب، مع اختلاف وحيد هو أنّه من بين عمليات التحميل المُسلسلة الموجودة بعد عملية تحميل-استهلاك، فلن تُرتَّب إلا تلك التي تعتمد على القيمة التي حُمِّلت بواسطة عملية تحميل-استهلاك.
</p>

<h3>
	الأسوار (Fences)
</h3>

<p>
	تسمح الأسوار بترتيب عمليات الذاكرة بين الخيوط، وقد يكون السور إمّا سور تحرير (release fence)، أو سور اكتساب (acquire fence).
</p>

<p>
	وإذا حدث سور التحرير قبل سور الاكتساب، فستكون المخازن المتسلسلة قبل سور التحرير مرئية للأحمال المتسلسلة بعد سور الاكتساب، وإن أردت ضمان تقديم سور التحرير قبل سور الاكتساب فاستخدام بدائل التزامن الأخرى، بما في ذلك العمليات الذرية المتراخية.
</p>

<h2>
	فائدة نموذج الذاكرة
</h2>

<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_7" style="">
<span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">bool</span><span class="pln"> ready </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    ready </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> use</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ready</span><span class="pun">)</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يستدعي أحد الخيطين دالة <code>‎init()‎</code>، بينما يستدعي الخيط الآخر (أو معالج الإشارة) الدالةَ <code>‎use()‎</code>. قد يتوقع المرء أنّ الدالّة <code>‎use()‎</code> إمّا ستطبع القيمة <code>‎5‎</code>، أو لن تفعل أيّ شيء، لكن هذه الحالة قد لا تحدث كل مرة، لعدّة أسباب:
</p>

<ul>
<li>
		قد تعيد وحدة المعالجة المركزية ترتيب عمليات الكتابة التي تحدث في <code>‎init()‎</code> بحيث تبدو الشيفرة التي ستنفذ على الشكل التالي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_9" style="">
<span class="kwd">void</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    ready </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		قد تعيد وحدة المعالجة المركزية ترتيب عمليّات القراءات التي تحدث في <code>‎use()‎</code> بحيث تصبح الشيفرة التي ستُنفّذ على الشكل التالي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_11" style="">
<span class="kwd">void</span><span class="pln"> use</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> local_x </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> local_y </span><span class="pun">=</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ready</span><span class="pun">)</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> local_x </span><span class="pun">+</span><span class="pln"> local_y</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		قد يقرّر مصرّف C++‎ إعادة ترتيب البرنامج بطريقة مماثلة لتحسين الأداء.
	</li>
</ul>
<p>
	لو كان البرنامج يُنفّذ في خيط واحد فلا يمكن أن يتغيّر سلوك البرنامج نتيجة لإعادة الترتيب، ذلك لأنّ الخيط لا يمكن أن يخلط بين استدعائي <code>‎init()‎</code> و <code>‎use()‎</code>. أمّا في حالة الخيوط المتعددة، فقد يرى أحد الخيوط جزءًا من عمليات الكتابة التي يؤدّيها الخيط الآخر، حيث أنّ <code>‎use()‎</code> قد ترى <code>‎ready==true‎</code> وكذلك المُهملات (garbage) في <code>‎x‎</code> أو <code>‎y‎</code> أو كليهما.
</p>

<p>
	يسمح نموذج الذاكرة في C++‎ للمبرمج بأن يعيد تعريف عمليات إعادة الترتيب المسموح بها أو غير المسموح بها، بحيث يمكن توقّع سلوك البرامج متعددة الخيوط. يمكن إعادة كتابة المثال أعلاه بطريقة ملائمة لتعدّد الخيوط على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_13" style="">
<span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">atomic </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> ready </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">false</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    ready</span><span class="pun">.</span><span class="pln">store</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">memory_order_release</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> use</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ready</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_acquire</span><span class="pun">))</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في المثال أعلاه، تُجري دالة <code>‎init()‎</code> عملية تخزين-تحرير ذرية، هذا لن يخزّن قيمة <code>‎true‎</code> في <code>‎ready‎</code> وحسب، ولكن سيُخطِر أيضًا المُصرِّف بأنّه لا يمكن نقل هذه العملية قبل عمليات الكتابة المتسلسلة التي تسبقها.
</p>

<p>
	كذلك تُجري الدالّة <code>‎use()‎</code> عملية تحميل-اكتساب ذرية. إذ تقرأ القيمة الحالية لـ <code>‎ready‎</code>، وتمنع المُصرِّف من تقديم عمليات القراءة المتسلسلة اللاحقة قبل عملية تحميل-اكتساب الذرية.
</p>

<p>
	هذه العمليات الذرية تجعل المصرّف يضع الإرشادات الضرورية المتعلقة بالجهاز لإبلاغ وحدة المعالجة المركزية بالامتناع عن تقديم عمليات إعادة الترتيب غير المرغوب فيها.
</p>

<p>
	ونظرًا لأنّ عملية تخزين-تحرير الذرية تقع في نفس موقع الذاكرة الخاص بعملية تحميل-اكتساب، ينصّ نموذج الذاكرة على أنّه إذا كانت عملية تحميل-اكتساب ترى القيمة المكتوبة بواسطة عملية تخزين-تحرير، فإنّ جميع عمليات الكتابة التي تُنفّذ بواسطة دالة الخيط <code>‎init()‎</code> التي تسبق عملية التخزين-التحرير (store-release) ستكون مرئية للتحميلات التي تنفّذها دالة الخيط <code>‎use()‎</code> بعد عملية تحميل-اكتساب. أي أنّه في حال رأت الدالةُ <code>‎use()‎</code> تعليمة <code>‎ready==true‎</code>، فسترى كذلك بالضرورة <code>‎x==2‎</code> و <code>‎y==3‎</code>.
</p>

<p>
	لاحظ أنّ المُصرِّف ووحدة المعالجة المركزية لا يزالان يستطيعان الكتابة في <code>‎y‎</code> قبل الكتابة في <code>‎x‎</code>، وبالمثل يمكن أن تحدث عمليات القراءة من المتغيّرات في <code>‎use()‎</code> وفق أيّ ترتيب.
</p>

<h2>
	مثال على الأسوار (Fences)
</h2>

<p>
	يمكن أيضًا تقديم المثال أعلاه باستخدام الأسوار والعمليات الذرية المتراخية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_15" style="">
<span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">atomic </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> ready </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">false</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    atomic_thread_fence</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_release</span><span class="pun">);</span><span class="pln">
    ready</span><span class="pun">.</span><span class="pln">store</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">memory_order_relaxed</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> use</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ready</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_relaxed</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        atomic_thread_fence</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_acquire</span><span class="pun">);</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا رأت عملية التحميل الذرية القيمةَ المكتوبة بواسطة عملية التخزين الذري، فسيحدث التخزين قبل التحميل، وكذلك الحال مع الأسوار: يحدُث تحرير السور قبل اكتساب السور، ما يجعل عملية الكتابة في <code>‎x‎</code> و <code>‎y‎</code> التي تسبق سور التحرير مرئية للعبارة <code>‎std::cout‎</code> التي تلي اكتساب السور.
</p>

<p>
	قد يكون استخدام السور مفيدًا في حال كان يقلّل العدد الإجمالي لعمليات الاكتساب أو التحرير أو عمليات المزامنة الأخرى. مثلّا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_17" style="">
<span class="kwd">void</span><span class="pln"> block_and_use</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">ready</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_relaxed</span><span class="pun">))</span><span class="pln">
    </span><span class="pun">;</span><span class="pln">
    atomic_thread_fence</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">memory_order_acquire</span><span class="pun">);</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ويستمرّ تنفيذ الدالّة <code>‎block_and_use()‎</code> إلى أن تُضبَط قيمة راية <code>‎ready‎</code> بمساعدة التحميل الذري المتراخي، ثم يُستخدَم سور اكتساب واحد لتوفير ترتيب الذاكرة المطلوب.
</p>

<h2>
	إدارة الذاكرة (Memory management)
</h2>

<h3>
	التخزين الحرّ (Free Storage)
</h3>

<p>
	مصطلح "الكومة" (heap) هو مصطلح عام في الحوسبة يشير إلى مساحة من الذاكرة يمكن تخصيص أجزاء منها أو تحريرها بشكل مستقل عن الذاكرة التي يوفرها المكدّس (stack). ويسمي المعيار في ++C هذه المساحة بالتخزين الحر، وهو أكثر دقة من الكومة.
</p>

<p>
	وقد تبقى مناطق الذاكرة المخصصة للتخزين الحرّ حتّى بعد الخروج من النطاق الأصلي الذي خُصِّصت فيه، ويمكن تخصيص ذاكرة للبيانات في التخزين الحر إن كانت مساحة البيانات أكبر من أن تُخزَّن في المكدّس.
</p>

<p>
	تُستخدم الكلمتان المفتاحيتان <code>new</code> و <code>delete</code> لتخصيص الذاكرة الخام (Raw memory) وتحريرها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_19" style="">
<span class="typ">float</span><span class="pln"> </span><span class="pun">*</span><span class="pln">foo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pun">;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">*</span><span class="pln">foo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">float</span><span class="pun">;</span><span class="pln">        </span><span class="com">// تخصيص ذاكرة لعدد عشري</span><span class="pln">
    </span><span class="typ">float</span><span class="pln"> bar</span><span class="pun">;</span><span class="pln">            </span><span class="com">// تخصيص المكدّس</span><span class="pln">
</span><span class="pun">}</span><span class="pln">                </span><span class="com">// ما تزال باقية foo لكنّ bar نهاية</span><span class="pln">

</span><span class="kwd">delete</span><span class="pln"> foo</span><span class="pun">;</span><span class="pln">        </span><span class="com">// وهذا يؤدي إلى جعل المؤشّر غير صالح ،pF حذف ذاكرة العدد العشري الموجودة عند</span><span class="pln">
foo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pun">;</span><span class="pln">        </span><span class="com">// من الممارسات السيئة `nullptr` يُعد ضبط المؤشر عند القيمة.</span></pre>

<p>
	من الممكن أيضًا تخصيص ذاكرة للمصفوفات ذات الحجم الثابت بكلمتيْ <code>new</code> و <code>delete</code>، لكن مع صيغة مختلفة قليلاً، ذلك أن تخصيص ذاكرة المصفوفات يختلف عن تخصيص الذاكرة للكائنات الأخرى، وسيؤدّي خلط الاثنتين إلى عطب في الكومة (heap corruption).
</p>

<p>
	ويؤدي تخصيص ذاكرة المصفوفات أيضًا إلى تخصيص ذاكرة مخصّصة لتعقّب حجم المصفوفة، وذلك لأجل استخدامها عند حذف المصفوفة لاحقًا (تتعلق بالتنفيذ).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_21" style="">
<span class="com">// تخصيص ذاكرة مؤلّفة من 256 عددًا صحيحًا</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">foo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">256</span><span class="pun">];</span><span class="pln">
</span><span class="com">// حذف المصفوفة</span><span class="pln">
</span><span class="kwd">delete</span><span class="pun">[]</span><span class="pln"> foo</span><span class="pun">;</span></pre>

<p>
	سيُنفَّذ المنشئ والمدمّر -كما هو حال الكائنات في المكدّس (Stack based objects)- عند استخدام <code>new</code> و <code>delete</code> بدلًا من <code>malloc</code> و <code>free</code>، لهذا فإنّ خيار <code>new</code> و <code>delete</code> خير من <code>malloc</code> و <code>free</code>. انظر المثال التالي حيث نخصص ذاكرة لنوعٍ <code>ComplexType</code>، ونستدعي منشئه، ثم نستدعي مدمر <code>()ComplexType</code> ونحذف ذاكرة <code>Complextype</code> عند الموضع pC.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_23" style="">
<span class="kwd">struct</span><span class="pln"> </span><span class="typ">ComplexType</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">ComplexType</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Ctor"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">~</span><span class="typ">ComplexType</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Dtor"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="typ">ComplexType</span><span class="pln"> </span><span class="pun">*</span><span class="pln">foo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ComplexType</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">delete</span><span class="pln"> foo</span><span class="pun">;</span></pre>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	يوصى باستخدام المؤشّرات الذكية منذ الإصدار C++‎ 11 للإشارة إلى المِلكِيّة.
</p>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	C++‎ 14 أضافت <code>‎std::make_unique‎</code> إلى مكتبة القوالب القياسية STL، مغيرة بذلك الإرشادات لتفضيل <code>‎std::make_unique‎</code> أو <code>std::make_shared</code> على استخدام <code>new</code> و <code>delete</code>.
</p>

<h3>
	new
</h3>

<p>
	قد لا ترغب في بعض الحالات في الاعتماد على التخزين الحرّ (Free Store) لتخصيص الذاكرة، وتريد تخصيص ذاكرة مخصصة باستخدام <code>‎new‎</code>.
</p>

<p>
	عندئذ يمكنك استخدام <code>‎Placement New‎</code>، بحيث تخبر المعامل "new" بأن يخصّص الذاكرة من موضع مُخصص مسبقًا. انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_25" style="">
<span class="typ">int</span><span class="pln"> a4byteInteger</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">a4byteChar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="pun">(&amp;</span><span class="pln">a4byteInteger</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">[</span><span class="lit">4</span><span class="pun">];</span></pre>

<p>
	في المثال السابق ، الذاكرة المشار إليها عبر <code>‎a4byteChar‎</code> هي 4 بايت، مخصصة "للمكدّس" عبر المتغيّر الصحيح <code>‎a4byteInteger‎</code>.
</p>

<p>
	وفائدة هذا النوع من تخصيص الذاكرة أنه سيكون للمبرمجين تحكّم كامل في التخصيص، فبما أن ذاكرة <code>‎a4byteInteger‎</code> في المثال أعلاه مُخصّصة في المكدّس فلن تحتاج إلى استدعاء صريح لـ <code>a4byteChar delete</code>. يمكن تحقيق نفس السلوك في حالة تخصيص ذاكرة ديناميكية أيضًا. مثلّا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_27" style="">
<span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">a8byteDynamicInteger </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">a8byteChar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a8byteDynamicInteger</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">[</span><span class="lit">8</span><span class="pun">];</span></pre>

<p>
	يشير مؤشّر الذاكرة <code>‎a8byteChar‎</code> في هذه الحالة إلى الذاكرة الديناميكية المخصصة عبر <code>‎a8byteDynamicInteger‎</code>. لكن مع ذلك، سنحتاج في هذه الحالة إلى استدعاء <code>‎a8byteDynamicInteger‎</code> صراحةً لتحرير الذاكرة.
</p>

<p>
	هذا مثال آخر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_29" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;complex&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ComplexType</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">ComplexType</span><span class="pun">():</span><span class="pln"> a</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">ComplexType</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> dynArray </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">[</span><span class="lit">256</span><span class="pun">];</span></pre>

<p>
	نستدعي منشئ <code>ComplexType</code> لتهيئة الذاكرة كـ <code>ComplexType</code>، نتابع …
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_31" style="">
<span class="pln">    </span><span class="kwd">new</span><span class="pun">((</span><span class="kwd">void</span><span class="pun">*</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> dynArray</span><span class="pun">)</span><span class="pln"> </span><span class="typ">ComplexType</span><span class="pun">();</span><span class="pln">
    </span><span class="com">// تنظيف الذاكرة بعد الانتهاء</span><span class="pln">
    </span><span class="kwd">reinterpret_cast</span><span class="pun">&lt;</span><span class="typ">ComplexType</span><span class="pun">*&gt;(</span><span class="pln">dynArray</span><span class="pun">)-&gt;~</span><span class="typ">ComplexType</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">delete</span><span class="pun">[]</span><span class="pln"> dynArray</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// placement new يمكن أيضا استخدام ذاكرة المكدّس مع</span><span class="pln">
    alignas</span><span class="pun">(</span><span class="typ">ComplexType</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> localArray</span><span class="pun">[</span><span class="lit">256</span><span class="pun">];</span><span class="pln"> </span><span class="com">//alignas() available since C++11</span><span class="pln">
    </span><span class="kwd">new</span><span class="pun">((</span><span class="kwd">void</span><span class="pun">*</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> localArray</span><span class="pun">)</span><span class="pln"> </span><span class="typ">ComplexType</span><span class="pun">();</span><span class="pln">
    </span><span class="com">// لا تحتاج إلى استدعاء المدمّر إلا لذاكرة المكدّس</span><span class="pln">
    </span><span class="kwd">reinterpret_cast</span><span class="pun">&lt;</span><span class="typ">ComplexType</span><span class="pun">*&gt;(</span><span class="pln">localArray</span><span class="pun">)-&gt;~</span><span class="typ">ComplexType</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	المكدّس
</h3>

<p>
	المكدّس هو منطقة صغيرة من الذاكرة توضع فيها القيم المؤقتة أثناء التنفيذ، ويعدّ تخصيص البيانات فيه سريعًا جدًا مقارنة بتخصيصها في الكومة، إذ أنّ الذاكرة كلها مسنَدة سلفًا لهذا الغرض.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_33" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// مُخزّنة على المكدّس</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وقد سُمِّي مكدّسًا لأنّ الاستدعاءات المتسلسلة للدوال ستكون لها ذاكرة مؤقتة "مُكدّسة" فوق بعضها البعض، وكل واحدة ستستخدم قسمًا صغيرًا منفصلًا من الذاكرة. في المثال التالي، ستوضع <code>f</code> على المكدس في النهاية بعد كل وضع كل شيء (انظر 1)، وتوضع <code>d</code> كذلك بعد كل شيء في نطاق <code>()main</code> (انظر 2):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_35" style="">
<span class="typ">float</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// (1)</span><span class="pln">
    </span><span class="typ">float</span><span class="pln"> f </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> f</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// (2)</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> bar</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> d</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// foo() لا تُخزّن في المكدّس أيّ متغيرات خاصّة بالمستخدم إلى حين استدعاء</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)</span><span class="pln"> foo</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستبقى البيانات المخزّنة على المكدّس صالحة ما دام النطاق الذي خَصص المتغيّر نشطًًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_372_37" style="">
<span class="typ">int</span><span class="pun">*</span><span class="pln"> pA </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pA</span><span class="pun">;</span><span class="pln">
    pA </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
    pA </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">a</span><span class="pun">;</span><span class="pln">
    foo</span><span class="pun">();</span><span class="pln">
    </span><span class="com">// خارج النطاق pA سلوك غير معرَّف، أصبحت القيمة التي يشير إليها</span><span class="pln">
    a </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">pA</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617022784="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 115: Memory management و Chapter 116: C++11 Memory Model من كتاب <a data-ss1617022784="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1029</guid><pubDate>Sat, 31 Oct 2020 13:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x642;&#x64A;&#x645; &#x645;&#x635;&#x646;&#x641;&#x629; &#x627;&#x644;&#x646;&#x648;&#x639; &#x627;&#x644;&#x645;&#x639;&#x631;&#x651;&#x64E;&#x641;&#x629; &#x645;&#x646; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; User-De&#xFB01;ned Literals &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D9%85%D8%B5%D9%86%D9%81%D8%A9-%D8%A7%D9%84%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D8%B1%D9%91%D9%8E%D9%81%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-user-de%EF%AC%81ned-literals-%D9%81%D9%8A-cpp-r1028/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/53.png.869d7a0e0c179c34bab38875be3643f2.png" /></p>

<h2>
	القيم الثنائية مصنفة النوع المُعرّفة من المستخدم (Self-made user-deﬁned literal for binary)
</h2>

<p>
	رغم إمكانية كتابة عدد ثنائي في C++‎ 14 على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_7" style="">
<span class="typ">int</span><span class="pln"> number </span><span class="pun">=</span><span class="lit">0b0001</span><span class="str">'0101; // ==21</span></pre>

<p>
	إلا أننا في الأسطر التالية سنستعرض مثالًا مشهورًا يوفّر طريقة أخرى ذاتية التنفيذ للأعداد الثنائية. لاحظ أن برنامج توسيع القالب التالي يعمل في وقت التصريف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_9" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> FIRST</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">...</span><span class="pln">REST </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> binary </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static_assert</span><span class="pun">(</span><span class="pln">FIRST </span><span class="pun">==</span><span class="pln"> </span><span class="str">'0'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> FIRST </span><span class="pun">==</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">"invalid binary digit"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">enum</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value </span><span class="pun">=</span><span class="pln"> </span><span class="pun">((</span><span class="pln">FIRST </span><span class="pun">-</span><span class="pln"> </span><span class="str">'0'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">...(</span><span class="pln">REST</span><span class="pun">))</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> binary </span><span class="pun">&lt;</span><span class="pln"> REST</span><span class="pun">...</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">::</span><span class="pln">value
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> binary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="str">'0'</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">enum</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> binary </span><span class="pun">&lt;</span><span class="pln"> </span><span class="str">'1'</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">enum</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// عامل قيمة خام مصنفة النوع</span><span class="pln">
</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">...</span><span class="pln">LITERAL </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">inline</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="str">""</span><span class="pln"> _b</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> binary </span><span class="pun">&lt;</span><span class="pln"> LITERAL</span><span class="pun">...</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">::</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// عاملُ قيمةٍ خامٍ مصنَّفةِ النوع</span><span class="pln">
</span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">...</span><span class="pln">LITERAL </span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">inline</span><span class="pln">
</span><span class="kwd">constexpr</span><span class="pln"> </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="str">""</span><span class="pln"> _B</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> binary </span><span class="pun">&lt;</span><span class="pln"> LITERAL</span><span class="pun">...</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">::</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">10101</span><span class="pln">_B </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">", "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">011011000111</span><span class="pln">_b </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تطبع 21, 1735</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	القيم مصنفة النوع المعيارية المُعرّفة من المستخدم (Standard user-deﬁned literals for duration)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	فيما يلي قيمُ مدةٍ مصنَّفةِ النوع، ومعرَّفة من قِبل المستخدم (duration user literals)، مصرح عنها في فضاء الاسم <code>namespace std::literals::chrono_literals</code>، حيث <code>‎literals‎</code> و <code>‎chrono_literals‎</code> هما فضاءا اسم ضمنيّان (inline namespaces). يمكن الوصول إلى هذه العوامل باستخدام <code>using namespace std::literals</code> و <code>using namespace std::chrono_literals</code> و <code>using namespace std::literals::chrono_literals</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_11" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;chrono&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">literals</span><span class="pun">::</span><span class="pln">chrono_literals</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">nanoseconds t1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">600ns</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">microseconds t2 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42us</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">milliseconds t3 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">51ms</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">seconds t4 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">61s</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">minutes t5 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">88min</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> t6 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">0.5h</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">auto</span><span class="pln"> total </span><span class="pun">=</span><span class="pln"> t1 </span><span class="pun">+</span><span class="pln"> t2 </span><span class="pun">+</span><span class="pln"> t3 </span><span class="pun">+</span><span class="pln"> t4 </span><span class="pun">+</span><span class="pln"> t5 </span><span class="pun">+</span><span class="pln"> t6</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout</span><span class="pun">.</span><span class="pln">precision</span><span class="pun">(</span><span class="lit">13</span><span class="pun">);</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> total</span><span class="pun">.</span><span class="pln">count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" nanoseconds"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> 
                          </span><span class="com">//  8941051042600 nanoseconds</span><span class="pln">
      std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">duration_cast  </span><span class="pun">&lt;</span><span class="pln">  std</span><span class="pun">::</span><span class="pln">chrono</span><span class="pun">::</span><span class="pln">hours </span><span class="pun">&gt;</span><span class="pln">  </span><span class="pun">(</span><span class="pln">total</span><span class="pun">).</span><span class="pln">count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" hours"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ساعتان</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	القيم مصنَّفة النوع المُعرّفة من المستخدم، ذات قيَم long double
</h2>

<p>
	يوضّح المثال التالي كيفية استخدام قيم مصنَّفة النوع، مُعرّفة من المستخدم وذات قيَم long double:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_13" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="str">""</span><span class="pln"> _km</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> val </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1000.0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="str">""</span><span class="pln"> _mi</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> val </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1609.344</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"3 km = "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">3.0</span><span class="pln">_km </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" m\n"</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"3 mi = "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="lit">3.0</span><span class="pln">_mi </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" m\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	خرج هذا البرنامج هو:
</p>

<pre class="ipsCode">
3 km = 3000 m
3 mi = 4828.03 m
</pre>

<h2>
	السلاسل النصية المجردة القياسية والمعرّفة من المستخدم (Standard user-defined literals for strings)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	فيما يلي سلاسل نصية مجردةٌ ومُعرّفة من المستخدم (string user literals)، مُصرَّح عنها في <code>namespace std::literals::string_literals</code>، حيث <code>‎literals‎</code> و <code>‎string_literals‎</code> هما فضاءا اسم مُضمّنان. ويمكن الوصول إلى هذه العوامل باستخدام <code>using namespace std::literals</code> و <code>using namespace std::string_literals</code> و <code>using namespace std::literals::string_literals</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_15" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;codecvt&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;locale&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">literals</span><span class="pun">::</span><span class="pln">string_literals</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string s </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hello world"</span><span class="pln">s</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">u16string s16 </span><span class="pun">=</span><span class="pln"> u</span><span class="str">"hello world"</span><span class="pln">s</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">u32string s32 </span><span class="pun">=</span><span class="pln"> U</span><span class="str">"hello world"</span><span class="pln">s</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">wstring ws </span><span class="pun">=</span><span class="pln"> L</span><span class="str">"hello world"</span><span class="pln">s</span><span class="pun">;</span><span class="pln">

    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> s </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">wstring_convert </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">codecvt_utf8_utf16 </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">char16_t</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> </span><span class="typ">char16_t</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> utf16conv</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> utf16conv</span><span class="pun">.</span><span class="pln">to_bytes</span><span class="pun">(</span><span class="pln">s16</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">wstring_convert </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">codecvt_utf8_utf16 </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">char32_t</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> </span><span class="typ">char32_t</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> utf32conv</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> utf32conv</span><span class="pun">.</span><span class="pln">to_bytes</span><span class="pun">(</span><span class="pln">s32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">wcout </span><span class="pun">&lt;&lt;</span><span class="pln"> ws </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: قد تحتوي السلاسل النصية المجردة على المحرف <code>‎\0‎</code>، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_17" style="">
<span class="com">// "foo"s النصية سينتج عنها C منشئات سلاسل</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">string s1 </span><span class="pun">=</span><span class="pln"> </span><span class="str">"foo\0\0bar"</span><span class="pun">;</span><span class="pln"> 

</span><span class="com">//  '\0' تحتوي هذه السلسلة النصية في وسطها على محرفين </span><span class="pln">
std</span><span class="pun">::</span><span class="pln">string s2 </span><span class="pun">=</span><span class="pln"> </span><span class="str">"foo\0\0bar"</span><span class="pln">s</span><span class="pun">;</span><span class="pln"> </span></pre>

<h2>
	القيم مصنفة النوع المركّبة المعرّفة من المستخدم (Standard user-deﬁned literals for complex)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	فيما يلي، قيم مركّبة مصنفة النوع ومعرّفة من المستخدم، مُصرًّح عنها في<code>namespace std::literals::complex_literals</code>، حيث <code>‎literals‎</code> و <code>‎complex_literals‎</code> فضاءا اسم ضمنيان. يمكن الوصول إلى هذه العوامل باستخدام <code>using namespace std::literals</code> و <code>using namespace std::complex_literals</code> و <code>using namespace std::literals::complex_literals</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3903_19" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;complex&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">literals</span><span class="pun">::</span><span class="pln">complex_literals</span><span class="pun">;</span><span class="pln">

    std</span><span class="pun">::</span><span class="pln">complex </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2.0</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1i</span><span class="pun">;</span><span class="pln"> </span><span class="com">// {2.0, 1.}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">complex </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">float</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> cf </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2.0f</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1if</span><span class="pun">;</span><span class="pln"> </span><span class="com">// {2.0f, 1.f}</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">complex </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> cl </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2.0L</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1il</span><span class="pun">;</span><span class="pln"> </span><span class="com">// {2.0L, 1.L}</span><span class="pln">

    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"abs"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> c </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" = "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// abs(2,1) = 2.23607</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"abs"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> cf </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" = "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">cf</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// abs(2,1) = 2.23607</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"abs"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> cl </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" = "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">cl</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="com">// abs(2,1) = 2.23607</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617022785="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 114: User-Defined Literals من كتاب <a data-ss1617022785="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1028</guid><pubDate>Thu, 29 Oct 2020 13:03:00 +0000</pubDate></item><item><title>&#x623;&#x646;&#x645;&#x627;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x648;&#x62A;&#x642;&#x646;&#x64A;&#x627;&#x62A; &#x625;&#x639;&#x627;&#x62F;&#x629; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%81%D9%8A-cpp-r1027/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/52.png.587ad59364eb13f8c1ba10582fa52fab.png" /></p>

<p>
	سنستعرض في هذا الدرس بعض أنماط التصميم الشهيرة في C++‎ ثم سنتطرق سريعًا إلى مفهوم إعادة التصميم (Refactoring) والنمط Goto Cleanup المتَّبع.
</p>

<h2>
	نمط المحوِّل (Adapter Pattern)
</h2>

<p>
	يتيح <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/adapter" rel="external">نمط المحوِّل</a> للأصناف غير المتوافقة أن تعمل معًا، والسبب الأساسي في استخدامه تكمن في أنّه يمكّن المطوّرين من إعادة استخدام البرامج الموجودة عبر تعديل الواجهة فقط.
</p>

<ol>
<li>
		يعتمد نمط المحول على تركيب الكائنات (object composition).
	</li>
	<li>
		العميل يستدعي العملية على المحوِّل.
	</li>
	<li>
		يستدعي المحوِّل الكائن المحوَّل <code>Adaptee</code> لتنفيذ العملية.
	</li>
	<li>
		تُبنى المكدّسات (stacks) في مكتبة القوالب القياسية STL على المتجهات، فمثلًا: عندما ينفّذ المُكدّس الدّالةَ <code>push()‎</code>، فإنّ المتجه الأساسي (underlying vector) سينفذ التابع <code>vector::push_back()‎</code>.
	</li>
</ol>
<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_7" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
 </span><span class="com">// الواجهة المقصودة</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// المركّب القديم - المحوَّل</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">LegacyRectangle</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            x1_ </span><span class="pun">=</span><span class="pln"> x1</span><span class="pun">;</span><span class="pln">
            y1_ </span><span class="pun">=</span><span class="pln"> y1</span><span class="pun">;</span><span class="pln">
            x2_ </span><span class="pun">=</span><span class="pln"> x2</span><span class="pun">;</span><span class="pln">
            y2_ </span><span class="pun">=</span><span class="pln"> y2</span><span class="pun">;</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"LegacyRectangle(x1,y1,x2,y2)\n"</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> oldDraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"LegacyRectangle:  oldDraw(). \n"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> x1_</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> y1_</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x2_</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> y2_</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// Adapter wrapper مغلِّف المحوَّل</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> h</span><span class="pun">):</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">+</span><span class="pln"> h</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"RectangleAdapter(x,y,x+w,x+h)\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"RectangleAdapter: draw().\n"</span><span class="pun">;</span><span class="pln">
        oldDraw</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> w </span><span class="pun">=</span><span class="pln"> </span><span class="lit">300</span><span class="pun">,</span><span class="pln"> h </span><span class="pun">=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> h</span><span class="pun">);</span><span class="pln">
    r </span><span class="pun">-&gt;</span><span class="pln"> draw</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// :الخرج</span><span class="pln">
</span><span class="com">//LegacyRectangle(x1,y1,x2,y2)</span><span class="pln">
</span><span class="com">//RectangleAdapter(x,y,x+w,x+h)</span></pre>

<p>
	شرح الشيفرة أعلاه:
</p>

<ol>
<li>
		يعتقد العميل أنّه يتحدث إلى <code>‎Rectangle‎</code>
	</li>
	<li>
		الهدف هو الصنف <code>‎Rectangle‎</code>، وهو الذي سيستدعي العميلُ التابعَ عليه.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_9" style="">
<span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> h</span><span class="pun">);</span><span class="pln">
r </span><span class="pun">-&gt;</span><span class="pln"> draw</span><span class="pun">();</span></pre>

<ol start="3">
<li>
		لاحظ أنّ صنف المحوِّل (adapter class) يستخدم الوراثة المتعدّدة.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_11" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="4">
<li>
		يتيح المحوِّل<code>‎RectangleAdapter‎</code> للصنف <code>‎LegacyRectangle‎</code> الاستجابة للطلب (استدعاء <code>‎draw()‎</code> على <code>‎Rectangle‎</code>) عن طريق وراثة الصنفين معًا.
	</li>
	<li>
		لا يملك الصنف <code>‎LegacyRectangle‎</code> نفس التوابع (<code>‎draw()‎</code>) التي يملكها <code>‎Rectangle‎</code>، لكن يمكن أن يأخذ <code>Adapter(RectangleAdapter)‎</code> استدعاءات التابعِ <code>‎Rectangle‎</code> ثمّ يعود لاستدعاء التابع <code>‎LegacyRectangle‎</code> على <code>‎oldDraw()‎</code>.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_13" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RectangleAdapter</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> h</span><span class="pun">):</span><span class="pln"> </span><span class="typ">LegacyRectangle</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">+</span><span class="pln"> h</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"RectangleAdapter(x,y,x+w,x+h)\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"RectangleAdapter: draw().\n"</span><span class="pun">;</span><span class="pln">
        oldDraw</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يُترجمُ نمط <strong>المحوِّل</strong> واجهةَ صنف معيّن إلى واجهة أخرى متوافقة، ولكن مختلفة. لذلك، فهو يشبه <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/proxy" rel="external">نمط الوكيل</a> من حيث أنّه مغلّف أحادي المكوّنات (single-component wrapper)، لكن قد تكون واجهة الصنف المحوَّل والصنف الأصلي مختلفة.
</p>

<p>
	ويمكن استخدام نمط المحوِّل لإظهار واجهة برمجية (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) معيّنة للسماح لها بالعمل مع شيفرات أخرى كما رأينا في المثال أعلاه. أيضًا، يمكننا أن نأخذ واجهات غير متجانسة، ونحوّلها لواجهة برمجية موحّدة ومتسقة.
</p>

<p>
	لدى <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/bridge" rel="external"><strong>نمط الجسر</strong></a> بنية مشابهة للكائنات المحوِّلة، بيْد أنّ للجسور هدفًا مختلفًا، إذ يُرادُ منها فصل الواجهة عن التقديم، حتّى يسهل تعديلها بشكل مستقل. أمّا المحوِّل فيُراد منه تعديل واجهة كائن موجود.
</p>

<h2>
	نمط المراقب (Observer pattern)
</h2>

<p>
	الهدف من <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/observer" rel="external"><strong>نمط المراقب</strong></a> هو تعريف اعتمادية واحد-إلى-متعدد (one-to-many) بين الكائنات، بحيث إذا تغيرت حالة كائن تُرسل إشعارات إلى جميع الكائنات المتعلّقة به وتحديثها تلقائيًا.
</p>

<p>
	يعرِّف الهدف (subject) والمراقبُون (observers) اعتمادية الواحد-إلى-متعدد، وفي هذه الاعتمادية يعتمد المراقبون على الأهداف، وعندما تتغيّر حالة الهدف يتم إشعار المراقبين تلقائيًا. وبناءً على ذلك يمكن تحديث المراقبين وإعطاؤهم قيمًا جديدة.
</p>

<p>
	فيما يلي مثال من كتاب "Design Patterns" من تأليف جاما (Gamma).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_15" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;vector&gt;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Subject</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Observer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">virtual</span><span class="pln"> </span><span class="pun">~</span><span class="typ">Observer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Update</span><span class="pun">(</span><span class="typ">Subject</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Subject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
</span><span class="kwd">virtual</span><span class="pln"> </span><span class="pun">~</span><span class="typ">Subject</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Attach</span><span class="pun">(</span><span class="typ">Observer</span><span class="pun">&amp;</span><span class="pln"> o</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> observers</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(&amp;</span><span class="pln">o</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Detach</span><span class="pun">(</span><span class="typ">Observer</span><span class="pun">&amp;</span><span class="pln"> o</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
observers</span><span class="pun">.</span><span class="pln">erase</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">remove</span><span class="pun">(</span><span class="pln">observers</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">(),</span><span class="pln"> observers</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(),</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">o</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Notify</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">auto</span><span class="pun">*</span><span class="pln"> o </span><span class="pun">:</span><span class="pln"> observers</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
o</span><span class="pun">-&gt;</span><span class="typ">Update</span><span class="pun">(*</span><span class="kwd">this</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pun">&lt;</span><span class="typ">Observer</span><span class="pun">*&gt;</span><span class="pln"> observers</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ClockTimer</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Subject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">SetTime</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> hour</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> minute</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> second</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> hour </span><span class="pun">=</span><span class="pln"> hour</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> minute </span><span class="pun">=</span><span class="pln"> minute</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> second </span><span class="pun">=</span><span class="pln"> second</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">Notify</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="typ">GetHour</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> hour</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="typ">GetMinute</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> minute</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> </span><span class="typ">GetSecond</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> second</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> hour</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> minute</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> second</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">DigitalClock</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Observer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">explicit</span><span class="pln"> </span><span class="typ">DigitalClock</span><span class="pun">(</span><span class="typ">ClockTimer</span><span class="pun">&amp;</span><span class="pln"> s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> subject</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">Attach</span><span class="pun">(*</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">~</span><span class="typ">DigitalClock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  subject</span><span class="pun">.</span><span class="typ">Detach</span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Update</span><span class="pun">(</span><span class="typ">Subject</span><span class="pun">&amp;</span><span class="pln"> theChangedSubject</span><span class="pun">)</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">theChangedSubject </span><span class="pun">==</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">subject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Draw</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> hour </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetHour</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> minute </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetMinute</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> second </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetSecond</span><span class="pun">();</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Digital time is "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> hour </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">":"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln">
            minute </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">":"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln">
            second </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ClockTimer</span><span class="pun">&amp;</span><span class="pln"> subject</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AnalogClock</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Observer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">explicit</span><span class="pln"> </span><span class="typ">AnalogClock</span><span class="pun">(</span><span class="typ">ClockTimer</span><span class="pun">&amp;</span><span class="pln"> s</span><span class="pun">):</span><span class="pln"> subject</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        subject</span><span class="pun">.</span><span class="typ">Attach</span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
             </span><span class="pun">~</span><span class="typ">AnalogClock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  subject</span><span class="pun">.</span><span class="typ">Detach</span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">);}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Update</span><span class="pun">(</span><span class="typ">Subject</span><span class="pun">&amp;</span><span class="pln"> theChangedSubject</span><span class="pun">)</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">theChangedSubject </span><span class="pun">==</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">subject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Draw</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> hour </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetHour</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> minute </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetMinute</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> second </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">.</span><span class="typ">GetSecond</span><span class="pun">();</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Analog time is "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> hour </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">":"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln">
            minute </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">":"</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln">
            second </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ClockTimer</span><span class="pun">&amp;</span><span class="pln"> subject</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">ClockTimer</span><span class="pln"> timer</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">DigitalClock</span><span class="pln"> digitalClock</span><span class="pun">(</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">AnalogClock</span><span class="pln"> analogClock</span><span class="pun">(</span><span class="pln">timer</span><span class="pun">);</span><span class="pln">
    timer</span><span class="pun">.</span><span class="typ">SetTime</span><span class="pun">(</span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">41</span><span class="pun">,</span><span class="pln"> </span><span class="lit">36</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الخرج:
</p>

<pre class="ipsCode">
Digital time is 14: 41: 36
Analog time is 14: 41: 36
</pre>

<p>
	توضّح النقاط التالية ملخّص نمط المراقب:
</p>

<ol>
<li>
		تستخدم الكائنات (<code>‎DigitalClock‎</code> أو <code>‎AnalogClock‎</code>) واجهات الموضوع (<code>‎Attach()‎</code> أو <code>‎Detach()‎</code>) إمّا لأجل الاشتراك (subscribe) كمراقبين، أو إلغاء الاشتراك (إزالة أنفسهم) من كونهم مراقبين (<code>‎subject.Attach(*this);‎</code>، <code>‎subject.Detach(*this);‎</code>.
	</li>
	<li>
		يمكن أن يكون لكل موضوع عدّة مراقبين (<code>‎vector&lt;Observer*&gt; observers;‎</code>).
	</li>
	<li>
		يحتاج جميع المراقبين إلى تنفيذ واجهة المراقب (Observer interface). لدى هذه الواجهة تابع واحد فقط، وهو <code>‎Update()‎</code>، ويُستدعى عند تغيّر حالة الموضوع (<code>‎Update(Subject &amp;)‎</code>)
	</li>
	<li>
		بالإضافة إلى التابعين <code>‎Attach()‎</code> و <code>‎Detach()‎</code>، ينفِّذ الهدف الحقيقي التابعَ <code>‎Notify()‎</code> الذي يُستخدم لتحديث جميع المراقبين الحاليين عندما تتغيّر الحالة، لكن تتم جميعها في هذه الحالة في الصنف الأب، <code>Subject (Subject::Attach (Observer&amp;)‎</code> و <code>‎void Subject::Detach(Observer&amp;)‎</code> و <code>void Subject::Notify()‎</code>.
	</li>
	<li>
		قد يحتوي الكائن الحقيقي أيضًا على توابع لضبط قيمة حالته، أو الحصول عليها.
	</li>
	<li>
		يمكن أن تكون المراقبات الحقيقية أيّ صنف ينفذ واجهة المراقب (Observer interface)، ويشترك كل مراقب مع هدف حقيقي ليحصل على التحديثات (<code>‎subject.Attach(*this);‎</code>).
	</li>
	<li>
		كائنا نمط المراقب مترابطان <strong>بشكل طفيف</strong>، إذ يمكنهما التفاعل مع بعضهما البعض، لكنّ معرفتها ببعضهما محدودة.
	</li>
</ol>
<h3>
	الإشارات والفتحات (Slots)
</h3>

<p>
	الإشارات والفتحات (Slots) هي بنية لغوية قُدِّمت في Qt، وتسهّل على المطوّرين تقديم نمط المراقب دون الحاجة لاستخدام الشيفرات المُتداولة (boilerplate code).
</p>

<p>
	الفكرة الرئيسية وراء الإشارات والفتحات هي أنّ عناصر التحكم (controls) التي تُعرف أيضًا باسم الودجات widgets) يمكنها إرسال إشارات تحتوي على معلومات حول الحدث، والتي يمكن استقبالها من قبل عناصر تحكم أخرى باستخدام دوال خاصة تُعرف باسم الفتحات (slots)، وهي أعضاء أصناف في Qt.
</p>

<p>
	يتوافق نظام الإشارة / الفتحة مع تصميم واجهات المستخدم الرسومية، كما يمكن استخدام نظام الإشارة-الفتحة للدخل / الخرج غير المتزامن (asynchronous I/O) بما في ذلك المقابس sockets، والأنابيب pipes، والأجهزة التسلسلية serial devices، وغيرها مما يختص بإشعارات الأحداث أو لربط أزمنة الأحداث (timeout events) مع نُسخ الكائن والتوابع أو الدوالّ المناسبة.
</p>

<p>
	لا يلزم كتابة شيفرة خاصة بالتسجيل/إلغاء التسجيل/الاستدعاء، لأنّ الكائن الوصفي للمصرّف (Meta Object Compiler أو اختصارًا MOC) الخاصّ بـ Qt يولّد البنية الأساسية اللازمة تلقائيًا .
</p>

<p>
	تدعم لغة C#‎ أيضًا إنشاءات مشابهة، لكنها تستخدم مصطلحات وصيغة مختلفة: فالإشارات تسمّى أحداثًا، والفتحات تسمّى مفوِّضَات (delegates). إضافة إلى ذلك يمكن أن يكون المفوّض متغيّرًا محليًا، مثل مؤشّرات الدوال، بينما يجب أن تكون الفتحة في Qt عضوًا في صنف.
</p>

<h2>
	نمط المصنع (Factory Pattern)
</h2>

<p>
	يقسّم <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/factory_method" rel="external">نمط المصنع (Factory pattern)</a> عمليّة إنشاء الكائنات، ويتيح الإنشاء بالاسم باستخدام واجهة مشتركة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_17" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> clone</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string getname</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Bear</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> clone</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_shared </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Bear</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string getname</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"bear"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Cat</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> clone</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_shared </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string getname</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"cat"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AnimalFactory</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> getAnimal</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">&amp;</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> </span><span class="str">"bear"</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_shared </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Bear</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> </span><span class="str">"cat"</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">();</span><span class="pln">

            </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">nullptr</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<h2>
	نمط الباني
</h2>

<p>
	يفصل <a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/builder" rel="external">نمط الباني (Builder Pattern)</a> عملية إنشاء الكائن عن الكائن نفسه، والفكرة الرئيسية وراء ذلك هي أنّ الكائن ليس عليه مسؤولية إنشائه، وقد تكون عمليّة تصريف الكائنات المعقّدة مهمّة معقدة في حدّ ذاتها، لذا يمكن تفويض هذه المهمة إلى صنف آخر.
</p>

<p>
	سأنشئ فيما يلي بانيًا بريديًا Email Builder بلغة C++‎، وهو مستوحى من فكرة مشابهة في في C#‎، كائن البريد الإلكتروني ليس بالضرورة كائنًا معقدًا، ولكنّه مثال جيد لتوضيح كيفية عمل هذا النمط.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_19" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sstream&gt;</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;string&gt;</span><span class="pln">

</span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">
</span><span class="com">// التصريح اللاحق للباني</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">EmailBuilder</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Email</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">friend</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">EmailBuilder</span><span class="pun">;</span><span class="pln"> </span><span class="com">//  Email يمكن للباني الوصول إلى الأعضاء الخاصة في</span><span class="pln">

    </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">EmailBuilder</span><span class="pln"> make</span><span class="pun">();</span><span class="pln">

    string to_string</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        stringstream stream</span><span class="pun">;</span><span class="pln">
        stream </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"from: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> m_from </span><span class="pun">&lt;&lt;</span><span class="pln">
            </span><span class="str">"\nto: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> m_to </span><span class="pun">&lt;&lt;</span><span class="pln">
            </span><span class="str">"\nsubject: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> m_subject </span><span class="pun">&lt;&lt;</span><span class="pln">
            </span><span class="str">"\nbody: "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> m_body</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">str</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Email</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">default</span><span class="pun">;</span><span class="pln"> </span><span class="com">// قصر الإنشاء على الباني</span><span class="pln">

    string m_from</span><span class="pun">;</span><span class="pln">
    string m_to</span><span class="pun">;</span><span class="pln">
    string m_subject</span><span class="pun">;</span><span class="pln">
    string m_body</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">EmailBuilder</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> from</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">&amp;</span><span class="pln">from</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            m_email</span><span class="pun">.</span><span class="pln">m_from </span><span class="pun">=</span><span class="pln"> from</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> to</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">&amp;</span><span class="pln">to</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        m_email</span><span class="pun">.</span><span class="pln">m_to </span><span class="pun">=</span><span class="pln"> to</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> subject</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">&amp;</span><span class="pln">subject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        m_email</span><span class="pun">.</span><span class="pln">m_subject </span><span class="pun">=</span><span class="pln"> subject</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> body</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">&amp;</span><span class="pln">body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        m_email</span><span class="pun">.</span><span class="pln">m_body </span><span class="pun">=</span><span class="pln"> body</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">*</span><span class="kwd">this</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">operator</span><span class="pln"> </span><span class="typ">Email</span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">move</span><span class="pun">(</span><span class="pln">m_email</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لاحظ عمليّة النقل</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Email</span><span class="pln"> m_email</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">EmailBuilder</span><span class="pln"> </span><span class="typ">Email</span><span class="pun">::</span><span class="pln">make</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">EmailBuilder</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// مثال إضافي</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">ostream</span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">ostream</span><span class="pun">&amp;</span><span class="pln"> stream</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Email</span><span class="pun">&amp;</span><span class="pln"> email</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    stream </span><span class="pun">&lt;&lt;</span><span class="pln"> email</span><span class="pun">.</span><span class="pln">to_string</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> stream</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Email</span><span class="pln"> mail </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Email</span><span class="pun">::</span><span class="pln">make</span><span class="pun">().</span><span class="pln">from</span><span class="pun">(</span><span class="str">"me@mail.com"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">to</span><span class="pun">(</span><span class="str">"you@mail.com"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">subject</span><span class="pun">(</span><span class="str">"C++ builders"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">body</span><span class="pun">(</span><span class="str">"I like this <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>, don't you?"</span><span class="pun">);</span><span class="pln">

    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> mail </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بالنسبة للإصدارات الأقدم من C++‎، يمكن تجاهل عملية <code>‎std::move‎</code> وإزالة <code>&amp;&amp;</code> من عامل التحويل لكنّ هذا سيؤدّي إلى إنشاء نسخة مؤقتة.
</p>

<p>
	ينهي المنشئ عمله عندما يُرسَل البريد الإلكتروني بواسطة <code>‎operator Email&amp;&amp;()‎</code>. يكون المنشئ في هذا المثال كائنًا مؤقتًا، ويعيد البريدَ الإلكتروني قبل تدميره.
</p>

<p>
	يمكنك أيضًا استخدام عملية صريحة مثل <code>‎Email EmailBuilder::build() {...}‎</code> بدلًا من عامل التحويل.
</p>

<h3>
	تمرير الباني
</h3>

<p>
	من الميزات الرائعة التي يوفّرها "نمط الباني" هي القدرةُ على استخدام عدّة عوامل (actors) لإنشاء كائن معيّن، ويمكن ذلك عن طريق تمرير المنشئ إلى العوامل الأخرى، والتي سيعطي كل منها بعض المعلومات الإضافية للكائن المبنِيّ. هذا مفيد بشكل خاص عندما تريد بناء الاستعلامات query، أو إضافة المُرشِّحات، وغيرها من المواصفات.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_21" style="">
<span class="kwd">void</span><span class="pln"> add_addresses</span><span class="pun">(</span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> builder</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    builder</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="str">"me@mail.com"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">to</span><span class="pun">(</span><span class="str">"you@mail.com"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> compose_mail</span><span class="pun">(</span><span class="typ">EmailBuilder</span><span class="pun">&amp;</span><span class="pln"> builder</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    builder</span><span class="pun">.</span><span class="pln">subject</span><span class="pun">(</span><span class="str">"I know the subject"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">body</span><span class="pun">(</span><span class="str">"And the body. Someone else knows the addresses."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">EmailBuilder</span><span class="pln"> builder</span><span class="pun">;</span><span class="pln">
    add_addresses</span><span class="pun">(</span><span class="pln">builder</span><span class="pun">);</span><span class="pln">
    compose_mail</span><span class="pun">(</span><span class="pln">builder</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">Email</span><span class="pln"> mail </span><span class="pun">=</span><span class="pln"> builder</span><span class="pun">;</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> mail </span><span class="pun">&lt;&lt;</span><span class="pln"> endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	الكائنات القابلة للتغيير
</h3>

<p>
	يمكنك تغيير تصميم نمط الباني بما يناسب احتياجاتك، سنوضّح هذا الأمر في هذه الفقرة: كائن البريد الإلكتروني في المثال السابق كان غير قابل للتغيير (immutable)، أي أنّه لا يمكن تعديل خاصّياته لأنّه لا يمكن الوصول إليها، وقد كانت هذه الميزة مطلوبة، لكن ماذا لو كنت بحاجة إلى تعديل الكائن بعد إنشائه، سيكون عليك أن توفّر له بعض الضوابط (setters). ولمّا كانت تلك الضوابط تتكرّر في المنشئ، فقد تفكر في جمعها جميعًا في صنف واحد -لن تكون هناك حاجة للصنف الباني إذن-. لكن قد يكون الأفضل جعل الكائن المبنِيّ قابلاً للتغيير.
</p>

<h2>
	نمط تصميم المفردة (Singleton Design Pattern)
</h2>

<h3>
	التهيئة المُرجأة (Lazy Initialization)
</h3>

<p>
	عثرت على هذا المثال في قسم <code>‎Q &amp; A‎</code> في <a data-ss1617022787="1" href="https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289" rel="external nofollow">هذا الرابط</a>.
</p>

<p>
	انظر أيضًا <a data-ss1617022787="1" href="https://stackoverflow.com/questions/270947/can-any-one-provide-me-a-sample-of-singleton-in-c/271104#271104" rel="external nofollow">هذه المقالة</a> للحصول على تصميم بسيط لتقييم مُرجأ مع مفردة مضمونة التدمير.
</p>

<p>
	انظر المثال التالي عن مفردة تقليدية ذات تقييم مُرجأ ومُدمَّرة بشكل صحيح.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_23" style="">
<span class="kwd">class</span><span class="pln"> S </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> S</span><span class="pun">&amp;</span><span class="pln"> getInstance</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">static</span><span class="pln"> S instance</span><span class="pun">;</span><span class="pln">    </span><span class="com">// تدميرها مضمون</span><span class="pln">
                        </span><span class="com">// تُستنسخ عند أوّل استخدام</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> instance</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        S</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">            </span><span class="com">// القوسان المعقوصان ضروريان هنا</span><span class="pln">

    </span><span class="com">// C++ 03</span><span class="pln">
    </span><span class="com">// ========</span><span class="pln">
    </span><span class="com">// لا تنس التصريح عن هذين الاثنين، احرص على أن يكونا غير مقبولين</span><span class="pln">
    </span><span class="com">// وإلّا فقد تُنسخ المفردة</span><span class="pln">

    S</span><span class="pun">(</span><span class="pln">S </span><span class="kwd">const</span><span class="pun">&amp;);</span><span class="pln"> </span><span class="com">// لا تنفذها</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">=(</span><span class="pln">S </span><span class="kwd">const</span><span class="pun">&amp;);</span><span class="pln"> </span><span class="com">// لا تنفذها</span><span class="pln">
    </span><span class="com">// C++ 11</span><span class="pln">
    </span><span class="com">// =======</span><span class="pln">
    </span><span class="com">// بإمكاننا أيضا استخدام طريقة حذف التوابع، لكنّنا لن نفعل</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        S</span><span class="pun">(</span><span class="pln">S  </span><span class="kwd">const</span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">S </span><span class="kwd">const</span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<strong>ملاحظة</strong>: ذكر سكوت مايرز (Scott Meyers) في كتابه Effective Modern C++ أن التوابع المحذوفة يجب أن تكون عامة، فذلك يسهل اكتشاف الأخطاء لأن رسائل الخطأ تكون أفضل حينها، فالمصرِّفات تتحقق من قابلية الوصول (accessibility) قبل الحالة المحذوفة.
</p>

<p>
	يمكنك معرفة المزيد عن المتفرّدات من الروابط التالية:
</p>

<ul>
<li>
		توضّح هذه الصفحة متى يجب استخدام نمط المفردة:
		<ul>
<li>
				<a data-ss1617022787="1" href="https://wiki.hsoub.com/Design_Patterns/singleton" rel="external">نمط المفردة Singleton في موسوعة حسوب</a>
			</li>
		</ul>
</li>
	<li>
		راجع هاتين المقالتين الأجنبيتين لمزيد من المعلومات حول ترتيب التهيئة وكيفية التعامل معها:
		<ul>
<li>
				<a data-ss1617022787="1" href="https://stackoverflow.com/questions/211237/static-variables-initialisation-order/211307#211307" rel="external nofollow">Static variables initialisation order</a>
			</li>
			<li>
				<a data-ss1617022787="1" href="https://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems/335746#335746" rel="external nofollow">Finding C++ static initialization order problems</a>
			</li>
		</ul>
</li>
	<li>
		تصف هذه المقالة الأجنبية دورة الحياة لمتغير ساكن في دالة ++C:
		<ul>
<li>
				<a data-ss1617022787="1" href="https://stackoverflow.com/questions/246564/what-is-the-lifetime-of-a-static-variable-in-a-c-function" rel="external nofollow">What is the lifetime of a static variable in a C++ function?</a>‎
			</li>
		</ul>
</li>
	<li>
		تناقش المقالة الأجنبية التالية بعض تأثيرات الخيوط على المفردات:
		<ul>
<li>
				<a data-ss1617022787="1" href="https://stackoverflow.com/questions/449436/singleton-instance-declared-as-static-variable-of-getinstance-method-is-it-thre/449823#449823" rel="external nofollow">Singleton instance declared as static variable of GetInstance method</a>
			</li>
		</ul>
</li>
	<li>
		توضّح هذه المقالة الأجنبية لماذا لن يعمل قفل التحقق المزدوج (double checked locking) في C++‎:
		<ul>
<li>
				‏<a data-ss1617022787="1" href="https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know-a/367690#367690" rel="external nofollow">What are all the common undefined behaviours that a C++ programmer should know about?</a>‎
			</li>
		</ul>
</li>
</ul>
<h3>
	المفردات الساكنة الآمنة من إلغاء التهيئة (Static deinitialization-safe singleton)
</h3>

<p>
	قد تعتمد بعض الكائنات الساكنة (static objects) في بعض الحالات على المفردة، وقد ترغب في ضمان منع تدميرها إلا عند عدم الحاجة إليها. لأجل ذلك يمكن استخدام <code>‎std::shared_ptr‎</code> لمنع تدمير المُتفرّدات وإبقائها متاحة لجميع من يستخدمها حتى عندما تُستدعى المدمّرات الساكنة في نهاية البرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_28" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Singleton</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Singleton</span><span class="pun">(</span><span class="typ">Singleton</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">Singleton</span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">=(</span><span class="typ">Singleton</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">static</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Singleton</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> instance</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Singleton</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> s </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Singleton</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> s</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Singleton</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<strong>ملاحظة</strong>: يظهر هذا المثال <a data-ss1617022787="1" href="https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/40337728#40337728" rel="external nofollow">كإجابة في قسم الأسئلة والأجوبة في موقع SO</a>.
</p>

<h3>
	المفردات الآمنة خيطيًا (Thread-safe Singeton)
</h3>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	يضمن معيار C++‎ 11 أنّ كائنات نطاق الدوالّ (function scope objects) تُهيَّأ بطريقة متزامنة، ويمكن استخدام هذا لتقديم مفردة آمنة خيطيًا مع تهيئة مُرجأة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_30" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">Foo</span><span class="pun">&amp;</span><span class="pln"> instance</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">Foo</span><span class="pln"> inst</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> inst</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="typ">Foo</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Foo</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Foo</span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="pun">=(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Foo</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<h3>
	الأصناف الفرعية (Subclasses)
</h3>

<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_32" style="">
<span class="kwd">class</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">static</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">&amp;</span><span class="pln"> instance</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">virtual</span><span class="pun">~</span><span class="pln"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    </span><span class="kwd">virtual</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> func1</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> func2</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">protected</span><span class="pun">:</span><span class="pln">
        <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
    <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">&amp;</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WindowsAPI</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">virtual</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> func1</span><span class="pun">()</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="com">/* شيفرة ويندوز */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> func2</span><span class="pun">()</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="com">/* شيفرة ويندوز */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LinuxAPI</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">virtual</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> func1</span><span class="pun">()</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="com">/* شيفرة لينكس */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> func2</span><span class="pun">()</span><span class="pln"> override </span><span class="pun">{</span><span class="pln">
        </span><span class="com">/* شيفرة لينكس */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
<abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">&amp;</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">::</span><span class="pln">instance</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="com">#if PLATFORM == WIN32</span><span class="pln">
       </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">WindowsAPI</span><span class="pln"> instance</span><span class="pun">;</span><span class="pln">
   </span><span class="com">#elif</span><span class="pln"> PLATFORM </span><span class="pun">=</span><span class="pln"> LINUX
       </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">LinuxAPI</span><span class="pln"> instance</span><span class="pun">;</span><span class="pln">
  </span><span class="com">#endif</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> instance</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	المُصرّف في هذا المثال يربط الصنف <code>‎<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>‎</code> بالصنف الفرعي المناسب، من أجل الوصول إلى <code>‎<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>‎</code> حتّى لو لم يكن مربوطًا بشيفرة مخصوصة بمنصّة معينة.
</p>

<h2>
	تقنيات إعادة التصميم (Refactoring Techniques)
</h2>

<p>
	يشير مفهوم إعادة البناء (Refactoring) إلى تعديل الشيفرة واستبدال نسخة مُحسّنة بها، ورغم أنّ إعادة البناء تُجرى غالبًا أثناء تغيير الشيفرة بُغية إضافة بعض الميزات أو تصحيح الأخطاء، إلّا أنّ هذا المصطلح مخصوص أساسًا بعمليات تحسين الشيفرة بدون إضافة ميزات أو تصحيح الأخطاء.
</p>

<h3>
	Goto Cleanup
</h3>

<p>
	يُستخدم أحيانًا نمط التصميم <code>‎goto cleanup‎</code> في شيفرات C++‎ التي بُنِيت على شيفرات مكتوبة بلغة C، ونظرًا لأنّ الأمر <code>‎goto‎</code> يصعِّب فهم سير عمل الدوال، فغالبًا ما يُوصى بتجنّبه. ويمكن استبدال تعليمة <code>return</code> أو الحلقات أو الدوال بالأمر <code>‎goto‎</code>. بالمقابل، يتيح استخدام <code>‎goto cleanup‎</code> التخلُّص من منطق التنظيف (cleanup logic).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_34" style="">
<span class="kwd">short</span><span class="pln"> calculate</span><span class="pun">(</span><span class="typ">VectorStr</span><span class="pln"> </span><span class="pun">**</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">short</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> FALSE</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">VectorStr</span><span class="pln"> </span><span class="pun">*</span><span class="pln">vec </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">goto</span><span class="pln"> cleanup</span><span class="pun">;</span><span class="pln"> </span><span class="com">//&lt; return false يمكن أن يُستعاض عنها بـ</span><span class="pln">
    result </span><span class="pun">=</span><span class="pln"> TRUE</span><span class="pun">;</span><span class="pln">
    cleanup</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">delete</span><span class="pun">[]</span><span class="pln"> vec</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في C++‎، يمكنك استخدام تقنية RAII لحلّ هذه المشكلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3797_36" style="">
<span class="kwd">struct</span><span class="pln"> </span><span class="typ">VectorRAII</span><span class="pln"> final </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">VectorStr</span><span class="pln"> </span><span class="pun">*</span><span class="pln">data </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">nullptr</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
    </span><span class="typ">VectorRAII</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln">
        </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">VectorRAII</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">delete</span><span class="pun">[]</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">VectorRAII</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">VectorRAII</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">delete</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">short</span><span class="pln"> calculate</span><span class="pun">(</span><span class="typ">VectorStr</span><span class="pln"> </span><span class="pun">**</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">VectorRAII</span><span class="pln"> vec </span><span class="pun">{};</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> FALSE</span><span class="pun">;</span><span class="pln"> </span><span class="com">//&lt; return false يمكن الاستعاضة عنها بـ</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> TRUE</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بعد هذا، يمكنك الاستمرار في إعادة بناء الشيفرة. مثلًا، عن طريق استبدال <code>‎VectorRAII‎</code> بمؤشّر فريد <code>std::unique_ptr</code> أو متّجه <code>std::vector</code>.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617022787="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 112: Design pattern implementation in C++‎ والفصل Chapter 113: Singleton Design Pattern من كتاب <a data-ss1617022787="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1027</guid><pubDate>Tue, 27 Oct 2020 13:01:00 +0000</pubDate></item><item><title>&#x625;&#x639;&#x627;&#x62F;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x64A;&#x647; &#x627;&#x644;&#x62A;&#x627;&#x645;&#x629; Perfect Forwarding &#x648;&#x62A;&#x642;&#x646;&#x64A;&#x629; &#x645;&#x624;&#x634;&#x631; &#x625;&#x644;&#x649; &#x62A;&#x646;&#x641;&#x64A;&#x630; Pimpl &#x648;&#x627;&#x644;&#x62A;&#x639;&#x628;&#x64A;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x637;&#x648;&#x64A;&#x629; Fold Expressions &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-%D8%A7%D9%84%D8%AA%D8%A7%D9%85%D8%A9-perfect-forwarding-%D9%88%D8%AA%D9%82%D9%86%D9%8A%D8%A9-%D9%85%D8%A4%D8%B4%D8%B1-%D8%A5%D9%84%D9%89-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-pimpl-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A8%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%8A%D8%A9-fold-expressions-%D9%81%D9%8A-cpp-r1026/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/51.png.122535a639a8e673b782268930056fca.png" /></p>

<h2>
	إعادة التوجيه التامة (Perfect Forwarding)
</h2>

<h3>
	الدوالّ المُنتِجة (Factory functions)
</h3>

<p>
	لنفترض أنّنا نرغب في كتابة دالّة منتِجة تقبل قائمة عشوائية من الوسائط، ثمّ تمرّر تلك الوسائط دون تعديل إلى دالّة أخرى. إن دالة <code>‎make_unique‎</code> هي مثال على مثل هذه الدوال، وتُستخدَم لاستنساخ نسخة جديدة من <code>‎T‎</code> بأمان وإعادة مؤشّر فريد <code>‎unique_ptr&lt;T&gt;‎</code> يملك تلك النُسخة.
</p>

<p>
	وتسمح لنا القواعد المتعلقة بالقوالب المتغيّرة (variadic templates) والمراجع اليمينية بكتابة مثل هذه الدالّة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_7" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> T</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">...</span><span class="pln">A </span><span class="pun">&gt;</span><span class="pln">
    unique_ptr</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> make_unique</span><span class="pun">(</span><span class="pln">A</span><span class="pun">&amp;&amp;...</span><span class="pln"> args</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> unique_ptr</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> T</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">forward</span><span class="pun">&lt;</span><span class="pln">A</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">)...));</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يشير استخدام علامات الحذف <code>‎...‎</code> إلى حزمة معاملات تمثّل عددًا عشوائيًا من الأنواع، وسينشر المُصرّف تلك الحزمة إلى العدد الصحيح من الوسائط في موضع الاستدعاء، ثم تُمرَّر تلك الوسائط إلى منشئ <code>‎T‎</code> باستخدام <code>‎std::forward‎</code>. ويُطلَب من هذه الدالّة المحافظة على المؤهّلات المرجعية (ref-qualiﬁers) للوسائط.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_9" style="">
<span class="kwd">struct</span><span class="pln"> foo
</span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    foo</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> foo</span><span class="pun">&amp;)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">    </span><span class="com">// مُنشئ النسخ</span><span class="pln">
    foo</span><span class="pun">(</span><span class="pln">foo</span><span class="pun">&amp;&amp;)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">    </span><span class="com">// مُنشئ النسخ</span><span class="pln">
    foo</span><span class="pun">(</span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
foo f</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> p1 </span><span class="pun">=</span><span class="pln"> make_unique</span><span class="str">&lt;foo&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">f</span><span class="pun">);</span><span class="pln">    </span><span class="com">// foo::foo(const foo&amp;) استدعاء</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> p2 </span><span class="pun">=</span><span class="pln"> make_unique</span><span class="str">&lt;foo&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">move</span><span class="pun">(</span><span class="pln">f</span><span class="pun">));</span><span class="pln">    </span><span class="com">// foo::foo(foo&amp;&amp;) استدعاء</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> p3 </span><span class="pun">=</span><span class="pln"> make_unique</span><span class="str">&lt;foo&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

<h2>
	تقنية مؤشر إلى تنفيذ (Pimpl)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<p>
	في ملف الترويسة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_11" style="">
<span class="com">// widget.h</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;memory&gt;</span><span class="pln">    </span><span class="com">// std::unique_ptr</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">experimental</span><span class="pun">/</span><span class="pln">propagate_const</span><span class="pun">&gt;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Widget</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Widget</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">~</span><span class="typ">Widget</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">DoSomething</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">private</span><span class="pun">:</span><span class="pln">

        </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Impl</span><span class="pun">;</span><span class="pln">    </span><span class="com">// تصريح لاحق</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">experimental</span><span class="pun">::</span><span class="pln">propagate_const</span><span class="pun">&lt;</span><span class="pln">std</span><span class="pun">::</span><span class="pln">unique_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Impl</span><span class="pun">&gt;&gt;</span><span class="pln"> pImpl</span><span class="pun">;</span><span class="pln">      
</span><span class="pun">};</span></pre>

<p>
	في ملف التنفيذ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_13" style="">
<span class="com">// widget.cpp</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">"widget.h"</span><span class="pln">
</span><span class="com">#include</span><span class="pln"> </span><span class="str">"reallycomplextype.h"</span><span class="pln">    </span><span class="com">// widget.h لا حاجة لتضمين هذه الترويسة في</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Widget</span><span class="pun">::</span><span class="typ">Impl</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="com">// widget هنا توضع السمات التي نحتاجها من </span><span class="pln">
    </span><span class="typ">ReallyComplexType</span><span class="pln"> rct</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="typ">Widget</span><span class="pun">::</span><span class="typ">Widget</span><span class="pun">():</span><span class="pln">
    pImpl</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">make_unique</span><span class="pun">&lt;</span><span class="typ">Impl</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">())</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="typ">Widget</span><span class="pun">::~</span><span class="typ">Widget</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Widget</span><span class="pun">::</span><span class="typ">DoSomething</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="com">// pImpl افعل شيئا هنا بـ</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحتوي <code>‎pImpl‎</code> على حالة <code>‎Widget‎</code> (أو بعضها)، ويمكن تجنّب كشف وصف <code>‎Widget‎</code> في الترويسة، وجعله داخل التنفيذ.
</p>

<p>
	<code>‎pImpl‎</code> هي اختصار لـ "pointer to implementation" (مؤشّر إلى تنفيذ)، أما التنفيذ "الحقيقي" لـ <code>‎Widget‎</code> موجود في <code>‎pImpl‎</code>.
</p>

<p>
	<strong>تنبيه</strong>: لاحظ أنّه لكي يعمل هذا مع مؤشّر حصري (<code>‎unique_ptr‎</code>)، يجب تنفيذ <code>‎~Widget()‎</code> في موضع من الملف حيث يكون <code>‎Impl‎</code> مرئيًا بالكامل، يمكنك تحديد الإعداد الافتراضي هناك، لكن إن حدّدت الإعداد الافتراضي في موضع لم تكن فيه <code>‎Impl‎</code> مُعرّفة، فقد يؤدّي ذلك إلى عطب في البرنامج.
</p>

<h2>
	التعبيرات المطوية (Fold Expressions)
</h2>

<h3>
	الطي الأحادي (Unary Folds)
</h3>

<p>
	يُستخدَم الطيّ الأحادي (Unary fold) لطيّ حزم المعامِلات (parameter packs) الخاصّة بعامل (operator) محدّد، وهناك نوعان من معاملات الطيّ الأحادية:
</p>

<ul>
<li>
		الطي الأحادي اليساري - Unary Left Fold -‏ <code>‎(... op pack)‎</code>، والذي يُوسَّع على النحو التالي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_15" style="">
<span class="pun">((</span><span class="typ">Pack1</span><span class="pln"> op </span><span class="typ">Pack2</span><span class="pun">)</span><span class="pln"> op</span><span class="pun">...)</span><span class="pln"> op </span><span class="typ">PackN</span></pre>

<p>
	*الطي الأحادي اليميني - Unary Right Fold -‏ <code>‎(pack op ...)‎</code>، والذي يُوسَّع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_17" style="">
<span class="typ">Pack1</span><span class="pln"> op</span><span class="pun">(...(</span><span class="typ">Pack</span><span class="pun">(</span><span class="pln">N </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> op </span><span class="typ">PackN</span><span class="pun">))</span></pre>

<p>
	انظر المثال التالي
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_19" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pun">...</span><span class="typ">Ts</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> sum</span><span class="pun">(</span><span class="typ">Ts</span><span class="pun">...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(...+</span><span class="pln">args</span><span class="pun">);</span><span class="pln"> </span><span class="com">// طيّ أحادي يساري</span><span class="pln">
        </span><span class="com">//return (args + ...); // طيّ أحادي يمينيّ</span><span class="pln">
        </span><span class="com">// associative سيكونان متكافئين إن كان المعامل تجميعيًا</span><span class="pln">
        </span><span class="com">// For +, ((1+2)+3) (left fold) == (1+(2+3)) (right fold)</span><span class="pln">
        </span><span class="com">// For -, ((1-2)-3) (left fold) != (1-(2-3)) (right fold)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  6</span></pre>

<h3>
	الطيّ الثنائي أو البتي (Binary Fold)
</h3>

<p>
	الطيّ الثنائي، أو البتّي (Binary Fold) هو طيّ أحادي بالأساس، لكن مع وسيط إضافي. وينقسم إلى نوعين:
</p>

<ul>
<li>
		الطيات البتية اليسارية - Binary Left Fold -‏ <code>‎(value op ... op pack)‎</code>، والتي تُوسَّع على النحو التالي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_21" style="">
<span class="pun">(((</span><span class="typ">Value</span><span class="pln"> op </span><span class="typ">Pack1</span><span class="pun">)</span><span class="pln"> op </span><span class="typ">Pack2</span><span class="pun">)</span><span class="pln"> op</span><span class="pun">...)</span><span class="pln"> op </span><span class="typ">PackN</span></pre>

<ul>
<li>
		الطيات البتّية اليمينية (Binary Right Folds)‏ <code>‎(pack op ... op value)‎</code>، والتي تُوسَّع كما يلي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_23" style="">
<span class="typ">Pack1</span><span class="pln"> op</span><span class="pun">(...</span><span class="pln">op</span><span class="pun">(</span><span class="typ">Pack</span><span class="pun">(</span><span class="pln">N </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> op</span><span class="pun">(</span><span class="typ">PackN</span><span class="pln"> op </span><span class="typ">Value</span><span class="pun">)))</span></pre>

<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_25" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pun">...</span><span class="typ">Ts</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> removeFrom</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> num</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Ts</span><span class="pun">...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">num </span><span class="pun">-</span><span class="pln"> </span><span class="pun">...-</span><span class="pln">args</span><span class="pun">);</span><span class="pln"> </span><span class="com">// طية يسرى ثنائية</span><span class="pln">
        </span><span class="com">// لاحظ أنّه لا يمكن استخدام عامل طيّ ثنائي يميني</span><span class="pln">
        </span><span class="com">// نظرًا لأنّ العامل غير تجميعي</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> removeFrom</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</span><span class="pun">);</span><span class="pln"> </span><span class="com">// =&gt; 1000 - 5 - 10 - 15 = 970</span></pre>

<h3>
	طيّ الفاصلة (Folding over a comma)
</h3>

<p>
	قد ترغب أحيانًا في تطبيق دالّة معيّنة على كل عنصر من عناصر حزمة من المُعاملات. وأفضل حلّ لذلك في C++‎ 11 هو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_27" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">...</span><span class="typ">Ts</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> print_all</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">ostream</span><span class="pun">&amp;</span><span class="pln"> os</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Ts</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;...</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">using</span><span class="pln"> expander </span><span class="pun">=</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[];</span><span class="pln">
        </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> expander </span><span class="pun">{</span><span class="pln">
            </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">(</span><span class="pln">os </span><span class="pun">&lt;&lt;</span><span class="pln"> args</span><span class="pun">),</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)...</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	ويصبح الأمر أسهل مع طي التعبيرات بحيث لا نحتاج إلى الشيفرات المتداولة (boilerplates) المبهمة، انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9842_29" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">...</span><span class="typ">Ts</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
    </span><span class="kwd">void</span><span class="pln"> print_all</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">ostream</span><span class="pun">&amp;</span><span class="pln"> os</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Ts</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;...</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">(</span><span class="pln">os </span><span class="pun">&lt;&lt;</span><span class="pln"> args</span><span class="pun">),</span><span class="pln"> </span><span class="pun">...);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	ترجمة -بتصرّف- للفصول Chapter 101: Perfect Forwarding و Chapter 107: Pimpl Idiom و Chapter 110: Fold Expressions من كتاب <a data-ss1617022788="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1026</guid><pubDate>Sat, 24 Oct 2020 13:02:00 +0000</pubDate></item><item><title>&#x625;&#x647;&#x645;&#x627;&#x644; &#x627;&#x644;&#x646;&#x633;&#x62E; copy elision &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A5%D9%87%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-copy-elision-%D9%81%D9%8A-cpp-r1025/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/50.png.855712fe5ac69e8bcdca1c2e217b2665.png" /></p>

<p>
	ينصّ المعيار على ضرورة نسخ الكائنات أو نقلها في بعض المواضع من أجل تهيئتها، وإهمال النسخ (Copy elision) الذي يسمى أحيانًا تحسين القيمة المُعادة (return value optimization) هو تحسينٌ يسمح للمُصرِّف بتجنّب النسخ أو النقل في ظروف معيّنة، حتى لو كان المعيار ينصّ على ذلك. انظر الدالة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_7" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string get_string</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">(</span><span class="str">"I am a string."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ووفقًا للمعيار فينبغي أن تُهيِّئ هذه الدالّة سلسلةً نصيةً <code>‎std::string‎</code> مؤقتة، ثم تنسخها أو تنقلها إلى الكائن المُعاد، ثم تدمّر السلسلة النصية المؤقتة، والمعيار واضح جدًا بخصوص كيفية تأويل الشيفرة.
</p>

<p>
	و"إهمال النسخ" قاعدة تسمح لمصرّف C++‎ بتجاهل إنشاء النسخة المؤقتة ثمّ نسخها وتدميرها لاحقًا، وهذا يعني أنّ االمُصرِّف يمكن أن يأخذ تعبير التهيئة (initializing expression) الخاص بالكائن المؤقت ويهيّئ القيمة المُعادة من الدالة منه مباشرة. وهذا أفضل أداءً.
</p>

<p>
	لكن رغم هذا فإن له تأثيران واضحان على المستخدم:
</p>

<ol>
<li>
		يجب أن يحتوي النوع على منشئ النسخ / النقل الذي سيُستدعى، فيجب أن يكون النوع قادرًا على النسخ أو النقل حتى لو إهمال المُصرِّف النسخ / النقل.
	</li>
	<li>
		الآثار الجانبية لمُنشئات النسخ / النقل غير مضمونة في الظروف التي يمكن أن يحدث فيها التـَّرك، انظر المثال التالي:
	</li>
</ol>
<p>
	<strong>الإصدار ≥ C++‎ 11</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_9" style="">
<span class="kwd">struct</span><span class="pln"> my_type </span><span class="pun">{</span><span class="pln">
    my_type</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln">
        </span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
    my_type</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> my_type </span><span class="pun">&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Copying\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    my_type</span><span class="pun">(</span><span class="pln">my_type </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Moving\n"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
my_type func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> my_type</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وستكون نتيجة استدعاء <code>func</code> هي عدم طباعة "Copying" أبدًا بما أن العنصرَ المؤقتَ قيمةٌ يُمنى (rvalue) والنوع <code>‎my_type‎</code> قابل للنقل (moveable type). فهل ستُطبَع إذن العبارة "Moving"؟
</p>

<p>
	في الواقع، وبدون قاعدة إهمال النسخ، فإن هذا سيكون مطلوبًا دومًا لطباعة "Moving"، لكن في ظل وجود قاعدة إهمال النسخ فقد يُستدعى مُنشئ النقل (move constructor) أو لا، فالأمر يتعلّق بالتنفيذ (implementation-dependent)، وعليه لا تستطيع الاعتماد على استدعاء منشئ النسخ / النقل في السياقات التي يكون فيها إهمال النوع ممكنًا.
</p>

<p>
	ونظرًا لأنّ الهدف من إهمال النوع هو تحسين الأداء، فقد لا يدعم المصرّف الإهمال في جميع الحالات، ويجب أن يدعم النوع العملية المتروكة بغض النظر عمّا إذا كان المُصرِّف سيفعّل قاعدة الإهمال في حالة معيّنة أم لا، لهذا يجب على النوع أن يحتوي مُنشئ نسخ حتّى في حال إهمال إنشاء النسخ (copy construction)، رغم أنّه لن يُستدعى على أي حال.
</p>

<h2>
	"إهمال النسخ" المضمون (Guaranteed copy elision)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 17</strong>
</p>

<p>
	ذكرنا من قبل أن الهدف من الإهمال (elision) هو التحسين، ورغم أنّ كل المُصرِّفات تقريبًا تدعم إهمال النسخ في الحالات البسيطة، إلا أنّ إهمال النسخ لا يزال يمثّل عبئًا خاصًا على المستخدمين. فمثلًا لا يزال على النوع الذي سيُإهمال نسخه أو نقله أن يحتوي على عملية النسخ أو النقل التي ستُإهمال. مثلّا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_11" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">mutex a_mutex</span><span class="pun">;</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">lock_guard </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">mutex </span><span class="pun">&gt;</span><span class="pln"> get_lock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">lock_guard </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">mutex </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a_mutex</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	قد يكون ذلك مفيدًا في الحالات التي يكون فيها <code>‎a_mutex‎</code> كائنَ مزامنة ممسوك بشكل مخصوص (privately held) من قِبل نظام مُعيّن، لكن قد يرغب مستخدم خارجيً في تأمين قفل نطاقي (scoped lock) عليه.
</p>

<p>
	لكن هذا غير جائز أيضًا، إذ لا يمكن نسخ أو نقل <code>‎std::lock_guard‎</code>، وعلى الرغم من أنّ كل مصرّفات C++‎ تقريبًا ستهمل النسخ أو النقل، إلّا أنّ المعيار يستوجب أن يحتوي النوع على هذه العملية. كذلك فإن الإصدار C++17 يفرض إجراء عملية الإهمال عبر إعادة تعريف معاني بعض التعبيرات لمنع إجراء أي عملية نسخ أو نقل، انظر الشيفرة أعلاه.
</p>

<p>
	وكانت هذه الشيفرة تنص قبل C++‎ 17 على إنشاء كائن مؤقت ثم استخدامه لإجراء عملية النسخ أو النقل إلى القيمة المُعادة، لكن يمكن إهمال نسخ الكائن المؤقت، أمّا في C++‎ 17 فلن يُنشأ كائن مؤقت على الإطلاق.
</p>

<p>
	في C++‎ 17، عند استخدام تعبير يميني (prvalue) لتهيئة كائن من نفس نوع التعبير، فلن يُنشئ كائنًا مؤقتًا، بل سيهيّئ التعبير ذلك الكائن مباشرة، وفي حال إعادة قيمة من نفس نوع القيمة المُعادة فلن تحتاج إلى كتابة مُنشئ نسخ أو نقل، ومن ثم فيمكن أن تعمل الشيفرة أعلاه بدون مشاكل، بموجب قواعد C++‎ 17.
</p>

<p>
	ومعلوم أن قواعد C++‎ 17 مناسبة في الحالات التي يتطابق فيها نوع القيمة اليمينية الخالصة (prvalue) مع النوع الذي تتم تهيئته، لذا وبالنظر إلى الدالة <code>‎get_lock‎</code> أعلاه، لن تكون هناك حاجة إلى النقل أو النسخ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_15" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">lock_guard the_lock </span><span class="pun">=</span><span class="pln"> get_lock</span><span class="pun">();</span></pre>

<p>
	ونظرًا لأنّ الدالة <code>‎get_lock‎</code> تعيد <a data-ss1617022789="1" href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D8%AF%D8%B1%D8%B3-40-%D9%81%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%8A%D9%85-value-categories-%D9%81%D9%8A-cpp-r969/" rel="">تعبيرًا يمينيا خالصًا</a> (prvalue) يُستخدم لتهيئة كائن من نفس النوع، فلن يحدث أي نسخ أو نقل. واعلم أن هذا التعبير لن ينشئ كائنًا مؤقتًا؛ بل سيُستخدَم لتهيئة <code>‎the_lock‎</code> مباشرة. كما لا يوجد أيّ إهمال لأنّه لا يوجد نسخ / نقل ليُهمل من الأساس.
</p>

<p>
	وبناء عليه يكون مصطلح "إهمال النسخ المضمون" (guaranteed copy elision) إذًا تسمية خاطئة، ولكنّه الاسم المُقترح في <a data-ss1617022789="1" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html" rel="external nofollow">معيار C++‎ 17</a>، وهو لا يضمن الإهمال أبدًا، وإنّما يلغي النسخ أو النقل بالكلية ويعيد تعريف C++‎ بحيث لا يكن هناك نسخ أو نقل لكَي يُترَك من الأساس. لا تعمل هذه الميزة إلا في الحالات التي تتضمن تعبيرًا يمينيًا خالصًا (prvalue).
</p>

<p>
	وعلى هذا النحو، ستستخدم هذه الشيفرة قواعد الإهمال المعتادة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_17" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">mutex a_mutex</span><span class="pun">;</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">lock_guard </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">mutex </span><span class="pun">&gt;</span><span class="pln"> get_lock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">lock_guard </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">mutex </span><span class="pun">&gt;</span><span class="pln"> my_lock</span><span class="pun">(</span><span class="pln">a_mutex</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// افعل شيئًا ما</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> my_lock</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	صحيح أنّ هذه الحالة صالحة لإهمال النسخ، إلّا إنّ قواعد C++‎ 17 لا توقف النسخ أو النقل في هذه الحالة، وعليه فيجب أن يكون للنوع مُنشئ نسخ/نقل لاستخدامه لتهيئة القيمة المُعادة. وبما أنّ <code>‎lock_guard‎</code> لا تحتوي على ذلك، فسيحدث خطأ في التصريف.
</p>

<p>
	ويُسمح للتنفيذات (Implementations) برفض إهمال النسخ عند تمرير أو إعادة كائن من نوع قابل للنسخ، والغرض من هذا هو السماح بنقل مثل تلك الكائنات في السجلات (registers)، التي قد تفرضها بعض واجهات التطبيقات الثنائيّة (Application binary interface أو اختصارًا ABIs) لأجل استدعائها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_19" style="">
<span class="kwd">struct</span><span class="pln"> trivially_copyable </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> foo</span><span class="pun">(</span><span class="pln">trivially_copyable a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
foo</span><span class="pun">(</span><span class="pln">trivially_copyable </span><span class="pun">{});</span><span class="pln"> </span><span class="com">// إهمال النسخ ليس إجباريًا</span></pre>

<h2>
	إهمال المُعاملات (Parameter elision)
</h2>

<p>
	إذا مُرِّر وسيط إلى دالة وكان الوسيط تعبيرًا يمينيًا خالصًا (prvalue expression) لنوع مُعامل الدالّة ولم يكن مرجعًا، فيمكن إهمال إنشاء القيمة اليمينيّة الخالصة (prvalue).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_21" style="">
<span class="kwd">void</span><span class="pln"> func</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
func</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">));</span></pre>

<p>
	في الشيفرة أعلاه، ننشئ سلسلة نصية <code>‎string‎</code> مؤقتة ثم ننقلها إلى مُعامل الدالة <code>‎str‎</code>، ويسمح إهمال النسخ لهذا التعبير بإنشاء الكائن مباشرة في <code>‎str‎</code>، بدلًا من استخدام نقل كائن مؤقت (temporary+move)، وهذا مفيد في تحسين الحالات التي يُصرَّح فيها عن مُنشئ صريح <code>‎explicit‎</code>.
</p>

<p>
	على سبيل المثال، كان بإمكاننا كتابة ما ورد أعلاه على هيئة <code>‎func("foo")‎</code>، ذلك أنّ [السلاسل النصية](رابط الفصل 47) تحتوي على مُنشئ ضمني (implicit) يقوم بالتحويل من <code>*‎const‎ ‎char‎</code> إلى <code>string</code>، ولو كان ذلك المُنشئ صريحًا <code>‎explicit‎</code>، لكان علينا استخدام كائن مؤقت لاستدعاء المُنشئ الصريح. ومن هذا نرى أن إهمال النسخ يُعفينا من القيام بعمليّات نسخ / نقل لا داعي لها.
</p>

<h2>
	إهمال القيمة المعادة (Return value elision)
</h2>

<p>
	إذا أعيد تعبير يميني خالص (prvalue) من دالة وكان نوع ذلك التعبير مساويًا لنوع القيمة المُعادة من الدالّة، فيمكن إهمال النسخ من القيمة اليمينيّة الخالصة المؤقتة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_23" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستهمل جميع المُصرِّفات تقريبًا إنشاء الكائن المؤقت في هذه الحالة.
</p>

<h2>
	إهمال القيمة المُعادة المُسمّاة (Named return value elision)
</h2>

<p>
	إذا أعيد تعبير قيمة يساري (lvalue expression) من دالّة، وكانت تلك القيمة:
</p>

<ul>
<li>
		تمثل متغيّرًا تلقائيًا محليًا لتلك الدالّة، والذي سيُدمَّر بعد <code>‎return‎</code>،
	</li>
	<li>
		ولم يكن المتغيّر التلقائي مُعامل دالة،
	</li>
	<li>
		ونوع المتغيّر يساوي نوع القيمة المُعادة من الدالّة.
	</li>
</ul>
<p>
	ففي مثل هذه الحالة، يمكن إهمال النسخ / النقل من القيمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_25" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string str</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// افعل شيئا ما</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> str</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك حالات أخرى أكثر تعقيدًا مؤهّلة لقاعدة الإهمال، ولكن كلّما زاد التعقيد، قلّت احتمالية قيام المُصرِّف بتنفيذ قاعدة الإهمال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_31" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string ret</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">some_condition</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"bar"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> ret</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بإمكان المُصرِّف أن يهمل <code>‎ret‎</code>، لكنّ ذلك مستبعد. أيضًا، وكما ذكرنا سابقًا، فلا يُسمح بالإهمال بالنسبة لمُعاملات القيمة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_29" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string func</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    str</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// افعل شيئًا ما</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> str</span><span class="pun">;</span><span class="pln"> </span><span class="com">// الإهمال غير ممكن</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	إهمال التهيئة الاستنساخية (Copy initialization elision)
</h2>

<p>
	إذا كنت تستخدم تعبيرًا يمينيًا خالصًا (prvalue) لتهيئة متغيّر استنساخيًا (copy initialize)، وكان نوع ذلك المتغيّر يساوي نوع التعبير اليميني الخالص (prvalue)، فيمكن حينئذٍ إهمال النسخ.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_27" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string str </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span></pre>

<p>
	التهيئة النسخية تحول السطر أعلاه إلى <code>‎std::string str("foo");‎</code> مع بعض الاختلافات الطفيفة. ويحدث نفس الشيء مع القيم المعادة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1056_33" style="">
<span class="pln">std</span><span class="pun">::</span><span class="pln">string func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">string str </span><span class="pun">=</span><span class="pln"> func</span><span class="pun">();</span></pre>

<p>
	ستستدعي الشيفرة أعلاه منشئ النقل الخاص بالسلاسل النصية مرّتين إذا لم تُطبَّق قاعدة إهمال النسخ، أمّا إذا طُبِّقت فسيُستدعى مرّة واحدة على الأكثر، وستختار معظم المُصرِّفات الخيار الأخير.
</p>

<p>
	هذا الدرس جزء من <a data-ss1617022789="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Chapter 109: Copy Elision من الكتاب <a data-ss1617022789="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1025</guid><pubDate>Thu, 22 Oct 2020 13:02:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x633;&#x62A;&#x646;&#x62A;&#x627;&#x62C; &#x627;&#x644;&#x62A;&#x644;&#x642;&#x627;&#x626;&#x64A; &#x644;&#x646;&#x648;&#x639; &#x645;&#x62A;&#x63A;&#x64A;&#x631; &#x639;&#x628;&#x631; auto &#x641;&#x64A; Cpp</title><link>https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%86%D8%AA%D8%A7%D8%AC-%D8%A7%D9%84%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A-%D9%84%D9%86%D9%88%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1-%D8%B9%D8%A8%D8%B1-auto-%D9%81%D9%8A-cpp-r1024/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/49.png.23c31d118f808df61cc6727eea68c62c.png" /></p>

<p>
	تمكّن الكلمة المفتاحية <code>‎auto‎</code> من الاستنتاج التلقائي لنوع متغيّر معيّن، وهي مناسبة بشكل خاص عند التعامل مع أسماء الأنواع الطويلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_7" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">map</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Widget</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> table</span><span class="pun">;</span><span class="pln">
</span><span class="com">// C++98</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">map</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">shared_ptr </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Widget</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">::</span><span class="typ">iterator</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> table</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="str">"42"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// C++11/14/17</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> j </span><span class="pun">=</span><span class="pln"> table</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="str">"42"</span><span class="pun">);</span></pre>

<p>
	يمكن استخدامها مع حلقات <code>for</code> النطاقية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_9" style="">
<span class="typ">vector</span><span class="pln"> v </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">};</span><span class="pln"> 
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">auto</span><span class="pln"> n</span><span class="pun">:</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> n </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">' '</span><span class="pun">;</span></pre>

<p>
	أو مع تعبيرات لامدا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_11" style="">
<span class="kwd">auto</span><span class="pln"> f </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"lambda\n"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
f</span><span class="pun">();</span></pre>

<p>
	أو يمكن استخدامها لتجنّب تكرار النوع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_13" style="">
<span class="kwd">auto</span><span class="pln"> w </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">make_shared </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Widget</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">();</span></pre>

<p>
	أو لتجنّب عمليّات النسخ غير الضرورية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_15" style="">
<span class="kwd">auto</span><span class="pln"> myMap </span><span class="pun">=</span><span class="pln"> std</span><span class="pun">::</span><span class="typ">map</span><span class="pun">&lt;</span><span class="typ">int</span><span class="pun">,</span><span class="typ">float</span><span class="pun">&gt;();</span><span class="pln">
myMap</span><span class="pun">.</span><span class="pln">emplace</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">3.14</span><span class="pun">);</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">pair</span><span class="pun">&lt;</span><span class="typ">int</span><span class="pun">,</span><span class="typ">float</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;</span><span class="pln"> firstPair2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">myMap</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">();</span><span class="pln"> </span><span class="com">// ! نسخ</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> </span><span class="kwd">const</span><span class="pun">&amp;</span><span class="pln"> firstPair </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">myMap</span><span class="pun">.</span><span class="pln">begin</span><span class="pun">();</span><span class="pln">  </span><span class="com">// !لا تنسخ</span></pre>

<p>
	سبب إجراء عمليّة النسخ في الشيفرة أعلاه يعود إلى أنّ النوع المُعاد هو <code>‎std::pair&lt;const int,float&gt;‎</code>!
</p>

<h2>
	تعبيرات لامدا العامة (C++‎ 14)
</h2>

<p>
	<strong>الإصدار ≥ C++‎ 14</strong>
</p>

<p>
	تسمح C++‎ 14 باستخدام الكلمة المفتاحية <code>‎auto‎</code> في وسيط لامدا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_17" style="">
<span class="kwd">auto</span><span class="pln"> print </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[](</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">&amp;</span><span class="pln"> arg</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> arg </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
print</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
print</span><span class="pun">(</span><span class="str">"hello world"</span><span class="pun">);</span></pre>

<p>
	ويكافئ تعبير لامدا في الغالب الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_19" style="">
<span class="kwd">struct</span><span class="pln"> lambda </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> T </span><span class="pun">&gt;</span><span class="pln">
        </span><span class="kwd">auto</span><span class="pln"> </span><span class="kwd">operator</span><span class="pln"> </span><span class="pun">()(</span><span class="kwd">const</span><span class="pln"> T</span><span class="pun">&amp;</span><span class="pln"> arg</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> arg </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ثمّ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_21" style="">
<span class="pln">lambda print</span><span class="pun">;</span><span class="pln">
print</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
print</span><span class="pun">(</span><span class="str">"hello world"</span><span class="pun">);</span></pre>

<h2>
	كائنات auto و proxy
</h2>

<p>
	في بعض الأحيان، قد لا تتصرّف الكلمة المفتاحية <code>‎auto‎</code> كما هو متوقّع، فقد تسعى إلى استنتاج نوع التعبير حتى عندما لا يكون استنتاج النوع مطلوبًا، كما هو الحال عند استخدام الكائنات الوكيلة (proxy objects) في الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_23" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> flags</span><span class="pun">{</span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">};</span><span class="pln"> 
</span><span class="kwd">auto</span><span class="pln"> flag </span><span class="pun">=</span><span class="pln"> flags</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
flags</span><span class="pun">.</span><span class="pln">push_back</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span></pre>

<p>
	في الشيفرة أعلاه، ليست <code>‎flag‎</code> من النوع <code>‎bool‎</code>، بل من النوع <code>‎std::vector&lt;bool&gt;::reference‎</code>، ففي تخصيص النوع <code>‎bool‎</code> للقالب <code>‎vector‎</code>، يعيد المعامل <code>‎operator []‎</code> كائنًا وكيلًا مع مُعامل التحويل <code>‎operator bool‎</code> المُحدَّد.
</p>

<p>
	وعندما يعدّل التابع <code>‎flags.push_back(true)‎</code> الحاويةَ، فقد يصبح المرجع مُعلّقًا (dangling)، أي يشير إلى عنصر لم يعد موجودًا. كما أنّه سيجعل الموقف التالي ممكنًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_25" style="">
<span class="kwd">void</span><span class="pln"> foo</span><span class="pun">(</span><span class="kwd">bool</span><span class="pln"> b</span><span class="pun">);</span><span class="pln">
std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> getFlags</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> flag </span><span class="pun">=</span><span class="pln"> getFlags</span><span class="pun">()[</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
foo</span><span class="pun">(</span><span class="pln">flag</span><span class="pun">);</span></pre>

<p>
	تُتجَاهل <code>‎vector‎</code> على الفور، لذلك ستكون <code>‎flag‎</code> مرجعًا زائفًا (pseudo-reference) يشير إلى عنصر تمّ تجاهله. وسيؤدّي استدعاء <code>‎foo‎</code> إلى سلوك غير محدّد.
</p>

<p>
	يمكنك التصريح في مثل هذه الحالات عن متغيّر باستخدام <code>‎auto‎</code>، ثمّ تهيئته عبر تحويله إلى النوع الذي تريد استنتاجه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_27" style="">
<span class="kwd">auto</span><span class="pln"> flag </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">static_cast</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">getFlags</span><span class="pun">()[</span><span class="lit">5</span><span class="pun">]);</span></pre>

<p>
	لكن قد يكون من الأفضل إحلال <code>‎bool‎</code> مكان <code>‎auto‎</code>.
</p>

<p>
	إحدى الحالات الأخرى التي قد تتسبّب الكائنات الوكيلة فيها بمشاكل هي قوالب التعبير (expression templates)، ففي هذه الحالة لا تكون القوالب مُصمّمة لتستمر إلى ما بعد التعبير الكامل الحالي، وذلك لأجل تحسين الكفاءة، لذا فإنّ استخدام الكائن الوكيل في هذه الحالة قد يؤدّي إلى سلوك غير معرَّف.
</p>

<h2>
	auto و قوالب التعبير
</h2>

<p>
	يمكن أن تتسبب <code>‎auto‎</code> بمشاكل في حال استخدامها مع قوالب التعبير:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_29" style="">
<span class="kwd">auto</span><span class="pln"> mult</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> c </span><span class="pun">*</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">valarray</span><span class="pun">{</span><span class="lit">1</span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> v </span><span class="pun">=</span><span class="pln"> mult</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> v</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span></pre>

<p>
	والسبب في ذلك هو أنّ استخدام المعامل <code>‎operator*‎</code> على <code>‎valarray‎</code> سيمنحك كائنًا وكيلًا يشير إلى <code>‎valarray‎</code>، وذلك كوسيلة للتقييم المُرجَأ (lazy evaluation). كذلك فإن استخدام <code>‎auto‎</code> سيؤدّي إلى إنشِاء مرجع مُعلّق (dangling reference)، وسيُعاد النوع <code>std::valarray&lt;int&gt;‎</code> بدلًا من <code>‎mult‎</code>، وتطبع الشيفرة القيمة 3.
</p>

<h2>
	auto و const والمراجع
</h2>

<p>
	تمثل الكلمة المفتاحية <code>‎auto‎</code> بحدّ ذاتها نوعًا من القيم، مثل <code>‎int‎</code> أو <code>‎char‎</code>، ويمكن تعديلها باستخدام الكلمة المفتاحية <code>‎const‎</code> والرمز <code>‎&amp;‎</code> لتمثيل نوع ثابت أو نوع مرجعي (reference type) على التوالي، كما يمكن دمج هذه المعدِّلات معًا.
</p>

<p>
	تمثل تمثّل <code>‎s‎</code> في هذا المثال، نوع قيمة - value type - (سيُستنتَج نوعها بأنّه سلسلة نصّية <code>‎std::string‎</code>)، ومن ثم فإنّ كل تكرار للحلقة <code>‎for‎</code> سَينسخ سلسلة نصية من [المتجهة](رابط الفصل 49) إلى <code>‎s‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_31" style="">
<span class="pln">std</span><span class="pun">::</span><span class="typ">vector</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">string </span><span class="pun">&gt;</span><span class="pln"> strings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"stuff"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"things"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"misc"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">auto</span><span class="pln"> s</span><span class="pun">:</span><span class="pln"> strings</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> s </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا عدَّل جسمُ حلقة التكرار العنصر <code>‎s‎</code> (مثلًا عبر استدعاء <code>‎s.append(" and stuff")‎</code>)، فلن تُعدَّل إلّا هذه النسخة فقط، وليس العضو الأصلي في المتجهة <code>‎strings‎</code>.
</p>

<p>
	من ناحية أخرى، فإن صُرِّح عن السلسلة النصّية <code>‎s‎</code> عبر <code>‎auto&amp;‎</code>، فستكون نوعًا مرجعيًا - reference type - (يُستنتج على أنّه <code>‎std::string&amp;‎</code>)، لذا، ففي كلّ تكرار للحلقة سيُسند إليها مرجع يشير إلى سلسلة نصية في المتجهة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_33" style="">
<span class="kwd">for</span><span class="pun">(</span><span class="kwd">auto</span><span class="pun">&amp;</span><span class="pln"> s </span><span class="pun">:</span><span class="pln"> strings</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> s </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستؤثّر التعديلات على <code>‎s‎</code> في جسم هذه الحلقة مباشرةً على العنصر الذي تشير إليه من المتجهة <code>‎strings‎</code>. وأخيرًا، فإن صُرِِّح عن<code>‎s‎</code> عبر <code>‎const auto&amp;‎</code>، فستكون نوعًا مرجعيًا ثابتًا (const reference type)، ممّا يعني أنّه في كل تكرار من الحلقة، سيُسنَد إليها مرجع ثابت يشير إلى سلسلة نصّية في المتجهة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_35" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">&amp;</span><span class="pln"> s</span><span class="pun">:</span><span class="pln"> strings</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">cout </span><span class="pun">&lt;&lt;</span><span class="pln"> s </span><span class="pun">&lt;&lt;</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا يمكن تعديل <code>‎s‎</code> (أي لا يمكن استدعاء توابع غير ثابتة عليها) داخل جسم هذه الحلقة. كذلك يُستحسن عند استخدام <code>‎auto‎</code> مع حلقات <code>‎for‎</code> النطاقية، أن تُستخدم <code>‎const auto&amp;‎</code> إذا كان متن الحلقة لن يعدّلَ البنية التي يُجري عليها التكرار، لأنّ هذا سيُجنّبك عمليات النسخ غير الضرورية.
</p>

<h2>
	نوع الإعادة الزائد Trailing return type
</h2>

<p>
	تُستخدَم <code>‎auto‎</code> في صياغة النوع المُعاد الزائد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_37" style="">
<span class="kwd">auto</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	تكافئ الشيفرة أعلاه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_39" style="">
<span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	يمكن استعمال <code>‎auto‎</code> مع <code>‎decltype‎</code> لاستخدام المُعاملات بدلًا من <code>‎std::declval&lt;T&gt;‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1934_41" style="">
<span class="kwd">template</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">typename</span><span class="pln"> T1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">typename</span><span class="pln"> T2</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">auto</span><span class="pln"> </span><span class="typ">Add</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> T1</span><span class="pun">&amp;</span><span class="pln"> lhs</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> T2</span><span class="pun">&amp;</span><span class="pln"> rhs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">decltype</span><span class="pun">(</span><span class="pln">lhs </span><span class="pun">+</span><span class="pln"> rhs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> lhs </span><span class="pun">+</span><span class="pln"> rhs</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	هذا الدرس جزء من <a data-ss1617022790="1" href="https://academy.hsoub.com/tags/%D8%B3%D9%84%D8%B3%D9%84%D8%A9%20++c%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">سلسلة دروس عن C++‎</a>.
</p>

<p>
	ترجمة -بتصرّف- للفصل Chapter 108: auto من كتاب <a data-ss1617022790="1" href="https://goalkickercom/CPlusPlusBook/CPlusPlusNotesForProfessionals.pdf" rel="external nofollow">C++ Notes for Professionals</a>
</p>
]]></description><guid isPermaLink="false">1024</guid><pubDate>Tue, 20 Oct 2020 13:09:00 +0000</pubDate></item></channel></rss>
