C++ 在基类中保存类型是否被认为是糟糕的编程

C++ 在基类中保存类型是否被认为是糟糕的编程,c++,derived-class,C++,Derived Class,我想知道编译时我的类的类型,我想知道我的想法是否被认为是糟糕的编程,或者它是否实际可行。如果有更好的方法来实现这一点,请纠正我 class Base { int type = 0; } class Derivative : public Base{ Derivative(){ type = 1; SomeObject1 o; SomeAnotherObject o1; } } class Derivative2 : public Bas

我想知道编译时我的类的类型,我想知道我的想法是否被认为是糟糕的编程,或者它是否实际可行。如果有更好的方法来实现这一点,请纠正我

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;
}
if(myBaseClass.type==1){
导数d=静态_转换(myBaseClass);
d、 o;
d、 o1;
}
if(myBaseClass.type==2){
派生2 d=静态转换(myBaseClass);
d、 试验;
d、 五,;
}
在我看来,为所有不同的对象编写虚拟方法是不寻常的

这是几年前我在编写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 );
    }
}
在基类中保存类型是否被认为是糟糕的编程 当然,是的! 使用多态虚拟设计,您不需要将额外的信息存储到基类中。编译器已经为您执行了以下操作:

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

class Derivative : public Base{
};

class Derivative2 : public Base{
};
不过,如果你需要知道这一点,这是一个糟糕设计的严重指标

您应该以纯虚拟函数定义的形式引入一些接口:

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

要安全有效地使用
静态\u cast
请改用:

模板
阶级基础{
公众:
无效剂量测定法(){
静态施法(本)->DoSomething();
}
};
类别衍生工具:公共基础{
};
类派生2:公共基{
};

这种技术的使用至少是合理的。我见过的一个涉及类层次结构,其实例需要由用户配置(由Python驱动),然后在性能关键代码中使用(在C++中)。基类提供了一个返回枚举的
getType()
方法;Python中的包装器代码调用它来发现向用户提供哪个接口。跨语言代码通常强制使用基于一致同意的整数标签的简单技术

更一般地说,有时好的设计原则,如MVC,会鼓励这种安排。即使不同的层是用同一种语言编写的,底层模型对象也不一定有像
makeqtwidts()
这样的方法,因为它要求该层不仅要知道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?
  }
}

您将如何使用
类型
????(除其他问题外)您的示例在调用构造函数的运行时之前不会设置类型变量。编译时永远不会发生在这个系统中。已经存在用于运行时确定的
typeid
关键字。你更适合询问你试图解决的问题,而不是具体的解决方法。一个典型的X/Y问题。是的,C++的思想是在数据结构的开始时远离“类型”字段。如果你发现这是你想要的方式,请回到C。@melpomene
dynamic\u cast
在我看来是不好的做法。如果我在
Base
@CarmacMondra
dynamic\u cast
中为所有对象声明函数,这看起来不是不好,不是因为它本身就是错误的,但是因为它支持你试图在这里编写的程序。换句话说,您试图做的是“不好的”(但在不了解更多细节和上下文的情况下很难说),但
dynamic\u cast
是实现它的最佳方式(当然比手动编码的整型字段要好)。:红脸:是的,这只是替换
addItem((pdfArray*)ptr的问题带有
附加项(动态\u castpr)
等,不是吗?如果我最好实现
addItem
成员,那就更好了。如果我调用
DoSomething
Base
中形成另一个方法,它是否也会调用
导数的实现,如果它是一个?@CarmacMondra是的。除非从
构造函数体中调用,否则它将被调用。这就是该功能的全部要点。与之密切相关。我需要使用CRTP吗?@CarmacMondra不,你不需要使用它。完全取决于您的实际用例。使用它有什么好处?
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) { /* ... */ }
  // ...
};
void foo(const Base &b) {
  switch(b.getType()) {
  case Base::derived1: /* ... */ break;
  case Base::derived2: /* ... */ break;
  default:
    // what goes here?
  }
}