将基类中的类型保存为编程错误

时间:2017-09-23 11:47:03

标签: c++ derived-class

我想在汇编时知道我班级的类型,我想知道我的想法是否被认为是错误的编程或者它是否真的可行。如果有更好的方法来实现这一点,可以纠正我。

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

在我看来,为所有不同的对象编写虚拟方法是不寻常的

3 个答案:

答案 0 :(得分:1)

  

将基类中的类型保存为编程错误

当然,是的!

使用多态虚拟设计,您不需要将额外信息存储到基类中。编译器已经为你做了这个:

class Base {
protected:
   virtual ~Base() {} // <<<<<<<<<<<<<
}; // Note the ;!

class Derivative : public Base{
};

class Derivative2 : public Base{
};

您始终可以使用Basedynamic_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?
  }
}