LINUX.ORG.RU

[C++] Наследование от (двух) интерфейсов с одинаковыми сигнатурами функций

 


0

1

Вот приспичило разобрать такую ситуацию: есть класс, наследующий от двух интерфейсов. У интерфейсов одинаковое количество функций, и одинаковые их сигнатуры, но «начинка» функций в каждом интерфейсе должна быть своя:

struct Iface0
{
 void func0(void) = 0;
 void func1(void) = 0;
};
struct Iface1
{
 void func0(void) = 0;
 void func1(void) = 0;
};
struct Implement : Iface0,Iface1
{
 //???
 void func0(void) { /**/ return; }; // Где чья реализация?
 void func1(void) { /**/ return; }; // Где чья реализация?
};

Не могу понять, как в Implement-е разграничить реализации функций Iface0 от Iface1? Я что-то упускаю, или так вообще делать нельзя?

Читай стандарт. Стандарт на язык - это именно тот документ, с которым ты должен сверяться в подобных ситуациях.

anonymous
()

Я что-то упускаю, или так вообще делать нельзя?

Нельзя. По крайней мере в стандартном C++.

Есть workaround:

struct Iface0Wrap : public Iface0 {
void Iface0_func0() = 0;
void func0() { Iface0_func0(); }
};

struct Iface1Wrap : public Iface1 {
void Iface1_func0() = 0;
void func0() { Iface1_func0(); }
};

struct Implement : Iface0Wrap, Iface1Wrap {
void Iface0_func0() {
 // ...
}
void Iface1_func0() {
 // ...
}
};

Если пишется непереносимый с винды код, с использованием компилятора от микрософт, то можно написать void Iface0::func0() { /* ... */ } в классе Implement

nozh
()
Ответ на: комментарий от nozh

> Нельзя. По крайней мере в стандартном C++

Понятно, спасибо.

Если пишется непереносимый с винды код, с использованием компилятора от микрософт


Не, GCC.

one_more_hokum ★★★
() автор топика
Ответ на: комментарий от one_more_hokum

> Так а в каком пункте Стандарта об этом говорится?

Это к анонимусу. :) А вообще, в C++ FAQ где-то об этом написано, насколько я помню.

nozh
()
Ответ на: комментарий от one_more_hokum

> Так а в каком пункте Стандарта об этом говорится?

О чём именно? Что тебе не понятно?

anonymous
()
Ответ на: комментарий от anonymous

> О чём именно?

О невозможности такого наследования интерфейсов, которое мне в голову взбрело.

one_more_hokum ★★★
() автор топика
Ответ на: комментарий от nozh

>> Так а в каком пункте Стандарта об этом говорится?

Это к анонимусу. :) А вообще, в C++ FAQ где-то об этом написано, насколько я помню.

Про C++ FAQ похоже соврал. Зато нашелся развернутый ответ на stackoverflow.com: http://stackoverflow.com/questions/2004820/inherit-interfaces-which-share-a-m...

В частности там пишут, что решение этой проблемы описывалось в книгах Страуструпа Design & Evolution of C++ section 12.8 и The C++ Programming Language section 25.6. Думаю, книги подойдут как достоверный источник информации о стандартном C++.

nozh
()

интерфейс определяет сигнатуры функций - они одинаковые, зачем 2 раза?

реализации же, пишутся уже в конкретном классе

shty ★★★★★
()

реализации функций Iface0 от Iface1?

Где здесь вы видите реализации функций? Есть только сигнатуры, сигнатуры совпадают. Как следствие, разграничивать попросту нечего. Если вы приведете экземпляр конкретного класса к разным типам абстрактных базовых, то получите одинаковое поведение каждой из виртуальных функций.

Да, не забудьте сделать у Iface[12] виртуальные деструкторы.

ilias
()

Наличие одинаковых вплоть до запятых интерфейсов как бы намекает, что вы ошиблись при проектировании вашей иерархии классов. Целесообразнее будет не искать описание поведения в Стандарте, а пересмотреть вашу иерархию.

ilias
()
Ответ на: комментарий от ilias

>Целесообразнее будет не искать описание поведения в Стандарте, а пересмотреть вашу иерархию.

В общем случае он может наследоваться от нескольких сторонних поставщиков базовых классов.

deathmokar
()

Есть еще virtual наследование, для таких случаев, но пользоваться не рекомендуется ибо костыль. И вообще избегайте множественного наследования без нужды

Gorthauer ★★★★★
()
Ответ на: комментарий от deathmokar

> В общем случае он может наследоваться от нескольких сторонних поставщиков базовых классов.

Вероятность полного совпадения интерфейсов пренебрежимо мала. Если они совпадают, то эти библиотеки почти наверное делают одно и то же.

Да и часто ли вы наследовали ваши классы одновременно, дрпустим, от классов Qt и GTK+?

ilias
()
Ответ на: комментарий от ilias

> Да и часто ли вы наследовали ваши классы одновременно, дрпустим, от классов Qt и GTK+?

Интересно, зачем это может понадобиться?

anonymous
()
Ответ на: комментарий от Gorthauer

>Есть еще virtual наследование, для таких случаев

Оно ж для ромбического случая, не?

yoghurt ★★★★★
()

Все больше убеждаюсь, что ЛОР прикольное место. Все что угодно, начиная от полностью лажовых ответов (нельзя), до отсылок к стандарту и обсуждения зачем, все что угодно кроме ответа на сабж;-))))))

На самом деле никаких проблем нет - реализации в Implement перекрывают обоих предков. Если в Implement надо вызвать ф-ю кого то из предков, надо явно указывать кого, напр. Iface0::func0(...).

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

В предложенном примере virtual вообще отсутвует, так что сыр бор про виртуальность вааще к чему был??????? Если все виртуальное, то опять таки - в Implement свои реализации нужны.

Множетсвенное наследование от предков с одинаковыми методами выглядит и правда странновато, но таки используется, напр iostream наследует istream и ostream одновременно, причем нек-е методы есть и там и там, ну и чего? И не такое бывает... хотя лично я стараюсь так не делать;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

> Если в Implement надо вызвать ф-ю кого то из предков, надо явно указывать кого, напр. Iface0::func0(...).

Слова Iface и void f() = 0; как бы намекают на чисто абстрактные классы.

ilias
()
Ответ на: комментарий от ilias

А... виноват, я проглядел (а автор описался?)
Но тогда тем более правблов никаких - есть Implement, есть два абстрактных предка - хошь юзай через одного, хошь через другого. Я тогда вопроса не понимаю - что значит где чья реализация???? Там одна реализация наследника, и она с-но везде...


#include <stdio.h>

struct Iface0
{
virtual void func0(void) = 0;
virtual void func1(void) = 0;
};
struct Iface1
{
virtual void func0(void) = 0;
virtual void func1(void) = 0;
};
struct Implement : Iface0,Iface1
{
//???
   void func0(void) { printf(«0\n»); }; // Где чья реализация?
   void func1(void) { printf(«1\n»); }; // Где чья реализация?
};


int main(){
   Implement I;
   ((Iface0*)(&I))->func0();
   ((Iface1*)(&I))->func1();
   return 0;
}

AIv ★★★★★
()
Ответ на: комментарий от ilias

Ну да, Вы с-но уже ответили, еще раз виноват;-)

AIv ★★★★★
()

>Защита от SQL-injection. (11.01.2011)

Есть ли смысл в private static методах? (14.01.2011)

Нужен совет по организации классов (14.01.2011)


[Mono][C#] Иерархия классов: как правильно сделать? (16.01.2011)


[C++] Наследование от (двух) интерфейсов с одинаковыми сигнатурами функций (17.01.2011)



...

yaws
()
Ответ на: комментарий от AIv

> > Не могу понять, как в Implement-е *разграничить реализации* функций Iface0 от Iface1? Я что-то упускаю, или так вообще делать нельзя?

Все что угодно, начиная от полностью *лажовых ответов (нельзя)*, до отсылок к стандарту и обсуждения зачем, все что угодно кроме ответа на сабж;-))))))

На самом деле никаких проблем нет - *реализации в Implement перекрывают обоих предков.*

Вывод: правильный ответ - «нельзя», а ты противоречишь сам себе

anonymous
()
Ответ на: комментарий от Gorthauer

избегайте множественного наследования без нужды

Но здесь же множественное наследование интерфейсов. Как я помню, в языках, где множественное наследование классов явно запрещено (та же Java) разрешается множественное наследование интерфейсов. Правда, там и интерфейсы немного более другие, чем в C++.

one_more_hokum ★★★
() автор топика
Ответ на: комментарий от AIv

> Все что угодно, начиная от полностью лажовых ответов (нельзя), до отсылок к стандарту и обсуждения зачем, все что угодно кроме ответа на сабж

Расширяет кругозор, улучшает аппетит, и всё такое прочее. :-)

one_more_hokum ★★★
() автор топика
Ответ на: комментарий от one_more_hokum

Наследование, как я понимаю, тут вообще ни при чём. Класс *реализует* несколько интерфейсов, а вовсе не наследуется от них

Смоляное Чучелко

anonymous
()
Ответ на: комментарий от anonymous

Вывод:

1) нельзя ответ нправильный, правильный ответ можно.

2) у ананимусов обычно что то с головой. То ли телевизор смотрят весь подряд, то ли стандартов С++ перечитали...

AIv ★★★★★
()
Ответ на: комментарий от anonymous

Наследование, как я понимаю, тут вообще ни при чём. Класс *реализует* несколько интерфейсов, а вовсе не наследуется от них

Ну да, можно наверное и так сказать. В общем некая терминологическая путаница.

AIv ★★★★★
()
Ответ на: комментарий от Gorthauer

Есть еще virtual наследование, для таких случаев, но пользоваться не рекомендуется ибо костыль.

Такой же костыль, как и virtual функции.

Правда, к данному случаю отношения не имеет.

И вообще избегайте множественного наследования без нужды

Множественное наследование полностью абстрактных классов - вполне разумная вещь.

Miguel ★★★★★
()
Ответ на: комментарий от AIv

Собственно вопрос был такой, цитирую: «Не могу понять, как в Implement-е разграничить реализации функций Iface0 от Iface1? Я что-то упускаю, или так вообще делать нельзя?»

1) нельзя ответ нправильный, правильный ответ можно.

Если вы считаете, что можно это сделать без костылей, то продемонстрируйте, пожалуйста, как.

nozh
()
Ответ на: комментарий от Miguel

Множественное наследование полностью абстрактных классов - вполне разумная вещь.

Ну да, только методы с одинаковыми сигнатурами в разных абстрактных классах не есть гуд.

Gorthauer ★★★★★
()
Ответ на: комментарий от nozh

Выше уже все сказали. Если предки невиртуальные - то см. мой первый ответ, если предки вирутальные - то непонятно, чего автору непонятно (сам вопрос некорректен, т.к. реализация токо у потомка и непоятно чего хочется разграничить).

Если вы считаете, что можно это сделать без костылей, то продемонстрируйте, пожалуйста, как.

С удовольствием, только сформулируйте пожалуйста ЧТО сделать???;-)))

AIv ★★★★★
()
Ответ на: комментарий от Gorthauer

Ну да, только методы с одинаковыми сигнатурами в разных абстрактных классах не есть гуд.

Не есть, но они могут быть в библиотеках.

Miguel ★★★★★
()
Ответ на: комментарий от AIv

Собственно вопрос был такой, цитирую: «Не могу понять, как в Implement-е разграничить реализации функций Iface0 от Iface1? Я что-то упускаю, или так вообще делать нельзя?»

Если вы считаете, что можно это сделать без костылей, то продемонстрируйте, пожалуйста, как.

С удовольствием, только сформулируйте пожалуйста ЧТО сделать???;-)))

Сделать то что спрашивал ТС.

На всякий случай объясняю вопрос ТС-а: нужно сделать что бы приведенный ниже код вывел в консоль «Hello, world!», не меняя функцию main

#include <cassert>
#include <cstdio>

struct Iface0 {
  virtual int foo() = 0;
};

struct Iface1 {
  virtual int foo() = 0;
};

class Implementer : public Iface0, public Iface1 {
// напишите реализацию функций Iface0::foo и Iface1::foo, что бы они исполняли разный код
}

int main() {
  Implementer impl;
  assert(static_cast<Iface0&>(impl).foo() == 0);
  assert(static_cast<Iface1&>(impl).foo() == 1);
  std::printf("Hello, world!");
  return 0;
}
nozh
()

интерфейс сам по себе всего лишь гарантия, что унаследовавший его класс имеет определенные методы.
смысла в двух эквивалентных друг другу интерфейсах нету в принципе. равно как и в наслодовании их обоих (ибо интерфейсы не содержат данных).
вы просто не совсем до конца понимаете понятие и профит интерфейсов.
в особенности это видно по вопросу «// Где чья реализация?»

xydo ★★
()

Scott Douglas Meyers

Effective C++, Second Edition.

Item 43: Use multiple inheritance judiciously.

dave ★★★★★
()
Ответ на: комментарий от nozh

Т.е. вы хотите сделать две разных реализации для двух разных веток наследования для одной синатуры ф-ии, думаете автор топика хочет именно этого (он сам понимает чего хочет)? Желание ИМНО довольно странное, в рамках C++ наверное самое разумное решение - то которое Вы предлагали выше, через промежуточных наследников. Что б напрямую - да, этого и правда сделать нельзя.

AIv ★★★★★
()
Ответ на: комментарий от nozh

Совершенно непонятна природа требования, когда поведение функций с одинаковыми сигнатурами должно различаться.

ilias
()
Ответ на: комментарий от xydo

> вы просто не совсем до конца понимаете понятие и профит интерфейсов

Ну, в плюсах интерфейсы, как таковые, вообще в противозачаточном состоянии находятся, в сравнении с той же Java.

смысла в двух эквивалентных друг другу интерфейсах нету в принципе


Не обязательно они будут полностью эквивалентны, достаточно совпадения одного метода.

one_more_hokum ★★★
() автор топика
Ответ на: комментарий от AIv

> он сам понимает чего хочет?

В голову порой приходят самые идиотские идеи.

one_more_hokum ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.