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;
}
Related
for example, I have a base class Unit and Unit has heirs, for example Soldier, Vampire. They have their own lives and damage. Is it possible to display data Vampire, Soldier using a separate class for example StateOfUnits where there is an overload of the output operator.
something like :
base class :
class Unit {
private:
int hpLimit;
int dmg;
int hp;
std::string name;
public:
Unit(int hpLimit = 100, int dmg = 10, const std::string& name = "noname");
~Unit();
int getDamage() const;
int getHP() const;
int getHpLimit() const;
const std::string& getName() const;
};
class that displays statistics:
class StateOfUnits {
///may be some code...
};
std::ostream& operator<<(std::ostream& out, const Unit& unit) {
out << "HP of " << unit.getName() << " is : " << unit.getHP() << "/" << unit.getHpLimit() << std::endl;
out << "Damage of " << unit.getName() << " is : " << unit.getDamage() << std::endl;
return out;
}
main:
int main() {
hp dmg name
Vampire vampire(100,25,"Vampire");
Soldier soldier(120, 20, "Soldier");
cout << soldier << endl;
cout << vampire << endl;
return 0;
}
I hope I explained it clearly.
Yes, if StateOfUnit is a base class:
#include <iostream>
using namespace std;
struct StateOfUnit
{
int hp;
int dmg;
string name;
StateOfUnit(int hp, int dmg, string name)
: hp(hp), dmg(dmg), name(name)
{}
friend ostream& operator<<(ostream& os, const StateOfUnit& s)
{
return os << s.hp << '\t' << s.dmg << '\t' << s.name;
}
};
struct Soldier : StateOfUnit
{
using StateOfUnit::StateOfUnit;
};
struct Vampire : StateOfUnit
{
using StateOfUnit::StateOfUnit;
};
int main() {
Vampire vampire(100,25,"Vampire");
Soldier soldier(120, 20, "Soldier");
cout << soldier << endl;
cout << vampire << endl;
return 0;
}
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;
}
How can i overload the operator << so it can show the attribute of the parent class as well when those attribute are private withing the parent class??
Parent Class:
Movie header
ifndef _Movie
#define _Movie
#include <string>
using namespace std;
Class Movie{
private:
string title;
int year;
float duration;
public:
Movie();
Movie(string, int, float);
Movie(const Movie&);
void Print();
};
#endif
Movie.cc
#include "Movie.h"
#include<iostream>
Movie::Movie(){
std::cout<< "Defaut Constructor" <<std::endl;
}
Movie::Movie(string t, int a, float d){
this->title = t;
this->year = a;
this->duration = d;
}
Movie::Movie(const Movie &M){
std::cout << "copy" << std::endl;
this->title = M.title;
this->year = M.year;
this->duration = M.duration;
void Movie::Print(){
std::cout << "Info" << std::endl;
std::cout << "------------------" << std::endl;
std::cout << "Title: " << title <<std::endl;
std::cout << "Year: " << year <<std::endl;
std::cout << "Duration: " << duration <<std::endl;
std::cout << "------------------" << std::endl;
if (duration>=60){
std::cout << "Long Movie" << std::endl;
}
else{
std::cout << "Short Movie" << std::endl;
}
}
Child Class:
Prize header:
#ifndef _Prize
#define _Prize
#include "Movie.h"
#include <iostream>
using namespace std;
class Prize : public Movie{
private :
int oscar;
bool por;
public:
Prize();
Prize(string, int, float, int, bool);
Prize(const Prize &);
friend ostream& operator<<(ostream& out,const Prize&f);
};
#endif
prize cc
#include "Prize.h"
#include<iostream>
Prize::Prize()
{
cout<<"Defaut Constructor Prize"<<endl;
}
Prize::Prize(string t, int a, float d, int c, bool p):Movie(t,a,d) //inherite t,a,d from the mother class
{
this->oscar = c;
this->por = p;
}
Prize::Prize(const Prize &f):Movie(f)
{
this->oscar = f.oscar;
this->por = f.por;
}
Here i need to show the attribute of the parent class
as well i can't really add Movie::Print()
and i can't do f.title because it's private within the Movie class
std::ostream& operator<<(std::ostream& out,const Prize& f){
// Movie::print;
// out << f.title << std:endl;
out << f.cesar <<std::endl;
out << f.por << std::endl;
return out;
}
My recommendation is that you instead make an operator<< function for the base class. This operator then calls a virtual "output" function to do the actual output.
Each child-class then overrides this output function to output its own data, and call the base-class output function to let it print its own data.
For your classes it could be something like this:
class Movie
{
private:
std::string title;
protected:
virtual std::ostream& output(std::ostream& out) const
{
return out << title;
}
public:
// Other public members...
friend std::ostream& operator<<(std::ostream& out, Movie const& movie)
{
return movie.output(out);
}
};
class Prize : public Movie
{
protected:
std::ostream& output(std::ostream& out) const override
{
return Movie::output(out) << ' ' << cesar << ' ' << por;
}
// Other public and private members...
};
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
I have problem with those lines in main:
*tab[1]=test1;
*tab[4]=test2;
It just adds colour, and variables (a,b,h) stay the same. I was trying something like this
cuboid operator=(const cuboid & base)
{
return cuboid(base.colour(), base.valueA(), base.valueB(),base.h_)
}
but this doesn't seem to be working either
next one is this:
*tab[4] * =2;
There is overloaded operator for this method and when I run this there occures some error. No match for operator*=. Operand types are figure and int.
The last one is: *tab[2] = "bright" + *tab[2]; I think that I need a new constructor for this, but where do I make one?
Thanks for any answer !!
#include <iostream>
#include <cstdio>
#include <cstring>
#include <math.h>
using namespace std;
class figure
{
string * colour_;
public:
figure(): colour_(new string("Empty")) {}
figure(const string & colour): colour_(new string(colour)) {}
figure(const figure & base)
{
colour_=new string(*base.colour_);
}
virtual ~figure() {delete colour_;}
string & colour () const {return *colour_;}
virtual double area () =0;
virtual void print(ostream& where) const
{
where << "Colour: " << colour() << " ";
}
friend ostream & operator <<(ostream &os, const figure & base)
{
base.print(os);
return os;
}
figure & operator=(const figure & base)
{
if(this==&base)
return *this;
else
{
colour_=new string(*base.colour_);
return *this;
}
}
};
class circle :public figure
{
int r_;
public:
circle() : r_(0) {}
circle(const string & colour,const int r) : figure(colour), r_(r) {}
double area()
{
return M_PI*r_*r_;
}
const int & radius() const {return r_;}
void print(ostream& where) const
{
where << "Colour: " << colour() << " ";
where << "Radius: " << radius() << " ";
}
circle & operator=(const circle & base)
{
r_=base.r_;
figure::operator=(base);
return *this;
}
};
class rectangle : public figure
{
int a_;
int b_;
public:
static int ObjectCount_;
rectangle() : a_(0), b_(0) {++ObjectCount_;}
rectangle(const string & colour, const int a, const int b) : figure(colour),a_(a), b_(b) {++ObjectCount_;}
~rectangle() {--ObjectCount_;}
double area()
{
return a_*b_;
}
const int & valueA () const {return a_;}
const int & valueB () const {return b_;}
int & changeA() {return a_;}
int & changeB() {return b_;}
void print(ostream& where) const
{
where << "Colour: " << colour() << " ";
where << "Value A: " << valueA() << " ";
where << "Value B: " << valueB() << " ";
}
rectangle & operator=(const rectangle & base)
{
a_=base.a_;
b_=base.b_;
return *this;
}
static int & ObjectCount() {return ObjectCount_; }
};
class cuboid :public rectangle
{
int h_;
public:
cuboid() : h_(0) {}
cuboid(const string & colour, const int a, const int b, const int h) : rectangle(colour,a,b), h_(h) {}
double area()
{
return 2*valueA()*valueB()+2*valueB()*h_+2*valueA()*h_;
}
void print(ostream& where) const
{
where << "Colour: " << colour() << " ";
where << "Value A: " << valueA() << " ";
where << "Value B: " << valueB() << " ";
where << "Height: " << h_ << " ";
}
cuboid & operator=(const cuboid & base)
{
figure::operator=(base);
rectangle::operator=(base);
h_=base.h_;
return *this;
}
cuboid & operator*=(const int number)
{
h_*=number;
changeA()*=number;
changeB()*=number;
return *this;
}
};
int rectangle::ObjectCount_=0;
int main()
{
figure * tab[5];
const circle test1("black",100);
const cuboid test2("grey", 2,2,2);
tab[0]=new circle("red",1);
tab[1]=new circle;
tab[2]=new rectangle("blue",1,1);
tab[3]=new cuboid("green",1,1,1);
tab[4]=new cuboid;
for(unsigned i=0; i<5;++i)
cout << tab[i]->area() << endl;
for(int i=0; i<5; ++i)
cout<<*tab[i]<<tab[i]->area()<<"\n";
cout << "***********************" << endl;
*tab[1]=test1; // it just assigns a colour, rest stays the same
*tab[4]=test2; // same here
/*
*tab[2] = "bright" + *tab[2]; //?????
*/
//*tab[4]*=2; //some error, no idea
for(int i=0; i<5; ++i)
cout<<*tab[i]<<tab[i]->area()<<"\n";
cout << "$ " << rectangle::ObjectCount() << endl;
for(int i=0; i<5; i++)
delete tab[i];
cout << "$ " << rectangle::ObjectCount() << endl;
}
You are defining array figure* tab[5], so then you make an assigments you are casting all pointers to your objects to figure*:
tab[0]=new circle("red",1);
tab[1]=new circle;
tab[2]=new rectangle("blue",1,1);
tab[3]=new cuboid("green",1,1,1);
tab[4]=new cuboid;
Class figure have only this assigment operator:
figure & operator=(const figure & base) {
if(this==&base)
return *this;
else {
colour_=new string(*base.colour_);
return *this;
}
}
So, then you are doing:
*tab[1]=test1;
*tab[4]=test2;
you are calling that assigment operator from class figure.
Same with operator *=. class figure just doesn't
have it. Thats why you are getting error.