4

構築中に固定される方向とサイズの 2 つのプロパティを持つクラス Channel があります。方向は、前方 (1) または後方 (-1) の 2 つの値のいずれかを取ることができます。サイズは任意の値を取ることができますが、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 のデフォルト コンストラクターのみが呼び出されます。Channel(1, 0) を初期化リストに追加する必要があります。

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

しかし、それは ForwardChannel と ZeroChannel から派生する目的の一部を無効にしているようです。これを行うより良い方法はありますか?

4

3 に答える 3

1

どうですか(以下は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;
}
于 2013-09-07T19:04:28.570 に答える
1

別のオプションはChannel、純粋な仮想サイズおよび方向関数とデフォルト コンストラクターを使用してインターフェイスを作成することです。次にForwardChannelZeroChannelChannel から派生させて、特定の機能を実装します。

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();
}
于 2013-09-07T19:24:12.800 に答える
1
       class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
           ForwardZeroChannel() : Channel(0, 1), ForwardChannel(0), ZeroChannel(1)
        ...
       }

「ハーブシャッター」によれば、コンストラクターを呼び出すことによって親クラスのサブオブジェクトを初期化するのは派生クラスオブジェクトの責任です(仮想派生の場合)。それ以外の場合、コンパイラーは親サブオブジェクトのコンストラクターを呼び出します。

于 2013-09-07T19:03:29.473 に答える