Полные вопросы и ответы на собеседовании по 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» производного класса.
Давайте разберемся, что здесь происходит внутри:
- Для каждого класса, имеющего виртуальную функцию, компилятор поддерживает виртуальную таблицу, содержащую информацию обо всех виртуальных функциях внутри класса.
- Для каждого объекта класса первые 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
Некоторые другие должны знать вопросы:
- Разница между C и C++?
- Знаете ли вы, что такое метапрограммирование? Как это достигается в C++?
- Сколько типов приведения доступно в C++, чем он лучше C?