classes object oriented programming help

This program has a lot of errors how do I do it?

The problem is to develop a billing system of a hostpital using a persontype base class

There's the other classes of doctortype which stores the doctor's specialty
then there's patienttype and datetype and billtype

a sample input is

fname lname id docName docspecialty admit discharge fees room
date date fees
xxxx yyyy 12222 James Flower Internal Medicine 12/13 12/24 1000 1200

My program
#include<iostream>
#include<fstream>
#include<string>
#include<iomanip>
#include<ctime>

using namespace std;

class PersonType
{
private:
string firstName;
string lastName;

public:
PersonType(string= " ", string = " "); // default constructor
void setName(string, string);
string getFirstName();
string getLastName();
void print(ofstream&); // ??? don't know if this is correct?
};

PersonType::PersonType(string f, string l)
{
setName(f,l);
}
void PersonType::setName(string f, string l)
{
firstName=f;
lastName=l;
}

string PersonType::getFirstName()
{
return firstName;
}
string PersonType::getLastName()
{
return lastName;
}

void PersonType::print(ofstream & fout)
{
fout<<firstName<<" "<<lastName;
}

class DoctorType: public PersonType
{
private:
PersonType doctor;
string doctorFirstName;
string doctorLastName;

string specialty;
public:
DoctorType(string=" ", string=" ");
void setDoctorSpecialty(string);
string getDoctorSpecialty();
void print (ofstream&);
};

DoctorType::DoctorType(string f, string l):PersonType(f,l)
{
//setName(string f, string l);
//PersonType(string f, string l);
specialty=" ";

}

void DoctorType::setDoctorSpecialty(string s)
{
/*doctorFirstName=f;
doctorLastName=l;*/
specialty=s;
}
string DoctorType::getDoctorSpecialty()
{
return specialty;
}


void DoctorType::print(ofstream& fout)
{

PersonType::print(fout);
fout<<"specializes in: "<<specialty;
}
class PatientType: public PersonType
{
private:
string patientID;
string dateOfBirth;
string attendingDoctor;
string dateOfAdmission;
string dateOfDischarge;
int age;


public:
PatientType();
void setPatientInfo(string=" ", int=0, string=" ", string=" ", string=" ");
void print(ofstream&);
};
// default constructor
PatientType::PatientType()
// to include or not to include? :DateType(b, a, d)
{
patientID="000000";
dateOfBirth="00/00/0000";
dateOfAdmission="00/00/0000";
dateOfDischarge="00/00/0000";
attendingDoctor="**** **** ";
}
void PatientType::setPatientInfo(string i, int age, string b, string a, string d)// :DateType(string b, string a, string d):BillType( string i, string d, string r)
{
patientID=i;
age=" " // find later
dateOfBirth=b;
dateOfAdmission=a;
dateofDischarge=d;
// attendingDoctor=
}

void PatientType::print(ofstream & fout)
{
fout<<"Patient ID: "<<patientID<<"age: "<< age <<"date of birth: "<<b<<"date of admission: "<<a
<<"date of discharge: "<<d;
}


class DateType


{
private:
string dateOfBirth;
string dateOfAdmission;
string dateOfDischarge;

public:
DateType(string="0/0/0000 ", string="0/0/0000 ", string="0/0/0000");
void setDate(string, string, string);
string getDateOfBirth();
string getDateOfAdmission();
string getDateOfDischarge();
void print(ofstream&);

};
DateType::DateType(string b, string a, string d)
{
setDate(b,a,d);
}
DateType::setDate(string b, string a, string d)
{
dateOfBirth=b;
dateOfAdmission=a;
dateOfDischarge=d;
}

string DateType::getDateOfBirth()
{
return dateOfBirth;
}
string DateType::getDateOfAdmission()
{
return dateOfAdmission;
}
string DateType::getDateOfDischarge()
{
return dateOfDischarge;
}

class BillType:
{
private:
string patientID;
string doctorFees;
string roomFees;
public:
BillType(string=" ", string=" ", string=" ");
setIDAndFees( string i, string d, string r);
getPatientID();
getDoctorFees();
getRoomFees();
print(ofstream&)
};
BillType::BillType(string, string, string)
{
setIDAndFees(string i, string d, string r)

}
void BillType::setIDAndFees(string i, string d, string r)
{
patientID=i;
doctorFees=d;
roomFees=r;
}

string BillType::getPatientID()
{
return patientID;
}
string BillType::getDoctorFees()
{
return doctorFees;
}
string BillType::getRoomFees()
{
return roomFees;
}
void BillType::print(ofstream & fout)
{
fout<<"The Patient's ID is: "<< patientID<<"Doctor Fees: "<<doctorFees<<"Room Fees: "<<roomFees;
}
int main ()
{

// declare an object
// ifstream fin("person.txt");

PatientType abi;

ifstream fin;
ofstream fout;

string firstName, lastName, patientID;
string dateOfBirth, dateOfAdmission, dateOfDischarge;
string specialty;
fout.open("output.txt");
fin.open("person.txt");

fin>>firstName>>lastName>>patientID>>dateOfBirth>>doctor>>specialty>>dateOfAdmission>>dateOfDischarge;

abi.setPatientInfo(string patientID, string age, string dateOfBirth, string dateOfAdmission, string dateOfDischarge);
// abi.print(fout);
abi.setDoctorInfo(string specialty);
abi.print(fout);
fin.close();
fout.close();
return 0;
//system("pause");
}
Last edited on
To understand classes you need to understand encapsulation. The whole idea is to design a singular "thing" (a class) that can be easily used, but not easily misused. The class maintains its own internal state that cannot be messed with by outside code. Ensuring that it's always stable.

To illustrate this, I'm going to go make a poorly-encapsulated class which wraps around stdio's FILE* structure. Each step I will illustrate why it has flaws, and how it can be improved by strengthening the encapsulation. In the end we'll have a fully encapsulated class which will be as solid as possible.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class File
{
public:
    FILE* theFile;      // pointer to the FILE structure if a file is opened, null otherwise

    bool Open(const char* filename)
    {
        if(IsOpen())                    // if we already have a file open...
            Close();                    //  ... close it first
        theFile = fopen(filename,"rb"); // then open the new file
        return (theFile != 0);          // returns true if successful
    }
  
    void Close()
    {
        if(IsOpen())        // if the file is open
        {
            fclose(theFile);    // close it
            theFile = 0;        // reset our pointer to null so we know it is now closed
        }
    }
    
    bool IsOpen()
    {
        return (theFile != 0);
    }
    
    int Read(void* buffer, int size)
    {
        if(IsOpen())
            return fread(buffer,1,size,theFile);
        else
            return 0;
    }
};


An example of how this class might be used is like this:

1
2
3
4
5
6
7
File myfile;
myfile.Open("foo.bin");

char somebytes[10];
myfile.Read(somebytes,10); // read 10 bytes from the file

myfile.Close();


Seems simple enough, right?

However, it has several problems.

Problem #1: its internal state is exposed
The class relies on its 'theFile' member to keep track of its internal state (whether or not the file is opened, which file it's dealing with). Since this member is public, it means anybody can mess with it, which is BAD because it allows for people to screw up and break the class. Example:

1
2
3
4
5
File myfile;
myfile.Open("foo.bin");
myfile.theFile++;  // mess with the pointer... now theFile is a bad pointer

myfile.Close();  // !!!!!EXPLODE!!!!! 


To prevent people from doing this (intentionally or accidentally), this can be solved by making 'theFile' private. This is the core concept of encapsulation: restrict access to critical information.

1
2
3
4
5
6
7
8
class File
{
private:
    FILE* theFile;
    
public:
    bool Open(const char* filename)
    //... 


With that, we've fixed that problem. But there's more problems:

Problem #2: its initial state is undefined
Say the user does something like this:

1
2
3
4
5
File myfile;
if( myfile.IsOpen() )
{
    // ...
}


IsOpen looks at theFile to determine whether or not a file has been opened. But since the only function that modifies theFile is Open, and Open was never called... that means theFile has never been set, and therefore it is unitialized! As a result, IsOpen might return true even though no file is opened!


To solve this, we use a constructor. Constructors are just functions that are automatically called when the object is created. Here's a basic constructor that will solve this problem:

1
2
3
4
5
6
7
8
9
10
class File
{
//...
public:
    File()
    {
        theFile = 0;
    }
//...
};


Now... if we try that same problem code again...
1
2
File myfile;  // this automatically calls the constructor, initializing 'theFile' to 0
if( myfile.IsOpen() )  // so we are now certain that IsOpen will return false, as it should 



Problem #3: it doesn't clean up after itself

What if the user forgets to close the file? Example:

1
2
3
4
5
6
7
8
9
void SomeFunc()
{
    File myfile;
    myfile.Open("foo.bin");
    
    myfile.Read(blah,10);
    
    // whoops, forgot to Close() the file
}


If this happens, the file remains open forever (or really, for the lifetime of the program). This puts an unnecessary strain on file I/O for the entire computer, and consumes extra memory. If 'SomeFunc' is called a few thousand times during the lifetime of the program, this might result is substantial memory leaks, and possibly even program crashes!

To solve this problem, we use a Destructor. Destructors are just functions that are automatically called when an object dies (goes out of scope). A basic destructor:

1
2
3
4
    ~File()
    {
        Close();        // automatically close the file when this object dies
    }


Now we're OK. That same problem code:

1
2
3
4
5
6
void SomeFunc()
{
    File myfile;
    //...
    // forgot to Close()
} // <- destructor called here.  So file is automatically closed 



Problem #4: it doesn't copy correctly

What if the user tries to do this:

1
2
3
4
5
6
7
8
9
10
{
    File a;
    a.Open("foo.bin");
    {
        File b = a;  // copy the file object
          // now 'b.theFile' points to the same FILE that 'a.theFile' points to
    }// <- b's destructor called, closing the file
    
    a.Read(foo,10); // FAIL OR EXPLODE, because the file has been closed!
}


There are a number of solutions to this problem. One solution would be to record the filename and have 'b' reopen the file on its own so that each object has a unique FILE. But the simplest would be to just forbid copying altogether... so let's do that.

When you copy an object, C++ calls a special "copy constructor". If you make that copy constructor private, then nobody outside the class can access it. If they try to copy it (intentionally or on accident), it will give a compiler error, rather than crash the program at runtime.

The same thing also happens with the assignment (=) operator. So we need to make that private as well. The general rule is known as the "rule of 3". If you need to write your own destructor, copy constructor, or assignment operator... then it's almost certain that you need to write ALL THREE of them. So with them it's usually all or nothing.

However, since we are just forbidding copying, we don't need to actually write code for the copy constructor / assignment operator. We just have to make them private:

1
2
3
4
5
6
7
class File
{
//...
private:
    File(const File&);              // private copy constructor
    void operator = (const File&);  // private assignment operator
};


Now that problem is solved! The same problem code:

1
2
3
4
File a;
a.Open("foo.bin");
{
    File b = a;  // COMPILER ERROR, 'b's copy constructor is private 




So what we're left with after all that is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class File
{
private:
    FILE* theFile;      // pointer to the FILE structure if a file is opened, null otherwise

public:
    File()
    {
        theFile = 0;
    }
    
    ~File()
    {
        Close();
    }

    bool Open(const char* filename)
    {
        if(IsOpen())                    // if we already have a file open...
            Close();                    //  ... close it first
        theFile = fopen(filename,"rb"); // then open the new file
        return (theFile != 0);          // returns true if successful
    }
  
    void Close()
    {
        if(IsOpen())        // if the file is open
        {
            fclose(theFile);    // close it
            theFile = 0;        // reset our pointer to null so we know it is now closed
        }
    }
    
    bool IsOpen()
    {
        return (theFile != 0);
    }
    
    int Read(void* buffer, int size)
    {
        if(IsOpen())
            return fread(buffer,1,size,theFile);
        else
            return 0;
    }
    
private:
    File(const File&);              // private copy constructor
    void operator = (const File&);  // private assignment operator
};


This is a fully encapsulated class that is simple to use... and next to impossible to break.
Last edited on
Topic archived. No new replies allowed.