Hi im continuing to practise for c++ for when I go to uni next year and i have a problem with a practise example I have been given. I have set it up so the user can define the shapes and size etc, but I am unable to work out what I need to add/where in the code itself a save function to store what is in the vector, and then obviously a load to load it after the vector has been saved. Any help would be sweet.....

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

//using std
using namespace std;

//shape class
class Shape{
public:
	virtual double GetArea() = 0; //get area function 
};

//2dshape class
class TwoDimensionalShape : public Shape {//inherits from shape class
public:
	virtual double GetArea() = 0; //get area function 
		
}; 



//2dshape square
class Square : public TwoDimensionalShape{//inherits from 2dshape class
public:
	double length;
	Square(double ilength){
		length = ilength;
	}
	
	virtual ~Square(){} // destructor
	virtual double GetArea() { return length * length;}//returns the Values for get area function
	

	
	
};

//2dshape circle
class Circle:public TwoDimensionalShape{//inherits from 2dshape class
public:

	double r;
	Circle(double radius){
		r=radius;
	}
	virtual ~Circle(){}// destructor
	virtual double GetArea() { return 3.14 * r*r;}//returns the Values for get area function
	

	
};
//2dshape Triangle
class Triangle : public TwoDimensionalShape{//inherits from 2dshape class
public:

	double width,height;
	Triangle(double iwidth, double iheight){
		width = iwidth;
		height = iheight;
	}
	virtual ~Triangle(){} // destructor
	virtual double GetArea() { return (width * height)/2;}//returns the Values for get area function
		
};

//3dshape class
class ThreeDimensionalShape : public Shape{//inherits from shape class
public: 
	
	virtual double GetVolume() = 0;//adds extra function for all subclass that inherit from it
}; 

class Cube : public ThreeDimensionalShape{//inherits from 3dshape class
public:
	Cube(double isideLength){
		sideLength = isideLength;
	}
	virtual ~Cube(){}// destructor
	virtual double GetArea(){return 6*sideLength *sideLength;}//returns the Values for get area function
	virtual double GetVolume(){return sideLength*sideLength *sideLength;}//returns the Values for get volume function
private:
	double sideLength;
};

class Sphere : public ThreeDimensionalShape{//inherits from 3dshape class
public:
	Sphere(double iradius){
		radius = iradius;
	}
	virtual ~Sphere(){}// destructor
	virtual double GetArea(){return 4 * 3.14 * radius * radius;}//returns the Values for get volume function
	virtual double GetVolume(){return (4/3)*3.14*radius*radius *radius;}//returns the Values for get volume function
private:
	double radius;
};



int main(){
	
	vector <Shape*> array0(5);//array size of five
	
	//elements in array
	array0[0]= new Square(6); 
	array0[1]= new Circle(8);
	array0[2]= new Triangle(6,10);
	array0[3]= new Cube(6);
	array0[4]= new Sphere(8);
	
	
	for(size_t num=0;num<array0.size();num++)
	{
		cout<<"\nArea: "<<array0[num]->GetArea()<<endl;//outputs the area to the screen

		if( ThreeDimensionalShape* pointer3DShape = (dynamic_cast<ThreeDimensionalShape*>(array0[num])) )//this  if checks whether the shape is 3d, then also print the volume
		{
			std::cout<<"\nVolume: "<<pointer3DShape->GetVolume()<<endl;
		}
	}
	
	//deleting the shapes in the array
	for(size_t i=0; i < array0.size(); i++)
	{
		delete array0[i];
	}

	//cout<<endl;

	cin.ignore(100, '\n');
	cin.get();

	return 0;
}

to save the data you need to save to a file

this uses #include <fstream> and std::ofstream fout there are plenty of articles on this
now you need a char * and know how many char need to be reserved if you want to save directly.

now you also need to convert and reinterpret your vector which requires converting to and from an array.

A more manageable appraoch writes to a txt file as ascii
now you can write your own header structure
so your file may look like:

shapes, 5
1,  triangle, 7.5
2, square, 3.4
3, rectangle, 7.8, 3.4

This adds a bit of overhead but it is readable by you

the idea is that you save a keyword add a number to tell you the size of a vector
then you have a line
with index, name, val1, val2, ....

This way shapes such as polygon can be of variable size

You would add a class called shape_helper

#include <vector>
#include <string>
#include "shape.h" //your shape class

class shape_helper
{
 bool save(std::string &file_name, std::vector<shape *> &data);
std::vector<shape*> load(std::string &file_name);
};

you still need to make sure you use delete to correspond to when you have finished with anything that you declared as new

Edited 6 Years Ago by Nick Evan: n/a

I have managed to sort out saving to a file, however I have tried a few attempts at trying to load without success, and would just like some advice on the best way of loading the .dat file.

Here is the updated code :

#include<iostream>
#include<fstream>
#include<vector>
#include<cmath>

//using std
using namespace std;
using std::ofstream;

//shape class
class Shape{
public:
	virtual double GetArea() = 0;//get area function
	virtual void saveFile(ofstream& outputFile){}

};

//2dshape class
class TwoDimensionalShape : public Shape {//inherits from shape class
public:

}; 

//3dshape class
class ThreeDimensionalShape : public Shape{//inherits from shape class
public: 

	virtual double GetVolume() = 0;//adds extra function for all subclass that inherit from it
}; 



//2dshape square
class Square : public TwoDimensionalShape{//inherits from 2dshape class
public:
	double length;
	Square(double ilength){
		length = ilength;
	}

	virtual ~Square(){} // destructor
	virtual double GetArea() 	
	{ return length * length;}//returns the Values for get area function

	virtual void saveFile(ofstream& outputFile)
	{
		outputFile <<"Square "<< length <<" " <<endl;
	}

};

//2dshape circle
class Circle:public TwoDimensionalShape{//inherits from 2dshape class
public:

	double r;
	Circle(double radius){
		r=radius;
	}
	virtual ~Circle(){}// destructor
	virtual double GetArea()		
	{ return 3.14 * r*r;}//returns the Values for get area function

	virtual void saveFile(ofstream& outputFile)
	{
		outputFile<<"circle" << r <<" " <<endl;
	}


};
//2dshape Triangle
class Triangle : public TwoDimensionalShape{//inherits from 2dshape class
public:

	double width,height;
	Triangle(double iwidth, double iheight){
		width = iwidth;
		height = iheight;
	}
	virtual ~Triangle(){} // destructor
	virtual double GetArea() { return (width * height)/2;}//returns the Values for get area function
	virtual void saveFile(ofstream& outputFile)
	{
		outputFile <<"Triangle "<< width<<" " <<endl;
		outputFile <<"Triange "<< height <<" " <<endl;
	}
};



class Cube : public ThreeDimensionalShape{//inherits from 3dshape class
public:
	Cube(double isideLength){
		sideLength = isideLength;
	}
	virtual ~Cube(){}// destructor
	virtual double GetArea(){return 6*sideLength *sideLength;}//returns the Values for get area function
	virtual double GetVolume(){return sideLength*sideLength *sideLength;}//returns the Values for get volume function
	virtual void saveFile(ofstream& outputFile)
	{
		outputFile <<"Cube "<< sideLength<<" " <<endl;		
	}
private:
	double sideLength;
};

class Sphere : public ThreeDimensionalShape{//inherits from 3dshape class
public:
	Sphere(double iradius){
		radius = iradius;
	}
	virtual ~Sphere(){}// destructor
	virtual double GetArea(){return 4 * 3.14 * radius * radius;}//returns the Values for get volume function
	virtual double GetVolume(){return (4/3)*3.14*radius*radius *radius;}//returns the Values for get volume function
	virtual void saveFile(ofstream& outputFile)
	{
		outputFile <<"Sphere "<< radius<<" " <<endl;	
	}
private:
	double radius;
};

/*void printShape()
{
vector <Shape*> array0(5);

for(size_t num=0;num<array0.size();num++)
{
cout<<"\nArea: "<<array0[num]->GetArea()<<endl;//outputs the area to the screen

if( ThreeDimensionalShape* pointer3DShape = (dynamic_cast<ThreeDimensionalShape*>(array0[num])) )//this  if checks whether the shape is 3d, then also print the volume
{
std::cout<<"\nVolume: "<<pointer3DShape->GetVolume()<<endl;
}
}
}*/

int main(){

	ofstream outputFile("shapes.dat", ios::out);

	if (!outputFile.is_open())
	{
		cerr<<"could not open shape.dat for writing "<<endl;
		exit (1);
	}

	int choice =0;
	int u = 0;
	int size = 0;
	int width = 0;


	vector <Shape*> array0(5);//array size of five

	cout<< "	0: Square" <<endl;
	cout<< "	1: Circle" <<endl;
	cout<< "	2: Triangle" <<endl;
	cout<< "	3: Cube" <<endl;
	cout<< "	4: Sphere" <<endl<<endl;
	cout<< "5: to Save."<<endl;
	cout<< "6: to Load."<<endl;
	cout<< "7: to print the shapes created."<<endl<<endl;
	cout <<"8: EXIT"<<endl;
	do //do loop
	{
		cout << " Please enter the number for the option you wish to use: "<<endl;
		cin >> choice;//storing to choice
		cout <<endl;

		switch(choice)//switch statement which deals with the different cases of the game. 
		{

		case 0:
			cout<<"please enter length: "<<endl;
			cin >> size;
			/*array0.push_back(new Square(size));*/
			array0[u]= new Square(size);
			u++;
			break;

		case 1:
			cout<<"please enter size: "<<endl;
			cin >> size;
			/*array0.push_back(new Circle(size));*/
			array0[u]= new Circle(size);
			u++;
			break;

		case 2:
			cout<<"please enter width: "<<endl;
			cin >> width;
			cout<<"please enter length: "<<endl;
			cin >> size;
			/*array0.push_back(new Triangle(width,size));*/
			array0[u]= new Triangle(width,size);
			u++;
			break;

		case 3:
			cout<<"please enter size: "<<endl;
			cin >> size;
			/*array0.push_back(new Cube(size));*/
			array0[u]= new Cube(size);
			u++;
			break;

		case 4:
			cout<<"please enter size: "<<endl;
			cin >> size;
			/*array0.push_back(new Cube(size));*/
			array0[u]= new Sphere(size);
			u++;
			break;

		case 5:
			//if (choice == 5)
			//{

			array0[0]->saveFile(outputFile);
			array0[1]->saveFile(outputFile);
			array0[2]->saveFile(outputFile);
			array0[3]->saveFile(outputFile);
			array0[4]->saveFile(outputFile);
			

			/*ofstream outFile ("testshape.dat", ios::out);
			{
			if(!outFile.is_open())
			{
			cerr <<"could not open testShape.dat for writing2 "<<endl;
			return 1;*/
			//}

			break;

		case 6:
			//load;
			break;

		case 7:
			//printShape;
			break;


		}
	}


	while(choice != 7);

	for(size_t num=0;num<array0.size();num++)
	{
		cout<<"\nArea: "<<array0[num]->GetArea()<<endl;//outputs the area to the screen

		if( ThreeDimensionalShape* pointer3DShape = (dynamic_cast<ThreeDimensionalShape*>(array0[num])) )//this  if checks whether the shape is 3d, then also print the volume
		{
			std::cout<<"\nVolume: "<<pointer3DShape->GetVolume()<<endl;
		}
	}

	//deleting the shapes in the array
	for(size_t i=0; i < array0.size(); i++)
	{
		delete array0[i];
	}



	cin.ignore(100, '\n');
	cin.get();

	return 0;
}

To load from a file you use std::ifstream

but your bigger problem is how do you know what shape you are loading?

since some shapes can require more data than others you don't know how to load the file

this is why I suggested using an ascii file so aswell as the data
you load the information to know
1 - how many shapes you have
2 - what is the current shape to load & therefore how much data

so then you could load the line of data or name depending upon your choice

check the name cast the shape * pointer and then load the remaining data.

so for a shape I would add the following methods

static std::string get_name();
bool is_my_name(std::string &name);
bool load(std::string &line);

if you load the file as std::ios::i

you can use get_line() until you get an empty line

check the line for the first word matching a shape_id
then

Shape * ps = 0;
//chop the 1st keyword
std::string shape_id = get_shape_id(line);
if(shape_id == square::get_name())
{
 square p_sq =  new square();
 p_sq->load(line)
 ps = p_sq;
}
else if(shape_id == circle::get_name())
{
circle p_cc =  new circle();
 p_cc->load(line);
 ps = p_cc;
}

if(ps != 0)
{
 data.push_back(ps);
}
///...

P.S. you probably don't need to post the entire code each time unless you have a bug

I have managed to sort out saving to a file, however I have tried a few attempts at trying to load without

Edited 6 Years Ago by tetron: typo

case 7:
                       
                        ifstream outputFile("shapes");
                        outputFile >>size;
 
                        cout<<"\n  "<<size;
                        cout<<"\n  "<<size;
                        cout<<"\n  "<<size;
                        cout<<"\n  "<<size;
                        cout<<"\n  "<<size;

the code is now displaying the first line of the .dat file however the way I have it set up it only outputs that specific line and does not move down to the next shape. Is there any way of moving down to the next line to produce the output for that shape (i.e the user input of size)

Like with cin, every time you call it a pointer should advance to the end of the loaded data.


Here is a crude example of a getline which loads a line at a time
fstream works like iostream so the same methods can be used

#include <fstream>
#include <string>
#include <iostream>

int main()
{
std::string file_loc("test.txt");
//open the file upon creation of fin
std::ifstream fin(file_loc.c_str(), std::ios::in);


if(fin.is_open() == true)
{
 fin.close();
 int max_lines = 20000;//an arbitrary stop condition
 int count(0);
 while(count < max_lines) 
 {
   int max_line_length(1023);
   char line_array[max_line_length];
   fin.getline(line_array, max_line_length);
   //put the data into a string
  std::string line(line_arrray);

 /*
 proc line as you want in here
*/
   ++count;
 }
}
else
{
std::cout << "failed to open:" << file_loc << std::endl;
}
return 0;
}

how you want to load and handle the data is up to you but as a beginner dealing with strings is easiest if you are using data of unfixed length and might want to change the design.

But you could store a list of values and load as binary file instead but your format of your load must correspond tyo your save

Edited 6 Years Ago by tetron: n/a

What you are discussing here is termed serialization, or sometimes object persistence. Boost contains an excellent library which will handle all of this for you and is worth researching.

Serialization is a non-trivial exercise with some interesting design choices which have to be made before selecting an implementation. Check out Marshal Clines C++ FAQ (http://www.parashift.com/c++-faq-lite/index.html) sections on serialization for some interesting points on the subject. For more detailed reading, check out the Boost Serialization library and go to the references section. This will point you towards a number of interesting resources which will cover this topic in detail.

http://www.microsoft.com/msj/archive/S385.aspx

Will take you to a pretty indepth article discussing some of the problems and pitfalls surrounding serialization and ways to address them. A good place to start looking.

This article has been dead for over six months. Start a new discussion instead.