Johannes
Johannes

Reputation: 3099

Avoid overriding members when inheriting

I need to use C++. C++11 would be interesting, but I'd prefer without. I have the following class structure.

class Wheel { /*...*/ };
class MtbWheel : public Wheel { /*...*/ };

class Bike { Wheel front_wheel; };
class Mountainbike : public Bike { MtbWheel front_wheel; };

Now, this completely works: The Mountainbike overrides front_wheel, and thus can use an MtbWheel. Softwaretechnically, I am not happy, though.

EDIT: Is there a solution without virtual functions, but with templates instead?

Upvotes: 0

Views: 136

Answers (5)

codeling
codeling

Reputation: 11448

Your front_wheel member in Mountainbike class does not override front_wheel, it hides it. Mountainbike class actually has two front_wheel members, but the one from the Bike class is hidden by the one declared in Mountainbike. Meaning that if Bike accesses front_wheel, it will access an object of type Wheel, while when Mountainbike accesses front_wheel, it will access an object of type MtbWheel - but these two wheel objects don't know of each other!

Better OO design would be to make front_wheel e.g. a pointer in Bike (or even better a smart pointer), and initialize it in the constructor of Mountainbike to hold an object of a class derived from Wheel which fits best to Mountainbike. That way, when accessing front_wheel, one way or the other virtual functions come into play of course.

An alternative solution, as per Steve Jessop's suggestion in the comments below, making use of templates instead of using polymorphism, would be:

class Wheel { /*...*/ };
class MtbWheel : public Wheel { /*...*/ };

template <typename WheelType>
class Bike { WheelType front_wheel; };

class Mountainbike : public Bike<MtbWheel> { /* ... */ };

That way, no virtual functions are involved when operating on front_wheel. There are some points to consider with such a solution, however:

  • For each different WheelType you use Bike with, separate code will be created; if you have many different such types, this might result in code bloat.
  • If you have more than one class derived from Bike with different WheelType parameters, they do not have the same base class (see Steve Jessop's comment, and point 1), and therefore also can't be accessed polymorphically.
  • You can not enforce an explicit interface on the WheelType being passed as template parameter to Bike; only the implicit interface is defined by the methods and members used from Bike. That should be no problem however, since the compiler can still verify that implicit interface.

Upvotes: 4

Pete Becker
Pete Becker

Reputation: 76523

The solution is to not do that. Writing a derived class requires careful study of the base class, followed by careful design. Magic cookies are no substitute for knowledge and thought. And, yes, I've made my share of this kind of mistake, and kicked myself for being careless.

Upvotes: 0

Juraj Blaho
Juraj Blaho

Reputation: 13471

The variable front_wheel is not overriden - it is just hidden. The member variable Wheel front_wheel is still in the Mountainbike class. So actually there are two variables named front_wheel in Mountainbike. But to access the hidden variable in Mountainbike you would need to say it explicitly: Bike::front_wheel.

A better way of doing what you want is to create an interface class with no data:

class Bike {
public:
  virtual Wheel const &getFronWheel() const = 0;
  virtual ~Bike() {}
};

and then derive any specific bike from that:

class RegularBike: public Bike {
public:
  virtual Wheel const &getFronWheel() const { return wheel; }
private:
  Wheel wheel;
}

class MtbBike: public Bike {
public:
  virtual MtbWheel const &getFronWheel() const { return wheel; }
private:
  MtbWheel wheel;
}

Edit: Without using virtuality, but templates instead:

template<typename WheelType>
class Bike {
public:
  /* Common methods for any bike...*/
protected: // or private
  WheelType wheel;
};

Then you can extend the Bike as you wish:

class RegularBike: public Bike<Wheel> {
   /* Special methods for regular bike...*/
};

class MtbBike: public Bike<MtbWheel> {
   /* Special methods for Mtb bike...*/
};

Upvotes: 1

alok_dida
alok_dida

Reputation: 1733

You can create a interface of IWheel instead of Class Wheel.

Create OverRide method in MtbWheel class. So that whoever wants to override this method can override this method else use the default method which we have implemented in MtbWheel class. By using this you can add properties of it.

Upvotes: 0

Puppy
Puppy

Reputation: 147054

You're not overriding anything. There is still a Wheel front_wheel; in your derived class. You can access it through m.Bike::front_wheel, or something like that. If you want derived classes to provide their own instantiations of front_wheel, then you will need to only provide a virtual accessor in the base class and let derived classes create their own.

Upvotes: 1

Related Questions