I am writing a project to calculate the area and volume of 2d and 3d shapes. I have a class hierarchy with my abstract base class called 'shape'. I want to make a separate class to calculate a prism of a specified depth from any 2d shape, yet am unsure how to do this.

Any help will be greatly appreciated.

This example is for a circle, yet I want to be able to do this for all 2d shapes.

(Relevant code)

header

template <class prism> class prism
{
	private:
	double depth;

	public:
	prism(){depth=0;}//default constructor
	prism(double depth1, shape* sha): shape(area){depth=depth 2; area=sha.getarea();}

};

then in main

if (choice=='c')
	{
	double depth(0.);
	a.push_back(new circle());
	vector<shape*>::const_iterator it;
	it = a.begin();
    (*it)->info();
	
	cout<<"Enter the depth for a prism"<<endl;
	cin>>depth;

	circle c(1);
	prism <shape*> b(depth, c);


	
        delete *it;
	a.pop_back();
	}

Recommended Answers

All 21 Replies

Hmmmm..... I have not done this before, so this will be a learning experience for both of us.

I know you'll want to create a "shape" class, as you have done. I also know you will then want to create shape-specific classes that inherit from "shape" for each shape you want to calculate the area of. Also, as you seem to have done.

class base {
};

class derive1_1 : public base {
};

class derive1_2 : public base {
};

This is where I might get lost:
Once you do that, you'll want to create a template class called "prism" that accepts any "shape" or "shape-derived" class.

From a strictly logical standpoint, I think you would want something like this:

template <class T> class derive2 : public T {
};

I don't know if this is legal though.

Assuming it is, when you create your prism you would use something like this:

derive2<derive1_1> aGenericObject;

Ultimately this would translate into something like:

class shape {
};

class circle : public shape {
};

class square : public shape {
};

template <class T> class prism : public T {
};

prism<circle> cylinder;
prism<square> rectangularPrism;

Thanks for the help, I am able to access the prism class now using the method you suggested. However I've come across one other problem I didn't realise I had. Again using my derived circle class to illustrate, I want to save the area calculated into a vector so it can be accessed by other functions, and ultimately by the prism class.

class circle : public twodshape
{
private:
	int num_vertices;
	vector <double> x;


public:

	circle(){num_vertices=0;}//default constructor
	circle(int numv)  {num_vertices=numv;

	x.push_back(2);
	cout<<x.size()<<endl;

	cout<<"enter radius"<<endl;
	cin>>x[0];
	area=PI*x[0]*x[0];
	x[1]=area;
    	
	}//paramatised constructor

	~circle(){}



	double get_numv() const {return num_vertices;}
	double setvalues(int a);
	double get_area() const{return x[1];}

	void info(){
	cout<<"The area of this this circle is "<<x[1]<<endl;
	};

};

(N.B. my variable Num_Vertices is left over from my square class, I realise circles do not have vertices.)

x.push_back(2);

Based on your intent, this is not correct. When you do a push_back(), you push the value of the argument into the vector as the last element. The surrounding context in your code makes it look like you are trying to use this to set the size of the vector.

Instead, try creating a vector<double> in main(). You would then iterate through your objects and push the result of the area() method for each one into the vector.

vector<double> myDoubleVector;

for(int i = 0; i < count; i++) {
  myDoubleVector.push_back(anotherFunctionWithDoubleReturnValue());
}

I like the idea of storing areas in a vector in main, however I think something may be wrong with my access function for area, or the way I am implementing it.

The output I get from this will output the correct area the first time when I calculate it in my constructor, however, in main and in the header void function the area is returned as 0 both times. Is there a way of storing it's value once it has been initially calculated in the constructor?

Thanks

class circle : public twodshape
{
private:
	int num_vertices;
	int count;
	vector <double> x;

public:

	circle(){num_vertices=count=0;}//default constructor
	circle(int numv, int cnt)  {num_vertices=numv; count=cnt;

	//cout<<"entered constructor "<<count<<endl;
	x.resize(2);
	cout<<"enter radius"<<endl;
	cin>>x[0];
	area=PI*x[0]*x[0];
	x[1]=area;

	cout<<x[1]<<endl;
    	
	}//paramatised constructor

	~circle(){}

	double get_numv() const {return num_vertices;}
	double setvalues(int a);
	double get_area() const{return area;}

	void info(){
	cout<<"entered info"<<endl;
	cout<<"The area of this this circle is "<<get_area()<<endl;
	};

};

and then in main

if (choice=='c')
	{
	double depth(0.);
	a.push_back(new circle());
	//cout<<a[0]<<endl;
	circle(1, 1);
	for (vector<shape*>::const_iterator it = a.begin(); it != a.end(); ++it) {
        (*it)->info();


		vector<double> mydouble;
		mydouble.push_back((*it)->get_area());

		cout<<mydouble[0]<<endl;

		double z = (*it)->get_area();
		cout<<"area 2 "<<z<<endl;

		
        delete *it;
    }

You don't want to have the vector ( mydouble ) declared inside the for-loop, because that gives you a fresh re-constructed vector on every iteration of the loop. So declare the vector in another scope, outside the loop.

Thanks for the help so far, I've moved the vector(my double) out to above the for-loop. The output I get when running the program and selecting circle is as follows:

"
//in constructor
enter radius
3
28.2743
//in info
entered info
the area of this circle is 0
//In main
0
area 2 0
Enter the depth for a prism

etc...."

I'm still unsure how to get my double get_area() to sucesfully return the area. I realise I could re-calculate the area each time by having a 'cin' entering the necessary values, however this is a bit of a clunky way of doing it.

Sorry to bump, but does anyone have any suggestions?

You might post the current code and point out the current problems.

Your shape-derived classes should store shape-specific information within their protected: sections. For example, circle should store a double called radius, square should store doubles called length and width. Then your get_area() methods would use a shape-specific equation to calculate the area. For example circle::get_area() would return a double that is the result of PI*radius^2 and square::get_area() would return a double that is the result of length * width. You would then store the return values into a vector<double>.

You would then do something similar with the prism template. The prism<T> class would have a private/protected double data member called depth and a public member called get_volume(). When called, the prism<T>::get_volume() method would subsequently call the T::get_area() method, then multiply that return value by the depth member.

To store the return values in the vector, you would declare the vector outside your loop, then push the return value(s) into the vector inside the loop.

Convert this into a loop and you should be on the right track:

prism<circle> cylinder;
vector<double> volumes;

volumes.push_back(cylinder.get_volume());

You would have a similar sequence for areas, except you wouldn't use prism<T>, you would use a specific shape:

circle aCircObj;
vector<double> areas;

areas.push_back(aCircObj.get_area());

Thanks again for the help so far, my (hopefully) last question (again using circle as an example), when i call: prism<circle> cylinder;, how will it know to use the area previously inputted in a.push_back(new circle(radius));

My code:

Main

if (choice=='c')
	{

	double radius(0.);
	cout<<"Please enter the radius for a circle"<<endl;
	cin>>radius;
	a.push_back(new circle(radius));

	
	for (vector<shape*>::const_iterator it = a.begin(); it != a.end(); ++it) {

		shapeareas.push_back((*it)->get_area());
		//(*it)->info();
		cout<<shapeareas[0]<<endl;

		double depth(0.);
		cout<<"Enter the depth of the prism"<<endl;
		cin>>depth;
		prism <circle> cylinder(depth);
		shapevolumes.push_back(cylinder.get_volume());
		cout<<shapevolumes[0]<<endl;
		delete *it;
	}

header

class circle : public twodshape
{
private:
	double radius;

public:

	circle(){radius;}//default constructor
	circle(double r)  {radius=r;//paramatised constructor
	
	area =PI*radius*radius;
	
	}

	~circle(){}
	
	double get_area()
	{
	
		return area;
	}

	void info(){
	cout<<"entered info"<<endl;
	cout<<"The area of this this circle is "<<get_area()<<endl;
	};
//
//
//
//
//
//
template <class p> class prism : public p
{
	private:
	double depth;


	public:
		prism(){depth=0;}//default constructor
	prism(double depth1) {depth=depth1;
	cout<<"entered prism"<<endl;
	}

    double get_volume(){
	//cout<<"Enter the depth"<<endl;
	//cin>>depth;
	area=get_area();
	double volume=area*depth;
	cout<<"volume is "<<volume<<endl;

	return volume;

	}
};

This outputs the volume as 0.

Hmmmm....... that would be hard to do because the current arrangement creates separate instances of the 2-dimensional shapes, 1 independent instance and 1 instance inside the prism object. I'll have to think on that more...

I think you'll want to eliminate creation of the 2-D shapes from the main code altogether and go straight to the prism<p> objects. The classes for the 2-D shapes would still exist and be utilized, but not directly within your main code.

Since prism<p> inherits from class p (where p = anyTwoDimensionalShape), and p inherits from abstractShape, prism<p>'s public interface contains not only its own methods but those of the inherited class(es) public interfaces as well (assuming public inheritance was specified). As a result, a prism<p> object would have a get_area() method appropriate for the 2-D shape specified by p.

This should work. With the added benefit of having fewer objects to keep track of.

int main() {
  vector<shape*> myShapes;  //create a vector of prism<p> pointers
  /* push some shapes into the vector
    i.e. myShapes.push_back(new prism<aShape>) */
  vector<double> crossSectionalAreas, prismVolumes; //create the data arrays

  /* manipulate the prism(s) */

  for (vector<prism<shape*>>::const_iterator it = myShapes.begin(); it != myShapes.end(); ++it) {
    crossSectionalAreas.push_back(it->get_area()); //get the cross-sectional area
    prismVolumes.push_back(it->get_volume());  //get the prism's volume
  }

  /* other functionality */

  //release the shapes / call the shape destructors
  myShapes.clear();
    
  return 0;
}

Ran out of edit time, I just realized that there is a goof related to the iterator object(s) in the code above, it won't work as shown, but it should give you the basic idea.

[edit]
Eh,... who am I kidding? I can't leave it hanging like that...
Should be more like this:

int main() {
  vector<shape*> myShapes;  //create a vector of shape pointers
  /* push some prism shapes into the vector
    i.e. myShapes.push_back(new prism<aShape>) */
  vector<double> crossSectionalAreas, prismVolumes; //create the data arrays

  /* manipulate the prism(s) */

  for (vector<shape*>::const_iterator it = myShapes.begin(); it != myShapes.end(); ++it) {
    crossSectionalAreas.push_back(it->get_area()); //get the cross-sectional area
    prismVolumes.push_back(it->get_volume());  //get the prism's volume
  }

  /* other functionality */

  //release the shapes / call the shape destructors
  myShapes.clear();
    
  return 0;
}

That should work, do you suggest I transfer reading the data into the header file get_area() functions themselves, rather than adding data into the constructor?

So instead of:

class circle : public twodshape
{
private:
	double radius;

public:

	circle(){radius;}//default constructor
	circle(double r)  {radius=r;//paramatised constructor
	
	area =PI*radius*radius;
	
	}

	~circle(){}
	
	double get_area()
	{
		return area;
	}
};

have,

private:
	double radius;

public:

	circle(){radius;}//default constructor
	circle(double r)  {radius=r;//paramatised constructor
	
	}

	~circle(){}
	
	double get_area()
	{
		cout<<"Enter radius"<<endl;
		cin>>area;
		area =PI*radius*radius;
		return area;
	}
	};

No, the radius should be a permanent, yet modifiable, part of the circle's properties. It should be entered as part of the object creation, if not before. Something like the first version would be better. Otherwise, you just have a plain ol' function and eliminate the point of having the object in the first place.

i.e.

double radius;
cin >> radius;
myShapes.push_back(new prism<circle>(radius))

That way, the radius is part of the circle's properties. Then, get_area() would just reference the circle's radius property and return the result of the equation.

adding the radius by:

myShapes.push_back(new prism<circle>(radius))

doesn't seem to put the radius into my constructor. This means I can't get the cross sectional area out nor the volume of the prism.

Once again, thanks for the help, I can see it's close......

You will need to use overloaded constructors with initialization lists (the link may pop up a modal, just close it):

class base1 {
  protected:
    double base1Priv1;
  public:
    base1(double newPriv1 = 0.0, double dummyArg = 0.0)
    : base1Priv1(newPriv1) {}
    //overloaded constructor that can function as default constructor
};

class base2 {
  protected:
    double base2Priv1, base2Priv2;
  public:
    base2(double newPriv1 = 0.0, double newPriv2 = 0.0)
    : base2Priv1(newPriv1), base2Priv2(newPriv2) {}
    //overloaded constructor that can function as default constructor
};

template <class T> class tempDerived : public T {
  public:
    tempDerived() { }  //default constructor
    tempDerived(double newB1P1) //overloaded constructor
      : T(newB1P1) {}
    tempDerived(double newB2P1, double newB2P2) //overloaded constructor
      : T(newB2P1, newB2P2) {}
};

This example has a huge bug where a non-existant base-class constructor could be called. I'm not sure how to correct it ATM, but it should give you the basic idea.

[edit]
I added dummyArg to the base1 constructor as a cheap workaround, it's really not the best solution though. I'll think on it more...

Actually, the more I think about it, the more I think the "buggy" version is better. That way, if someone attempts to build an object based on the base1 class incorrectly, it will cause an error of some sort, most likely a linker error.

As a designer, I would consider this a desirable outcome. That way, I help ensure that the class is being used correctly.

I've modified my circle and template classes to the following:

class circle : public twodshape
{
private:
	double radius;

public:

	circle(double rad=0.0, double dummy_arg=0.0)//default constructor
		: radius(rad)  {//paramatised constructor

	cout<<radius<<endl;

	    area =PI*radius*radius;
		

	}
	~circle(){}
	
	double get_area()
	{
		//cout<<radius<<endl;
		//cin>>radius;
		return area;
	}

};

//
//
//
//
//
//
	template <class p> class prism : public p
{
	private:
	double depth;


	public:
		
	prism(){}//default constructor
	prism(double IN)
		: p(IN){}

	prism(double IN1, double IN2)
		: p(IN1, IN2){}

    double get_volume(){
	cout<<"Enter the depth"<<endl;
	cin>>depth;
	area=get_area();
	double volume=area*depth;
	return volume;
	}
};

In main I've modified it to

if (choice=='c')
	{

	double radius(0.);
	cout<<"Please enter the radius for a circle"<<endl;
	cin>>radius;
	a.push_back(new prism<circle>(radius));
	vector<double> crossSectionalAreas, prismVolumes;
	
	for (vector<shape*>::const_iterator it = a.begin(); it != a.end(); ++it) {

		crossSectionalAreas.push_back((*it)->get_area());
		//(*it)->info();
		cout<<"111 "<<crossSectionalAreas[0]<<endl;

		double depth(0.);
		prism <circle> cylinder(radius);
		prismVolumes.push_back(cylinder.get_volume());  
		cout<<prismVolumes[0]<<endl;
		delete *it;
	}


  
	}

This now correctly outputs both the area and the volume! Does it look like I have made any mistakes with the initialization lists?

You really need to stop cut/pasting my examples. I'm specifically writing them in a way that isn't directly compatible with your code for a reason.

area =PI*radius*radius;

What's area? Where did it come from? Is this a member of twodshape?

Sorry, I tried to use your code without fully reading up on initialization lists, I thought the example you gave me was directly relevant so the change to the circle constructor was necessary, my (stupid) mistake...also 'area' is a member of twodshape.

I restored the circle constructor to how it was (I don' t think i need to implement initialization lists with the 2dshapes) and continued adapting the prism class so it can accept all my 2dshapes from circle(radius) to trapezium(base,top, height). This has the program outputting the correct areas/prism volumes on request as well as printing them at the end if necessary. 3dshapes doesn't seem to have any problems.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.