Hi guys, first post. I am doing a project for uni in which I have to build a vector database of shapes, computing their area and volume, and then printing said data.
I have an abstract base class called shape, then derived classes for each shape type, all of which work fine.
However, I want to make a template derived class called prism, which takes a 2D shape and a double "depth" as arguments, and then calculate the volume of the prism made of this 2D shape with the depth.
Abstract base class:
//***********************************ABSTRACT BASE CLASS***********************************
class shape
{
protected:
double area;//Give 3D shape classes this info to calculate volume, so all classes contain this info
std::string name;//Use to print out name of each shape. Note std:: is REQUIRED for it to work
double volume;//For 2D shape, this is zero.
virtual double compute_area()=0;
virtual double compute_volume()=0;
virtual std::string get_name()=0;
virtual double get_length(){return 0;}//these are just virtual functions as they are not contained in every derived class
virtual double get_breadth(){return 0;}//so give function definition here and make virtual (NOT pure virtual) so they can be overridden
virtual double get_radius(){return 0;}
virtual double get_height(){return 0;}
virtual double get_depth(){return 0;}
virtual double set_area(){return 0;}
};
My template:
template <class T> class prism : public shape
{
protected:
T* myshape;
double depth;
//double area;
//std::string name;
public:
// Constructors & destructor
prism(){depth=0;}//default constructor
prism(T* _myshape, double _depth, std::string _name){myshape=_myshape; depth=_depth; name=_name;}//parameterised constructor with new type T specified
~prism(){}//destructor
//default copy constructor
My problem is that I cannot access the data of the 2D shape in the prism class. My program compiles and runs fine, but it insists that the area, radius and name of my 2D shape created in the prism constructor is 0. If I put "double area;" as protected or public data in my prism class, it returns that my area is undefined, ie -6.2x10^66.
I am trying to access the area using:
case 'p':
cout<<shapestack[i]->get_radius();
cout<<"The volume of a prism made from a circle of area "<<shapestack[i]->compute_area()<<" and a depth of "<<shapestack[i]->get_depth()<<" is "<<shapestack[i]->compute_volume()<<endl;
cout<<""<<endl;
The get_radius() also returns 0.
How can I access the data in the circle created in the prism constructor?
Thanks in advance.
PS I know there is a lot of redundant code in this, I'm going to tidy it up once this template starts working!
PPS Sorry if this appears messy, I don't know of another way to display my code!
Why does prism have to be a template class? Why can it not just contain a shape*, getting rid of the template
parameter? The template parameter does not benefit you at all.
So any idea why I can't access the data of the shape it is taking as its argument?
Is the way I construct it correct? What commands should I use to get at the data? I'm not sure its the template that is the problem, it seems to be accessing the area of the constructed 2D shape in its argument is the problem as apparently the area of the 2D shape is 0, even if I construct it with an area
Note here, in new circle the 10,10,10 is radius,area,volume respectively so even when I DEFINE the area as 10 rather than calculating it from radius, the calculate_volume function for the prism insists that the area of the 2D shape is 0, and so calculates the volume as zero. Or, when I put "double area;" in the prism template protected data, it insists the area is undefined and so says the area is -6.2*10^66.
How do I allow the prism class to access the area of the 2D shape that is being made into the prism, so I can calculate and print its volume?
Well, you have two different double compute_area(); functions. Correct me if I am wrong, but it seems that
shapestack[i]->myshape->compute_area()
is the pointer to the function you would want to call. Your class prism is inheriting class shape, there is no way for it to see class circle. Since the only pointer to compute_area you can see in class shape equals 0, 0 is the only value you can get at shapestack[i]->compute_area.
On another note: conceptually, templates and virtual functions are very similar in the sense that they are different tools used to solve the same problem. The main difference is templates compile statically yielding 1) faster execution 2) no shared code (i.e. more RAM use) and 3) compile time type checking. On the other hand virtual functions are resolved on run time, yielding 1) smaller(=shared) code but 2) slower execution and 3) program crashes on execution, rather than having the compiler complain.
I am clearly a fan of templates instead of virtual functions. Except for the higher RAM use I believe templates are a more programmer-friendly method for doing the job.
Mixing the two approaches seems to be extremely unusual to me.
You are spot on, got a coursemate to have a look and said the same thing, in my compute_area and compute_volume functions in the prism template I had to use
{return area=myshape->compute_area();}
and
{return volume=myshape->compute_area()*depth;}
which works as desired. Thanks for the assistance!
On the template note, this is a university assignment and so I think it was asked for just to show I know how to implement a template, even if its entirely unnecessary.
What it actually asked for as a "generic class for prism" which took a 2D shape and a depth as arguments, so if I just did a normal derived class instead of a template that would do the same job, yes?
However, you can instead do the whole thing with templates (which is how I would do it). In this case, you *do not* declare any virtual functions: in fact, you do not declare a base class "shape" at all.
class circle
{
public:
double radius;
double get_area(){return radius*radius*3.1415;};
};
template <class T> class prism
{
public:
T* Base;
double height;
double get_volume(){return Base->get_area()*height;};
prism(){Base=new T;};
~prism(){delete Base;};
};
class car
{
public:
std::string model;
};
int main()
{ prism<circle> p1;
prism<car> p2; //works just fine! as long as you don't call
//p2.Base->get_area(), this code is completely legal!
p1.height=1;
p1.Base->radius=1;
std::cout<< p1.get_volume();//works perfect
std::cout<< p2.get_volume(); //this line is the only thing that will not compile
//since class car has no get_area() method.
return 0;
}
[Edit:] I find this solution far more elegant than the virtual function approach because:
1) You write MUCH less.
2) If you declare prism<something> and something has no get_area function, the compiler will complain and save you from debugging mysteriously crashing code
3) You can actually declare prism<circle>, prism<square> and have a method called get_number_of_edges(), which is well defined for rectangular prism, but is not defined for a cylinder. You can still do prism<circle>::get_volume(); To me, a cylinder is a prism without edges, (or if you wish, a prism is a cylinder with polygonal base-shape).
In this particular case, the analogy with natural language is much better (to me) for the template approach.
template <class T> class prism : public T
{
//whatever here
};
Just a note which is not entirely related. But the term for the code posted above is Bolt-in-templates (See the book Imperfect C++) . The nice thing about Bolt-in templates is that you can stack them and create new code in the body of the code rather then the definition: ie
Arms<Leg<Feet<> >> biped;
Its useful in some cases where you need to generate a lot of different sorts of classes with different combinations of things. Of course a lot of the time, component based architecture is better.