Билеты 34-43

34. Чем инициализируются переменная типа bool по умолчанию?

Локальные переменные (автоматическая область видимости) не инициализируются по умолчанию, то есть их значение неопределено, если их не инициализировать явно. Глобальные переменные, статические переменные и члены классов, имеющие статическую длительность хранения, инициализируются нулём, что для bool означает значение false.

35. Какие варианты приведения типов по иерархии наследования существуют и чем отличаются?

Upcasting (приведение к базовому типу)

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

Downcasting (приведение к производному типу)

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

static_cast

Может использоваться для downcasting, но не выполняет проверку во время выполнения. Его следует применять, если вы уверены в типе объекта.

dynamic_cast

Выполняет проверку типа во время выполнения и возвращает nullptr (для указателей) или генерирует исключение (для ссылок) при неудачном приведении. Его используют для безопасного downcasting, когда базовый класс имеет хотя бы одну виртуальную функцию.

Другие виды приведения

const_cast

Удаляет или добавляет квалификатор const (или volatile).

reinterpret_cast

Выполняет низкоуровневое преобразование между указателями, не связанными с наследованием (небезопасное, платформозависимое).

36. В чем разница между enum и enum class, зачем нужны последние?

В C++ существуют два типа перечислений: классические enum (обычные или "неограниченные" перечисления) и enum class (ограниченные перечисления, введенные в C++11). Вот их ключевые различия и преимущества enum class:

Основные различия

Область видимости (scoping):

enum: Имена перечислителей находятся в той же области видимости, что и само перечисление (может приводить к конфликтам имен)

enum Color { RED, GREEN, BLUE };
enum TrafficLight { RED, YELLOW, GREEN }; // Ошибка: повторное определение

enum class: Имена перечислителей находятся внутри области видимости перечисления

enum class Color { RED, GREEN, BLUE };
enum class TrafficLight { RED, YELLOW, GREEN }; // OK
Неявное преобразование типов:

enum: Неявно преобразуется в целочисленные типы

enum class: Нет неявного преобразования в целочисленные типы

Тип перечисления:

enum: Базовый тип не фиксирован (компилятор выбирает)

enum class SmallEnum : uint8_t { VALUE1, VALUE2 }; // 8 бит

enum class: Можно явно указать базовый тип

Преимущества enum class: Предотвращение конфликтов имен

Типобезопасность

Явное указание размера

enum class NetworkPacketType : uint16_t {
    DATA = 0x0102,
    ACK  = 0x0203
};

enum class: Всегда, когда возможен C++11 и выше (рекомендуемый выбор)

Вывод:

enum class обеспечивают:

  • Лучшую инкапсуляцию (избегают "загрязнения" пространства имен)
  • Повышенную типобезопасность
  • Возможность явного контроля размера
  • Более чистый и поддерживаемый код

37. Есть ли разница между структурой struct и классом class?

Различие между структурами и классами

  • struct – по умолчанию, public права доступа к членам, public наследование
  • class – по умолчанию, private права доступа к членам, private наследование

Стилистические различия

Хотя функционально они почти идентичны, сложились соглашения об использовании: class используют для:

  • Сложных объектов с инкапсуляцией
  • Когда нужны private-члены и методы
  • Для реализации ООП (наследование, полиморфизм)

struct используют для:

  • Простых контейнеров данных (POD — Plain Old Data)
  • Когда все члены должны быть публичными
  • Для совместимости с C

38. Какие есть спецификаторы прав доступа в C++ и в чем отличие между ними?

В C++ существуют три спецификатора доступа, которые определяют видимость членов класса (полей и методов) и базовых классов:

  • public
  • private
  • protected

public

-Доступ: Из любого места программы -Для членов класса:

class MyClass {
public:
    int publicVar;  // Доступно отовсюду
    void publicMethod() {}
};

-Для наследования:

class Derived : public Base {}; // Публичное наследование

protected (ограниченный доступ)

Доступ:

  • Из методов самого класса
  • Из методов производных классов
  • Недоступно извне иерархии наследования

private (максимально закрытый)

Доступ: Только из методов самого класса

Пример

class BankAccount {
private:          // Скрытая реализация
    double balance;

protected:        // Для наследников
    void logTransaction() {}

public:           // Интерфейс
    void deposit(double amount) {
        balance += amount;
        logTransaction();
    }
};

class SavingsAccount : public BankAccount {
public:
    void addInterest() {
        // balance -= 10;  // Ошибка: private
        logTransaction();  // OK: protected
    }
};

int main() {
    SavingsAccount acc;
    acc.deposit(1000);     // OK: public
    // acc.logTransaction(); // Ошибка: protected
}

Когда что использовать:

  • public: Интерфейс класса (что могут использовать все)
  • protected: Для расширения класса в наследниках
  • private: Внутренняя реализация (инкапсуляция)

Эти спецификаторы — основа инкапсуляции в C++, позволяющая скрывать детали реализации и предоставлять четкие интерфейсы.

39. Что такое конструктор и когда он вызывается?

Конструктор

Конструктор — это специальная не статическая функция-элемент класса, которая используется для инициализации объектов своего классового типа.

  • Имя функции совпадает с именем класса.
  • При создании объекта класса всегда вызывается один конструктор.
  • Конструктор нельзя вызвать явно.
  • У класса может быть произвольное число конструкторов (в том числе ноль).

Основные особенности:

  • Имя совпадает с именем класса
  • Не имеет возвращаемого типа (даже void)
  • Может быть перегружен (несколько версий с разными параметрами)

Когда вызывается конструктор:

При создании объекта:

MyClass obj;          // Вызывается конструктор по умолчанию
MyClass obj2(42);     // Вызывается параметризованный конструктор

При динамическом выделении памяти:

MyClass* ptr = new MyClass();  // Вызывается конструктор
При создании временных объектов:
MyClass func() {
    return MyClass();  // Вызывается конструктор
}

В составе других объектов:

class Container {
    MyClass member;   // Конструктор MyClass вызовется при создании Container
};

При копировании (если не используется move-семантика):

MyClass obj1;
MyClass obj2 = obj1;  // Вызывается копирующий конструктор

Типы конструкторов:

По умолчанию (без параметров):

class MyClass {
public:
    MyClass() { /* инициализация */ }
};

Параметризованный:

class MyClass {
public:
    MyClass(int value) { /* использование value */ }
};

Копирующий:

class MyClass {
public:
    MyClass(const MyClass& other) { /* копирование из other */ }
};

Перемещающий (C++11):

class MyClass {
public:
    MyClass(MyClass&& other) { /* перемещение ресурсов из other */ }
};

Важные нюансы:

  • Автоматическая генерация: Если не объявить ни одного конструктора, компилятор создаст конструктор по умолчанию
  • Если объявить любой конструктор, конструктор по умолчанию не генерируется

40. Что такое деструктор и когда он вызывается?

Деструктор — это специальная функция-элемент, которая вызывается, когда заканчивается время жизни объекта. Цель деструктора освободить ресурсы, которые объект мог получить за время своего существования (или сделать какую-то финализацию).

  • Имя функции ~ClassName.
  • У деструктора не может быть аргументов.
  • По умолчанию noexcept.
  • При любом удалении объекта класса вызывается его деструктор.
  • У класса может быть только один деструктор.
  • Деструктор можно вызвать явно.

Основные особенности:

  • Имя: ~ИмяКласса() (тильда + имя класса)
  • Не имеет параметров и возвращаемого типа
  • Не может быть перегружен (только один деструктор на класс)
  • Виртуальный, если класс предназначен для наследования

Когда вызывается деструктор:

При выходе объекта из области видимости:

{
    MyClass obj;  // Конструктор
}                 // Деструктор (при выходе из блока)

При явном удалении динамических объектов:

MyClass* ptr = new MyClass();
delete ptr;       // Вызывается деструктор

Для элементов массива:

MyClass* arr = new MyClass[5];
delete[] arr;     // Деструкторы всех 5 элементов

Для членов класса:

class Container {
    MyClass member;
public:
    ~Container() { /* Деструктор member вызовется автоматически после этого */ }
};

При исключениях (для локальных объектов в стеке)

41. Можно ли явно вызвать конструктор?

Конструктор нельзя вызвать явно. (см. 39 билет)

42. Можно ли явно вызвать деструктор?

Деструктор можно вызвать явно. (см. 40 билет)

43. В чем разница между оператором присваивания и конструктором копирования?

Это не шаблонный конструктор, принимающий первым аргументом lvalue ссылку на объект того же типа, что и класс конструктора, который может быть вызван с одним аргументом. Конструктор копирования вызывается всегда, когда объект класса инициализируется из lvalue выражения того же типа (класса).

Ключевые различия:

ХарактеристикаКонструктор копированияОператор присваивания
Когда вызываетсяПри создании объектаДля существующего объекта
Тип операцииИнициализацияПрисваивание
Возвращаемое значениеНетMyClass& (ссылка на объект)
Проверка самокопированияНе требуетсяОбязательна
ОтветственностьСоздание новой копииОсвобождение старых ресурсов + копирование
class String {
    char* data;
    size_t size;
public:
    // Конструктор копирования
    String(const String& other) : 
        size(other.size),
        data(new char[other.size + 1]) 
    {
        std::copy(other.data, other.data + size + 1, data);
    }

    // Оператор присваивания
    String& operator=(const String& other) {
        if (this != &other) {  // Защита от самоприсваивания
            delete[] data;      // Освобождаем старые данные
            size = other.size;
            data = new char[size + 1];
            std::copy(other.data, other.data + size + 1, data);
        }
        return *this;
    }

    ~String() { delete[] data; }
};