【问题标题】:Workaround for virtual static function functionality虚拟静态功能功能的解决方法
【发布时间】:2019-06-14 09:13:22
【问题描述】:

假设我有一堂课:

class StateVector {
protected:
    float* _v;

public:

    StateVector():_v(new float[size()]) {}
    virtual ~StateVector() { delete [] _v; }

    virtual size_t size() = 0;

    // ...

};

class PositionState : public StateVector {
public:
    size_t size() { return 3; }
    float* x()    { return _v; }
};

class MovingState : public PositionState {
public:
    size_t size() { return PositionState::size() + 3; }
    float* v()    { return _v + PositionState::size(); }
};

这里的目的是允许派生类通过覆盖size() 来指定状态向量的大小。 (此信息需要提供给StateVector 的构造函数,它是拥有底层数组的基类)。

但是,由于以下几个原因,这并不理想:

  • 首先,在此实现中,size() 必须/将对于类的所有实例都相同。但是在这种分解中,没有什么可以阻止同一类的不同实例对size() 有不同的看法。

  • 其次,其他类需要生成实例才能查询到合适的大小:

    template <typename State>
    class StateTransition {
    
        Matrix<float> _m;
    
        // constructor for Matrix takes #rows, #cols
        StateTransition():_m(State().size(), State().size()) {}
    
        // ...
    
    };
    

这很愚蠢,因为对于所有States,size() 将是相同的。在这种情况下,size() 可能会非常大,并且在StateTransition 的构造函数中(通过构造两个States)分配两个该大小的数组,然后立即将它们丢弃!

最后,预计每个派生类都将携带其基类状态的超集,因此基类的 size() 永远不应该比派生类小——但由于我们无法遍历继承树,我不不知道以编程方式执行此操作的方法。这是次要问题,但如果有一种干净的方法来处理它会很好。

能够写作是最有意义的:

class StateVector {
    float* _v;
    StateVector:_v(new float[size()]) {}

    virtual static size_t size() = 0;
};

class PositionState {
    static size_t size() { return 3; }
    // ...
};

// etc.

template <typename State>
class StateTransition {

    Matrix<float> _m;

    StateTransition():_m(State::size(), State::size()) {}
};

但是,此处(和其他地方)的其他答案表明不允许使用虚拟静态函数(其中一些无益地暗示它“没有意义”或“没有用”)。

解决这个问题的惯用方法是什么,使派生类尽可能容易地遵循规则?

【问题讨论】:

  • 为什么不能在构造函数中传递size作为参数?
  • @KunalPuri StateTransition 如何访问该信息?
  • virtual 在这里帮不上忙。在基类的构造函数中调用size() 将调用基类的版本。
  • @trbabb 为什么不能通过定义数据成员将该信息存储在基类中?并在需要时归还?
  • @KunalPuri 因为,根据问题,这需要构造一个实例来查询大小,这是不必要/任意昂贵的。

标签: c++ inheritance static virtual


【解决方案1】:

虽然我不确定你为什么坚持使用虚函数而不是传递给基类构造函数的数字参数,但有一个解决方案涉及虚函数,但属于不同的类:

class StateVectorInfo {
public:
  virtual int size() const = 0;
protected:
  ~StateVectorInfo () = default;
};

class PositionStateInfo : public StateVectorInfo {
  PositionStateInfo (); // don't create other instances
public:
  virtual int size() const;
  static PositionStateInfo info; // single instance
};

PositionStateInfo PositionStateInfo::info; // a definition is needed

class StateVector {
    float* _v;
public:
    StateVector (const StateVectorInfo& info):
       _v(new float[info.size()]) {
    }
};

【讨论】:

  • 我并不坚持任何东西都是虚拟的。我只要求:(a)派生类对大小的看法获胜,(b)类的所有实例的大小是统一的(c)其他类可以在不构造实例的情况下查询大小。数值传递不满足 (c)。如果存在静态虚函数,它们将满足所有这些。但他们没有,所以问题是,“应该怎么做?”
  • @trbabb "如果存在静态虚函数" 没有意义,因为这些函数可能有不同的语义。无论如何,您可以为动态查询提供一个虚拟函数,为返回相同信息的静态查询提供一个静态函数。派生类可以有一个构造函数接受参数并将其传递给基类ctor,从而允许进一步派生。
  • 但是如果你坚持在构造函数中对this使用虚函数,我可以提出另一种解决方案。
  • 以脆弱的方式将信息从派生构造函数传递到基构造函数地址 (a) 和可能 (b),但不是 (c)。如果您认为这三者都对您的解决方案感到满意,您能否提供更完整的代码?从您的示例中不清楚如何满足要求。例如,派生类是如何构造的?如果可以通过其他方式满足要求,则不需要使用虚函数。
  • @trbabb 如果我正确阅读了他的答案,他建议您在单例中定义向量信息,然后您的状态向量引用其中一个单例。这将以非常直接的方式允许 a 和 c,b 将在 info 类上强制执行。它变得更像是一个向量有某种定义,而不是一个向量定义自己。
猜你喜欢
  • 2012-10-08
  • 2014-06-11
  • 1970-01-01
  • 2014-05-12
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多