صفوف الأصدقاء
في لغة C++، يمكن للفئة الصديقة الوصول إلى الأعضاء الخاصة والمحمية للفئة التي تم تعريفها كصديقة فيها. [ 1 ] يتمثل أحد الاستخدامات المهمة للفئة الصديقة في تمكين جزء من بنية بيانات ، ممثل بفئة، من الوصول إلى الفئة الرئيسية التي تمثل تلك البنية. تسمح آلية الفئة الصديقة بتوسيع نطاق التخزين والوصول إلى الأجزاء، مع الحفاظ على التغليف المناسب كما يراه مستخدمو بنية البيانات.
على غرار فئة الصديق، فإن دالة الصديق هي دالة يتم منحها حق الوصول إلى الأعضاء الخاصين والمحميين للفئة التي تم تعريفها كصديق فيها.
منذ إصدار C++26 ، يدعم C++ "الأصدقاء المتغيرين" (أي فئات الأصدقاء التي تأتي من القوالب المتغيرة ). [ 2 ]
مثال
يوضح المثال التالي استخدام فئة صديقة لهيكل بيانات الرسم البياني ، حيث يتم تمثيل الرسم البياني بواسطة الفئة الرئيسية Graph، ويتم تمثيل رؤوس الرسم البياني بواسطة الفئة Vertex .
استيراد std ؛template < typename T > using SharedPtr = std :: shared_ptr < T > ; using String = std :: string ; template < typename T > using UniquePtr = std :: unique_ptr < T > ; template < typename T > using HashSet = std :: unordered_set < T > ; // default-specialize the remaining template-parametersclass Vertex { private : // Vertex يمنح حقوق الوصول إلى Graph. friend class Graph ;HashSet < SharedPtr <Vertex> > edges ; String name ; using EdgesIterator = decltype ( edges.cbegin ( )); public : explicit Vertex ( const String & name ) : name { std :: move ( name ) } { }EdgesIterator begin () const { return edges . cbegin (); }EdgesIterator end () const { return edges . cend (); }String getName () const { return name ; } };class Graph { private : HashSet < SharedPtr <Vertex> > vertices ; using VerticesIterator = decltype ( vertices.cbegin ( ) ); public : ~ Graph ( ) { while ( ! vertices.empty ( ) ) { VerticesIterator vertex = vertices.begin ( ); removeVertex ( * vertex ) ; } }void addVertex ( const String & name ) { UniquePtr <Vertex> vertex = std :: make_unique <Vertex> ( name ) ; VerticesIterator iter = vertices.insert ( vertex.get ( ) ) ; }void removeVertex ( SharedPtr < Vertex > vertex ) { vertices . erase ( vertex ); }void addEdge ( SharedPtr < Vertex > from , SharedPtr < Vertex > to ) { // يمكن للرسم البياني الوصول إلى الحقول الخاصة بالرأس لأن الرأس أعلن عن الرسم البياني كصديق . from -> edges . insert ( to ); }VerticesIterator begin () const { return vertices . cbegin (); }VerticesIterator end () const { return vertices . cend (); } };استخدام الأصدقاء المتغيرين:
استيراد std ؛template < typename ... Friends > class ClassWithFriends { private : int secretValue ;صديق الأصدقاء ...؛ عام : صريح ClassWithFriends ( int secret ) : secretValue { secret } {} };class A { public : void readSecret ( const class ClassWithFriends < A , B >& instance ) const { std :: println ( "Inspecting secret value from A: {}" , instance.secretValue ) ; } } ;class B { public : void readSecret ( const class ClassWithFriends < A , B >& instance ) const { std :: println ( "Inspecting secret value from B: {}" , instance.secretValue ) ; } } ;int main ( int argc , char * argv []) { ClassWithFriends < A , B > secretHolder ( 135 ); A a ; B b ; a . readSecret ( secretHolder ); b . readSecret ( secretHolder ); }التغليف
يُعزز الاستخدام الأمثل للفئات الصديقة التغليف، إذ يسمح بتوسيع نطاق الوصول الخاص لبنية البيانات ليشمل أجزاءها - التي تملكها بنية البيانات نفسها - دون السماح بالوصول الخاص لأي فئة خارجية أخرى. وبهذه الطريقة، تبقى بنية البيانات محمية من أي محاولات غير مقصودة لخرق ثوابتها من الخارج.
من المهم ملاحظة أن الصنف لا يمكنه منح نفسه حق الوصول إلى الأجزاء الخاصة لصنف آخر؛ فهذا يُخلّ بمبدأ التغليف. بدلاً من ذلك، يمنح الصنف حق الوصول إلى أجزائه الخاصة لصنف آخر - وذلك بتعريف ذلك الصنف كصديق. في مثال الرسم البياني، لا يمكن للصنف Graph تعريف نفسه كصديق للصنف Vertex. بل يقوم الصنف Vertex بتعريف Graph كصديق، وبالتالي يمنح Graph حق الوصول إلى حقوله الخاصة.
حقيقة أن الفصل يختار أصدقاءه بنفسه تعني أن الصداقة ليست متناظرة بشكل عام. في مثال الرسم البياني، لا يستطيع Vertex الوصول إلى الحقول الخاصة بـ Graph، على الرغم من أن Graph يستطيع الوصول إلى الحقول الخاصة بـ Vertex.
البدائل
تُوفّر لغة C# ميزة لغوية مشابهة، وإن لم تكن مكافئة، internalوهي كلمة "access modifier" التي تسمح للفئات داخل نفس التجميعة بالوصول إلى الأجزاء الخاصة من فئات أخرى. وهذا يُشابه وضع علامة "صديق" على كل فئة في نفس التجميعة؛ وتُعدّ الفئات الصديقة أكثر دقةً في تحديدها.
ستضطر لغات البرمجة التي تفتقر إلى دعم فئات الأصدقاء، أو ميزة لغوية مماثلة، إلى تطبيق حلول بديلة لتحقيق واجهة آمنة قائمة على الأجزاء لهيكل البيانات. ومن أمثلة هذه الحلول البديلة ما يلي:
- اجعل حقول الأجزاء عامة. يقلل هذا الحل من التغليف من خلال السماح بانتهاك ثوابت بنية البيانات من الخارج.
- انقل جميع البيانات الهيكلية القابلة للتغيير من الجزء إلى بنية البيانات، وأضف توجيهًا غير مباشر من كل جزء إلى بنية بياناته. يُغيّر هذا الحل تنظيم بنية البيانات، ويزيد من استهلاك الذاكرة في الحالات التي لا حاجة فيها لهذه المعلومات.
ملكيات
- الصداقات ليست متناظرة - إذا
Aكانت الطبقة صديقة للطبقةB، فإن الطبقةBليست بالضرورة صديقة للطبقةA. - الصداقات ليست متعدية - إذا
Aكانت الطبقة صديقة للطبقةB، والطبقةBصديقة للطبقةC، فإن الطبقةAليست بالضرورة صديقة للطبقةC. - لا تُورَث الصداقات ؛ فإذا
Baseكانت الفئة `class` صديقةً للفئة `class`X، فإن الفئة الفرعية `class`Derivedلا تُصبح تلقائيًا صديقةً للفئة `class`X؛ وإذاXكانت الفئة `class` صديقةً للفئة `class`Base، فإن الفئة `class`Xلا تُصبح تلقائيًا صديقةً للفئة الفرعية `class`Derived. مع ذلك، إذاYكانت الفئة `class` صديقةً للفئة الفرعية `class`Derived،Yفستتمكن الفئة `class` أيضًا من الوصول إلى الأجزاء المحمية من الفئة `class`Base، تمامًا كماDerivedتفعل الفئة الفرعية `class`.
مراجع
- ↑ "9 المزيد عن لغة C++" .
- ↑ جودي هاغينز، آرثر أودواير (22 مارس 2024). "P2893R3 - الأصدقاء ذوو المتغيرات" . open-std.org . ISO/IEC 14882 لغات البرمجة - C++، ISO/IEC JTC1/SC22/WG21.
روابط خارجية
- الفصل الدراسي (برمجة الحاسوب)
