typedef مقابل using في C++: أيهما تختار وما الفرق الحقيقي بينهما؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

typedef مقابل using في C++: دليل كامل للمطور الحديث

\n\n

هل تساءلت يومًا لماذا يكتب بعض المطورين typedef في كود C++ بينما يكتب آخرون using؟ يبدو أن كلاهما يفعل الشيء نفسه - إنشاء أسماء مستعارة للأنواع. ولكن هل هذا صحيح حقًا؟ إذا كنت تريد كتابة كود نظيف وحديث وتجنب الوقوع في فخاخ الإنشاءات القديمة، فهذه المقالة لك.

\n\n

سنقوم بتحليل الاختلافات بين typedef و using في C++، ومتى نستخدم أيهما، ولماذا يميل مجتمع C++ بشكل متزايد نحو using. هل أنت مستعد؟ لنبدأ!

\n\n

تاريخ موجز: من C إلى C++11

\n\n

typedef جاء إلينا من لغة C. إنها الطريقة التقليدية للإعلان عن اسم مستعار لنوع. تبدو كالتالي:

\n\n
typedef unsigned long long ULL;\ntypedef std::vector<std::string> StringVector;\ntypedef void (*FunctionPtr)(int, double);
\n\n

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

\n\n

في C++11، ظهر بناء جديد - using (أو alias declaration). يبدو أكثر طبيعية بكثير:

\n\n
using ULL = unsigned long long;\nusing StringVector = std::vector<std::string>;\nusing FunctionPtr = void (*)(int, double);
\n\n

يتضح على الفور: على اليسار - الاسم، على اليمين - النوع. يُقرأ كـ "إسناد"، وهو أمر بديهي لأي مبرمج.

\n\n

الفرق الرئيسي: العمل مع القوالب

\n\n

هنا يكمن الاختلاف الأكثر أهمية بين typedef و using. using يدعم alias templates (الأسماء المستعارة القالبية)، بينما typedef لا يدعمها.

\n\n

دعنا ننظر إلى مثال. لنفترض أنك تريد إنشاء اسم مستعار لـ std::unique_ptr مع محوّل مخصص:

\n\n
// باستخدام using - سهل وجميل\ntemplate <typename T>\nusing CustomPtr = std::unique_ptr<T, void(*)(T*)>;\n\n// باستخدام typedef - غير ممكن مباشرة!\n// سيتعين عليك بناء أغلفة عبر الهياكل\ntemplate <typename T>\nstruct CustomPtrOld {\n    typedef std::unique_ptr<T, void(*)(T*)> type;\n};\n\n// الاستخدام:\nCustomPtr<int> ptr1;                    // using - بسيط\nCustomPtrOld<int>::type ptr2;           // typedef - معقد
\n\n

هل تشعر بالفرق؟ using يجعل الكود أنظف وأقصر. وإذا كنت بحاجة لاستخدام هذا الاسم المستعار داخل قالب فئة أو دالة، يصبح الفرق أكثر وضوحًا:

\n\n
template <typename T>\nclass Container {\npublic:\n    // باستخدام using\n    using Ptr = CustomPtr<T>;\n    \n    // باستخدام typedef - تحتاج typename و ::type\n    typedef typename CustomPtrOld<T>::type OldPtr;\n    \nprivate:\n    Ptr data;\n    OldPtr oldData;\n};
\n\n

قابلية قراءة الكود: الفائز الواضح

\n\n

حتى بالنسبة للأنواع البسيطة، يفوز using في قابلية القراءة. قارن:

\n\n
// باستخدام typedef - غير واضح أين الاسم وأين النوع\ntypedef std::map<std::string, std::vector<int>> StringToIntVectorMap;\n\n// باستخدام using - كل شيء واضح\nusing StringToIntVectorMap = std::map<std::string, std::vector<int>>;
\n\n

عند قراءة الكود، تقوم عينك بشكل طبيعي بمسح البناء من اليسار إلى اليمين. using يتبع هذا المبدأ: أولاً الاسم، ثم النوع. بينما typedef يجبرك على قراءة النوع أولاً ثم الاسم - وهذا يتعارض مع الحدس.

\n\n

المؤشرات للدوال: ألم typedef

\n\n

المؤشرات للدوال هي ربما الجزء الأقل تفضيلاً في C++ للعديد من المطورين. و typedef يجعلها أكثر إرباكًا:

\n\n
// typedef - اقرأها كتعويذة\ntypedef void (*Callback)(int, const std::string&);\n\n// using - كمتغير عادي\nusing Callback = void (*)(int, const std                

المدونات

توصيات الكتب