i have to serialize a CList of base and derived classes. How can i do that.
typedef CList<Student*, Student*> StVec;
class Student : public CObject
{
public:
DECLARE_SERIAL(Student);
Student();
Student(Student& s);
Student(CString _name, CString _last, int _age);
virtual ~Student(void);
virtual void cin();
virtual void cout();
virtual void Serialize(CArchive& ar);
static Student* FromInput();
inline const CString GetName(){return name;}
inline const CString GetLastName(){return last_name;}
inline const int GetAge(){return age;}
virtual inline Student& operator=( const Student &s )
{
name = s.name;
last_name = s.last_name;
age = s.age;
return *this;
}
protected:
CString name;
CString last_name;
int age;
};
class Warden : public Student
{
protected:
virtual void Serialize(CArchive& ar);
UINT salary;
};
class Group : public CObject
{
public:
DECLARE_SERIAL(Group);
enum FindType { First = 0, Last, All};
typedef bool (*StudentsFindCallback) (Student* student, void* condition);
Group(){ name = _T("Default Group"); }
Group(CString _name);
Group(Group& s);
~Group();
void CreateUser();
void ShowUsers();
//StVecOfIt FindUser(StudentsFindCallback f, void* condition, FindType ft);
inline Student* getStudent(POSITION pos) {return students->GetNext(pos); }
inline void DeleteUser(POSITION it){ students->RemoveAt(it); }
virtual void Serialize(CArchive& ar);
void Clean();
/*static bool FindbyName(Student* student, void* condition);
static bool FindbyLastName(Student* student, void* condition);
static bool FindbyAge(Student* student, void* condition);*/
virtual inline Group& operator=( const Group &s )
{
name = s.name;
students = s.students;
return *this;
}
protected:
CString name;
StVec* students;
};
void Student::Serialize(CArchive& ar)
{
CObject::Serialize( ar );
if (ar.IsStoring())
{
ar.SerializeClass(Student::GetRuntimeClass());
ar << name << last_name << age;
}
else
{
ar.SerializeClass(Student::GetRuntimeClass());
ar >> name >> last_name >> age;
}
}
void Group::Serialize(CArchive& ar)
{
ar.SerializeClass(RUNTIME_CLASS(Group));
if (ar.IsStoring())
{
ar << (int)students->GetCount();
POSITION pos = students->GetHeadPosition();
while(pos != NULL)
{
Student* student = students->GetNext(pos);
student->Serialize(ar);
}
}
else
{
Clean();
int count = 0;
ar >> count;
for(INT_PTR i = 0; i < count; ++i)
{
Student* st = new Student();
st->Serialize(ar);
students->AddTail(st);
}
}
}
I found an article at the codeproject but i'cant understend it at all.
Ofcourse i can add a flag which indicate the type of class. Also i can serialize to derived class and if the derived properties are empty use static_cast to derive it to base class. Or i'am thinking of using CArchive::ReadObject or CArchive::ReadClass to load classes, but is there another way to do it?
My MFC is rusty but I believe this should work (not tested!).
Also, your Serialize() method should never call SerializeClass for its own class. It's up to the caller to decide to call it. For instance, the << and >> operators do it to identify the class of the object that comes next in the archive.
void Student::Serialize( CArchive& ar )
{
CObject::Serialize( ar );
if (ar.IsStoring())
{ // No need for SerializeClass here.
ar << name << last_name << age;
}
else
{ // No need for SerializeClass here.
ar >> name >> last_name >> age;
}
}
void Group::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << (int)students->GetCount();
POSITION pos = students->GetHeadPosition();
while(pos != NULL)
{
Student* student = students->GetNext(pos);
ar << student; // << includes class info about the serialized object
}
}
else
{
Clean();
int count = 0;
ar >> count;
for(INT_PTR i = 0; i < count; ++i)
{
CObject *p;
ar >> p; // Deserialize whatever kind of object comes next
students->AddTail(p);
}
}
}
Related
I have an UserAcount class that has an abstract class ContBancar, and other class Banca which reads some users from a file (with method void Banca::citire_conturi()). When it reads the users, I get an error "Access violation writing location" in ContBancar at void setBal(double bal) { _balanta = bal; }. Thx for help !
PS : The file has only one line : 1CBS Dragos 0 dragos12! Gzpvia01= .
Also, i want to make a bank account system, with an user class that has an bank account class which inherits 3 types of a bank accounts, and a bank class which reads some users from a file or put them on it.
class UserAccount
{
private:
std::string _nume, _user, _pass;
std::string _cod_us;
std::shared_ptr <ContBancar> _cont;
public:
void setUser(std::string user) { _user = user; }
void setPass(std::string pass) { _pass = pass; }
void setNume(std::string nume) { _nume = nume; }
void setCodUs(std::string cod) { _cod_us = cod; }
void setContBal(double balanta) { (*_cont).setBal(balanta); }
std::string getUser() const { return _user; }
std::string getPass() const { return _pass; }
std::string getNume() const { return _nume; }
std::string getCodUs() const { return _cod_us; }
double getContBal() const { return (*_cont).getBal(); }
void setContBancar();
};
void UserAccount::setContBancar()
{
if (_cod_us == "1CBS")
_cont.reset(new ContBancarSilver());
else if (_cod_us == "2CBG")
_cont.reset(new ContBancarGold());
else
_cont.reset(new ContBancarDiamond());
}
class ContBancar
{
protected:
double _balanta;
public:
void setBal(double bal) { _balanta = bal; }
double getBal() { return _balanta; }
virtual bool depozitare(unsigned int) = 0;
virtual bool retragere(unsigned int) = 0;
};
class Banca
{
private:
std::vector<UserAccount> vec;
public:
void citire_conturi();
};
void Banca::citire_conturi()
{
std::ifstream file;
file.open("Baza_Date.txt");
UserAccount temp;
std::string cod, nume, user, pass;
double balanta;
while (file >> cod >> nume >> balanta >> user >> pass)
{
temp.setCodUs(cod);
temp.setNume(nume);
temp.setContBal(balanta);
temp.setUser(user);
temp.setPass(pass);
vec.push_back(temp);
}
file.close();
}
class ContBancarSilver : public ContBancar
{
private:
static constexpr unsigned int max_balanta = 5000;
static constexpr unsigned int max_depozitare = 2500;
static constexpr unsigned int max_retragere = 1000;
static constexpr double tax_retragere = 0.08;
static constexpr double bonus_depunere = 0.03;
static constexpr double bonus_tax_retragere = 0.05;
static constexpr unsigned int max_depozitari = 1;
static constexpr unsigned int max_retrageri = 1;
public:
virtual bool depozitare(unsigned int) override;
virtual bool retragere(unsigned int) override;
};
Based on available informationyou should fix your code like this:
class UserAccount
{
.....
void setCodUs(std::string cod) {
_cod_us = cod;
setContBancar();
}
void setContBal(double balanta) {
if (!_cont) setContBancar(); // lazy initialization
_cont->setBal(balanta);
}
...
};
void UserAccount::setContBancar()
{
if (_cod_us == "1CBS")
_cont = std::make_shared<ContBancarSilver>();
else if (_cod_us == "2CBG")
_cont = std::make_shared<ContBancarGold>();
else
_cont = std::make_shared<ContBancarDiamond>();
}
Note I do not understand what kind of logic you are implementing. This changes just ensured that _cont is initialized and up to date with _cod_us.
Please stop use explicitly new and delete. Everything can be created by std::make_shared and std::make_unique and containers like std::vector.
I am trying to initialize 2 dynamic arrays which class student will have an array containing all the courses registered and class course will have an array containing all the students registered to the class. I defined class Course and Student like this:
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
class Course;
class Student {
std::string name; // person’s name
int id; // person’s age
Course* courses;
int course_register; // number of organization registered to
public:
Student();
Student(const Student& s) {
name = s.getName();
id = s.getAge();
course_register = s.getorg_register();
}
Student(std::string n, int i) {
id = i;
name = n;
}
int getAge() const { return id; }
string getName() const { return name; }
int getorg_register() const { return course_register; }
};
class Course {
std::string name; // name of the org
Student* students; // list of members
int size; // actual size of the org
int dim; // max size of the org
public:
Course()
{
dim = 100;
students = new Student[dim];
};
Course(const Course& course)
{
name = course.getName();
dim = course.getDim();
size = course.getSize();
students = new Student[dim];
for (int i = 0; i < size; i++) {
students[i] = course.getStudent(i);
}
}
~Course()
{
delete[] students;
}
Student getStudent(int i) {return students[i];}
Student* getStudents() {return students;}
int getSize(){return size;}
int getDim() {return dim;}
string getName() {return name;}
};
Student::Student() {
courses = new Course[5];
}
When I try to compile, I get an exception unhandled at runtime for constructor Student::Student(). Can someone explain to me why I get a runtime error? And how would you change it to make it work?
A Student should contain a list that refers to the Courses it is enrolled in, it should not create the Courses themselves.
A Course should contain a list that refers to the Students enrolled in it, it should not create the Students themselves.
Does that make sense? In this case, refers means "use a pointer". For instance, Student could have a std::vector<Course*>, and Course could have a std::vector<Student*>. Then you can have a method that enrolls a Student into a Course, where a pointer to the Student is added to the Course's list, and a pointer to the Course is added to the Student's list, eg:
Student.h
#ifndef StudentH
#define StudentH
#include <string>
#include <vector>
class Course;
class Student {
std::string name;
int id;
std::vector<Course*> courses;
void addCourse(Course *c);
void removeCourse(Course *c);
friend class Course;
public:
Student(std::string n, int i);
Student(const Student& s);
Student(Student&& s);
~Student();
Student& operator=(Student rhs);
int getId() const;
string getName() const;
std::vector<Course*> getCourses() const;
void enroll(Course &c);
void drop(Course &c);
};
#endif
Student.cpp
#include "Student.h"
#include "Course.h"
Student::Student(std::string n, int i) {
name = n;
id = i;
}
Student::Student(const Student& s) {
name = s.name;
id = s.id;
for (Course *c : s.courses) {
c->enroll(*this);
}
}
Student::Student(Student&& s) {
name = std::move(s.name);
id = s.id; s.id = 0;
courses = std::move(s.courses);
for (Course *c : courses) {
c->removeStudent(&s);
c->addStudent(this);
}
}
Student::~Student() {
for(Course *c : courses) {
c->removeStudent(this);
}
}
Student& Student::operator=(Student rhs) {
Student temp(std::move(rhs));
for (Course *c : courses) {
c->removeStudent(this);
}
name = std::move(temp.name);
id = temp.id; temp.id = 0;
courses = std::move(temp.courses);
for (Course *c : courses) {
c->removeStudent(&temp);
c->addStudent(this);
}
return *this;
}
void Student::addCourse(Course *c) {
if (std::find(courses.begin(), courses.end(), c) == courses.end()) {
courses.push_back(c);
}
}
void Student::removeCourse(Course *c) {
auto iter = std::find(courses.begin(), courses.end(), c);
if (iter != courses.end())
courses.erase(iter);
}
}
int Student::getId() const {
return id;
}
string Student::getName() const {
return name;
}
std::vector<Course*> Student::getCourses() const {
return courses;
}
void Student::enroll(Course &c) {
c.enroll(*this);
}
void Student::drop(Course &c) {
c.drop(*this);
}
Course.h
#ifndef CourseH
#define CourseH
#include <string>
#include <vector>
class Student;
class Course {
std::string name;
std::vector<Student*> students;
void addStudent(Student *s);
void removeStudent(Student *s);
friend class Student;
public:
Course(std::string n);
Course(const Course& c);
Course(Course&& c);
~Course();
Course& operator=(Course rhs);
string getName() const;
std::vector<Student*> getStudents() const;
void enroll(Student &s);
void drop(Student &s);
};
#endif
Course.cpp
#include "Course.h"
#include "Student.h"
Course::Course(std::string n) {
name = n;
}
Course::Course(const Course& c) {
name = c.name;
for (Student *s : c.students) {
enroll(*s);
}
}
Course::Course(Course&& c) {
name = std::move(c.name);
students = std::move(c.students);
for (Student *s : students) {
s->removeCourse(&c);
s->addCourse(this);
}
}
Course::~Course()
{
for(Student *s : students) {
s->removeCourse(this);
}
}
Course& Course::operator=(Course rhs)
{
Course temp(std::move(rhs));
for (Student *s : students) {
s->removeCourse(this);
}
name = std::move(temp.name);
students = std::move(temp.students);
for (Student *s : students) {
s->removeCourse(&temp);
s->addCourse(this);
}
return *this;
}
void Course::addStudent(Student *s) {
if (std::find(students.begin(), students.end(), s) == students.end()) {
students.push_back(s);
}
}
void Course::removeStudent(Student *s) {
auto iter = std::find(students.begin(), students.end(), s);
if (iter != students.end())
students.erase(iter);
}
}
string Course::getName() const {
return name;
}
std::vector<Student*> Course::getStudents() const {
return students;
}
void Course::enroll(Student &s) {
addStudent(&s);
s.addCourse(this);
}
void Course::drop(Student &s) {
removeStudent(&s);
s.removeCourse(this);
}
Main.cpp
#include "Student.h"
#include "Course.h"
int main()
{
Course c("Math");
Student p("Joe", 12345);
p.enroll(c);
return 0;
}
You have infinite recursion. The Course constructor calls the Student constructor. The Student constructor calls the Course constructor. This continues until you've used up all the stack space.
You'll want to rethink your design of your two classes.
I have a base Class named Animals and 2 derived class Dog and Cat
class Animal{
protected:
std::string name;
std::string color;
public:
std::string getName() {
return name;
}
std::string getColor() {
return color;
}
...
class Cat : public Animal {
private :
int lives;
public :
int getLives() {
return lives;
}
...
class Dog : public Animal {
private :
std::string gender;
public:
std::string getGender(){
return gender;
}
...
and i have e vec of shared_ptr
std::vector<std::shared_ptr<Animal>> animals
I've added some cats and dogs in the vector and i am trying to print all the characteristics of each animal from vector ,using operator >>(this is a homework,we have to use this ) and i did this
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<std::shared_ptr<T>>& v)
{
for (int i = 0; i < v.size(); ++i) {
os << v[i]->getName();
os << "-";
os << v[i]->getColor();
if (i != v.size() - 1)
os << ", ";
os<<"\n";
}
return os;
}
but in this way i can print only the name and color or the animals(these atributes are in the base class)
My question is :
How can i print all the attributes ,for cats lives and for dogs gender???
Try this:
class Animal {
protected:
std::string name;
std::string color;
public:
virtual void print(std::ostream& os){
os << "Name:" << name <<" Color:"<< color;
}
void setName(string n) { name = n; }
void setColor(string c) { color = c; }
std::string getName() {
return name;
}
std::string getColor() {
return color;
}
};
class Cat : public Animal {
private:
int lives;
public:
void setLives(int n) { lives = n; }
void print(std::ostream& os) {
Animal::print(os);
os <<" Lives:"<<lives;
}
int getLives() {
return lives;
}
};
class Dog : public Animal {
private:
std::string gender;
public:
void setGender(string g) { gender = g; }
void print(std::ostream& os) {
Animal::print(os);
os << " Gender:" << gender;
}
std::string getGender() {
return gender;
}
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<std::shared_ptr<T>>& v)
{
for (int i = 0; i < v.size(); ++i) {
v[i]->print(os);
os << "\n";
}
return os;
}
int main()
{
shared_ptr<Dog> d1 = make_shared<Dog>();
d1->setName("Dog1"); d1->setColor("White"); d1->setGender("Male");
shared_ptr<Cat> c1 = make_shared<Cat>();
c1->setName("Cat1"); c1->setColor("Brown"); c1->setLives(1);
vector<shared_ptr<Animal>> vec;
vec.push_back(d1);
vec.push_back(c1);
cout << vec;
return 0;
}
I have two classes: Product and derived Juice. I need to implement MFC Serialazation for these classes.
class Product : CObject
{
protected:
DECLARE_SERIAL(Product) //IMPLEMENT_SERIAL(Product, CObject, 0) in .cpp
CString name;
int expiring;
double price;
public:
Product();
~Product();
virtual void input_data();
virtual void print_data();
virtual void Serialize(CArchive& archive)
{
CObject::Serialize(archive);
if (archive.IsStoring())
archive << name << expiring << price;
else
archive >> name >> expiring >> price;
};
};
class Juice :
public Product
{
private:
double volume;
CString taste;
public:
Juice();
~Juice();
void input_data() override;
void print_data() override;
void Serialize(CArchive& archive) override
{
Product::Serialize(archive);
if (archive.IsStoring())
archive << volume << taste;
else
archive >> volume >> taste;
}
};
To store objects of the classes I have Stock class which has container of Product class pointers.
class Stock
{
private:
vector<shared_ptr<Product>> stock;
public:
Stock();
~Stock();
void Add(shared_ptr<Product> p);
void Print();
bool Save(string fname);
bool Load(string fname);
void Clear();
};
In Save and Load methods I'm trying to implement serialazation (according to discussion in this topic C++ MFC Serialization).
bool Stock::Save(string fname)
{
CFile out;
if (!out.Open(fname.c_str(), CFile::modeWrite | CFile::modeCreate))
return false;
CArchive ar(&out, CArchive::store);
ar.WriteCount(stock.size());
for (auto it = stock.begin(); it != stock.end(); ++it)
{
(*it)->Serialize(ar);
}
ar.Close();
out.Close();
return true;
}
bool Stock::Load(string fname)
{
CFile in;
if (!in.Open(fname.c_str(), CFile::modeRead))
return false;
CArchive ar(&in, CArchive::load);
int cnt = ar.ReadCount();
for (int i = 0; i < cnt; i++)
{
auto p = make_shared<Product>();
p->Serialize(ar);
stock.push_back(p);
}
ar.Close();
in.Close();
return true;
}
Now I got a problem.
While reading objects from file Juice objects are read like Product (without volume ant taste fields). The reading of the object after Juice starts with the rest of Juice information, so I got CArchiveException in Serialaize method of Product.
If I use only Product objects to add to Stock everything works fine. What are my mistakes and what should I do to implement MFC serialization correctly?
Stock::Save needs to change to:
for (auto it = stock.begin(); it != stock.end(); ++it)
{
ar << (*it).get();
}
And Stock::Load needs to change to:
for (int i = 0; i < cnt; i++)
{
Product* obj = nullptr;
ar >> obj;
stock.emplace_back(obj);
}
When you use ar << obj, it saves type information with the object so it can be retrieved properly when you load it. Calling Serialize directly won't save the type data.
For reference, here's what the MFC serialization code looks like inside of CObArray (basically what you'd use instead of a vector if you stuck to MFC only)
if (ar.IsStoring())
{
ar.WriteCount(m_nSize);
for (INT_PTR i = 0; i < m_nSize; i++)
ar << m_pData[i];
}
else
{
DWORD_PTR nOldSize = ar.ReadCount();
SetSize(nOldSize);
for (INT_PTR i = 0; i < m_nSize; i++)
ar >> m_pData[i];
}
A program that stores a phone company's consumers data in a linked list. At the end it displays the bill for each human. I have the following codes:
class BaseTypeOfContract
{
private:
int minutePrice;
int SMSPrice;
public:
void setminutePrice(int x) { minutePrice = x; }
void setSMSPrice(int x) { SMSPrice = x; }
virtual int calculateBill(int talkedMinutes, int sentSMS) = 0;
int getminutePrice() const { return minutePrice; }
int getSMSPrice() const { return SMSPrice; }
};
class SMSBaseType : public BaseTypeOfContract
{
private:
int freeSMS;
public:
SMSBaseType(int minutePrice, int SMSPrice, int freeSMS)
{
setminutePrice(minutePrice);
setSMSPrice(SMSPrice);
setfreeSMS(freeSMS);
}
public:
void setfreeSMS(int free) { this->freeSMS = free; }
virtual int calculateBill(int talkedMinutes, int sentSMS)
{
int billedSMS = (freeSMS > sentSMS) ? 0 : sentSMS - freeSMS;
return talkedMinutes * getminutePrice() + billedSMS * getSMSPrice();
}
};
class Base : public BaseTypeOfContract
{
public:
Base()
{
setminutePrice(30);
setSMSPrice(10);
}
virtual int calculateBill(int talkedMinutes, int sentSMS) { return talkedMinutes * getminutePrice() + sentSMS * getSMSPrice();}
};
class SMSMax : public SMSBaseType
{
public:
SMSMax() : SMSBaseType(20, 5, 150) {}
};
class MobiNET: public SMSBaseType
{
public:
MobiNET() : SMSBaseType(10, 15, 25) {}
};
Client's class:
class Client
{
public:
std::string name;
std::string phoneNumber;
BaseTypeOfContract* typeOfContract;
int talkedMinutes;
int sentSMS;
Client *next;
public:
Client(){}
Client(std::string n, std::string p, int bp, int ks) : name(n), phoneNumber(p), talkedMinutes(bp), sentSMS(ks) {}
void preSetPlan(std::string s)
{
if (s == "MobiNET")
this->typeOfContract = new MobiNET();
else if (s == "SMSMax")
this->typeOfContract = new SMSMax();
else this->typeOfContract = new Base();
}
std::string getname() const { return name; }
std::string getphoneNumber() const { return phoneNumber; }
void setname(std::string n) { name = n; }
void setphoneNumber(std::string pn) { phoneNumber = pn; }
void settalkedMinutes(int bp) { talkedMinutes = bp; }
void setsentSMS(int SSMS) { sentSMS = SSMS; }
int getBill() const { return this->typeOfContract->calculateBill(talkedMinutes, sentSMS); }
};
I read the data from 2 files. First file contains the name, phone number, type of contract. Second file contains the phone number, talked minutes and sent SMS.
Client* file_read_in()
{
std::ifstream ClientData;
ClientData.open("clients.txt");
Client *first = new Client;
first = NULL;
while (!ClientData.eof())
{
std::string name, phoneNumber, typeOfContract;
ClientData >> name;
ClientData >> phoneNumber;
ClientData >> typeOfContract;
std::ifstream ClientTalkedSent;
ClientTalkedSent.open("used.txt");
while(!ClientTalkedSent.eof())
{
std::string phoneNumber2;
ClientTalkedSent >> phoneNumber2;
if (phoneNumber2 == phoneNumber)
{
int talkedMinutes, sentSMS;
ClientTalkedSent >> talkedMinutes;
ClientTalkedSent >> sentSMS;
Client* tmp = new Client(name, phoneNumber, talkedMinutes, sentSMS);
tmp->preSetPlan(typeOfContract);
tmp->next = NULL;
if (first == NULL)
{
first = tmp;
}
else
{
Client *cond = first;
while (cond->next != NULL) cond = cond->next;
cond->next = tmp;
}
}
}
ClientTalkedSent.close();
}
ClientData.close();
return first;
}
And the main:
int main()
{
Client* first = file_read_in();
while(first != NULL)
{
std::cout << first->getname() << " " << first->getphoneNumber() << " " << first->getBill() << std::endl;
first = first->next;
}
return 0;
}
My problem that I should free the allocated memory but I got on idea how. Which class' destructor should do the dirty job. I would appreciate if someone could use my code, to show how the "destructor inheritance" works.
Sorry for my bad english and thanks for the help. This site helped me alot of times, but for this problem I did not find a solution.
If you have a pointer BaseTypeOfContract* typeOfContract; that is used to point to different derived classes, then BaseTypeOfContract needs to have a virtual destructor for delete typeOfContract to work.
And as Client seems to create the objects pointed to, it also ought to be responsible for cleaning them up. Either by using delete typeOfContract; in its destructor, or by storing a smart pointer to get the work done automatically.
The other part is that each Client stores a pointer to the next Client. That seems like not the best design. In real life it is not at all like each person knowing who is the next person that buys a cell phone in the same store. :-)
You would be much better of with a container, like std::vector<Client>, that would also handle the lifetime of the Client objects.