Создание объекта из экземпляров классов, от которых он (умножается) наследуется - PullRequest
0 голосов
/ 25 апреля 2018

Скажем, у меня есть класс C, который наследуется от I, S1 и S2.Допустим, я просто интерфейс, а S1 и S2 - простые структуры без функций.Ни один из классов / структур не имеет общих членов или методов и не наследует ничего другого.

Гарантируется, что не будет проблем с алмазными иерархиями и тому подобным.Цель структур - просто предоставить альтернативу композиции.

Код будет выглядеть примерно так:

class C : public I, public S1, public S2
{
  ...
};

Теперь полезный аспект этого заключается в том, что я могу просто приводить экземпляр C к S1 или S2 всякий раз, когда мне нужен объектэто только та часть этого.Дело в том, что использование существующего C в качестве S1 или S2 (или даже I) является простым (и полезным).

Однако в дополнение к этому варианту использования я также хотел бы иметь возможность создатьновый экземпляр C, когда предоставляется объект S1 и объект S2.Первой (и единственной) вещью, которая пришла на ум, был конструктор, который будет работать примерно так:

C::C(const S1& s1, const S2&s2) : I(), S1(s1), S2(s2)
{
  ...
}

Однако у меня есть три проблемы с этим решением:

  • Требуется дополнительный код и дополнительные конструкторы из моего класса C, что довольно не элегантно, и я предполагаю, что должен быть лучший способ.
  • Требуется, чтобы мои структуры S1 и S2 содержали конструкторы копирования (или перемещали конструкторы, если реализованы по-другому).Хотя для меня это работает нормально, даже с конструктором по умолчанию, это явный показатель того, что это не общее решение и, следовательно, является плохим или, по крайней мере, неполным.
  • Из-за структуры фреймворка иВ приложении, которое я создаю, это решение потребовало бы еще больше функций и кода, чем в простом примере.Еще раз, написать код не так уж и сложно, а просто больше кода, который нужно поддерживать и обдумывать.Кроме того, использование простой композиции с экземплярами S1 и S2 внутри C может оказаться сопоставимым, если не менее подробным.

Итак, чтобы подвести итог:

Каковы возможные способы создания объекта, который умножается, наследуется от некоторых классов, когда у нас уже есть готовые объекты этих классов (или просто необходимая вещь для их создания)?

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

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

Композиция с помощью наследования - это вещь в языках, которые ее поддерживают, но это считается практикой, не соответствующей стандартам, или даже композицией против шаблона в ООП.

С наследованием вы получаете автоматическую реализацию метода и преобразования. Но вы отказываетесь от контроля над API. И вы вводите связь между S1 и S2, потому что их реализация ограничивает друг друга, они не могут называть своих членов одинаковыми, не вводя двусмысленности. ИМХО, здесь больше ловушек из-за большей неявности, идущей таким образом.

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

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

Вам не нужны копии в любом случае. Для наследования вы можете предоставить make_C средство и переслать аргументы c'or. S1 и S2 должны обеспечивать любой способ построения, который вы можете использовать повторно, что верно для обоих подходов. Вы должны быть в состоянии написать соответствующие c'tors для шаблона наследования.

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

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

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

Я бы сказал, что чем более закрыт и окостеневший дизайн, и вы уверены, что никакое наследование помех не будет более безопасным для его профессионалов. В противном случае, это может быть PIA и необходимость рефакторинга в какой-то момент времени. Если у вас огромные классы, повторяющиеся интерфейсы будут PIA.

0 голосов
/ 25 апреля 2018

В идеале было бы замечательно, если бы я мог создать не полностью построенный C, а затем просто сделать c = s1; c = s2;, и только части s1 и s2 в c изменились бы с каждым назначением.

Что ты знаешь! Вы можете очень легко достичь этого точного синтаксиса. По правде говоря, у каждого базового класса есть operator =, который вы наследуете. Он просто скрыт собственным производным классом operator = (неявно сгенерированным или нет). Таким образом, вам нужно всего лишь вернуть их в область видимости:

class C : public I, public S1, public S2
{
public:
    using I::operator=;
    using S1::operator=;
    using S2::operator=;
};

... который ведет себя именно так, как вы описали. Однако ничего особенного в том, что базовые классы созданы по умолчанию, что может растянуть то, что вы подразумеваете под «не полностью собранными». К сожалению (?) Невозможно иметь действительный производный объект, базовые классы которого вообще не были инициализированы.

Смотрите его в прямом эфире на Колиру

...