In C++, how does one access members of a derived class in base functions? (Is this even the correct practice?)

StackOverflow https://stackoverflow.com/questions/22315222

Question

I am writing a physics program in C++ to approximate the range of an object using several algorithms. I have declared a base class Simulation which contains both concrete and abstract methods. (e.g. The function to approximate the range is pure virtual while the function to obtain the next position is concrete, as this is the same regardless of the algorithm.) To define these concrete methods, I have declared all of the variables in the base class as well. In the derived classes, I define both the abstract methods of the base class and new, algorithm-specific ones; this requires that I define the members in both the base class and the derived classes.

The problem is that the concrete methods defined in the base class are accessing the members of the base class, not the overridden ones defined in the derived classes. Is there a way to do this, or is my approach itself incorrect?

I'm also having a similar problem with constructors; calling the base constructor from the constructor of a derived class is initializing the base members.

Anyways, thanks for your time. Though I'm not entirely new to C++, I've tended to use it more as C; I'm rather inexperienced with object-oriented concepts. I could be making a very elementary mistake, (or more likely design flaw) but I haven't found anything similar or had any positive results from testing other than redefining methods in the derived classes. (which I am assuming I should avoid doing)

Update: Though making the members protected worked, (thanks Ozraptor; not quite sure how I missed that) as requested by R Sahu, I'll post some of my (now updated) code:

The Base Class:

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

One of the Derived Classes:

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_;
};
Was it helpful?

Solution

If the need is to access members from the base class function implementations, "promote" those members to be (protected) base class members only - you do need to declare them in the derived classes as well. The base and derived class functions can access them then directly in both cases.

Re constructors - due to the order of constructor invocation, you can only intialise members of the actual class being constructed, or it's base class members. You cannot initialise derived class members, as they do not exist yet!

OTHER TIPS

It is kind of a broad question that require you to have a better understanding on inheritance. But here are few things that you must know first:

If you inherit a class(superclass) and superclass has some concrete methods, you do NOT need to implement them again in subclasses, unless of course you want to have a different implementation specific to that subclass. And this is called overriding that method.

In case you have an overridden method in your subclass, which class members and methods will be used is totally dependent on the declaration of the object that you called the method on:

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

The Base class methods that should be overridden in each specific algorithm (each derived class) should be declared virtual. This approach is sometimes called the Template method pattern.
As for member data, even though you can access a base class's protected data members in derived classes and it's often the fastest way to write code, the cleanest way to go about this is to not access member data from a base class directly, but have member functions that access or manipulate this data, and call those instead. This derives from the encapsulation OO principle. See: Is it good practice to make member variables protected?

To give you a slightly contrived (C++11) example, here's a base simulation class:

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

And here is a specific simulation class:

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

This is just an example, but if you want to feel comfortable with the concepts it uses, I strongly suggest you pickup a beginner guide from The Definitive C++ Book Guide and List and read at least the class- and inheritance-related chapters

Regarding constructors, they pretty much follow the same principle of encapsulation: a class's constructor is responsible for initializing the member data defined at that level of the class hierarchy. You should note a few things:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top