9. Приведение типа в С++: static_cast, dynamic_cast, const_cast, reinterpret_cast.

«Умные указатели» в С++: unique_ptr, shared_ptr, weak_ptr. Контейнерные классы и итераторы. Работа с итераторами. Цикл for для работы с контейнерными объектами.

Приведение типа в C++

Основные типы приведения

  1. static_cast

    • Используется для явного приведения типов. Безопасен при корректном использовании, но не проверяет типы во время выполнения.

    • Применим для приведения между базовым и производным классами (если гарантирована правильность приведения).

    double a = 10.5;
    int b = static_cast<int>(a);  // b = 10
  2. dynamic_cast

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

    • Проверяет тип во время выполнения и возвращает nullptr (для указателей) или выбрасывает исключение std::bad_cast (для ссылок), если приведение неудачно.

    class Base {
        virtual void foo() {}
    };
    
    class Derived : public Base {};
    
    Base* b = new Derived();
    Derived* d = dynamic_cast<Derived*>(b);  // Успешное приведение
  3. const_cast

    • Используется для добавления или удаления квалификатора const из переменной.

    • Безопасен, если используется для снятия const с переменной, которая изначально не была const.

    const int a = 10;
    int* b = const_cast<int*>(&a);
    *b = 20;  // Поведение неопределено, поскольку `a` изначально `const`
  4. reinterpret_cast

    • Используется для низкоуровневого приведения типов.

    • Применяется для приведения указателей к несовместимым типам, но может быть небезопасным.

    int a = 10;
    void* ptr = &a;
    int* intPtr = reinterpret_cast<int*>(ptr);  // Приведение void* обратно к int*

«Умные указатели» в C++

Умные указатели из стандартной библиотеки C++ (C++11 и выше) помогают управлять динамическими ресурсами и предотвращать утечки памяти.

  1. unique_ptr

    • Управляет уникальным владением ресурсом. Ресурс освобождается при уничтожении unique_ptr.

    • Нельзя копировать unique_ptr, но можно перемещать.

    std::unique_ptr<int> ptr1(new int(10));
    std::unique_ptr<int> ptr2 = std::move(ptr1);  // ptr1 больше не владеет ресурсом
  2. shared_ptr

    • Управляет совместным владением ресурсом. Ресурс освобождается, когда последний shared_ptr перестает им владеть.

    • Поддерживает подсчет ссылок для отслеживания количества владельцев ресурса.

    std::shared_ptr<int> ptr1(new int(20));
    std::shared_ptr<int> ptr2 = ptr1;  // ptr1 и ptr2 владеют одним и тем же ресурсом
  3. weak_ptr

    • Отслеживает ресурс, управляемый shared_ptr, но не влияет на его срок жизни.

    • Используется для устранения циклических зависимостей между shared_ptr.

    std::shared_ptr<int> sharedPtr = std::make_shared<int>(30);
    std::weak_ptr<int> weakPtr = sharedPtr;  // weakPtr отслеживает ресурс, но не владеет им

Контейнерные классы и итераторы

Основные контейнеры STL

  1. std::vector

    • Динамически изменяемый массив.

    std::vector<int> vec = {1, 2, 3, 4, 5};
  2. std::list

    • Двусвязный список.

    std::list<int> lst = {1, 2, 3, 4, 5};
  3. std::set

    • Множество, содержащие уникальные элементы, упорядоченные по значению.

    std::set<int> s = {3, 1, 4, 1, 5, 9};  // Элементы будут {1, 3, 4, 5, 9}
  4. std::map

    • Ассоциативный массив (словарь), хранящий пары ключ-значение.

    std::map<int, std::string> m;
    m[1] = "one";
    m[2] = "two";

Итераторы

  1. Основные типы итераторов

    • InputIterator: Поддерживает чтение из последовательности.

    • OutputIterator: Поддерживает запись в последовательность.

    • ForwardIterator: Поддерживает однонаправленное перемещение.

    • BidirectionalIterator: Поддерживает движение вперед и назад.

    • RandomAccessIterator: Поддерживает произвольный доступ, аналогично указателям.

    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
  2. Использование итераторов

    • Итераторы позволяют абстрагироваться от конкретного контейнера и работать с элементами в стандартной манере.

    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto it = vec.begin();
    std::advance(it, 2);  // Переход на два элемента вперед
    std::cout << *it << std::endl;  // Вывод: 3
  3. Цикл for для контейнерных объектов (range-based for)

    • Упрощает итерацию по контейнерам, введенный в C++11.

    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (int n : vec) {
        std::cout << n << " ";
    }

Last updated