C++ Modifying class object within a vector - c++

My issue is my last increment_watch function. I would like to be able modify parts of the Movie class while it is in a vector container. Right now I am only able to grab a specific Movie object and its increment but I am unable to change or modify any of it while it is in a vector. I would like to be able to modify or add +1 to the watched element. Is this at all possible?
EDIT: I have updated the "increment_watched" function taking suggestions from the comments. I have tried to pass by reference the watched variable but the "mov.set_watched" call in the increment_watched function is not taking effect.
#include <iostream>
#include <vector>
#include <string>
class Movie {
friend std::ostream &operator<<(std::ostream &os, const Movie &p);
private:
std::string name;
std::string ratting;
int watched;
public:
Movie() = default;
Movie(std::string name, std::string ratting, int watched)
: name{name}, ratting{ratting}, watched{watched} {}
void increment_watched(std::vector<Movie> vec, std::string name);
bool operator<(const Movie &rhs) const { // always overload
return this->watched < rhs.watched;
}
bool operator==(const Movie &rhs) const {
return (this->name == rhs.name && this->watched == rhs.watched); // always overload
}
void set_name(std::string name) {
this->name = name;
}
std::string getName() {
return name;
}
void set_ratting(std::string ratting) {
this->ratting = ratting;
}
std::string getRatting() {
return ratting;
}
void set_watched(int watched) {
this->watched = watched;
}
int getWatched() {
return watched;
}
};
std::ostream &operator<<(std::ostream &os, const Movie &p) {
os << p.name << " : " << p.ratting << " : " << p.watched;
return os;
}
// template function to display any vector
template <typename T>
void display(const std::vector<T> &lem) {
std::cout << "[ ";
for (const auto &elem: lem)
std::cout << elem << " ";
std::cout << " ]"<< std::endl;
}
// I want to modify this function to increment the "watched" variable by one
void increment_watched(std::vector<Movie> vec, std::string name, int &watched){
watched =+ 1;
for (auto &mov: vec){
if (mov.getName() == name){
std::cout << name << std::endl;
mov.set_watched(watched);
}
}
}
int main() {
std::vector<Movie> vec;
int watched = 1;
Movie p1 {"Star Wars", "PG", 2};
Movie p2 {"Indiana Jones", "PG", 1};
Movie p3 {"Matrix", "PG-13", 5};
vec.push_back(p2);
vec.push_back(p3);
std::cout << p1.getName() << std::endl;
std::cout << p1.getRatting() << std::endl;
p1.set_watched(100);
vec.push_back(p1);
std::cout << p1.getWatched() << std::endl;
display(vec);
increment_watched(vec, "Star Wars", watched);
display(vec);
return 0;
}

Thank you user WhozCraig, this lead me to answering my question!
You response lead the to the correct output. I wanted to change the values and completely over looked passing the vec variable to pass by reference. I knew it was something small that I was over looking
To all those who posted helpful comments to help post better questions on the site, thank you. I am new to StackOverflow. I will review the provided helpful links in order to post better questions in the future. Some commented that I posted the same question twice, I panicked and deleted the post because it was locked, I didnt know what to do so I thought I would start over.
Here is the corrected code:
#include <iostream>
#include <vector>
#include <string>
class Movie {
friend std::ostream &operator<<(std::ostream &os, const Movie &p);
private:
std::string name;
std::string ratting;
int watched;
public:
Movie() = default;
Movie(std::string name, std::string ratting, int watched)
: name{name}, ratting{ratting}, watched{watched} {}
void increment_watched(std::vector<Movie> vec, std::string name);
bool operator<(const Movie &rhs) const { // always overload
return this->watched < rhs.watched;
}
bool operator==(const Movie &rhs) const {
return (this->name == rhs.name && this->watched == rhs.watched); // always overload
}
void set_name(std::string name) {
this->name = name;
}
std::string getName() {
return name;
}
void set_ratting(std::string ratting) {
this->ratting = ratting;
}
std::string getRatting() {
return ratting;
}
void set_watched(int watched) {
this->watched = watched;
}
int getWatched() {
return watched;
}
};
std::ostream &operator<<(std::ostream &os, const Movie &p) {
os << p.name << " : " << p.ratting << " : " << p.watched;
return os;
}
// template function to display any vector
template <typename T>
void display(const std::vector<T> &lem) {
std::cout << "[ ";
for (const auto &elem: lem)
std::cout << elem << " ";
std::cout << " ]"<< std::endl;
}
// I want to modify this function to increment the "watched" variable by one
void increment_watched(std::vector<Movie> &vec, std::string name, int &watched){
for (auto &mov: vec){
if (mov.getName() == name){
std::cout << name << std::endl;
mov.set_watched(watched + 1);
}
}
}
int main() {
std::vector<Movie> vec;
int watched = 3;
Movie p1 {"Star Wars", "PG", 2};
Movie p2 {"Indiana Jones", "PG", 1};
Movie p3 {"Matrix", "PG-13", 5};
vec.push_back(p2);
vec.push_back(p3);
std::cout << p1.getName() << std::endl;
std::cout << p1.getRatting() << std::endl;
p1.set_watched(100);
vec.push_back(p1);
std::cout << p1.getWatched() << std::endl;
display(vec);
increment_watched(vec, "Star Wars", watched);
display(vec);
return 0;
}

Related

Output of map of set custom objects c++

So I have a map that has user defined keys and the values in it are sets of objects too. So I'm trying to write some print function but I have no idea how to do that. (I'm kind of new to maps and sets).
My problem function:
void print() const
{
for (auto& itr : my_mmap)
{
std::cout << "Key for this set:" << itr.first << "\n\n";
for (int i = 0; i< itr.second.size(); i++)
{
std::cout << itr.second[i] << std::endl;
}
}
}
Here's my class:
#include <iostream>
#include <map>
#include <string>
#include <tuple>
#include <utility>
#include <set>
class Enclosing {
private:
class Key {
int m_number;
std::string m_name;
public:
Key(int num, std::string name) :m_number(num), m_name(std::move(name)) {};
bool operator<(const Key& rhs) const {
return std::tie(m_number, m_name) < std::tie(rhs.m_number, rhs.m_name);
}
friend std::ostream& operator<<(std::ostream& os, const Key& k) {
return os << '{' << k.m_number << ',' << k.m_name << '}';
}
};
class Nested {
std::string m_str;
double m_dbl;
bool m_boolean;
public:
Nested(std::string str, double dbl, bool boolean) :m_str(std::move(str)), m_dbl(dbl), m_boolean(boolean) {};
friend std::ostream& operator<<(std::ostream& os, const Nested& n) {
return os << '{' << n.m_str << ',' << n.m_dbl << ',' << n.m_boolean << '}';
}
};
std::multimap<Key, std::set<Nested>> my_mmap;
public:
template <class... Args>
void add_new_object_to_mmap(Args&&... args) {
my_mmap.emplace(std::piecewise_construct, std::forward<Args>(args)...);
}
/*
THAT PROBLEM FUNCTION
*/
void print() const
{
for (auto& itr : my_mmap)
{
std::cout << "Key for this set:" << itr.first << "\n\n";
for (int i = 0; i< itr.second.size(); i++)
{
std::cout << itr.second[i] << std::endl;
}
}
}
static Enclosing& get_singleton() {
static Enclosing instance;
return instance;
}
};
}
So the problem is that I am getting an error "no operator "[]" match these operands". How can I output my map and set in the best way?
The problem is that we cannot use indexing on a std::set. Thus itr.second[i] is not valid because itr.second is an std::set.
To solve this you can use a range-based for loop as shown below:
for (const auto&elem:itr.second)
{
std::cout << elem << std::endl;
}

Saving vector of objects using SDL2

Im learning SDL2 and now im trying to save a game ranking into a .bin file. I have placed the ranking data into a vector. But im unable to save the data. Perhabs it has to do with file size, but im unable to solve that. This is the code that im using now:
class Player {
private:
std::string name, faction, dific;
int points;
public:
Player() {};
virtual ~Player() {};
void addName(std::string s);
void addFacti(std::string s);
void addDific(std::string s);
void addPoint(int p);
std::string getName() const;
std::string getFacti() const;
std::string getDific() const;
int getPoint() const;
bool operator>(const Player& p) const;
};
Player p1; Player p2;//just two examples
//add properties to each object
std::vector<Player>classi;
classi.push_back(p1); classi.push_back(p2);
std::sort(classi.begin(), classi.end(), std::greater<Player>());
//load file
SDL_RWops* rankfile = SDL_RWFromFile("ranking.bin", "r+b");
for (int i = 0; i < classi.size(); ++i) { SDL_RWread(rankfile, &classi[i], sizeof(Player), 1); }
SDL_RWclose(rankfile);
//im able to render the objects
//save file - but it doesnt save anything
rankfile = SDL_RWFromFile("ranking.bin", "w+b");
for (int i = 0; i < classi.size(); ++i) { SDL_RWwrite(rankfile, &classi[i], sizeof(Player), 1); }
SDL_RWclose(rankfile);
Here's an example of how I might serialize a struct containing std::string to/from binary. It doesn't use the SDL functions because I don't use SDL but it wouldn't be difficult to modify if desired.
You may have a different problem since you say your file is empty, and I am concerned that you try and read the file before you write it, but you could try something like this and see if it helps.
#include <iostream>
#include <string>
#include <fstream>
#include <tuple>
template <typename T>
std::ostream &writeBinary(std::ostream &f, T data)
{
return f.write(reinterpret_cast<char *>(&data), sizeof(data));
}
std::ostream &writeBinary(std::ostream &f, const std::string &str)
{
// If file size is a concern you might use a smaller type like uint16_t.
// Just make sure to mirror the change in readBinary.
std::string::size_type sz = str.size();
if (f)
{
f.write(reinterpret_cast<char *>(&sz), sizeof(sz));
}
if (f)
{
f.write(str.data(), str.size());
}
return f;
}
template <typename T>
std::istream &readBinary(std::istream &f, T &data)
{
if (f)
{
f.read(reinterpret_cast<char *>(&data), sizeof(data));
}
return f;
}
std::istream &readBinary(std::istream &f, std::string &str)
{
std::string::size_type sz = 0;
if (f)
{
f.read(reinterpret_cast<char *>(&sz), sizeof(sz));
}
if (f)
{
str.resize(sz);
f.read(str.data(), sz);
}
return f;
}
struct Thing
{
std::string shortString;
int i;
double d;
std::string longString;
Thing()
: i(99)
, d(99.99)
{ }
bool operator==(const Thing &rhs) const
{
return std::tie(shortString, i, d, longString)
== std::tie(rhs.shortString, rhs.i, rhs.d, rhs.longString);
}
bool write(std::ofstream &f)
{
if (!writeBinary(f, shortString))
{
return false;
}
if (!writeBinary(f, i))
{
return false;
}
if (!writeBinary(f, d))
{
return false;
}
if (!writeBinary(f, longString))
{
return false;
}
return true;
}
bool read(std::ifstream &f)
{
if (!readBinary(f, shortString))
{
return false;
}
if (!readBinary(f, i))
{
return false;
}
if (!readBinary(f, d))
{
return false;
}
if (!readBinary(f, longString))
{
return false;
}
return true;
}
};
std::ostream &operator<<(std::ostream &o, const Thing &t)
{
return o << "'" << t.shortString << "'" << ", "
<< t.i << ", " << t.d << ", "
<< "'" << t.longString << "'";
}
int main()
{
Thing t1;
// Shorter string to hopefully fit in any short string optimization buffer in the string.
t1.shortString = "Short";
t1.longString = "Long string that should be long enough to not fit in the SSO buffer.";
t1.i = 42;
t1.d = 42.42;
std::cout << "t1 Before Write: " << t1 << "\n";
std::ofstream out("thing.bin", std::ios::binary);
if (!t1.write(out))
{
std::cout << "Error writing t1!\n";
return -1;
}
out.close();
std::cout << "t1 After Write: " << t1 << "\n";
Thing t2;
std::cout << "t2 Before Read: " << t2 << "\n";
std::ifstream in("thing.bin", std::ios::binary);
if (!t2.read(in))
{
std::cout << "Error reading t2!\n";
return -1;
}
in.close();
std::cout << "t2 After Read: " << t2 << "\n";
std::cout << "t1 == t2: " << std::boolalpha << (t1 == t2) << "\n";
return 0;
}

No match for operator << in exception handling program

#include <iostream>
#include <string>
#include "NegativeBalanceException.hpp"
#include <memory>
class Account
{
private:
std::string name;
double balance;
public:
Account(std::string name, double balance);
~Account() {std::cout <<"Calling Account Destroyer" << std::endl;}
void get_name() const {std::cout << name << std::endl;}
void get_balance() const {std::cout << balance << std::endl;}
friend std::ostream &operator<<(std::ostream &os, const Account &account);
};
Account::Account(std::string name, double balance)
: name{name}, balance{balance} {
if (balance < 0)
throw NegativeBalanceException();
}
std::ostream &operator<<(std::ostream &os, const Account &account) {
os << "Account Name: " << account.get_name() << "\n" << "Account
Balance: " << account.get_balance() << std::endl;
return os;
}
int main () {
std::unique_ptr<Account> Austin;
try {
Austin = std::make_unique<Account>("Austin",1000);
std::cout << *Austin << std::endl;
}
catch (const NegativeBalanceException &ex) {
std::cerr << ex.what() << std::endl;
}
};
Hello I am a beginner programmer and I am practicing exception handling and I do not know why my overloaded operator << is not working. It won't let me display my data that I want.
Your get_name() and get_balance() methods are both declared with void return values, so your overloaded operator<< can't pass them to os << .... Internally, the methods are doing their own logging to std::cout, so you would need to change your operator<< accordingly, eg:
std::ostream &operator<<(std::ostream &os, const Account &account) {
os << "Account Name: ";
account.get_name();
os << "Account Balance: ";
account.get_balance();
return os;
}
However, this will not work as expected if os does not refer to std::cout, for example if your main() decided to stream the Account to a std::ofstream instead.
A better option is to change get_name() and get_balance() to return values instead, and not do their own logging directly:
std::string get_name() const { return name; }
double get_balance() const { return balance; }
Then your overloaded operator<< will work as expected.
These
void get_name() const {std::cout << name << std::endl;}
void get_balance() const {std::cout << balance << std::endl;}
should be written like this
std::string get_name() const { return name; }
double get_balance() const { return balance; }
Functions which return values are not the same as functions which print values. In your overloaded operator<< you need two functions to return the values in the account object, so the operator<< can print them.

I want to create a shared ptr vector of different objects

You have 2 classes Cats and Dogs and need to create a vector of shared pointers which stores the data from those 2 classes. Hints: polymorphism and keep in mind that classes can have similar fields.
So this is what I've done until now. I want to insert in that shared_ptr vector all the info Cats and Dogs classes have, but I don't know how. I only managed to insert in that vector the data from the base class.
#include <iostream>
#include <vector>
#include <memory>
class Animal
{
protected:
int tip;
std::string name;
int age;
public:
Animal(int t, std::string n, int a): tip(t), name(n), age(a) {}
friend std::ostream& operator<<(std::ostream& os, const Animal& a)
{
os << "Name: " << a.name << std::endl;
os << "Age: " << a.age << std::endl;
return os;
}
};
class Cats: public Animal
{
std::string race;
std::string pref_food;
public:
Cats(int t = 0, std::string n = "", int a = 0, std::string r = "", std::string mnprf = ""):
Animal(t, n, a), race(r), pref_food(mnprf) {}
friend std::ostream& operator<<(std::ostream& os, const Cats& c)
{
// auto n = static_cast<Animal> (c);
os << "Name: " << c.name << std::endl;
os << "Age: " << c.age << std::endl;
os << "race: " << c.race << std::endl;
os << "Fav food: " << c.pref_food << std::endl;
return os;
}
};
class Dog: public Animal
{
std::string disease;
std::string master;
public:
Dog(int t = 1, std::string n = "", int a = 0, std::string b = "", std::string s = "" ):
Animal(t, n, a), disease(b), master(s) {}
friend std::ostream& operator<<(std::ostream& os, const Dog& d)
{
os << "Name: " << d.name << std::endl;
os << "Age: " << d.age << std::endl;
os << "disease: " << d.disease << std::endl;
os << "master: " << d.master << std::endl;
return os;
}
};
template<typename T>
void add(std::vector<std::shared_ptr<Animal>>& vec, const T& a)
{
auto newptr = std::make_shared<Animal>(a);
vec.push_back(newptr);
}
int main()
{
std::vector<std::shared_ptr<Animal>> Animals;
Dog d(1,"Rex", 12, "idk", "Oscar");
Cats c(0,"Meaw", 11, "Sfinx", "Catfood");
add(Animals,d);
add(Animals,c);
for(auto i: Animals)
{
std::cout << *i;
}
}
There are a few problems with your code:
Animal lacks any virtual methods. At the very least, it needs a virtual destructor, so that the destructors of Cats and Dog are called correctly when shared_ptr<Animal> calls delete on its held Animal* pointer.
add() is creating an instance of Animal specifically, regardless of T. So your vector contains only real Animal objects. add() needs to create an instance of T instead. A std::shared_ptr<T> can be assigned to a std::shared_ptr<Animal> when T derives from Animal. Of course, add() is redundant, main() can just create and add the new objects directly to its vector without using add() at all.
When main() calls operator<< on an Animal, it will not call the operator<< defined by Cats or Dog, only the operator<< defined by Animal. This can be fixed by having operator<< in Animal call a virtual method that Cats and Dog override. There is no need to define operator<< in derived classes when the base class also has an operator<<.
Try this instead:
#include <iostream>
#include <vector>
#include <memory>
class Animal
{
protected:
int tip;
std::string name;
int age;
public:
Animal(int t, std::string n, int a): tip(t), name(n), age(a) {}
virtual ~Animal() {}
virtual void print(std::ostream& os) const
{
os << "Name: " << name << std::endl;
os << "Age: " << age << std::endl;
}
friend std::ostream& operator<<(std::ostream& os, const Animal& a)
{
a.print(os);
return os;
}
};
class Cat : public Animal
{
std::string race;
std::string pref_food;
public:
Cat(int t = 0, std::string n = "", int a = 0, std::string r = "", std::string mnprf = ""):
Animal(t, n, a), race(r), pref_food(mnprf) {}
void print(std::ostream& os) const override
{
Animal::print(os);
os << "race: " << race << std::endl;
os << "Fav food: " << pref_food << std::endl;
}
};
class Dog : public Animal
{
std::string disease;
std::string master;
public:
Dog(int t = 1, std::string n = "", int a = 0, std::string b = "", std::string s = ""):
Animal(t, n, a), disease(b), master(s) {}
void print(std::ostream& os) const override
{
Animal::print(os);
os << "disease: " << disease << std::endl;
os << "master: " << master << std::endl;
}
};
template<typename T>
void add(std::vector<std::shared_ptr<Animal>> &vec, const T &a)
{
auto newptr = std::make_shared<T>(a);
vec.push_back(newptr);
}
int main()
{
std::vector<std::shared_ptr<Animal>> Animals;
Dog d(1,"Rex", 12, "idk", "Oscar");
Cat c(0,"Meaw", 11, "Sfinx", "Catfood");
add(Animals, d);
add(Animals, c);
/* alternatively:
Animals.push_back(std::make_shared<Dog>(1,"Rex", 12, "idk", "Oscar"));
Animals.push_back(std::make_shared<Cat>(0,"Meaw", 11, "Sfinx", "Catfood"));
*/
for(auto &i: Animals)
{
std::cout << *i;
}
return 0;
}
Output:
Name: Rex
Age: 12
disease: idk
master: Oscar
Name: Meaw
Age: 11
race: Sfinx
Fav food: Catfood
Live Demo

C++ - copy-on-write basic implementation

We are supposed to use copy-on-write on our school project. I've been experimenting with a very simple class, but without any luck. I have this:
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
class CPerson {
public:
CPerson ();
CPerson (const CPerson&);
~CPerson ();
char* m_name;
char* m_surname;
int m_refs;
void rename (const char*, const char*);
};
CPerson :: CPerson () : m_name(NULL), m_surname(NULL), m_refs(1) {}
CPerson :: CPerson (const CPerson& src) : m_name (src.m_name), m_surname (src.m_surname), m_refs(src.m_refs+1) {} // supposed to be a shallow copy
CPerson :: ~CPerson () {
if (m_refs == 1) {
delete [] m_name;
delete [] m_surname;
}
else --m_refs;
}
void CPerson :: rename (const char* name, const char* surname) {
delete [] m_name;
delete [] m_surname;
m_name = new char [strlen(name)+1];
m_surname = new char [strlen(surname)+1];
strcpy (m_name, name);
strcpy (m_surname, surname);
}
int main () {
CPerson a;
a.rename ("Jack", "Smith");
cout << a.m_name << " " << a.m_surname << endl;
CPerson b(a);
cout << a.m_name << " " << a.m_surname << endl;
cout << b.m_name << " " << b.m_surname << endl;
// good so far...
a.rename ("John", "Anderson"); // should rename both 'a' and 'b'
cout << a.m_name << " " << a.m_surname << endl;
cout << b.m_name << " " << b.m_surname << endl;
// prints random values
return 0;
}
It is strange because when I take out the couts, everything works OK (no leaks, no errors by valgrind).
Any help would be appreciated.
Your design is flawed. You need to implement the reference count on the character data itself, not on the individual CPerson objects, as they are not sharing a single reference count variable with each other.
Try something more like this instead:
#include <iostream>
#include <string>
using namespace std;
struct SPersonData
{
string m_name;
string m_surname;
int m_refcnt;
SPersonData() : m_refcnt(0) {}
void incRef() { ++m_refcnt; }
void decRef() { if (--m_refcnt == 0) delete this; }
};
class CPerson
{
private:
SPersonData *m_data;
public:
CPerson ();
CPerson (const CPerson&);
~CPerson ();
CPerson& operator= (const CPerson&);
string getName() const;
string getSurname() const;
void rename (const string&, const string&);
};
CPerson::CPerson ()
: m_data(NULL) {}
CPerson::CPerson (const CPerson& src)
: m_data (src.m_data)
{
if (m_data) m_data->incRef();
}
CPerson::~CPerson ()
{
if (m_data) m_data->decRef();
}
CPerson& operator= (const CPerson &src)
{
if (this != &src)
{
if (m_data) m_data->decRef();
m_data = src.m_data;
if (m_data) m_data->incRef();
}
return *this;
}
string CPerson::getName() const
{
if (m_data) return m_data->m_name;
return string();
}
string CPerson::getSurname() const
{
if (m_data) return m_data->m_surname;
return string();
}
void CPerson::rename (const string &name, const string &surname)
{
if ((m_data) && (m_data->m_refcnt > 1))
{
m_data->decRef();
m_data = NULL;
}
if (!m_data)
{
m_data = new SPersonData;
m_data->incRef();
}
m_data->m_name = name;
m_data->m_surname = surname;
}
Which can be greatly simplified in C++11 and later by using std::shared_ptr to manage the reference count:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
struct SPersonData
{
string m_name;
string m_surname;
};
class CPerson
{
public:
shared_ptr<SPersonData> m_data;
string getName() const;
string getSurname() const;
void rename (const string&, const string&);
};
string CPerson::getName() const
{
if (m_data) return m_data->m_name;
return string();
}
string CPerson::getSurname() const
{
if (m_data) return m_data->m_surname;
return string();
}
void CPerson::rename (const string &name, const string &surname)
{
if (!((m_data) && m_data.unique()))
m_data = make_shared<SPersonData>();
m_data->m_name = name;
m_data->m_surname = surname;
}
Either way, your test would then look like this:
int main ()
{
CPerson a;
a.rename ("Jack", "Smith");
cout << a.getName() << " " << a.getSurname() << endl;
CPerson b(a);
cout << a.getName() << " " << a.getSurname() << endl;
cout << b.getName() << " " << b.getSurname() << endl;
// good so far...
a.rename ("John", "Anderson"); // should rename only 'a' not 'b'
cout << a.getName() << " " << a.getSurname() << endl;
cout << b.getName() << " " << b.getSurname() << endl;
return 0;
}