Полные вопросы и ответы на собеседовании по C++

В статье рассматриваются наиболее часто задаваемые вопросы на собеседованиях по C++ и краткое объяснение каждого вопроса.

1. Что такое ООП?

Объектно-ориентированное программирование — это парадигма программирования, основанная на концепции «объектов» классов. Объекты могут содержать данные в виде полей и связанный код в виде методов. Объекты могут обращаться к своим собственным процедурам и изменять свои поля данных.

2. Что такое перегрузка функций, как она сделала жизнь программиста на C++ лучше, чем программиста на C?

В C++ разрешено создавать функции с одинаковыми именами в одной области видимости. Функции с одинаковыми именами называются перегруженными функциями. Разница, которая должна существовать между двумя функциями с одинаковым именем, заключается в передаваемых параметрах. Перегрузку функций можно выполнить, используя разные типы параметров и изменяя количество аргументов. C, с другой стороны, не поддерживает перегрузку функций, что снижает качество и читабельность кода.

3. Как достигается перегрузка функций?

В приведенной ниже программе есть функция «добавить» с двумя разными реализациями. Один для чисел типа double и другой для целых чисел. Функция имеет то же имя, но другие параметры, так что это явный пример перегрузки функции.

#include<bits/stdc++.h>
using namespace std;
int add(int a, int b){//Add Integers
    return a+b;
}
double add(double a, double b){//Add Double
    return a+b;
}
int main(){
    cout<<"Integer Type Numbers after addition : "<<add(1,2)<<"\n";
    cout<<"Double Type Numbers after addition : "<<add(1.1,2.2);
}
Output:
Integer Type Numbers after addition : 3
Double Type Numbers after addition : 3.3

4. что такое динамическая привязка?

Чтобы понять динамическое связывание, давайте посмотрим, что такое виртуальные функции и что означает связывание.

Привязка относится к процессу преобразования идентификаторов, таких как вызовы переменных и функций, в адреса компилятором.

Виртуальная функция в C++ — это функция, которая определена в «базовом» классе и переопределена в «производном» классе. Это делается путем добавления ключевого слова «виртуальный» в определение функции.
Динамическое связывание: в C++, когда компилятор не может решить, какую функцию вызывать во время компиляции, но вычисляет то же самое при время выполнения, то этот тип привязки известен как динамическая привязка.
Пример динамической привязки — это когда виртуальная функция определена в «базовом» классе и переопределена в «производном» классе, и это Функция вызывается с помощью указателя базового класса, в котором хранится адрес объекта «Производного класса».

#include<bits/stdc++.h>
using namespace std;
class Base{
    public:
        virtual void print(){
            cout<<"Base Class";
        }
};
class Derived : public Base{
    public:
        void print(){
            cout<<"Derived Class";
        }
};
int main(){
    Base *ptr;
    Derived d;
    ptr=&d;
    ptr->print();
}
Output : Derived Class

В приведенной выше программе, когда компилятор компилирует программу, создается указатель с именем «ptr», который инициализируется адресом объекта производного класса, и этот адрес доступен только во время компиляции. С помощью этой информации компилятор может узнать, на какую функцию печати ссылается программист, поэтому в результате вызывается функция «печать» производного класса.

5. Что такое абстрактный класс?

Абстрактный класс в С++ — это класс, у которого нет объекта, и этот класс состоит как минимум из одной чистой виртуальной функции. Нам не разрешено создавать экземпляры абстрактных классов. Этот класс, как следует из его названия, предоставляет нам уровень абстракции в отношении функций, с которыми мы столкнемся в программе, таких как функция печати, описанная ниже.
Пример:

#include<bits/stdc++.h>
using namespace std;
class Base{
    public:
        virtual void print()=0;
};
class Derived : public Base{
    public:
        void print(){
            cout<<"Abstract Class print method defination.";
        }
};
int main(){
    ptr=&d;
    d.print();
}

Чистая виртуальная функция «print» объявлена ​​в абстрактном классе с именем «Base» и определена в классе «Derived», таким образом, абстрактный класс предоставил нам абстракцию того, с чем мы столкнемся. Если производный класс наследует абстрактный класс и не определяет чисто виртуальные функции базового класса, он также становится абстрактным классом.

6. Что происходит внутри виртуальных функций? (vPointer, vTable и т. д.)

Для понимания этой части давайте перейдем к примеру, который мы рассматривали при изучении динамического связывания.

#include<bits/stdc++.h>
using namespace std;
class Base{
    public:
        virtual void print(){
            cout<<"Base Class";
        }
};
class Derived : public Base{
    public:
        void print(){
            cout<<"Derived Class";
        }
};
int main(){
    Base *ptr;
    Derived d;
    ptr=&d;
    ptr->print();
}
Output : Derived Class

Как и в приведенной выше программе, ptr является указателем на базовый класс, но по-прежнему ptr-›print() вызывает функцию «print» производного класса.
Давайте разберемся, что здесь происходит внутри:

  1. Для каждого класса, имеющего виртуальную функцию, компилятор поддерживает виртуальную таблицу, содержащую информацию обо всех виртуальных функциях внутри класса.
  2. Для каждого объекта класса первые 4 байта содержат указатель на vTable класса, к которому он принадлежит, и этот указатель называется vPointer.

Вышеупомянутые два пункта являются причиной того, что ptr в приведенной выше программе, даже будучи указателем базового класса, вызывает функцию печати производного класса, как ptr=&d, где d является объектом производного класса, и его первые 4 байта содержат vPointer, который укажите на vTable класса Derived, который содержит функцию печати.

7. Что такое алмазная проблема?

Проблема, когда два класса наследуют базовый класс, а эти два класса наследуются другим классом, тогда класс, который наследует оба класса, имеет две копии методов и свойств базового класса.

  A
 / \
B   C // B and C inherit A
 \ /
  D // D inherit both B and C so has A two times

Приведенная ниже программа выдаст ошибку:

#include<bits/stdc++.h>
using namespace std;
class Base{
    public:
        int a=10;
};
class Derived1 : public Base{
    public:
        int b=20;
};
class Derived2 : public Base{
    public:
        int c=30;
};
class Derived3 :  public  Derived1, public Derived2{
    public:
        int d=40;
};
int main(){
    Derived3 d;
    cout<<d.a;
}

Ошибка заключается в том, что a встречается дважды в классе Derived3, и компилятор находит неоднозначность в элементе данных a.
Чтобы решить эту проблему, мы виртуально наследуем базовый класс в Derived1 и Derived2, что создает только один экземпляр члена данных a и, таким образом, решает ромб. проблема.
Правильный код будет таким:

#include<bits/stdc++.h>
using namespace std;
class Base{
    public:
        int a=10;
};
class Derived1 : public virtual Base{
    public:
        int b=20;
};
class Derived2 : public virtual Base{
    public:
        int c=30;
};
class Derived3 :  public  Derived1, public Derived2{
    public:
        int d=40;
};
int main(){
    Derived3 d;
    cout<<d.a;
}

8. Что такое перегрузка оператора?

Обычно для добавления двух чисел используется оператор «+», но если мы хотим добавить объекты двух классов, это также возможно, поскольку используется концепция перегрузки этого оператора.

Рассмотрим следующую программу:

#include<bits/stdc++.h>
using namespace std;
class A{
    public:
        int a=10;
};
class B{
    public:
        int a=20;
};
int operator+(A obj1,B obj2){
    return obj1.a+obj2.a;
}
int main(){
    A obj1;
    B obj2;
    cout<<obj1+obj2;
}
Output: 30

В приведенной выше программе мы видим, как добавляются два объекта, потому что мы перегрузили оператор +. Для перегрузки мы определили int как возвращаемое значение функции «оператор+», которая принимает два аргумента, которые являются объектами двух классов. Таким образом, мы смогли добавить два объекта с помощью оператора + . Перегрузка операторов работает одинаково для всех перегружаемых операторов.
Неперегружаемые операторы:

::
.
*.
?:

9. Что такое пространство имен?

Пространство имен — это декларативная область, предоставляющая область действия идентификаторам внутри нее. Пространство имен помогает группировать элементы в группы для обеспечения коллизий при использовании нескольких библиотек. Идентификаторы в одном пространстве имен видны друг другу без квалификации, но для переменных за пределами одного пространства имен необходимо указывать полное имя, например: «std::string».

10. Когда и почему вы будете использовать статические функции в классе?

Статическая функция используется в классе, когда объекты класса используют общую функцию, которой не требуется новая копия для каждого объекта этого класса.

#include<bits/stdc++.h>
using namespace std;
class A{
    public:
        static printName(){
            cout<<"I am object of class A\n";
        }
    int a;
};
int main(){
    A obj1;
    A obj2;
    obj1.printName();
    obj2.printName();
}
Output:
I am object of class A
I am object of class A

В приведенной выше программе для каждого объекта функция «printName» останется неизменной, поэтому ключевое слово static перед определением функции создает только одну общую функцию для каждого объекта класса A.

11. У вас есть представление о стандартной библиотеке шаблонов в C++?

Стандартная библиотека шаблонов в C++ представляет собой набор классов шаблонов, которые обеспечивают прямую реализацию общих структур данных, таких как стек, очередь, список, набор и т. д. STL имеет четыре основные категории:

  • Алгоритмы
  • Контейнеры
  • Функции
  • Итераторы

12. Как обрабатываются ошибки в C++?

В C++ исключения обрабатываются с помощью блоков «try» и «catch». Блок кода, который может вызвать исключение, хранится в блоке try.
Пример:

#include<bits/stdc++.h>
using namespace std;
float divide(float a,float b){
    try{
        if(b!=0){
            return a/b;
        }
        else{
            throw "Divide By Zero Exception\n";
        }
    }
    catch(const char* err){
        cout<<err;
    }
}
int main(){
    cout<<divide(1.2,0);
}
Output: Divide By Zero Exception

Некоторые другие должны знать вопросы:

  1. Разница между C и C++?
  2. Знаете ли вы, что такое метапрограммирование? Как это достигается в C++?
  3. Сколько типов приведения доступно в C++, чем он лучше C?