بخشی از مقاله

مقايسه زبان‌هاي برنامه‌نويسي C # و جاوا

مقدمه
بسياري از زبان‌هاي برنامه‌نويسي امروزي از اين قرارند: C++,C ، Javad , C# , COBOL , Microsoft Visual Basic و غيره. با وجود اين همه زبان، يك مهندس نرم‌افزار چگونه تصميم مي‌گيرد كه كداميك از آنها را براي يك پروژه استفاده كند. گاهي اوقات، يك زبان به اين دليل انتخاب مي‌شود كه توليد كنندگان يك شركت كار با آن را دوست دارند و يا مي‌شناسند، كه اين مي‌تواند يك دليل منطقي باشد. گاهي اوقات يك زبان به دليل جديد بودن و فوق العاده بودنش انتخاب مي‌شود، كه اين يك ابزار بازاريابي براي جلب نظر عمومي به يك محصول مي‌باشد، و ممكن است اين دليل منطقي به نظر نرسد. در حالت ايده‌آل، يك زبان برنامه‌نويسي بايد بر مبناي توانايي‌هاي آن جهت اجراي يك كار خاص انتخاب شود و حل يك مشكل بايد تعيين كننده زبان باشد.


ما تنها به مقايسه زبان‌هاي C# و جاوا مي‌پردازيم. برخي زبان‌ها، همچون C++ و پاسكال، نيز در اين مقايسه استفاده مي‌شوند، اما تنها براي كمك به اثبات انگيزه‌هاي بالقوه براي ايجاد زبان‌هاي برنامه‌نويسي جديدتر با ويژگي‌هاي جديدتر. اگر در زبان قديمي‌تر ضعف‌هايي وجود دارد و در زبان جديدتر اين ضعف‌ها ديده نمي‌شوند و يا از نظرها پنهان شده‌اند، اين مسئله مي‌تواند بيانگر انگيزه معماران در انجام برخي تغييرات در زبان جديدتر باشد. شناخت اين انگيزه اغلب حائز اهميت است،‌ چرا كه در غير اينصورت انتقاد هدف‌دار از يك زبان غيرممكن مي‌شود.


مثلا، اگر ويژگي معروفي كه در زبان قبلي وجود داشته از زبان جديدتر حذف شود، يك توليد كننده برنامه كاربردي ممكن است احساس كند كه زبان جديدتر جايگزين با ارزشي براي زبان قبلي نيست، چرا كه قدرت زبان قبلي را ندارد.

هر چند كه زبان جديدتر ممكن است واقعا ويژگي‌هاي موثري را در اختيار او قرار دهد و او را از گرفتار شدن در برخي مشكلات شناخته شده حفظ نمايد.
توليد جاوا به قبل C# باز مي‌گردد، و C# جداي از ديگر زبان‌ها ايجاد نشد. كاملا طبيعي است كه C# در برگيرنده نقاط قوت و ضعف جاوا است، درست مانند جاوا كه برگرفته از Objective – C بود و آن هم برگرفته از C و به همين ترتيب.


بنابراين، C# نبايد متفاوت از جاوا باشد. اگر جاوا كامل بود، ديگر دليلي براي ايجاد C# وجود نداشت. اگر C# كامل باشد، ديگري دليلي براي ايجاد زبان برنامه‌نويسي جديدتر وجود ندارد. بهرحال، آينده نامشخص است، و هم اكنون C# و جاوا زبان‌هاي برنامه‌نويسي شي‌ءگراي خوبي هستند.
شباهت‌هاي بين C# و جاوا


از نقطه نظر توليد كننده برنامه كاربردي، C# و جاوا كاملا شبيه هم هستند، در اين بحث به شباهت‌هاي اصلي اين دو زبان خواهيم پرداخت.
تمامي آبجكت‌ها مرجع هستند


انواع مرجع‌ها بسيار شبيه اشاره‌گرها (pointer) در C++ هستند، به خصوص وقتي كه شناسه‌اي را براي برخي نمونه‌هاي جديد كلاس تنظيم مي‌كنيد. اما هنگام دستيابي به نمونه‌هاي داده‌ها در C++ است كه در پشته ايجاد مي‌شوند. تمامي نمونه‌هاي كلاس با استفاده از اپراتور جديد در هيپ ايجاد مي‌شوند، اما استفاده از delete مجاز نيست چرا كه هر دو زبان از روش‌هاي garbage collection متعلق به خود استفاده مي‌كنند.
Garbage Collection


طبيعتا، ياري نكردن حافظه مشكل بزرگي در زبان‌هاي نظير C++ است. اين فوق‌العاده است كه شما بتوانيد بطور پويا نمونه‌هاي كلاس را در زمان اجرا در هيپ ايجاد كنيد، اما مديريت حافظه مي‌تواند مشكل‌ساز باشد.


C# و جاوا هر دو داراي garbage collection توكار هستند. به عبارتي براي آزادسازي حافظه ديگر نيازي به فراخواني delete نيست. هيچ زباني اجازه تسهيم كردن Object اي را كه قابل مصرف است به شما نمي‌دهد. اما ممكن است از شما خواسته شود تا new را حتي بيشتر از آنچه كه دوست داريد، فرا بخوانيد. علت اين مسئله آن است كه در هر دو زبان تمامي Object ها در هيپ ايجاد مي‌شوند، به اين معني كه چنين چيزي در هر زباني قابل قبول نيست.

Class BadaBing
{
Public BadaBing ( )
{
}
}
BadaBing badaBing ( ) ; // You can’t create
temporary data but You must use parens on
a constructor
كامپايلر پيغام كوتاهي را در اين باره براي شما مي‌فرستد، چرا كه شما سعي مي‌كنيد ذخيره‌سازي موقتي را ايجاد كنيد. بايد اين كار را انجام دهيد:
BadaBing badaBing = new BadaBing ( ) ;
حال badaBoom ساخته شد و داراي حداقل يك مرجع است. بعد، ممكن است بخواهيد تا از دست آن خلاص شويد.
delete BadaBoom; // illegal in C# and


Java – the compiler will complain
تا جائيكه مي‌خواهيد از badaBoom استفاده كنيد، سپس زمانيكه تصميم مي‌گيريد مرجع خود را به ديگري بدهيد، garbage colletor شما را از دست آن خلاص مي‌كند.
بسياري از توليد كنندگان برنامه‌هاي كاربردي از garbage collection شكايت دارند، شايد آنها كنترل مي‌خواهند. شايد با وجود اين امكان احساس مي‌كنند برنامه‌نويسان واقعي نيستند، چرا كه نمي‌توانند Object اي را كه ايجاد كرده‌اند، delete كنند. شايد داشتن يك زبان بسيار پيچيده و مستعد خطا، مالكيت كد را از جانب توليد كننده اصلي به مدت طولاني تضمين مي‌كند. بدون توجه به اين دلايل garbage collection داراي مزايايي است كه برخي از آنها از اين قرارند:


1ـ عدم ياري حافظه. اين مزيت مشخصي است. هر دو روش garbage collection تضمين مي‌كنند تا در برخي مواقع هنگام اجراي برنامه، تمامي آبجكت را حذف كنند، اما هيچكدام زمان آن را تضمين نمي‌كنند، جز اينكه هيچ آبجكتي حذف نمي‌گردد تا زماني كه حداقل تمام ارجاعات برنامه به آن حذف گردد.


2ـ garbage collection توليد كنندگان را تشويق به نوشتن كدهاي شي‌ءگراي بيشتر مي‌كند. اين يك مزيت ظريف و دقيق است. مثلا، در C++، توليدكنندگاني كه متدهاي كلاس را ايجاد مي‌كنند بايد داده‌هايي را بازگردانند كه معمولا مرجع non-const يا پارامترهاي اشاره‌گر را در هنگام اجراي متد تنظيم مي‌كنند، يا بايد نمونه كلاسي از نوع ديگر را باز گردانند كه تركيبي از تمام داده‌هاي ضروري را نگاه مي‌دارد.


به نظر مي‌رسد مورد دوم بهتر از مورد اول باشد. چه كسي مي‌خواهد تابعي با10 پارامتر را فرا بخواند؟ و پارامترهاي بيشتري كه بين سرويس گيرنده و كد سرويس دهنده رد و بدل مي‌شوند، درگيري بيشتري ايجاد مي‌كند، كه اين اصلا خوب نيست. مثلا، اگر بعدا مشخص شود كه تابعي نياز به بازگرداندن داده‌هاي بيشتري دارد، تنها لازم است اين اجراي تابع با يك افزايش به كلاس مركب، كه ممكن است براي سرويس گيرنده تنها يك recompiler باشد، تغيير داده شود. نه تنها از اين جهت، بلكه زمانيكه يك تابع تنها يك آبجكت را باز مي‌گرداند، اين تابع مي‌تواند با ديگر فراخواني‌هاي تابع تو در تو شود، در حاليكه داده‌هاي بازگشتي

با پارامترهاي in/out مانع اين تو در تويي مي‌شوند. هنگاميكه آبجكت‌ها با اين متد بهتر بازگردانده مي‌شوند، معمولا توليد كننده تنها دو گزينش پيش رو دارد: بازگرداندن يك كپي از داده‌هاي موقت كه در تابع ساخته و مقداردهي اوليه مي‌شوند، يا ايجاد اشاره‌گر جديد آبجكت در تابع، side – effect كردن مقادير derefrence شده آن، سپس بازگرداندن اشاره‌گر، كه مطمئن است، چرا كه كامپايلر، اشاره‌گرها يا داده‌هاي هيپ را در ميان فراخواني‌هاي توابع خراب نخواهد كرد. با وجود اينكه بازگرداندن يك اشاره‌گر داراي مزايايي است (يك سازنده كپي نبايد فراخواني شود بنابراين ممكن است سريعتر باشد، خصوصا با داده‌هاي بزرگتر، زير كلاسي از يك نوع اشاره‌گر مي‌تواند براي گسترده شدن به فراخوانده بازگردانده شود)، اما در C++ از اشكالات جدي نيز برخوردار است: حال سرويس گيرنده بايد با فراخواني delete در اشاره‌گر بازگشتي، به مديريت حافظه توجه كند.


براي اين كار راه‌هايي وجود دارد، يكي از اين راه‌ها شمارش مرجع با استفاده از الگوهاي عمومي است (براي اطلاعات بيشتر به سايت زير مراجعه كنيد.
Ms-help : //MS. VSCC/MS.MSDNVS/vbcon/html/


Vbcon Reference Counting Garbage Collection Object Lifetime. htm
بهرحال، شمارش مرجع به دليل نحوه گرامري الگو كاملا رضايت‌بخش نيست، و اگر زمانيكه طرح‌‌هاي garbage collection جاوا و C# كار مي‌كنند، اجراهاي شمارش چرخه‌ها را به درستي اداره نكند، وضعيت بدتر هم مي‌شود (يك چرخه از شمارش مرجع ساده‌اي استفاده مي‌كند: اگر دو آبجكت به يكديگر ارجاع داشته باشند، و سپس تنها مرجعات بيروني براي هر دو منتشر شود، هيچ كدام delete نمي‌شوند، زيرا هر كدام يك مرجع ديگر دارند، و هر Object تا زمانيكه شماره مرجع آن به صفر برسد delete نمي‌شود. بنابراين، توليد كنندگان معمولا ايمن‌ترين راه را انتخاب مي‌كنند، و تنها يك كپي از نوع كلاس شناخته شده زمان كامپايل را باز مي‌گردانند.


اما از آنجائيكه هر دو زبان C# و جاوا از garbage collection استفاده مي‌كنند، توليد كنندگان تشويق مي‌شوند هنگام نوشتن الگوسازي‌هاي تابع (بجاي استفاده از پارامترهاي داخلي / خارجي) ارجاع جديد به داده‌ها را باز گردانند، كه در اينصورت نيز ترغيب مي‌شوند در جائيكه فراخواننده نبايد از نوع دقيق داده‌ها اطلاعي داشته باشد، زير كلاسي از نوع بازگشتي تعريف شده، يا نمونه كلاسي كه رابط‌ها را اجرا مي‌كند، بازگردانند. بدين طريق توليدكنندگان به راحتي مي‌توانند در صورت نياز، كد سرويس را در آينده بدون متوقف كردن سرويس گيرنده‌هاي آن، با ايجاد زير كلاس‌هاي مخصوص نوع بازگشتي، تغيير دهند. جكت هستند. مشكلاتي كه در تعريف مسئوليت‌هاي clean up وجود دارد از اين قرارند: اگر آبجكتA و آبجكتB، pointer C را به اشتراك بگذارند، آيا آبجكتA بايد C delete كند يا آبجكتB ؟ مشكل همين حذف كردن است: B,A نبايد (و يا نمي‌توانند) C را كه از C# يا جاوا، استفاده مي‌كند delete كنند. آبجكت B,A تا زمانيكه لازم باشد از C استفاده مي‌كنند، سپس زمانيكه ديگر ارجاعي صورت نمي‌گيرد، سيستم مسئول حذف كردنC است. طبيعتا، توليد كننده نيز هنگام دسترسي به داده‌هاي مشترك بايد به قسمت‌هاي اصلي توجه داشته باشد و بايد به روش استاندارد آن را اداره نمايد.


4 ـ برنامه‌ها بايد بطور خودكارصحيح‌تر شوند. توليد كنندگان برنامه كاربردي مي‌توانند بجاي مديريت حافظه، به منطق برنامه و صحت آن توجه و تمركز كنند تا كدي با اشكالات كمتر ايجاد شود. اين مسئله بسيار حائز اهميت مي‌باشد.
جاوا و C# هر دو زبان‌هاي Type – Safe هستند


Saraswat در صفحه وب خود مي‌گويد:يك زبان در صورتي Type–Safe است كه تنها عمليات انجام شده بر روي داده‌هاي آن، از نوع عملياتي باشد كه توسط نوع داده‌ها تصويب مي‌شوند. بنابراين، نتيجه مي‌گيريم كه طبق اين تعريف، C++ ، Type – Safe نيست، حداقل به اين دليل كه يك توليد كننده ممكن است نمونه‌اي از برخي كلاس‌ها را به درگيري تبديل نوع (cast) كند و داده‌هاي نمونه را با استفاده از تبديل نوع غير قانوني و متدها و اپراتورهاي ناخواسته رونويسي نمايد.
جاوا و C# طراحي شدند تا Type–Safe باشند. يك تبديل نوع غير قانوني در صورتيكه غيرقانوني بودن آن نشان داده شود، در زمان كامپايل گير خواهد افتاد، و يا اينكه اگر Object نتواند به نوع جديد تبديل شود، در زمان اجرا يك استثناء به وجود خواهد آمد. بنابراين Type–Safe بودن حائز اهميت است، زيرا نه تنها توليد كننده را مجبور به نوشتن كد صحيح‌تر مي‌كند، بلكه به سيستم كمك مي‌كند تا از دست افراد بي‌توجه در امان بماند.


جاوا و C# هر دو زبان‌هاي شي‌ءگرا هستند
هر كلاسي در هر زباني تلويحا (يا صريحا) يك Object را به زير كلاس‌هايي تقسيم مي‌كند. اين ايده بسيار خوبي است، چرا كه در اينصورت يك كلاس پايه پيش فرض براي هر كلاس توكار (built-in) يا تعريف توسط كاربر ارائه مي‌شود. C++ مي‌تواند تنها با استفاده از اشاره‌گرهاي void اين پشتيباني را شبيه‌سازي كند، به دلايلي از جمله Type – Safe مسئله‌ساز هستند. چرا اين افزايش در زبان جاوا و C# خوب است؟ يك دليل آن اينست كه ايجاد محفظه‌هاي (container) بسيار عمومي مجاز مي‌شود. مثلا هر دو زبان داراي كلاس‌هاي پشته از پيش تعريف شده هستند، كه به كد برنامه كاربردي اجازه مي‌دهند تا هر Object را به يك نمونه پشته مقداردهي شده push كند، سپس pop را فرا بخواند تا مرجع بالايي Object را حذف و به فراخواننده بازگرداند ـ شبيه تعريف قديمي پشته است.

طبيعتا، اين امر مستلزم آن است كه توليد كننده، مرجع حذف شده را به كلاس‌هاي خاص برگرفته از Object تبديل نوع كند، بگونه‌اي كه عمليات معناداري انجام شود، اما در حقيقت نوع تمامي Object هايي كه در هر نمونه پشته وجود دارد بايد از جانب توليد كننده به زمان كامپايل شناسانده شود حداقل به اين دليل كه اگر رابط عمومي كلاس ناشناخته باشد، زمانيكه Object هاي حذف شده را ارجاع مي‌دهد، اغلب كار كردن با آن Objectها دشوار است.


در C++، اكثر توليدكنندگان از تطبيق دهنده محفظه stack در Standard Template Library (STL) استفاده مي‌كنند. براي آنهايي كه با STL آشنايي ندارند، Schildt مي‌گويد كه STL در اوايل سال 1990 توسط Alexander Stepanov طراحي شد و در سال (5) 1994 توسط انجمن ANSI C++ مورد تصويب واقع شده و در دسترس اكثر كامپايلرهاي تجاري C++ و امروزه IDE ها، از جمله eMbdded Visual Tools.NET قرار گرفت. بخصوص اينكه، STL كه مجموعه‌اي از محفظه‌ها، تطبيق دهنده‌هاي محفظه، الگوريتم‌ها و غيره مي‌باشد، توليد برنامه كاربردي C++ را آسان مي‌سازد به اين ترتيب كه به هر كلاس C++ (و ابتدائي‌ترين آنها) اجازه مي‌دهد از طريق تعريف يك الگوسازي عمومي، ساخته و درج شود.


بهرحال، در مورد الگوسازي در C++ مسائلي وجود دارد. مثلا پشته جديد int ها بدين طريق ايجاد مي‌شود:
# include < stack >
using namespace std;
stack < int > intstack;
تعريف الگوسازي مشخص شد، پس تعريف واقعي كلاس پشته int و كد اجرايي با استفاده از آن الگوسازي، در پشت پرده ايجاد مي‌شود. به اين ترتيب طبيعتا كدهاي بيشتري به فايل اجرايي اضافه مي‌گردد. حافظه و درايو ديسك سخت امروزه ارزان است. اما اگر انواع مختلف الگوسازي‌ها توسط يك توليد كننده استفاده شود، مسئله‌ساز مي‌گردد. يك پشته بايد داراي يك constructor يك destructor ، يك push، يك pop و شايد يك متد size و يا Boolean empty باشد. به چيز ديگري نيز نياز ندارد. و تا زمانيكه آن نوع يك Object است، به ديگر نوع‌هاي موجود توجه نمي‌كند.


بهرحال، پشته STL به اين روش كار نمي‌كند. اين كار مستلزم دانستن زمان كامپايل نوعي است كه آن را نگاه مي‌دارد (تعريف الگوسازي مهم نيست، اما تعريف كامپايلر لازم مي‌باشد). و پشته STL شامل مجموعه‌ايي از توابع پشته classic نيست. براي مثال، pop يك تابع void است، توليد كننده ابتدا بايد top را فرا بخواند، كه در اين صورت يك آدرس به بالاي پشته باز مي‌گردد، سپس pop را فرا بخواند، كه اين بدان معني است كه يك عمل هم اكنون به دو عمل تبديل شده

است. مشكل وراثت در C++ به احتمال زياد موجب اين تصميم مي‌شود: اگر تابع pop عنصر را بازگرداند و آن را از stack حذف كند، بايد يك كپي را بازگرداند(آدرس آن عنصر ديگر معتبر نيست). سپس اگر فراخواننده پس از بازرسي آن عنصر را نخواهد، بايد كپي را به push , stack كند. مجموعه اين عمليات روند كندتري دارد، بخصوص اگر نوع stack كاملا بزرگ باشد. بنابراين يك متد بازرسي top اضافه شد كه هيچ‌گونه تاثير جانبي بر تعداد عناصر stack نداشت، و به توليدكننده اجازه مي‌داد تا قبل از حذف يك نمونه كلاس آن را peek كند. اما هنگاميكه يك توليد كننده به عنصر top دسترسي مي‌يابد، برخي توابع را در آن فرا مي‌خواند، وي

ممكن است يك عنصر داخل محفظه (container) را side-effect كند، كه حداقل در برخي سناريوها ممكن است روش خوبي نباشد. معماري اصلي STL ممكن است از توليد كنندگان بخواهد تنها از اشاره‌گرها يا محفظه‌ها استفاده كنند (در حقيقت، اشاره‌گرها مجازند، اما شما همچنان بايد مديريت حافظه را در نظر بگيريد)، كه ممكن است تغيير الگوسازي را تحت تاثير قرار دهد و موجب شود متد pop، يك اشاره‌گر را حذف و به عنصر بالايي بازگرداند، اما مسئله فقدان

garbage collection مجددا مطرح مي‌شود.
به نظر مي‌رسد با وجود محدوديت‌ها و مشكلاتي كه به C++ به ارث رسيده، لازم است تعريف عمومي STL , Stack از ديدگاه كلاسيك تغيير كند كه چنين چيزي اصلا خوب نيست. تنها مزيت قابل بحث در C++ با استفاده از الگوسازي‌هاي پشته در مقايسه با الگوسازي‌هاي پشتهC# و جاوا از اين قرار است: در متد الگوي top، نياز به هيچ‌گونه تبديل نوع نيست، زيرا زمان كامپايلي كه كلاس stack را ايجاد كرده داراي اطلاعاتي درباره نوع آدرسي است كه بايد حفظ كند. اما اگر نظريه بالا توليد كننده را متقاعد كند كه وي حتما بايد بداند از چه نوعي در نمونه stack مختص خود استفاده كند، اين مسئله اهميت كمتري مي‌يابد.


در عوض، رابط‌هاي عمومي مربوط به كلاس‌هاي stack در هر دو زبان جاوا و C# از روش كلاسيك پيروي مي‌كنند: push دادن يك Object به Stack، كه در اين صورت Object در بالا قرار مي‌گيرد، سپس pop كردن stack، كه عنصر بالايي را حذف و باز مي‌گرداند. چرا جاوا C# قادر به انجام اين كار هستند؟ به اين دليل كه آنها هر دو زبان OOP هستند، و شايد مهمتر اينكه، آنها با استفاده از مرجع‌ها از garbage collection پشتيباني مي‌كنند. كد Stack مي‌تواند بسيار جدي باشد، چرا كه مي‌داند سرويس گيرنده نبايد cleanup را اداره كند. و يك Stack مي‌تواند هر Object اي را نگاه دارد.


دلايل زياد ديگري وجود دارد كه چرا ترجيحا بايد از زبان شي‌ءگراي محض ـ توسعه‌پذيري برنامه كاربردي، مدل‌سازي جهان واقعي و غيره استفاده كرد. اما چه چيزي يك زبان شي‌ءگراي محض را تعريف مي‌كند:
ـ زباني كه ايجاد انواع تعريف شده توسط كاربر را كه معمولا يك كلاس نام دارد، مجاز بداند
ـ زباني كه گسترش كلاس‌ها را از طريق ارث‌بري و يا استفاده از يك رابط مجاز بداند
ـ تمام نوع‌هاي ايجاد شده توسط كاربر بطور تلويحي زير كلاس برخي از كلاس‌هاي پايه باشند، كه معمولا Object خوانده مي‌شود.
ـ زباني كه اجازه دهد متدها در كلاس‌هاي مشتق شده، متدهاي كلاس پايه را override كنند
ـ زباني كه اجازه دهد يك نمونه كلاس به يك كلاس خاص‌تر يا معمولي‌تر تبديل شود
ـ زباني كه اجازه دهد ترازهاي امنيتي داده‌هاي كلاس، كه معمولا به صورت public تعريف مي‌شوند، به صورت private , protected تغيير يابند.
ـ بايد overload كردن اپراتور را مجاز بداند
ـ نبايد فراخواني‌هاي عمومي تابع را مجاز بداند يا بايد آن را بسيار محدود كند ـ در برخي كلاس‌ها تابع بايد بيشتر متد يا نمونه رابط باشد.
ـ هر نوع (type) داراي داده و مجموعه عملياتي است كه مي‌تواند در آن نوع اجرا شوند ـ بايد type-safe باشد
ـ داراي توانايي‌هاي خوبي در مديريت حالت استثناء‌ها باشد.


ـ آرايه‌ها بايد Object هاي first-class باشند: يك توليد كننده بايد قادر به پرس و جوي يك Object در اندازه خود و نوعي باشد كه آن را نگاه خواهد داشت.
از آنجائيكه C++ از ابتدا طوري طراحي شده كه سازگار باC باشد، در برنامه‌نويسي شي‌ءگرايي محض سريعا ناسازگاري نشان خواهد داد، زيرا استفاده از فراخواني‌هاي عمومي تابع را حداقل در صورت استفاده از نيازمندي‌ها شرح داده شده فوق، مجاز مي‌داند. و آرايه‌ها در C++ ، Objectهاي first-class نيستند كه اين امر موجب نگراني توليدكنندگان شده است. طبيعتا، اگر يك توليد كننده با ايجاد لفافه‌هاي آرايه، استفاده از وراثت، دوري از فراخواني‌هاي عمومي تابع و غيره از زيرمجموعه‌اي از مجموعه مشخصات C++ استفاده كند، كد مخصوص C++ وي را مي‌توان شي‌ءگرا ناميد. اما از آنجائيكه C++ به شما اجازه انجام كارهايي را مي‌دهد كه در تعريف زبان شي‌ءگراي محض مجاز به انجام آنها نيستنيد، بهتر است آن را هيبريد بناميد.


بنظر مي‌رسد C# و جاوا معيارهاي فوق را برآورده مي‌سازند، بنابراين مي‌توان گفت كه هر دوي آنها زبان‌هاي برنامه‌نويسي شي‌ءگراي محض هستند. به نظر مي‌رسد كه تنها تجربه در برنامه‌نويسي است كه به شما مي‌گويد كه زباني واقعا شي‌ءگرا است يا خير، نه ضرورتا بر اساس يكسري نيازمندي‌هاي سخت و محكم.


تك وراثتي
C# و جاوا تنها يك وراثت را مجاز مي‌دانند. در هر دو زبان، هر كلاسي مجاز به اجراي هر تعداد رابطي است كه نياز دارد. يك رابط چيست؟ رابط شبيه يك كلاس است ـ رابط داراي مجموعه متدهايي است كه مي‌توانند در هر نمونه كلاسي كه آنها را اجرا مي‌كند فراخوني شوند ـ اما رابط از داده‌ها استفاده نمي‌كند و تنها يك تعريف است. هر كلاسي كه يك رابط را اجرا مي‌كند بايد بازاي تمام متدها يا خصوصيات تعريف شده توسط رابط، تمام كد اجرايي را بكار بگيرد. بنابراين، يك رابط بسيار شبيه يك كلاس در C++ است كه داراي تمام توابع مجازي محض مي‌باشد (متدهاي يك construct

 

or خصوصي يا محافظت شده و يك destructor عمومي كه عملكرد جالبي را ارائه نمي‌دهد) و از داده‌هاي اضافي استفاده نمي‌كند.
چرا اين دو زبان همانند C++ از وراثت چندگانه پشتيباني نمي‌كنند؟ Lippman وراثت را به صورت سلسله مراتبي در نظر مي‌گيرد و آن را به صورت نمودار مستقيم غير مدور (DAG) شرح مي‌دهد، بگونه‌اي كه تعريف هر كلاس از طريق يك گره نشان داده مي‌شود و براي هر رابطه پايه به فرزند مستقيم (base-to-direct-child) يك لبه (edge) وجود دارد. بنابراين، مثال زير سلسله مراتب يك پاندا در باغ‌وحش را نشان مي‌دهد.

چه چيزي در اين شكل اشتباه است؟ تا زمانيكه تنها يك وراثت پشتيباني مي‌شود، هيچ اشكالي وجود ندارد. در مورد تك وراثتي، بدون در نظر گرفتن فاصله، تنها يك مسير از هر كلاس پايه به هر كلاس مشتق شده وجود دارد. در وراثت چندگانه، وجود دارد، بازاي هر گره، مجموعه‌اي از حداقل دو كلاس پايه وجود دارد كه يك پايه از خودشان را به اشتراك مي‌گذارند. بنابراين مثال Lippman به اين طريق به نمايش در مي‌آيد:

 

در اين مورد، استفاده صريح از كلاس‌هاي پايه virtual براي يك توليد كننده عادي است، بگونه‌اي كه تنها يك بلوك حافظه بدون توجه به تعداد دفعاتي كه در نمودار وراثت به نمايش در مي‌آيد، بازاي هر نمونه‌سازي كلاس ايجاد مي‌شود. اما اين كار مستلزم آن است كه توليدكننده اطلاعات خوبي از سلسله مراتب وراثتي داشته باشد و اين تضمين نمي‌شود، مگر آنكه وي نمودار را بازرسي كند و كد خود را به درستي براي هر كلاسي كه مي‌سازد يادداشت نمايد. در حاليكه توليد كننده موظف است هرگونه ابهامي را با استفاده از اين طرح حل نمايد، با اين وجود، اين طرح مي‌تواند موجب سردرگمي و كد نادرست گردد. در شيوه تك

وراثتي، چند رابطي (single-inheritance-multiple-interface)، اين مشكل روي نمي‌دهد. در حاليكه اين امكان وجود دارد كه يك كلاس زيركلاس دو يا چند كلاس پايه باشد، در صورتيكه هر پايه رابط مشتركي را اجرا كند، به زيركلاس تقسيم كند،(اگر كلاس One زيركلاس كلاس Two باشد، و Tow رابط I را اجرا نمايد، One نيز I را تلويحا اجرا مي‌كند)، از آنجائيكه رابط‌ها نمي‌توانند از داده‌هاي اضافي استفاده كنند و نمي‌توانند هر اجرايي را ارائه دهند، اين مسئله حائز اهميت

نيست. در اين مورد، هيچ حافظه اضافي را نمي‌توان اختصاص داد و در مورد آن نسخه متد virtual كه بايد فراخواني شود، هيچ ابهامي وجود ندارد چرا كه تك وراثتي است.
وراثت چندگانه خوب است، زيرا در برخي كلاس‌هاي پايه كه مي‌توانند توسط زيركلاس‌ها بكار روند يا مورد استفاده مجدد قرار بگيرند، توليد كننده ممكن است تنها يكبار كد را بنويسد. در تك وراثتي،اگر توليدكننده كلاسي بخواهد كه داراي رفتار كلاس‌هاي چندگانه نامرتبط باشد، لازم است كد را تكرار كند. يك workaround در تك وراثتي، استفاده از رابط‌ها و سپس ايجاد يك كلاس اجرايي جداگانه است كه در واقع عملكردي را براي رفتار مطلوب ارائه دهد، و توابع اجراكننده را در هنگام فراخواني يك رابط، فرا بخواند.

در متن اصلی مقاله به هم ریختگی وجود ندارد. برای مطالعه بیشتر مقاله آن را خریداری کنید