- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Делегаты на C++ - Александр Шаргин


- Жанр: Компьютеры и Интернет / Программирование
- Название: Делегаты на C++
- Автор: Александр Шаргин
- Возрастные ограничения: (18+) Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту для удаления материала.
Шрифт:
Интервал:
Закладка:
Александр Шаргин
Делегаты на C++
Введение
Делегаты - это объектно-ориентированные указатели на функции, используемые для callback-вызовов в среде CLR фирмы Microsoft. Делегат можно связать со статической функцией или с нестатическим методом любого класса (единственное условие - совпадение сигнатуры метода с сигнатурой, указанной в описании делегата). Затем связанную с делегатом функцию или метод можно вызывать, используя стандартный синтаксис вызова функции в C++. Несколько делегатов можно связать в цепочку. Благодаря этому можно "одним махом" вызвать все связанные с ними callback-функции. Следующий пример демонстрирует применение делегатов в языке C#.
using System;
using System.IO;
namespace CSharpDelegates {
class App {
// Определяем делегат Callback,
// который принимает 1 параметр и ничего не возвращает.
public delegate void Callback(string str);
// Это метод класса App.
public void OutputToConsole(string str) {
Console.WriteLine(str);
}
// А это статический метод класса App.
public static void OutputToFile(string str) {
StreamWriter sw = new StreamWriter("output.txt", true);
sw.WriteLine(str);
sw.Close();
}
public static void Main(string[] args) {
App app = new App();
// Создаём делегат.
App.Callback callback = null;
if (callback != null) callback("1");
// Добавляем ссылку на OutputToFile.
// Вызываем её через делегата.
callback += new App.Callback(App.OutputToFile);
if (callback != null) callback("2");
// Добавляем ссылку на OutputToConsole.
// Вызывается вся цепочка:
// сначала OutputToFile, потом OutputToConsole.
callback += new App.Callback(app.OutputToConsole);
if (callback != null) callback("3");
// Убираем ссылку на OutputToFile.
// Вызывается только OutputToConsole.
callback -= new App.Callback(App.OutputToFile);
if (callback!= null) callback("4");
// Убираем оставшуюся ссылку на OutputToConsole.
callback -= new App.Callback(app.OutputToConsole);
if (callback != null) callback("5");
}
}
}
Делегаты в CLR удобны, типобезопасны и эффективны. Последнее время на форумах RSDN часто поднимается вопрос о том, можно ли реализовать делегаты с аналогичными свойствами, оставаясь в рамках "чистого" C++. Оказывается, это вполне возможно. В этой статье я покажу, как это сделать.
Частное решение
Для начала создадим делегат для callback-вызова функций и методов с простейшей сигнатурой void(void). Интерфейс этого делегата будет выглядеть так.
class IDelegateVoid {
public:
virtual ~IDelegateVoid() {}
virtual void Invoke() = 0;
virtual bool Compare(IDelegateVoid* pDelegate) = 0;
};
Invoke используется для вызова функции или метода, связанного с делегатом, а Compare сравнивает 2 делегата и возвращает true, если они связаны с одной и той же функцией (методом). Очевидно, что реализация интерфейса IDelegateVoid будет отличаться для статических функций и нестатических методов класса, поэтому мы разнесём эти реализации по различным классам. Класс CStaticDelegateVoid будет "отвечать" за статические функции, а класс CMethodDelegateVoid - за нестатические методы.
Класс CStaticDelegateVoid просто инкапсулирует указатель типа void (*)():
class CStaticDelegateVoid: public IDelegateVoid {
public:
typedef void (*PFunc)();
CStaticDelegateVoid(PFunc pFunc) { m_pFunc = pFunc; }
virtual void Invoke() { m_pFunc(); }
virtual bool Compare(IDelegateVoid* pDelegate);
private:
PFunc m_pFunc;
};
Метод Compare должен проверить, что переданный ему указатель IDelegateVoid* в действительности ссылается на объект CStaticDelegateVoid. Если это не так, делегаты различны (ссылаются на разные функции) и Compare просто возвращает false. Иначе результат определяется сравнением переменных-членов m_pFunc у двух объектов. Реализация этой идеи выглядит так.
bool CStaticDelegateVoid::Compare(IDelegateVoid *pDelegate) {
CStaticDelegateVoid* pStaticDel = dynamic_cast‹CStaticDelegateVoid*›(pDelegate);
if (pStaticDel == NULL || pStaticDel-›m_pFunc != m_pFunc) return false;
return true;
}
Класс CMethodDelegateVoid чуть-чуть сложнее. Он должен инкапсулировать указатель на объект и указатель на метод этого объекта. Поскольку в C++ указатели на методы двух разных классов принципиально отличаются (и могут даже иметь разный размер), нам нужна отдельная реализация CMethodDelegateVoid для каждого нового класса, на методы которого мы хотим ссылаться. Поэтому класс CMethodDelegateVoid должен быть шаблоном. В остальном его реализация аналогична CStaticDelegateVoid.
template‹class TObj›
class CMethodDelegateVoid: public IDelegateVoid {
public:
typedef void (TObj::*PMethod)();
CMethodDelegateVoid(TObj* pObj, PMethod pMethod) {
m_pObj = pObj;
m_pMethod = pMethod;
}
virtual void Invoke() {
(m_pObj-›*m_pMethod)();
}
virtual bool Compare(IDelegateVoid* pDelegate);
private:
TObj *m_pObj;
PMethod m_pMethod;
};
template‹class TObj›
bool CMethodDelegateVoid‹TObj›::Compare(IDelegateVoid* pDelegate) {
CMethodDelegateVoid‹TObj›* pMethodDel = dynamic_cast‹CMethodDelegateVoid‹TObj›* ›(pDelegate);
if (pMethodDel == NULL || pMethodDel-›m_pObj != m_pObj || pMethodDel-›m_pMethod != m_pMethod) return false;
return true;
}
Классы CStaticDelegateVoid и CMethodDelegateVoid можно использовать непосредственно. Но для пользователя удобнее работать исключительно с интерфейсом IDelegateVoid, не задумываясь о существовании двух различных классов реализации. Поэтому напишем перегруженную функцию NewDelegate, которая будет создавать нужный объект и возвращать пользователю IDelegateVoid*. Её реализация будет выглядеть так:
IDelegateVoid* NewDelegate(void (*pFunc)()) {
return new CStaticDelegateVoid(pFunc);
}
template ‹class TObj›
IDelegateVoid* NewDelegate(TObj* pObj, void (TObj::*pMethod)()) {
return new CMethodDelegateVoid‹TObj› (pObj, pMethod);
}
Мы уже почти закончили. Осталось написать объектную обёртку над интерфейсом IDelegateVoid, которая будет поддерживать список указателей и определять набор операторов, аналогичных используемым в C# - operator=, operator(), operator+= и operator-=. Для простоты будем использовать стандартный класс std::list для хранения списка указателей.
#include ‹list›
class CDelegateVoid {
public:
CDelegateVoid(IDelegateVoid* pDelegate = NULL) {
Add(pDelegate);
}
~CDelegateVoid() { RemoveAll(); }
bool IsNull() { return (m_DelegateList.size() == 0); }
CDelegateVoid& operator=(IDelegateVoid* pDelegate) {
RemoveAll();
Add(pDelegate);
return *this;
}
CDelegateVoid& operator+=(IDelegateVoid* pDelegate) {
Add(pDelegate);
return *this;
}
CDelegateVoid& operator-=(IDelegateVoid* pDelegate) {
Remove(pDelegate);
return *this;
}
void operator()() { Invoke(); }
private:
void Add(IDelegateVoid* pDelegate);
void Remove(IDelegateVoid* pDelegate);
void RemoveAll();
void Invoke();
private:
std::list‹IDelegateVoid*› m_DelegateList;
};
Для реализации необходимого набора операторов используются вспомогательные методы Add, Remove, RemoveAll и Invoke. Метод Add добавляет новый указатель IDelegateVoid* в список:
void CDelegateVoid::Add(IDelegateVoid* pDelegate) {
if (pDelegate != NULL) m_DelegateList.push_back(pDelegate);
}
Метод Remove ищет в списке делегат, ссылающийся на заданную функцию, и в случае обнаружения удаляет его:
void CDelegateVoid::Remove(IDelegateVoid* pDelegate) {
std::list‹IDelegateVoid*›::iterator it;
for(it = m_DelegateList.begin(); it!= m_DelegateList.end(); ++it) {
if((*it)-›Compare(pDelegate)) {
delete (*it);
m_DelegateList.erase(it);
break;
}
}
delete pDelegate;
}
Метод RemoveAll просто очищает список, удаляя из него все делегаты:
void CDelegateVoid::RemoveAll() {
std::list‹IDelegateVoid*›::iterator it;
for(it = m_DelegateList.begin(); it != m_DelegateList.end(); ++it) delete (*it);
m_DelegateList.clear();
}
Наконец, метод Invoke вызывает все функции и методы, на которые ссылаются делегаты из списка:
void CDelegateVoid::Invoke() {
std::list‹IDelegateVoid*›::const_iterator it;
for (it = m_DelegateList.begin(); it != m_DelegateList.end(); ++it) (*it)-›Invoke();
}
Использовать полученный класс делегата можно примерно так.

