Originally Posted by
ASURugger
I thought I was using polymorphism, when I just use the same instance of the type string for all my subclasses. Is that wrong?
Not necessarily wrong, but such type checking tends to break the Open-Closed principle: software entities (classes and functions, in C++'s context) should be open for extension but closed for modification. If you add a new derived class, the code in main() must change (and the code in Shape::getType() as well). Sure, you say that "Square and Circle are the only 2 dimensional shapes created", but that kind of assumption can be rather fragile.
Basically, the problem is that if the object is 2D shape, you want to get the area, but if the object is a 3D shape, you want to get the volume. One way I can think of is to have a virtual function named getQuantity() that returns the quantitative measure of the shape, be it area, volume, or some other measure devised for shapes of higher dimensions:
Code:
class Shape
{
public:
virtual ~Shape() {}
virtual double getQuantity() const = 0;
friend std::ostream& operator<<(std::ostream& out, const Shape& shape)
{
shape.print(out);
return out;
}
private:
virtual void print(std::ostream& out) const = 0;
};
Notice that I made Shape into an abstract base class. There is no way of computing the quantitative measure of a Shape, hence getQuantity() is pure virtual. The operator<< will be used to print the shape. Notice that it calls print(), which is pure virtual. This allows derived classes to determine what to print.
Oh, and the destructor is virtual - base class destructors should be virtual in order to avoid undefined behaviour if you should delete a derived class object through a base class pointer.
Now, a TwoDimensionalShape is-a Shape, but it is also an abstract base class:
Code:
class TwoDimensionalShape : public Shape
{
public:
virtual double getArea() const = 0;
virtual double getQuantity() const
{
return getArea();
}
};
Likewise, define ThreeDimensionalShape.
Great, now we can begin to implement the concrete classes:
Code:
class Square : public TwoDimensionalShape
{
public:
Square(double length) : sideLength(length) {}
virtual double getArea() const
{
return sideLength * sideLength;
}
private:
double sideLength;
virtual void print(std::ostream& out) const
{
out << "Type=Square\t area=" << getArea();
}
};
Right, so you have implemented Square, Circle and Cube. Defining main() is easy:
Code:
int main()
{
using namespace std;
Square square1(2.1);
Circle circle(3.0);
Cube cube(2.0);
Square square2(6.0);
Shape* shapes[] = { &square1, &circle, &cube, &square2 };
const size_t num_shapes = sizeof(shapes) / sizeof(shapes[0]);
for (size_t i = 0 ; i < num_shapes; ++i)
{
cout << *shapes[i] << "\tquantity=" << shapes[i]->getQuantity() << endl;
}
return 0;
}
I have chosen to demonstrate the polymorphic nature of getQuantity() in the above code, though it is not actually part of your desired output. Notice that if you add say, a Rectangle class, the only code that needs to be added is the code for the Rectange class. This is the Open-Closed principle at work.