C ++ алмазоподобное наследование

У меня есть класс Channel с двумя двумя свойствами, направлением и размером, которые фиксируются во время строительства. Направление может принимать только одно из двух значений: вперед (1) или назад (-1). Размер может принимать любое значение, но между 0 и любым ненулевым значением есть физически значимое различие.

Я хотел бы иметь возможность писать функции, которые принимают объекты Channel с известными значениями для направления и / или размера, и я подумал реализовать это с использованием производных классов:

                            Channel
|
-----------------------------------------------
|                |              |             |
ForwardChannel  BackwardChannel  ZeroChannel  NonzeroChannel
|                |              |             |
|                ----------------            ...
|                        |      |
|          BackwardZeroChannel  |
|                               |
---------------------------------
|
ForwardZeroChannel

Очевидно, я не нарисовал все перестановки.

Я попытался реализовать это так

class Channel {
Channel(int direction, int size) { ... };
...
}

class ForwardChannel: public virtual Channel {
ForwardChannel(int size) : Channel(1, size) { ... }
...
}

class ZeroChannel: public virtual Channel {
ZeroChannel(int direction) : Channel(direction, 0) { ... }
...
}

class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
ForwardZeroChannel() : ForwardChannel(0), ZeroChannel(1)
...
}

Создание ForwardChannel и ZeroChannel работает нормально. Создание ForwardZeroChannel вызывает только конструктор по умолчанию для Channel, который не устанавливает значения. Я должен добавить канал (1, 0) в список инициализатора:

class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
ForwardZeroChannel() : Channel(0, 1), ForwardChannel(0), ZeroChannel(1)
...
}

но это, кажется, побеждает некоторые цели получения из ForwardChannel и ZeroChannel. Есть ли лучший способ сделать это?

4

Решение

как насчет (после C ++ 11, но он может быть перенесен на C ++ 99 (кроме «использование шаблона»)):

class Channel {
public:
virtual ~Channel();
protected:
Channel(int direction, int size);
};

template<bool forward, bool zero>
class ChannelT : public Channel {
public:
template <bool b = zero, typename T = typename std::enable_if<b>::type>
ChannelT() : Channel(forward ? 1 : 0, 0) {}

template <bool b = zero, typename T = typename std::enable_if<!b>::type>
explicit ChannelT(int size) : Channel(forward ? 1 : 0, size) { assert(size != 0); }
};

template <bool zero> using ForwardChannel = ChannelT<true, zero>;
using ForwardZeroChannel = ChannelT<true, true>;
using ForwardNonZeroChannel = ChannelT<true, false>;
// And so on for the 5 other types...

int main() {
ForwardZeroChannel forwardZeroChannel;
ForwardNonZeroChannel forwardNonZeroChannel(42);
return 0;
}
1

Другие решения

       class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
ForwardZeroChannel() : Channel(0, 1), ForwardChannel(0), ZeroChannel(1)
...
}

В соответствии с «Травяным затвором» ответственность за объектные объекты родительского класса заключается в инициализации дочерних объектов родительского класса путем вызова конструкторов (в случае виртуального деривации), в противном случае компилятор вызовет конструктор родительских дочерних объектов.

1

Другой вариант будет делать Channel интерфейс с чисто виртуальными функциями размера и направления и конструктором по умолчанию. затем ForwardChannel или же ZeroChannel получить из канала и реализовать конкретные функции.

struct Channel
{
virtual int direction() const = 0;
virtual int size() const = 0;
virtual ~Channel() {}
};

struct ForwardChannel: virtual public Channel
{
virtual int direction() const override { return 1; }
};

struct ZeroChannel: virtual public Channel
{
virtual int size() const override { return 0; }
};

struct ForwardZeroChannel: public ForwardChannel, public ZeroChannel
{

};

int main()
{
ForwardZeroChannel z;
return z.size() + z.direction();
}
1
По вопросам рекламы [email protected]