Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

Начнем закрывать пробелы с небольшой темы.

Итак, первое что нужно вспомнить, это то что у нас есть то, когда проект собирается и то, когда он выполняется. Как это легко мысленно разделить? Например, мы можем на тачке с установленной Linux собрать проект, который будет запускаться на Windows. Тут ни у кого не возникает сомнений, что в какой момент происходит.

Данный пример лишь для того, чтобы было наглядно понятно, что есть этап компиляции/сборки проекта, который может выполняться на одной тачке и есть этап выполнения программы, которая может выполняться совсем на другой тачке. И тут важно не путать, какие операции в какой момент вычисляются.

Сегодня мы поговорим о динамической идентификации типа данных RTTI. Возьмем баянный пример виртуального наследования.

 
struct Animal
{
    virtual string GetName() const
    {
        return "Animal";
    }
};

struct Cat : public Animal
{
    virtual string GetName() const
    {
        return "Cat";
    }
};

struct Dog : public Animal
{
    virtual string GetName() const
    {
        return "Dog";
    }
};

void PrintName(const Animal& a)
{
    cout << a.GetName() << endl;
}

int main()
{
    Cat cat;    
    PrintName(cat);
    return 0;
}

Итак, есть базовый класс животного, и от него наследуется кошка и собака. И есть удобненькая функция PrintName, которая независимо от того собака или кошка ей передается в аргументах, всегда будет выводить правильное имя класса.

В данном примере выведется Cat, а не Animal. Удобно это тем, что каких бы других животных отнаследованных от Animal мы не создавали, функция не будет меняться и при этом будет всегда правильно работать

Но представим, что то пошло не так и мы где то в коде ошиблись и передаем не Cat, а что то явно другое. Как нам узнать какой класс был передан? Самый простой вариант - просто посмотреть тип typeid(a).name().

 
void PrintName(const Animal& a)
{
    cout << a.GetName() << endl;
    cout << typeid(a).name() << endl;
}

typeid() это первая часть RTTI, т.е. тип данных будет вычисляться во время работы приложения. Минусом этого подхода являются дополнительные расходы на получение типа.

Вторая часть RTTI это dynamic_cast. В случае преобразования из типа Cat в тип Animal нам не нужно производить никаких дополнительных телодвижений. Можно создать ссылку Animal и присвоить ей объект cat без всяких проблем. Результат работы не изменится.

 
int main()
{
    Cat cat;
    Animal &a = cat;
    PrintName(a);
   
    return 0;
}

Такое преобразование называется upcast. Но в обратную сторону, так же легко это сделать не получится. Для downcast используется dynamic_cast. Например, доработаем функцию PrintName. В примере ниже для Cat дополнительный текст будет выводиться, для Dog нет.

 
#include 
#include 

using namespace std;

struct Animal
{
    virtual string GetName() const
    {
        return "Animal";
    }
};

struct Cat : public Animal
{
    virtual string GetName() const
    {
        return "Cat";
    }
};

struct Dog : public Animal
{
    virtual string GetName() const
    {
        return "Dog";
    }
};

void PrintName(Animal& a)
{
    cout << a.GetName() << endl;
    if(Cat* c = dynamic_cast(&a))
    {
        cout << "Meow meow mf" << endl;
    }
}

int main()
{
    Cat a;
    Dog d;
    PrintName(a);
    PrintName(d);
    

    return 0;
}

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Счетчик
Яндекс.Метрика