Search code examples
c++oopc++17range-based-loop

C++ Why are my Objects being destroyed, and sometimes twice in a row when using range based for loop


I'm a beginner programmer looking to understand why my objects are being deleted, sometimes even twice. I'm trying to avoid creating them on the heap for this project as that is a more advance topic I will try at a later time.

Whats causing the book1, book2 etc.. objects to be instantly deleted? Also it outputs the same book object being deleted twice in a row sometimes for some reason? The same effect happens when using 'case 3: ' in the switch statement through the menu.

#include <iostream>
#include <vector>

using namespace std;

class Book
{
    public:
        // Constructor Prototype
        Book(string author = "Auth", string title = "Title", string publisher = "Pub", float price = 1.99, int stock = 1);

        // Destructor Prototype
        ~Book();

        // Methods
        string get_title() const
        {
            return title;
        }

    private:
        string author;
        string title;
        string publisher;
        float price;
        int stock;
};

// Constructor definition
Book::Book(string author, string title, string publisher, float price, int stock)
    : author{author}, title{title}, publisher{publisher}, price{price}, stock{stock}
{
    cout << "Book: " << title << " was created." << endl;
}

// Destructor definition
Book::~Book()
{
    cout << "Book: " << title << " has been destroyed." << endl;
}

// Functions
void showMenu();
void getChoice();
void userChoice(int choice);
void showBooks(vector<Book> bookList);

// Global Vector
vector<Book> bookList;

int main()
{
    // Creating Book objects for testing purposes
    Book book1("Marcel Proust", "In Search of Lost Time", "Pub", 14.99, 5);
    Book book2("James Joyce", "Ulysses", "Pub", 25.99, 4);
    Book book3("Miguel de Cervantes", "Don Quixote", "Pub", 35.99, 3);
    Book book4("Gabriel Garcia Marquez", "One Hundred Years of Solitude", "Pub", 100.99, 2);
    Book book5("F. Scott Fitzgerald", "The Great Gatsby", "Pub", 49.99, 1);

    // Pushing book1-5 into the vector of Book objects
    bookList.push_back(book1);
    bookList.push_back(book2);
    bookList.push_back(book3);
    bookList.push_back(book4);
    bookList.push_back(book5);

    while(true)
    {
        showMenu();
        getChoice();
    }

    return 0;
}

void showMenu()
{
    cout << "\tMENU" << endl;
    cout << "1. Purchase Book" << endl;
    cout << "2. Search for Book" << endl;
    cout << "3. Show Books available" << endl;
    cout << "4. Add new Book" << endl;
    cout << "5. Edit details of Book" << endl;
    cout << "6. Exit" << endl;
}

void getChoice()
{
    int choice;
    cout << "Enter your choice (1-6): ";
    cin >> choice;
    userChoice(choice);
}

void userChoice(int choice)
{
    switch(choice)
    {
        case 1:
            cout << "Case 1 called." << endl;
            break;
        case 2:
            cout << "Case 2 called." << endl;
            break;
        case 3:
            cout << "Case 3 called." << endl;
            showBooks(bookList);
            break;
        case 4:
            cout << "Case 4 called." << endl;
            break;
        case 5:
            cout << "Case 5 called." << endl;
            break;
        case 6:
            cout << "Case 6 called." << endl;
            exit(0);
    }
}

void showBooks(vector<Book> bookList)
{
    for (Book bk : bookList)
    {
        cout << "Book: " << bk.get_title() << endl;
    }
}

Edit:

Why does it delete the same object 2-3 times in a row sometimes? output (in this case it was "In Search of Lost Time"):

Book: In Search of Lost Time was created.
Book: Ulysses was created.
Book: Don Quixote was created.
Book: One Hundred Years of Solitude was created.       
Book: The Great Gatsby was created.
Book: In Search of Lost Time has been destroyed.       
Book: In Search of Lost Time has been destroyed.       
Book: Ulysses has been destroyed.
Book: In Search of Lost Time has been destroyed. 

Solution

  • As per the various suggestions in the comments I have compiled the program to suit your needs. It will work without any unwanted allocations. Changes I made are: 1.Reserving the vector to 5 elements. 2. Emplacement. 3. Passing the vector as a reference to the function and 4. Taking the vector elements as a reference while iteration.

    #include <iostream>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    class Book
    {
    public:
        // Constructor Prototype
        Book(string author = "Auth", string title = "Title", string publisher = "Pub", float price = 1.99f, int stock = 1);
    
        // Destructor Prototype
        ~Book();
    
        // Methods
        string get_title() const
        {
            return title;
        }
    
    private:
        string author;
        string title;
        string publisher;
        float price;
        int stock;
    };
    
    // Constructor definition
    Book::Book(string author, string title, string publisher, float price, int stock)
        : author( author ), title( title ), publisher( publisher ), price( price ), stock( stock )
    {
        cout << "Book: " << title << " was created." << endl;
    }
    
    // Destructor definition
    Book::~Book()
    {
        cout << "Book: " << title << " has been destroyed." << endl;
    }
    
    // Functions
    void showMenu();
    void getChoice();
    void userChoice(int choice);
    void showBooks(vector<Book>& bookList);
    
    // Global Vector
    vector<Book> bookList;
    
    int main()
    {
        bookList.reserve(5);
        bookList.emplace_back("Marcel Proust", "In Search of Lost Time", "Pub", 14.99f, 5);
        bookList.emplace_back("James Joyce", "Ulysses", "Pub", 25.99f, 4);
        bookList.emplace_back("Miguel de Cervantes", "Don Quixote", "Pub", 35.99f, 3);
        bookList.emplace_back("Gabriel Garcia Marquez", "One Hundred Years of Solitude", "Pub", 100.99f, 2);
        bookList.emplace_back("F. Scott Fitzgerald", "The Great Gatsby", "Pub", 49.99f, 1);
    
        while (true)
        {
            showMenu();
            getChoice();
        }
    
        return 0;
    }
    
    void showMenu()
    {
        cout << "\tMENU" << endl;
        cout << "1. Purchase Book" << endl;
        cout << "2. Search for Book" << endl;
        cout << "3. Show Books available" << endl;
        cout << "4. Add new Book" << endl;
        cout << "5. Edit details of Book" << endl;
        cout << "6. Exit" << endl;
    }
    
    void getChoice()
    {
        int choice;
        cout << "Enter your choice (1-6): ";
        cin >> choice;
        userChoice(choice);
    }
    
    void userChoice(int choice)
    {
        switch (choice)
        {
        case 1:
            cout << "Case 1 called." << endl;
            break;
        case 2:
            cout << "Case 2 called." << endl;
            break;
        case 3:
            cout << "Case 3 called." << endl;
            showBooks(bookList);
            break;
        case 4:
            cout << "Case 4 called." << endl;
            break;
        case 5:
            cout << "Case 5 called." << endl;
            break;
        case 6:
            cout << "Case 6 called." << endl;
            exit(0);
        }
    }
    
    void showBooks(vector<Book>& bookList)
    {
        for (const auto& book : bookList){
            cout << "Book: " << book.get_title() << endl;
        }
    }