Множественное наследование интерфейсов
От: Максим Рогожин Россия  
Дата: 28.05.18 17:55
Оценка:
Привет!

Подскажите, пожалуйста, что почитать по теме дизайна использующего множественное наследование интерфейсов?
Какие паттерны там применяются?

Например, как решается такая задача

class INotifiable {
public:
   virtual void notify() =0;
};

class ISerializable {
public:
   virtual void serialize() =0;
};

shared_ptr<INotifiable> obj = ...;

if ( obj поддерживает еще и интерфейс ISerializable ) {
   сохранить obj в файл
}

Просто с помощью dynamic_cast?
Отредактировано 28.05.2018 17:57 Максим Рогожин . Предыдущая версия .
Re: Множественное наследование интерфейсов
От: Maniacal Россия  
Дата: 29.05.18 07:18
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Привет!


МР>Подскажите, пожалуйста, что почитать по теме дизайна использующего множественное наследование интерфейсов?

Почитать про RTTI (run-time type identification), динамическую типизацию данных
Хотя,
  Скрытый текст


МР>
МР>if ( obj поддерживает еще и интерфейс ISerializable ) {
МР>   сохранить obj в файл
МР>}
МР>

МР>Просто с помощью dynamic_cast?

dynamic_cast тоже из RTTI

if (typeof(ISerializable).IsAssignableFrom(typeof(obj))) {
   сохранить obj в файл
}
Re: Множественное наследование интерфейсов
От: Amygdala Россия  
Дата: 29.05.18 07:20
Оценка: 2 (1)
Здравствуйте, Максим Рогожин, Вы писали:

Я бы сделал как в COM у IUnknown есть метод QueryInterface
Re[2]: Варинант без RTTI
От: Maniacal Россия  
Дата: 29.05.18 07:36
Оценка:
Или, как вариант:

template <class T, class I> struct is_iface_supported : std::false_type
{
};

class A
{
};

class B
{
};

class C : public A
{
};
template <> struct is_iface_supported <C, A> : std::true_type
{
};

class D : public B
{

};
template <> struct is_iface_supported <D, B> : std::true_type
{
};

class E : public A, public B
{

};
template <> struct is_iface_supported <E, A> : std::true_type
{
};
template <> struct is_iface_supported <E, B> : std::true_type
{
};


int main()
{

    A a;
    B b;
    C c;
    D d;
    E e;

    if (is_iface_supported<decltype(c), A>::value) puts("c is support interface A");
    if (is_iface_supported<decltype(c), B>::value) puts("c is support interface B");
    if (is_iface_supported<decltype(d), A>::value) puts("d is support interface A");
    if (is_iface_supported<decltype(d), B>::value) puts("d is support interface B");
    if (is_iface_supported<decltype(e), A>::value) puts("e is support interface A");
    if (is_iface_supported<decltype(e), B>::value) puts("e is support interface B");

return 0;
}
Re: Множественное наследование интерфейсов
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 29.05.18 09:51
Оценка: 2 (1) -1 :)
Здравствуйте, Максим Рогожин, Вы писали:

МР>Например, как решается такая задача

Тут надо разруливать через статический полиморфизм.
Sic luceat lux!
Отредактировано 29.05.2018 9:52 Kernan . Предыдущая версия .
Re: Множественное наследование интерфейсов
От: YuriV  
Дата: 29.05.18 11:08
Оценка: 5 (3) +1 -2 :)
Здравствуйте, Максим Рогожин, Вы писали:

МР>
МР>class INotifiable {
МР>public:
МР>   virtual void notify() =0;
МР>};

МР>class ISerializable {
МР>public:
МР>   virtual void serialize() =0;
МР>};

МР>shared_ptr<INotifiable> obj = ...;

МР>if ( obj поддерживает еще и интерфейс ISerializable ) {
МР>   сохранить obj в файл
МР>}
МР>

МР>Просто с помощью dynamic_cast?

Все операции с типами нужно производить в компайл-тайм.

if(std::is_convertible<std::decay_t<decltype(obj)>*, ISerializable*>::value) { // std::is_base_of<ISerializable, std::decay_t<decltype(obj)>>::value для прямого наследования
   сохранить obj в файл
}

В С++17 можно использовать if constexpr(...), но оптимизатор и так сработает.
Re[2]: Множественное наследование интерфейсов
От: sergii.p  
Дата: 29.05.18 12:32
Оценка: 2 (1)
Здравствуйте, YuriV, Вы писали:

YV>Все операции с типами нужно производить в компайл-тайм.


YV>[ccode]

YV>if(std::is_convertible<std::decay_t<decltype(obj)>*, ISerializable*>::value) { // std::is_base_of<ISerializable, std::decay_t<decltype(obj)>>::value для прямого наследования
YV> сохранить obj в файл
YV>}

std::decay_t<decltype(obj)>* тут лишний, мы и так знаем тип obj: INotifiable
std::is_convertible<INotifiable*, ISerializable*> ничего не знает об RTTI, так что вернёт false
Re[3]: Множественное наследование интерфейсов
От: Maniacal Россия  
Дата: 29.05.18 13:10
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>std::is_convertible<INotifiable*, ISerializable*> ничего не знает об RTTI, так что вернёт false


Эта функциональность относится к компилятору и работает на этапе компиляции. К RTTI отношения не имеет и вполне себе работает, только что проверил.
Re[4]: Множественное наследование интерфейсов
От: sergii.p  
Дата: 29.05.18 13:35
Оценка:
Здравствуйте, Maniacal, Вы писали:

M>Эта функциональность относится к компилятору и работает на этапе компиляции. К RTTI отношения не имеет и вполне себе работает, только что проверил.


я тоже проверил. У меня не работает https://ideone.com/rgpggr
Re[5]: Множественное наследование интерфейсов
От: Maniacal Россия  
Дата: 29.05.18 14:00
Оценка: :)
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, Maniacal, Вы писали:


M>>Эта функциональность относится к компилятору и работает на этапе компиляции. К RTTI отношения не имеет и вполне себе работает, только что проверил.


SP>я тоже проверил. У меня не работает https://ideone.com/rgpggr


A* в B* не конвертируется, естественно, это C* конвертируется в A* и в B*, что и нужно проверить

class A
{
};

class B
{
};

class C : public A
{
};

class D : public A, public B
{
};

int main()
{
    if (std::is_convertible<C*, A*>::value) puts("C convertible to A"); // true
    if (std::is_convertible<C*, B*>::value) puts("C convertible to B"); // false
    if (std::is_convertible<D*, A*>::value) puts("D convertible to A"); // true
    if (std::is_convertible<D*, B*>::value) puts("D convertible to B"); // true
    return 0;
}
Re[6]: Множественное наследование интерфейсов
От: sergii.p  
Дата: 29.05.18 14:05
Оценка: +5
Здравствуйте, Maniacal, Вы писали:

M>A* в B* не конвертируется, естественно, это C* конвертируется в A* и в B*, что и нужно проверить


M>
M>class A
M>{
M>};

M>class B
M>{
M>};

M>class C : public A
M>{
M>};

M>class D : public A, public B
M>{
M>};

M>int main()
M>{
M>    if (std::is_convertible<C*, A*>::value) puts("C convertible to A"); // true
M>    if (std::is_convertible<C*, B*>::value) puts("C convertible to B"); // false
M>    if (std::is_convertible<D*, A*>::value) puts("D convertible to A"); // true
M>    if (std::is_convertible<D*, B*>::value) puts("D convertible to B"); // true
M>    return 0;
M>}
M>


ну а в стартовом сообщении спрашивается совсем другое. Имеем указатель на базовый класс (INotifiable) и хотим проверить, а объект, который скрывается за этим базовым классом, поддерживает ли ещё и ISerializable. Эту информацию без RTTI не получить.
Отредактировано 29.05.2018 14:07 sergii.p . Предыдущая версия .
Re[7]: Множественное наследование интерфейсов
От: Maniacal Россия  
Дата: 29.05.18 15:12
Оценка:
Здравствуйте, sergii.p, Вы писали:


SP>ну а в стартовом сообщении спрашивается совсем другое. Имеем указатель на базовый класс (INotifiable) и хотим проверить, а объект, который скрывается за этим базовым классом, поддерживает ли ещё и ISerializable. Эту информацию без RTTI не получить.


Ну или для всех интерфейсов сделать базовый интерфейс с функциями isSerializable и пр, в унаследованных реализовать возврат true/false
Re[8]: Множественное наследование интерфейсов
От: σ  
Дата: 29.05.18 15:34
Оценка:
M>Ну или для всех интерфейсов сделать базовый интерфейс с функциями isSerializable и пр, в унаследованных реализовать возврат true/false



В одном фреймворке примерно так сделали с иерархией классов гистограмм: базовый класс это одномерные гистограммы, но при этом у них есть методы доступа к осям Y и Z. Просто для этих осей они возвращают всегда 0. Двумерные возвращают 0 в методах для Z. Ну а в трёхмерных всё реализовано.
Re: Множественное наследование интерфейсов
От: Максим Рогожин Россия  
Дата: 01.06.18 18:16
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

Подскажите, пожалуйста, а есть ли минусы у такого дизайна?
class SomeClass : public ConcreteClass, // класс
                  public Interface1,    // интерфейс
                  public Interface2,    // интерфейс
                  ...
                  public InterfaceN     // интерфейс
{
//...
};


И есть ли разница в каком порядке перечислены ConcreteClass, Interface1, ..., InterfaceN?
// Так можно? Этот вариант чем-нибудь отличается от первого варианта?
class SomeClass : public Interface1,    // интерфейс
                  public ConcreteClass, // класс
                  public Interface2,    // интерфейс
                  ...
                  public InterfaceN     // интерфейс
{
//...
};
Отредактировано 01.06.2018 18:20 Максим Рогожин . Предыдущая версия . Еще …
Отредактировано 01.06.2018 18:19 Максим Рогожин . Предыдущая версия .
Отредактировано 01.06.2018 18:18 Максим Рогожин . Предыдущая версия .
Re: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 13:57
Оценка: 2 (1)
Здравствуйте, Максим Рогожин, Вы писали:

МР>Привет!


МР>Подскажите, пожалуйста, что почитать по теме дизайна использующего множественное наследование интерфейсов?

МР>Какие паттерны там применяются?

МР>Например, как решается такая задача


МР>
МР>class INotifiable {
МР>public:
МР>   virtual void notify() =0;
МР>};

МР>class ISerializable {
МР>public:
МР>   virtual void serialize() =0;
МР>};

МР>shared_ptr<INotifiable> obj = ...;

МР>if ( obj поддерживает еще и интерфейс ISerializable ) {
МР>   сохранить obj в файл
МР>}
МР>

МР>Просто с помощью dynamic_cast?

Есть поддержка:
std::dynamic_pointer_cast<ISomeInterface>(obj); // Начиная от C++11

Ну и если уже "ударять кувалдой":
std::reinterpret_pointer_cast<ISomeInterface>(obj); // Начиная от C++17
Reinterpret — это на тот случай, если классы НЕ связаны отношением наследования

 shared_ptr<ISerializable> objSer = std::dynamic_pointer_cast<ISerializable>(obj);


http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
http://www.cplusplus.com/reference/memory/dynamic_pointer_cast
Отредактировано 04.06.2018 14:07 AlexGin . Предыдущая версия . Еще …
Отредактировано 04.06.2018 14:05 AlexGin . Предыдущая версия .
Re[2]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 14:00
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Здравствуйте, Максим Рогожин, Вы писали:


МР>>Например, как решается такая задача

K>Тут надо разруливать через статический полиморфизм.

Можно и через динамический:
http://www.cplusplus.com/reference/memory/dynamic_pointer_cast
http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
Re[2]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 14:11
Оценка: 2 (1) +1 -1
Здравствуйте, Максим Рогожин, Вы писали:

МР>Здравствуйте, Максим Рогожин, Вы писали:


МР>Подскажите, пожалуйста, а есть ли минусы у такого дизайна?

МР>
МР>class SomeClass : public ConcreteClass, // класс
МР>                  public Interface1,    // интерфейс
МР>                  public Interface2,    // интерфейс
МР>                  ...
МР>                  public InterfaceN     // интерфейс
МР>{
МР>//...
МР>};
МР>


МР>И есть ли разница в каком порядке перечислены ConcreteClass, Interface1, ..., InterfaceN?

МР>
МР>// Так можно? Этот вариант чем-нибудь отличается от первого варианта?
МР>class SomeClass : public Interface1,    // интерфейс
МР>                  public ConcreteClass, // класс
МР>                  public Interface2,    // интерфейс
МР>                  ...
МР>                  public InterfaceN     // интерфейс
МР>{
МР>//...
МР>};
МР>


Разницы нет, это одно и то же.

P.S. Уточним — что под понитиями Interface1, Interface2, ... InterfaceN, подразумеваются именно
абстрактные базовые классы — не имеюшие какой-либо реализации:
http://qaru.site/questions/80587/abstract-class-vs-interface-in-c
http://natalia.appmat.ru/c&amp;c++/lezione17.php

К сожалению, в стандарте C++ понятия "интерфейс" нет, мы просто условимся считать так, как указано выше.
Отредактировано 04.06.2018 19:12 AlexGin . Предыдущая версия .
Re[2]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 14:19
Оценка:
Здравствуйте, Maniacal, Вы писали:

M>Здравствуйте, Максим Рогожин, Вы писали:


МР>>Привет!


МР>>Подскажите, пожалуйста, что почитать по теме дизайна использующего множественное наследование интерфейсов?

M>Почитать про RTTI (run-time type identification), динамическую типизацию данных
+100500
Это, IMHO, весьма важная тема для современного C++ (хотя, есть свои достоинства и у статической типизации).
Re[2]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 14:30
Оценка:
Здравствуйте, YuriV, Вы писали:

YV>Здравствуйте, Максим Рогожин, Вы писали:

...
YV>
YV>В С++17 можно использовать if constexpr(...)
YV>

+100500
Да, это ИМХО очень удобная штука, расширяющая реальное применение шаблонов!
Без неё — было более сложно сделать статическую проверку типов
Re[3]: Множественное наследование интерфейсов
От: SaZ  
Дата: 04.06.18 14:35
Оценка: -1
Здравствуйте, AlexGin, Вы писали:

AG>Разницы нет, это одно и то же.


https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-dtor-order
Re[4]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 14:57
Оценка: -1
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте, AlexGin, Вы писали:


AG>>Разницы нет, это одно и то же.


SaZ>https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-dtor-order


Я в курсе, что такое виртуальный базовый класс, но в данном случае — ТС наследует только от одного класса,
всё остальное — интерфейсы (абстрактные базовые классы). Множественного наследования реализаций — здесь нет.
Так что данная ссылка — не в тему.

P.S. Что касается виртуальных деструкторов, то в первоначальном примере только один класс ConcreteClass осуществляет очистку ресурсов.
Посему, от его позиции в списке объявляемых базовых классов зависимости нет.
Отредактировано 04.06.2018 15:58 AlexGin . Предыдущая версия .
Re[5]: Множественное наследование интерфейсов
От: SaZ  
Дата: 04.06.18 16:08
Оценка: -1 :)
Здравствуйте, AlexGin, Вы писали:

AG>Я в курсе, что такое виртуальный базовый класс, но в данном случае — ТС наследует только от одного класса,

AG>всё остальное — интерфейсы (абстрактные базовые классы). Множественного наследования реализаций — здесь нет.
AG>Так что данная ссылка — не в тему.

Совершенно чётко ясно, что порядок наследования определяет порядок инициализации полей класса и вызова конструкторов/деструкторов. Ваше утверждение что это не так — неверно.
В си++ пока ещё нет понятия интерфейса. Это может быть договоренность в рамках конкретного проекта.
Re[6]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 04.06.18 18:53
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте, AlexGin, Вы писали:


AG>>Я в курсе, что такое виртуальный базовый класс, но в данном случае — ТС наследует только от одного класса,

AG>>всё остальное — интерфейсы (абстрактные базовые классы). Множественного наследования реализаций — здесь нет.
AG>>Так что данная ссылка — не в тему.

SaZ>Совершенно чётко ясно, что порядок наследования определяет порядок инициализации полей класса и вызова конструкторов/деструкторов. Ваше утверждение что это не так — неверно.


У абстрактных базовых классов, выполняющих роль интерфейсов — полей нет (также как и конструкторов).
Насчёт деструкторов — да, вероятно, есть пустые вирт-деструкторы (а правильнее — чисто виртуальные деструкторы),
то-есть конкретно — отработает только тот, что в ConcreteClass.
Посему, данное твоё высказывание, правильное в общем случае, здесь не актуально.

SaZ>В си++ пока ещё нет понятия интерфейса.


Это понятно, но в данном случае — роль интерфейса выполняет именно он.
Вопрос был — о конкретном куске кода, не надо уходить от вопроса!

SaZ>Это может быть договоренность в рамках конкретного проекта.


http://qaru.site/questions/80587/abstract-class-vs-interface-in-c

P.S. Здесь ставился именно конкретный вопрос, на который я дал верный ответ.
Дальнейшие теоретизирования с твоей стороны, только дополнительно показывают отсутствие практических возражений.
Отредактировано 04.06.2018 19:16 AlexGin . Предыдущая версия . Еще …
Отредактировано 04.06.2018 18:56 AlexGin . Предыдущая версия .
Re[6]: Множественное наследование интерфейсов
От: Erop Россия  
Дата: 07.06.18 05:50
Оценка:
Здравствуйте, Maniacal, Вы писали:

M>A* в B* не конвертируется, естественно, это C* конвертируется в A* и в B*, что и нужно проверить


C может быть вообще в коде плагина какого-нибудь, который пока ещё не написан и не спроектирован даже. И будет поставлен сильно позже чем хост этих плагинов
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Множественное наследование интерфейсов
От: Erop Россия  
Дата: 07.06.18 05:51
Оценка: +1
Здравствуйте, Maniacal, Вы писали:

M>Ну или для всех интерфейсов сделать базовый интерфейс с функциями isSerializable и пр, в унаследованных реализовать возврат true/false


Лучше уж тогда getISerializable, который вернёт интерфейс или 0
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Множественное наследование интерфейсов
От: Максим Рогожин Россия  
Дата: 12.06.18 14:36
Оценка: :)
Привет!

Наследование только от интерфейсов:
class SomeClass : public Interface1,    // интерфейс
                  public Interface2,    // интерфейс
                  ...
                  public InterfaceN     // интерфейс
{
public:
   ~SomeClass();
//...
};


~SomeClass() переопределяет сразу все деструкторы интерфейсов? Для удаления объекта SomeClass можно любой интерфейс использовать? Например, так можно?

std::unique_ptr<Interface2> obj(new SomeClass()); // удаляться будет через интерфейс Interface2
Re[4]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 12.06.18 22:02
Оценка: 2 (1)
Здравствуйте, Максим Рогожин, Вы писали:

МР>Привет!


МР>Наследование только от интерфейсов:

МР>
МР>class SomeClass : public Interface1,    // интерфейс
МР>                  public Interface2,    // интерфейс
МР>                  ...
МР>                  public InterfaceN     // интерфейс
МР>{
МР>public:
МР>   ~SomeClass();
МР>//...
МР>};
МР>


МР>~SomeClass() переопределяет сразу все деструкторы интерфейсов?


У класса может быть только один деструктор!
Деструктор — не принимает никаких аргументов.
В деструкторе следует очистить все ресурсы, используемые в объекте данного класса и требующие очистки.

МР>Для удаления объекта SomeClass можно любой интерфейс использовать? Например, так можно?


МР>
МР>std::unique_ptr<Interface2> obj(new SomeClass()); // удаляться будет через интерфейс Interface2
МР>


Да, если в Interface2 определён виртуальный деструктор.
Если же там вирт/деструктора нет, то очистки ресурсов не будет
Отредактировано 13.06.2018 4:55 AlexGin . Предыдущая версия . Еще …
Отредактировано 12.06.2018 22:08 AlexGin . Предыдущая версия .
Re[5]: Множественное наследование интерфейсов
От: SaZ  
Дата: 13.06.18 06:43
Оценка: :)
Здравствуйте, AlexGin, Вы писали:

AG>У класса может быть только один деструктор!


Ага, конечно. Смотрите, у класса A сразу три деструктора!
class A : public B, public C {};


AG>Деструктор — не принимает никаких аргументов.

AG>В деструкторе следует очистить все ресурсы, используемые в объекте данного класса и требующие очистки.

Главное — не выкинуть при этом исключений
И вообще, это зависит от реализуемой логики.
Re[6]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 13.06.18 08:30
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте, AlexGin, Вы писали:


AG>>У класса может быть только один деструктор!


SaZ>Ага, конечно. Смотрите, у класса A сразу три деструктора!

SaZ>
SaZ>class A : public B, public C {};
SaZ>


У класса A — один деструктор!
При разрушении объекта класса A — вызываются деструкторы базовых классов (если они определены) — точнее: сначала свой деструктор (класса A), затем деструкторы базовых классов. Это поведение — стандартно для C++.

При создании объекта — всё происходит противоположно:
Первоначально отработают конструкторы базовыйх классов, а затем уже конструктор нашего (наследника).

AG>>Деструктор — не принимает никаких аргументов.

AG>>В деструкторе следует очистить все ресурсы, используемые в объекте данного класса и требующие очистки.

SaZ>Главное — не выкинуть при этом исключений

+100500
Также, как и в любых деструкторах...

SaZ>И вообще, это зависит от реализуемой логики.

+100500
...естесственно...
Отредактировано 13.06.2018 8:42 AlexGin . Предыдущая версия .
Re[5]: Множественное наследование интерфейсов
От: Максим Рогожин Россия  
Дата: 13.06.18 18:08
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>У класса может быть только один деструктор!


Спасибо! А вот так можно делать?

class Interface1 {
public:
   virtual ~Interface1() {}         // vtbl[0]
   virtual void doSomething() = 0;  // vtbl[1] Такой же как и в Interface2, но на первой позиции в vtbl
   virtual void doSomething1() = 0; // vtbl[2]
   
};

class Interface2 {
public:
   virtual ~Interface2() {}          // vtbl[0]
   virtual void doSomething2() = 0;  // vtbl[1]
   virtual void doSomething() = 0;   // vtbl[2] Такой же как и в Interface1, но на второй позиции в vtbl
};

class SomeClass : public Interface1,
                  public Interface2
{
public:
   ~SomeClass();                      // vtbl[0]
   void doSomething1() override;      // vtbl[1]
   void doSomething2() override;      // vtbl[2]
   void doSomething() override;       // vtbl[3]
   //...
};

std::unique_ptr<Interface1> obj1(new SomeClass());
std::unique_ptr<Interface2> obj2(new SomeClass());

obj1->doSomething();
obj2->doSomething();


Тут все то же самое что и с деструкторами? Метод doSomething() можно вызывать через любой интерфейс? Не зависимо от того на каком месте он в интерфейсах объявлен?
Re[6]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 14.06.18 05:00
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Здравствуйте, AlexGin, Вы писали:


AG>>У класса может быть только один деструктор!


МР>Спасибо! А вот так можно делать?

...
ИМХО — неудачный дизайн архитектуры проекта, я бы не советовал так делать
Например: у нас есть сервер БД и специализированный сервер, выполняющий некую обработку наших запросов.

И там и там есть метод, выполняющий подсоединение: Connect()
Для меня НЕ ясно — к чему подсоединяется объект типа твоего SomeClass — к серверу Базы Данных, или к спец-серверу?

В общем:
Компилятор — тебя поймёт, но коллеги по проекту (а возможно — ты же сам, через полгода-год) будут плеваться...
...
МР>Тут все то же самое что и с деструкторами? Метод doSomething() можно вызывать через любой интерфейс? Не зависимо от того на каком месте он в интерфейсах объявлен?

Ещё раз подчеркну: неоднозначности для компилятора — не будет, компилятор такой код успешно проглотит.

Так, если оба интерфейса имеют одноименный мембер, то — зачем наследовать от обоих?

Может быть — удачнее было бы отнаследоать новый интерфейс (назовем его Interface1Cool):

class Interface1 {
public:
    virtual ~Interface1() {}         // vtbl[0]
    virtual void doSomething() = 0;  // vtbl[1] Такой же как и в Interface2, но на первой позиции в vtbl
    virtual void doSomething1() = 0; // vtbl[2]

};

class Interface1Cool : public Interface1
{
    virtual void doSomethingCool() = 0; 
    virtual void doSomething2() = 0;
};

class SomeClass : public Interface1Cool
{
public:
    ~SomeClass() {};                   
    void doSomething1() override;      
    void doSomething2() override;      
    void doSomething() override;  
    void doSomethingCool() override;     
                   //...
};


P.S. В данном вопросе — множественного наследования интерфейсов вполне можно избежать,
что только поспособствует повышению читабельности кода и упрощению работы с твоим проектом
Отредактировано 14.06.2018 5:23 AlexGin . Предыдущая версия . Еще …
Отредактировано 14.06.2018 5:16 AlexGin . Предыдущая версия .
Отредактировано 14.06.2018 5:02 AlexGin . Предыдущая версия .
Re[7]: Множественное наследование интерфейсов
От: Максим Рогожин Россия  
Дата: 14.06.18 05:51
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Ещё раз подчеркну: неоднозначности для компилятора — не будет, компилятор такой код успешно проглотит.


А как компилятор будет определять где в vtbl какой метод находится? Вот, например:

class Interface1 {
public:
   virtual ~Interface1() {}         // vtbl[0]
   virtual void doSomething() = 0;  // vtbl[1] 
   virtual void doSomething1() = 0; // vtbl[2] = void doSomething1()
   
};

class SomeClass : public Interface1,
                  public Interface2
{
public:
   ~SomeClass();                      // vtbl[0]
   void doSomething1() override;      // vtbl[1] = void doSomething1()
   void doSomething2() override;      // vtbl[2]
   void doSomething() override;       // vtbl[3]
   //...
};

std::unique_ptr<Interface1> obj1(new SomeClass());
obj1->doSomething1(); // как компилятор определяет где в vtbl находится метод doSomething1()?

// метод doSomething1() в Interface1 - это vtbl[2]
// но в классе SomeClass метод doSomething1() - это уже vtbl[1]


Как компилятор определяет какой код сгенерировать?

call vtbl[2]; // такой?
call vtbl[1]; // или такой?
Отредактировано 14.06.2018 5:54 Максим Рогожин . Предыдущая версия .
Re[8]: Множественное наследование интерфейсов
От: AlexGin Беларусь  
Дата: 14.06.18 08:17
Оценка:
Здравствуйте, уважаемый Максим Рогожин, Вы писали:
...
МР>А как компилятор будет определять где в vtbl какой метод находится?

Современный компилятор C++ это достаточно догадливая система

Работая с такой системой, абстрагируешься от решений, принятых в ней.
Концентрируеш внимание на своей (точнее — пользовательской) задаче.

Вы, уважаемый Максим Рогожин, собралися разработать свой компилятор C++

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

P.S. Абстрагирование от ненуждых деталей — очень важное свойство для разработчика.
Так, например, меня не интересует какой именно компилятор у тебя/твоей команды.
Для какой OS вы пишете свой проект.

Мы знаем, что имеется таблица виртуальных функций у класса, у которого есть хотя бы одна виртуальная функция (или вирт-деструктор).
От подробностей реализации данного механизма — предлагаю абстрагироваться.
По крайней мере до тех пор, пока нам не требуется делать свой компилятор
Отредактировано 14.06.2018 8:59 AlexGin . Предыдущая версия . Еще …
Отредактировано 14.06.2018 8:24 AlexGin . Предыдущая версия .
Re[8]: Множественное наследование интерфейсов
От: vopl Россия  
Дата: 14.06.18 10:19
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Как компилятор определяет какой код сгенерировать?


Как уже говорили — этот аспект не регулируется стандартом, каждый компилятор делает как ему угодно. Далее один из возможных вариантов:

Для SomeClass таблица будет выглядеть не совсем так как ты ее нарисовал. Примерно так (упрощенно):

//первая часть - собственная
vtbl[0] -> SomeClass::~SomeClass
vtbl[1] -> void SomeClass::doSomething1()
vtbl[2] -> void SomeClass::doSomething2()
vtbl[3] -> void SomeClass::doSomething()

//вторая часть - для базы Interface1
vtbl[4] -> SomeClass::~SomeClass
vtbl[5] -> void SomeClass::doSomething()
vtbl[6] -> void SomeClass::doSomething1()

//еще часть - для базы Interface2
vtbl[7] -> SomeClass::~SomeClass
vtbl[8] -> void SomeClass::doSomething2()


Так, например, при касте SomeClass->Interface1 — в качестве таблицы для Interface1 используется вторая часть таблицы SomeClass
при касте SomeClass->Interface2 — третья часть

В такой схеме неоднозначностей при выборе слота метода — нет.
SomeClass *p = ...
p->doSomething1(); //SomeClass::vtbl[1]

Interface1 *p1 = p; //Interface1::vtbl = SomeClass::vtbl+4, "вторая часть"
p1->doSomething1(); //Interface1::vtbl[2] == SomeClass::vtbl[6]

на этом полигоне можно погонять вживую https://godbolt.org/g/ZVZzmy
в частности, увидеть реальные таблицы, строки 357, 351, 345, 327 в сгенерированном ассемблере
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.