【问题标题】:Access members of derived class through base class pointer C++通过基类指针C++访问派生类的成员
【发布时间】:2012-09-04 14:08:59
【问题描述】:

有没有办法通过基类指针让派生类的通用代码访问成员?或者有什么其他方法可以解决这个问题?

假设我有一个 Shape 类。我有继承它的类 Square 和 Triangle。两者都有自己的私有成员,它们彼此无关,因此将它们放在基类中是没有意义的。 现在,如果我需要将类写入文件,但直到需要将类写入文件时才知道该类是方形还是三角形,该怎么办?

我一直在想办法解决这个问题。最坏的解决方案是将 Square 和 Triangle 的数据写入文件,为读写添加一个标识符(Triangle 或 Square),并在加载数据时让一个小型解析器将类放在一起。这将是低效且浪费时间的。

我想知道是否有一些技巧或设计模式或任何可以帮助解决这种情况的方法。

【问题讨论】:

  • 你的意思是Strategy Design Pattern?这个想法是创建writeToFile() 方法作为抽象(纯虚拟)并让派生类实现它
  • 或许你应该先阅读一本 C++ 书籍...

标签: c++ inheritance


【解决方案1】:

这个序列化应该使用虚函数来完成。在应序列化对象的基类中定义一个函数。三角形和正方形覆盖了这个函数并写了

  • 标识符
  • 应序列化的所有数据

如果合适,您可以在基类中实现公共部分。

当您要加载文件时,您需要创建与标识符对应的类实例的工厂方法。必须调用新的实例虚拟反序列化方法来加载实际数据。

【讨论】:

  • 这个。您当然也可以在使用 typeid 检查类型后转换为 Square* 等,但这完全破坏了多态性的意义。
  • @Dan 他为什么要投?
  • 他说的是知道它是指向 Square 还是 Triangle 对象的指针。只是想为了完整起见我会提到它。
  • 感谢您的回答。这似乎是我需要的,但不幸的是它不起作用。我正在使用 Boost 序列化,它使用模板用于序列化函数,它不适用于虚函数。
【解决方案2】:

你可以在你的基类中拥有一个纯虚拟吸气剂。并且您的所有派生类都将覆盖它。像这样

class Shape{
  public:
    virtual int data() const = 0;
};
class Square: public Shape{
  private:
     int _member;
  public:
    virtual int data() const{
     //do something with private members and return it
     return _member;
    };
};

【讨论】:

  • 这假设您只需要一个数据并且它始终属于同一类型。不是很灵活。
  • 那么您需要使用模板。但是无论什么时候你请求数据,你现在应该返回什么类型
【解决方案3】:

我认为没有直接的方法可以消除这种开销。通常这是通过两件事来完成的。首先,对象需要一个序列化机制:

要序列化事物,需要一个位置来序列化。在这种情况下,我们将使用数据容器来执行此操作,但这也可以是文件流或容器类。序列化可以从对象内部或外部进行,现在最容易实现的是内部:

简单的序列化部分:

class Shape{
public:
  virtual void serialize( Datacontainer &o ) const = 0;
};

class Triangle: public Shape{
  void serialize( Datacontainer &o ) const{
    o.add('T');
    o.add(corners);
  }
  std::vector<point> corners;
}

class Circle: public Shape{
  void serialize( Datacontainer &o ) const{
    o.add('C');
    o.add(center);
    o.add(radius);
  }
  point center;
  double radius;
}

在序列化过程中,您可以通过使用基本类Shape来做到这一点:

Shape *tri = new Triangle;
tri->serialize( dataContainer or file );

反序列化并不容易,因为你需要知道类型。一个很好的模式是 Builder 模式。尽管如此,我们可以实现一个更可能的 C++ 方式来做到这一点:

将以下内容添加到所有类中:

static Shape* createFrom( Datacontainer &o );

例如。 Circle 实现:

Shape* Circle::createFrom( Datacontainer &o )
{
  Circle *c = new Circle()
  c->center = o.get();
  c->radius = o.get();
}

这使我们能够创建一个具体的实例,但我们有一个通用的函数封装用于该方法。现在可以像这样实现一个非常简单的构建器:

class ShapeBuilder
{
public:
  static Shape* createShape( Datacontainer& o )
  {
    char id = o.get();
    swith(id){
    case 'T':
      return Triangle::createFrom(o);
    case 'C':
      return Circle::createFrom(o);
    }        
  }
}

【讨论】:

    【解决方案4】:

    您需要在基类中声明虚方法,并让派生类定义它们。但是,如果您想将它们保存到文件中 - 您将需要一种方法来识别文件中的特定类实例,因为它们可能具有不同的内存布局。

    【讨论】:

      【解决方案5】:

      可能最好的方法是做这样的事情。基本模式是您可以将公共代码(保证对于每个派生类始终相同)放在基类中。需要不同的东西,放在派生类各自实现不同的虚函数中。

      class Shape {
          virtual void writeSerialData(std::ostream &) const = 0;
        public:
          void writeToFile(const std::string &filename) const {
              std::ofstream outfile(filename); // filename.c_str() in C++03
              writeSerialData(outfile);
              if (!outfile.close()) {
                  // report the error
              }
          }
          virtual ~Shape() {}
      };
      
      class Square : public Shape {
          double length;
          virtual void writeSerialData(std::ostream &out) const {
              out << "Square{" << length << '}';
          }
        public:
          Square(double l) : length(l) {}
      };
      

      现在你有下一个问题——你如何从文件中读回一个对象,而不事先知道它是哪个派生类?为此,您需要一种查看文本Square 的方法,并且要么(a)调用知道如何解释数据的Square 类的静态函数,要么(b)通过为其提供要解释的数据来实例化类Square .在您走得太远之前,值得研究 Boost Serialization 或其他序列化库。

      【讨论】:

        猜你喜欢
        • 2011-01-27
        • 1970-01-01
        • 2015-02-04
        • 2016-07-31
        • 1970-01-01
        • 2021-08-05
        • 2021-12-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多