creating a class vector that does not delete it's content - c++

I am a beginner , so i wanted to ask , can we create a class object vector/array , that does not delete it's content when i close the program like , so like I want a customer record , but whenever if we try to restart the program we need to enter the customer details again and again ...
how to prevent that from happening
#include <iostream>
#include <vector>
using namespace std;
class customer{
public:
int balance;
string name;
int password;
};
int main(){
vector <customer> cus;
...
if(choice == 1){
cout << cus[i].balance
}
return 0;
}

As a complement to Adam's answer, it is possible to encapsulate the serialization in the container class itself. Here is an simplified example:
The header file defining a persistent_vector class that saves its content to a file:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <initializer_list>
namespace {
// Utility functions able to store one element of a trivially copyable type
template <class T>
std::ostream& store1(std::ostream& out, const T& val) {
out.write(reinterpret_cast<const char*>(&val), sizeof(val));
return out;
}
template <class T>
std::istream& load1(std::istream& in, T& val) {
in.read(reinterpret_cast<char*>(&val), sizeof(val));
return in;
}
// Specialization for the std::string type
template <>
std::ostream& store1<std::string>(std::ostream& out, const std::string& val) {
store1<size_t>(out, val.size());
if (out) out.write(val.data(), val.size());
return out;
}
template <>
std::istream& load1<std::string>(std::istream& in, std::string& val) {
size_t len;
load1<size_t>(in, len);
if (in) {
char* data = new char[len];
in.read(data, len);
if (in) val.assign(data, len);
delete[] data;
}
return in;
}
}
template <class T>
class persistent_vector {
const std::string path;
std::vector<T> vec;
// load the vector from a file
void load() {
std::ifstream in(path);
if (in) {
for (;;) {
T elt;
load1(in, elt);
if (!in) break;
vec.push_back(elt);
}
if (!in.eof()) {
throw std::istream::failure("Read error");
}
in.close();
}
}
// store the vector to a file
void store() {
std::ofstream out(path);
size_t n = 0;
if (out) {
for (const T& elt : vec) {
store1(out, elt);
if (!out) break;
++n;
}
}
if (!out) {
std::cerr << "Write error after " << n << " elements on " << vec.size() << '\n';
}
}
public:
// a bunch of constructors, first ones load data from the file
persistent_vector(const std::string& path) : path(path) {
load();
}
persistent_vector(const std::string& path, size_t sz) :
path(path), vec(sz) {
load();
};
// last 2 constructors ignore the file because they do receive data
persistent_vector(const std::string& path, size_t sz, const T& val) :
path(path), vec(sz, val) {
};
persistent_vector(const std::string& path, std::initializer_list<T> ini) :
path(path), vec(ini) {
}
// destructor strores the data to the file before actually destroying it
~persistent_vector() {
store();
}
// direct access to the vector (const and non const versions)
std::vector<T>& data() {
return vec;
}
const std::vector<T>& data() const {
return vec;
}
};
It can, out of the box, handle any trivially copyable type and std::string. User has to provide specializations of store1 and load1 for custom types.
Here is a trivial program using it:
#include <iostream>
#include <string>
#include "persistent_vector.h"
int main() {
std::cout << "Create new vector (0) or read an existing one (1): ";
int cr;
std::cin >> cr;
if (!std::cin || (cr != 0 && cr != 1)) {
std::cout << "Incorrect input\n";
return 1;
}
if (cr == 0) {
persistent_vector<std::string> v("foo.data", 0, "");
// skip to the end of line...
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for (;;) {
std::string line;
std::cout << "Enter a string to add to the vector (empty string to end program)\n";
std::getline(std::cin, line);
if (line.empty()) break;
v.data().push_back(line);
}
}
else {
persistent_vector<std::string> v("foo.data");
for (const std::string& i : v.data()) {
std::cout << i << '\n';
}
}
return 0;
}

When a programmer creates a vector class, he must ensure that the resources acquired for that vector are released when they are no longer needed. (See RAII)
C++ Reference : https://en.cppreference.com/w/cpp/language/raii
Wikipedia : https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
Stack Overflow : What is meant by Resource Acquisition is Initialization (RAII)?
Microsoft : https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
Before the program closes, all resources must be released.
(No leaking resources, memory included)
It is not possible to create a vector class that does not delete its contents after closing a program. Secure operating systems will release program resources when the program is closed.
If you want the program not to lose customer information after closing, you need to save the information in persistent (non-volatile) storage device, such as a disk.
As CinCout, 김선달, Serge Ballesta say, you have to save the customer information to a file, and write the program so that you can read that file during the start of the program.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct customer {
std::string name;
int balance;
int password;
};
int main() {
std::vector <customer> customers;
std::ifstream ifs("info.txt");
{
customer customer{};
while (ifs >> customer.name >> customer.balance >> customer.password)
customers.push_back(customer);
}
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
std::cout << "\n\nWelcome\n\n";
std::ofstream ofs("info.txt", std::ios_base::app);
char cont{};
do {
customer customer{};
std::cout << "Name : ";
std::cin >> customer.name;
std::cout << "Balance : ";
std::cin >> customer.balance;
std::cout << "Password : ";
std::cin >> customer.password;
ofs << customer.name << ' ' << customer.balance << ' ' << customer.password << '\n';
std::cout << "Add another customer? (Y/N) : ";
std::cin >> cont;
} while (cont == 'Y');
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
}
CPlusPlus : https://www.cplusplus.com/doc/tutorial/files/
LearnCpp : https://www.learncpp.com/cpp-tutorial/basic-file-io/
(About File I/O)
This program is a prototype, I left some things incomplete (like check readings, user-defined I/O operators, duplicate code, formatting, reallocations of customers, ifs is not required after range-for + structured binding,...).
I suggest you read the book "Programming: Principles and Practice Using C+", I’m reading it and it helped me a lot.
(I’m also a beginner)
Edit: I also suggest you use "using namespace std;" only for small projects, examples or simple exercises.
Do not use "using namespace std;" for real projects, large projects or projects that may include other dependencies because the use of "using namespace std;" could lead to a possible naming collisions between names within std and the names of other codes and libraries.
It’s not good practice to use it all the time.

Related

How to add a list to a string? [duplicate]

This question already has answers here:
Printing out contents of a list from the c++ list library [duplicate]
(5 answers)
Closed 7 months ago.
I am trying to add a list to a string.
int main() {
std::cout << "Hello, welcome to Jay's Coffee!!\n";
std::string name; std::cout <<"What is your name "; std::cin >> name;
std::cout <<"Hello " << name << ", thank you so much for coming in today";
std::list <std::string> menu = {"Black Coffee" "Espresso" "Latte" "Cappucino"};
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n" << menu;
}
Returns
invalid operands to binary expression ('basic_ostream<char>' and 'std::list<std::string>' (aka 'list<basic_string<char>>'))
There is no operator<< for lists. You have to write a loop. For example
for (auto& item : menu)
{
std::cout << item << '\n';
}
If you think about it it's obvious why you have to do this yourself. How are you doing to separate the list items? I've chosen to put each item on a new line. You might choose to separate them with commas or spaces or some fancy format. Because there is no obvious single way to print a list there is no predefined way in the C++ library.
You should write code this way. In c++, you can't print a list directly.
#include <string>
#include <list>
using namespace std;
int main() {
cout << "Hello, welcome to Jay's Coffee!!\n";
string name;
cout <<"What is your name ";
cin >> name;
cout <<"Hello " << name << ", thank you so much for coming in today";
list <string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
cout << name <<",what would you like from our menu today? Here is what we are serving.\n" ;
for ( string& s : menu )
{
cout << s << '\n';
}
}
The error message means that the operator << that you are trying to use with your object menu of the type std::list<std::string> is not defined for the class std::list<std::string>.
Also you need to separate strings in the initializer list with commas.
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
Otherwise the list will contain only one string due to the concatenation of string literals.
You could define such an operator as shown in the demonstration program below.
#include <iostream>
#include <string>
#include <list>
std::ostream & operator <<( std::ostream &os, const std::list<std::string>& lst )
{
for ( const auto &s : lst )
{
os << s << '\n';
}
return os;
}
int main()
{
std::list <std::string> menu =
{
"Black Coffee", "Espresso", "Latte", "Cappucino"
};
std::cout << menu;
}
The program output is
Black Coffee
Espresso
Latte
Cappucino
Or just use the range-based for loop directly in main like
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n";
for ( const auto &s : menu )
{
std::cout << s << '\n';
}
Or place the range-based for loop in a separate function similar to the operator << shown above.
First thing, you didn't define a list: in the declaration of menu, initializer-list elems were not separated by comma (,).
To make it reusable, I'd do it something like this:
#include <iostream>
#include <list>
static const std::string list_sep = ", ";
template<typename C> struct FormattedContainer
{
FormattedContainer(const C& cont, const std::string& sep = list_sep)
: cont_(cont)
, sep_(sep) {}
friend std::ostream& operator<<(std::ostream& os, const FormattedContainer& fc)
{
bool first = true;
for (auto&& e : fc.cont_)
{
if (first)
{
os << e;
first = false;
}
else
{
os << fc.sep_ << e;
}
}
return os;
}
const C& cont_;
const std::string sep_;
};
template<typename C>
auto make_fc(const C& cont, const std::string& sep = list_sep)
-> FormattedContainer<C>
{
return FormattedContainer<C>(cont, sep);
}
int main() {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << "What would you like from our menu today? Here is what we are serving.\n" << make_fc(menu);
}
This way, you don't need to define operator<< for something in std namespace (which might result in ambiguous calls as others might also define it, don't need to import another namespace, just simply call a wrapper around the type. You can use it with basically any container or iterable type using this method.
All other answers are correct but here is the better way to do the same
#include <iostream>
#include <algorithm>
#include <list>
template <typename T>
std::ostream & operator << (std::ostream & os, const std::list<T> & vec){
std::for_each (vec.begin () , vec.end() , [&](const auto& val){
std::cout << val << " ";
});
return os;
}
int main () {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << menu << "\n";
return 0;
}

How to use pointer with struct to refer to the field of each struct

Below code is the normal way to get the input from a text and store it in an array in a structure.
Wanted to ask how can i use pointer to store all these data into the array of structure ? Like p1->Years (this is without array, but how can i apply this to way of writing in below code)
Any better suggestion to use pointer to take in the input?
int years = 4;
struct maju_company {
int Year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
int main() {
string line;
maju_company p1[years];
fstream yeecinnfile("MajuSales.txt");
if(yeecinnfile.is_open()) {
//ignoring the first four line of code and store the rest of the code
string line1,line2,line3,line4;
getline(yeecinnfile,line1);
getline(yeecinnfile,line2);
getline(yeecinnfile,line3);
getline(yeecinnfile,line4);
while(!yeecinnfile.eof()) {
for(int i =0; i<years; i++) {
yeecinnfile>>p1[i].Year>>p1[i].quarter1>>p1[i].quarter2>>p1[i].quarter3>>p1[i].quarter4;
}
}
for(int i =0; i<years; i++) {
cout<<p1[i].Year<<setw(10)<<p1[i].quarter1<<setw(10)<<p1[i].quarter2<<setw(10)<<p1[i].quarter3<<setw(10)<<p1[i].quarter4<<endl;
}
cout<<endl;
}
}
I see nothing wrong with the way you do this.
However, you could create a pointer to each record inside the loop
maju_company* p = &p1[i];
and then use p-> instead of p1[i]., but I really don't see this as an improvement.
If the reading loop looks too complicated, I would rather move the code to a separate function, perhaps
void read_record(maju_company& company);
or maybe
maju_company read_record();
and then only have to handle a single company inside the function (so no indexing and no ponters there).
I think you wouldn't need pointers at all for your example.
Use a std::vector to hold all your data and then there are other
things from C++ I think you should learn to use, example here :
(if you have questions let me know)
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
// dont use : using namespace std;
struct maju_company_data
{
int year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
// describe how to stream data to an output stream (like std::cout)
std::ostream& operator<<(std::ostream& os, const maju_company_data& data)
{
os << "-----------------------------------------------------\n";
os << "Company data for year : " << data.year << "\n";
os << "Quarter 1 : " << data.quarter1 << "\n";
os << "Quarter 2 : " << data.quarter1 << "\n";
os << "Quarter 3 : " << data.quarter1 << "\n";
os << "Quarter 4 : " << data.quarter1 << "\n";
os << "\n";
return os;
}
int main()
{
// no need to manage pointers yourself use a vector
std::vector<maju_company_data> company_yearly_data; // give variables a meaningful name
std::ifstream ifile("MajuSales.txt"); // ifstream your using file as input
std::string line1, line2, line3, line4;
// ignore first line
ifile >> line1;
while (ifile >> line1 >> line2 >> line3 >> line4) // probably you need to read a few more lines here
{
maju_company_data data;
// convert read strings to numbers
data.year = std::stoi(line1);
data.quarter1 = std::stof(line2);
data.quarter2 = std::stof(line3);
data.quarter3 = std::stof(line4);
//..
//data.quarter4 = std::stof(line5);
//data.total_sales = std::stof(line6);
company_yearly_data.push_back(data);
};
// this is a range based for loop
// it is prefered since you cant go out of bounds
// const auto& means that data will be an unmodifiable
// reference to each of the structs stored in the vector
for (const auto& data : company_yearly_data)
{
std::cout << data; // since we overloaded << this loop will be nice and clean
}
return 0;
}
A C++ approach to this to overload the istream operator>> and ostream operator<< for your specific type. E.g.
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
static constexpr auto years{4};
struct maju_company {
int Year{};
float quarter1{}, quarter2{}, quarter3{}, quarter4{};
float total_sales{}, average_sales{}; // ALWAYS init your floats.
};
auto& operator>>(std::istream& is, maju_company& mc) {
is >> mc.Year
>> mc.quarter1 >> mc.quarter2 >> mc.quarter3 >> mc.quarter4
>> mc.total_sales >> mc.average_sales;
return is;
}
auto& operator<<(std::ostream& os, maju_company const& mc) {
os << mc.Year
<< std::setw(10) << mc.quarter1
<< std::setw(10) << mc.quarter2
<< std::setw(10) << mc.quarter3
<< std::setw(10) << mc.quarter4;
return os;
}
You can then go on to use the type using the std library, e.g.
int main() {
auto p1{std::array<maju_company, years>{}};
{
auto fs{std::fstream("MajuSales.txt")};
if (!fs.is_open()) return -1;
{
// throw away 4 lines
auto dummy{std::string{}};
for (auto i{0}; i < 4; ++i) getline(fs, dummy);
}
std::copy_n(std::istream_iterator<maju_company>{fs},
years,
begin(p1));
}
std::copy(cbegin(p1), cend(p1),
std::ostream_iterator<maju_company>{std::cout, "\n"});
}

Array based on strings, char list conversions

I create a multiple table of string type. I keep variables inside (int, string). It gives me an error:
[Error] cannot convert 'std::string {aka std::basic_string}' to 'char' in assignment
I've created a tree-shaped suite of functions.The program create a multiple array from a file with this format:
11 10 2001
CSKA Moscow
12 1
Bayern Munich
...
Program:
void llegir(std::fstream &_contingut, std::string *_taula) {
//declaro variables
int dia, mes, any, puntsLocal, puntsVisitant, i = 0;
std::string equipLocal, equipVisitant;
while (!(_contingut.eof())) {
//llegeixo arxiu
_contingut >> dia >> mes >> any; //primera linea
_contingut.ignore();
getline(_contingut, equipLocal); //segona linea
_contingut >> puntsLocal >> puntsVisitant; //tercera linea
_contingut.ignore();
getline(_contingut, equipVisitant); //quarta linea
_taula[i][0] = dia;
_taula[i][1] = mes;
_taula[i][2] = any;
_taula[i][3] = equipLocal.c_str();
_taula[i][4] = puntsLocal;
_taula[i][5] = equipVisitant.c_str();
_taula[i][6] = puntsVisitant;
i++;
}
}
void creartaulaDelFitxer(std::string _fitxer, std::string *_taula, int &n_taula) {
std::fstream arxiu;
arxiu.open(_fitxer, std::fstream:: in );
if (arxiu.is_open()) {
std::cout << "existeix";
} else {
std::cout << "ERROR OBERTURA FITXER";
}
llegir(arxiu, _taula);
}
int main(int argc, char** argv) {
std::string fitxer;
std::string eurolliga[300][7];
int n_taula = 0;
std::cout << "INTRODUEIX NOM FITXER:" << std::endl;
std::cin >> fitxer;
creartaulaDelFitxer(fitxer, *eurolliga, int n_taula);
}
You are mixing pointers, chars and strings which will certainly cause a lot of headache. Try to use the standard containers, like std::string and std::vector. If you need many strings, put them in a vector. When you have a collection of data like
11 10 2001
CSKA Moscow
12 1
Bayern Munich
that describes some entity, create a class for it. You can then add streaming operators for that class to read in one of these entities. If you have a collection of entities, make a container and add streaming operators for that too.
Example:
#include <iostream>
#include <fstream>
#include <vector>
class Game {
std::string equipLocal{};
std::string equipVisitant{};
int dia{}, mes{}, any{};
int puntsLocal{}, puntsVisitant{};
public:
friend std::istream& operator>>(std::istream&, Game&);
friend std::ostream& operator<<(std::ostream&, const Game&);
};
// read one entity from an istream
std::istream& operator>>(std::istream& is, Game& g) {
if(is >> g.dia >> g.mes >> g.any) {
is.ignore();
if(std::getline(is, g.equipLocal) && (is >> g.puntsLocal >> g.puntsVisitant)) {
is.ignore();
std::getline(is, g.equipVisitant);
}
}
return is;
}
// write one entity to an ostream
std::ostream& operator<<(std::ostream& os, const Game& g) {
return os << g.dia << " " << g.mes << " " << g.any << "\n"
<< g.equipLocal << "\n"
<< g.puntsLocal << " " << g.puntsVisitant << "\n"
<< g.equipVisitant << "\n";
}
class EuroLiga {
std::vector<Game> games{};
public:
bool Load(const std::string& filename) {
std::ifstream arxiu(filename);
if(arxiu) {
games.clear();
arxiu >> *this; // use this class' friend, operator>>
return true;
} else
return false;
}
// support for basic non-const iteration over the 'games'
std::vector<Game>::iterator begin() { return games.begin(); }
std::vector<Game>::iterator end() { return games.end(); }
friend std::istream& operator>>(std::istream&, EuroLiga&);
};
// read all entities from an istream
std::istream& operator>>(std::istream& is, EuroLiga& el) {
Game tmp;
while(is >> tmp) {
el.games.push_back(std::move(tmp));
}
return is;
}
int main() {
EuroLiga euroliga;
std::string fitxer;
std::cout << "INTRODUEIX NOM FITXER: ";
std::cin >> fitxer;
euroliga.Load(fitxer);
// display all entities read from the file
for(auto& g : euroliga) {
std::cout << g << "\n";
}
}
void llegir(std::fstream &_contingut, std::string *_taula)
Gets a pointer to a string called _taula, this is probably your array.
However you assign something to your array like this:
_taula[i][0] = dia; // allowed, but bad because char is basically a number.
[...]
_taula[i][3] = equipLocal.c_str(); // not allowed, you are assigning a char pointer to a char.
taula[i] is the i-th string in your array. And by putting [0] you assign to the first character in that string. dia is an integer though.
For example
std::string[] = {"Hello", "world", "I", "am", "alive"};
std::cout << string[1] << std::endl; // output "world"
std::cout << string[1][0] << std::endl; // output 'w'
You can not assign a string to a single character.
As a side note, you should look into declaring an enumeration for your array index (and a constant for it's size) to make it more clear and improve maintainability.
What you should probably be doing is create a struct or class for your, whatever it is
struct whateverItIs {
int dia, mes, any, puntsLocal, puntsVisitant;
std::string equipLocal, equipVisitant;
};
Make a new instance of that in your llegir and push it to the back of a std::vector you get by reference.
Just remember to delete() them later especially before that vector goes out of scope.

C++ How to load a textfile and adding the content to a class?

I am trying to learn how to use classes and I figured I'd create some sort of supermarket system to aid me with learning. After I have saved all the values from my text file into the temp variables, how do I then use them to create an object? I assume I want one object per item you can "buy"?
If you have any other tips on how to improve my code, please mention them as I just started with C++ a few days ago.
My text file looks like:
42 68 Apples
35 1 Oranges
70 25 Bananas
And my code is below:
// Classes.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
class Products {
private:
int price;
int ID;
int quantity;
public:
void setPrice(int newPrice) {
price = newPrice;
}
void setID(int newID) {
ID = newID;
}
void setQuantity(int newQuantity) {
quantity = newQuantity;
}
int getPrice() {
return price;
}
int getID() {
return ID;
}
int getQuantity() {
return quantity;
}
};
int main()
{
std::string line;
std::string input;
std::string temp;
std::string temp2;
std::string temp3;
int counter = 0;
while (input != "e" && input != "r") {
std::cout << "Do you want to (r)ead the inventory or (e)dit it? (input 'r' or 'e'): ";
getline(std::cin, input);
}
if (input == "r") {
std::ifstream pFile ("products.txt");
if (pFile.is_open()) {
while (getline(pFile, line)) {
std::istringstream iss(line);
iss >> temp >> temp2 >> temp3;
counter++;
}
}
}
return 0;
}
Looking at the text file that you are using as an example and trying to populate a set of classes with the information I'd do something like this:
Text File
42 68 Apples
35 1 Oranges
70 25 Bananas
Looking at the text file you have an int followed by space then another int followed by another space finally followed by a varying size of char[]. We can use this information to create your class or struct and this is how I would create the class based off of the content from the file that is being read in or parsed.
Produce.h
#ifndef PRODUCE_H
#define PRODUCE_H
#include <string>
class Produce {
private:
unsigned price_; // Normally would use float but will use unsigned for simplicity
unsigned quantity_;
std::string name_; // You could use the int as an id as you were using
public:
Produce() {} // Default Constructor
~Produce() {} // Default Destructor
// User Constructor
Produce(const std::string& name, const unsigned& price, const unsigned& qty )
: name_(name), price_(price), quantity_( qty ) {}
// Copy By Const Reference Constructor
Produce(const Produce& other) {
name_ = other.name_;
price_ = other.price_;
quantity_ = other.quantity_;
}
// Assignment Operator
Produce& operator=(const Produce& other) {
name_ = other.name_;
price_ = other.price_;
quantity_ = other.quantity_;
return *this;
}
// Setters & Getters
void setOrChangePrice(const unsigned& priceChange) {
price_ = priceChange;
}
unsigned getPrice() const { return price_; }
void setName(const std::string& name) {
// Already Has A Name? Return!
if (!name_.empty())
return;
}
std::string getName() const { return name_; }
void setOrChangeQuantity(const unsigned& qty) {
quantity_ = qty;
}
unsigned getQuantity() const {
return quantity_;
}
};
#endif // PRODUCE_H
Produce.cpp
#include "Produce.h"
// Normally Constructor(s) & Function Definitions Would Go Here
// Since this is a simple class; I declared & defined them in the header.
Inventory.h
#ifndef INVENTORY_H
#define INVENTORY_H
#include <vector>
#include "Produce.h"
class Inventory {
private:
std::vector<Produce> produce_;
public:
Inventory() {}
~Inventory() {
if (!produce_.empty()) {
produce_.clear();
}
}
std::vector<Produce> getProduceList() const {
return produce_;
}
unsigned getProduceListSize() const {
return static_cast<unsigned>( produce_.size() );
}
void addProduce(const Produce& produce) {
produce_.push_back(produce);
}
Produce getProduce(const std::string& name) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n];
} else {
return Produce();
}
}
}
unsigned getPrice(const std::string& name) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n].getPrice();
} else {
return 0;
}
}
}
unsigned getQuantity( const std::string& name ) const {
for (unsigned n = 0; n < produce_.size(); ++n) {
if (name == produce_[n].getName()) {
return produce_[n].getQuantity();
} else {
return 0;
}
}
}
};
#endif // INVENTORY_H
Inventory.cpp
#include "Inventory.h"
Main.cpp
// #include <vector> // Also Included In Inventory.h
// #include <string> // Also Included In Produce.h
#include <iostream>
#include <fstream>
// #include "Produce.h" // Also Included In Inventory.h
#include "Inventory.h"
int main( ) {
// Same As Your Text File
std::string strFilename("ProduceInventory.txt");
std::ifstream fileIn;
// Temps
unsigned price = 0;
unsigned qty = 0;
std::string name;
Inventory inventory;
fileIn.open(strFilename.c_str());
if (!fileIn.is_open()) {
std::cout << "Can not read file\n";
}
while (fileIn >> price >> qty >> name) {
Produce produce(name, price, qty);
inventory.addProduce(produce);
}
if ( fileIn.is_open() ) {
fileIn.close();
}
// Test Our Inventory From File
for (unsigned n = 0; n < inventory.getProduceListSize(); n++) {
std::cout << "Name: " << inventory.getProduceList()[n].getName() << " "
<< "Price: " << inventory.getProduceList()[n].getPrice() << " "
<< "Qty: " << inventory.getProduceList()[n].getQuantity() << "\n";
}
std::cout << std::endl;
// Check To See If Our Search Method Works
std::cout << "Enter a product type by name to get its price and qty on hand.\n";
name.clear(); // reuse
std::cin >> name;
Produce p = inventory.getProduce(name);
if (p.getName().empty()) {
std::cout << "We either do not carry this produce or we are out of stock\n";
} else {
std::cout << "Our price is " << p.getPrice() << " and we have " << p.getQuantity() << " on hand\n";
// Or
std::cout << "Our price is " << inventory.getPrice(name) << " and we have " << inventory.getQuantity(name) << " on hand\n";
}
return 0;
}
Now this is just one of many ways this can be done; but also make sure that your text file is in the appropriate location of the working directory for your IDE or that you specify the correct path along with the filename and extension to be able to open and read from the file in the first place.
Much of this code can be fined even more; but I was showing how to use the std::vector<T> container and some of its functions and how to iterate through them and to retrieve its data. This is a basic way of parsing a simple text file when you know the exact format of the text file and for each and every line you will have the same data types in the same order.
This will not work for every kind of file parsing because it depends on the file's format. For example another file might have different kind of data from one line to the next and might have keywords or tags to give a description of what kind of data is coming next. For that kind of parsing you would have to read the entire line in first into a string stream then you would have to be able to parse that string stream via tokens to extract the data. Some times parsing a file might have multiple lines that belong to a data set and for that you would have to parse by chunks or blobs.
Parsing text files is harder to parse than binary file formats because you have to check each line and character of the text and you also have to be cautious
of character returns, new line characters etc. With binary format you just have to know how many bytes to read in and what type of data it is expected to be. There are many books that are written on just this topic alone on how to parse data from a file. There is no one simple way that fits all.

C++: How to save classes having pointers as member in file?

For example the following class:
class example
{
public:
int *numbers;//int pointer
int size;
example(int s)
{
size=s;
numbers=new int [size];
}
~example()
{
delete []numbers;
}
}
How can this class be stored in a file ?
There is no easy solution to this question - simply put pointers represent a location in memory but when you restore data from the file you expect it to be stored somewhere else (i.e. not in the original location) so storing the value of the pointer in anyway will fail.
Instead you need to set the pointers as you load from the file to relink with the correct memory location. Generally the tactic used to handle this sort of problem is to store a numeric ID for each record - as you load the ID from the file that you need to link to you look up the ID from what is already loaded and set the pointer to that record. This process is surprisingly called pointer swizzling (thanks #keyser).
This problem is generally referred to a serialization of a class this question and this question are good places to start reading about this process on Stack Overflow.
There are widely used tools for this kind of problem; take a look at the boost::serialzation documentation.
Untested, but gives you an idea.
class Example
{
public:
vector<int> numbers;
explicit Example(int s = 0)
: numbers(s)
{
}
friend ostream& operator << (ostream& os, const Example& x)
{
os << x.numbers.size() << " ";
for (auto n : x.numbers)
{
os << n << " ";
}
if (os.bad()) ERROR;
return os;
}
friend istream& operator >> (istream& is, Example& x)
{
vector<int>::size_type size;
is >> size;
numbers.resize(size);
for (auto& n : x.numbers)
{
is >> n;
}
if (is.bad()) ERROR;
return is;
}
};
{
ofstream outfile("myfile.txt");
Example x;
outfile << x;
}
{
ifstream infile("mytext.txt");
Example x;
infile >> x;
}
You need to implement a serialization method. Any class that needs to be serialized should know how to selialize and deserialize itself. You can choose one of the common human readable formats available formats, such as JSON, XML etc. You also have the option of using more compact formats such as Google protocol buffers, Apache thrift, or msgpack. If your serilization needs are very simple, then another option is to roll out your own. Here is a full compilable version of the answer given by Neil Krik which should help your understanding . . .
#include <iostream>
#include <vector>
#include <sstream>
#include <assert.h>
#include <fstream>
using namespace std;
class Example
{
vector<int> nums;
public:
explicit Example(vector<int> s = {}) : nums(s)
{
}
vector<int>& getNums()
{
return nums;
}
vector<int> getNums() const
{
return nums;
}
string stringify() const
{
stringstream s;
for (auto x : nums) {
s << x << ", ";
}
return s.str();
}
friend ostream& operator << (ostream& os, const Example& x)
{
os << x.getNums().size() << " ";
for (auto n : x.getNums())
{
os << n << " ";
}
assert(os.good());
return os;
}
friend istream& operator >> (istream& is, Example& x)
{
vector<int>::size_type size;
is >> size;
x.getNums().resize(size);
for (auto& n : x.getNums())
{
is >> n;
}
assert(is.good());
return is;
}
};
int main()
{
// Example 1
string store;
{
ostringstream outfile;
Example x( {1,2,3,4,5,4,3,2,1} );
outfile << x;
store = outfile.str();
}
{
istringstream infile(store);
Example x;
infile >> x;
std::cout << x.stringify() << std::endl;
}
// Example 2
{
ofstream outfile("myfile.txt");
Example x( {1,2,3,4,1,2,3,4} );
outfile << x;
}
{
ifstream infile("myfile.txt");
Example x;
infile >> x;
std::cout << x.stringify() << std::endl;
}
return 0;
}
This is a very broad topic and there are many ways of solving the problem. One popular way of doing class serialization now is using Google Protocol Buffers.