Reputation: 822
I have a problem about the usage of the interface classes in C++ but don't know its name and how to search about it. Hope you can help me, kindly.
I will simply try to tell about my problem with a simple example.
I have 5 different possible objects, like Triangle, Square, Rectangle, Pentagon and Hexagon.
All these objects have common attributes, so I will have an interface class Shape.
Now, what I want is: I will have a Shape class object and want to be able to use it as one of the other 5 objects due to the selection during runtime.
So I did something like below:
class Shape
{
public:
virtual int getArea()=0;
virtual void setWidth(int w)
{
width = w;
}
virtual void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
class Triangle: public Shape
{
public:
int getArea()
{
return (m_Width * m_Height)/2;
}
};
class Rectangle: public Shape
{
public:
int getArea()
{
return (m_Width * m_Height);
}
};
When using, I want to create only an object of Shape and initialize it with one of the derived classes. So, from that time on, I want it to behave like an instance of that object like this:
void main(){
Shape* shape;
std::string shapeType;
std::cout<<"Enter Shape Type: triangle or rectangle."<<std::endl;
std::cin>>shapeType;
if (shapeType == "triangle")
shape = new Triangle();
else if (shapeType == "rectangle")
shape = new Rectangle();
shape->setWidth(5);
shape->setHeight(7);
std::cout<<shape->getArea()<<std::endl;
}
No problem up to here. The problem starts here. These derived classes may have different attributes, methods. When I add these methods vs to their own classes, shape object can not access it (rightly). Another way can be used is to casting a new derived object to shape object like:
Triangle* triangle = (Triangle*)shape;
// now I can access own attributes of Triangle object.
But this is not a good way to handle it as you may think so. Except this, I only know one way that forces me to write all these attributes to the Shape class and implement them in needed derived class and if not needed with other classes, implement it as empty.
Do you have any good solution to this problem? I'm sure there would be but I am not that much experienced about this subjects so wishing you have a solution that fits what I want.
Thanks in advance.
Upvotes: 0
Views: 214
Reputation: 1741
First of all your Shape
isn't interface, but base implementation class. Separate them.
Interface is an abstract class with only pure virtual methods and virtual destructor:
struct IShape
{
virtual int getArea() =0;
virtual void setWidth(int w) =0;
virtual void setHeight(int h) =0;
// without virtual destructor you cannot
// properly destroy object through IShape pointer
virtual ~IShape() {}
};
Now lets write base implementation:
class CShapeBaseImpl : public IShape
{
public:
void setWidth(int w) override { width_ = w; }
void setHeight(int h) override { height_ = h; }
protected:
int width_ = 0;
int height_ = 0;
};
In most cases you want to create some concrete object and work with it only through it's interface. Usually it's done through abstract factory pattern:
std::unique_ptr<IShape> CreateTriangle( int a, int b, int c )
{
auto triangle = std::make_unique<Triangle>(a,b,c);
// there you can work with it as Triangle
return triangle;
}
After CreateTriangle
returns it's better to forget that it is Triangle
. It's IShape
only now.
Upvotes: 2
Reputation: 20730
Do all animals bark?
Consider a hierarchy like
-> animal
-> dog
-> great_dane
-> chiwawa
-> fish
-> tuna
-> shark
There are people who think that if you have a collection of animal*
-s and you want to ask them to bark, animal
must declare an abstract virtual void bark()=0
to be implemented appropriately in great_dane
and chiwawa
(the fist will cout << "BARK"
, the second cout << "bark"
), while fish
will just implement it as a "do nothing".
If you want to know if something barks, just ask and listen. No sound? It doesn't.
There are also people (myself included) who think that such a way to go will lead to animal
to declare all the verses of the zoo (including the ones of not yet existing species) thus making the animal class a sort of "god object" (the ultimate knowledge-owner of the entire universe) that's not the case.
For these people, bark()
belongs to dog
, and asking an animal to bark makes essentially no sense, unless you check before that it actually is a dog.
And after that, it will bark as appropriate to its race.
To check if an animal is a dog, given animal* myanimal
, you just convert to dog* mydog = dynamic_cast<dog*>(myanimal)
mydog
null ? Sorry: that's not a dog: don't treat it as such. Bad things could happen. fish
-esIf you feel that cast to be too ugly and frequent, you can implement a helper function in animal
:
template<class T>
T* animal::as_a() { return dynamic_cast<T*>(this); }
so that you can have
auto mydog = myanimal->as_a<dog>();
if(mydog) mydog->bark();
Now, change animals into shapes, dogs to rectangles and fishes to triangles!
Upvotes: 1
Reputation: 988
I believe that you need to use a pointer to your base class to store the Object. So you have Shape*
which you use to store a new Triangle()
, and you should be able to access the requisite functions and variables. This is based off remembering how to do it in a vector, that's detailed better here, but I can't see why it wouldn't transfer.
Shape* shape;
shape = new Triangle();
//shape->foo accesses Triangle methods
It's either that, or use a dynamic/static cast to get it to the correct type at runtime, although that means that you'll need to check what type it is in order to do the correct cast (So possibly have an identifying string or something in the base class that's set by each subclass)
Upvotes: 0
Reputation: 4021
If the attribute is related to the derived ONLY, but has nothing to do with the base, it has no sense to implement it in the base. E.g. attribute isEquilateral()
should be implemented in the Triangle class but not in the Shape base class.
So what you can do is to use a Type()
attribute that is reimplemented in each derived class giving the derived object type (triangle, rectangle, etc...)
Then a static casting:
Triangle *triangle = static_cast<Triangle*>(shape);
So something like this:
if (shape->Type() == Shape::Triangle){
Triangle *triangle = static_cast<Triangle*>(shape);
bool eq = triangle->isEquilateral();
...
}
Or simply:
Triangle *triangle = static_cast<Triangle*>(shape);
if (triangle){
bool eq = triangle->isEquilateral();
...
}
But I would prefer the first approach.
Upvotes: 0
Reputation: 13278
If your type is a Shape
that means you intend to use it as a Shape
. If you intend to use one of the specific child classes then use that specific type.
If you still really want to have a stored generic type and still want to use some specific operation, then you will need to downcast your pointer to a child class pointer with a static_cast
, but you will either need to know which type is stored there or use a dynamic_cast
, in both cases this smells like bad design.
You should be able to do all your polymorphic operations with virtual functions or depending on the situation and your needs you can use other design patterns like double dispatch.
Upvotes: 1
Reputation: 328
This can absolutly not work the way you explained it. Its not the sense of a heritage. You should read about heraditing. Basicly its following rule: if you create a subordinate u can use the funcs and attributes of the father, if they are public or protected
Upvotes: -3