我有一个抽象基类,它对所有派生类强制执行某些操作。除此之外,我还想强制执行某些特定于派生类中声明的子类的其他操作。
以下是一个最小的例子:
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()方法。
除此之外,我想强制所有衍生的分类都有
resetData()
的方法,用于调用此变量Data &getData()
的方法,它获取对变量的引用实现这一目标的最佳方式是什么?
答案 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();
}
};