I have a class "Car" and a subclass "LuxuryCar" derived from "Car". I also have a class "Radio" and a subclass "SatelliteRadio" derived from "Radio".
I would like the Car to maintain a reference to a Radio. And I would like a LuxuryCar to maintain a reference to a SatelliteRadio.
When instantiating a LuxuryCar object, it creates a reference to a SatelliteRadio. SatelliteRadio has available all the methods of Radio and a method unique to SatelliteRadio. But since LuxuryCar is a subclass of Car, it by default has a reference to a Radio (inherited from the Car class) that I don't really need. And the constructor of the base class Car instantiates a Radio I don't need (I only want a SatelliteRadio). I do need the methods of Car.
I could instantiate the Radio and SatelliteRadios outside of the Car and LuxuryCar classes. But I would still have a reference to Radio in LuxuryCar that I don't really want.
My gut tells me this is not correct and there is a better way to design this. I would like to maintian the inheritence relationship between the cars and the radios. And I want to be able to instantiate both a Car and LuxuryCar if I want.
Any help or thoughts would be appreciated.
Thanks,
Adam
class Car
{
public:
Car();
virtual ~Car();
virtual Start();
provate:
Radio* theRadio;
};
You can take Radio* as parameter for Car's C'tor. while contrcting the Car object pass the Radio object & while constructing LuxuryCar pass SateliteRadio object.
You can refer the method of SateliteRadio by dynamic casting the Radio to SateliteRadio inside the LuxuryCar.
I think you're missing the correct "IS-A" relationship here. Although it seems obvious that a LuxuryCar IS-A Car, the "Car" that it is is an abstraction, not a "real" car. That is, a CheapCar is ALSO a car, but a LuxuryCar IS NOT a CheapCar.
This suggests that the root of your difficulty is that Radio should not be a member of Car at all (after all, how would define CarWithNoRadio, then?)
I would say that you need something like
Code:
class Car { }; // this is an abstract base class - no member data
class OrdinaryCar : public Car
{
public:
OrdinaryCar(Radio* r);
// ...
private:
Radio* radio;
};
class LuxuryCar : public Car
{
public:
LuxuryCar(SatelliteRadio* r);
// ...
private:
SatelliteRadio* radio;
};
Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
-- Sutter and Alexandrescu, C++ Coding Standards
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson and Gerald Jay Sussman
The cheapest, fastest and most reliable components of a computer system are those that aren't there. -- Gordon Bell
class Radio
{
};
class MP3Radio: public Radio
{
};
class MP4Radio: public Radio
{
};
class Car
{
public:
Car(void): mRadio(0)
{
}
Car(const Radio& pRadio): mRadio(&pRadio)
{
}
protected:
Radio* mRadio;
};
class OrdinaryCar : public Car
{
public:
OrdinaryCar(const Radio& pRadio): Car(pRadio)
{
}
};
class LuxeryCar : public Car
{
public:
LuxeryCar(const Radio& pRadio): Car(pRadio)
{
}
};
...
LuxeryCar myCar(*(new MP4Radio));
This suggests that the root of your difficulty is that Radio should not be a member of Car at all (after all, how would define CarWithNoRadio, then?)
your right if we want car with no radio.
But i am not agree on storing radio object in each derived car.
What if we want to derive some car from luxury car & want some diff. radio in that it will create same probelm as first post
if we want car with radio & no radio. I think it shoud go with one more abstrract level 1)Car with radio 2)Car without radio.
Car with radio abstract class will store the Radio reference
Code:
class Car { }; // this is an abstract base class - no member data
class CarwithRadio: public Car
{
public:
Car(Radio*);
protected:
Radio*
}
class CarwithoutRadio :
{
}
class LuxuryCar : public CarwithRadio
{
public:
LuxuryCar(Radio*);
}
Yep its simple...
But may be it will became very costly as application grows
By making it simple we are forgetting some basic rules of OOP
"Don’t put members in class which are not related to it"
First by putting Radio in car & providing function to determine whether it has radio or not may lead to some big cost in future.
With this design all the cars objects is going to have extra pointer(4 bytes) regardless of whether car has radio or not. i am fine with extra 4 bytes. But there is no guaranty the overhead is going to be only 1 pointer. In future somebody might want to put some supporting information in the Car for Radio & more. So the car with no radio object has going to lot of unncessary data & methods.
Classes should be derived if the behaviour of the intended derived class changes or is different than that of the base class. If there is only a change in state of the base class then one shouldn't derive from the base class to incorporate that change.
I think having a radio or not inside Car is more of a behaviourial change than a change of state so abstraction is welcome instead of implementing a bool function to check the state of having a radio or not.
For this kind of thing, I like the idea of "traits". Simply put, a class can implement a number of different traits - chunks of behaviour that are reasonably self enclosed.
I've never implemented traits in C++, so this is just a thought experiment until I give it a real try sometime. The code example is unlikely to compile, and might even have even some fatal flaws with it... but it should demonstrate the idea and how I might try and implement them.
The complication with all this, is that it uses some RTTI, and you need to take care with you class heirarchy so that there is only one unique subclass of each trait (otherwise, dynamic_cast<> will return 0 since it can't know which instance to use!). All traits MUST be virtually inherited to make this true.
(Warning: untested, uncompiled code... so it's probably full of bugs but should demonstrate the idea.)
Let's have a radio class, and a better radio class
Code:
class Radio { /* whatever */ };
class BetterRadio : public Radio { /* whatever */ };
A base class for something which can have traits...
Code:
class Traitable {};
And a trait which means "this thing can have a radio", making sure that if somethings traitable it's only traitable once (virtual inheritence!)
And now have a better radio, and a trait for being able to have a better radio...
Code:
class BetterRadio : public Radio { /* whatever */ };
class HasBetterRadioTrait : public virtual HasRadioTrait
{
public:
static bool HasTrait(Traitable* t) { return AsTrait(t)!=0; }
static HasBetterRadioTrait* AsTrait(Traitable* t) { return dynamic_cast<HasBetterRadioTrait*>(t); }
virtual BetterRadio& GetBetterRadio()=0;
};
So, let's have a car which can have a radio, and a luxury car which can have better radio...
Code:
class Car : public virtual HasRadioTrait { /* whatever */ };
class LuxuryCar : public Car, public virtual HasBetterRadioTrait { /* whatever */ };
If I've done this right, we should be able to do this:
Code:
void DoSomethingWithARadioIfWeHaveOne(Traitable* c)
{
// play with the radio
if (HasRadioTrait::HasTrait(c)) HasRadioTrait::AsTrait(c)->GetRadio().DoSomething();
// hey... what if it's got a better radio?
if (HasBetterRadioTrait::HasTrait(c)) HasBetterRadioTrait::AsTrait(c)->GetBetterRadio().DoSomethingFancy();
};
Why would you want to bother? Well, I can now have ANYTHING I like with a radio... any kind of car, bike, bus, wibble, thingy or whotsit.
Hell, you could even split radio into traits and have your radio working functions use the basics and then check for the "graphic equaliser" trait to open up some new functionallity.
I'm not happy about a few things, like you can't implement a trait more than once and have this work... and there's no way of enforcing that traits are always inherited virtually.
Last edited by Noddon; June 2nd, 2006 at 10:29 AM.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.