2009-08-26 2 views
0

У меня есть ручки разных типов внутри иерархии.Полиморфные ручки

class Handle { common data } 
class HandleA : Handle { data specific to a } 
class HandleB : Handle { data specific to b } 

Большинство частей кода касается только ручек. Но некоторые части («менеджеры» для HandleA/HandleB) нуждаются в доступе к данным в дочерних классах. например:

void ManagerA::DoSomething(Handle handle) 
{ 
    // needs access to data in handleA 
} 

Есть ли какое-либо решение этого, которые не связаны кастинг?

Мои идеи до сих пор:
- Сохранение дополнительных данных внутри карты в ManagerA/B и использовать ручку для поиска, что данные (дополнительные Хеш подстановки)
- Есть полиморфные методы в ручках (handle.DoSomething ()), которые называют подходящие методы менеджера (требуется дополнительный указатель в каждом дескрипторе)
- Заверните его и используйте отливки

Любые идеи? Я что-то упускаю?

Благодаря

+0

Почему вы хотите избежать вызова dynamic_cast для доступа к объекту HandleX подкласса? – chollida

+0

Наверное, потому, что у каста есть «Bad Karma» во многих учебниках на C++: свисток: –

+0

Если это непрозрачно, то это действительно ручка? Если вы знаете, что такое дескриптор, и нужно использовать его как подходящий тип, то почему бы не использовать литье? Что случилось с актером? – Pod

ответ

7

Прием аргумента по значению, как вы делаете в:

void ManagerA::DoSomething(Handle handle) 

ВОЛЯ «ломтик прочь» ничего в пройденном в аргументе за то, что держит Handle экземпляра, так что ваш аргумент будет handle есть NO "дополнительные данные". Вам обязательно нужно передать указатель или ссылку (возможно, const, если данные не нуждаются в изменении, конечно).

При этом нормальный полиморфный подход включает определение виртуальных методов в базовом классе и их надлежащее переопределение в подклассы.Почему бы не следовать такой совершенно нормальной архитектуре, а не бороться с против подхода OO? Могут быть веские причины (которые оправдывают, например, принятие какого-либо варианта в шаблоне visitor и т. Д.), Но вы просто не объясняете достаточно сил в игре, чтобы мы могли помочь в этих строках; на представленной информации я должен был бы предложить «rearchitect использовать виртуальные методы».

4

Если это конкретные данные только один - и только один тип, используйте dynamic_cast<T>, это то, что он там. В противном случае объявить виртуальную функцию в базовом классе.

EDIT: маловероятно, что любое решение приведет к измеримым различиям в производительности во время выполнения.

0

Что об изменении подписи для DoSomething на:

void ManagerA::DoSomething(HandleA handle) 
1

я бы не использовать полиморфизм для ручек - существо ручки, а не указатели, они должны абсолютно скрыть реализацию упомянутого объекта. Если вы используете виртуальные функции, пользователь дескриптора может вызывать те функции, которые, безусловно, являются плохими идеями.

Два общих решения - это литье и использование карты. Если позже, ваш дескриптор даже не должен быть классом - он может быть таким же, как и int. В Windows ручками являются указатели void *. Я понятия не имею, что действительно стоит за указателем, но мне все равно. Насколько я понимаю, это дело рук.

0

Ваши первые и третьи идеи будут работать. Другая идея заключается в использовании double-dispatch (я не знаю, является ли эта статья Википедии понятной: оригинальная статья/объяснение в Meyer's More Effective C++ - это 20 с лишним страниц), что означает реализацию виртуального метода, такого как Handle::DoSomething(Manager&).

0

Другая возможность - сохранить конкретный тип в каждом дескрипторе, возможно, в виде целого числа или перечисления. Вы либо жестко кодируете все возможные типы конкретных дескрипторов, либо используете какой-то механизм регистрации типа. Очевидно, что этот подход имеет свои недостатки, но это есть еще одна возможность, которую вы не упомянули. Это подход X-Windows, используемый для типов событий. Структура данных событий была объединением всех возможных данных событий с переменной типа, указывающей истинный тип данных для конкретного события. Не сказать, что это хорошо, просто сказать, что это вариант, который не требует динамического кастинга.

enum HandleType 
{ 
    HANDLE_TYPE_A, 
    HANDLE_TYPE_B 
}; 

class Handle 
{ 
private: 
    HandleType _type; 
protected: 
    Handle(HandleType type) : 
    _type(type) 
    {} 
public: 
    HandleType get_type() const 
    { return _type; } 
}; 

class HandleA 
{ 
    HandleA() : 
    Handle(HANDLE_TYPE_A) 
    {} 
}; 

void ManagerA::DoSomething(Handle& handle) 
{ 
    if (handle.get_type() == HANDLE_TYPE_A) 
    do_something(); 
} 
Смежные вопросы