Hi, just a quick question (i hope!!), i'm working on an assignment where i need to build a user-defined library of shapes. The class structure is to have "shape" as a base-class with both 2d and 3d shapes as derived classes. I've handled this by storing them in a vector template (though i've omitted this in the code below for simplicity).

One of the main requirements is to pass a 2D shape derived class object (e.g. "circle ci1") as an argument in a generic derived class known as "prism" that will take the area of the 2d shape and allow a depth to be specified to create a prism (for circle->cylinder).

My question is how to pass the derived class object in the prism constructor? I figured i could use a template class to allow for any of the derived class' objects to be passed (though i can restrict to 2d shapes), though i'm not sure as to the syntax to allow for this.

Here's a simplified version of my code (simplified just to illustrate the one area that i'm asking about):

#include <iostream>
#include <cmath>
#include <string>

using namespace std;

const double pi = 3.141592;


class shape	
{
public:
	virtual double calcarea()=0;
	virtual double calcvolume()=0;
	virtual string gettype()=0;
	virtual bool flat()=0;
	virtual ~shape(){}
};

class square : public shape
{
private:
	string type;
	double length;
	string gettype(){return type;}
	bool flat(){return true;}
	double calcvolume(){return 0;}
public:
	square(){type="Square";length=0;}
	square(double l){type="Square";length=l;}
	double calcarea(){double temp=pow(length,2.);return temp;}
	
};

class circle : public shape
{
private:
	string type;
	double radius;
	string gettype(){return type;}
	bool flat(){return true;}
	double calcvolume(){return 0;}
public:
	circle(){type="Circle";radius=0;}
	circle(double r){type="Circle";radius=r;}
	double calcarea(){double temp=pi*pow(radius,2.);return temp;}
	
};


template <class T> class prism : public shape
{
private:
	string type;
	double depth;
	double basearea;
	string gettype(){return type;}
	bool flat(){return false;}
	double calcarea(){return 0;}
public:
	double calcvolume(){double temp=depth*basearea; return temp;}
	prism(){type="Prism";depth=0;basearea=0;}
	prism(double d, T baseshape){type="Prism";depth=d;basearea=baseshape.calcarea;}
};

int main()
{
	circle ci1(2);
	prism<circle> pr1(2,ci1);
	
	shape *sh1 = &ci1;
	shape *sh2 = &pr1;
	
	cout << sh1->calcarea() << endl;
	cout << sh2->calcvolume() << endl;

}

I'm aware that line 63 (and consequently line 69) are both very wrong, but this is where i have no idea what to do. I'll keep trying but if someone could advise me, that would be really helpful.

Thanks!

You have to initialize the constructor of the object you are inheriting

struct shape 
 {
   int d;
   shape (int x) : d(x) {}
  }

struct prism : public shape
  {
    int x;
     prism () : shape(x) {}
   }

Actually prism does not need to be a template. What it needs to do is store a shape* that points to a shape that is the cross section of the prism.

That would let you change the cross section if you wanted to.

Note that this pointer should probably point to a shape constructed specifically for use by the prism so that would need to be allocated on the heap.

Also when passing objects into functions always use either a constant reference const shape& or if the object passed in is going to be modified a reference shape& to avoid lots of copy constructor and destructor calling.

Also when initialising variables in a constructor where possible it is better to use a constructor initialisation list rather than assigning to them in constructor body. Again for classes this is more efficient because it calls a single constructor rather than calling the default constructor and the assignment operator.

So prism might want to look something like

class prism : public shape
{
private:
	string type;
	double depth;
        shape* crosssection

public:
	prism(double d, const square& cs)
        : type("Prism"),
          depth(d)
          crosssection(new square(cs))
        {
        }
};

Note this prism only has a constructor for squares you would have 2 choices, either you write a constructor for every plan subclass of shape but that really is object orientated and is a pain and requires prism to alter every time a new plan shape is added so a better option is to add a new pure virtual function to shape something like virtual shape *duplicate() = 0; . This method would create a copy on the heap of the current shape and return a pointer to it. This would then allow the prism constructor to become

prism(double d, const shape& cs)
        : type("Prism"),
          depth(d)
          crosssection(cs.duplicate())
        {
            if (!crosssection->flat())
            {
                 delete crosssection;
                 throw exception prism cross section must be flat;
            }
        }

in this final version prism has no specialist knowledge of the class of the cross section it is completely hidden and accessed through the interface defined in shape.


Oh and one final point, if you are going to create a abstract class like shape then you MUST also declare the constructor as virtual. EDIT: Oh you have.... OK

Edited 6 Years Ago by Banfa: n/a

Thank you so much! I've re-written it and it works brilliantly. Plus thanks for the advice on constructors, it was an area of this course that the lecturer spent very little time.

Ok, having replied previously after writing this test program:

#include <iostream>
#include <cmath>
#include <vector>
#include <string>

using namespace std;

const double pi = 3.141592;


class shape	
{
public:
	virtual double calcarea()=0;
	virtual double calcvolume()=0;
	virtual string gettype()=0;
	virtual bool flat()=0;
	virtual ~shape(){};
	virtual shape *duplicate()=0;
};

class square : public shape
{
private:
	string type;
	double length;
	string gettype(){return type;}
	bool flat(){return true;}
	double calcvolume(){return 0;}
public:
	square(){type="Square";length=0;}
	square(double l){type="Square";length=l;}
	double calcarea(){double temp=pow(length,2.);return temp;}
	shape *duplicate(){shape *temp;square s1(length);temp=&s1;return temp;}
	
};

class circle : public shape
{
private:
	string type;
	double radius;
	string gettype(){return type;}
	bool flat(){return true;}
	double calcvolume(){return 0;}
public:
	circle(){type="Circle";radius=0;}
	circle(double r){type="Circle";radius=r;}
	double calcarea(){double temp=pi*pow(radius,2.);return temp;}
	shape *duplicate(){shape *temp;circle c1(radius);temp=&c1;return temp;}
};


class prism : public shape
{
private:
	string type;
	double depth;
	shape *crosssection;
	string gettype(){return type;}
	bool flat(){return false;}
	double calcarea(){return 0;}
	
public:
	prism(){depth=0; type="Prism";}
	prism(double d, shape& cs)
	: type("Prism"),
	depth(d),
	crosssection(cs.duplicate())
	{
	}
	double calcvolume(){double temp=depth*crosssection->calcarea(); return temp;}

	
	shape *duplicate(){shape *temp;prism p1(depth,*crosssection);temp=&p1;return temp;}
};

int main()
{
	
	shape *ss;
	square sq1(2);
	ss=&sq1;
	prism pr1(2,*ss);
	ss=&pr1;
	cout << ss->calcvolume() << endl;

	return 0;
}

which works fine, i tried to integrate the use of duplicate() into my assignment, where i am using a vector template to store base class pointers, unfortunately i keep getting "Program received signal: “EXC_BAD_ACCESS”" Here's the code:

#include <iostream>
#include <cmath>
#include <vector>
#include <string>

using namespace std;

const double pi = 3.141592;


class shape	
{
public:
	virtual double calcarea()=0;
	virtual double calcvolume()=0;
	virtual string gettype()=0;
	virtual bool flat()=0;
	virtual ~shape(){}
	virtual shape *duplicate()=0;
};

class square : public shape
{
private:
	string type;
	double length;
	double calcarea(){double temp=pow(length,2.);return temp;}
	double calcvolume(){return 0;}
	string gettype(){return type;}
	bool flat(){return true;}
	shape *duplicate(){shape *temp;square s1(length);temp=&s1;return temp;}
public:
	square(){type="Square";length=0;}
	square(double l){type="Square";length=l;}
	
};

class cube : public shape
{
private:
	string type;
	
	double length;
	double calcarea(){return 0;}
	double calcvolume(){double temp=pow(length,3.); return temp;}
	string gettype(){return type;}
	bool flat(){return false;}
	shape *duplicate(){shape *temp;cube c1(length);temp=&c1;return temp;}
public:
	cube(){type="Cube";length=0;}
	cube(double l){type="Cube";length=l;}
};

class prism : public shape
{
private:
	string type;
	double depth;
	shape *crosssection;
	string gettype(){return type;}
	bool flat(){return false;}
	double calcarea(){return 0;}
	shape *duplicate(){shape *temp;prism p1(depth,*crosssection);temp=&p1;return temp;}
	
public:
	prism(){depth=0; type="Prism";}
	prism(double d, shape& cs)
	: type("Prism"),
	depth(d),
	crosssection(cs.duplicate())
	{
	}
	double calcvolume(){double temp=depth*crosssection->calcarea(); return temp;}
};



int main()
{
	
	vector<shape *> shapelib;
	char dim,c;
	int count(0);
	double l,b,h,r,d;
	
	
	do
	{
		cout << "Do you want to add a (2)D or (3)D shape? (Q to quit, P to print current library of shapes)" << endl;
		cin >> dim;
		switch (dim) 
		{
			case '2':
				cout << "Choose a shape to add to the library:\n(s)quare" <<endl;
				
				cin >> c;
				switch (c) 
			{
				case 'S':
				case 's':
					cout << "Enter the length of the square" << endl;
					cin >> l;
					cout << "What's this?" << shapelib.size() << endl;
					shapelib.push_back(new square(l));
					cout << "What's this?" << shapelib.size() << endl;
					break;
					
				default:
					cout << "You did not enter a valid shape" << endl;
					break;
			}
				
				break;
				
			case '3':
				cout << "Choose a shape to add to the library:\n(c)ube, (g)eneric prism" <<endl;
				
				cin >> c;
				switch (c) 
			{
				case 'C':
				case 'c':
					cout << "Enter the length of the cube" << endl;
					cin >> l;
				
					shapelib.push_back(new cube(l));
					break;
					
					case 'G':
					 case 'g':
					 
					 cout << "Choose an existing 2D shape from the library:" << endl;
					 
					 
					 for (int j=0;j<(shapelib.size());j++)
					 {
					 if (shapelib[j]->flat()==true)
					 {
					 cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has area " << shapelib[j]->calcarea() << endl;
					 count = count+1;
					 }
					 }
					 
					 
					 if (count >0)
					 {
					 int n;
					 cin >> n;
					 cout << "Enter the depth of the triangular prism" << endl;
					 cin >> d;
					 
					 shapelib.push_back(new prism(d,*shapelib[n-1]));
					 }
					 
					 else
					 {
					 cout << "Sorry there are no 2D shapes to choose" << endl;
					 }
					 
					 
					 
					 
					 break;
					
				default:
					cout << "You did not enter a valid shape" << endl;
					break;
			}
				
				break;
				
				
			case 'Q':
			case 'q':
				cout << " " << endl;
				for (int j=0;j<(shapelib.size());j++)
				{
					if (shapelib[j]->flat()==true)
					{
						cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has area " << shapelib[j]->calcarea() << endl;
					}
					else 
					{
						cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has volume " << shapelib[j]->calcvolume() << endl;
					}
				}
				
				
				for( vector<shape *>::iterator i = shapelib.begin(); i != shapelib.end(); ++i )
				{
					delete *i;
				}
				
				shapelib.clear();
				
				return 0;
				break;
				
				
			case 'p':
			case 'P':
				
				cout << " " << endl;
				for (int j=0;j<(shapelib.size());j++)
				{
					if (shapelib[j]->flat()==true)
					{
						cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has area " << shapelib[j]->calcarea() << endl;
					}
					else 
					{
						cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has volume " << shapelib[j]->calcvolume() << endl;
					}
				}
				
				cout << " " << endl;
				break;
				
				
			default:
				cout << "That was not a valid choice.\n\nHere is the library of shapes thus far: " << endl;
				for (int j=0;j<(shapelib.size());j++)
				{
					cout << "Shape number " << j+1 << " is a " << shapelib[j]->gettype() << " and has area " << shapelib[j]->calcarea() << endl;
				}
				
				
				break;
				
		}
		
	}	
	
	while (dim!=('q'||'Q'));
	
	
	return 0;
}

the main area of concern is line 152:

shapelib.push_back(new prism(d,*shapelib[n-1]));

it seems to work fine, until it has to call the calcvolume function.

Can someone help please!

(Also, this is just a small excerpt from my code so there may be some unused variables etc.)

Your duplicate() methods are consistently wrong. Instead of passing an address of an object on stack like you do, allocate the object from the heap using new and return the pointer to it.

For example ..

shape *duplicate()
{
  return new square(length);
}

And once you've done with the duplicated objects, remember to delete them.

the main area of concern is line 152:

shapelib.push_back(new prism(d,*shapelib[n-1]));

Note that n - 1 must not result in a negative value.

This question has already been answered. Start a new discussion instead.