Template inheritance

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.

public:
shape(){volume=area=0; name="No shape";}//default constructor
shape(std::string _name, double _area, double _volume){volume=_volume; area=_area; name=_name;}//parameterised constructor with relevent shape data
~shape(){}//destructor
//default copy constructor

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

//prototypes
double compute_area();
double compute_volume();
std::string get_name();
//double get_radius(){return 0;}
//double get_length();
//double get_height(){return 0;}
//double get_breadth();
double get_depth();
//double set_area();
//double get_radius();

};


}//close brace of namespace definition

using namespace my_shape_namespace;
//const double pi=3.14159265;

template <class T> std::string prism<T>::get_name()
{return name;}

//template <class T> prism<S>::get_length()
//{return length;}

//template <class T> prism<S>::get_breadth()
//{return breadth;}

template <class T> double prism<T>::compute_area()
{return area;}

template <class T> double prism<T>::compute_volume()
{return volume=area*depth;}

template <class T> double prism<T>::get_depth()
{return depth;}

//template <class T> double prism<T>::get_radius()
//{return radius;}

#endif

My circle derived class:

class circle : public shape
{
protected:

double radius;
double volume;
std::string name;

public:
double area;
circle(){name="circle default"; radius=area=volume=0;}
circle(std::string _name, double _radius, double _area, double _volume){radius=_radius; area=_area; volume=_volume; name=_name;}
~circle(){};//destructor
//default copy constructor

//prototypes
double get_radius();
double compute_area();
double compute_volume();
std::string get_name();
//double get_length(){return 0;}
//double get_height(){return 0;}
//double get_breadth(){return 0;}
//double get_depth(){return 0;}
};


And finally, I create the prism object as follows, using vector library:

shapestack.push_back(new prism<shape>(new circle("circle2",10,0,0),100,"pcircle"));


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.
Thats what I thought! In the specification, it requires a "generic prism class that takes a 2D shape and a depth as its arguments".

How can I fulfil that?
You already are -- by having the constructor take a pointer to a shape as the first parameter instead of, for example,
a circle.
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
Wouldn't the accessor functions for prism have to call down to members of the
contained shape to get some data? I don't see that in your above code.
Sorry, I'm a bit of a rookie to all this, its an intro course.

How would I go about doing that? Somehow get prism to call the circle compute_area function? How would I do that?

Also, if I was constructing, for example, a rectangle instead of a circle, how would that differ?
Umm, I was left with the impression that you want to make a prism out of whichever 2D shape, which would correspond to something like this:
1
2
3
4
template <class T> class prism : public T
{
//whatever here
};


Note that it compiles perfect as it is.

You easily instantiate:
 
prism<circle> P; //works just fine, although technically speaking, it should be called a cylinder :) 

Last edited on
Thanks for the replies guys.

I can create the prism fine, the problem I am having is retrieving the area of the 2D shape the prism is made from. I create it thus:

shapestack.push_back(new prism<circle>(new circle("circle2",10,10,0),100,"pcircle"));

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.
Last edited on
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?
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.

Imagine you had:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.
Last edited on
Thanks for the help. A shape base class with virtual functions, with rectangle, square, triangle etc derived classes was specified so I have to do it.

I have now implemented all the code, and am getting the dreaded C1075 error. Cue ages looking through code for 1 damn bracket >.<
1
2
3
4
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.
Last edited on
Topic archived. No new replies allowed.