0

すべての派生クラスに対して特定の操作を強制する抽象基本クラスがあります。これに加えて、派生クラスで宣言されたサブクラスに固有の特定の操作を強制したいと考えています。

以下は最小限の例です。

class Base {
    public:
        virtual void init() = 0;
        virtual void reset() = 0;
};

class Derived1 : public Base {
    class Data {
        int *x1;
    public:
        Data() {
            x1 = NULL;
        }

        void alloc(int num) {
            x1 = new int[num];
        }

        ~Data() {
            delete[] x1;
            x1 = NULL;
        }
    } data;

public:
    void init() { ... }
    void reset() { ... }

    void resetData() { 
        data.~Data(); 
    }
};

class Derived2 : public Base {
    class Data {
        float *x2;
    public:
        Data() {
            x2 = NULL;
        }

        void alloc(int num) {
            x2 = new float[num];
        }

        ~Data() {
            delete[] x2;
            x2 = NULL;
        }
    } data;

public:
    void init() { ... }
    void reset() { ... }

    void resetData() { 
        data.~Data(); 
    }
};

上記の例では、Base はすべての派生クラスで init() および reset() メソッドを強制します。

これに加えて、すべての派生クラスが持つことを強制したいと思います

  1. 名前付きデータのメンバー変数と
  2. resetData()この変数のデストラクタを呼び出すメソッド
  3. Data &getData()変数への参照を取得するために呼び出されるメソッド

これを達成するための最良の方法は何ですか?

4

3 に答える 3

2
  • 名前付きデータのメンバー変数と
  • この変数のデストラクタを呼び出すresetData()というメソッド
  • 変数への参照を取得するData&getData()というメソッド

これらがすべての派生クラスに共通である場合、基本クラスでこれらが必要であるように私には思えます。

class Base {
    public:
        Data data;
        void resetData();  //if data is not a pointer, are you sure you want 
                           //to call its destructor?
                           //this will lead to undefined behavior when
                           //Base is destroyed, as data will automatically 
                           //be freed
        Data& getData();
        virtual void init() = 0;
        virtual void reset() = 0;
};

これが問題だったとしても、万が一の場合に備えて、クラスはまだ抽象化されたままです。

このアプローチなし

  • メンバーを宣言する派生クラスを強制する方法はありません
  • テンプレートメソッドパターンを調べることはできますが、繰り返しになりますが、要点がわかりません
  • あなたは純粋な仮想を持つことができますが、繰り返しますgetDataが、私は要点がわかりません

設計の観点からは、これらすべてを基本クラスに含める必要があります。

于 2012-04-18T17:13:34.913 に答える
0

ご回答ありがとうございます。これが私が思いついたものです。元の要件を破っていることはわかっています。つまり、データは派生クラスのサブクラスである必要がありますが、次の設計パターンでは、すべての派生クラスがdataそれぞれ異なる型のメンバー変数を持つことが保証されますが、これらにはそれぞれいくつかの基本的なメソッドがあります。それらに適用されます。これは、@emsr が彼のコメントの 1 つで提案したものだと思います。

また、現在resetData()は別の方法で行われています。デストラクタを明示的に呼び出すことで発生する可能性のある問題を指摘してくれた @LuchianGrigore に感謝します。代わりに、このメソッドを明示的に使用するようになりました。仮想デストラクタも同じ関数を呼び出します。デストラクタから仮想関数を呼び出すべきではないことはわかっていますが、関数のスコープを明示的に設定することで、あいまいさを回避できることを願っています。(それともこれも問題?)

struct Data {
    virtual void resetData() = 0;
    virtual ~Data() {}
};

template<typename _DT>
class Base {
protected:
    _DT data;

public:
    _DT &getData() {
        return data;
    }

    void resetData() {
        data.resetData();
    }

    virtual void init() = 0;
    virtual void reset() = 0;
};

struct Data1 : public Data {
    int *x;

    Data1() {
        x = NULL;
    }

    void alloc(int num) {
        x = new int[num];
    }

    virtual void resetData() {
        delete[] x;
        x = NULL;
    }

    virtual ~Data1() {
        Data1::resetData();
    }

};

class Derived : public Base<Data1> {
public:
    virtual void init() {
        // Carry out other init operations
        Data1 &x = getData();
        x.alloc(10);
    }

    virtual void reset() {
        // Carry out other reset operations
        data.resetData();
    }
};
于 2012-04-19T07:43:23.920 に答える
0

Data 型の基本クラスをテンプレート化し、派生クラスから Data 定義を移動します。欠点は、ベースのタイプが 1 つではなくなったことです。

template <class Data>
class Base {
public:
  virtual ~Base() {}
  virtual void init() = 0;
  virtual void reset() = 0;
  virtual Data& getData() {
    return data;
  }
  virtual void resetData() {
    data.reset();
  }

protected:
  Data data;
};

class Data1 {
  int *x1;
public:
  Data1() {
    x1 = 0;
  }

  void alloc(int num) {
    x1 = new int[num];
  }

  void reset() {
    delete[] x1;
    x1 = 0;
  }

  ~Data1() {
    delete[] x1;
    x1 = 0;
  }
};



class Derived1 : public Base<Data1> {
public:

public:
  void init() { }
  void reset() { }
};


class Data2 {
  float *x2;
public:
  Data2() {
    x2 = 0;
  }

  void reset() {
    delete[] x2;
    x2 = 0;
  }

  void alloc(int num) {
    x2 = new float[num];
  }

  ~Data2() {
    delete[] x2;
    x2 = 0;
  }
};


class Derived2 : public Base<Data2> {
public:
public:
  void init() { }
  void reset() { }
  Data2& getData() {
    return data;
  }
  void resetData() {
    data.reset();
  }
};

これを行う別の方法は、単一の基本クラスから Data クラスを継承することです。この場合、メンバー変数の名前を強制的にデータにすることはできません。

class IData {
public:
  virtual ~IData() {}
  virtual void reset() = 0;
};

class Base {
public:
  virtual ~Base() {}
  virtual void init() = 0;
  virtual void reset() = 0;
  virtual IData& getData() = 0;
  virtual void resetData() = 0;
};

class Derived1 : public Base {
  class Data : public IData {
    int *x1;
  public:
    Data() {
      x1 = 0;
    }

    void alloc(int num) {
      x1 = new int[num];
    }

    void reset() {
      delete[] x1;
      x1 = 0;
    }

    ~Data() {
      delete[] x1;
      x1 = 0;
    }
  } data;

public:
  void init() { }
  void reset() { }

  IData& getData() {
    return data;
  }
  void resetData() {
    data.reset();
  }
};

class Derived2 : public Base {
  class Data : public IData {
    float *x2;
  public:
    Data() {
      x2 = 0;
    }

    void reset() {
      delete[] x2;
      x2 = 0;
    }

    void alloc(int num) {
      x2 = new float[num];
    }

    ~Data() {
      delete[] x2;
      x2 = 0;
    }
  } data;

public:
  void init() { }
  void reset() { }
  IData& getData() {
    return data;
  }
  void resetData() {
    data.reset();
  }
};
于 2012-04-18T19:28:17.420 に答える