I have implemented different classes derived from an abstract class and each one has different methods. The problem is that I have to declare the object only at runtime, so I have to create a pointer to the base class and I can't use the methods of each derived class.
I have created an example to explain better what I mean:
#include <iostream>
using namespace std;
class poligon
{
public:
double h, l;
void setPoligon(double h, double l) {
this->h = h;
this->l = l;
}
virtual double GetArea() = 0;
virtual void GetType() = 0;
};
class triangle : public poligon
{
double GetArea() { return l*h / 2; }
void GetType() { cout << "triangle" << endl; }
double GetDiag() { return sqrt(l*l + h*h); }
};
class rectangle : public poligon
{
double GetArea() { return l*h; }
void GetType() { cout << "rectangle" << endl; }
};
void main()
{
poligon* X;
int input;
cout << "1 for triangle and 2 for rectangle: ";
cin >> input;
if (input == 1)
{
X = new triangle;
}
else if (input == 2)
{
X = new rectangle;
}
else
{
cout << "Error";
}
X->h = 5;
X->l = 6;
X->GetType();
cout << "Area = " << X->GetArea() << endl;
if (input == 2)
{
cout << "Diangonal = " << X->GetDiag() << endl; // NOT POSSIBLE BECAUSE " GetDiag()" IS NOT A METHOD OF "poligon" CLASS !!!
}
}
Obviously the method X->GetDiag()
at the end of the main can't be used because it is not a method of the "poligon" class. Which is the correct implementation of a program with this logic?
Introduce a method in the base class
virtual bool boHasDiagonal(void) =0;
Declare unconditionally in base class:
virtual double GetDiag();
Implement it differently in both derived classes:
virtual bool boHasDiagonal(void) {return true;} // rectangle
virtual bool boHasDiagonal(void) {return false;} // triangle
Change output line:
if (X->boHasDiagonal())
{cout << "Diangonal = " << X->GetDiag() << endl;}
For a nice touch of paranoia (a healthy state of mind for a programmer in my opinion), use concept by Gluttton of a default implementation of GetDiag()
, which signals an error (as in his answer here) .
For the case of many poligons, I like the proposal by Rakete1111 in the comment.
Define method in the base class which define implementation throws exception:
class poligon
{
public:
virtual double GetDiag()
{
throw std::logic_error ("Called function with inappropriate default implementation.");
}
};
In class that has meaningful implementation override it:
class rectangle : public poligon
{
double GetDiag() override
{
return diagonale;
}
};
Usage:
int main () {
try {
X->GetDiag();
}
catch (...) {
std::cout << "Looks like polygon doesn't have diagonal." << std::endl;
}
}
You can use dynamic_cast
.
dynamic_cast<triangle*>(X)->GetDiag();
Note that you already have a bug: You only create a triangle
if input == 1
, but you get the diagonal if input == 2
. Also, the above is not really safe, because dynamic_cast
can return nullptr
if the conversion is invalid.
But it would be better to check whether dynamic_cast
succeeds, then you could also drop the input == 2
check:
if (triangle* tri = dynamic_cast<triangle*>(X))
std::cout << "Diagonal = " << tri->GetDiag() << '\n';
You use dynamic_cast
to access subclass-methods. It returns nullptr
if it is not derived from the class. This is called down cast , as you are going down the class-tree:
triangle* R = dynamic_cast<triangle*>(X);
if(R) {
cout << "Diagonale = " << R->GetDiag() << '\n';
};
Edit: You can put the declaration in the first line into the if-condition, which goes out of scope outside the if-statement:
if(triangle* R = dynamic_cast<triangle*>(X)) {
cout << "Diagonale = " << R->GetDiag() << '\n';
};
if(rectangle* R = ...) {...}; // reuse of identifier
If you want to allow, that multiple subclasses
have the GetDiag
function you can inherit from the poligon
-class and another diagonal
-class. The diagonal
-class only defines the GetDiag
function and has not really to do with the polygon
-class:
class polygon {
// stays the same
};
class diagonal {
virtual double GetDiag() = 0;
};
class triangle : public polygon, public diagonal {
// body stays the same
};
And like above, you access the methods via casting with dynamic_cast
but this time you cast to type diagonal
. This time it is side cast , because poligon
has nothing to do with diagonal
, so you are going sideways in the tree.
polygon diagonal
| | |
| |_____________|
| |
| |
rectangle triangle
Use dynamic casting to check if the base class' pointer is actually a triangle, like this:
int main()
{
...
if(triangle* t = dynamic_cast<triangle*>(X))
std::cout << "Triangle's diagonal = " << t->GetDiag() << std::endl;
return 0;
}
PS: I assume that your example is just a draft, since it has some bugs.
As others have said, you can use dynamic_cast
to change the static type in your program, add a method to the base-class with a pseudo implementation or use some form of type-switching. However, I would consider all these answers as signs of a design flaw in your program and would reject the code. They all encode assumptions about the types existing in your program into the code and pose a maintenance burden. Imagine adding new types of shapes to your program. You then have to search and modify all the places you dynamic_cast
your objects.
I think your example hierarchy is wrong in the first place. When you declare a base-class for ploygons, and derive triangles from it, the whole purpose of polymorphism is to be able to treat similar objects identically. So anything that is not common behavior ( not implementation ) is put in the base-class.
class poligon
{
public:
double h, l;
void setPoligon(double h, double l) {
this->h = h;
this->l = l;
}
virtual double GetArea() = 0;
virtual void GetType() = 0;
};
class triangle : public poligon
{
double GetArea() { return l*h / 2; }
void GetType() { cout << "triangle" << endl; }
double GetDiag() { return sqrt(l*l + h*h); }
};
You explicitly say that I can replace any instance of polygon with an instance of triangle everywhere in your program. This is the the Liskov substitution principle . What about circles? They don't have height and length. Can you use a rectangle everywhere you expect a polygon? Currently you can, but polygons can have more edges, be self-intersecting etc. I cannot add a new edge to a rectangle, otherwise it would be a rectangle anymore.
There are some solutions, but as it is a design question, the solution depends on what you want to do with the objects.
A downcast is usually a sign of a bad design and is rarely needed in practice.
I can't see why it is needed in this particular case. You have discarded the information about which type you have for no reason. An alternative could be:
void printDiagonal(const triangle& tri)
{
std::cout << "Diangonal = " << tri.GetDiag() << std::endl;
}
void process(poligon& p)
{
p.h = 5;
p.l = 6;
p.GetType();
std::cout << "Area = " << p.GetArea() << std::endl;
}
int main()
{
int input;
std::cout << "1 for triangle and 2 for rectangle: ";
std::cin >> input;
if (input == 1)
{
triangle tri;
process(tri);
printDiagonal(tri);
}
else if (input == 2)
{
rectangle rect;
process(rect);
}
else
{
std::cout << "Error\n";
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.