Частное решение
Частное решение
Для начала создадим делегат для 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();
}
Использовать полученный класс делегата можно примерно так.
void Global() {
std::cout ‹‹ "Global" ‹‹ std::endl;
}
class C {
public:
void Method() { std::cout ‹‹ "Method" ‹‹ std::endl; }
static void StaticMethod() { std::cout ‹‹ "StaticMethod" ‹‹ std::endl; }
};
int main() {
C c;
CDelegateVoid delegate = NewDelegate(Global);
delegate += NewDelegate(&c, &C::Method);
delegate += NewDelegate(C::StaticMethod);
delegate(); // вызывается Global, Method и StaticMethod.
delegate -= NewDelegate(C::StaticMethod);
delegate -= NewDelegate(Global);
delegate(); // вызывается только Method.
return 0;
}
Как видим, класс CDelegateVoid очень похож на делегаты из C#. Он полностью типобезопасен, так как попытка передать функции NewDelegate ссылку на функцию или метод, сигнатура которых отличается от void(void), немедленно приведёт к ошибке. Реализация класса CDelegateVoid не использует никаких предположений о размере и устройстве указателя на метод класса, поэтому он может использоваться как при обычном, так и при множественном и виртуальном наследовании. Его можно без изменений переносить на новые платформы и компиляторы.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Решение
Решение Внедрите в ваше приложение фреймворк Social и воспользуйтесь классом SLComposeViewController для обеспечения социального обмена сообщениями, например
Решение
Решение Воспользуйтесь классом NSXMLParser.
Решение
Решение Используйте метод экземпляра URLsForDirectory: inDomains:, относящийся к классу
Решение
Решение Все классы Cocoa, обеспечивающие сохранение информации, например NSString, UIImage и NSData, предоставляют методы экземпляра, позволяющие сохранять данные на диске по заданному
Решение
Решение Пользуйтесь методом экземпляра createDirectoryAtPath: withIntermediateDirectories: attributes: error:, относящимся к классу NSFileManager, как показано далее:— (BOOL) application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions{NSFileManager *fileManager = [[NSFileManager alloc] init];NSString *tempDir = NSTemporaryDirectory();NSString *imagesDir = [tempDir
Решение
Решение Используйте метод экземпляра contentsOfDirectoryAtPath: error:, относящийся к классу NSFileManager, как показано далее. В данном примере мы перечисляем все файлы, каталоги и символьные ссылки, расположенные в каталоге пакета с
Решение
Решение Используйте один из двух методов экземпляра, removeItemAtPath: error: или removeItemAtURL: error:, относящихся к классу NSFileManager. Первый метод принимает путь как строку, а второй — как
Решение
Решение Убедитесь, что ваш класс соответствует протоколу NSCoding, и реализуйте все необходимые методы данного протокола. Не волнуйтесь, я все подробно объясню в подразделе «Обсуждение» данного
Решение
Решение Применяйте метод класса isSourceTypeAvailable:, относящийся к классу UIImagePickerController, со значением UIImagePickerControllerSourceTypeCamera следующим образом:— (BOOL) isCameraAvailable{return [UIImagePickerController
Решение
Решение Инстанцируйте объект типа UIImagePickerController и представьте его пользователю как модальный вид в актуальном контроллере вида. Вот объявление этого контроллера
Решение
Решение Воспользуйтесь объектом UIImagePickerController с источником типа UIImagePickerControllerSourceTypeCamera и медийной информацией типа kUTTypeMovie:— (void)viewDidAppear:(BOOL)animated{[super viewDidAppear: animated];static BOOL beenHereBefore = NO;if (beenHereBefore){/* Отображаем элемент для выбора даты только после того, как вызываетсяметод
Решение
Решение Воспользуйтесь процедурой UIImageWriteToSavedPhotosAlbum:— (void) imageWasSavedSuccessfully:(UIImage *)paramImagedidFinishSavingWithError:(NSError *)paramErrorcontextInfo:(void *)paramContextInfo{if (paramError == nil){NSLog(@"Image was saved successfully.");} else {NSLog(@"An error happened while saving the
Решение
Решение Воспользуйтесь методом экземпляра writeVideoAtPathToSavedPhotosAlbum: completionBlock:, относящимся к классу ALAssetsLibrary:#import «AppDelegate.h»#import <AssetsLibrary/AssetsLibrary.h>@interface AppDelegate ()@property (nonatomic,
Ваннах Михаил Частное охранное предприятие времён киберпанка
Ваннах Михаил Частное охранное предприятие времён киберпанка Было в истории России занятное время, конец 50-х — начало 60-х годов ХХ века, когда ракетные успехи вывели страну в лидеры мировой научно-технической гонки. Сейчас об этом времени напоминают русские имена на
Частное распространение информации PKI
Частное распространение информации PKI Цель PKI состоит в управлении ключами и сертификатами, посредством которого корпорация может поддерживать надежную сетевую среду. Эффективность управления зависит от механизмов распространения информации о действующих и
Глава 7. Частное предприятие
Глава 7. Частное предприятие Russia. Examples.То лето выдалось в Гузеево жарким и сухим. Дождей не было чуть ли не два месяца, болота в лесу все высохли и, как обычно, начались пожары. Потушить горящее торфяное болото практически невозможно, огонь уходит вглубь, тлеет, а затем