C ++设计模式,用于继承类中子类的操作

时间:2012-04-18 17:11:27

标签: c++ design-patterns

我有一个抽象基类,它对所有派生类强制执行某些操作。除此之外,我还想强制执行某些特定于派生类中声明的子类的其他操作。

以下是一个最小的例子:

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. 上的析构函数
  4. 一个名为Data &getData()的方法,它获取对变量的引用
  5. 实现这一目标的最佳方式是什么?

3 个答案:

答案 0 :(得分: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,但是我再也没有看到这一点

从设计的角度来看,你应该在你的基类中拥有所有这些。

答案 1 :(得分:0)

我会在数据类型上模板化基类,并将数据定义移出派生类。缺点是您不再有Base的单一类型。

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();
  }
};

答案 2 :(得分:0)

感谢您的回复。这就是我想出的。我知道我正在破坏我的原始要求,即Data必须是派生类的子类,但是以下设计模式确保所有派生类都有一个data成员变量,每个变量都有不同的类型,但同样,每个这些都有一些强制执行的基本方法。我认为这是@emsr在他的一条评论中所建议的。

现在,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();
    }
};