Моделирование библиотеки

Я хотел бы рассказать о симуляции библиотеки, которую я создал, используя C++. По сути, он имитирует среду, в которой люди приходят в библиотеку и берут книги, а также выводит информацию о книгах, такую как их полки, страницы и т.д. Есть несколько вещей, которые необходимо знать заранее:

1. Эта программа использует довольно много классов и структур, которые связаны друг с другом для отображения данных, т.е. (Книжная полка содержит книги, книги содержат историю, а люди связаны с активными книгами и т.д.).

2. В этом проекте нет внешних библиотек типа boost.

3. Данные предопределены, но также рандомизированы из жестко закодированного набора значений, что означает, что результат не всегда будет точно таким же, но каждый раз будет похожим.

4. У каждой книги есть только три копии, поэтому, когда этот лимит достигнут, книга не может быть взята снова.

После этого я хотел бы разделить все функции, которые я здесь использовал:

struct DualReturnType — пользовательская структура, которую я сделал, которая позволяет мне хранить данные указателя, а также его размер в одном возвращаемом значении в качестве средства легкого доступа.

LibraryGuests int const — постоянное целочисленное значение, которое хранит максимальное количество людей, которых я хотел видеть в симуляции.

ID — статический счетчик, который увеличивается при добавлении гостей.

Класс Person — класс, который хранит всю информацию о человеке, например, его имя и книги, которые у него есть на данный момент.

класс BookHistory — Пользовательский класс, который специально хранит историю книги, в первую очередь, кто ее взял, с информацией о названии в виде, который может быть легко распечатан.

класс Book — класс Book, который хранит информацию о книге, включая ее название, количество страниц, доступные копии и ссылку на экземпляр BookHistory.

класс BookShelf — Действует как реальная книжная полка, поэтому содержит список только из 13 книг на полку (экземпляр) и является тем, на что ссылаются при поиске книг. Полки соответствуют буквам A-Z

класс BookGroup — База данных этой программы. Он ссылается почти на все другие классы, такие как Книги и полки, и имеет ссылки на них. Когда делается запрос, он выполняется через этот класс.

void SetUpBookNames функция — Занимается задачей установки главной ссылки BookGroup с данными, такими как имена и страницы, которые используются в программе.

void setUpBookNames функция — То же самое, что и SetUpBookNames, но только с гостями библиотеки.

void Main — Устанавливает программу с необходимым инстанцированием гостей, имен книг и экземпляров книжных групп для запуска. Зацикливается до тех пор, пока пользователь не завершит программу.

класс UserInputHelperClass — класс, который обрабатывает направления ввода, которые выполняет пользователь, будь то поиск человека, книги или завершение программы.

Теперь я хотел бы рассказать о каждой из этих переменных и функций более подробно.

Первая из них — это структура DualReturn Type. DualReturnType struct — это структура, которая в своем конструкторе принимает в качестве ссылок указатель и его размер. Это делается для того, чтобы мне было проще ссылаться на указатели в других местах программы, в частности, в классе BookShelf.

Вот код для этого:

// Helper struct that returns pointers and their sizes for use in other functions
struct DualReturnType
{
    // The default constructor. Parameters are the array and size.
    DualReturnType(int *Ref, int Size)
    {
        // checker to make sure the parameter value isn't equal to NULL
        if (Ref != NULL)
        {
            int_Array = Ref;
            int_size = Size;
        }
        // If the incoming pointer is NULL, output an error message
        else
            cout << "Error: failed to initialize integer dual type" << endl;
    }
Войти в полноэкранный режим Выход из полноэкранного режима
// The default constructor for this struct. Makes everything NULL
DualReturnType()
{
    int_Array = NULL;
    int_size = 0;
}

// An integer array pointer, and a integer size variable
int *int_Array, int_size;

// Same as the specialized constructor but as a void function
void Initialize_Int_Type(int *Ref, int Size)
{
    if (Ref != NULL)
    {
        int_Array = Ref;
        int_size = Size;
    }
    else
        cout << "Error: Failed to initialize integer dual type" << endl;
}
Войти в полноэкранный режим Выход из полноэкранного режима

};

Следующий класс — это класс person. Класс person в своем конструкторе инициализирует некоторые значения по умолчанию, включая имя, две книги (массив), которые разрешено проверять («» или пусто), а также счетчик книг (хэш-ключ), который увеличивается при добавлении книги в этот класс. Класс также имеет идентификатор, похожий на библиотечный билет, который специально используется для идентификации людей, даже если вы не знаете их имени, и его можно легко отследить. Единственные три функции этого класса — обновлять массив книг внутри него (убирать или добавлять книги) и выводить их информацию в специальной функции, когда это необходимо.

Ниже приведен код для этого:

// The Person/Guest for this simulation. Has parameters attached to it such as ID, booklimits, name, etc...
class Person
{
public:
    //Default constructor which initializes all values to basic functions.
    Person()
    {
        name = "Empty";
        BookLimit = 2;
        behavior = "Browsing";
        GuestID = 0;
        books[0] = "";
        books[1] = "";
        bookcounter = 0;
    }

    // Copy constructor
    Person(const Person &copy)
    {
        name = copy.name;
        BookLimit = copy.BookLimit;
        behavior = copy.behavior;
        GuestID = copy.GuestID;
        books[0] = "";
        books[1] = "";
        bookcounter = 0;
    }

    // Initialize name if the incoming string is valid
    Person(string Name)
    {
        if (!Name.empty())
            this->name = Name;
        else
        {
            cout << "This person doesn't have a valid name." << endl;
            name = "Empty";
        }
        BookLimit = 2;
        behavior = "Browsing";

        books[0] = "";
        books[1] = "";
        bookcounter = 0;
    }
Войти в полноэкранный режим Выйти из полноэкранного режима
//Default destructor
~Person()
{

}

// Adds a book to the person's book array
void AddBook(string BookName)
{
    if (bookcounter <= 1)
    {
        books[bookcounter] = BookName;
        bookcounter++;
    }
    else
        cout << "Person()::AddBook cannot add more booksn";
}

// Prints the books that a person added
void PrintBooks()
{
    if (bookcounter > 0)
    {
        cout << name << " current books:              ";
        for (int i = 0; i < bookcounter; ++i)
        {
            if (books[i] != "")
                cout << i+1 << ": " << books[i] << "      ";
        }

        cout << "n";
    }
    else
        cout << name << " has no history yet.n";
}

// Prints the general info about this person
void PrintGeneralInfo()
{
    cout << "Name: " << name << "tBooks in possession: " << bookcounter << "tGuestID: " << GuestID << endl;
}

string name, behavior, books[2];                        // Name of the person, and arrayof booknames
int BookLimit, GuestID, bookcounter;                    // BookLimit to limit the person inside the library to a certain amount, and that persons GuestID, bookcounter to access current book   
Войти в полноэкранный режим Выйти из полноэкранного режима

};

Следующий класс — BookHistory. Он имеет публичное меню BookName, приватную переменную counter, указатель Person под названием people, целочисленную переменную currentlimit и строковую переменную order. Внутри этого класса есть только две функции: AddHistory и PrintHistory. AddHistory принимает входящий объект Person и добавляет его к ссылке people этого класса, а другая просто распечатывает данные, содержащиеся в этом классе, вместе с названием книги.

Вот как все это выглядит:

// Records the history of a book which can be called at any time later
class BookHistory
{
public: 
    string bookName;                            // Name of the book that is recorded
Вход в полноэкранный режим Выход из полноэкранного режима
// Default constructor which initializes all variables
BookHistory()
{
    people = new Person[3];
    currentLimit = 3;
    currentCounter = 0;
    order[0] = "first.";
    order[1] = "second.";
    order[2] = "third.";
}

// Copy constructor which initializes based on another bookHistory reference
BookHistory(const BookHistory &otherBookHistory)
{
    currentLimit = otherBookHistory.currentLimit;
    people = new Person[currentLimit];
    currentCounter = otherBookHistory.currentCounter;
    order[0] = "first.";
    order[1] = "second.";
    order[2] = "third.";

    for (int i = 0; i < currentCounter; i++)
        people[i] = otherBookHistory.people[i];
}

// Destructor which only destroys our people pointer
~BookHistory()
{
    if (people != NULL)
        delete [] people;
}

// How people are added to this books history. 
void AddHistory(Person person)
{
    // If the currentcounter hasn't yet reached the limit, then set the people pointer inside our history to the incoming person. Basically adds it inside the array
    if (currentCounter < currentLimit)
    {
        people[currentCounter] = person;
        currentCounter++;                   // finally increase the currentCounter
    }
    else
        cout << "Cannot update this books history at this time" << endl;
}

// Prints the histoy of this book based on the currentCounter which increases when people check out this book
void PrintHistory()
{
    if (currentCounter >= 1)
    {
        // Will print the persons Info assuming the people pointer isn't NULL and it's name isn't == Empty
        for (int i = 0; i < currentCounter; i++)
        {
            if (people != NULL)
                if (people[i].name != "Empty")
                    cout << people[i].name << " ID: " << people[i].GuestID << " checked out '" << bookName << "' " << order[i] << "nn";
                else
                    cout << "No names setup for this bookn";
        }
    }
    else
        cout << bookName << " has no check out history yet.nn";       // This should print out if the current counter isn't at least equal to 1. Book has no history.
}
Войти в полноэкранный режим Выход из полноэкранного режима
private:
    Person *people;                         // people pointer which holds references to anyone who checks out this book
    int currentLimit;                       // A max limit variable which is initialized to 3 for this simulation
    int currentCounter;                     // A counter that goes up until it reaches the currentLimit
    string order[3];                        // Simply a string helper which has strings based on the order of an index. So 0 = first, 1 = seconds, 2 = third. 
};
Войти в полноэкранный режим Выход из полноэкранного режима

Следующий класс, который я хотел бы затронуть, — это класс Book. Класс Book имеет все открытые члены, включая количество страниц в книге, ее экземпляры, наличие, имя и член класса BookHistory. Его функции включают проверку пустых книг и удаление копий книги, если количество копий равно 0, которое уменьшается при проверке. Конструктор инициализирует большинство данных с переданными аргументами или без них.

Посмотрите здесь:

// Book class which is used to house all information about a book including its name, number of pages, whether it's avaiable or not, its history, and number of copies
class Book
{
public:
    // Default constructor for this class. Intializes all values. Name of the book is blank (set elsewhere), pages is also reset elsewhere, copies is set to 3,
    // avaialable is set to true, and the the historys bookname is set to empty
    Book()
    {
        name = "Empty";
        pages = 0;
        copies = 3;
        available = true;
        thisBookHistory.bookName = "Empty";
    }

Вход в полноэкранный режим Выход из полноэкранного режима
// Book with all parameters setup to copy into this variables
Book(string BookName, int numPages, int copies, BookHistory book)
{
    name = BookName;
    pages = numPages;
    thisBookHistory = book;
    available = true;
    this->copies = copies;
}

// Destructor of the book class
~Book()
{

}

// If the book is a dummy or a real book
bool emptyBook()
{
    if (name == "" || name == "Empty")
        return true;
    else
        return false;
}

// Removes a copy of the book from the list. Once a copy reaches zero, no one may check out this book
void RemoveCopy()
{
    // Simply decrease the number of copies by 1 and return
    if (copies >= 2)
    {
        copies -= 1;
    }
    // If the copy equals 1, then this is the last copy of the book and available should be set to false for other functions. Copies is also decremented by 1
    else if (copies == 1)
    {
        available = false;
        //cout << "This is the last copy of " << name << endl;
        copies -= 1;
    }
    // This shouldn't occur but is set just in case this function is called by accident
    else
        cout << "Sorry. " << "No more copies of: " << name << " are avaialable.n";
}

string name;                    // Name of the book
int pages, copies;              // The number of pages of the book and its copies
bool available;                 // Whether the book is available or not. Used in the bookshelf class
BookHistory thisBookHistory;    //History variable of the book which can be called by the shelf
Войти в полноэкранный режим Выход из полноэкранного режима

};

Следующий класс — Bookshelf. Этот класс напоминает книжную полку в библиотеке, и его дизайн предполагает наличие нескольких классов, соответствующих группам из двух букв. Всего их тринадцать и расположены они в порядке целочисленности. Так, bookshelf[0] соответствует книгам, начинающимся с A и B, bookshelf[1] C и D и так далее. Она имеет все открытые члены, которые включают двенадцать массивов Books (соответствующих полкам), строку, содержащую идентификатор полки, булево значение fillboolean для легкого доступа, массив fullindex размером двенадцать, также соответствующий полке, и счетчик stockcounter, который содержит книги, доступные для использования. Его функции включают функцию AddBook, которая добавляет книгу и ее страницы в класс, класс PrintAllBooksOnShelf, который печатает все книги, доступные в данный момент на этой полке, функцию printbyindex, которая делает то же самое, но в ограниченном виде, сначала проверяя, действителен ли входящий индекс, две функции printbookhistory, которые делают то же самое, что и printindex и printall, булев помощник locateBook, который возвращает true или false, если книга найдена, функция getbookID, которая пытается найти книгу по ее ID вместо названия с помощью функции locateBook, булева функция AllBooksTaken, которая перебирает все книги, чтобы найти ту, которая не является ложной, и, наконец, вспомогательная функция DualReturnType под названием ReferenceArrayHelper, которая использует целочисленное значение StockCounter для ручного создания целочисленной ссылки-указателя, которая может быть использована в других местах, например, в BookGroup, которая будет рассмотрена далее.

Вот как выглядит Книжная полка:

// Bookshelf class that holds all data about a bookshelf instance. 
class Bookshelf
{
public:
    // Default constructor. Sets stockcounter (current books) to 0, then set fullindex incidies to the value in the loop
    Bookshelf()
    {
        StockCounter = 0;
        for (int i = 0; i <= 11; ++i)
            fullindex[i] = i;
    }
Вход в полноэкранный режим Выход из полноэкранного режима
// Destructor
~Bookshelf()
{

}

// Add a book to this shelf. Parameters include the name of the book and its pages.
void AddBook(string BookName, int pages)
{   
    // If current book couner isn't at its max (12), then proceed
    if (StockCounter <= 11)
    {
        //  If the current name of the book is empty, prepare to replace it with the parameters.
        if (books[StockCounter].name == "Empty")
        {
            books[StockCounter].name = BookName;
            books[StockCounter].pages = pages;
            books[StockCounter].thisBookHistory.bookName = BookName;
            StockCounter++;                                         // Increase the number of books on this instance by 1       
        }
        // if we tried to add a name that wasn't valid. However this won't trigger because of the stockCounter variable after the last add
        else
            cout << "Somehow, this spot is already takenn";
    }
    else
    {
        cout << "No more room on this shelf to add new booksn";        //  Prints if we tried to add a book but the current value exceeds 11
    }
}

// Prints all books data that are currently on the shelf
void PrintAllBooksOnShelf()
{
    string available = "";
    for (int i = 0; i < StockCounter; ++i)
    {
        books[i].available ? available = "Yes" : available = "No";

        cout << "Shelf ID: " << shelfID << " tBook number: " << i+1 << "      Book name: " << books[i].name 
            << " tPages: " << books[i].pages << " tAvailabile: " << available << endl;
    }
}

// Print the books general data based on a index parameter assuming it is in bounds
void PrintBookByIndex(int index)
{
    // Temp string to hold the avialability string to print out to the user
    string available = " ";

    // If the book we want is available, set the available string to Yes, otherwise it is set to no
    books[index].available ? available = "Yes" : available = "No";

    // Print the information that we want about this book
    if (index >= 0 && index <= 11)
        cout << "Shelf ID: " << shelfID << ". Book number: " << index+1 << ". Book name: " << books[index].name
        << ". Pages: " << books[index].pages << ". Available: " << available << endl;
}

// Prints the usage history of a specific book assuming its index is in bounds
void PrintBookHistory(int bookID)
{
    if (bookID != -1 && bookID <= 11)
        books[bookID].thisBookHistory.PrintHistory();
}

// Prints all of the history of all current books on this shelf only
void PrintAllBookHistory()
{
    for (int i = 0; i < StockCounter; ++i)
    {
        books[i].thisBookHistory.PrintHistory();
    }
}

// Attempts to find a single book based on the name.
bool LocateBook(string BookName)
{
    //cout << endl;
    // Loop through the elements and if the parameter name matches any of the elements name, Simply return true to indicate success.
    for (int i = 0; i <= StockCounter - 1; ++i)
    {
        if (BookName == books[i].name)
            return true;
    }

    // This occurs if we went through all elements and there is no match. In this case print a message and return false to indicate failure.
    cout << BookName << " does not exist on shelf " << shelfID << "." << endl;
    return false;
}

// Attempts to grab a booksID based on that books name.
int GetBookID(string BookName)
{
    int bookID = -1;
    if (LocateBook(BookName))
    {
        for (int i = 0; i < StockCounter; ++i)
        {
            // If a match was found, then break the loop and set the id to i.
            if (BookName == books[i].name)
            {
                bookID = i;
                break;
            }
        }
    }
    return bookID;
}

// Boolean return type based on the filled boolean variable. If ANY book is still available, this will return false. Otherwise it will return true.
bool AllBooksTaken()
{
    for (int i = 0; i < 12; ++i)
    {
        // If a book is available, set filled to false and return immediately
        if (books[i].available)
        {
            filled = false;
            return filled;
        }
        // If a book isn't available, then set the fullindex based on that space to -1, used for ReferenceArrayHelper().
        else
            fullindex[i] = -1;
    }

    // If we got here, this shelf really is full and return true.
    filled = true;
    return filled;
}

// DualReturnType reference funcion that will return a pointer and its size together.
DualReturnType ReferenceArrayHelper()
{
    int SizeCounter(0);                         // SizeCounter reference to determine valid books inside the shelf

    // Loop through the active books on the shelf and if the fullindex based on the loop value i is equal to -1, increase the sizecounter variable.
    // This is to intialize another variable later so that it only contains valid indicies from the shelf.
    for (int i = 0; i < StockCounter; ++i)
    {
        if (fullindex[i] != -1)
            SizeCounter += 1;
    }

    // Assuming that at least one of the elements in the above was valid (SizeCounter >=1), proceed.
    if (SizeCounter >= 1)
    {
        int *indexArray = new int[SizeCounter];             // Create a pointer array that is based on the SizeCounter index;
        int indexCounter = 0;                               // new variable reference that tracks the indices of the indexArray. This is so we can set them as we proceed
                                                            // through the loop based on its own counter

        // Loop through the StockCounter again and if the sizecounter is greater than or equal to 1, proceed with the following
        for (int i = 0; i < StockCounter; ++i)
        {
            // If the fullindex element isn't -1, the proceed.
            if (fullindex[i] != -1)
            {
                if (SizeCounter >= 1)
                {
                    indexArray[indexCounter] = i;           // Set the indexArray element based on the current counter equal to i in the loop to indicate a goood value
                    indexCounter++;                         // Increase the index counter by 1 to move to the next element
                    SizeCounter--;                          // Decrease the counter since we hit a successfull element
                }
                else
                {
                    break;                                  // If the sizecounter has gone below 1, stop since there are no other valid elements
                }
            }
        }

        // Finally return this newly created array, along with the index counter, which is indexArray's size
        return DualReturnType(indexArray, indexCounter);
    }
    else
        return DualReturnType();        // IF we got here, return a default or blank DualReturnType
}

Book books[12];                                 // Array of books in that shelf
string shelfID;                                 // String that containts the name of each shelfID
int StockCounter;                               // The number of books that are actually avialable to use, increases when books are added
bool filled;                                    // If the entire structure is taken
int fullindex[12];                              // Useful when checking out a book. This is so this same book isn't looked at again. (Removed from the pool)
Войти в полноэкранный режим Выход из полноэкранного режима

};

Класс BookGroup содержит всю информацию, связанную с книгами и полками. После основных функций настройки и класса ввода, это класс, на который ссылаются при добавлении, поиске, нахождении или удалении информации, относящейся к книгам, людям и истории. Он имеет один приватный член — массив bookshelf, что сделано для того, чтобы данные, относящиеся к нему, не могли быть легко изменены простым созданием экземпляра этого класса. Изменение данных вокруг него требует использования функций-членов, которые вызывают данные и функции внутри класса, относящиеся к требуемой цели.
В число членов класса входят конструктор, булева функция FindBook, функция void AddBook, функция void AddBooks, индекс getbookshelfID на основе названия книги, строковая функция GetbookshelfID, три варианта printbook, которые печатают все книги, одну по индексу, другую по shelfID, функция void CheckoutBook, которая принимает ссылку на человека и его желаемую книгу, строковая вспомогательная функция randomBookSelection, которая выбирает книгу без необходимости ввода названия, две вспомогательные функции bookhistory, которые печатают историю книг по полкам, возвращаемый тип GetBook Book, который пытается найти книгу по ее идентификатору полки и месту, и, наконец, строковая функция GetBook, которая напрямую дает пользователю название книги в определенном месте. Эта функция очень длинная, поэтому я не буду объяснять ее здесь.

Однако я написал много заметок по этому поводу здесь, если вы хотите пройти через это самостоятельно:

// The main class that controls everything. Operating as the library's database, this class houses all bookshelves and the bookshelves house the books.
class BookGroup
{
public:
    // Default constructor that sets up the bookshelves
    BookGroup()
    {
        // Create a string that has all of the letters of the alphabet
        string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

Вход в полноэкранный режим Выйти из полноэкранного режима
    int stringcounter = 0;          // Counter variable that will be used to pull letters out of the alpabet variable

    // Loop based on the total number of shelves (12 in total).
    for (int i = 0; i < 13; ++i)
    {
        // Make the shelf ID a group of two from the alphabet. So shelf 1 = AB, 2 = CD, 3 = EF etc...
        // Uses the stringcounter as a index from the alphabet in groups of 2. So 0 first then 0 + 1 next. Then simply increase the string counter by 2
        // so when we get here again it references the next set of letters. 
        bookshelf[i].shelfID.append(1, alphabet[stringcounter]);
        bookshelf[i].shelfID.append(1, alphabet[stringcounter + 1]);

        stringcounter += 2;
    }

}

// Default destructor which does nothing
~BookGroup()
{

}

// Find a book based on its name. 
bool FindBook(string book)
{
    // holder boolean which is initialized to false
    bool returnvalue = false;

    // Shelf holder that will be changed based on the first letter of the book (parameter)
    int Shelf = 0;

    // Create a single character variable which references the first character inside the book parameter
    char bookcopy = book[0];

    // Capatitalize this letter to avoid errors and to match the switch case below
    bookcopy = toupper(bookcopy);

    // Switches based on the bookcopy variable
    switch (bookcopy)
    {
    case 'A':
        //cout << "Letter A n";
        break;
    case 'B':
        //cout << "Letter B n";
        break;
    case 'C':
        //cout << "Letter C n";
        Shelf = 1;
        break;
    case 'D':
        //cout << "Letter D n";
        Shelf = 1;
        break;
    case 'E':
        //cout << "Letter E n";
        Shelf = 2;
        break;
    case 'F':
        //cout << "Letter F n";
        Shelf = 2;
        break;
    case 'G':
        //cout << "Letter G n";
        Shelf = 3;
        break;
    case 'H':
        //cout << "Letter H n";
        Shelf = 3;
        break;
    case 'I':
        //cout << "Letter I n";
        Shelf = 4;
        break;
    case 'J':
        //cout << "Letter J n";
        Shelf = 4;
        break;
    case 'K':
        //cout << "Letter K n";
        Shelf = 5;
        break;
    case 'L':
        //cout << "Letter L n";
        Shelf = 5;
        break;
    case 'M':
        //cout << "Letter M n";
        Shelf = 6;
        break;
    case 'N':
        //cout << "Letter N n";
        Shelf = 6;
        break;
    case 'O':
        //cout << "Letter O n";
        Shelf = 7;
        break;
    case 'P':
        //cout << "Letter P n";
        Shelf = 7;
        break;
    case 'Q':
        //cout << "Letter Q n";
        Shelf = 8;
        break;
    case 'R':
        //cout << "Letter R n";
        Shelf = 8;
        break;
    case 'S':
        //cout << "Letter S n";
        Shelf = 9;
        break;
    case 'T':
        //cout << "Letter T n";
        Shelf = 9;
        break;
    case 'U':
        //cout << "Letter U n";
        Shelf = 10;
        break;
    case 'V':
        //cout << "Letter V n";
        Shelf = 10;
        break;
    case 'W':
        //cout << "Letter W n";
        Shelf = 11;
        break;
    case 'X':
        //cout << "Letter X n";
        Shelf = 11;
        break;
    case 'Y':
        //cout << "Letter Y n";
        Shelf = 12;
        break;
    case 'Z':
        //cout << "Letter Z n";
        Shelf = 12;
        break;
    default:
        cout << "First letter is not A - Z. Error.n";
        break;
    }

    // Now that we have our shelf index, then attempt to find the book based on that shelf
    bool FoundBook = bookshelf[Shelf].LocateBook(book);

    // If the FoundBook boolean is equal to true, set the returnvalue to true and the same if its the opposite
    FoundBook ? returnvalue = true: returnvalue = false;
    return returnvalue;
}

// Adds a single book based on a int array value. 
void AddBook(string BookName, int *PagesArray, int PageIndex)
{
    int Shelf = GetBookShelfID_Index(BookName);             // Get the bookID based on the bookName

    // If the shelfID isn't equal to negative one proceed normally, otherwise output a error message
    if (Shelf != -1)
        bookshelf[Shelf].AddBook(BookName, PagesArray[PageIndex]);
    else
        cout << "Error in AddBook: Impossible to add " << BookName << " to the list";
}

// Adds a array of strings to the shelf based size of the array
void AddBooks(string *Books, int BookSize, int *PageArray)
{
    for (int Shelf, i = 0; i < BookSize; ++i)
    {
        Shelf = GetBookShelfID_Index(Books[i]);
        if (Shelf != -1)
        {
            bookshelf[Shelf].AddBook(Books[i], PageArray[i]);
        }
        else
            break;
    }
}

// Get the shelf index based on the first letter of the incoming string. Return that index. If the later cannot be found, return -1
int GetBookShelfID_Index(string Name)
{

    Name[0] = toupper(Name[0]);
    if (Name[0] == 'A' || Name[0] == 'B')
        return 0;
    else if (Name[0] == 'C' || Name[0] == 'D')
        return 1;
    else if (Name[0] == 'E' || Name[0] == 'F')
        return 2;
    else if (Name[0] == 'G' || Name[0] == 'H')
        return 3;
    else if (Name[0] == 'I' || Name[0] == 'J')
        return 4;
    else if (Name[0] == 'K' || Name[0] == 'L')
        return 5;
    else if (Name[0] == 'M' || Name[0] == 'N')
        return 6;
    else if (Name[0] == 'O' || Name[0] == 'P')
        return 7;
    else if (Name[0] == 'Q' || Name[0] == 'R')
        return 8;
    else if (Name[0] == 'S' || Name[0] == 'T')
        return 9;
    else if (Name[0] == 'U' || Name[0] == 'V')
        return 10;
    else if (Name[0] == 'W' || Name[0] == 'X')
        return 11;
    else if (Name[0] == 'Y' || Name[0] == 'Z')
        return 12;
    else
    {
        cout << "Error in the bookshelfID grabber function" << endl;
        cout << Name << endl;
        return -1;
    }
}

// string return of the bookID based on the Name of a book
string GetBookShelfID(string Name)
{
    // Get and Set the index variable to the index based off the name
    int ShelfID_Index = GetBookShelfID_Index(Name);

    // If index is within bounds then return the ID, otherwise return NULL
    if (ShelfID_Index >= 0 && ShelfID_Index <= 12)
        return bookshelf[ShelfID_Index].shelfID;
    else
        return "NULL";
}

// Simply prints the books in order by shelves. Calls the shelf variables PrintAllBooksOnShelf 
void PrintAllBooks()
{
    int AllShelves = sizeof(bookshelf) / sizeof(bookshelf[0]);      // Size that we'll loop through
    for (int i = 0; i < AllShelves; ++i)
    {
        bookshelf[i].PrintAllBooksOnShelf();                    // Prints all books per shelf in the array
    }
}

// Print all the books based on a desired shelf
void PrintBooksByShelf(int shelf)
{
    if (shelf >= 0 && shelf <= 12)
        bookshelf[shelf].PrintAllBooksOnShelf();            // Prints all books on the shelf that we want
}

// Print a specific book with its shelf and slot index
void PrintBookByShelfAndID(int shelf, int bookID)
{
    bookshelf[shelf].PrintBookByIndex(bookID);
}

// The actual function that checks out books from the shelevs. Requires a guest parameter, and that guests desired book to be checked out. 
// This function will continue until a successful book has been removed. The library has been adjusted to accomodate this.
void CheckoutBook(Person &guest, string desiredBook)
{
    // Guest name cannot be blank
    if (guest.name != "Empty")
    {
        // Guest must be able to at least check out one book
        if (guest.BookLimit >= 1)
        {
            // attempts to find the guests desired book
            bool FoundBook = FindBook(desiredBook);

            // If it is found proceed.
            if (FoundBook)
            {
                // Gathers the shelfID and the bookID based on the desiredbook
                int Shelf = GetBookShelfID_Index(desiredBook);
                int bookID = bookshelf[Shelf].GetBookID(desiredBook);

                // If the shelf and bookIDs are -1(failure) then proceed.
                if (Shelf != -1 && bookID != -1)
                {
                    // Checker to detrmine if all the books on the desired shelf are empty or not
                    bool AllbooksTaken = bookshelf[Shelf].AllBooksTaken();

                    // If its NOT TRUE that all the books are gone, proceed
                    if (!AllbooksTaken)
                    {
                        // If the book that we want is still available, proceed
                        if (bookshelf[Shelf].books[bookID].available)
                        {
                            // Add this person to book they wants history. Reduce this persons booklimit by 1 Add the book to their reference. Finally 
                            // reduce the number of the copies of the book by calling the books removecopy function
                            bookshelf[Shelf].books[bookID].thisBookHistory.AddHistory(guest);
                            guest.BookLimit--;
                            guest.AddBook(desiredBook);
                            bookshelf[Shelf].books[bookID].RemoveCopy();
                        }
                        // This shouldn't execute but is set just in case. Simply recalls this same function again but sets SelectRandomBook() as the second
                        // argument (desiredBook) and keeps the guest reference the same. So the guest is simply searching for another book
                        else
                        {
                            cout << "Sorry, " << desiredBook << " is not currently available" << endl;
                            return CheckoutBook(guest, SelectRandomBook() );
                        }
                    }
                    else
                        cout << "Sorry all books have been taken on this shelf. n";
                }
                else
                    cout << "Program error. Shelf and or bookID were not initialized properly.nShelfID: " << 
                    Shelf << ". BookID: " << bookID << ". Desired book: " << desiredBook << endl; // Either there was a failure or something went wrong
            }
            else
                cout << "Sorry. " << desiredBook << " doesn't exist in this library!" << endl;
        }
        else
            cout << "Sorry, you can't check out any more books" << endl;
    }
    else
        cout << "Error, guest must have a valid name" << endl;
}

// Attempts to return a random book name on one of the shelves. This function will repeat until a reliable name is found.
string SelectRandomBook()
{
    string bookName = "Empty";                      // Empty bookName holder

    int shelfID(rand() % 13),                       // shelfID picks a random shelf between 0 and 13
    bookID;                                         // bookID variable that will be used for later

    if (shelfID != 13)
    {
        bool TakenBooks = bookshelf[shelfID].AllBooksTaken();       // create a bool to hold whether or not all books on the desired shelf are taken or not
        // If Taken isn't true (or isn't full capacity) then proceed to this branch
        if (!TakenBooks)
        {
            DualReturnType intType = bookshelf[shelfID].ReferenceArrayHelper();     // Uses DualReturnType structure to hold a reference based on the Reference
            // Array Helper function with the shelf id and bookshelf.

            // If the array pointer inside our dualtype variable (intType) isn't NULL, proceedd
            if (intType.int_Array != NULL)
            {
                // Initialize this variable based on size we pased into our intType variable
                bookID = rand() % intType.int_size;

                // Important because we created our own array specifically with its own size and values. We get the value inside at the point of the
                // randomly generated bookID
                int originalBookID = intType.int_Array[bookID];

                // If the bookshelf we want and the book inside it that we want are available, proceed.
                if (bookshelf[shelfID].books[originalBookID].available)
                {
                    bookName = GetBookName(shelfID, originalBookID);
                }
                else
                {
                    // If we get here, delete the array reference we set and call this function again(recursion). This will happen until we get a book that isn't taken.
                    // Note: the TakenBooks variable prevents us from needlessly repeating the search
                    delete intType.int_Array;
                    return SelectRandomBook();
                }
                // This continues if the book we want is avaialable
                delete intType.int_Array;
            }
            // This shouldn't happen but is here just in case. Simply recalls this function again
            else
            {
                cout << "Retrying book selection. " << endl;
                return SelectRandomBook();
            }
        }
    }
    return bookName;        // If we got here that means the selection was a success. The function ends normally
}

// Simply outputs the history of a desiredbook if it exists on any of the shelves. Different from PrintBooks() which only prints all books on the shelves
void PrintBookHistoryByBook(string Book)
{
    if (Book != "")
    {
        bool Found = FindBook(Book);                                // Bool reference to hold whether or not the book exists on any of the shelves
        int BookSlot(-1);                                           // Slot reference initialized to -1 error

        // If the book was found continue, if not ouput a error message
        if (Found)
        {
            int ShelfID = GetBookShelfID_Index(Book);               // ShelfID variable class the GetShelfID index function based on the book
            BookSlot = bookshelf[ShelfID].GetBookID(Book);          // BookSlot variable class which uses the bookshelf variable based on shelfID to find a book. 
                                                                    // Basically the shelf is used to quicken search time

            // If the bookslot value isn't negative 1 then, then call PrintBookHistory based on the slot we just received
            if (BookSlot != -1)
                bookshelf[ShelfID].PrintBookHistory(BookSlot);
        }
        else
            cout << Book << " can't be found. (PrintBookHistoryByBook)n";
    }
    else
        cout << "Sorry, you must insert a valid name into PrintBookHistoryByBook().n";
}

// Prints the history of the books in order from the first shelf to the last
void PrintAllBookHistory()
{
    for (int i = 0; i <= 12; ++i)
    {
        bookshelf[i].PrintAllBookHistory();
    }
}

// Gets a book based on a shelfID parameter and on spot parameter.
Book GetBook(int ShelfID, int spot)
{
    if (ShelfID >= 0 && ShelfID <= 12 && spot >= 0 && spot <= 11)
    {
        return bookshelf[ShelfID].books[spot];
    }
    // If the parameters are out of bounds, then simply return a empty book
    else
    {
        cout << "Spot or shelfId inside GetBook() is out of boundsn";
        return Book();
    }
}

// Does the same as the above but simply gives the name directly instead of the entire book
string GetBookName(int shelfID, int bookspot)
{
    // Simply ensures that the shelfID and bookspot variables are within reasonable bounds (0 - 12) & (0 - 11) respectively
    if (shelfID >= 0 && shelfID <= 12 && bookspot >= 0 && bookspot <= 11)
    {
        return bookshelf[shelfID].books[bookspot].name;
    }
    else
    {
        cout << "ShelfID or bookspot inside GetBookName() is out of bounds";
        return "NULL";
    }
}
Войти в полноэкранный режим Выход из полноэкранного режима
private:
    Bookshelf bookshelf[13];                                    // array of shelves A-Z AB, CD, EF, etc. Is private so the user can't do things directly
};
Войти в полноэкранный режим Выход из полноэкранного режима

Следующий класс — UserInputHelperClass. Этот класс имеет только две функции, это функции HandlePrintBooks и HandlePrintBooksHistory. Функция HandlePrintBooks работает в одном из трех направлений: она печатает все книги по полкам, позволяет пользователю искать книгу, используя название книги и ее идентификатор полки, и, наконец, завершает работу по запросу. В качестве параметра она принимает BookGroup. Что касается функции HandlePrintHistory, то она позволяет просмотреть историю всех книг или конкретной книги, если она существует, а также позволяет пользователю вернуться в главное меню по запросу. Функция позволяет повторять это снова и снова, используя цикл, пока не будет нажата определенная клавиша.

Код для этого приведен здесь:

class UserInputHelperClass
{
public:
    // Handles the print case 1 from the center. This function will do 1 of 3 actions: 1.) Terminate upon user request 2.) Find a specific book by asking for a
    // shelfID and a bookID 3.) PrintAll books on all shelves
    void HanldePrintingAllBooks(BookGroup mainRef)
    {
        cout << "Would you like to print all books or just one? A for all, 1 for specific, N to returnn";
        char input(' ');

Войти в полноэкранный режим Выход из полноэкранного режима
    // First ask the user for one of three keys, 1, A or N. If the user doesn't do this this will repeat until then
    while (input != '1' && input != 'A' && input != 'N')
    {
        // Asks for only a single key only
        input = _getch();

        // Digit checkers so digits won't proceed through. If the key that the user has entered is a lowercase letter, then simply raise it to uppercase
        if (!isdigit(input) )
            if (islower(input))
                input = toupper(input);

        // Simply repeats the same string as the above but with a error message
        if (input != 'A' && input != '1' && input != 'N')
            cout << "Sorry please try again. A for All books, 1 for specific, N for terminationn";
    }

    // Assuming one of the 3 keys above was given, then proceed to execute based on their action.
    switch (input)
    {
        // The first case if the user wants to manually select a book based on index
        case '1':
        {
            cout << "Please enter a shelfID between 1 to 13 or N to terminate" << endl;
            int indexShelf = 0;             // initialized bookshelf ID
            int indexBook = 0;                  // initialized book ID
            bool continueLoop = false;          // Boolean to determine whether or not we need to keep asking the user for information
            string holdervalue("");             // will hold the input of our user

            // This will continue on until the user either quits, or they enter a correct value
            while (!continueLoop)
            {
                // ask for user input
                cin >> holdervalue;

                // If the value the user just entered is less than or equal to 2 in length, i.e a number (0 - 99) or two letters, proceed
                if (holdervalue.size() <= 2)
                {
                    // The case for if the size is only one character long
                    if (holdervalue.size() == 1)
                    {
                        // If the only character is a digit, then set our indexShelfID to what that digit is, if it isn't a digit, then captialize the letter inside
                        if (isdigit(holdervalue[0]))
                        {
                            indexShelf = holdervalue[0] - '0';
                        }
                        else
                            holdervalue[0] = toupper(holdervalue[0]);
                    }

                    // The case for if the size is 2, not 1 and not 0
                    else
                    {
                        // If the first character in the input string is a digit, then set the indexShelf variable to this value. Remove the null character to convert
                        // it to a digit. If we got here then if the second character in the string is also a digit, then mutliply the value that's in the indexShelf
                        // already by ten, and then add this second value to the string. This is ensure that the number is what was originally typed. I.e, 77 Index = 7.
                        // 7 * 10 = 70 + 7 = 77
                        if (isdigit(holdervalue[0]))
                        {
                            indexShelf = holdervalue[0] - '0';

                            if (isdigit(holdervalue[1]))
                            {
                                indexShelf *= 10;
                                indexShelf += holdervalue[1] - '0';
                            }
                        }

                    }

                    // Start with the null termination first to avoid early cancellation by some other value, and include certain types. If this is true, exit the function
                    if (holdervalue == "N" || holdervalue == "NN" || holdervalue == "nn")
                    {
                        cout << "Terminating" << endl;
                        return;
                    }
                    // If the indexShelf is greater than or equal to 14, then it is too high. Print a string and continue
                    else if (indexShelf >= 14)
                        cout << "Number too high. Please enter a shelfID between 1 to 13 or N to terminate" << endl;
                    // If the indexShelf value is less than or equal to 0, then this value is too low. Print a string and continue
                    else if (indexShelf <= 0)
                        cout << "Number too low. Please enter a shelfID between 1 to 13 or N to terminate" << endl;
                    // If all above conditions were not triggered, then the user entered a valid value. Set the continueLoop to true (!false) and stop the loop.
                    // Finally decrease by 1, this is to ensure that the values aren't out of bounds
                    else
                    {
                        cout << "Valid input." << endl;
                        continueLoop = !(continueLoop);
                        indexShelf -= 1;
                    }
                }
                // Then the size is too long. Simply print a string and continue like the above conditions
                else
                    cout << "Value too long. Please enter a valid shelfID between 1 to 13 or N to terminate" << endl;
            }

            // Reset the basic values again for the bookID
            continueLoop = false;
            holdervalue = " ";

            cout << "Please enter a bookID between 1 to 12 or N to terminate" << endl;

            // Repeats the same above while loop but it does this for a bookID, and the max limit is decreased by 1 to match our bookID limit and prevent out of bounds errors
            while (!continueLoop)
            {
                cin >> holdervalue;

                // If the value the user just entered is less than or equal to 2 in length, i.e a number (0 - 99) or two letters, proceed
                if (holdervalue.size() <= 2)
                {
                    // The case for if the size is only one character long
                    if (holdervalue.size() == 1)
                    {
                        // If the only character is a digit, then set our indexBook to what that digit is, if it isn't a digit, then captialize the letter inside
                        if (isdigit(holdervalue[0]))
                        {
                            indexBook = holdervalue[0] - '0';
                        }
                        else
                            holdervalue[0] = toupper(holdervalue[0]);
                    }

                    // The case for if the size is 2, not 1 and not 0
                    else
                    {
                        // If the first character in the input string is a digit, then set the indexBook variable to this value. Remove the null character to convert
                        // it to a digit. If we got here then if the second character in the string is also a digit, then mutliply the value that's in the indexBook
                        // already by ten, and then add this second value to the string. This is ensure that the number is what was originally typed. I.e, 77 Index = 7.
                        // 7 * 10 = 70 + 7 = 77
                        if (isdigit(holdervalue[0]))
                        {
                            indexBook = holdervalue[0] - '0';

                            if (isdigit(holdervalue[1]))
                            {
                                indexBook *= 10;
                                indexBook += holdervalue[1] - '0';
                            }
                        }

                    }

                    // Start with the null termination first to avoid early cancellation by some other value, and include certain types. If this is true, exit the function
                    if (holdervalue == "N" || holdervalue == "NN" || holdervalue == "nn")
                    {
                        cout << "Terminating" << endl;
                        return;
                    }
                    // If the indexBook is greater than or equal to 14, then it is too high. Print a string and continue
                    else if (indexBook >= 13)
                        cout << "BookID too high. Please enter a shelfID between 1 to 13 or N to terminate" << endl;
                    // If the indexBook value is less than or equal to 0, then this value is too low. Print a string and continue
                    else if (indexBook <= 0)
                        cout << "BookID too low. Please enter a shelfID between 1 to 13 or N to terminate" << endl;
                    // If all above conditions were not triggered, then the user entered a valid value. Set the continueLoop to true (!false) and stop the loop. Then 
                    // indexBook is decremented by 1 to fit the loop.
                    else
                    {
                        cout << "Valid bookID." << endl;
                        continueLoop = !(continueLoop);
                        indexBook -= 1;
                    }
                }
            }

            // This line is for confirmation only
            cout << "IndexShelf: " << indexShelf << " BookID: " << indexBook << endl;

            // Call the PrintBookByShelfAndID helper function to take care of the printing of the book we want
            mainRef.PrintBookByShelfAndID(indexShelf, indexBook);
            break;
        }
        case 'A':
        {
            mainRef.PrintAllBooks();                // In this case simply print all the books in the entire library
            cout << "Returning to centernn";
            break;
        }
        case 'N':
            cout << "Returning." << endl;
            break;
    }
}

// Function that prints the books history of either all books or a desired book
void HandlePrintingBookHistory(BookGroup &mainRef)
{
    cout << "Would you like to enter the name of a book or the ID? 1 for bookID, A for all books, F to search manually , N to return. n";

    // Initializes basic variables that we will use for this function
    bool continueLoop = false;
    bool innerloop = false;
    string bookName;
    char input = ' ';

    // Initial placement loop to determine the outcome based on user value. If it is valid we will either terminate or proceed deeper into the function
    while (!continueLoop)
    {
        // Asks for only a single key press
        input = _getch();

        // If the input isn't a number that we want, then captialize it. Note: This works because we only gather a single key press, so it is impossible for the user to enter 
        // a number that is greater than 9 or less than 0. Negatives can't be counted as the '-' is a special character
        if (input <= 0 || input >= 10)
            input = toupper(input);

        // Switch statement based on that input value the user just entered
        switch (input)
        {
        // Easiest case scenario. Simply print the history of all books and then return
        case 'A':
        {
            mainRef.PrintAllBookHistory();
            cout << "n";
            continueLoop = !continueLoop;
            cout << "Returning.nn";
            return;
        }
        // If this key is pressed, simply proceed forward as this is a main case
        case '1':
            continueLoop = !continueLoop;
            break;
        // This key is also a special case we will proceed again
        case 'F':
            continueLoop = !continueLoop;
            break;
        // This key is a termination key but unlike 'A' we don't print anything and just exist
        case 'N':
            cout << "Returning. nn";
            continueLoop = !continueLoop;
            return;
            break;
        // User didn't print a valid key. Will print a error message and then go back to the top which repeats this entire procees until a valid key is given
        default:
            cout << "Sorry, you didn't enter a valid character. Please enter 1 to use bookID, A for all books F to search for the bookname, or N to return. n";
            break;
        }
    }

    continueLoop = !continueLoop;
    // Placeholder values that hold shelfID and bookID values
    int shelfID(-1);
    int bookID(-1);
    // A string that we will use to hold the users input from now on
    string bookdetector("");

    // The main loop or outer loop that will continue to ask for input until user termination or search failure
    while (!continueLoop)
    {
        // Switch statement based on input
        switch (input)
        {
        case '1':
        {
            cout << "Please enter a shelfID first between 1 to 13.n";
            cin >> bookdetector;                                        // ask for user input

            innerloop = false;
            // Our loop inside of the main loop. This innerloop is attempting to ask the user for a valid shelfID between 1 to 13 or for N to terminate
            while (!innerloop)
            {
                // Checker for the N key. If pressed then it will break the entire function and return.
                if (bookdetector == "N" || bookdetector == "n")
                {
                    cout << "Returning.nn";
                    innerloop = true;
                    return;
                }

                // If the value the user entered is 2 keys or less continue
                if (bookdetector.size() <= 2)
                {
                    // If the user only entered 1 key
                    if (bookdetector.size() == 1)
                    {
                        // If that key is a digit, then set the shelfID to this value. If it isn't a digit, then set the users input to E for error handling
                        if (isdigit(bookdetector[0]))
                        {
                            shelfID = bookdetector[0] - '0';
                        }
                        else
                            bookdetector = "E";
                    }
                    // The user has entered a size that is equal to 2. 
                    else
                    {
                        // If the first key is a digit, then set shelfID to that digit
                        if (isdigit(bookdetector[0]))
                        {
                            shelfID = bookdetector[0] - '0';

                            // If the second key is a digit, then multiply the shelfID by 10, and then add the second key to the shelfID
                            if (isdigit(bookdetector[1]))
                            {
                                shelfID *= 10;
                                shelfID += bookdetector[1] - '0';
                            }
                            // If the second key isn't a digit, then set the users value to E for error handling
                            else
                                bookdetector = "E";
                        }
                        // If the first key isn't a digit, then set the users value to E for error handling
                        else
                            bookdetector = "E";
                    }

                    // Termination key
                    if (bookdetector == "N" || bookdetector == "n")
                    {
                        cout << "Returning.nn";
                        return;
                    }
                    // Error handling key. Will simply print a error message and let the flow proceed normally thus resulting in a repeat
                    else if (bookdetector == "E")
                    {
                        cout << "Invalid value. ShelfID must be between 1 and 13. Retry or press N to terminate.n";
                    }

                    // Another error case. This happens if the user entered a value that doesn't corresspond to the program. Prints 2 sepearte message depending on the case
                    // and lets the program flow normally resulting in a repeat
                    else if (shelfID <= 0 || shelfID >= 14)
                    {
                        if (shelfID == 0)
                            cout << "Error, shelfID is too low. Please enter a value between 1 to 13 or N to terminate.n";
                        else
                            cout << "Error, shelfID is too high. Please enter a value between 1 to 13 or N to terminaten";
                    }

                    // If the user got here they successflly entered a valid shelfID. Decrease it by 1 to fit our loop, since value must be between 1 to 13,
                    // set the innerloop to true now, and then break the flow of the innerLoop which stops repetition
                    else
                    {
                        cout << "Success!! " << shelfID << endl;
                        shelfID -= 1;
                        innerloop = !innerloop;
                        break;
                    }
                }
                // User entered a value of 3 or above. These are not accepted
                else
                {
                    cout << "Value entered is too long.nPlease enter a shelfID between 1 to 13 or N to trminate.n";
                }

                // Asks the user for input again at the end if this loop isn't broken
                cin >> bookdetector;
            }

            // reset the innerloop so we will use it again
            innerloop = false;
            cout << "Please print a bookID between 1 to 12 or press N to terminaten";
            // Exactly the same as the above innerloop but with the book condition instead. So values are between 1 to 12
            while (!innerloop)
            {
                cin >> bookdetector;

                if (bookdetector == "N" || bookdetector == "n")
                {
                    cout << "Returning.nn";
                    innerloop,continueLoop = true;
                    return;
                }

                if (bookdetector.size() <= 2)
                {
                    if (bookdetector.size() == 1)
                    {
                        if (isdigit(bookdetector[0]))
                        {
                            bookID = bookdetector[0] - '0';
                        }
                        else
                            bookdetector = "E";
                    }
                    else
                    {
                        if (isdigit(bookdetector[0]))
                        {
                            bookID = bookdetector[0] - '0';

                            if (isdigit(bookdetector[1]))
                            {
                                bookID *= 10;
                                bookID += bookdetector[1] - '0';
                            }
                            else
                                bookdetector = "E";
                        }
                        else
                            bookdetector = "E";
                    }

                    if (bookdetector == "N" || bookdetector == "n")
                    {
                        cout << "Returning.nn";
                        innerloop = true;
                        continueLoop = true;
                        return;
                    }

                    else if (bookdetector == "E")
                    {
                        cout << "Invalid value. bookID must be between 1 and 12. Retry or press N to terminate.n";
                    }

                    else if (bookID <= 0 || bookID >= 13)
                    {
                        if (bookID == 0)
                            cout << "Error, bookID is too low. Please enter a value between 1 to 12 or N to terminate.n";
                        else
                            cout << "Error, bookID is too high. Please enter a value between 1 to 12 or N to terminaten";
                    }

                    else
                    {
                        cout << "Success!! " << shelfID << endl;
                        bookID -= 1;
                        innerloop = !innerloop;
                        break;
                    }
                }
                else
                {
                    cout << "Value entered is too long.nPlease enter a bookID between 1 to 12 or N to trminate.n";
                }

            }

            // Create a book reference so we can check if the shelfID and bookID found a valid book
            Book myBook = mainRef.GetBook(shelfID, bookID);

            // If the book we just created isn't empty, then print that books history, otherwise print a error message. Regardless of outcome, terminate this function
            if (!myBook.emptyBook() )
                mainRef.PrintBookHistoryByBook(myBook.name);
            else
                cout << "Sorry, but that book doesn't exist. Returning.nn";

            continueLoop = true;
            break;
        }
        // The case when the user presses the F key. Allows them to find the book by its name
        case 'F':
        {
            cout << "Please enter the name of the desired book.n";
            std::getline(std::cin, bookName);

            // If the bookName the user entered exists inside the main structre, then print that books history and terminate this loop
            if (mainRef.FindBook(bookName))
            {
                mainRef.PrintBookHistoryByBook(bookName);
                continueLoop = !continueLoop;
            }
            // If the bookname isn't valid, basically ask them if they'd like to try again. If the input they entered isn't a number between the values we want, captialize it.
            else
            {
                cout << "Would you like to try again (F) or terminate(N)?n";
                input = _getch();
                if (input < 0 || input >= 10)
                    input = toupper(input);
            }

            break;
        }
        case 'N':
            cout << "Returning.nn";
            continueLoop = !continueLoop; 
            return;
        default:
        {
            cout << "Sorry, invalid character entered. Please type 1 to search by 1, F to search by name, or N to terminate.n";
            input = _getch();
            if (input < 0 || input >= 10)
                input = toupper(input);
        }
        }
    }
}
Войти в полноэкранный режим Выход из полноэкранного режима

};

Следующая функция — это функция setUpBookNames. Это действительно быстрая и простая функция. Она принимает параметр, указывающий на активный экземпляр BookGroup, и добавляет список случайных названий видеоигр в качестве названий книг и случайный набор страниц, вызывая функцию AddBooks внутри параметра BookGroup.

Это выглядит следующим образом:

ref.AddBooks(Alphabet, books_Ref, Pages);
Вход в полноэкранный режим Выход из полноэкранного режима

Переменная Alphabet — строковый массив названий типа «Crash Bandicoot», «Jack and Daxter», «Horizon Zero Dawn», а books_Ref — массив целых чисел, имеющих случайные значения от 20 до 1020. Вот один из таких индексов, хотя все они выглядят одинаково:

rand() % 1000 + 20
Вход в полноэкранный режим Выход из полноэкранного режима

Далее следует функция InitializeLibraryGuests, которая принимает в качестве единственного аргумента указатель типа Person. Я использую это пространство для установки двух массивов строк, оба значительных размеров; один только из имен, другой только как фамилии. В конце этой функции я объединяю их в одно имя, передавая их оба вместе в качестве аргумента в аргументе указателя Person, а также увеличивая статический целочисленный ID на единицу для каждого успешного имени и передавая его также. Массивы строк FirstNames и LastNames основаны на переменной LibaryGuestSize, которая, как мы помним, является глобальной. Сгенерировав случайный индекс в этом диапазоне, я могу легко получить индекс, который я использовал для прямого подключения к обоим соответствующим строковым массивам.

Это выглядит примерно так:

// Go through a loop based on the guest size and setup the guests values
    for (int i = 0; i < LibraryGuestSize; ++i)
    {
        randomFirstIndex = rand() % LibraryGuestSize;       // Generate a random number between 0 and LibraryGuestSize(156)
        randomLastIndex = rand() % LibraryGuestSize;        // Same as the above but inside lastindex

Вход в полноэкранный режим Выход из полноэкранного режима
    // Make the guests name a full value based on random selections between the first name and last names. The indicies we made are used as selectors
    Guests[i].name = FirstNames[randomFirstIndex] + " " + LastNames[randomLastIndex];

    // Increase the static integer value by 1 so every guests has a incremeting ID value
    ID += 1;
    Guests[i].GuestID = ID;         // Set the guests' ID to the static ID.
}
Войти в полноэкранный режим Выход из полноэкранного режима

}

Последняя часть, которую необходимо обсудить, это та, которая собирает все вместе, и это функция main. Функция main сначала создает массив лиц для симуляции:

Person *LibraryGuests = new Person[LibraryGuestSize];
Вход в полноэкранный режим Выход из полноэкранного режима

Затем она настраивает их для использования, вызывая функцию InitializeLibraryGuest следующим образом:

InitializeLibraryGuests(LibraryGuests);
Войти в полноэкранный режим Выход из полноэкранного режима

А затем создает ссылку на BookGroup и вызывает setUpBookNames, используя этот обработчик.

После этого переменная LibraryGuests, которая была только что создана, добавляется в обработчик BookGroup вместе со случайной книгой в цикле for для легкого доступа. Этот код выглядит следующим образом:

string myBook;
myBook = mainHandle.SelectRandomBook();                     // Selects a random book from somewhere on the shelf and returns its value
            mainHandle.CheckoutBook(LibraryGuests[i], myBook);          // Makes it so that every user checks out a book
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, цикл while используется для различных случаев выбора чисел путем проверки ввода с помощью переменной держателя символов и класса UserInputHelperClass, который обрабатывает все дополнительные случаи и возвращается сюда по завершении или по желанию.

Postmortem
Самое главное, что я бы сделал по-другому, если бы делал это снова, — это изменил бы способ извлечения данных из книг. Хотя все хорошо так, как есть сейчас, я думаю, что все было бы лучше, если бы я просто использовал хэш-таблицу для ключей и индексов, что я в некоторой степени и сделал, но не так, как мне нравится. Я решил не использовать LinkedList, который я добавил, потому что это не имело смысла для меня в то время, постоянно удаляя и удаляя узлы, поскольку они всегда статичны в любом случае. Однако я мог бы просто очистить данные внутри узла, пока он не станет активным. Я не разочарован тем, что сделал, просто интересно, мог ли я сделать это по-другому. Изначально я хотел взять книги, напечатанные BookHistory, и добавить их в файл, который можно было бы легко увидеть. К сожалению, я не сделал этого, а хотелось бы. Было бы здорово посмотреть, потому что результаты не статичны. Еще одна вещь, которую я хотел сделать, это сделать данные более гибкими. Под гибкостью я подразумеваю, что пользователи могут добавлять книги и имена в базу данных, а не жестко кодировать ее, как сейчас. Несмотря на эти недостатки, я все равно доволен тем, что получилось, и это определенно можно улучшить.
Это было довольно длинное и подробное объяснение, и я надеюсь, что оно не было слишком плохим с точки зрения длины. Вы можете посетить мою страницу на GitHub, если хотите попробовать эту программу самостоятельно, здесь.

Спасибо за чтение, если вы дочитали до этого момента, и не стесняйтесь проверить некоторые из моих других работ по кодированию.

Искренне,

Кристиан Бэнкс

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *