// Following are the Interfaces and Implementations for two classes // InfoRecord is meant for student record objects. There is a data element // for each piece of information about a student. The associated methods // make it possible to initialize, input, output, assign, and compare such // objects. class InfoRecord { private: string name; // Student's name int id; // Student's ID number float gpa; // Grade Point Average int level; // Class level string major; // Student's major public: InfoRecord(void) { } // Constructors InfoRecord (string n, int i, float g, int l, string m): name(n), id(i), gpa(g), level(l), major(m) { } InfoRecord (char * n, int i, float g, int l, char * m): name(n), id(i), gpa(g), level(l), major(m) { } ~InfoRecord (void) // Destructor { } InfoRecord(const InfoRecord &rhs); // Copy constructor int operator< (const InfoRecord &rhs) // Less than based on name strings { return name < rhs.name; } const InfoRecord & operator= (const InfoRecord &); // Assignment bool FileGet (istream &); // Read in a record void FilePut (ostream &); // Write a record bool Valid (void); // Making sure data is valid friend istream & operator>> (istream &, InfoRecord &); friend ostream & operator<< (ostream &, const InfoRecord &); friend class DataBase; }; // Copy constructor // Copy fields of object InfoRecord::InfoRecord (const InfoRecord &rhs) { name = rhs.name; gpa = rhs.gpa; id = rhs.id; level = rhs.level; major = rhs.major; } // Assignment // Copy fields of right-side object to left-side object // Precondition: Source and Destination should be different const InfoRecord & InfoRecord::operator= (const InfoRecord & rhs) { if (this != &rhs) // Make sure source is not destination { name = rhs.name; gpa = rhs.gpa; id = rhs.id; level = rhs.level; major = rhs.major; } return *this; } // Read in the elements of an InfoRecord // Precondition: Input stream should be open and not failed // Postcondition: All fields of object are filled in with // the name built from separate strings and // padded with spaces to 21 total characters bool InfoRecord::FileGet (istream &dataIn) { string first; // Holds the first name string last; // Holds the last name int nameSize; // Length of name getline (dataIn, first,','); // Read first name and make sure it is ok if (dataIn.fail()) return false; dataIn >> last; // Read last name, combine names name = first + ", " + last; nameSize = name.length(); for (;nameSize < 21; nameSize++) // Pad to 21 characters name = name + ' '; dataIn >> id >> gpa >> level; // Get id, gpa, and level dataIn >> ws; getline (dataIn, major); // Read rest of line as major if (dataIn.fail()) // If anything went wrong, return false return false; else return true; } // Output contents of InforRecord in a standard format // Precondition: Object must have valid values // Postcondition: Standard output form of fields void InfoRecord::FilePut (ostream &saveData) { saveData << name << setw(5) << id << fixed << showpoint << setprecision(2) << setw(6) << gpa << setw(3) << level << " " << major; saveData << endl; } // Check the validity of the object - based on id only bool InfoRecord::Valid(void) { return id >= 0; } // Friend function for input of an InfoRecord // Precondition: Input stream must be cin; source must be keyboard // Postcondition: Fields of InfoRecord are filled in istream & operator>> (istream &inputdata, // Stream to read from InfoRecord &one) // Object to fill in { int letter; // No. letters in name string junk; // To discard incorrect input cout << "\nName: "; // Prompt for and read in name getline (inputdata, one.name); letter = one.name.length(); // Determine length and pad with blanks for (; letter < 21; letter++) one.name = one.name + ' '; do { // Prompt for and read in ID cout << "ID number? "; if (inputdata.fail()) // Make sure we get a valid one { inputdata.clear(); getline(inputdata, junk); } inputdata >> one.id; } while (inputdata.fail()); do { // Prompt for and read in a GPA cout << "GPA? "; if (inputdata.fail()) // Make sure we get a valid one { inputdata.clear(); getline(inputdata, junk); } inputdata >> one.gpa; } while (inputdata.fail()); do { // Prompt for and read in a class level cout << "Level (1-4)? "; if (inputdata.fail()) // Make sure we get a valid one { inputdata.clear(); getline(inputdata, junk); } inputdata >> one.level; } while (inputdata.fail()); getline(inputdata, junk); cout << "Major? "; // Prompt for and read in the major getline (inputdata, one.major); cout << endl; return inputdata; } // Friend function to output the fields of an InfoRecord // Precondition: The fields must be valid; it is generally assumed // that the output stream is to the screen // Postcondition: The fields are displayed in standard form ostream & operator<< (ostream &out, const InfoRecord &one) { char cname[5][3] = {" ", "Fr", "So", "Jr", "Sr" }; out << one.name // Show name as 21 chars << setw(6) << one.id // Show id // Show gpa columnated << showpoint << fixed // with 2 decimal places << setprecision(2) << setw(6) << one.gpa << " "; out << cname[one.level] << " " // Show equivalent mnemonic for level << one.major; // Show major return out; } // ------------------------------------------------------------------------- // DataBase is meant for objects that are collections of InfoRecords. The // collections may be of any size. The associated methods make it possible // to add and delete InfoRecords from the DataBase, to do input and output on // the DataBase, to search the DataBase for an InfoRecord based on either of // two data elements of an InfoRecord, to access indivicual InfoRecords using // an associative approach (with specification by data element value). class DataBase { private: InfoRecord *dBase; // Pointer to array of InfoRecord int size; // Number of elements in the array int count; // Number of used elements DataBase (const DataBase &rhs) // Prevent use of copy constructor { } void GetArray(void) // Get space for expanded database { dBase = new InfoRecord [size]; } void Extend (void); // Increase database size const int Find (const string, bool &) const; // Search based on name const int Find (const int, bool &) const; // Search based on id public: DataBase (int); // Constructor initializing size ~DataBase (void); // Destructor InfoRecord operator[] (const int) const; // Associative access with id InfoRecord IdAccess (const int) const; InfoRecord operator[] (const string) const; // Associative access with name InfoRecord NameAccess (const string) const; void Rank (void); // Display ranking based on GPA bool Open (void); // Open database and fill from file void Close (void); // Close database and allow file update const DataBase & operator+= (const InfoRecord &); // Add to database const DataBase & Add (const InfoRecord &); const DataBase & operator-= (const int); // Delete based on id const DataBase & Delete (const int); const DataBase & operator-= (const string); // Delete basec on name const DataBase & Delete (const string); friend ostream & operator<< (ostream &, const DataBase &); }; // Constructor // Precondition: len should be > 0; if not size is 1 DataBase::DataBase (int len) { size = (len > 0) ? len : 1; // Set database size (default is 1) count = 0; // Show no entries GetArray (); // Acquire space } // Destructor - return all space occupied DataBase::~DataBase (void) { delete [] dBase; } // Indexed access based on associative use of name field // Precondition: what should have the string value of a name // Postcondition: Returns an InfoRecord containing the specified name // or an invalid InfoRecord InfoRecord DataBase::operator[] (const string what) const { int which; // Array index where found bool found; // Indicator of name being found InfoRecord junk ("",-1,0.0,0,""); // Invalid record which = Find(what, found); // Search for the name if (found) return dBase[which]; // Return the valid entry else return junk; // Return invalid entry } // Access to a database entry based on name // Precondition: what should have the string value of a name // Postcondition: Returns an InfoRecord containing the specified name // or an invalid InfoRecord InfoRecord DataBase::NameAccess (const string what) const { return (*this)[what]; } // Indexed access based on associative use of id field // Precondition: what should have the value of an id // Postcondition: Returns an InfoRecord containing the specified id // or an invalid InfoRecord InfoRecord DataBase::operator[] (const int what) const { int which; // Array index where found bool found; // Indicator of id being found InfoRecord junk ("",-1,0.0,0,""); // Invalid record which = Find(what, found); // Search for the id if (found) return dBase[which]; // Return the valid entry else return junk; // Return invalid entry } // Access database entry based on an id // Precondition: what should have the value of an id // Postcondition: Returns an InfoRecord containing the specified id // or an invalid InfoRecord InfoRecord DataBase::IdAccess (const int what) const { return (*this)[what]; } // Private function to do sequential search of database for a name // If only a prefix of an existing name is given as target, it // will be found also. // Precondition: what must be a valid string // Postcondition: success indicates if search was successful // returned integer is index to array entry of found entry const int DataBase::Find (const string what, // Name to search for bool &success) const // Search outcome { int record=0; // Record number being checked int theLength = what.length(); // Length of the string to search for // Loop to check until no more or possibly found while ((record < count) && (what > dBase[record].name.substr(0,theLength)) ) record++; if (what == dBase[record].name.substr(0,theLength)) // If found, set indicator success = true; else success = false; return record; // Return array position where stopped } // Private function to do sequential search of database for an ID // Postcondition: success indicates if search was successful // returned integer is index to array entry of found entry const int DataBase::Find (const int what, // ID to search for bool &success) const // Search outcome { int record=0; // Record number being checked // Loop to check until no more or found while ((record < count) && (what != dBase[record].id)) record++; if (record < count) // If found, set indicator success = true; else success = false; return record; // Return array position where stopped } // Display all records in the database in descending order based on GPA // Precondition: Database must contain some data and be consistent // Postcondition: Display of data in GPA order without disturbing current // order; an "indexed" sort is used void DataBase::Rank (void) { int record; // Record number bool exch; // Exchange indicator to control sort int limit; // Controls inner sort loop int temp; // Holds an entry index during an exchange int *index; // Pointer to the index array index = new int[count]; // Create the index array for (record = 0; record < count; record++) index[record] = record; exch = true; // Initialize exchange flag and limit limit = count - 1; while (exch) // Loop as long as an exchange occurs { exch = false; for (record = 0; record < limit; record++) // Compare GPAs if (dBase[index[record]].gpa < dBase[index[record+1]].gpa) { temp = index[record]; // Exchange index entries index[record] = index[record+1]; index[record+1] = temp; exch = true; } limit--; // Reduce no. records to consider } cout << "Name GPA\n"; // Display in GPA order for (record = 0; record < count; record++) cout << dBase[index[record]].name << showpoint << fixed << setw(5) << setprecision(2) << dBase[index[record]].gpa << endl; } // Read the records from the data file into the database // Precondition: The data file must exist, be in the right form, // and contains some records. The records are assumed // to be in alphabetical order by name. // Postcondition All file data is stored in the database object bool DataBase::Open (void) { // Read in the records InfoRecord one; // Holds one record read in char filespec[40]; // Data file name fstream dataIn; // Input stream for the file cout << "Enter input file spec: "; // Prompt for and read file name cin.getline(filespec, 39); dataIn.open(filespec, ios::in); // Open the file if (dataIn.fail()) // and make sure it is OK { cout << "Problem opening data file" << endl; return false; } while (one.FileGet(dataIn)) // Get an InfoRecord { if (count == size) // When size is reached, expand Extend(); dBase[count] = one; count++; } dataIn.close(); // Close the input file return true; } // Close the database by optionally writing its contents to a file void DataBase::Close(void) { int record; // Record number in database char filespec[40]; // Output file name fstream saveData; // Output stream char writeIt; // Indicator of write or not cout << "Write to output (Y/N)? "; // Ask about writing to a file cin >> writeIt; if ((writeIt == 'y') || (writeIt == 'Y')) // If yes, open the file { cin.getline(filespec, 20); // Trash any leftovers cout << "Enter output filespec: "; // Get file name cin.getline(filespec, 39); saveData.open(filespec, ios::out); if (saveData.fail()) // Report any problem { cout << "Can't open file" << filespec << endl; return; } for (record = 0; record < count; record++) // Write each record to file dBase[record].FilePut(saveData); saveData.close(); } // Give a summary of database cout << "DataBase allocation: " << size << " entry slots; Number in use: " << count << endl; } // Add a record to the database // Precondition: The record must be valid and the ID and name must not // already be in the database // Postcondition: The database has one more record const DataBase & DataBase::operator+= (const InfoRecord &one) { int check; // Entry number with the same name bool found; // Indicates if name is in the database bool yes; // Indicates if ID is in the database int record; // Record number used in the loop Find(one.id, yes); // Look for the id check = Find(one.name, found); // Look for the name if (found || yes) // If either, report the problem { if (found) cout << "Name " << one.name << " is already in the DataBase.\n"; else cout << "ID " << one.id << " is already in the DataBase.\n"; } else { if (count == size) // Increase database size if needed Extend(); for (record = count - 1; record >= check; record--) // Locate place to add dBase[record+1] = dBase[record]; dBase[record+1] = one; // Add the record count++; // Adjust record count cout << endl << one.name << " added.\n"; // Report the result } return *this; } // Add a record to the database // Precondition: The record must be valid and the ID and name must not // already be in the database // Postcondition: The database has one more record const DataBase & DataBase::Add (const InfoRecord & one) { return (*this += one); } // Delete a record from the database (record specified with ID) // Precondition: Record to delete must be in the database, else error // Postcondition: Record is removed from the database const DataBase & DataBase::operator-= (const int who) { int result; // Location of record to delete int record; // Record number for loop bool found; // Indicator if record is found result = Find(who, found); // Search for the ID if (!found) // Not found, report problem cout << "No student with ID " << who << " in the data base\n"; else { cout << endl << dBase[result].name << " deleted\n"; // Report deletion for (record = result; record < (count - 1); record++) // Do deletion dBase[record] = dBase[record+1]; count--; // Adjust record count } return *this; } // Delete a record from the database (record specified with ID) // Precondition: Record to delete must be in the database, else error // Postcondition: Record is removed from the database const DataBase & DataBase::Delete (const int who) { return (*this -= who); } // Delete a record from the database (record specified with name) // Precondition: Record to delete must be in the database, else error // Postcondition: Record is removed from the database const DataBase & DataBase::operator-= (const string who) { int result; // Location of the record to delete int record; // Record number in loop bool found; // Indicator of record found result = Find(who, found); // Search for record if (!found) // If not found, report cout << "No student " << who << " in the data base\n"; else { cout << endl << dBase[result].name << " deleted\n"; // Report deletion for (record = result; record < (count - 1); record++) // Do deletion dBase[record] = dBase[record+1]; count--; // Adjust record count } return *this; } // Delete a record from the database (record specified with name) // Precondition: Record to delete must be in the database, else error // Postcondition: Record is removed from the database const DataBase & DataBase::Delete (const string who) { return (*this -= who); } // Friend function to output contents of database (assumed to screen) // Precondition: Database must have some data ostream & operator<< (ostream &out, const DataBase &thedata) { // Display info for whole DataBase int record; // For loop control // Display titles out << endl << "Name ID GPA Class Major\n"; for (record = 0; record < thedata.count; record++) out << thedata.dBase[record] << endl; // Show each record return out; } // Enlarges the array holding the database // Postcondition: Allocation for the database is larger by 10 record positions void DataBase::Extend (void) { InfoRecord *old = dBase; // Make pointer to current database int newsize = size + 10; // Make new size larger by 10 int record; // Record counter for loop dBase = new InfoRecord [newsize]; // Allocate space for new database for (record = 0; record < size; record++) // Copy records to new database dBase[record] = old[record]; size = newsize; // Set size to newsize delete [] old; // Free space for old database }