Loop Lunatic
Loop Lunatic
خواندن ۱۰ دقیقه·۲ سال پیش

سوالات استخدامی برای برنامه نویسان ++C

سلام

امیدوارم که حالتان خوب باشد

از این به بعد قرارِ برای اینجا بیشتر وقت بزارم لذا برای شروع می‌خوام یک سری از سؤال‌هایی که تو مصاحبه‌های استخدامی برنامه‌نویسی می‌پرسم رو برایتان بگزارم البته با جواب و سعی می‌کنم جواب‌ها هم کامل باشند فقط توجه داشته باشید که من برای خودم فقط سؤال‌ها را دارم و نوشتن جواب‌ها با مثال کمی سخت است و بعضی از کلمه‌ها هم واقعاً معادل فارسی مناسبی ندارند، لذا این‌گونه مطالب خالی از اشکال نیست. خلاصه اینکه اینجانب نیز خالی از اشکال نبوده و نیستم بنابراین اگر جایی اشکالی دیدید حتماً پیام بگذارید تا درستش کنم یا اگر مطلبی گنگ بود اطلاع بدید تا توضیحات بیشتری برایش اضافه کنم.امیدوارم که به دردتان بخورد.

درباره virtual function درC++ توضیح دهید و بگویید چگونه کار می‌کند؟
یک virtual function تابعی است که در یک کلاس پایه تعریف‌شده است که می‌تواند توسط یک تابع در یک کلاس مشتق شده بازنویسی شود. این موضوع اجازه استفاده از چندریختی را در سی پلاس پلاس می‌دهد. در هنگامی‌که یک virtual function از طریق اشاره‌گر کلاس پایه یا مرجع فراخوانی می‌شود، تابعی که درواقع فراخوانی می‌شود به نوع شیء اشاره‌شده یا ارجاع داده‌شده بستگی دارد، نه به نوع خود اشاره‌گر یا مرجع، درواقع تابعی که فراخوانی می‌شود در زمان اجرا بر اساس نوع شی‌ء تعیین می‌شود، نه در زمان کامپایل بر اساس نوع اشاره‌گر یا مرجعی که برای فراخوانی تابع استفاده می‌شود.

در اینجا یک مثال برای نشان دادن نحوه عملکرد توابع مجازی آورده شده است:

https://gist.github.com/iarash84/46a63831b45516d039356591c7c92f3e

در این مثال، یک کلاس پایه Shape با تابع مجازی draw() داریم. ما همچنین یک کلاس مشتق شده Circle داریم که تابع draw() را با پیاده‌سازی خودش override می‌کند. در تابع main() یک شی‌ء Circle ایجاد می‌کنیم و آن را به یک اشاره‌گر از نوع Shape* اختصاص می‌دهیم. وقتی تابع draw() را از طریق اشاره‌گر فراخوانی می‌کنیم، مکانیسم تابع مجازی تضمین می‌کند که تابع Circle::draw() فراخوانی می‌شود، حتی اگر اشاره‌گر از نوع Shape* باشد.

این ارسال dynamic از توابع مجازی از طریق استفاده از یک جدول تابع مجازی (vtable) که برای هر کلاسی که دارای توابع مجازی است ایجاد می‌شود، به دست می‌آید. vtable حاوی اشاره‌گرهایی به توابع مجازی کلاس است و هر شی‌ء از کلاس حاوی یک اشاره‌گر به vtable مربوط به خود است. هنگامی‌که یک تابع مجازی بر روی یک شیء فراخوانی می‌شود، از نشانگر vtable شی‌ء برای جستجوی تابع صحیح برای فراخوانی، بر اساس نوع شیء استفاده می‌شود

تفاوت بین stack و heap در تخصیص حافظه چیست؟

تخصیص حافظه در C++ به دو بخش stack و heap تقسیم می‌شود. متغیرهای اعلام‌شده در داخل یک تابع در stack ذخیره می‌شوند و با خروج تابع به‌طور خودکار آزاد می‌شوند. تخصیص حافظه پویا بر روی heap با استفاده از توابعی مانند new و delete انجام می‌شود. حافظه تخصیص داده‌شده روی heap باید به‌صراحت با استفاده از عملگر حذف آزاد شود.

در مورد template در C++ و اینکه چگونه می‌توان از آن‌ها استفاده کرد توضیح دهید؟

در C++، یک template مکانیزمی است که به شما امکان می‌دهد کلاس‌ها یا توابعی عمومی ایجاد کنید که می‌توانند با انواع داده‌های مختلف کار کنند. از template ها می‌توان برای نوشتن کدهایی استفاده کرد که قابل‌استفاده مجدد هستند و می‌توانند انواع داده‌ها را بدون نیاز به نوشتن توابع یا کلاس‌های جداگانه برای هر نوع مدیریت کنند.

در اینجا یک مثال از یک تابع قالب است که حداکثر مقدار را در یک آرایه پیدا می‌کند:

https://gist.github.com/iarash84/a0d154a49fefe51d75edb634533cffb0

در این مثال، تابع findMax یک تابع template است که آرایه‌ای از نوع T و اندازه صحیح را به‌عنوان آرگومان می‌گیرد. از کلمه کلیدی typename برای اعلام T به‌عنوان پارامتر الگو استفاده می‌شود که می‌تواند هر نوع داده‌ای باشد. سپس این تابع از یک حلقه برای تکرار در آرایه و یافتن حداکثر مقدار استفاده می‌کند که در پایان برگردانده می‌شود.

در اینجا مثالی از نحوه فراخوانی تابع findMax با انواع داده‌های مختلف آورده شده است:

https://gist.github.com/iarash84/12207048791b0d9871d99d962dea0204

در این مثال ابتدا یک آرایه صحیح intArr و یک آرایه دوگانه doubleArr را اعلام می‌کنیم و سپس تابع findMax را با هر آرایه و اندازه آن فراخوانی می‌کنیم. این تابع با استفاده از پارامتر الگوی T که در زمان کامپایل با نوع داده واقعی جایگزین می‌شود، می‌تواند هر دو نوع داده صحیح و دوگانه را مدیریت کند.

همچنین می‌توان از template ها برای ایجاد کلاس‌های عمومی استفاده کرد که می‌توانند با انواع داده‌های مختلف کار کنند. در اینجا یک مثال از یک کلاس template است که یک stack را نشان می‌دهد:

https://gist.github.com/iarash84/665be6450f372f106f91b5340b3d6ef4

در این مثال، کلاس Stack یک کلاس template است که دارای پارامتر الگوی T است که نشان‌دهنده نوع داده‌ای است که Stack ذخیره خواهد کرد. کلاس سازنده‌ای دارد که ظرفیت عدد صحیح را به‌عنوان آرگومان می‌گیرد و از آن برای ایجاد آرایه‌ای از نوع T استفاده می‌کند. کلاس همچنین دارای متدهای push و pop است که عناصر را به stack اضافه و حذف می‌نماید.

در اینجا مثالی از نحوه استفاده از کلاس Stack آورده شده است:

https://gist.github.com/iarash84/9871ff0d6fd07da16e39f07351321f94

در مورد constructor و destructor در C++ و دلیل اهمیتشان توضیح دهید؟

یک constructor یک تابع ویژه در یک کلاس است که با ایجاد یک شی‌ء از کلاس به‌طور خودکار فراخوانی می‌شود. Destructor یک تابع ویژه است که به‌طور خودکار هنگامی‌که یک شی‌ء از کلاس از بین می‌رود فراخوانی می‌شود. constructor ها مهم هستند زیرا به شما اجازه می‌دهند تا اعضای داده یک کلاس را مقداردهی اولیه کنید. destructor ‌ها مهم هستند زیرا به شما اجازه می‌دهند منابعی را که توسط شی‌ء اختصاص داده‌شده است آزاد کنید.

تفاوت pass by value و pass by reference در C++ چیست؟

ارسال با مقدار به معنای ارسال یک کپی از داده‌ها به تابع است، درحالی‌که ارسال با مرجع به معنای ارسال یک مرجع از داده‌ها به تابع است. رمز عبور با مرجع کارآمدتر است زیرا از ایجاد یک کپی از داده‌ها جلوگیری می‌کند، اما همچنین به این معنی است که تابع می‌تواند داده‌ها را تغییر دهد.

در مورد abstract class در C++ و تفاوتش با یک interface توضیح دهید؟

یک abstract class کلاسی است که یک یا چند تابع مجازی دارد. نمی‌توان آن را نمونه‌سازی کرد و باید زیر کلاس‌بندی شود. یک interface کلاسی است که فقط دارای توابع مجازی است و که هیچ‌کدام پیاده‌سازی نشده‌اند. interface ها را نمی‌توان نمونه‌سازی کرد و توابع آن باید توسط کلاسی که از آن ارث می‌برد پیاده‌سازی شود.

اشاره‌گر هوشمند در C++ چیست و چگونه کار می‌کند؟

در C++، اشاره‌گر هوشمند کلاسی است که مانند یک اشاره‌گر معمولی رفتار می‌کند اما عملکردهای اضافی مانند مدیریت خودکار حافظه و ایمنی خطاها را ارائه می‌دهد. اشاره‌گرهای هوشمند برای کمک به مدیریت حافظه در C++ با ارائه راه ایمن‌تر و راحت‌تر برای تخصیص و بازیابی حافظه طراحی‌شده‌اند.

سه نوع اشاره‌گر هوشمند در C++ وجود دارد:

1. اشاره‌گر std::unique_ptr: یک اشاره‌گر هوشمند که دارای یک شی‌ء تخصیص دینامیک است و تضمین می‌کند که وقتی‌که unique_ptr از محدوده خارج می‌شود یا تنظیم مجدد می‌شود، شی‌ء حذف می‌شود.

2. اشاره‌گر std::shared_ptr: یک اشاره‌گر هوشمند که دارای یک شی‌ء تخصیص پویا است و مالکیت شی‌ء را با سایر اشیاء shared_ptr به اشتراک می‌گذارد. هنگامی‌که آخرین shared_ptr مالک شی‌ء از محدوده خارج شود یا بازنشانی شود، شی‌ء حذف می‌شود.

3. اشاره‌گر std::weak_ptr: یک اشاره‌گر هوشمند که یک مرجع غیر مالک به یک شی‌ء که توسط shared_ptr مدیریت می‌شود را نگه می‌دارد. ضعیف_ptr به تعداد مرجع شی‌ء کمک نمی‌کند و می‌تواند برای بررسی اینکه آیا شی‌ء هنوز وجود دارد یا خیر، قبل از تلاش برای دسترسی به آن از طریق shared_ptr استفاده شود.

در اینجا مثالی از استفاده از std::unique_ptr برای مدیریت حافظه آورده شده است:

https://gist.github.com/iarash84/706c17d595209b78e9f7b1c8b00fc1a8

در این مثال، ما یک std::unique_ptr ایجاد می‌کنیم تا یک شی‌ء int اختصاص داده‌شده به‌صورت پویا را مدیریت کند. unique_ptr مالکیت شی‌ء را در اختیار می‌گیرد و اطمینان می‌دهد که وقتی unique_ptr از محدوده خارج می‌شود یا بازنشانی می‌شود، حذف ‌شود. ما می‌توانیم مانند یک اشاره‌گر معمولی با استفاده از عملگر * به مقدار شی‌ء دسترسی پیدا کنیم.

سپس از متد reset مربوط به unique_ptr استفاده می‌کنیم تا مقدار شی‌ء را به 10 تغییر دهیم. این به‌طور خودکار شی‌ء قبلی را حذف می‌کند و شی‌ء جدید را جایگزین می‌کند.

در اینجا مثالی از استفاده از std::shared_ptr برای مدیریت حافظه آورده شده است:

https://gist.github.com/iarash84/a640c7bbd80832c546b8f5ff21fe5920

در این مثال، ما یک std::shared_ptr برای مدیریت یک شی‌ء MyClass اختصاص داده‌شده به‌صورت پویا ایجاد می‌کنیم. سپس یک shared_ptr دوم ایجاد می‌کنیم و همان شی‌ء را به آن اختصاص می‌دهیم که تعداد مرجع شی‌ء را به 2 افزایش می‌دهد.

ما می‌توانیم مانند یک اشاره‌گر معمولی با استفاده از عملگر -> به متدهای شی‌ء دسترسی داشته باشیم. همچنین می‌توانیم از متد use_count برای بررسی تعداد مرجع شی‌ء استفاده کنیم.

سپس از روش reset دومین shared_ptr برای آزادسازی مالکیت شی‌ء استفاده می‌کنیم که تعداد مراجع را به 1 کاهش می‌دهد.

یک copy constructor در C++ چیست و چه زمانی فراخوانی می‌شود؟

در C++، یک copy constructor یک سازنده خاص است که با کپی کردن یک شی‌ء موجود، یک شی‌ء جدید ایجاد می‌کند. copy constructor به یک شی‌ء از همان نوع کلاس ارجاع می‌دهد و یک شی‌ء جدید ایجاد می‌کند که یک کپی از شی‌ء ارسال‌شده است.

سازنده کپی در چندین موقعیت فراخوانی می‌شود، ازجمله:

1. وقتی یک شی‌ء با مقدار به یک تابع ارسال می‌شود.

2. هنگامی‌که یک شی‌ء با مقدار از یک تابع برگردانده می‌شود.

3. هنگامی‌که یک شی‌ء با یک شی‌ء دیگر از همان نوع کلاس مقداردهی اولیه می‌شود.

4. هنگامی‌که یک شی‌ء به شی‌ء دیگری از همان نوع کلاس اختصاص داده می‌شود.

در اینجا یک نمونه از سازنده کپی آورده شده است:

https://gist.github.com/iarash84/7017466667fd0ec25005ea1ae7de6743

در این مثال، ما یک کلاس MyClass را با متغیر عضو int m_value تعریف می‌کنیم. ما یک سازنده کپی تعریف می‌کنیم که ارجاع به شی‌ء دیگر MyClass را می‌گیرد و متغیر عضو m_value شی‌ء جدید را با m_value شی‌ء دیگر مقداردهی می‌کند.

سپس سه شیء obj1، obj2 و obj3 از MyClass ایجاد می‌کنیم. اشیاء obj2 و obj3 با استفاده از سازنده کپی ایجاد می‌شوند که وقتی اشیاء با obj1 مقداردهی اولیه می‌شوند، به‌طور خودکار فراخوانی می‌شود.

تفاوت بین array و vector در C++ چیست؟

یک array مجموعه‌ای با اندازه ثابت از عناصر از همان نوع داده است. vector مجموعه‌ای پویا از عناصر از همان نوع داده است که می‌تواند در صورت نیاز بزرگ یا کوچک شود. vector ها نسبت به array ها انعطاف‌پذیرتر هستند، اما ممکن است در برخی موارد کارایی کمتری داشته باشند.

یک operator overloading در ++C چیست و چگونه می‌توان از آن استفاده کرد؟

یک operator overloading اپراتور به شما امکان می‌دهد تا نحوه عملکرد یک اپراتور را برای یک کلاس خاص تعریف کنید. به‌عنوان‌مثال، می‌توانید عملگر + را برای اضافه کردن دو شی‌ء از یک کلاس بازنویسی کنید. operator overloading امکان نوشتن کدی را فراهم می‌کند که بصری‌تر و خواناتر باشد.

در سی پلاس پلاس، operator overloading فرآیند تعریف معانی جدید برای عملگرهای داخلی است، مانند +، -، *، /، =، ==، !=، <، >، <=، >=، << و >>. این به ما امکان می‌دهد از یک عملگر برای انجام عملیات مختلف بر روی انواع مختلف داده‌ها استفاده کنیم.

یک operator overloading با تعریف توابع ویژه به نام توابع عملگر حاصل می‌شود که زمانی که عملگر مربوطه با انواع مشخص‌شده استفاده می‌شود، فراخوانی می‌شوند.

به‌عنوان‌مثال، ما می‌توانیم عملگر + را برای به هم پیوستن دو رشته به‌صورت زیر بارگذاری کنیم:

https://gist.github.com/iarash84/c5f6a3d1f43cc95c8be01869cdba97d5

در این مثال، یک کلاس MyString تعریف می‌کنیم که یک شی‌ء std::string را بسته‌بندی می‌کند. عملگر + را overload می‌کنیم تا دو شی‌ء MyString را به هم متصل کنیم و یک شی‌ء MyString جدید را با نتیجه الحاقی برگردانیم. ما همچنین عملگر << را برای چاپ شی‌ء MyString در کنسول اضافه بارگذاری می‌کنیم.

سپس دو شی‌ء str1 و str2 را از نوع MyString ایجاد می‌کنیم و با استفاده از عملگر + و تابع overloaded operator+ فراخوانی می‌شود تا آن‌ها را به هم متصل کنیم؛ و نتیجه را در یک شی‌ء جدید MyString ذخیره می‌کنیم.

سپس با استفاده از عملگر << که تابع overloaded<< را فراخوانی می‌کند، شی‌ء نتیجه را در کنسول چاپ می‌کنیم.

یک operator overloading می‌تواند در ساده‌سازی نحو کد ما بسیار مفید باشد و آن را بصری و خواناتر کند. بااین‌حال، مهم است که از آن بااحتیاط استفاده کنید و از استفاده بیش‌ازحد operator overloading به روش‌هایی که ممکن است گیج‌کننده یا غیرمنتظره باشد، اجتناب کنید.

موفق و پیروز باشید

ارادتمند شما




interviewcppبرنامه نویسیمصاحبه شغلیshared ptr
از طریق این وبلاگ، قصد دارم دانش و بینش خود را در مورد دنیای کامپیوتر و توسعه نرم افزار با شما به اشتراک بگذارم.
شاید از این پست‌ها خوشتان بیاید