- 1. Привіт шаблони
- 1.
- 2. Реальний код:
Введіть ідіому стирання
Стаття передбачає наявність у читача базових знань C++
Відразу до діла.
Припустимо, що вам потрібно динамічно (наприклад, як мені, за натисканням кнопки) змінювати текст
кількох об'єктів, таких як:
- QLabel
- QPushButton
- QLineEdit
Більшість із вас знає, що ці віджети успадковують QWidget, і тому чому б не оголосити метод, який приймає вказівник на цей базовий клас, а потім уже викликати метод setText?
QWidget * base_pointer = new QLabel(); base_pointer->setText("ERROR");
Проблема в тому, що QWidget не визначений метод setText(), і тому зробити це не вдасться.
Мій знайомий порадив мені 2 варіанти вирішення цього завдання:
- Через ідіому "стирання типу"
- Через сигнали та слоти
Про сигнали та слоти ви вже точно знаєте, і тому я розповім про один спосіб. Коротко.
Ідіома "стирання типу" (type erasure idiom) дозволяє стерти тип будь-якого об'єкта, приховавши його за
об'єктом найбільш загального типу.
Привіт шаблони
Як це виглядає на словах:
- Створюємо абстрактний базовий клас А, у ньому оголошуємо віртуальну функцію SetText
Наслідуємо від цього класу вже шаблонний клас, а в ньому всього лише є:
поле у вигляді вказівника на тип шаблону
реалізація віртуального методу SetText
У класі-клієнті оголошуємо покажчик типу А
- Використовуємо :)
Як це виглядає насправді:
- Абстрактний базовий клас та його спадкоємець:
namespace om_animation { class AbstractWritableWidget { public: ~AbstractWritableWidget() {} virtual void SetText(const QString& text) = 0; }; template <class Widget> class WritableWidget : public AbstractWritableWidget { public: WritableWidget(Widget* writable_widget) : writable_widget_(writable_widget) {} ~WritableWidget() {} void SetText(const QString& text) override { writable_widget_->setText(text); } private: Widget* writable_widget_ = nullptr; }; }
- Використовуємо (просто приклад):
class Client { public: template <typename T> void Run(T * type){ // do what you want writable_widget_ = new WritableWidget<T> (type); writable_widget_->SetText("Success"); // do what you want } private: AbstractWritableWidget* writable_widget = nullptr; }
template <typename Widget> void om_animation::TextAnimator::RunAnimation(Widget* widget) { if (WritableMatcher::IsWidgetWritable(widget->metaObject()->className())) { if (writable_widget_) { delete writable_widget_; } writable_widget_ = new WritableWidget<Widget>(widget); timer_->start(animation_delay_); } else { throw std::logic_error( "incompatible type for text animation, add type to " "WritableMatcher class"); } }
У реальному коді я ще зробив клас зі статичними методами для перевірки, який клас підтримує
метод setText у бібліотеці Qt. Там просто вектор під капотом. Нічого складного.
Через метаоб'єкт отримуємо ім'я класу, порівнюємо з наявним у списку. Так – так, ні – ні.
Далі я зробив просту перевірку вказівника, і власне вже використання.
Якщо об'єкт не знайдено, клієнт отримує exception.
От і все. Я знаходжу цю ідіому дуже крутою.
Спасибі за увагу.
Идиома достаточно интересная, единственное, код кажется весьма громоздким.
В стандарте С++14 также есть возможность объявлять в качестве аргумента лямбда функции auto аргументы. Которые также шаблонизируются. И если метод не существуют у объекта, то проект не скомпилируется.
Спасибо за отзыв. Очень приятно, что кто-то прочитал и ответил.
Не совсем.
Под наследованием интерфейса понимается вот что