Reputation: 3099
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
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:
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.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
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
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
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
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