-1

I'm making a program that keeps track of different employees. Some of the employees have partners (wifes and husbands), so all of the Employee objects have a data member called "Partner* partner" (a pointer to a Partner object).

My problem comes when I want to write an Employee to a file. I can successfully write all of the Employee data (name, address, birth date etc.) to the file, but I don't know how to write the partner to file. I have a function in the Partner class called "writeToFile" which outputs all of the partner data, but I don't know how to "connect" it to the correct Employee object. I tried to just output the "partner"-object to the end of the file, but that just added a bunch of zeros.

Should I use two separate files (one for employees and one for partners), or should I just append the partner data to the employee data? Wouldn't that mess up the file structure when reading it back in, since only some of the employees have partners and some of the partner objects just point to NULL?

My classes inherits each other, so both the Partner and Employee class inherits the Adult class, which again inherits the Person class.

Can anyone give me a "pointer" to what is the best way of writing an object which has a pointer to another object inside it? Here's my temporary code btw, if it is of any interest:

#include  <iostream>
#include  <fstream>
#include  <cstring>
#include  <cctype> 
#include  <cstdlib>
using namespace std;

const int MAXTXT = 80;

class Person {     
    protected:
        char* firstname;   
        char  birthdate[6]; 
    public:
        Person() {
            char fname[MAXTXT];
            cout << "First name: "; cin.getline(fname, MAXTXT);
            firstname = new char[strlen(fname + 1)];
            strcpy(firstname, fname);
            cout << "Birth date (DDMMYY): ";
            cin >> birthdate; cin.ignore();
            }
        void display() {
            cout << "\nFirst name: " << firstname;
            cout << "\nBorn: " << birthdate;
            }
        void writeToFile(ofstream & ut) {
            ut << firstname << "\n" << birthdate;
            }
        };

class Adult: public Person {
    protected:
        char* lastname;
    public:
        Adult() {
            char lname[MAXTXT];
            cout << "Last name: "; cin.getline(lname, MAXTXT);
            lastname = new char[strlen(lname + 1)];
            strcpy(lastname, lname);
            }
        void writeToFile(ofstream & out) {
            out << "\n" << lastname << "\n";
            }
        void display() {
            cout << "\nLast name: " << lastname;
            }
        };

class Partner: public Adult {
    private:
        int phone1;
        int phone2;
    public:
        Partner() {
            cout << "Phone (mobile): "; cin >> phone1;
            cout << "\nPhone (job): ";  cin >> phone2; cin.ignore();
            }
        void writeToFile(ofstream & out) {
            Person::writeToFile(out);
            Adult::writeToFile(out);
            out << "\n" << phone1 << " " << phone2;
            }
        void display() {
            Person::display();
            Adult::display();
            cout << "\nPhone (mobile): " << phone1;
            cout << "\nPhone (job): " << phone2;
            }
        };

class Employee: public Adult {      
    private:
        int      nr;                    
        char*    address;               
        Partner* partner;               
    public:
        Employee() {
            }
        Employee(int n) {
            char adr[MAXTXT];
            nr = n;
            cout << "Address: "; cin.getline(adr, MAXTXT);
            address = new char[strlen(adr + 1)];
            strcpy(address, adr);
            partner = NULL;
            }
        void changePartner() {
            Partner::Partner();
            }
        void writeToFile(ofstream & out) {
            Person::writeToFile(out);
            Adult::writeToFile(out);
            out << nr << "\n" << address << endl;
            }
        void display() {
            Person::display();
            Adult::display();
            cout << "\nAddress: " << address;
            if(partner) {
                partner->display();
                }
            }
        int returnEmpNr() {
            return nr;
            }
        };

Employee* employees[100];
int lastUsed = 0;

int main() {

    }

void writeToFile() {
    ofstream outfile("EMPLOYEES.DAT");
    ofstream outfile2("PARTNERS.DAT");
    outfile << lastUsed << "\n";
    for(int i = 1; i <= lastUsed; i++) {
        employees[i]->writeToFile(outfile);
        }
Andreas BH
  • 9
  • 2
  • 6
  • 3
    You are going to have to make your classes serializeable. – NathanOliver Mar 01 '16 at 17:42
  • OT: Any reasons why you avoid `std::string` for names and don't use some kind of `std::shared_pointer`/`std::unique_ptr` for the partners? – Simon Kraemer Mar 01 '16 at 17:56
  • You can add an ID member (an int maybe) to Person class (database like...). You are also assuming (monogamy apart) that a partner can't be an Employee too. BTW, consider using `std:vector`, `std::map`, `std::string`... – Bob__ Mar 01 '16 at 19:49
  • @SimonKraemer because his professor hasn't gotten to covering the standard library yet, I'm sure. – Rob K Mar 01 '16 at 20:37

1 Answers1

0

A pointer is meaningless except to a single run of a program and could very well be meaningless by the time the file is read if the pointer at value has gone out of scope. The odds of the same Partner being in the same spot in memory, assuming space has even been allocated for it, the next time around could be as bad as 1 in 18,446,744,073,709,551,616 and being wrong has often fatal results. And those fatal results mean you got lucky. You could smash perfectly valid memory that belongs to something else, and results in behaviour that is weird, undefined, and much harder to debug than an outright crash.

In C++ pointers are often a sucker bet. Use them as a last resort because they can really ramp up the amount of code you need to write.

I recommend two lists (but not necessarily two files. Both lists can easily exist in one file) one of Partners and one of Employees. The Partners don't seem to need to know about the Employees, so save yourself some trouble and write out and read in the partner list first.

When writing the Employee list, don't store the Partner pointer because it won't work. Instead store the index of the Partner in the Partner list. Then when you read the Employee list, you can read the Partner's position in the Partner table look up that Partner, and point to them. This is why it's much easier to write and read the Partners first; it is hard to look up data in a list that hasn't been read yet.

Or ditch the concept of Partner pointers entirely and always use the index to look the Partner up. This is much safer approach, so long as you always append new partners to the list and never insert into the list or do something silly like shuffle the list.

While we're messing with pointers, take a good solid read of "What is The Rule of Three?" because you are heading into a quagmire of memory management carnage.

As written every time you copy an Employee, you will get another Employee that is copied stupidly. It copies the pointers, not the content, so you now have 2 Employee objects pointing to the same names and Partners. When you free the memory allocated to the partner and names, which you don't and really, really should, the other copy is pointing at memory that your program no longer owns and becomes a ticking time bomb just waiting to crash your program.

Using a std::vector<Partner> (note it is a vector of Partner, not Partner *. This allows the vector to own and manage all of the memory for you and not just the memory needed by the list) and indexes clears up the problem with Partners, because the std::vector manages the memory for you, but the names are still waiting to kill the program. Replacing the char *s with std::string will solve that problem, also by moving the memory management burden from your code to std::string where it was written for you some time in the 1990s and has been under use in millions of programs ever since. Not many bugs left to trip over after 20 years.

If you have to use pointers and arrays of pointers, you need to make custom destructors, copy constructors, move constructors, assignment operators and move operators. That's a lot of extra work.

But those vectors and strings contain pointers and you have to handle writing them carefully. This topic, serialization, has been beaten to death elsewhere, so here's a link to a reputable site that can probably handle it better than I can.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54