30 ta C++ intervyu savoli va javobi (+Test!)
C++ intervyusidan muvaffaqiyatli o'tmoqchimisiz? Barcha darajadagi dasturchilar uchun 30 ta C++ intervyu savoli va javobi jamlangan qo'llanmamiz bilan tanishing.
Muallif: William Imoh
C++ intervyulari unchalik oson kechmasligi mumkin. Bugungi recruiter'lar dasturlash tilini qanchalik tushunishingizni va muammolarga qanday yondashishingizni tekshiradigan murakkabroq savollar berishadi.
Agar yaqinda C++ intervyusiga borayotgan bo'lsangiz, recruiter'lar odatda beradigan savol va javoblarni takrorlab olganingiz ma'qul. Bu sizga asoslarni qayta ko'rib chiqishga va recruiter'lar tez-tez so'raydigan ilg'or mavzularda ham o'zingizni erkin his qilishga yordam beradi.
Ushbu qo'llanmada qiyinlik darajasi hamda sinflar, obyektlar, funksiyalar va inheritance kabi muhim OOP tushunchalari bo'yicha guruhlangan 30 ta C++ intervyu savolini o'rganasiz.
TL;DR:
-
30 ta keng tarqalgan C++ intervyu savolini o'rganing
-
Flashcard'lar yordamida o'zingizni sinab ko'ring
-
Sinflar, obyektlar, funksiyalar va inheritance kabi asosiy OOP tushunchalarini takrorlang
-
Ham boshlang'ich, ham ilg'or mavzular bo'yicha bilimingizni mustahkamlang
-
Tilni o'zlashtirishda davom etish uchun C++ roadmap bilan tanishing
Bundan ham uzoqroqqa borish uchun roadmap'dagi AI tutor'dan foydalanib yordam olishingiz va savollar berishingiz, tushunchalarni anglab olishingiz, natijalaringizni kuzatib borishingiz hamda shaxsiylashtirilgan o'quv maslahatlarini olishingiz mumkin.
C++ intervyusiga tayyorlanish bo'yicha maslahatlar
C++ bo'yicha kod yozish ko'nikmalaringiz sinovdan o'tkaziladigan intervyuga tayyorlanish, ayniqsa yangi boshlovchi bo'lsangiz, qiyin tuyulishi mumkin. Ushbu maslahatlar yaxshiroq tayyorlanishingizga, har bir savolga ishonch bilan javob berishingizga va kod yozish orqali yakuniy saralashgacha yetib borishingizga yordam beradi.
-
Asoslarni puxta o'zlashtiring: Barcha muhim yo'nalishlarni qamrab olish uchun OOP tushunchalari, xotirani boshqarish, ma'lumotlar tuzilmalari va algoritmlar kabi asoslardan boshlang. Bu tushunchalar haqida o'qing va sintaksisni mashq qiling.
-
Standart kutubxonani biling: Standard Template Library (STL) bilan yaqindan tanishing. U zamonaviy C++ dasturlashning asosiy qismi hisoblanadi. Agar C++ bo'yicha ma'lum tajriba talab qilinadigan lavozimga ariza topshirayotgan bo'lsangiz, container'lar, algoritmlar va smart pointer'larni o'rganing.
-
Muammo yechish va debugging'ni mashq qiling: Sizdan doskada yoki umumiy muharrirda biror muammoni yechadigan kod yozish so'ralishi mumkin. Xuddi shunday, kod allaqachon yozilgan bo'lib, undagi bug'larni tuzatish sizning vazifangiz bo'lgan debugging topshirig'i ham berilishi mumkin. Bunday vaziyatlarga tayyorlanish uchun internetda C++ bo'yicha amaliy testlarni topishingiz mumkin.
-
Ishingizni aniq va tushunarli tushuntiring: Shunchaki ishlaydigan kod yetarli emas. Uning ortidagi mantiqni tushuntira olishingiz kerak.
-
Eng so'nggi imkoniyatlarni takrorlang: C++11, C++14, C++17 va C++20 bilan ko'plab yangiliklar kiritilgan. Yangi imkoniyatlarni va ulardan turli ilovalarni dasturlashda qanday foydalanish mumkinligini o'rganing.
-
Xulq-atvorga oid savollarga tayyorlaning: Hiring manager'lar siz ularning dasturlash jamoalariga qanchalik mos kelishingizni ham bilishni xohlaydi. Ular sizning tajribangiz va shaxslararo muloqot ko'nikmalaringiz bilan qiziqishadi. C++ bilan bog'liq texnik savollardan tashqari, avvalgi loyihalaringiz va boshqalar bilan hamkorlikdagi ishlaringizni muhokama qilishga ham tayyorlaning.
Flashcard'lar yordamida o'zingizni sinab ko'ring
Ushbu flashcard'lardan foydalanishingiz yoki savollarni ro'yxat ko'rinishida ko'rish uchun quyidagi savollar ro'yxati bo'limiga o'tishingiz mumkin.
Savollar ro'yxati
Agar savollarni ro'yxat ko'rinishida ko'rishni afzal ko'rsangiz, ularni quyida topasiz.
Boshlovchilar uchun C++ intervyu savollari (asosiy tushunchalar)
1. C++ nima? U C'dan nimasi bilan farq qiladi?
C++ — C tilining kengaytmasi sifatida yaratilgan umumiy maqsadli, obyektga yo'naltirilgan dasturlash tili. C structured programming'ga e'tibor qaratsa, C++ sinflar, inheritance, polymorphism va encapsulation kabi OOP imkoniyatlarini qo'shadi. U, shuningdek, function va operator overloading'ni hamda template'larni qo'llab-quvvatlaydi, bu esa kodni qayta foydalanishga qulayroq, samaraliroq va moslashuvchanroq qiladi.
2. C++'da access modifier'lar nima?
Access modifier'lar sinf member'lari va data member'lariga kim murojaat qila olishini boshqaradi. Ular encapsulation kabi obyektga yo'naltirilgan dasturlash tamoyillariga rioya qilinishini ta'minlashga yordam beradi. C++'da uchta access modifier mavjud:
-
public: Istalgan joydan murojaat qilish mumkin, sinfdan tashqaridagi sinf obyektlari ham bu member function'larni chaqira oladi. -
private: Faqat shu sinfning o'zi ichida murojaat qilish mumkin. Private va protected member'larga boshqa sinflar yoki friend function'lar, agar friend class sifatida e'lon qilinmagan bo'lsa, bevosita murojaat qila olmaydi. Agar hech qanday access specifier ko'rsatilmasa, sukut bo'yicha sinflar uchun private, struct'lar uchun esa public qo'llaniladi. -
protected: Sinf ichida va uning derived class(lar)ida murojaat qilish mumkin. Derived class obyekti bu member'lardan foydalana oladi, ammo ierarxiyadan tashqaridagi kod foydalana olmaydi.
class Example {
private:
int secret; // only within class
public:
int value; // accessible anywhere
protected:
int code; // accessible in derived classes
};
3. C++'da std nima?
std — C++'dagi namespace bo'lib, "standard" so'zini anglatadi. U standart kutubxonaning katta qismini o'z ichiga oladi, jumladan input/output stream'lar (cin, cout), satrlar bilan ishlash (string) hamda turli ma'lumotlar tuzilmalari va algoritmlar (vector, sort) kabi muhim qismlarni.
Namespace qanday ishlashiga misol:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!"; // using std::cout
return 0;
}
4. C++'da pointer nima va u reference'dan nimasi bilan farq qiladi?
Pointer o'zgaruvchining xotiradagi manzilini saqlaydi. Har bir pointer o'zining noyob xotira manziliga ega va u ishora qilayotgan qiymatga bevosita murojaat qila oladi. Pointer'lar new va delete yordamida dynamic memory allocation uchun asosiy vosita bo'lib, xotirani qo'lda boshqarish ustidan nazorat beradi. Noto'g'ri ishlatilsa, leak'lar yoki dangling pointer'larga olib kelishi mumkin.
Reference esa mavjud o'zgaruvchi uchun alias hisoblanadi. Ikkalasi o'rtasidagi asosiy farq shundaki, pointer null bo'lishi va boshqa qiymatga qayta tayinlanishi mumkin. Biroq reference null bo'la olmaydi va uni qayta tayinlab ham bo'lmaydi. Shuning uchun u e'lon qilinayotgan paytda initialize qilinishi shart.
int x = 10;
int *p = &x; // pointer
int &r = x; // reference
cout << *p; // dereference pointer -> prints 10
cout << r; // reference -> prints 10
5. Stack xotira va heap xotira o'rtasidagi farq nima?
Stack xotira static allocation'ni (lokal o'zgaruvchilar, funksiya chaqiruvlari) boshqaradi, heap xotira esa dynamic allocation'ni (obyektlar, kattalashadigan yoki kichrayadigan ma'lumotlar) boshqaradi.
Quyidagi jadvalda asosiy farqlar keltirilgan.
| Stack xotira | Heap xotira |
|---|---|
| Compiler tomonidan avtomatik boshqariladi | new va delete yordamida qo'lda boshqariladi |
| Xotirani ajratish va bo'shatish tezroq | Xotirani ajratish va bo'shatish sekinroq |
| Hajmi kichikroq | Hajmi kattaroq |
| Lokal o'zgaruvchilar va funksiya chaqiruvlarini saqlaydi | Dinamik ajratilgan o'zgaruvchilarni saqlaydi |
Misollar:
int a = 10; // stored on stack
int* p = new int(20); // stored on heap
delete p; // free heap memory
6. Pass by value va pass by reference o'rtasidagi farq nima?
Pass-by-value'da ma'lumotlarning faqat nusxasi uzatiladi, asl ma'lumot esa daxlsiz qoladi. Pass-by-reference'da esa haqiqiy ma'lumotning o'zi ulashiladi, shuning uchun har qanday o'zgarish aslga ta'sir qiladi.
Aniqroq taqqoslash uchun quyidagi jadvalga qarang.
| Pass by value | Pass by reference |
|---|---|
| O'zgaruvchining nusxasi yaratiladi va funksiyaga uzatiladi | Haqiqiy o'zgaruvchi reference (&) orqali uzatiladi |
| Funksiya nusxa bilan ishlaydi; ichkaridagi o'zgarishlar aslga ta'sir qilmaydi | Funksiya asl o'zgaruvchi bilan ishlaydi; ichkaridagi o'zgarishlar aslga ta'sir qiladi |
| Katta obyektlar nusxalansa, ko'proq xotira sarflanadi | Nusxa yaratilmagani uchun xotira tejamliroq ishlatiladi |
| Xavfsizroq (asl ma'lumot o'zgarmay qoladi) | Xavfliroq (asl ma'lumot beixtiyor o'zgartirib yuborilishi mumkin) |
| Katta obyektlar uchun biroz sekinroq (nusxalash sababli) | Katta obyektlar uchun tezroq, chunki faqat reference uzatiladi |
| Odatda oddiy data type'lar (int, char, float) uchun ishlatiladi | Odatda obyektlarni o'zgartirishda yoki katta struct/sinflarni samarali uzatish uchun ishlatiladi |
Mana misol:
void byValue(int x) { x = 20; }
void byReference(int &y) { y = 20; }
int main() {
int a = 10, b = 10;
byValue(a); // a remains 10
byReference(b); // b becomes 20
}
C++ bo'yicha chuqurroq bilim talab qilinadigan lavozimlar uchun intervyu oluvchilar ko'pincha o'rta qiyinlikdagi savollarga e'tibor qaratishadi. Bu savollar ilg'orroq tushunchalarni qamrab oladi va bilimingizni ko'rsatish uchun kod misollarini yozishingizni talab qilishi mumkin.
O'rta darajadagi dasturchilar uchun C++ intervyu savollari
7. C++'dagi shallow copy, deep copy va copy constructor'ni tushuntiring
C++'dagi copy constructor — mavjud obyektning nusxasi sifatida yangi obyekt yaratish uchun ishlatiladigan maxsus constructor. Sukut bo'yicha compiler shallow copy constructor taqdim etadi, u obyektning faqat tashqi tuzilmasini nusxalaydi.
Shallow copy obyektni faqat uning tashqi tuzilmasi takrorlanadigan tarzda nusxalashni anglatadi. Agar obyekt ichida pointer'lar yoki reference'lar bo'lsa, asl obyekt ham, nusxa ham baribir bir xil xotiraga ishora qiladi. Bu usul tez, lekin xavfli, chunki bir obyektdagi o'zgarishlar ikkinchisiga ta'sir qilishi mumkin. Bundan tashqari, u double deletion kabi muammolarga sabab bo'lishi mumkin.
Deep copy esa obyektning to'liq, mustaqil nusxasini yaratadi, jumladan obyekt ishora qilayotgan barcha ma'lumotlarni ham nusxalaydi. Bu asl obyekt va nusxa bir-biridan butunlay ajralgan bo'lishini ta'minlaydi, shuning uchun birida qilingan o'zgarish ikkinchisiga ta'sir qilmaydi. Deep copy ko'proq vaqt va xotira talab qiladi, ammo xavfsizroq.
Eslatma: Sukut bo'yicha C++'ning copy constructor'i va copy assignment operator'i, agar ularni override qilmasangiz, shallow copy bajaradi.
Mana shallow copy'ni tushuntiruvchi misol:
#include <iostream>
class DeepCopy {
public:
int* data;
// Constructor
DeepCopy(int value) {
data = new int(value);
}
// User-defined copy constructor (deep copy)
DeepCopy(const DeepCopy& other) {
data = new int(*other.data); // allocate new memory and copy the value
}
~DeepCopy() {
delete data;
}
void print() {
std::cout << "Value: " << *data << std::endl;
}
};
int main() {
DeepCopy obj1(10);
DeepCopy obj2 = obj1; // Deep copy happens here
*obj2.data = 20; // Modifying obj2 does NOT affect obj1
obj1.print(); // Output: Value: 10
obj2.print(); // Output: Value: 20
// Both destructors delete different memory blocks safely.
return 0;
}
Maslahat: Agar sinfingiz ownership'ni boshqarsa (masalan, new/delete), Rule of 3'ni (copy ctor, copy assign, dtor) yoki Rule of 5'ni (move ctor va move assign qo'shiladi) implement qiling. Odatda xotirani qo'lda boshqarishdan qochish uchun RAII type'laridan (
std::string, std::vector,smart pointer'lar) foydalanib, Rule of 0'ga amal qilgan ma'qul.
8. Recursive function nima? Misol keltiring.
Recursive function muammoni kichikroq quyi muammolarga bo'lib yechish uchun o'zini o'zi chaqiradi va recursion'ni to'xtatish uchun kamida bitta base case'ga ega bo'ladi. Muammoning tuzilishi tabiatan recursive bo'lsa (tree'lar, divide-and-conquer, backtracking), recursion'dan foydalanishingiz mumkin.
Misol (factorial + tail recursion varianti):
#include <stdexcept>
long long fact(long long n) {
if (n < 0) throw std::invalid_argument("neg!");
if (n == 0) return 1; // base case
return n * fact(n - 1); // recursive step
}
// tail-recursive helper (may be optimized by some compilers)
long long fact_tail(long long n, long long acc = 1) {
if (n < 0) throw std::invalid_argument("neg!");
return n == 0 ? acc : fact_tail(n - 1, n * acc);
}
Qochish kerak bo'lgan xatolar: Chuqur recursion stack overflow xavfini tug'diradi. Juda chuqur chaqiruvlar uchun iterative yechimlar yoki explicit stack'lardan foydalangan ma'qul. Tree yoki graph muammolarida cheksiz recursion'ning oldini olish uchun visited tekshiruvlarini qo'shishni unutmang.
9. C++'da function overloading nima (compile time polymorphism)?
Function overloading bir xil nomga ega, lekin parametrlar ro'yxati (type'lari yoki arity'si) har xil bo'lgan bir nechta funksiyani aniqlash imkonini beradi. Compiler compile-time'da eng mos variantni tanlaydi.
Mana misol:
#include <stdexcept>
long long fact(long long n) {
if (n < 0) throw std::invalid_argument("neg!");
if (n == 0) return 1; // base case
return n * fact(n - 1); // recursive step
}
// tail-recursive helper (may be optimized by some compilers)
long long fact_tail(long long n, long long acc = 1) {
if (n < 0) throw std::invalid_argument("neg!");
return n == 0 ? acc : fact_tail(n - 1, n * acc);
}
10. C++'da struct va class o'rtasidagi farq nima?
C++'da struct va class o'rtasidagi asosiy farq — ularning member'lari uchun sukut bo'yicha belgilanadigan access specifier. Struct member'lari sukut bo'yicha public, class member'lari esa sukut bo'yicha private bo'ladi. Bundan tashqari, ular aslida bir xil til imkoniyatidir. struct class qila oladigan hamma narsani qila oladi.
Bog'liq ma'lumotlarni guruhlash uchun struct'dan foydalanishingiz mumkin. Uning member'lari sukut bo'yicha public bo'lgani uchun ularga struct'dan tashqarida turib bevosita murojaat qila olasiz.
Har birining sintaksisi: struct S { int a; }; // a is public by default
class C { int a; }; // a is private by default
11. decltype va auto kalit so'zlari nima uchun ishlatiladi?
-
autoo'zgaruvchining type'ini initializer asosida aniqlab oladi. U uzun iterator type'lari, lambda'lar va template'lar uchun juda qulay. -
decltype(expr)expression'ning aniq type'ini (reference'lar va cv-qualifier'lar bilan birga) uni hisoblamasdan beradi.
auto i = 42; // int
const int& cref = i;
decltype(cref) j = i; // j is const int&
template <class T, class U>
auto add(T a, U b) -> decltype(a + b) { return a + b; } // trailing return
Maslahat: Type kontekstdan aniq ko'rinib turganda, kod o'qilishi oson bo'lishi uchun auto'dan foydalaning. decltype'ni esa template'lar va metaprogramming'da ishlating.
12. Friend function nima?
Friend function (yoki class) sinfning private va protected member'lariga murojaat qila oladi. Undan symmetric operator'larni yoki bir-biriga qattiq bog'langan yordamchi funksiyalarni sinfning ichki tuzilishini public tarzda ochib qo'ymasdan implement qilish uchun kam hollarda, ehtiyotkorlik bilan foydalanish mumkin.
Mana misol:
class Box {
double w{0}, h{0}, d{0};
public:
Box(double w, double h, double d): w(w), h(h), d(d) {}
friend double volume(const Box& b); // grant access
};
double volume(const Box& b) { return b.w * b.h * b.d; }
13. C++'da object slicing nima? Undan qanday qochish mumkin?
Object slicing derived obyekt base obyektga by value nusxalanganda yuz beradi va bunda derived qism yo'qoladi. Bu jarayonda derived class'ning qo'shimcha data member'lari "kesib tashlanadi" yoki yo'qoladi, faqat base class'ning member'lari qoladi.
struct Base { virtual ~Base() = default; virtual void f() const {} };
struct Derived : Base { int extra{42}; void f() const override {} };
void takesBaseByValue(Base b); // slicing risk
Derived d;
takesBaseByValue(d); // Derived part sliced away
Object slicing odatda ikki holatda yuz beradi:
-
By value uzatishda: derived class obyekti base class obyektini by value qabul qiladigan funksiyaga uzatilganda
-
Tayinlashda: derived class obyekti base class obyektiga tayinlanganda
Slicing'dan quyidagicha qochish mumkin:
-
Obyektni base'ga pointer yoki reference orqali uzating va saqlang:
base: void takesBaseRef(const Base& b);. -
Smart pointer'lardan (
std::unique_ptr<Base>,std::shared_ptr<Base>) foydalaning va base class'da virtual destructor bo'lishini ta'minlang. -
Ierarxiya bo'ylab value semantics kerak bo'lsa, type erasure yoki
std::variant'dan foydalanishni ko'rib chiqing (dizaynga bog'liq).
14. C++'da abstract class va interfeys o'rtasidagi farq nima? C++'da interfeysga o'xshash xatti-harakatni qanday implement qilasiz?
Abstract class — kamida bitta pure virtual function'ga (= 0) ega sinf. Unda data member'lar, constructor'lar, virtual bo'lmagan funksiyalar va virtual function'larning standart implementation'lari ham bo'lishi mumkin. Uni instantiate qilib bo'lmaydi.
C++'da interface kalit so'zi yo'q. Obyektga yo'naltirilgan dasturlashda interfeys — sinf implement qilishi shart bo'lgan metodlar to'plamini hech qanday implementation tafsilotlarisiz belgilab beradigan shartnoma.
C++'da interfeys bo'lmasa-da, faqat pure virtual function'lar va virtual destructor'dan iborat abstract class yordamida interfeysga o'xshash xatti-harakatga erishishingiz mumkin.
Boshqa muqobillar:
-
C++20 concept'lari template'lar uchun compile-time "interfeys"larini taqdim etadi (runtime polymorphism'siz).
-
Type erasure (masalan,
std::function, maxsus wrapper'lar) inheritance'siz runtime "interfeys"ini taqdim eta oladi.
struct IShape { // interface-like: pure virtuals only
virtual ~IShape() = default; // always virtual dtor in polymorphic base
virtual double area() const = 0;
virtual void draw() const = 0;
};
class Circle final : public IShape {
double r_;
public:
explicit Circle(double r) : r_(r) {}
double area() const override { return 3.141592653589793 * r_ * r_; }
void draw() const override { /* draw circle */ }
};
15. Encapsulation tushunchasini misol bilan tushuntiring.
Encapsulation — OOP'ning asosiy tamoyillaridan biri. U ma'lumotlarni (member variable'larni) shu ma'lumotlar ustida ishlaydigan metodlar (member function'lar) bilan birga sinf deb ataladigan yagona birlik ichiga jamlaydi. U obyektning ayrim qismlariga bevosita murojaatni cheklaydi va ma'lumotlarning yaxlitligini himoya qilish uchun ularni yashiradi. Encapsulation tashqi kodga obyektning ichki holatini bevosita o'zgartirishga yo'l qo'ymaydi. Buning o'rniga siz ma'lumotlar bilan xavfsiz interfeysni ta'minlovchi public metodlar (getter va setter'lar) orqali ishlaysiz.
Mana BankAccount sinfiga misol: unda balance member'i private va encapsulate qilingan.
class BankAccount {
public:
explicit BankAccount(double opening) { deposit(opening); }
void deposit(double amount) {
if (amount < 0) throw std::invalid_argument("neg deposit");
balance_ += amount;
}
void withdraw(double amount) {
if (amount < 0 || amount > balance_) throw std::invalid_argument("bad withdraw");
balance_ -= amount;
}
double balance() const { return balance_; } // read-only accessor
private:
double balance_ {0.0}; // hidden; only modified via methods
};
16. Multiple inheritance nima va u qanday muammolarga olib kelishi mumkin?
Multiple inheritance (MI) sinfga bir nechta base'dan meros olish imkonini beradi: class D : public A, public B {}. MI natijasida yuzaga keladigan keng tarqalgan muammolar:
-
Noaniqlik: ikkita base bir xil member'ni taqdim etadi; member nomini qualify qilish kerak bo'ladi.
-
Diamond problem: umumiy base ikki marta meros qilib olinadi; natijada ikkita subobject, noaniqlik va takrorlanish yuzaga keladi.
-
Murakkablik: coupling'ni va qo'llab-quvvatlash yukini oshiradi.
struct A { int x = 1; };
struct B { int x = 2; };
struct D : A, B {
int f() { return A::x + B::x; } // disambiguate
};
// Diamond with virtual inheritance:
struct VBase { int id = 7; };
struct V1 : virtual VBase {};
struct V2 : virtual VBase {};
struct VDiamond : V1, V2 {
int g() { return id; } // only one VBase subobject
};
MI muammolarini quyidagicha yumshatish mumkin:
-
Diamond pattern'larda yagona base subobject'ni ulashish uchun virtual inheritance'dan foydalaning.
-
Imkon bo'lganda MI o'rniga composition'ni afzal ko'ring.
Mana misol:
#include <memory>
#include <vector>
struct Animal {
virtual ~Animal() = default;
virtual const char* speak() const { return "???"; }
};
struct Dog : Animal {
const char* speak() const override { return "woof"; }
};
struct Cat : Animal {
const char* speak() const override { return "meow"; }
};
int main() {
std::vector<std::unique_ptr<Animal>> zoo;
zoo.emplace_back(std::make_unique<Dog>());
zoo.emplace_back(std::make_unique<Cat>());
for (const auto& a : zoo) { /* prints woof, meow */ a->speak(); }
}
Maslahatlar:
-
Polymorphic base'larga har doim virtual destructor bering.
-
Derived metodlarda override'dan foydalaning; keyingi override'larning oldini olish uchun final'ni qo'llashni ko'rib chiqing.
-
Virtual chaqiruvlar inline bog'lanmaydi (bu ba'zi optimallashtirishlarga to'sqinlik qilishi mumkin), lekin ular runtime'da xatti-harakatni o'zgartirish uchun to'g'ri vosita hisoblanadi.
17. Static data member'lar va oddiy data member'lar o'rtasidagi farq nima?
-
Oddiy (non-static) member: har bir obyekt o'zining nusxasiga ega bo'ladi.
-
Static data member: barcha instance'lar uchun umumiy; birorta ham instance bo'lmasa ham mavjud bo'ladi. Uni sinfdan tashqarida (C++17 gacha) yoki inline static sifatida (C++17+) aniqlang.
-
Static member function:
thispointer'isiz ishlaydi va faqat static member'larga bevosita murojaat qila oladi.
Mana ikkalasiga misollar:
#include <string>
class Session {
public:
Session(std::string user) : user_(std::move(user)) { ++live_count; }
~Session() { --live_count; }
static int live() { return live_count; } // static member function
const std::string& user() const { return user_; }
private:
std::string user_; // normal: per-object
inline static int live_count=0; // C++17+: defined in-class
};
18. “this” pointer nima va u qanday ishlatiladi?
Non-static member function ichida this joriy obyektga ishora qiluvchi pointer (ClassName*) hisoblanadi. U quyidagilar uchun ishlatiladi:
-
Member nomlarini parametrlardan farqlash
-
Method chaining'ni ta'minlash uchun
*this'ni qaytarish -
Move/assignment operator'larini xavfsiz implement qilish (self-assignment tekshiruvi)
-
Pointer kutadigan API'lar uchun obyekt manzilini olish
Const member function'larda this'ning type'i ClassName const* bo'ladi.
Misol:
class Builder { int x_{0}, y_{0};
public: Builder& setX(int x) { this->x_ = x; return *this; } // chaining Builder& setY(int y) { y_ = y; return *this; } // `this->` optional
};
class Resource { int* p_{nullptr};
public: Resource& operator=(Resource&& other) noexcept { if (this != &other) { // self-assignment guard uses `this` delete p_; p_ = other.p_; other.p_ = nullptr; } return *this; // chaining }
};
19. C++'da function overriding nima va base class unga qanday ta'sir qiladi?
Function overriding'da derived class base'dagi bilan bir xil signature'ga ega virtual function uchun yangi implementation taqdim etadi. Qaysi funksiya chaqirilishi runtime'da aniqlanadi (dynamic dispatch). Function overloading'da esa bir xil nomli, lekin parametrlar ro'yxati har xil bo'lgan bir nechta funksiyaga ega bo'lasiz. Bunda qaysi funksiya chaqirilishi compile time'da aniqlanadi.
Mana function overriding'ga misol:
struct Base {
virtual ~Base() = default;
virtual void log(int x) const { /* base impl */ }
};
struct Derived : Base {
void log(int x) const override { /* derived impl */ } // overriding
};
// Overloading example (same scope):
void print(int);
void print(const char*);
20. Function pointer nima va undan qanday foydalaniladi?
Function pointer ma'lum signature'ga ega funksiyaning manzilini saqlaydi, bu esa uni bilvosita chaqirish yoki boshqa joylarga uzatish (callback'lar, table-driven kod) imkonini beradi. Zamonaviy C++'da moslashuvchanlik uchun std::function yoki auto + lambdas'dan foydalaning.
Biroq raw pointer'lar yengil va ABI-stable hisoblanadi.
#include <iostream>
// a free function
int add(int a, int b) { return a + b; }
// pointer to function taking (int,int) returning int
int (*op)(int, int) = nullptr;
int main() {
op = &add; // or just: op = add;
std::cout << op(2, 3); // calls add(2,3)
}
Massivlar/dispatch table'lar bilan:
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int (*ops[])(int,int) = { add, sub, mul };
int apply(int i, int a, int b) { return ops[i](a, b); }
Member function pointer'lar boshqacha sintaksisga ega va obyekt talab qiladi:
struct Calc {
int inc(int x) const { return x + 1; }
};
int (Calc::*mf)(int) const = &Calc::inc;
Calc c;
int y = (c.*mf)(41); // 42
21. Funksiyalarning inline expansion'i unumdorlikka qanday ta'sir qiladi?
Inline expansion funksiya chaqiruvini funksiya tanasi bilan almashtiradi, bu chaqiruv overhead'ini kamaytirishi va keyingi optimallashtirishlarga (constant propagation, loop unrolling) yo'l ochishi mumkin. Biroq u kod hajmini oshirishi (ICache pressure) va ba'zan code bloat tufayli unumdorlikni pasaytirishi mumkin.
inline int square(int x) { return x * x; } // may be inlined
22. C++'da exception handling qanday amalga oshiriladi?
C++'da exception handling uchta kalit so'z yordamida amalga oshiriladi: try, catch va throw. Bu mexanizm dasturga runtime xatolarini tartibli tarzda qayta ishlash imkonini beradi, natijada dastur to'satdan to'xtab qolmaydi.
Misol:
#include <iostream>
#include <stdexcept>
struct Guard { ~Guard(){ std::cout << "cleanup\n"; } };
int compute(bool bad) {
Guard g; // will be destroyed during unwinding
if (bad) throw std::runtime_error("boom");
return 42;
}
int main() {
try {
std::cout << compute(true) << "\n";
} catch (const std::exception& e) {
std::cout << "caught: " << e.what() << "\n";
}
}
23. std::exception va user-defined exception'lar o'rtasidagi farq nima?
std::exception standart ierarxiyaning ildizi bo'lib, virtual what() satrini taqdim etadi. Standart kutubxona xatolari undan derive qilingan (masalan, std::bad_alloc, std::runtime_error, std::out_of_range).
User-defined exception'lar domen kontekstini (identifikatorlar, fayl yo'li, xato kodi) ifodalash imkonini beradi. Agar ular oxir-oqibat std::exception'dan derive qilingan bo'lsa, chaqiruvchi kod ularni umumiy tarzda catch qila oladi va kerak bo'lganda aniq type'larni ham alohida ushlay oladi.
try {
// ...
} catch (const std::exception& e) { // catches std + user exceptions (if derived)
// uniform handling
}
24. RAII (Resource Acquisition is Initialization) exception safety'ga qanday yordam beradi?
RAII resursning lifetime'ini obyektning lifetime'iga bog'laydi, shunda destructor'lar oddiy bajarilish paytida ham, exception unwinding paytida ham tozalashni avtomatik bajaradi. Bu leak'larning oldini oladi va kodni exception-safe qiladi.
RAII bo'lmasa, resurs olingandan keyin, lekin u bo'shatilishidan oldin exception yuz bersa, resurs leak bo'lib qoladi. Siz catch bloklarida tozalash kodini qo'lda yozishingizga to'g'ri keladi, bu esa zerikarli va xatolarga moyil ish.
Misol:
#include <fstream>
#include <memory>
#include <vector>
std::vector<int> readAll(const char* path) {
std::ifstream in(path); // RAII for file
if (!in) throw std::runtime_error("open failed");
auto data = std::make_unique<std::vector<int>>(); // RAII memory
int x;
while (in >> x) data->push_back(x);
return *data; // NRVO/move; no leaks even if exceptions occur
}
// Strong guarantee via copy-then-swap
struct Buffer {
std::vector<char> bytes;
void append(const std::vector<char>& more) {
auto tmp = bytes; // copy
tmp.insert(tmp.end(), more.begin(), more.end());
bytes.swap(tmp); // commit
}
};
Concurrency'da RAII: lock guard'lar exception yuz berganda ham unlock qiladi.
#include <mutex>
std::mutex m;
void safe_increment(int& x) {
std::lock_guard<std::mutex> lk(m); // unlocks on exit or exception
++x;
}
Bu bo'lim asoslardan tashqariga chiqadi va C++ tushunchalarini real hayotiy vaziyatlarda qanchalik yaxshi qo'llay olishingizga e'tibor qaratadi. Savollar ko'pincha performance optimization, xotirani boshqarish va design pattern'lar kabi mavzularga bag'ishlanadi, ularga ishonch bilan javob berish uchun esa amaliy tajriba kerak bo'ladi.
Shuningdek, smart pointer'lar, move semantics va concurrency kabi yangiroq va kamroq tanish C++ imkoniyatlariga ham duch kelishingiz mumkin. Bu sohalar intervyu oluvchilarga tilni endigina o'rgangan dasturchilar bilan undan murakkabroq vaziyatlarda foydalangan dasturchilar o'rtasidagi farqni ko'rishga yordam beradi.
Ilg'or darajadagi C++ intervyu savollari
25. C++'dagi template'larni tushuntiring va oddiy misol keltiring.
Template'lar kodni type'lar (va C++11/14/17/20'dan beri qiymatlar) bo'yicha parametrlash orqali generic programming'ni ta'minlaydi. Compiler template ishlatilgan joyda aniq instantiation'larni generatsiya qiladi.
Mana turli template'lar va ular nima qilishi:
-
Function template'lar algoritmlarni umumlashtiradi.
-
Class template'lar ma'lumotlar tuzilmalarini umumlashtiradi.
-
Non-type template parametrlari qiymatlarni qabul qiladi (masalan, o'lchamlarni).
-
Constraint'lar yoki Concept'lar (C++20) yaroqli template argumentlarini cheklaydi.
// function template
template <class T>
T max_of(T a, T b) { return (a < b) ? b : a; }
// class template with non-type parameter
template <class T, std::size_t N>
struct FixedArray {
T data[N]{};
constexpr std::size_t size() const { return N; }
};
// C++20 constrained template
#include <concepts>
template <std::totally_ordered T>
T min_of(T a, T b) { return (b < a) ? b : a; }
Qochish kerak bo'lgan xatolar:
Ko'plab instantiation'lar keltirib chiqaradigan code bloat'dan ehtiyot bo'ling; aniqroq diagnostika uchun auto return yoki decltype(auto) va concept'larni afzal ko'ring.
26. Standard Template Library (STL) nima? Keng qo'llaniladigan ba'zi container'larni sanab bering.
STL — C++ standart kutubxonasining generic algoritmlar, container'lar, iterator'lar va function object'lardan iborat poydevori.
STL'dagi ba'zi keng tarqalgan container'lar:
-
Sequence: std::vector, std::array, std::deque, std::list, std::forward_list
-
Associative (tartiblangan, tree-based): std::set, std::multiset, std::map, std::multimap
-
Unordered (hash-based): std::unordered_set, std::unordered_map, std::unordered_multiset, std::unordered_multimap
-
Container adaptor'lar: std::stack, std::queue, std::priority_queue
-
Algoritmlar (iterator range'lari ustida): std::sort, std::find, std::accumulate, std::transform, std::binary_search va hokazo.
Mana bitta misol:
#include <vector>
#include <algorithm>
#include <numeric>
std::vector<int> v{3,1,4,1,5};
std::sort(v.begin(), v.end());
int sum = std::accumulate(v.begin(), v.end(), 0);
27. C++11 va undan keyingi versiyalardagi lambda expression'lar nima?
Lambda'lar — ixtiyoriy capture'larga ega inline, anonim function object'lar. Ular ixcham callback'lar va algoritmlar yozish imkonini beradi.
Sintaksis: [capture] (params) mutable noexcept -> ret { body }
Mana misol:
#include <vector>
#include <algorithm>
#include <iostream>
std::vector<int> v{5,2,9,1};
int pivot = 4;
// capture by reference:
int count_lt = std::count_if(v.begin(), v.end(), [&](int x){ return x < pivot; });
// init-capture (move):
auto big = [buf = std::vector<int>(1000)](int x){ return x + buf.size(); };
// generic lambda:
auto add = [](auto a, auto b){ return a + b; };
std::cout << add(1, 2.5) << "\n";
28. C++11'dagi move semantics nima va u nima uchun muhim?
Move semantics resurslarni qimmatga tushadigan deep copy'lar o'rniga temporary yoki muddati tugayotgan obyektlardan o'tkazish (move qilish) imkonini beradi, bu esa unumdorlikni oshiradi va exception safety'ni yaxshilaydi.
-
Rvalue reference'lar (T&&) temporary'larga bog'lanadi.
-
Move constructor / move assignment resurslarni o'zlashtirib oladi.
-
std::move(x) move qilishni yoqish uchun qiymatni rvalue'ga cast qiladi (o'zi hech narsani move qilmaydi).
-
Imkon bo'lganda move'larni noexcept deb belgilang (STL container'lari reallocation paytida strong guarantee berish uchun bunga tayanadi).
Misol:
#include <vector>
#include <utility>
struct Buffer {
std::vector<char> data;
Buffer() = default;
Buffer(const Buffer&) = default; // copy
Buffer& operator=(const Buffer&) = default;
Buffer(Buffer&& other) noexcept : data(std::move(other.data)) {}
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) data = std::move(other.data);
return *this;
}
};
Buffer make_buffer() {
Buffer b; b.data.resize(1'000'000);
return b; // NRVO or move
}
29. Lvalue va rvalue nima?
Lvalue — xotirada aniq joyni egallaydigan va nomi orqali murojaat qilish mumkin bo'lgan obyekt. U bitta expression doirasidan tashqarida ham mavjud bo'ladi va odatda uning manzilini & operator'i bilan olish mumkin.
int x = 5; // x is an lvalue
x = 10; // assignment works because x refers to a memory location
int* p = &x; // ok, you can take the address of x
Rvalue — doimiy xotira manziliga ega bo'lmagan vaqtinchalik qiymat. U odatda expression hisoblanayotganda hosil bo'ladi va ko'pincha tayinlashlarning o'ng tomonida uchraydi.
int y = x + 5; // (x + 5) is an rvalue
y = 42; // 42 is an rvalue literal
- SFINAE nima? U template specialization bilan qanday bog'liq?
SFINAE — Substitution Failure Is Not An Error iborasining qisqartmasi. Bu C++ template metaprogramming qoidasi bo'lib, unga ko'ra template substitution muvaffaqiyatsiz tugasa, compiler xato bermaydi, aksincha o'sha nomzodni ko'rib chiqiladiganlar qatoridan chiqarib tashlaydi. Bu imkoniyat ko'pincha template specialization uchun, berilgan type'larga qarab ma'lum funksiyalarni yoqish yoki o'chirish maqsadida ishlatiladi.
#include <type_traits>
#include <iostream>
// Overload only if T is an integral type
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
print(T) {
std::cout << "Integral type\n";
}
// Overload only if T is a floating-point type
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
print(T) {
std::cout << "Floating-point type\n";
}
int main() {
print(42); // Integral type
print(3.14); // Floating-point type
}
Template specialization'da template muayyan type'lar bilan o'zini qanday tutishi uchun explicit qoidalar yozasiz.
template <typename T>
struct Traits; // Primary template
template <>
struct Traits<int> { // Explicit specialization
static const char* name() { return "int"; }
};
template <>
struct Traits<double> {
static const char* name() { return "double"; }
};
Yakuniy fikrlar
Ushbu C++ intervyu qo'llanmasi boshlang'ich, o'rta va ilg'or darajalardagi asosiy tushunchalarning aksariyatini qamrab oladi. Asoslarni takrorlang, ushbu qo'llanmadagi misol dasturlarni ko'rib chiqing va eng muhimi, roadmap qo'llanmasidagi loyiha g'oyalari ustida ishlashni boshlang hamda fikr-mulohaza olish uchun yechimlaringizni hamjamiyat bilan baham ko'ring.
Har doimgidek, mashq mahoratni mukammal qiladi. Flashcard'lar bilan mashq qilish yoki qo'shimcha loyihalar va mashqlar ustida ishlash sizni boshqa nomzodlardan ajratib turishga yordam beradi.
Va unutmang, ular faqat kod yozish ko'nikmalaringizni emas, balki tushunchalar bo'yicha umumiy bilimingizni, o'zingizga bo'lgan ishonchingizni hamda muloqot va jamoada ishlash kabi soft skill'laringizni ham sinab ko'rishni xohlaydi. Shuning uchun o'zingizni har tomonlama tayyor va g'alaba qozonishga shay nomzod sifatida ko'rsating.
Agar ko'nikmalaringizni yanada mustahkamlash uchun tizimli yo'l kerak bo'lsa, tilni bosqichma-bosqich o'zlashtirish bo'yicha qo'llanma uchun C++ roadmap'iga qarang.