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

您的课程仍然是抽象的,以防万一您认为这是一个问题。

没有这种方法

  • 没有办法强制派生类声明成员
  • 您可以查看模板方法模式,但同样,我不明白这一点
  • 你可以有一个纯 virtual getData,但我还是不明白这一点

从设计的角度来看,您应该将所有这些都包含在您的基类中。

于 2012-04-18T17:13:34.913 回答
0

我将在 Data 类型上对基类进行模板化,并将 Data 定义移出派生类。缺点是 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();
  }
};
于 2012-04-18T19:28:17.420 回答
0

感谢您的回复。这就是我想出的。我知道我打破了我原来的要求,即数据必须是派生类的子类,但以下设计模式确保所有派生类都有一个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();
    }
};
于 2012-04-19T07:43:23.920 回答