بمجرد أن تمت مقابلتي لشغل منصب C ++ Developer في مكتب واحد لائق وحتى معروف. سبق لي أن اكتسبت بعض الخبرة ، حتى أني كنت أعتبر مطورًا رائدًا في صاحب العمل في ذلك الوقت. لكن عندما سئل عما إذا كنت أعرف أشياء مثل DRY ، KISS ، YAGNI ، NIH ، كان علي الإجابة "لا" مرارًا وتكرارًا.
لقد فشلت فشلا ذريعا ، بالطبع. ولكن بعد ذلك تم اختصار الاختصارات أعلاه وتذكرها. عندما قرأت مقالات وكتبًا مواضيعية ، وأعدت للمقابلات ، وتحدثت للتو مع زملائي ، تعلمت المزيد من الأشياء الجديدة ، ونسيتها ، وغوغل مرة أخرى وفهمتها. قبل شهرين ، ذكر أحد زملائي عرضًا في دردشة العمل التي أجراها معهد IIFE في سياق C ++. مثل هذا الجد في مزحة ، كدت أنزل عن الموقد ودخلت Google مرة أخرى.

بعد ذلك قررت أن أؤلف (بشكل أساسي لنفسي) ورقة غش للاختصارات المفيدة لمطور C ++ لمعرفتها. هذا لا يعني أنها تنطبق فقط على C ++ ، أو أنها كلها مفاهيم من C ++ (يمكنك كتابة مجلدات حول التعابير اللغوية). لا ، هذه فقط مفاهيم واجهتها بالفعل في العمل والمقابلات ، وعادة ما يتم التعبير عنها في شكل اختصارات. حسنًا ، فاتني أشياء تافهة تمامًا مثل LIFO و FIFO و CRUD و OOP و GCC و MSVC.
ومع ذلك ، ظهرت الاختصارات بشكل لائق ، لذلك قمت بتقسيم ورقة الغش إلى جزأين: تتميز بقوة C ++ وأكثر شيوعًا. عندما كان ذلك مناسبًا ، قمت بتجميع المفاهيم معًا ، وإلا فسردتها أبجديًا. بشكل عام ، ليس هناك معنى كبير في ترتيبها.
الأشياء الأساسية:•
ODR•
POD•
POF•
PIMPL•
RAII•
RTTI•
المحكمة الخاصة بلبنان•
UBالتفاصيل الدقيقة للغة:•
ADL•
CRTP•
CTAD•
EBO•
IIFE•
NVI•
RVO و NRVO•
SFINAE•
SBO ، SOO ، SSOUPDATE:•
السيرة الذاتية•
LTO•
PCH•
PGO•
SEH / VEH•
TMP•
VLAالأشياء الأساسية
ODR
قاعدة تعريف واحدة. حكم تعريف واحد. المبسطة تعني ما يلي:
- ضمن وحدة ترجمة واحدة ، لا يمكن أن يكون لكل متغير أو وظيفة أو فصل دراسي ، وما إلى ذلك ، أكثر من تعريف واحد. هناك أكبر عدد ممكن من الإعلانات (باستثناء عمليات النقل بدون نوع أساسي معين ، والتي لا يمكن التصريح عنها دون تعريف) ، ولكن ليس أكثر من تعريف واحد. أقل ممكن إذا لم يتم استخدام الكيان.
- خلال البرنامج بالكامل ، يجب أن يكون لكل وظيفة غير مضمنة ومتغير مستخدم تعريف واحد تمامًا. يجب أن يكون لكل وظيفة مضمّنة ومتغير مستخدم تعريف واحد في كل وحدة ترجمة.
- يمكن أن تحتوي بعض الكيانات - على سبيل المثال ، الفئات والوظائف المضمّنة والمتغير والقوالب والأعداد وما إلى ذلك - على العديد من التعريفات في البرنامج (ولكن ليس أكثر من تعريف في وحدة الترجمة). في الواقع ، يحدث هذا عندما يكون الرأس نفسه الذي يحتوي على فئة مطبقة بالكامل ، على سبيل المثال ، متصلاً بعدة ملفات .cpp. لكن يجب أن تتزامن هذه التعريفات (أنا أبسطها كثيرًا ، لكن الجوهر هو هذا). وإلا سيكون UB .
سيقوم المترجم
بقبض انتهاك
ODR بسهولة داخل وحدة الترجمة. لكنه لن يكون قادرًا على فعل أي شيء في حالة انتهاك القاعدة على نطاق البرنامج - فقط لأن المترجم يعالج وحدة ترجمة واحدة في كل مرة.
يمكن للرابط أن يجد المزيد من الانتهاكات ، ولكن ، بصرامة ، ليس مضطرًا للقيام بذلك (لأنه ، وفقًا للمعيار ،
UB موجود هنا) ويمكنه تفويت شيء ما. بالإضافة إلى ذلك ، فإن عملية البحث عن انتهاكات
ODR في مرحلة الربط لها تعقيد من الدرجة الثانية ، وتجميع C ++ code ليس بهذه السرعة.
نتيجة لذلك ، فإن المسؤولية الرئيسية عن الامتثال لهذه القاعدة (خاصة على مستوى البرنامج) هي المطور نفسه. ونعم - يمكن فقط للكيانات التي لها رابط خارجي أن تنتهك
ODR على نطاق البرنامج ؛ لا يشارك الأشخاص من الداخل (أي المعرّفين في مساحات أسماء مجهولة) في هذا الكرنفال.
اقرأ المزيد:
مرة واحدة
(الإنجليزية) ،
اثنان (الإنجليزية)POD
سهل البيانات القديمة. بنية بيانات بسيطة. أبسط تعريف: هذا هو الهيكل الذي يمكنك ، كما هو ، في شكل ثنائي إرسال / استقبال من مكتبة C. أو ، وهو نفسه ، نسخ صحيح مع
memcpy
بسيطة.
من قياسي إلى قياسي ، تغير التعريف الكامل بالتفصيل. آخر C ++ 17
POD يحدد حاليا كيف
- نوع العددية
- أو فئة / هيكل / اتحاد:
- هناك فئة تافهة
- هناك فئة مع جهاز قياسي
- لا يحتوي على حقول غير ثابتة POD
- أو مجموعة من هذه الأنواع
فئة تافهة:
- لم يحذف واحد على الأقل:
- المنشئ الافتراضي
- نسخة منشئ
- منشئ متحرك
- نسخة مشغل الاحالة
- نقل مشغل الاحالة
- جميع المنشئات الافتراضية التي تقوم بنسخ ونقل المنشئات ومشغلي المهام تافهة (مبسطة - تم إنشاؤها بواسطة المترجم) أو عن بعد
- لديه التدمير تافهة غير عن بعد
- جميع الأنواع الأساسية وجميع حقول أنواع الطبقات لها مدمرات تافهة
- لا توجد طرق افتراضية (بما في ذلك المدمر)
- لا توجد أنواع قاعدة افتراضية
فئة مع جهاز قياسي (فئة تخطيط قياسي):
ومع ذلك ، في C ++ 20 لن يكون هناك مفهوم لنوع
POD ، فقط النوع التافه والنوع مع الجهاز القياسي سيبقيان.
اقرأ المزيد:
واحد (الروسية) واثنان (الإنجليزية) وثلاثة (الإنجليزية)ميناء الفجيرة
وظيفة القديمة عادي. وظيفة بسيطة على غرار C. مذكورة في المعيار قبل C ++ 14 شاملة فقط في سياق معالجات الإشارات. متطلبات ذلك هي:
- يستخدم فقط الأشياء الشائعة بين C و C ++ (على سبيل المثال لا استثناءات
try-catch
، على سبيل المثال) - لا يتسبب بشكل مباشر أو غير مباشر في وظائف غير POF ، باستثناء العمليات الخالية من الكتلة الذرية (
std::atomic_init
، std::atomic_fetch_add
، إلخ)
فقط هذه الوظائف ، التي لها أيضًا ارتباط C (
extern "C"
) ، مسموح بها بواسطة المعيار لاستخدامها كمعالجات إشارات. يعتمد دعم الوظائف الأخرى على المترجم.
في C ++ 17 ، يختفي مفهوم
POF ، بدلاً من ذلك يظهر تقييمًا آمنًا للإشارات. في مثل هذه الحسابات محظورة:
- يدعو إلى جميع وظائف المكتبة القياسية ، باستثناء ذرية ، وخالية من قفل
new
delete
المكالمات- باستخدام
dynamic_cast
- استدعاء إلى
thread_local
الكيان - أي عمل مع استثناءات
- تهيئة متغير ثابت محلي
- انتظار تهيئة متغير ثابت لإكمال
في حالة قيام معالج الإشارة بأي مما سبق ، يعد المعيار
UB .
اقرأ المزيد:
الوقت (الإنجليزية)PIMPL
المؤشر إلى التنفيذ. مؤشر التنفيذ. المصطلح الكلاسيكي في لغة C ++ ، والمعروف أيضًا باسم d-pointer ، المؤشر غير شفاف ، جدار الحماية المترجم. يتكون في حقيقة أن كل الطرق الخاصة والحقول وتفاصيل التنفيذ الأخرى لفئة معينة يتم تخصيصها في فصل منفصل ، والطرق العامة فقط (على سبيل المثال ، واجهة) ومؤشر لمثيل هذه الفئة المنفصلة الجديدة تبقى في الفصل الأصلي. على سبيل المثال:
foo.hpp class Foo { public: Foo(); ~Foo(); void doThis(); int doThat(); private: class Impl; std::unique_ptr<Impl> pImpl_; };
foo.cpp #include "foo.hpp" class Foo::Impl {
لماذا هذا ضروري ، أي المزايا:
- التغليف: يحصل مستخدمو الفصل من خلال اتصال الرأس على ما يحتاجون إليه فقط - واجهة عامة. إذا تغيرت تفاصيل التنفيذ ، فلن يلزم إعادة ترجمة رمز العميل (انظر ABI ).
- وقت التجميع: بما أن الرأس العام لا يعرف شيئًا عن التنفيذ ، فإنه لا يشمل الرؤوس الكثيرة التي يحتاجها. وفقًا لذلك ، يتم تقليل عدد الرؤوس المتصلة ضمنيًا في رمز العميل. يتم أيضًا تبسيط عملية البحث عن أسماء وتحميل الأحمال الزائدة ، لأن العنوان العام لا يحتوي على أعضاء خاصين (رغم أنهم خاصون ، إلا أنهم يشاركون في هذه العمليات).
السعر ، أي عيوبه:
بعض أوجه القصور هذه قابلة للإزالة ، لكن السعر يزيد من تعقيد الكود ويقدم مستويات إضافية من التجريد (انظر
FTSE ).
اقرأ المزيد:
واحد (روسي) واثنان (روسي) وثلاثة (انجليزي)RAII
اكتساب الموارد هو التهيئة. التقاط مورد هو التهيئة. معنى هذا المصطلح هو أن الاحتفاظ بمورد معين يستمر طوال حياة الكائن المقابل. يحدث التقاط المورد في وقت إنشاء / تهيئة الكائن ، والإطلاق - في وقت تدمير / الانتهاء من الكائن نفسه.
من الغريب (في المقام الأول لمبرمجي C ++) ، يتم استخدام هذا المصطلح أيضًا بلغات أخرى ، حتى تلك التي تستخدم أداة تجميع مجمعي البيانات المهملة. في Java ، يكون هناك
try--
، في Python the statement ، في C # التوجيه
using
، في Go the
defer
. ولكن في C ++ مع العمر المتوقع للغاية للأجسام ، فإن
RAII يناسب الكائنات
العضوية بشكل خاص.
في C ++ ، يتم عادةً التقاط مورد في المنشئ ويتم تحريره في المدمر. على سبيل المثال ، تتحكم المؤشرات الذكية في الذاكرة بهذه الطريقة ، حيث تقوم تدفقات الملفات بإدارة الملفات ، وتؤدي مزامنة المزامنة إلى مزامنة المزامنة. الجمال هو أنه بغض النظر عن كيفية خروج الكتلة (النطاق) - هل هو طبيعي من خلال أي من نقاط الخروج ، أو تم طرح استثناء - سيتم تدمير كائن التحكم في الموارد الذي تم إنشاؤه في هذه الكتلة ، وسيتم تحرير المورد. أي بالإضافة إلى تغليف
RAII في C ++ ، فإنه يساعد أيضًا في ضمان الأمان بمعنى الاستثناءات.
القيود ، حيث بدونها. Destructors في C ++ لا تُرجع القيم بشكل قاطع ولا يجب رمي استثناءات. وفقًا لذلك ، إذا كان إصدار المورد مصحوبًا بواحد أو آخر ، فسيكون من الضروري تطبيق منطق إضافي في المدمر الخاص بكائن التحكم.
اقرأ المزيد:
مرة واحدة (الروسية) ،
اثنان (الإنجليزية)RTTI
معلومات نوع وقت التشغيل. تحديد الهوية في وقت التشغيل. هذه آلية للحصول على معلومات حول نوع الكائن أو التعبير في وقت التشغيل. إنه موجود بلغات أخرى ، لكن في لغة C ++ يتم استخدامه من أجل:
dynamic_cast
typeid
و type_info
- استثناء الصيد
أحد القيود المهمة: يستخدم
RTTI جدول الوظائف الافتراضية ، وبالتالي لا يعمل إلا مع أنواع متعددة الأشكال (المدمر الظاهري كافٍ). أحد التفسيرات المهمة:
dynamic_cast
و
typeid
لا تستخدمان
RTTI دائمًا ، وبالتالي تعمل مع الأنواع غير متعددة الأشكال. على سبيل المثال ، لإلقاء ارتباط ديناميكي على سليل إلى رابط أحد الأسلاف ، فإن
RTTI ليست ضرورية ؛ كل المعلومات متوفرة في وقت الترجمة.
RTTI ليس مجانيًا ، وإن كان قليلاً ، ولكنه يؤثر سلبًا على أداء وحجم الذاكرة المستهلكة (ومن هنا تأتي النصيحة المتكررة بعدم استخدام
dynamic_cast
بسبب بطئه). لذلك ، يسمح لك برنامج التحويل البرمجي ، كقاعدة عامة ، بتعطيل
RTTI . يعد مجلس التعاون الخليجي و MSVC بأن هذا لن يؤثر على صحة الاستثناءات.
اقرأ المزيد:
مرة واحدة (الروسية) ،
اثنان (الإنجليزية)STL
مكتبة القوالب القياسية. مكتبة القوالب القياسية. جزء من مكتبة C ++ القياسية التي توفر الحاويات العامة والتكرارات والخوارزميات ووظائف المساعد.
على الرغم من اسمها المعروف ، فإن
STL لم يطلق عليها في المعيار. من أقسام المعيار ، يمكن أن تعزى
STL بوضوح إلى مكتبة الحاويات ، ومكتبة التكرار ، ومكتبة الخوارزمية ، ومكتبة المرافق العامة جزئيًا.
في التوصيف الوظيفي ، يمكنك غالبًا العثور على متطلبين منفصلين - معرفة لغة C ++ والألفة مع
STL . لم أفهم هذا أبدًا ، لأن
STL جزء لا يتجزأ من اللغة منذ معيار 1998 الأول.
اقرأ المزيد:
مرة واحدة (الروسية) ،
اثنان (الإنجليزية)UB
سلوك غير محدد. سلوك غير محدد. هذا السلوك في حالات الخطأ التي لا يوجد فيها متطلبات القياسية. يتم سرد العديد من هذه صراحة في المعيار باعتبارها تؤدي إلى
UB . وتشمل هذه ، على سبيل المثال:
- انتهاك حدود مجموعة أو حاوية STL
- استخدام متغير غير مهيأ
- dereferencing مؤشر فارغة
- وقعت عدد صحيح تجاوز
تعتمد نتيجة
UB على كل شيء على التوالي - سواء على إصدار المترجم أو على الطقس على المريخ. علاوة على ذلك ، يمكن أن تكون هذه النتيجة أي شيء: خطأ في الترجمة ، والتنفيذ الصحيح ، والتعطل. السلوك غير المحدد هو الشر ، فمن الضروري التخلص منه.
السلوك غير
المحدد ، من ناحية أخرى ، لا ينبغي الخلط بينه وبين
السلوك غير المحدد . السلوك غير المحدد هو السلوك الصحيح للبرنامج الصحيح ، لكن الذي ، بإذن من المعيار ، يعتمد على المترجم. والمترجم غير مطلوب لتوثيق ذلك. على سبيل المثال ، هذا هو الترتيب الذي يتم به تقييم وسيطات دالة أو تفاصيل تطبيق
std::map
.
حسنًا ، هنا يمكنك استدعاء السلوك المحدد بالتنفيذ. من غير محدد يختلف في توافر الوثائق. مثال: المحول البرمجي مجاني لجعل النوع
std::size_t
أي حجم ، ولكن يجب أن يشير إلى أي واحد.
اقرأ المزيد:
واحد (روسي) واثنان (روسي) وثلاثة (انجليزي)الدقيقة من اللسان
ADL
بحث يعتمد على الوسيطة. بحث يعتمد على الوسيطة. إنه البحث عن كوينيج - على شرف أندرو كوينيج. هذه مجموعة من القواعد لحل أسماء الوظائف غير المؤهلة (أي ، الأسماء بدون المشغل
::
:) ، بالإضافة إلى تحليل الاسم المعتاد. ببساطة: يتم البحث عن اسم الوظيفة في مساحات الأسماء المتعلقة بوسائطها (هذه هي المساحة التي تحتوي على نوع الوسيطة ، والنوع نفسه ، إذا كان فئة ، وجميع أسلافها ، إلخ).
مثال أبسط #include <iostream> namespace N { struct S {}; void f(S) { std::cout << "f(S)" << std::endl; }; } int main() { N::S s; f(s); }
f
العثور على الدالة
f
في مساحة الاسم
N
فقط لأن الوسيطة الخاصة بها تنتمي إلى هذه المساحة.
حتى
std::cout << "Hello World!\n"
التافهة
std::cout << "Hello World!\n"
يستخدم
ADL ،
std::basic_stream::operator<<
لا
std::basic_stream::operator<<
زائد على
const char*
. لكن الوسيطة الأولى لهذا البيان هي
std::basic_stream
، ويبحث المترجم ويجد حمل زائد مناسب في
std
.
بعض التفاصيل: لا ينطبق
ADL إذا وجد بحث منتظم إعلانًا لأحد أعضاء الفصل ، أو عن إعلان دالة في الكتلة الحالية دون استخدام أو إعلان لا دالة ولا قالب دالة. أو إذا كان اسم الوظيفة موضحًا بين قوسين (المثال أعلاه لا يتم ترجمة مع
(f)(s)
؛ سيتعين عليك كتابة
(N::f)(s);
).
في بعض الأحيان ، يفرض عليك
ADL استخدام أسماء الوظائف المؤهلة بالكامل حيث يبدو الأمر غير ضروري.
على سبيل المثال ، لا يتم ترجمة هذا الرمز namespace N1 { struct S {}; void foo(S) {}; } namespace N2 { void foo(N1::S) {}; void bar(N1::S s) { foo(s); } }
اقرأ المزيد:
واحد (الإنجليزية) ،
اثنان (الإنجليزية) ،
ثلاثة (الإنجليزية)CRTP
الغريب نموذج قالب المتكررة. نمط العودية غريب. جوهر القالب كما يلي:
- يرث بعض الفئة من فئة القالب
- يتم استخدام الفئة المنحدرة كمعلمة قالب للفئة الأساسية الخاصة بها
من الأسهل إعطاء مثال:
template <class T> struct Base {}; struct Derived : Base<Derived> {};
CRTP هو مثال رئيسي على تعدد الأشكال الساكن. توفر الفئة الأساسية واجهة ؛ وتوفر الفئات المشتقة تطبيقًا. ولكن على عكس تعدد الأشكال العادي ، لا يوجد أي تكاليف لإنشاء واستخدام جدول من الوظائف الافتراضية.
مثال template <typename T> struct Base { void action() const { static_cast<T*>(this)->actionImpl(); } }; struct Derived : Base<Derived> { void actionImpl() const { ... } }; template <class Arg> void staticPolymorphicHandler(const Arg& arg) { arg.action(); }
عند استخدامها بشكل صحيح ، تكون
T
دائمًا من سليل
Base
، لذلك يكفي
static_cast
. نعم ، في هذه الحالة ، تعرف الفئة الأساسية الواجهة المنحدرة.
مجال شائع آخر لاستخدام
CRTP هو امتداد (أو تضييق) وظائف الفئات الموروثة (شيء يسمى mixin في بعض اللغات). ولعل الأمثلة الأكثر شهرة:
struct Derived : singleton<Derived> { … }
struct Derived : private boost::noncopyable<Derived> { … }
struct Derived : std::enable_shared_from_this<Derived> { … }
struct Derived : counter<Derived> { … }
- حساب عدد الكائنات التي تم إنشاؤها و / أو الموجودة
العيوب ، أو بالأحرى ، اللحظات التي تتطلب الانتباه:
- لا توجد فئة أساسية شائعة ، لا يمكنك إنشاء مجموعة من أحفاد مختلفة والوصول إليها من خلال مؤشر إلى نوع الأساس. ولكن إذا كنت تريد ، يمكنك أن ترث Base من النوع متعدد الأشكال المعتاد.
- هناك فرصة إضافية لإخراج قدمك من الإهمال:
مثال template <typename T> struct Base {}; struct Derived1 : Base<Derived1> {}; struct Derived2 : Base<Derived1> {};
ولكن يمكنك إضافة الحماية:
private: Base() = default; friend T;
- لأن نظرًا لأن جميع الأساليب غير افتراضية ، فإن أساليب السليل تخفي أساليب الفئة الأساسية التي تحمل نفس الأسماء. لذلك ، من الأفضل أن ندعو لهم بشكل مختلف.
- بشكل عام ، لدى الأحفاد طرق عامة لا ينبغي استخدامها في أي مكان باستثناء الفئة الأساسية. هذا ليس جيدًا ، لكن يتم تصحيحه من خلال مستوى إضافي من التجريد (انظر FTSE ).
اقرأ المزيد:
مرة واحدة (الروسية) ،
اثنان (الإنجليزية)CTAD
قالب حجة فئة الفصل. استنتاج نوع المعلمة قالب الفئة تلقائياً. هذه ميزة جديدة من C ++ 17. في السابق ، لم يتم عرض أنواع المتغيرات (
auto
) وقالب الدالة تلقائيًا تلقائيًا ، وهذا هو السبب في ظهور الوظائف المساعدة مثل
std::make_pair
،
std::make_tuple
، وما إلى ذلك. الآن ، في معظم الأحيان ، ليست هناك حاجة إليها ، لأن المترجم قادر على عرض معلمات قوالب الفصل تلقائيًا:
std::pair p{1, 2.0};
CTAD هي فرصة جديدة ، لا تزال
بحاجة إلى التطور والتطور (تعد C ++ 20 بالفعل بتحسينات). القيود حتى الآن على النحو التالي:
الاستدلال الجزئي لأنواع المعلمات غير مدعوم std::pair<double> p{1, 2};
الأسماء المستعارة للنموذج غير مدعومة template <class T, class U> using MyPair = std::pair<T, U>; MyPair p{1, 2};
لا يتم دعم المنشئات المتوفرة فقط في تخصصات القوالب. template <class T> struct Wrapper {}; template <> struct Wrapper<int> { Wrapper(int) {}; }; Wrapper w{5};
القوالب المتداخلة غير مدعومة template <class T> struct Foo { template <class U> struct Bar { Bar(T, U) {}; }; }; Foo::Bar x{ 1, 2.0 };
من الواضح ، لن يعمل CTAD إذا لم يكن نوع معلمة القالب مرتبطًا بوسائط المُنشئ. template <class T> struct Collection { Collection(std::size_t size) {}; }; Collection c{5};
في بعض الحالات ، سوف تساعد قواعد الاستدلال الصريح التي يجب الإعلان عنها في نفس الكتلة مثل قالب الفئة.
مثال template <class T> struct Collection { template <class It> Collection(It from, It to) {}; }; Collection c{v.begin(), v.end()};
اقرأ المزيد:
مرة واحدة (الروسية) ،
اثنان (الإنجليزية)EBO
قاعدة فارغة الأمثل. تعظيم الاستفادة من فئة أساسية فارغة. يُطلق عليها أيضًا تحسين فئة القاعدة الفارغة (EBCO).
كما تعلم ، في C ++ ، لا يمكن أن يكون حجم كائن من أي فئة صفراً. خلاف ذلك ، سيتم كسر كل حساب من المؤشرات ، لأنه في عنوان واحد سيكون من الممكن وضع علامة على العديد من الكائنات المختلفة كما تريد. لذلك ، حتى كائنات الفئات الفارغة (أي الفئات التي لا تحتوي على حقل غير ثابت) لها حجم غير صفري ، والذي يعتمد على برنامج التحويل البرمجي ونظام التشغيل ويكون عادةً مساويًا لـ 1.
وبالتالي ، تضيع الذاكرة عبثا على جميع الكائنات من الطبقات الفارغة. ولكن ليس الكائنات من نسلهم ، لأنه في هذه الحالة المعيار يجعل صراحة استثناء. لا يُسمح للمترجم بتخصيص ذاكرة لفئة أساسية فارغة ، وبالتالي لا يحفظ فقط بايت واحد من الفئة الفارغة ، ولكن أيضًا الكل 4 (حسب النظام الأساسي) ، نظرًا لوجود محاذاة أيضًا.
مثال struct Empty {}; struct Foo : Empty { int i; }; std::cout << sizeof(Empty) << std::endl;
ولكن
نظرًا لأنه لا يمكن وضع كائنات مختلفة من نفس النوع على نفس العنوان ، فلن يعمل
EBO إذا:
تم العثور على فئة فارغة مرتين بين الأجداد struct Empty {}; struct Empty2 : Empty {}; struct Foo : Empty, Empty2 { int i; }; std::cout << sizeof(Empty) << std::endl;
أول حقل غير ثابت هو كائن من نفس الفئة الفارغة أو سليلها struct Empty {}; struct Foo : Empty { Empty e; int i; }; std::cout << sizeof(Empty) << std::endl;
في الحالات التي تكون فيها كائنات الفئات الفارغة عبارة عن حقول غير ثابتة ، لا يتم توفير تحسينات (في الوقت الحالي ،
[[no_unique_address]]
السمة
[[no_unique_address]]
في C ++ 20). لكن إنفاق 4 بايت (أو ما يحتاج إليه المترجم) لكل حقل من هذا القبيل هو عار ، لذلك يمكنك "طي" كائنات الفئات الفارغة مع أول حقل غير ثابت فارغ في منطقتك.
مثال struct Empty1 {}; struct Empty2 {}; template <class Member, class ... Empty> struct EmptyOptimization : Empty ... { Member member; }; struct Foo { EmptyOptimization<int, Empty1, Empty2> data; };
غريب ، ولكن في هذه الحالة ، يختلف حجم Foo عن المترجمين المختلفين ، أما MSVC 2019 فهو 8 ، وبالنسبة لخليج GCC 8.3.0 هو 4. لكن على أي حال ، فإن زيادة عدد الفئات الفارغة لا يؤثر على حجم
Foo
.
اقرأ المزيد:
مرة واحدة
(الإنجليزية) ،
اثنان (الإنجليزية)IIFE
استدعاء دالة فورًا. التعبير الوظيفي يسمى على الفور. بشكل عام ، هذا هو المصطلح في JavaScript ، حيث استعاره Jason Turner من الاسم. في الواقع ، إنه مجرد إنشاء lambda واستدعائه على الفور:
const auto myVar = [&] { if (condition1()) { return computeSomeComplexStuff(); } return condition2() ? computeSonethingElse() : DEFAULT_VALUE; } ();
لماذا هذا ضروري؟ حسنًا ، على سبيل المثال ، كما في التعليمة البرمجية أعلاه من أجل تهيئة ثابت من خلال حساب غير تافهي وليس تسد النطاق مع المتغيرات والوظائف غير الضرورية.
اقرأ المزيد:
مرة واحدة
(الإنجليزية) ،
اثنان (الإنجليزية)NVI
واجهة غير افتراضية. واجهة غير افتراضية. وفقًا لهذا المصطلح ، يجب ألا تحتوي واجهة الطبقة المفتوحة على وظائف افتراضية. جميع الوظائف الافتراضية تكون خاصة (محميّة إلى أقصى حد) وتُسمى بالداخل غير الظاهري.
مثال class Base { public: virtual ~Base() = default; void foo() {
لماذا هذا ضروري:
- تقوم كل وظيفة افتراضية مفتوحة بعمل شيئين: فهي تحدد الواجهة العامة للفصل وتشارك في السلوك الغالب في الفصول التناسلية. استخدام NVI يلغي مثل هذه الوظائف مع تحميل مزدوج: يتم تعريف الواجهة من قبل بعض الوظائف ، وتغيير السلوك من قبل الآخرين. يمكنك تغيير كل منهما بشكل مستقل عن بعضها البعض.
- إذا كانت هناك بعض المتطلبات العامة لجميع الخيارات لتنفيذ وظيفة افتراضية (قبل الشيكات السابقة واللاحقة ، والتقاط المزج ، وما إلى ذلك) ، فمن الملائم للغاية تجميعها في مكان واحد (انظر DRY ) - في الفئة الأساسية - ومنع الورثة من تجاوز هذا السلوك. أي اتضح حالة خاصة لطريقة قالب النمط.
إن رسوم استخدام NVI هي بعض التورمات في الكود ، وانخفاض محتمل في الأداء (بسبب استدعاء طريقة إضافية واحدة) وزيادة التعرض لمشكلة فئة أساسية هشة (انظر FBC ).اقرأ المزيد: مرة واحدة (الإنجليزية) ، اثنان (الإنجليزية)RVO و NRVO
(الاسم) عائد القيمة الأمثل. تحسين قيمة الإرجاع (المسماة). هذه حالة خاصة لاستخراج النسخ يسمح بها المعيار - يمكن للمترجم أن يتجاهل نسخًا غير ضرورية من الكائنات المؤقتة ، حتى إذا كان لمنشئيها ومدمراتها تأثيرات جانبية واضحة. يكون هذا التحسين مسموحًا به عندما تقوم الدالة بإرجاع كائن حسب القيمة (الحالات الأخرى المسموح بها في نسخ النسخة هي رمي الاستثناءات والتقاطها).مثال Foo bar() { return Foo(); } int main() { auto f = bar(); }
بدون RVO ، سيتم إنشاء كائن مؤقت Foo
في الوظيفة هنا bar
، ثم من خلال مُنشئ النسخ ، سيتم إنشاء كائن مؤقت آخر في الوظيفة منه main
(للحصول على النتيجة bar
) ، وعندئذٍ فقط سيتم إنشاء الكائن وسيتم f
تعيين قيمة الكائن المؤقت الثاني إليها. تتخلص RVO من كل هذه النسخ والواجبات ، bar
وتخلق الوظيفة مباشرة f
.يحدث مثل هذا: وظيفة main
يخصص مساحة لكائن في إطار المكدس الخاص به f
. تمكن الوظيفة bar
(التي تعمل بالفعل في إطارها) من الوصول إلى هذه الذاكرة المخصصة في الإطار السابق وإنشاء الكائن المطلوب هناك.NRVO يختلف عنيقوم RVO بنفس التحسين ، لكن ليس عند إنشاء الكائن في التعبير return
، ولكن عندما يتم إرجاع الكائن الذي تم إنشاؤه مسبقًا في الوظيفة.مثال Foo bar() { Foo result; return result; }
على الرغم من الفارق الصغير الظاهر ، إلا أن NRVO يصعب تنفيذه ، وبالتالي فهو لا يعمل في كثير من الحالات. على سبيل المثال ، إذا قامت دالة بإرجاع كائن عمومي أو إحدى وسائطه ، أو إذا كان للدالة عدة نقاط خروج وتم إرجاع كائنات مختلفة من خلالها ، فلن يتم تطبيق NRVO .NRVO لا يعمل هنا Foo bar(bool condition) { if (condition) { Foo f1; return f1; } Foo f2; return f2; }
تقريبا جميع المجمعين قد دعمت طويلة RVO . يمكن أن تختلف درجة دعم NRVO من مترجم إلى مترجم ومن إصدار إلى آخر.RVO و NRVO هي مجرد تحسينات. وعلى الرغم من عدم نسخ نسخ المُنشئ ومشغل المهمة ، إلا أنهما يجب أن يكونا في فئة الكائن. لقد تغيرت القواعد قليلاً في C ++ 17: الآن لا يعتبر RVO نسخة elision ، فقد أصبح إلزاميًا ، وليست هناك حاجة إلى المُنشئ المقابل ومشغل التعيين.ملاحظة: (N) RVO بعبارات ثابتة هو موضوع زلق. حتى C ++ 14 ضمنيًا ، لم يُقال شيئًا عن ذلك ، تتطلب C ++ 17 RVO في مثل هذه التعبيرات ، ويحظر C ++ 20 القادم.بضع كلمات حول العلاقة مع دلالات النزوح. أولا ، (N) RVO لا يزال أكثر فعالية ، ل لا حاجة لاستدعاء الخطوة منشئ والمدمرة. ثانياً ، إذا بدلاً result
من العودة من نفس الوظيفة std::move(result)
، فإن NRVO يضمن عدم العمل. لإعادة صياغة المعيار: ينطبق RVO على القيمة ، ينطبق NRVO على القيمة ، وهي std::move(result)
عبارة عن قيمة xvalue.اقرأ المزيد: واحد (الإنجليزية) ، اثنان (الإنجليزية) ، ثلاثة (الإنجليزية)SFINAE
فشل الإحلال ليس خطأ. الاستبدال الفاشل ليس خطأ. SFINAE هي ميزة لعملية إنشاء مثيل للقوالب - وظائف وفئات - في C ++. خلاصة القول هي أنه إذا تعذّر إنشاء قالب معين ، فلن يعتبر ذلك خطأً إذا كانت هناك خيارات أخرى. على سبيل المثال ، تعمل خوارزمية مبسطة لاختيار التحميل الزائد للوظائف الأكثر ملائمة مثل:- تم حل اسم الوظيفة - يبحث المترجم عن جميع الوظائف بالاسم المحدد في جميع مساحات الأسماء المحددة (انظر ADL ).
- يتم تجاهل الوظائف غير الملائمة - وليس عدد الوسائط ، ولا يوجد تحويل ضروري لأنواع الوسائط ، ولم يكن من الممكن اشتقاق أنواع لقالب الوظيفة ، إلخ.
- (viable functions), . — .
SFINAE : , , , ( ). .
SFINAE , , . - , . . , .
#include <iostream> #include <type_traits> #include <utility> template <class, class = void> struct HasToString : std::false_type {}; // , // - , // — , , template <class T> struct HasToString<T, std::void_t<decltype(&T::toString)>> : std::is_same<std::string, decltype(std::declval<T>().toString())> {}; struct Foo { std::string toString() { return {}; } }; int main() { std::cout << HasToString<Foo>::value << std::endl; // 1 std::cout << HasToString<int>::value << std::endl; // 0 }
إن ما ظهر في C ++ 17 static if
قد يحل في بعض الحالات محل SFINAE ، والمفاهيم المتوقعة في C ++ 20 ستجعلها غير ضرورية. لنرى.اقرأ المزيد: (. روس) وقت ، اثنان (المهندس) ، ثلاثة (المهندس)SBO ، SOO ، SSO
عازلة صغيرة / كائن / سلسلة الأمثل. تعظيم الاستفادة من المخازن المؤقتة الصغيرة / الكائنات / الخطوط. أحيانًا يتم استخدام SSO بمعنى تحسين الحجم الصغير ، ولكن نادرًا ما نفترض ، لذلك نفترض أن SSO تدور حول سلاسل. SBO و SOO هي مرادفات ببساطة ، و SSO هي الحالة الخاصة الأكثر شهرة.جميع هياكل البيانات التي تستخدم الذاكرة الديناميكية تشغل بالتأكيد مكانًا في المجموعة. على الأقل من أجل تخزين مؤشر لمجموعة. وجوهر هذه التحسينات ليس طلب ذاكرة من الكومة للأجسام الصغيرة نسبيا (وهي مكلفة نسبيا) ، ولكن لوضعها في مساحة مكدس مخصصة بالفعل.على سبيل المثال ، يمكن تنفيذ std :: string مثل هذا:مثال class string { char* begin_; size_t size_; size_t capacity_; };
حجم هذه الفئة ، أحصل على 24 بايت (يعتمد على المترجم والنظام الأساسي). أي
سلاسل لا تزيد عن 24 حرفا يمكن وضعها على المكدس. في الواقع ، ليس حتى 24 ، بالطبع ، لأنه من الضروري التمييز بطريقة ما بين الموضع على المكدس وعلى الكومة. ولكن هنا هي أبسط طريقة لخطوط قصيرة تصل إلى 8 أحرف (بنفس الحجم - 24 بايت):مثال class string { union Buffer { char* begin_; char local_[8]; }; Buffer buffer_; size_t _size; size_t _capacity; };
بالإضافة إلى عدم وجود مخصصات على الكومة ، هناك ميزة أخرى - وهي درجة عالية من محلية البيانات. سيشغل صفيف أو متجه لهذه الكائنات المحسنة بالفعل قطعة متواصلة من الذاكرة فقط. تستخدمجميع التطبيقات تقريبًا SSO وبعض التطبيقات على الأقل . ولكن لم يتم تحسينها على الإطلاق بهذه الطريقة ، حيث أن المعيار يتطلب أنه بالنسبة لاثنين من المتجهات لا يسبب نسخ أو تعيين عناصرها ، وأن تظل جميع التكرارات الصالحة صالحة. سوف SBO لا تسمح للوفاء بهذه المتطلبات ( لأنها ليست). ولكن ، كما قد تتخيل ، يستخدم SBO . قراءة المزيد: الوقت (الإنجليزية) ،std::string
std::function
std::vector
std::swap
std::string
boost::container::small_vector
اثنين (المهندس)UDPATE
بفضل PyerK على هذه القائمة الإضافية من الاختصارات.CV
تصفيات مثل const ومتقلبة. const
يعني أنه لا يمكن تعديل الكائن / المتغير ، وستؤدي محاولة القيام بذلك إلى حدوث خطأ في وقت الترجمة أو في UB في وقت التشغيل. volatile
يعني أن الكائن / المتغير يمكن أن يتغير بغض النظر عن تصرفات البرنامج (على سبيل المثال ، يملأ بعض المتحكم الدقيق شيئًا ما على الذاكرة) ، ويجب ألا يقوم المحول البرمجي بتحسين الوصول إليه. الوصول إلى volatile
كائن ليس من خلال volatile
ارتباط أو مؤشر يؤدي أيضًا إلى UB .اقرأ المزيد: (. روس) وقت ، اثنان (المهندس) ، ثلاثة (. روس)عفرتو
وصلة الوقت الأمثل. رابط التحسين. كما يوحي الاسم ، يحدث هذا التحسين أثناء الارتباط ، أي بعد التجميع. يمكن للرابط أن يفعل شيئًا لم يجرؤ المترجم على القيام به: جعل بعض الوظائف مضمنة ، وطرح الكود والبيانات غير المستخدمة. يزيد وقت الارتباط ، بالطبع.اقرأ المزيد: الوقت (الإنجليزية)PCH
رؤوس المترجمة مسبقا. رؤوس المترجمة مسبقا. غالبًا ما يتم تجميعها ، لكن نادراً ما يتم تجميع ملفات الرأس المعدلة مرة واحدة وحفظها في تنسيق برنامج التحويل البرمجي الداخلي. وبالتالي ، سيستغرق إعادة تجميع المشروع وقتًا أقل ، وأحيانًا أقل.اقرأ المزيد: الوقت (روس).PGO
ملف التعريف الموجه. التحسين على أساس نتائج التنميط. هذه طريقة لتحسين البرنامج ، ولكن ليس من خلال تحليل الشفرة الثابتة ، ولكن من خلال إطلاق برنامج الاختبار وجمع إحصائيات حقيقية. على سبيل المثال ، يمكن تحسين الوظائف المتفرعة واستدعاء الوظائف بهذه الطريقة.اقرأ المزيد: الوقت (روس).سي / ه
Structured/Vectored Exception Handling. MSVC .
try-catch
SEH :
__try
,
__except
,
__finally
, , , , - , . .
VEH , .
:
(.)TMP
قالب ميتا البرمجة. metaprogramming قالب. Metaprogramming هو عندما يقوم أحد البرامج بإنشاء برنامج آخر نتيجة لعمله. تطبيق القوالب في C ++ مثل metaprogramming. يقوم مترجم القوالب بإنشاء العدد المطلوب للفئات أو الوظائف. من المعروف أن TMP في C ++ مكتملة Turing ، أي أنه يمكن تنفيذ أي وظيفة عليها.اقرأ المزيد: الوقت (روس).VLA
صفيف متغير الطول. صفائف متغيرة الطول. أي
صفائف طولها غير معروف في وقت الترجمة: void foo(int n) { int array[n]; }
لا يسمح المعيار C ++ بهذا. وهو أمر غريب إلى حد ما ، لأنها موجودة في C النقي منذ معيار C99. ويدعمها بعض المترجمين C ++ امتداد.اقرأ المزيد: الوقت (روس).PS
إذا فاتني شيء ما أو كنت مخطئًا في مكان ما - اكتب التعليقات. فقط تذكر ، من فضلك ، فقط الاختصارات المرتبطة مباشرة بـ C ++ مدرجة هنا. للآخرين ، ولكن ليس أقل فائدة ، سيكون هناك وظيفة منفصلة.الجزء الثاني