0

我正在用 C++ 编写一个物理程序,以使用几种算法来近似对象的范围。我已经声明了一个包含具体和抽象方法的基类 Simulation。(例如,近似范围的函数是纯虚函数,而获得下一个位置的函数是具体的,因为无论算法如何,这都是相同的。)为了定义这些具体的方法,我已经在基类中声明了所有变量也是。在派生类中,我定义了基类的抽象方法和新的、特定于算法的方法;这要求我在基类和派生类中都定义成员。

问题是基类中定义的具体方法正在访问基类的成员,而不是派生类中定义的覆盖方法。有没有办法做到这一点,还是我的方法本身不正确?

我对构造函数也有类似的问题;从派生类的构造函数调用基构造函数正在初始化基成员。

无论如何,谢谢你的时间。虽然我对 C++ 并不完全陌生,但我更倾向于将它用作 C;我对面向对象的概念相当缺乏经验。我可能犯了一个非常基本的错误(或更可能是设计缺陷),但除了在派生类中重新定义方法之外,我还没有发现任何类似的东西或从测试中得到任何积极的结果。(我假设我应该避免这样做)

更新:尽管按照 R Sahu 的要求使成员受到保护(感谢 Ozraptor;不太确定我是如何错过的),但我将发布一些我的(现已更新的)代码:

基类:

class Simulation {
 public:
  Simulation();
  Simulation(float, float, float, float, float, float, float, float, float);
  bool IsInitialized(),
       set_air_density(float),
       set_delta_time(float),
       set_drag_coefficient(float),
       set_mass(float),
       set_reference_area(float);
  float next_x_position();
  void set_next_x_position(float),
       set_next_x_velocity(float),
       set_next_y_position(float),
       set_next_y_velocity(float);
 protected:
  static const float gravitational_acceleration_;
  virtual bool SimulateRangeOrDie() = 0;
  virtual void GetNextVelocity() = 0,
               GetNextXVelocity() = 0,
               GetNextYVelocity() = 0,
               InitializeConstant() = 0;
  void GetNextPosition(),
       GetNextXPosition(),
       GetNextYPosition(),
       PushBackPositionVelocity();
  bool x_position_initialized_,
       x_velocity_initialized_,
       y_position_initialized_,
       y_velocity_initialized_;
  float air_density_,
        current_x_position_,
        current_x_velocity_,
        current_y_position_,
        current_y_velocity_,
        delta_time_,
        drag_coefficient_,
        constant_,
        mass_,
        next_x_position_,
        next_x_velocity_,
        next_y_position_,
        next_y_velocity_,
        reference_area_;
};

派生类之一:

class MomentumSimulation : public Simulation {
 public:
  MomentumSimulation();
  MomentumSimulation(float, float, float, float, float, float, float, float,
                     float);
  virtual bool SimulateRangeOrDie();
 private:
  virtual void GetNextVelocity(),
               GetNextXVelocity(),
               GetNextYVelocity(),
               InitializeConstant();
  void GetNextMomentum(),
       GetNextXMomentum(),
       GetNextYMomentum(),
       Initialize(),
       InitializeMomentum(),
       InitializeXMomentum(),
       InitializeYMomentum(),
       PushBack(),
       PushBackMomentum();
  float current_x_momentum_,
        current_y_momentum_,
        next_x_momentum_,
        next_y_momentum_;
};
4

3 回答 3

2

如果需要从基类函数实现中访问成员,则仅将这些成员“提升”为(受保护的)基类成员 - 您还需要在派生类中声明它们。在这两种情况下,基类和派生类函数都可以直接访问它们。

重新构造函数 - 由于构造函数调用的顺序,您只能初始化正在构造的实际类的成员,或者它的基类成员。您不能初始化派生类成员,因为它们还不存在!

于 2014-03-11T02:31:34.790 回答
0

这是一个广泛的问题,需要您对继承有更好的理解。但是,您必须首先了解以下几点:

如果你继承了一个类(超类)并且超类有一些具体的方法,你不需要在子类中再次实现它们,除非你当然希望有一个特定于该子类的不同实现。这称为覆盖该方法。

如果您的子类中有一个重写的方法,将使用哪些类成员和方法完全取决于您调用该方法的对象的声明:

SuperClass a = new SuperClass();
a.someMethod(); //definitely the one in the superclass is called

SuperClass b = new SubClass();
b.someMethod(); //the one in the SuperClass called
((SubClass)b).someMethod(); //the one in SubClass called

SubClass c = new SubClass(); //trivial
于 2014-03-11T02:37:51.763 回答
0

应声明应在每个特定算法(每个派生类)中覆盖的基类方法virtual。这种方法有时称为模板方法模式
至于成员数据,即使您可以在派生类中访问基类的受保护数据成员,并且这通常是编写代码的最快方法,但最简洁的方法是不直接从基类访问成员数据,而是访问或操作此数据的成员函数,并改为调用它们。这源于封装OO原理。请参阅:使成员变量受保护是一种好习惯吗?

为了给你一个稍微做作的 (C++11) 示例,这是一个基本模拟类:

using namespace std;
using namespace chrono;

class Simulation {
  public:
  // this is the same across all simulations:
    void run() {
      recordStartTime();
      bool result = actuallySimulateStuff();
      printSimulationResult(result);
    }

  protected:
  // virtual methods should be overridden in derived classes
  // this one has to be
    virtual const string& name() const = 0;
  // this one may not be, but if you don't, the simulation isn't going tobe very interesting
    virtual bool actuallySimulateStuff() { return false; }

  // these methods, like run() above, are invariant for all inherited classes
    void recordStartTime() {
      start_time_ = system_clock::now();
    }
    const start_time_& start_time() const {
      return start_time_;
    }
    void printSimulationResult(bool result) {
      auto end_time = system_clock::now();
      auto durationInMS = duration_cast<milliseconds>(end_time - start_time_);

      cout << "Simulation: '" << name() << "'";
      cout << (result ? "Succeeded" : "Failed");
      cout  << " in " << durationInMS << "ms."; 
    }

  private:
    system_clock::time_point start_time_ {};
};

这是一个特定的模拟类:

class TransmogrifySimulation : public Simulation {
  protected:
  // virtual methods should be overridden in derived classes
    virtual const string& name() const {
       static const string name_ = "TransmogrifySimulation";
       return name_;
    }

    virtual bool actuallySimulateStuff() {
       // simulate transmogrification here...
       ...
       // which implies, at some point:
       someSimulationDetail();
       ...

       return result_;
    }

    void someSimulationDetail() {
       // this is a really weird, unreliable simulation
       auto currentRunTime = duration_cast<milliseconds>(system_clock::now() - start_time());
       result_ = 0 != (currentRunTime % 2);
    }

   private:
     bool result_ = false;
};

这只是一个例子,但如果你想对它使用的概念感到满意,我强烈建议你从The Definitive C++ Book Guide and List中挑选一份初学者指南,并至少阅读与类和继承相关的章节

关于构造函数,它们几乎遵循相同的封装原则:类的构造函数负责初始化在类层次结构中定义的成员数据。你应该注意几点:

于 2014-03-11T03:07:41.487 回答