I've recently begun learning C++ and I'm having some trouble updating a pointer in my Movie class. I've got a feeling I have an issue somewhere in my Move/Copy constructors but have been trying to solve this issue for hours, swapping pointers to references to values and finally coming here for help.
I have a class Movie, which contains a string name, string rating and int watched member variables. Another class Movies, holds a vector of the movies. On movie, my method increment_watched is supposed to increment the int watched by one, the value does get incremented but it seems like the value is not saved. The display_all_movies method in Movies, which simply displays the movies that it stores holds the old value for watched. I know the issue is probably something really small but I haven't been able to work out why the value isn't being saved.
If someone could explain why the pointer value seems to be getting overridden I'd appreciate it greatly. Thanks in advance!
Code is below, there is some debug couts in there.
Movie.h
#ifndef _MOVIE_H_
#define _MOVIE_H_
#include <string>
#include <iostream>
class Movie {
private:
std::string *name;
std::string *rating;
int *watched;
public:
std::string get_name();
std::string get_rating();
int get_watched();
void increment_watched();
Movie(std::string name, std::string rating, int watched_val); // normal constructor
Movie(const Movie &source); // copy constructor
Movie(Movie &&source); // move constructor
~Movie();
};
#endif // _MOVIE_H_
Movie.cpp
#include "Movie.h"
std::string Movie::get_name() {
return *name;
}
std::string Movie::get_rating() {
return *rating;
}
int Movie::get_watched() {
return *watched;
}
void Movie::increment_watched() {
std::cout << *watched << std::endl;
(*watched)++; // TODO FIX!
std::cout << *watched << std::endl;
}
Movie::Movie(std::string name, std::string rating, int watched_val)
: name{nullptr}, rating{nullptr}, watched{nullptr}{
std::cout << "Creating movie " << watched_val << " with normal constructor" << std::endl;
this->name = new std::string{name};
this->rating = new std::string{rating};
this->watched = new int{watched_val};
}
Movie::Movie(const Movie &source)
: Movie(*source.name, *source.rating, *source.watched) {
std::cout << "Creating movie " << *source.watched << " with copy constructor" << std::endl;
}
Movie::Movie(Movie &&source)
: Movie(*source.name, *source.rating, *source.watched) {
std::cout << "Creating movie " << *source.watched << " with move constructor" << std::endl;
source.name = nullptr;
source.rating = nullptr;
source.watched = nullptr;
}
Movie::~Movie() {
delete name;
delete rating;
delete watched;
}
Movies.h
#ifndef _MOVIES_H_
#define _MOVIES_H_
#include <vector>
#include <string>
#include <iostream>
#include "Movie.h"
class Movies {
private:
static int count;
std::vector<Movie> *movies;
public:
void add_movie(std::string &&name, std::string &&rating, int &&watch);
void increment_movie_count(std::string &&name);
void display_all_movies();
void count_movies();
Movies();
Movies(const Movie &source); // copy constructor
Movies(Movie &&source); // move constructor
~Movies();
};
#endif // _MOVIES_H_
Movies.cpp
#include "Movies.h"
int Movies::count = 0;
void Movies::add_movie(std::string &&name, std::string &&rating, int &&watch) {
bool contains {false};
for(auto movie : *movies) {
if(movie.get_name() == name) {
contains = true;
}
}
if(!contains) {
movies->push_back(Movie{name, rating, watch});
count++;
}
}
void Movies::increment_movie_count(std::string &&name) {
for(auto movie : *movies) {
if(movie.get_name() == name) {
movie.increment_watched();
}
}
}
void Movies::display_all_movies() {
for(auto movie : *movies) {
std::cout << "Title " << movie.get_name() << " Rating " << movie.get_rating() << " Watched " << movie.get_watched() << std::endl;
}
}
void Movies::count_movies() {
std::cout << "There are " << count << " movies " << std::endl;
}
Movies::Movies() {
movies = new std::vector<Movie>{};
}
Movies::~Movies() {
delete movies;
}
And finally main.cpp
#include <iostream>
#include "Movies.h"
using std::cout;
using std::endl;
int main() {
Movies *my_movies;
my_movies = new Movies();
(*my_movies).count_movies();
my_movies->add_movie("Big", "PG-13", 2);
my_movies->increment_movie_count("Big");
my_movies->display_all_movies();
return 0;
}
Changefor(auto movie : *movies) to for(auto& movie : *movies) to update the original movie objects. Otherwise, you're only updating copies of the movie objects.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 23 days ago.
Improve this question
Recently I've started working on my first OOP project and after having written the program I am trying to optimize it for code efficiency. I want to place the parts of the program that are being copied a lot on the heap.
I can't understand why in certain places objects are copied.
An example:
In the main.cpp movies object, which stories movie objects, is created. Add_movie function is called that checks if the movie we are trying to add has already been added, if not, we create a temp object, initialize its private members to the argument values being passed, append it to the vector of the movies object. A copy constructor would be called when movie object is being appended to the vector. WHY? I can't understand the part WHY is it being copied? Is it because of the scope???
If there was an object initialized in the main like
Movie movie1{arguments};
and other movie is created based on movie1
Movie movie2{movie1}.
It makes sense to me, but in the example I gave, it doesn't make sense to me at all
The example of the function I am referring to
bool Movies::add_movie(std::string name, std::string rating, int watched)
{
for (const Movie& obj : movies_list)
{
if (obj.get_name() == name) // search for a match
{
return false; // if found stop executing
}
}
Movie temp{ name, rating, watched }; // creates a new object and initializes its private members to the passed arguments
# movies_list.push_back(temp); // appends the object to the vector
# *** return true;
}
I don't know if it will help, but there is the code of the program
**main.cpp **
#include "Movie.h"
#include "Movies.h"
#include <iostream>
#include <string>
void add_movie(Movies& obj, std::string name, std::string rating, int watched)
{
if (obj.add_movie(name, rating, watched))
{
std::cout << name << " succesfully added" << std::endl;
}
else
{
std::cout << name << " already has been added" << std::endl;
}
}
// if the parent increment_watched function returns true, inform the user about the result of the operation
void increment_watched(Movies &obj, std::string name)
{
if (obj.increment_watched(name)) // if Movies::increment_watched returns
{
std::cout << name << " watch count succesfully incremented by 1" << std::endl;
}
else {
std::cout << name << " movie not found" << std::endl;
}
}
int main()
{
Movies list;
add_movie(list, "Fight Club", "A", 1);
add_movie(list, "Fight Club", "A", 1);
add_movie(list, "Inception", "A", 1);
increment_watched(list, "Fight Club");
increment_watched(list, "Else Test");
list.display();
return 0;
}
movies.cpp
#include "Movie.h"
#include "Movies.h"
#include <iostream>
bool Movies::add_movie(std::string name, std::string rating, int watched)
{
for (const Movie& obj : movies_list)
{
if (obj.get_name() == name) // search for a match
{
return false; // if found stop executing
}
}
Movie temp{ name, rating, watched }; // creates a new object and initializes its private members to the passed arguments
movies_list.push_back(temp); // appends the object to the vector
return true;
}
void Movies::display() const
{
if (movies_list.size() == 0) // checks the vector size
{
std::cout << "The list is empty" << std::endl;
}
else
{
std::cout << "\nThe list of the movies: " << std::endl;
std::cout << "----------------------------" << std::endl;
for (const Movie& obj : movies_list)
{
obj.display_members(); // accesses the private members of the object that are stored in the vector and outputs them to the user
}
}
}
bool Movies::increment_watched(std::string name)
{
for (Movie &obj : movies_list) // iterates through the movie objects until finds the match in name
{
if (obj.get_name() == name)
{
obj.increment_watched(); // increments watched by 1
return true;
}
}
return false;
}
movie.cpp
#include <string>
#include <iostream>
#include "Movie.h"
// constructor for initializing private members of the object
Movie::Movie(std::string name, std::string rating, int watched)
{
this->name = name;
this->rating = rating;
this->watched = watched;
}
// get methods
std::string Movie::get_name() const { return name; }
std::string Movie::get_rating() const { return rating; }
int Movie::get_watched() const { return watched; }
// display private members
void Movie::display_members() const
{
std::cout << "Name: " << get_name() << std::endl;
std::cout << "Rating: " << get_rating() << std::endl;
std::cout << "Times watched: " << get_watched() << std::endl;
std::cout << "\n" << std::endl;
}
// setter function
void Movie::increment_watched() {watched++;}
// DEBUGGING
Movie::Movie(const Movie &obj):name{obj.name}, rating{obj.rating}, watched{obj.watched} {std::cout << "copy constructor called for " << name << std::endl;}
Movie::~Movie() {std::cout << "destructor called for movie " << name << std::endl;}
Debugging the program for hours to see which parts are being copied, when copied, when destructed to get a better grasp.
Watching countless videos that explain the lifetime of the objects, copy constructors, destructors, but it still doesn't make sense for me!
push_back() takes an object and appends it at the end of the vector. It has to make a copy because it must keep the original object intact because you might need it later. If you want to avoid the copy, you’d have you use std::move to trigger the move constructor.
movies_list.push_back(std::move(temp));
However, in your example you basically want to construct an object at the end of the vector. emplace_back is just what you need; no copying or moving, just pass the constructor arguments.
movies_list.emplace_back(name, rating,watched);
#include <iostream>
#include <string.h>
#include "Date.h"
#include "Employee.h"
using std::cout;
using std::endl;
using std::to_string;
class TestOps {
public:
int sex = 1;
string toString() {
return " sex:" + to_string(sex) ;
}
};
class Test {
public:
TestOps* testOps;
Test(const Test& t) :Test{} {
this->testOps = new TestOps{ *(t.testOps) };
};
Test() {
TestOps ops;
//this->testOps = new TestOps{}; // it will be ok with this way
this->testOps = &ops;
}
};
int main() {
// code not understand
Test t1;
cout <<"first testOps:" << t1.testOps->toString() << endl; // sex: 1
Test t2{ t1 };
cout << "first testOps:" << t1.testOps->toString() << endl; // sex: -858893460 ???? why?
cout << "second testOps:" << t2.testOps->toString() << endl; // sex: -858893460 ???? why?
return 0;
}
As you can see, why the first log is as expected while the later logs are not?
Also, t1.testOps address is different from t2.testOps which is as expected.
I have done some research but didn't find the answer. Maybe because I'm pretty new to cpp.
I'm trying to use my own list with a class called "NameCard" which has two variables, name and phone.
However, complier crushed when I use LFirst(LData pData).
It worked with simple int type list.
any feedback would be greatly appreciated
Here is my code.
Class name: ArrayList.cpp
int ArrayList::LFirst(LData* pData)
{
if (numOfData == 0)
return 0;
curPosition = 0;
*pData = arr[0];
return 1;
}
Class name: NameCard.cpp
NameCard::NameCard()
{
}
NameCard::NameCard(const char* iName, const char* iPhone)
{
strcpy_s(name, iName);
strcpy_s(phone, iPhone);
}
void NameCard::ShowNameCardInfo()
{
std::cout << "Name: " << name << ", phone: " << phone << std::endl;
}
int NameCard::NameCompare(char* iName)
{
return strcmp(name, iName);
}
void NameCard::ChangePhoneNum(char* iPhone)
{
strcpy_s(phone, iPhone);
}
Class name: NameCardImplementation.cpp
#include <iostream>
#include "ArrayList.h"
#include "NameCard.h"
int main()
{
ArrayList list;
NameCard *pData(NULL);
NameCard nc1("Alice", "010-1111-2222");
NameCard nc2("Brandon", "010-2222-3333");
NameCard nc3("Jack", "010-3333-4444");
list.LInsert(nc1);
list.LInsert(nc2);
list.LInsert(nc3);
//nc1.ShowNameCardInfo();
//list.arr[0].ShowNameCardInfo();
//std::cout << list.LCount() << std::endl;
int a = list.LFirst(pData);
std::cout << a << std::endl;
//if (list.LFirst(pData))
//{
// pData->ShowNameCardInfo();
//}
}
Crash is due to null pointer access in ArrayList::LFirst.
From the limited code you have pasted, I see that there is no memory created for pData.
NameCard *pData(NULL);
Are you attaching the memory for pData anywhere else in your code?
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'mm getting a bit confused why this isn't printing the name!
I've got a human.cpp :
#include <string>
#include <iostream>
#include "human.h"
human::human(int age, human *n){
m_age=age;
name = new char[2];
human::~human() = default;
void human::printDetails(){
std::cout <<"name is " << name << " age is " << m_age << std::endl;
}
and human.h:
class human {
public: //: needed
human(int age, human *name);
~human();
void printDetails();
private :
char *name;
int m_age;
};
and finally the main.cpp:
#include <iostream>
#include <string>
#include "human.h"
int main()
{
human *Alex = new human(10, Alex); //pointer // needs argument //should have both age and name
Alex->printDetails(); //print not Print
}
So my issue is: it prints the age, but does not print the name? Any suggestions? Thanks :)
There is no need for any new in your code. Since you #included <string> in your code I assume you want to use it:
#include <string>
#include <iostream>
class Person
{
int age;
std::string name;
public:
Person(int age, std::string name)
: age { age },
name { name }
{}
int get_age() const { return age; }
std::string const& get_name() const { return name; }
void print_details() const {
std::cout << "My name is " << name << ". I am " << age << " years old.\n";
}
};
int main()
{
Person p{ 19, "Alex" };
p.print_details();
}
If you *really* want to do it the hard waytm:
#include <cstring> // std::strlen()
#include <utility> // std::exchange(), std::swap()
#include <iostream>
class Person
{
char *name_;
int age_;
public:
Person(int age, char const *name) // constructor
// we don't want to call std::strlen() on a nullptr
// instead allocate just one char and set it '\0'.
: name_ { new char[name ? std::strlen(name) + 1 : 1]{} },
age_ { age }
{
if (name)
std::strcpy(name_, name);
}
Person(Person const &other) // copy-constructor
: name_ { new char[std::strlen(other.name_) + 1] },
age_ { other.age_ }
{
std::strcpy(name_, other.name_);
}
Person(Person &&other) noexcept // move-constructor
: name_ { std::exchange(other.name_, nullptr) }, // since other will be
age_ { other.age_ } // wasted anyway, we
{} // "steal" its resource
Person& operator=(Person other) noexcept // copy-assignment operator
{ // since the parameter other got
std::swap(name_, other.name_); // copied and will be destructed
age_ = other.age_; // at the end of the function we
return *this; // can simply swap the pointers
} // - know as the copy&swap idiom.
~Person() { delete[] name_; } // destructor
void print_details() const
{
std::cout << "I am " << name_ << ". I am " << age_ << " years old.\n";
}
};
int main()
{
Person p{ 19, "Alex" };
p.print_details();
}
If you don't want to implement the special member functions you'd have to = delete; them so the compiler-generated versions - which won't work correctly for classes managing their own resources - won't get called by accident.
#include <iostream>
#include <cstring>
#include <new>
using namespace std;
class human {
public:
human(int age, const char * name)
{
m_age=age;
m_name = new char[strlen(name)+1];
strcpy(m_name,name);
}
~human()
{
delete[] m_name;
}
void printDetails()
{
std::cout <<"name is " << m_name << " age is " << m_age << std::endl;
}
private :
char *m_name;
int m_age;
};
int main()
{
human *Alex = new human(10, "alex"); //pointer // needs argument //should have both age and name
Alex->printDetails(); //print not Print
delete Alex;
return 0;
}
You need to read more. The example you shared was wrong at many level, also read about dynamic memory management.
I guess you're confused with the second parameter of the human constructor. Look at this changes:
human.h
class human {
public:
human(int age, char *name);
human(const human& h);
human& operator=(const human& h);
void printDetails();
virtual ~human();
private:
int age;
char *name;
};
human.cpp
human::human(int _age, char *_name) {
age = _age;
name = new char[strlen(_name)+1];
strcpy(name, _name);
}
human::human(const human& _h) { //Copy constructor
age = _h.age;
name = new char[strlen(_h.name)+1];
strcpy(name, _h.name);
}
human& human::operator=(const human& _h) { //Copy assignment operator
age = _h.age;
name = new char[strlen(_h.name)+1];
strcpy(name, _h.name);
return *this;
}
void human::printDetails(){
std::cout <<"name is " << name << " age is " << age << std::endl;
}
human::~human() { //Destructor
delete[] name;
}
main.cpp
int main() {
human *alex = new human(10, "Alex");
alex->printDetails();
human *anotherAlex = new human(*alex);
anotherAlex->printDetails();
delete alex;
delete anotherAlex;
}
A suggestions: I would use Human as the class names and alex for the variable names. (See how I capitalized them)
For beginner you may use std::string, so you already included it. ))
human.h
#include <string>
class human
{
public: //: needed
human(int age, std::string name);
void printDetails();
private :
std::string name;
int m_age;
};
human.cpp
#include <iostream>
#include "human.h"
human::human(int age, std::string n)
{
m_age = age;
name = n;
}
void human::printDetails()
{
std::cout <<"name is: " << name << " age is: " << m_age << std::endl;
}
main.cpp
#include "human.h"
int main()
{
human *Alex = new human(10, "Alex");
Alex->printDetails();
}
I have a base class called Item:
#ifndef ITEM_H
#define ITEM_H
#include <ostream>
class Item {
public:
virtual ~Item() {}
virtual void print(std::ostream& out) const {}
friend std::ostream& operator << (std::ostream& out, Item& item){
item.print(out);
return out;
}
};
#endif
and I have a derived class Tower:
#ifndef TOWER_H
#define TOWER_H
#include <iostream>
#include <iostream>
#include <exception>
#include "item.h"
#include "Card.h"
class Tower : public Item {
unsigned height;
void print(std::ostream& o) const;
public:
Tower(const Card& card);
int demolish(Card& card) const;
unsigned getHeight() const;
};
#endif
Source code for Tower:
#include "tower.h"
Tower::Tower(const Card& card){
height = card.getDis();
}
void Tower::print(std::ostream& o) const {
o << height;
}
int Tower::demolish(Card& card) const{
try {
if(height != card.getDis()){
throw std::exception ();
} else {
return height;
}
} catch (std::exception e){
cout<< "Card's value not enough to demolish the tower." << endl;
}
}
unsigned Tower::getHeight() const {
return height;
}
Now I'm trying to test the code to see if the operator overloading works properly:
void test() {
Card card (Card::SOUTH, 3);
Tower tower(card);
std::cout << "Printing tower: " << tower << std::endl; //PRINTS OUT 3 AS EXPECTED
Card one (Card::NORTH, 2);
Card two (Card::WEST, 3);
std::cout << "Expecting to receive an error: " <<endl;
tower.demolish(one);
std::cout << "Expecting to have the tower demolished: ";
std::cout << tower.demolish(two) <<std::endl;
std::cout << "Height of the tower: " << tower.getHeight() <<std::endl;
std::vector<Item> items; //creating an Item vector
items.push_back(Tower(one));
Item items2[1]; //creating an array of Items
items[0]= tower;
std::cout << "Printing out an Item: ";
std::cout << items.back()<<std::endl; //EXPECTING TO GET 2 BUT IT PRINTS NOTHING, WHY IS THAT?
std::cout << "OR: " << items2[0]<<std::endl; //SAME ISSUE HERE, EXPECTING TO GET 3
}
As can be understood from the code, a Card holds an integer value distance and an enum value direction. It would've been a mess if i included that code too. I have commented my questions in the last piece of code test(). Thanks for your help in advance.
std::vector<Item> items; //creating an Item vector
items.push_back(Tower(one));
What happens here is called "slicing". Since you're not storing pointers, but actual objects, the Tower part of the class is just cut off and only the Item part is pushed into the vector. To use virtual functions and polymorphism, you need a reference or pointer to the base class.
std::vector<Item*> items; //creating an Item vector
items.push_back(new Tower(one));
// ...
// at the end of main:
for(int i=0; i < items.size(); ++i)
delete items[i];
Or with smart pointers from Boost or a C++11 library
std::vector<shared_ptr<Item>> items;
items.push_back(make_shared<Tower>(one));
// nothing further needs to be done
For printing, you now obviously need to dereference the pointer:
std::cout << "Printing out an Item: ";
std::cout << *items.back()<<std::endl;
std::cout << "OR: " << *items2[0]<<std::endl;
}