'incrementing a counter for every object [closed]

I have a class called Student, and in that class I have a std::map of books that looks like this:

std::map<std::string,int>books;

The string is the book name, and the int is the number of students that have the same book.

Then I create a new Student, and when I do I want that Student to also get the same books, so I do a copy like this:

new_student->getBookmap() = old_student->getBookmap();
new_student_3->getBookmap() = old_student->getBookmap();

getBookmap() returns the map for each Student. I have them as private members.

The new Student gets the exact copy of the same map, and now I want to increase the counter, but the counter should also be increased for all three Students.

But, if I increase the counter for one Student, the other Students' counters don't increase.

for (auto it = new_student->getBookmap().begin(); it != new_student->getBookmap().end(); it++)
{
    it->second++;
}

When I do this, the counter only increases for the new_student, and not for old_student and new_student_1.

Could someone help me out?



Solution 1:[1]

The problem with your code is that each Student object has its own copy of the std::map. You need them to share a single std::map object instead.

The simplest way to share the books field is to make it a static member of Student, then you don't need to copy it around at all, eg:

using bookCounter = std::map<std::string, int>;

class Student {
private:
    static bookCounter books;
    ...
    void checkoutBook(std::string bookName);
    void returnBook(std::string bookName);
    ...
};
bookCounter Student::books;

void Student::checkoutBook(std::string bookName)
{
    books[bookName]++;
}

void Student::returnBook(std::string bookName)
{
    books[bookName]--;
}
int main()
{
    ...
    new_student = new Student();
    new_student->checkoutBook("...");
    ...
    new_student->returnBook("...");
    ...
}

Or, you can declare books as a global variable, say near main(), and then just refer to that variable inside of Students when needed, eg:

class Student {
    ...
};

using bookCounter = std::map<std::string, int>;
extern bookCounter books;
void Student::checkoutBook(std::string bookName)
{
    books[bookName]++;
}

void Student::returnBook(std::string bookName)
{
    books[bookName]--;
}
bookCounter books;

int main()
{
    ...
}

Or, you could make books be a local variable of main() itself, and then you can pass a pointer/reference to it to each Student, eg:

using bookCounter = std::map<std::string, int>;

class Student {
private:
    bookCounter &books;
    ...
public:
    Student(bookCounter &books) : books(books) {}
    ...
    void checkoutBook(std::string bookName);
    void returnBook(std::string bookName);
    ...
};
void Student::checkoutBook(std::string bookName)
{
    books[bookName]++;
}

void Student::returnBook(std::string bookName)
{
    books[bookName]--;
}
int main()
{
    bookCounter books;
    ...
    new_student = new Student(books);
    new_student->checkoutBook("...");
    ...
    new_student->returnBook("...");
    ...
}

Alternatively, you might consider using std::shared_ptr to share a single std::map object amongst the various Student objects, eg:

using bookCounter = std::map<std::string, int>;

class Student {
private:
    std::shared_ptr<bookCounter> books;
    ...
public:
    Student(std::shared_ptr<bookCounter> books) : books(books) {}
    ...
    void checkoutBook(std::string bookName);
    void returnBook(std::string bookName);
    ...
};
void Student::checkoutBook(std::string bookName)
{
    (*books)[bookName]++;
}

void Student::returnBook(std::string bookName)
{
    (*books)[bookName]--;
}
int main()
{
    auto books = std::make_shared<bookCounter>();
    ...
    new_student = new Student(books);
    new_student->checkoutBook("...");
    ...
    new_student->returnBook("...");
    ...
}

In which case, you might instead consider changing the std::map into a std::vector of std::shared_ptr<Book> objects, to share individual book objects rather then just strings, and not manage your own counters at all (std::shared_ptr has its own counter, which you can query via its use_count() method), eg:

struct Book : std::enable_shared_from_this<Book>
{
    std::string name;
    ...
    int checkoutCount() const;

    [[nodiscard]] static std::shared_ptr<Book> create();

private:
    Book() = default;
};
using bookPtr = std::shared_ptr<Book>;

class Student {
private:
    std::vector<bookPtr> books;
    ...
public:
    ...
    void checkoutBook(std::string bookName);
    void returnBook(std::string bookName);
    ...
};
std::vector<bookPtr> library;

std::shared_ptr<Book> Book::create()
{
    return std::shared_ptr<Book>(new Book);
}

int Book::checkoutCount() const
{
    auto pthis = shared_from_this();
    return (pthis.use_count() - 2); // not counting pthis and the library ...
}

void Student::checkoutBook(std::string bookName)
{
    auto iter = std::find_if(library.begin(), library.end(),
        [&](bookPtr &book){ return book->name == bookName; }
    );
    if (iter != library.end())
        books.push_back(*iter);
}

void Student::returnBook(std::string bookName)
{
    auto iter = std::find_if(books.begin(), books.end(),
        [&](bookPtr &book){ return book->name == bookName; }
    );
    if (iter != books.end())
        books.erase(*iter);
}
int main()
{
    // populate library with Books as needed...
    ...
    new_student = new Student();
    new_student->checkoutBook("...");
    ...
    new_student->returnBook("...");
    ...
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1