我想在汇编时知道我班级的类型,我想知道我的想法是否被认为是错误的编程或者它是否真的可行。如果有更好的方法来实现这一点,可以纠正我。
class Base {
int type = 0;
}
class Derivative : public Base{
Derivative(){
type = 1;
SomeObject1 o;
SomeAnotherObject o1;
}
}
class Derivative2 : public Base{
Derivative2(){
type = 2;
RandomObject test;
AnotherObject v;
}
}
将myBaseClass
作为Base
的一些方法:
if(myBaseClass.type == 1){
Derivative d = static_cast<Derivative>(myBaseClass);
d.o;
d.o1;
}
if(myBaseClass.type == 2){
Derivative2 d = static_cast<Derivative2>(myBaseClass);
d.test;
d.v;
}
在我看来,为所有不同的对象编写虚拟方法是不寻常的
答案 0 :(得分:1)
将基类中的类型保存为编程错误
使用多态虚拟设计,您不需要将额外信息存储到基类中。编译器已经为你做了这个:
class Base {
protected:
virtual ~Base() {} // <<<<<<<<<<<<<
}; // Note the ;!
class Derivative : public Base{
};
class Derivative2 : public Base{
};
您始终可以使用Base
从dynamic_cast
指针或引用检测实际类类型,然后:
Base* pd1 = new Derivative();
Base* pd2 = new Derivative2();
if(dynamic_cast<Derivative>(pd1)) { // Yields true
}
if(dynamic_cast<Derivative>(pd2)) { // Yields false
}
虽然你需要知道这一点,但这是一个糟糕设计的严重指标。
您应该以纯虚函数定义的形式介绍一些接口:
class Base {
protected:
virtual ~Base() {}
public:
virtual void DoSomething() = 0;
};
class Derivative : public Base{
public:
void DoSomething() override {
// provide an implementation specific for Derivative
}
};
class Derivative2 : public Base{
public:
void DoSomething() override {
// provide an implementation specific for Derivative2
}
};
这允许您在不知道实现该功能的特定类型的情况下调用DoSomething()
:
Base* pd1 = new Derivative();
Base* pd2 = new Derivative2();
pd1->DoSomething(); // calls Derivative specific implementation
pd2->DoSomething(); // calls Derivative2 specific implementation
为了安全有效地使用static_cast
,请改用CRTP:
template<typename Derived>
class Base {
public:
void DoSomething() {
static_cast<Derived*>(this)->DoSomething();
}
};
class Derivative : public Base<Derivative> {
};
class Derivative2 : public Base<Derivative2> {
};
答案 1 :(得分:0)
这是我在几年前使用pdf编写器进行黑客攻击时使用的(丑陋)方法。它似乎解决了你所遇到的完全相同的问题。
pdfArray::pdfArray(const pdfArray &src)
{
vecObjPtrIter iter;
pdfObj *ptr;
mArray = new vecObjPtr;
for (iter=src.mArray->begin(); iter!=src.mArray->end(); iter++)
{
ptr = *iter;
if (typeid(*ptr) == typeid(pdfString))
addItem( (pdfString*)ptr );
if (typeid(*ptr) == typeid(pdfInt))
addItem( (pdfInt*)ptr );
if (typeid(*ptr) == typeid(pdfFloat))
addItem( (pdfFloat*)ptr );
if (typeid(*ptr) == typeid(pdfArray))
addItem( (pdfArray*)ptr );
}
}
答案 2 :(得分:0)
这种技术的使用至少是合理的。我见过的一个涉及一个类层次结构,其实例需要由用户配置(由Python驱动),然后用于性能关键代码(在C ++中)。基类提供了一个返回枚举的getType()
方法; Python中的包装器代码调用它来发现为用户提供的接口。跨语言代码通常会强制使用基于商定的整数标签的这种简单技术。
更一般地说,有时像MVC这样的好的设计原则会鼓励这种安排。即使不同的层使用相同的语言编写,底层模型对象也不一定要有makeQtWidgets()
这样的方法,因为它要求该层不仅要知道GUI库,还要知道关于用户界面的布局和控制流程。
实用点:为了避免派生类无法指定其类型的情况,基类应该在其构造函数中需要值:
struct Base {
enum Type { derived1, derived2 };
Base(Type t) : typ(t) { /* ... */ }
virtual ~Base()=0;
Type getType() const {return typ;}
// ...
private:
Type typ;
};
struct Derived1 : Base {
Derived1() : Base(derived1) { /* ... */ }
// ...
};
您也可以将所有可能性的enum
放在基类中,因为每个派生类的值必须已经有一个中央注册表,即使它只是在纸上。这是其他人提到的一个缺点:这种设计要求所有类别集中管理,不可能进行独立扩展。
最后,尽管缺乏灵活性,客户必须始终面对意外类型对象的丑陋可能性:
void foo(const Base &b) {
switch(b.getType()) {
case Base::derived1: /* ... */ break;
case Base::derived2: /* ... */ break;
default:
// what goes here?
}
}