Есть несколько объявлений интерфейсов, каждый из которых имеет одинаковую по сигнатуре функцию:
class IFace1
{
public:
virtual int GetCount() const=0;
//--другие функции интерфейса IFace1
};
class IFace2
{
public:
virtual int GetCount() const=0;
//--другие функции интерфейса IFace2
};
и т.д. И так получается, что я хочу, чтобы имплементировал все эти интерфейсы один класс (читай компонент, хотя к COM описываемая ситуевина не относится), т.к. только в этом классе имеется достаточно информации (в данных этого класса), чтобы реализовывать эти интерфейсы. Так вот, по сути IFace1::GetCount() и IFace2::GetCount() — это разные функции и возвращать они должны разные значения. Но если я сделаю
class MyCoolMultipleIFacesImpl: public IFace1, public IFace2, ... ,public IFaceN
{
public:
virtual int GetCount() const {return чего-то там такое;}
};
то великий и могучий компилятор засунет указатель на этот ГетКаунт во все реализуемые интерфейсы. Собственно вопрос в следующем: как написать разные реализации функций для разных реализуемых интерфейсов в одном классе?
На вопросы типа "зачем так надо?" отвечать долго, здесь я ситуацию упрощаю, привожу только суть проблемы. Все разумные приписки к имени функций уже сделаны (остались разве что приписки в стиле __AFX_GUID__, что, ИМХО, не выход). Можно ли как-то решить эту проблему или это глобально? Что по этому поводу думает уважаемый All?
"programmater" <34509@users.rsdn.ru> wrote in message news:1904454@news.rsdn.ru... > Есть несколько объявлений интерфейсов, каждый из которых имеет одинаковую по сигнатуре функцию: >
> class IFace1
> {
> public:
> virtual int GetCount() const=0;
> //--другие функции интерфейса IFace1
> };
>
> class IFace2
> {
> public:
> virtual int GetCount() const=0;
> //--другие функции интерфейса IFace2
> };
>
> и т.д. И так получается, что я хочу, чтобы имплементировал все эти интерфейсы один класс (читай компонент, хотя к COM описываемая ситуевина не относится), т.к. только в этом классе имеется достаточно информации (в данных этого класса), чтобы реализовывать эти интерфейсы. Так вот, по сути IFace1::GetCount() и IFace2::GetCount() — это разные функции и возвращать они должны разные значения. Но если я сделаю >
> class MyCoolMultipleIFacesImpl: public IFace1, public IFace2, ... ,public IFaceN
> {
> public:
> virtual int GetCount() const {return чего-то там такое;}
> };
>
> то великий и могучий компилятор засунет указатель на этот ГетКаунт во все реализуемые интерфейсы. Собственно вопрос в следующем: как написать разные реализации функций для разных реализуемых интерфейсов в одном классе? > На вопросы типа "зачем так надо?" отвечать долго, здесь я ситуацию упрощаю, привожу только суть проблемы. Все разумные приписки к имени функций уже сделаны (остались разве что приписки в стиле __AFX_GUID__, что, ИМХО, не выход). Можно ли как-то решить эту проблему или это глобально? Что по этому поводу думает уважаемый All?
Может перейти от наследования к агрегированию?
Posted via RSDN NNTP Server 2.0
--
Не можешь достичь желаемого — пожелай достигнутого.
class IFace1
{
public:
int GetCount() const
{
return GetCountIFace1();
}
protected:
virtual int GetCountIFace1() const=0;
//--другие функции интерфейса IFace1
};
class IFace2
{
public:
int GetCount() const
{
return GetCountIFace2();
}
protected:
virtual int GetCountIFace2() const=0;
//--другие функции интерфейса IFace2
};
class MyCoolMultipleIFacesImpl: public IFace1, public IFace2
{
protected:
virtual int GetCountIFace1() const {return 0;}
virtual int GetCountIFace2() const {return 1;}
};
class IFace1
{
public:
int GetCount() const
{
return GetCountIFace1();
}
//--другие функции интерфейса IFace1protected:
virtual int GetCountIFace1() const=0;
};
class IFace2
{
public:
int GetCount() const
{
return GetCountIFace2();
}
//--другие функции интерфейса IFace2protected:
virtual int GetCountIFace2() const=0;
};
class MyCoolMultipleIFacesImpl: public IFace1, public IFace2
{
protected:
virtual int GetCountIFace1() const {return 0;}
virtual int GetCountIFace2() const {return 1;}
};
Уже ближе. Если не будет других идей — сойдет как решение. Спасибо. У народа другие идеи есть?
[кусь] R>Может перейти от наследования к агрегированию?
Да...вроде ничего не мешает, тока никрасиво. Но насколько я знаю, невозможно создать экземпляр класса, имеющего чисто виртуальные функции (т.е. не имеющие реализации). А реализовать эти функции может только этот самый великий и могучий класс-имплементатор. Как здесь поможет агрегирование?
"programmater" <34509@users.rsdn.ru> wrote in message news:1904928@news.rsdn.ru... > Здравствуйте, rg45, Вы писали: > > [кусь] > R>Может перейти от наследования к агрегированию? > Да...вроде ничего не мешает, тока никрасиво. Но насколько я знаю, невозможно создать экземпляр класса, имеющего чисто виртуальные функции (т.е. не имеющие реализации). А реализовать эти функции может только этот самый великий и могучий класс-имплементатор. Как здесь поможет агрегирование?
Мне как-то в руки попала книжка Дейла Роджерсона "Основы COM". Там обсуждаются именно эти вопросы, о доступе к интерфейсам, о подсчете ссылок, как сделать лучше и чего делать не стоит. Там приводится пример, где объединение интерфейсов делается сначала с помощью наследования, а далее это же самое делается с помощью агрегирования. Мне это книжка очень понравилась, читается на одном дыхании, вот небольшая цитата оттуда:
Правила и соглашения QueryInterface
В этом разделе представлены некоторые правила, которым должны следовать все реализации QueryInterface.
Если их выполнять, клиент сможет узнать о компоненте достаточно, чтобы (надеяться) управлять им и
использовать его в своих целях. Без этих правил поведение QueryInterface было бы неопределенным, и писать
программы было бы невозможно.
— Вы всегда получаете один и тот же IUnknown.
— Вы можете получить интерфейс снова, если смогли получить его раньше.
— Вы можете снова получить интерфейс, который у Вас уже есть.
— Вы всегда можете вернуться туда, откуда начали.
— Если Вы смогли попасть куда-то хоть откуда-нибудь, Вы можете попасть туда откуда угодно.
Posted via RSDN NNTP Server 2.0
--
Не можешь достичь желаемого — пожелай достигнутого.
R>"programmater" <34509@users.rsdn.ru> wrote in message news:1904928@news.rsdn.ru... >> Здравствуйте, rg45, Вы писали: >> >> [кусь] >> R>Может перейти от наследования к агрегированию? >> Да...вроде ничего не мешает, тока никрасиво. Но насколько я знаю, невозможно создать экземпляр класса, имеющего чисто виртуальные функции (т.е. не имеющие реализации). А реализовать эти функции может только этот самый великий и могучий класс-имплементатор. Как здесь поможет агрегирование?
R>Мне как-то в руки попала книжка Дейла Роджерсона "Основы COM". Там обсуждаются именно эти вопросы, о доступе к интерфейсам, о подсчете ссылок, как сделать лучше и чего делать не стоит. Там приводится пример, где объединение интерфейсов делается сначала с помощью наследования, а далее это же самое делается с помощью агрегирования. Мне это книжка очень понравилась, читается на одном дыхании, вот небольшая цитата оттуда: R>
R>Правила и соглашения QueryInterface
R>В этом разделе представлены некоторые правила, которым должны следовать все реализации QueryInterface.
R>Если их выполнять, клиент сможет узнать о компоненте достаточно, чтобы (надеяться) управлять им и
R>использовать его в своих целях. Без этих правил поведение QueryInterface было бы неопределенным, и писать
R>программы было бы невозможно.
R>- Вы всегда получаете один и тот же IUnknown.
R>- Вы можете получить интерфейс снова, если смогли получить его раньше.
R>- Вы можете снова получить интерфейс, который у Вас уже есть.
R>- Вы всегда можете вернуться туда, откуда начали.
R>- Если Вы смогли попасть куда-то хоть откуда-нибудь, Вы можете попасть туда откуда угодно.
Сабж читал, правда насчет "одного дыхания" не согласен. Дочитал до фабрик и IClassFactory и сдох. Надолго. Потом этот материал изучал уже по другим источникам. Приведенную цитату читал (если не ошибаюсь, она где-то недалеко от начала), но пока не понимаю, как в моем случае поможет агрегирование. Роджерсон описывает другую ситуацию: когда у тебя уже есть компонент, который реализует нужный тебе интерфейс. А у меня такого компонента нет, так что агрегировать мне нечего. Или я не правильно понял Роджерсона?
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, programmater, Вы писали:
P>>Уже ближе. Если не будет других идей — сойдет как решение. Спасибо. У народа другие идеи есть?
B>У VC7 есть такое вот нестандартное расширение:
B>
Здравствуйте, programmater, Вы писали: P>Уже ближе. Если не будет других идей — сойдет как решение. Спасибо. У народа другие идеи есть?
Вариация на тему. Допустим, интерфейсы менять нельзя.
P>
P>class IFace1
P>{
P>public:
P> virtual int GetCount() const=0;
P>};
P>class IFace2
P>{
P>public:
P> virtual int GetCount() const=0;
P>};
class IFace1Redirect : public IFace1
{
public:
virtual int GetCount() const {
return GetCountIFace1();
}
protected:
virtual int GetCountIFace1()const=0;
};
class IFace2Redirect : public IFace2
{
public:
virtual int GetCount() const {
return GetCountIFace2();
}
protected:
virtual int GetCountIFace2()const=0;
};
P>class MyCoolMultipleIFacesImpl: public IFace1Redirect, public IFace2Redirect
P>{
P> protected:
P> virtual int GetCountIFace1() const {return 0;}
P> virtual int GetCountIFace2() const {return 1;}
P>};
P>
class Counted
{
virtual int GetCount() const=0;
};
class IFace1 : public virtual Counted
{
};
class IFace2 : public virtual Counted
{
};
class CountedImplementation : public virtual Counted
{
virtual int GetCount() const { return 0; }
};
class MyCoolMultipleIFacesImpl: public virtual IFace1, public virtual IFace2, ... ,public virtual IFaceN, public virtual CountedImplementation
{
};
"programmater" <34509@users.rsdn.ru> wrote in message news:1905029@news.rsdn.ru...
> Сабж читал, правда насчет "одного дыхания" не согласен. Дочитал до фабрик и IClassFactory и сдох. Надолго. Потом этот материал изучал уже по другим источникам. Приведенную цитату читал (если не ошибаюсь, она где-то недалеко от начала), но пока не понимаю, как в моем случае поможет агрегирование. Роджерсон описывает другую ситуацию: когда у тебя уже есть компонент, который реализует нужный тебе интерфейс. А у меня такого компонента нет, так что агрегировать мне нечего. Или я не правильно понял Роджерсона?
Да все правильно, вроде. Агрегация в данном случае — это оффтопик, прошу меня извинить, но все же я выскажусь. Все таки, идея с наследованием мне кажется не очень удачной в данном случае. Потенциально возможен конфликт двух несовместимых интерфейсов,. например, если в разных интерфейсах две одноименные функции будут отличаться только возвращаемым значением. В этом случае прийдется прибегать, например, к адаптеру, предложенному Шахтером
. При этом наследование оказывается достаточно бесполезным: вызвать эти функции можно будет только через преобразование к одному из базовых интерфейсов, а для производного интерфеса нельзя. Следовательно производный интерфейс не является видом и не работает подобно ни одному из базовых. Таким образом, налицо все предпосылки для замены наследования агрегированием с доступом к интерфейсам посредством QueryInterface() или чего то похожего.
Posted via RSDN NNTP Server 2.0
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, programmater, Вы писали:
M> А так ?
M>
M>class Counted
M>{
M> virtual int GetCount() const=0;
M>};
M>class IFace1 : public virtual Counted
M>{
M>};
M>class IFace2 : public virtual Counted
M>{
M>};
M>class CountedImplementation : public virtual Counted
M>{
M> virtual int GetCount() const { return 0; }
M>};
M>class MyCoolMultipleIFacesImpl: public virtual IFace1, public virtual IFace2, ... ,public virtual IFaceN, public virtual CountedImplementation
M>{
M>};
M>
M>Ромбовидное наследование
Не, это совсем не то. Ты предлагаешь то, от чего я всеми силами хочу уйти. Я хочу, чтобы GetCount() были разными. А ты наоборот — свел все не только в одну функцию, но и в один интерфейс вообще .
Спасибо всем ответившим. В общем, подумав над тем, что мне тут насоветовали, родил решение. Точнее 2. Первое решение для случая, когда объявления интерфейсов IFace1 — IFaceN можно немножко менять (заменять virtual на не-virtual и добавлять другие функции) — это Non Virtual Interface, предложенное здесь
. Если же интерфейс менять нельзя, то у меня получился гибрид на тему того же NVI и курьёзли рикурсив темплейтс (уже не знаю кем и когда придуманными, но активно и по делу использующимися в WTL). Решение следующего вида. IFace1 — IFaceN оставляем как были (с чисто виртуальными функциями int GetCount() const=0)
Далее пишем переходники-имплементаторы вида:
template<class RealImpl> class IFace_1_LocImpl : public IFace1
{
public:
virtual int GetCount() const {return static_cast<RealImpl*>(this)->GetCountIFace1Impl();}
};
template<class RealImpl> class IFace_2_LocImpl : public IFace2
{
public:
virtual int GetCount() const {return static_cast<RealImpl*>(this)->GetCountIFace2Impl();}
};
И наконец наследуем наш могучий класс-имплементатор от всех этих переходников — имплементаторов:
class MyCoolMultipleIFacesImpl : public IFace_1_LocImpl<MyCoolMultipleIFacesImpl> ,
public IFace_2_LocImpl<MyCoolMultipleIFacesImpl>,...
public IFace_N_LocImpl<MyCoolMultipleIFacesImpl>
{
int GetCountIFace1Impl() const {return Counter_1;}
int GetCountIFace2Impl() const {return Counter_2;}
...
int GetCountIFaceNImpl() const {return Counter_N;}
};
Здесь функции-реализации GetCountIFace1Impl() — GetCountIFaceNImpl() НЕ виртуальные, так что никакого ран-тайм оверхеда не будет. Клиенты получают реальный интерфейс (с реальной виртуальной функцией GetCount()), так что код даже скомпилированных клиентов не изменится. Оценяйте
Если же таких жестких требований нет, то вполне сгодится NVI, там тоже нет ран-тайм оверхеда. Просто немного поменяются объявления интерфейсов и все.
В общем всем спасибо. Хотя, если еще есть идеи — не откажусь