I am currently programming a sportsmanagement software.
You can add clubs and members into those clubs, however you can also add subclubs to clubs themselves.
Problem: If i make a new Society and add new members, the cost calculation works.
Club 1: Member 1, Member 2, Member 3 --> cost calculation is correct
if i now add a second club the cost calculation for that Club is correct as well.
Club 2: Member 5, Member 6. --> cost calculation is correct
However if i now add Club 2 as a branch of Club 1, the calculation is still the same one as it was without the branch of Club 2
Club 1: Member 1, Member 2, Member 3 , Club 2 -> calculation not correct.
I would really appreciate your help. Down below is my code.
Members.cpp
#include <iostream>
#include <string>
#include "Members.h"
static int memberid = 0;
Members::Members()
{
this->name = "default";
this->id = memberid++;
}
Members::Members(std::string name)
{
this -> name = name;
this->id = memberid++;
}
Members::~Members()
{
}
void Members::setname(std::string newname)
{
this -> name = newname;
}
std::string Members::getName()
{
return this->name;
}
int Members::getID()
{
return this->id;
}
double Members::getIncome()
{
return this->income;
}
void Members::setIncome(double income)
{
this->income = income;
}
double Members::getCosts()
{
return this->costs;
}
void Members::setCosts(double costs)
{
this->costs = costs;
}
double Members::getSurplus()
{
return this->income - this->costs;
}
std::string Members::toString()
{
std::string formated = " Member: " + getName() + "\n Income: " + std::to_string(getIncome()) + "\n Costs: " + std::to_string(getCosts()) + "\n Surplus: " + std::to_string(getSurplus());
return formated;
}
Society.cpp in Society.h it is class Society : public Members
#include "Society.h"
#include "Membertypes.h"
#include <sstream>
#include <vector>
#include <algorithm>
Society::Society(std::string name, int maximalmembers)
{
setname(name);
setMax(maximalmembers);
Members::setCosts(this->getCosts());
Members::setIncome(this->getIncome());
}
double Society::getIncome()
{
double income=0;
for (int i = 0, n = members.size(); i < n; i++)
{
income += members[i].getIncome();
}
return income;
}
double Society::getCosts()
{
double costs=0;
for (int i = 0, n = members.size(); i < n; i++)
{
costs += members[i].getCosts();
}
return costs;
}
double Society::getSurplus()
{
double surplus=0;
for (int i = 0, n = members.size(); i < n; i++)
{
surplus += members[i].getSurplus();
}
return surplus;
}
void Society::addMember(Members* newMember)
{
if ((int) members.size() < this -> maximalMembers)
{
this ->members.push_back(*newMember);
}
else
{
std::cout << "To many members" << std::endl;
}
}
Thanks for your help
To an extend this is a wild stab in the dark, but you have a std::vector<Members> members; inside the Society class.
You have designed Members as a polymorphic class - you have virtual functions, and the various constructors set different costs etc to each type.
HOWEVER, you just slice these when you put them in the vector.
Your add method:
void Society::addMember(Members* newMember)
{
if ((int) members.size() < this -> maximalMembers)
{
this ->members.push_back(*newMember);
}
else
{
std::cout << "To many members" << std::endl;
}
}
takes a pointer - which is a potential memory leak - and then treats this as a Member.
Consider using a std::vector<std::shared_ptr<Members>> instead.
Related
When trying to 'equip' the weapon and Armour objects they result in a read access violation and the output damage and input is all blanks. (Anything in parenthesis is an alternative output) The output should be
Attacker : Gary using Wooden Sword does 1 (or 2) damage.
Defender : Mary(4 HP) is equipped with Iron Armour and receives 0 (or 1) damage
but output is
Attacker : Gary using Weapon does damage.
Defender : Mary( HP) is equipped with Armour and recieves
This is the code:
class Weapon {
public:
std::string weapon_Name = "Weapon";
int MIN_DAMAGE = 0;
int MAX_DAMAGE = 1;
};
class Wooden : public Weapon {
public:
std::string weapon_Name = "Wooden Sword" ;
int MIN_DAMAGE = 1;
int MAX_DAMAGE = 2;
};
class Iron : public Weapon {
public:
std::string weapon_Name = "Iron Sword";
int MIN_DAMAGE = 2;
int MAX_DAMAGE = 4;
};
class Armour {
public:
std::string armour_Name = "Armour";
int DAM_REDUCTION = 0;
int DAM_ABSORBTION = 0;
};
class Leather : public Armour {
public:
std::string armour_Name = "Leather Armour";
int DAM_REDUCTION = 0;
int DAM_ABSORBTION = 1;
};
class Chainmail : public Armour {
public:
std::string armour_Name = "Iron Armour";
int DAM_REDUCTION = 1;
int DAM_ABSORBTION = 2;
};
class Player {
private:
int hp;
std::string name;
Weapon *WEAPON_EQUIPPED;
Armour *ARMOUR_EQUIPPED;
public:
Player(std::string pName, int startHP) {
this->hp = startHP;
this->name = pName;
}
void equipWeapon( Weapon &Weapon){ this->WEAPON_EQUIPPED = &Weapon; }
void equipArmour( Armour &Armour){ this->ARMOUR_EQUIPPED = &Armour; }
void getWeaponName() { std::cout << WEAPON_EQUIPPED->weapon_Name; }
void getArmourName() { std::cout << ARMOUR_EQUIPPED->armour_Name; }
void getPlayerName() { std::cout << name; }
int getPlayerHP() { return hp; }
int getOutDamage() {
srand((int)time(0));
int outDamage = ((rand() % this->WEAPON_EQUIPPED->MAX_DAMAGE) + this->WEAPON_EQUIPPED->MIN_DAMAGE);
return outDamage;
}
int getInDamage() {
int inDamage = getOutDamage();
srand((int)time(0));
int totalDamage = inDamage - (((rand() % this->ARMOUR_EQUIPPED->DAM_ABSORBTION) + this->ARMOUR_EQUIPPED->DAM_REDUCTION) % this->ARMOUR_EQUIPPED->DAM_ABSORBTION);
return totalDamage;
}
int recieveDamage(int inDam) {
//this->hp - inDam;
int dam = this->hp - inDam;
return dam;
}
};
void Attack(Player& attacker, Player& defender) {
std::cout << "Attacker : ";
attacker.getPlayerName();
std::cout << " using ";
attacker.getWeaponName();
std::cout << " does ";
attacker.getOutDamage();
std::cout << " damage." << std::endl;
std::cout << "Defender : ";
defender.getPlayerName();
std::cout << "(";
defender.getPlayerHP();
std::cout << " HP) is equipped with ";
defender.getArmourName();
std::cout << " and recieves ";
defender.recieveDamage(defender.getInDamage());
std::cout << " damage(";
defender.getPlayerHP();
std::cout << ")" << std::endl;
}
int main() {
Player Gary("Gary", 4);
Player Mary("Mary", 4);
Wooden wood;
Chainmail chain;
Wooden* ptrWeap = &wood;
Chainmail* ptrChain = &chain;
Gary.equipWeapon(wood);
Mary.equipArmour(chain);
Attack(Gary, Mary);
return 0;
}
Sorry for long code I do need help in understanding how to fix this, any help would be much appreciated
If you look at call stack while the exception if being thrown you will see, that in main you call Attack(Gary, Mary);, where Gary is an attacker and Mary is a defender. It then calls defender.recieveDamage(defender.getInDamage());. Then getInDamage(), calls getOutDamage() again on object Player and the instance of that object is Mary, for whom you never actually set WEAPON_EQUIPPED. And since it is uninitialized, it is by default a nullptr (at least on Debug) and here is where your access violation reading comes from.
I am writing some C++ code to create an item of a class i have created inside a vector of another class. I seem to be able to create the items inside the vector but when i try to read a variable of the item inside the vector i get the error
Exception thrown: read access violation.
_Right_data was 0x8.
inside the document xstring.
I think it might have something to do with me not actually creating each team inside the vector.
the code i have written that is relavent is
for (int x = 1; x <= mainLeague.getNumTeams(); x++) {
std::cout << "please enter the name of team " << x << ":";
std::getline(std::cin, currLine);
parsed = parseText(currLine, &posResponsesTeamNames);
if (parsed == 2) {
prepForEnd();
return 1;
}
else if (parsed == 0) goto enterTeamNames;
mainLeague.createTeam(currLine);
}
std::cout << mainLeague.getName(5);
}
#pragma once
#include "team.h"
#include <string>
#include <vector>
#include <iostream>
class league
{
std::vector<team*> teams;
int numTeams, numInitTeams = 0;
const float sysCon = 0.5;
public:
league(int a);
int getNumTeams();
void initVector(int numTeams);
void createTeam(std::string name);
std::string getName(int num);
};
void league::createTeam(std::string name)
{
if (numInitTeams < teams.size()) {
team currTeam = team::team(name);
teams.at(numInitTeams) = &currTeam;
numInitTeams;
}
else {
std::cout << "error max amount of teams already created";
}
}
#pragma once
#include<string>
class team
{
float RD;
int rating;
std::string name;
public:
team(std::string name);
team();
std::string getName();
};
std::string team::getName()
{
return team::name;
}
#include<iostream>
#include<string>
using namespace std;
class Student {
public:
const int codeStud;
int noGrades = 0;
int* grades = NULL;
Student(int code) :codeStud(code) {
}
Student(int code, int* grades, int noGrades) :codeStud(code) {
this->noGrades = noGrades;
this->grades = new int[noGrades];
for (int i = 0; i < noGrades; i++)
this->grades[i] = grades[i];
}
Student(const Student&existent):codeStud(existent.codeStud) {
this->noGrades = existent.noGrades;
this->grades = new int[this->noGrades];
for (int i = 0; i < this->noGrades; i++)
this->grades[i] = existent.grades[i];
}
int getCode() {
return this->codeStud;
}
int getNoGrades() {
return this->noGrades;
}
void setGrades(int grades[],int noGrades) {
this->noGrades = noGrades;
this->grades = new int[noGrades];
for (int i = 0; i < noGrades; i++)
this->grades[i] = grades[i];
}
};
void main() {
Student s1(101);
cout<<s1.getNoGrades();
int grades[] = { 10,7,8,10,4 };
Student s2(104, grades, 5);
cout << "\n" << s2.getNoGrades();
Student s3 = s2;
cout << "\n" << s3.getCode();
int grades2[] = { 5,5,4,10 };
s1.setGrades(grades2,4);
cout << "\n" << s1.getNoGrades(); // here is the problem
}
After I changed the grades for student 1 it shows that he has 0 grades, when the output should be 4, the number of these grades: 5,5,4,10.
The rest of output is correct, even when I want to know the number of grades for student 1, which is 0 , and then for student 2, which is 5.
I've changed some things in your code to compile it
#include<iostream>
#include<string>
using namespace std;
class Student {
public:
int codeStud;
int noGrades = 0;
int* grades = NULL;
Student(int code) {
codeStud = code;
}
Student(int code, int* grades, int noGrades) {
this->noGrades = noGrades;
this->grades = new int[noGrades];
for (int i = 0; i < noGrades; i++)
this->grades[i] = grades[i];
}
Student(const Student&existent){
this->noGrades = existent.noGrades;
this->grades = new int[this->noGrades];
for (int i = 0; i < this->noGrades; i++)
this->grades[i] = existent.grades[i];
}
int getCode() {
return this->codeStud;
}
int getNoGrades() {
return this->noGrades;
}
void setGrades(int grades[],int noGrades) {
this->noGrades = noGrades;
this->grades = new int[noGrades];
for (int i = 0; i < noGrades; i++)
this->grades[i] = grades[i];
}
};
int main() {
Student s1(101);
cout<<s1.getNoGrades();
int grades[] = { 10,7,8,10,4 };
Student s2(104, grades, 5);
cout << "\n" << s2.getNoGrades();
Student s3 = s2;
int grades2[] = { 5,5,4,10 };
s1.setGrades(grades2,4);
cout << "\n" << s1.getNoGrades(); // here is the problem
}
and output is:
0
5
4
what is correct, because you don't assign number of grades of s1 anywhere in your code before first printing
Also look for:
https://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c
why void main() is not correct
After I corrected the typo (codStud --> codeStud) your code produced the correct results for me. Before I did that I couldn't even compile it, so my guess would be that your IDE just run the latest working version of it that could be compiled successfully (look for error messages somewhere). That's the reason you got the wrong result, because your changes weren't even in that version.
A couple of note about your code:
In your setGrades function check that grades not pointing to something already. For example, if I call Student(int code, int* grades, int noGrades) and after I call setGrades your code leaks memory because it loses the array that Student(int code, int* grades, int noGrades) allocated before.
You should use vector instead of C-style arrays. It will make your code much more cleaner and less error-prone (see my example).
You could make your getter functions to const (like in my example), so it would be guaranteed that those functions don't change the value of any member of the class (you get a compile error if they do). Other than that, you can make the member variables to private.
Implementation using vectors:
#include <iostream> // cout
#include <vector> // vector
using namespace std;
class Student
{
public:
Student(const int code)
: m_code{code}
{
}
Student(const int code, const std::vector<int>& grades)
: m_code{code},
m_grades{grades}
{
}
// Default copy constructor is sufficient because the class can be copied
// memberwise.
int getCode() const {
return m_code;
}
int getNoGrades() const {
return m_grades.size();
}
void setGrades(const std::vector<int>& grades) {
m_grades = grades;
}
private:
const int m_code;
std::vector<int> m_grades;
};
int main()
{
Student s1(101);
cout << s1.getNoGrades();
Student s2(104, {10, 7, 8, 10, 4});
cout << "\n" << s2.getNoGrades();
Student s3 = s2;
cout << "\n" << s3.getCode();
s1.setGrades({5, 5, 4, 10});
cout << "\n" << s1.getNoGrades();
return 0;
}
I'm wondering if there is a way to index structs in order to run through the member variables in a for-loop. I'm asking repeatingly for stuff and get input via cin>>:
struct Fruits {
string name;
int weight, diameter, prize;
string colour, smell, taste;
}apple;
cout << "Apple Name?"; cin >> apple.name;
cout << "Apple weight?"; cin >> apple.weight;
.
.
you get the idea. I want sth like
vector<string> questions {"Apple Name?", "Apple weight?", ... };
for (int j = 0; j < "StructureSize of Fruits"; j++)
{
cout << questions[j]; cin >> apple.j; //apple.j is the key point I want to know
}
thank you very much!
you can't.
Arrays access works on the following way.
int A[4];
A[0] = 5;
A[1] = 7;
A[3] = 8;
You know a int (in some archytectures) use 4 bytes. So, the first element of A is in the address A, the next element of A is in the address A+4 what is equal to say A+sizeof(int)
When you do the first assignment A[0] the code is really something like A+sizeof(int)*0, in the next A[1] is something like A+sizeof(int)*1 If you see, any adjacent space of the array sums 4 bytes ( or sizeof(int) ) and goes on. If you see, you know that A have integers inside of it and you know the pattern to access each element the general pattern in a for loop would be A+sizeof(int)*j in your struct you don't know what is inside, you can't do A+sizeof(X)*j becouse X isn't fixed, so, if you want to do that you must implement it yourself, but It would be a pain becouse you have mixed datatypes.
If your struct is fixed, the strings for the user should also be fixed, then why you try to print them from a loop?, The vector need to allocate and that use spaces and gpu, I think is better if you just print each option and then read the user input.
You can.
But not without overkilling it.
#include <string>
#include <iostream>
#include <memory>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
struct Fruit {
std::string name;
int weight, diameter, price;
std::string colour, smell, taste;
};
BOOST_FUSION_ADAPT_STRUCT(
Fruit,
(std::string, name)
(int, weight)
(int, diameter)
(int, price)
(std::string, colour)
(std::string, smell)
(std::string, taste)
)
std::array<char const *const, 7> questions = {
"Name ?",
"Weight ?",
"Diameter ?",
"Price ?",
"Colour ?",
"Smell ?",
"Taste ?"
};
int main() {
Fruit apple;
auto input = [i = std::size_t{0u}](auto &field) mutable {
do {
std::cout << questions[i] << ' ';
} while(!(std::cin >> field));
++i;
};
boost::fusion::for_each(apple, std::ref(input));
std::cout << "----------\n";
auto output = [i = std::size_t{0u}](auto const &field) mutable {
std::cout << questions[i] << ' ' << field << '\n';
++i;
};
boost::fusion::for_each(apple, std::ref(output));
}
I just learned that boost::fusion::for_each can't accept a mutable functor, hence the weird and a bit dangerous use of std::ref()... It's a shame, really.
Output :
Name ? Apple
Weight ? 150
Diameter ? 8
Price ? 1
Colour ? red
Smell ? good
Taste ? sweet
----------
Name ? Apple
Weight ? 150
Diameter ? 8
Price ? 1
Colour ? red
Smell ? good
Taste ? sweet
enum FruitValueProperty { weight=0, diameter, prize, lastValueProperty };
enum FruitStringProperty { name=0, colour, smell, taste, lastStringProperty };
struct Fruit {
int ValueProperty[3];
string StringProperty[4];
int Weight() { return ValueProperty[weight]; }
int Diameter() { return ValueProperty[diameter]; }
int Prize() { return ValueProperty[prize]; }
string Name() { return StringProperty[name]; }
string Colour() { return StringProperty[colour]; }
string Smell() { return StringProperty[smell]; }
string Taste () { return StringProperty[taste ]; }
};
Fruit fruit;
vector<string> questions {"Fruit weight?", ... "Fruit name?", ... };
for (int i=0; i<lastValueProperty; i++) {
cout << questions[j];
cin >> fruit.ValueProperty[i];
}
for (int j=0; i<lastStringProperty; i++) {
cout << questions[lastValueProperty+j];
cin >> fruit.StringProperty[j];
}
int dim = fruit.Diameter();
string sm = fruit.Smell();
If you used enums for the string properties (limiting the scope of what the values could be i.e an enum for names, enum for colors etc) then you could get your result by condensing the two loops down to one.
Alternatively you could have set property for the int values that takes a string and parses it to int and go that route but I honestly thing the above is the simplest and clearest means to almost achieve what you want.
This is my take on the matter using just vanilla c++ and evil macros and casts. Still definitely overkill though.
The basic plan is to provide an operator[] to return a variant-type proxy class which in turn handles the io streams.
This interface allows you the ability to expose the class members as you choose and in the order you want.
In order not to repeat myself and to avoid having the burden of keeping things in sync I declare the fields I want in a separate header and include the header twice but changing the macro between includes. If I now add a new field or remove one then I only have to change one place.
fruit_fields.h
//NO Header Guards desired since we want to include multiple times
DECLARE_FIELD(std::string, Fruit, name)
DECLARE_FIELD(int, Fruit, weight)
DECLARE_FIELD(int, Fruit, diameter)
DECLARE_FIELD(int, Fruit, prize)
DECLARE_FIELD(std::string, Fruit, colour)
DECLARE_FIELD(std::string, Fruit, smell)
DECLARE_FIELD(std::string, Fruit, taste)
Utility Classes
#include <iostream>
#include <string>
#include <assert.h>
#include <vector>
#include <array>
#include <typeinfo>
//TypeId info
namespace FieldType
{
static const size_t String = typeid(std::string).hash_code();
static const size_t Integer = typeid(int).hash_code();
};
/*
Field Proxy is a simple variant class
If you want to support more field types then more code is needed
*/
class FieldProxy
{
public:
FieldProxy(int* value)
: m_typeHash( typeid(int).hash_code() )
, m_intValue(value)
{}
FieldProxy(std::string* value)
: m_typeHash(typeid(std::string).hash_code())
, m_stringValue(value)
{}
operator int() const
{
assert(m_typeHash == typeid(int).hash_code());
return *m_intValue;
}
operator std::string() const
{
assert(m_typeHash == typeid(std::string).hash_code());
return *m_stringValue;
}
friend std::ostream& operator<<(std::ostream& os, const FieldProxy& field);
friend std::istream& operator>>(std::istream& os, FieldProxy& field);
private:
size_t m_typeHash;
union
{
int* m_intValue;
std::string* m_stringValue;
};
};
std::ostream& operator<<(std::ostream& os, const FieldProxy& field)
{
if (field.m_typeHash == FieldType::Integer)
{
os << *(field.m_intValue);
}
else if (field.m_typeHash == FieldType::String)
{
os << *(field.m_stringValue);
}
return os;
}
std::istream& operator>>(std::istream& is, FieldProxy& field)
{
if (field.m_typeHash == FieldType::Integer)
{
is >> *(field.m_intValue);
}
else if (field.m_typeHash == FieldType::String)
{
is >> *(field.m_stringValue);
}
return is;
}
//data to obtain pointer to given field
struct FieldInfo
{
size_t fieldType;
size_t offset;
};
Fruit Class
//The struct we actually care about
struct Fruit
{
public:
static size_t getNumberFields()
{
return m_numberFields;
}
FieldProxy operator[](size_t index);
private:
//First include just declares the class members as you might expect
// string name;
// int weight; etc
#define DECLARE_FIELD(t, c, f) t f;
#include "fruit_fields.h"
#undef DECLARE_FIELD
static FieldInfo m_fields[];
static const size_t m_numberFields;
};
//Work out where the fields are positioned in the class
FieldInfo Fruit::m_fields[] =
{
//second time around - define the macro to generate the data
//needed to build the FieldProxy
#define DECLARE_FIELD( t, c, f) {typeid(t).hash_code(), offsetof(c,f)},
#include "fruit_fields.h"
#undef DECLARE_FIELD
};
//calculate how many fields there are
const size_t Fruit::m_numberFields = sizeof(Fruit::m_fields) / sizeof(Fruit::m_fields[0]);
//Index to a FieldInfo object and use it to create a FieldProxy variant
FieldProxy Fruit::operator[](size_t index)
{
assert(index < m_numberFields);
auto& fieldInfo = m_fields[index];
if (fieldInfo.fieldType == FieldType::Integer)
{
return FieldProxy(reinterpret_cast<int*> (reinterpret_cast<char*>(this) + fieldInfo.offset));
}
else if (fieldInfo.fieldType == FieldType::String)
{
return FieldProxy(reinterpret_cast<std::string*> (reinterpret_cast<char*>(this) + fieldInfo.offset));
}
else
{
assert(false);
return FieldProxy((int*)nullptr);
}
}
Main
You can now index in to the fruit class quite simply:
int main()
{
Fruit apple;
std::array<std::string, 7> questions = {
"Name ?",
"Weight ?",
"Diameter ?",
"Price ?",
"Colour ?",
"Smell ?",
"Taste ?"
};
assert(questions.size() == Fruit::getNumberFields());
for (size_t index = 0; index < Fruit::getNumberFields(); ++index)
{
bool succeeded = false;
do {
std::cout << questions[index] << ' ';
if (!(std::cin >> apple[index]))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}
else
{
succeeded = true;
}
} while (!succeeded);
}
std::cout << "----------" << std::endl;
for (size_t index = 0; index < Fruit::getNumberFields(); ++index)
{
std::cout << questions[index] << ' ' << apple[index] << std::endl;
}
return 0;
}
OK it is long since the thread was started.
For indexing a Struct with values of same type I have written this code:
struct DataValues
{
int Value1;
int Value2;
int Value3;
int Value4;
int Value5;
int Value6;
int Value7;
int Value8;
// the operator
// return the reference to make it settable.
int& operator[](int n)
{
// the idea, get the pointer of the first element
// and treat it as an array
return (&Value1)[n];
}
};
usage:
int main()
{
DataValues values;
values.Value4 = 6;
values[1] = 3;
values[0] = sizeof(DataValues);
return 0;
}
The downside is that you have to check the input parameter to avoid accessing memory which does not belong to the struct.
Chears
A problem has come up in my application where my PrintAll function will not work correctly and only ultimately crash my application. My app is supposed to read strings from a file and insert them into an array. The problem is it is reading incorrectly and will ultimately crash my app. Here is where I think the problem lies:
int main()
{
LoadMovies();
MovieList *movies = LoadMovies();
//movies->MovieList::PrintAll();
// // test methods for the Movie and MovieList classes
//PrintAllMoviesMadeInYear(movies, 1984);
//PrintAllMoviesWithStartLetter(movies, 'B');
//PrintAllTopNMovies(movies, 5);
//delete movies;
return 0;
}
MovieList* LoadMovies()
{
vector<string> movies;
ReadMovieFile(movies);
MovieList ml = MovieList(movies.size());
string name;
int year;
double rating;
int votes;
for (int i = 0; i < movies.size(); i++)
{
istringstream input_string(movies[i]);
getline(input_string, name, '\t');
input_string >> year >> rating >> votes;
Movie movie (name, year, votes, rating);
ml.Add(movie);
}
ml.PrintAll();
}
Complete Example:
/*
* File: MovieStatsProgram.cpp
* Author:
* Date:
* ===============================================================
* This is a console app to test the Movie and MovieList classes.
*
* TODO:
*
* You need to finish the implementation of the loadMovies method
* to create and initialize the MovieList object.
*
* You also need to create three static methods:
*
* PrintAllMoviesMadeInYear - it will print all the movies made in a
* given year once sort in alphabetical order and once sorted by the number
* of votes with the movie with the most number of votes printed first.
*
* PrintAllMoviesWithStartLetter - it will print all the movies started with
* a given letter sorted in alphabetical order
*
* PrintAllTopNMovies - it will display the top N movies based on the number of
* votes
*/
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;
class Movie {
public:
Movie();
Movie(string n, int y, int v, double r);
string get_name();
void set_name(string n);
int get_year();
void set_year(int y);
int get_votes();
void set_votes(int v);
double get_rating();
void set_rating(double r);
string PrintMovie();
private:
string name;
int year_made;
int votes;
double rating;
};
Movie::Movie() {
name = "null";
year_made = 0;
votes = 0;
rating = 0.0;
}
Movie::Movie(string n, int y, int v, double r) {
name = n;
year_made = y;
votes = v;
rating = r;
}
string Movie::get_name() {
return name;
}
void Movie::set_name(string n) {
name = n;
}
int Movie::get_year() {
return year_made;
}
void Movie::set_year(int y) {
year_made = y;
}
int Movie::get_votes() {
return votes;
}
void Movie::set_votes(int v) {
votes = v;
}
double Movie::get_rating() {
return rating;
}
void Movie::set_rating(double r) {
rating = r;
}
string Movie::PrintMovie() {
cout << fixed << setprecision(1) << rating << "\t\t" << votes << "\t\t" << "(" <<
year_made << ")" << "\t" << name << endl;
}
class MovieList {
public:
MovieList(int size);
~MovieList();
int Length();
bool IsFull();
void Add(Movie const& m);
string PrintAll();
private:
Movie* movies;
int last_movie_index;
int movies_size;
int movie_count = 0;
};
MovieList::MovieList(int size) {
movies_size = size;
movies = new Movie[movies_size];
last_movie_index = -1;
}
MovieList::~MovieList() {
delete [] movies;
}
int MovieList::Length() {
return last_movie_index;
}
bool MovieList::IsFull() {
return last_movie_index == movies_size;
}
void MovieList::Add(Movie const& m)
{
if (IsFull()) {
cout << "Cannot add movie, list is full" << endl;
return;
}
++last_movie_index;
movies[last_movie_index] = m;
}
string MovieList::PrintAll() {
for (int i = 0; i < last_movie_index; i++) {
movies[last_movie_index].Movie::PrintMovie();
//cout << movies[last_movie_index] << endl;
}
}
void ReadMovieFile(vector<string> &movies);
MovieList* LoadMovies();
enum MovieSortOrder
{
BY_YEAR = 0,
BY_NAME = 1,
BY_VOTES = 2
};
int main()
{
LoadMovies();
MovieList *movies = LoadMovies();
//movies->MovieList::PrintAll();
// // test methods for the Movie and MovieList classes
//PrintAllMoviesMadeInYear(movies, 1984);
//PrintAllMoviesWithStartLetter(movies, 'B');
//PrintAllTopNMovies(movies, 5);
//delete movies;
return 0;
}
MovieList* LoadMovies()
{
vector<string> movies;
ReadMovieFile(movies);
MovieList ml = MovieList(movies.size());
string name;
int year;
double rating;
int votes;
for (int i = 0; i < movies.size(); i++)
{
istringstream input_string(movies[i]);
getline(input_string, name, '\t');
input_string >> year >> rating >> votes;
Movie movie (name, year, votes, rating);
ml.Add(movie);
}
ml.PrintAll();
}
void ReadMovieFile(vector<string> &movies)
{
ifstream instream;
instream.open("imdbtop250.txt");
if (instream.fail())
{
cout << "Error opening imdbtop250.txt" << endl;
exit(1);
}
while (!instream.eof())
{
string movie;
getline(instream, movie);
movies.push_back(movie);
}
instream.close();
}
When I use MovieList::PrintAll in the main function, my function just crashes, and when I put it in the LoadMovies function, it will read and add data incorrectly before crashing. The size of the list is 251 and the application will just read the same data 251 times.
You have a two part problem:
1: As Brad S stated, your function returns nothing. This is a no-no.
MovieList* LoadMovies()
{
MovieList ml = MovieList(movies.size());
// Your function returns a pointer to a MovieList, so...
return &ml;
}
So, problem #2 is that you're going to return a pointer to something you created on the stack in your function. When you try to access it outside of your function, you'll run into undefined behavior.
Option 1:
MovieList* ml = new MovieList( movies.size() );
return ml;
You now need to delete ml when you're done w/ it.
Option 2:
Change your function to return a non-pointer... then you don't have the hassle of managing the memory.
EDIT: Try this
int main()
{
// Don't need this
// LoadMovies();
MovieList *movies = LoadMovies();
// Uncommented this
delete movies;
return 0;
}
MovieList* LoadMovies()
{
vector<string> movies;
ReadMovieFile(movies);
// CHANGE
MovieList* ml = new MovieList(movies.size());
// CHANGE
string name;
int year;
double rating;
int votes;
for (int i = 0; i < movies.size(); i++)
{
istringstream input_string(movies[i]);
getline(input_string, name, '\t');
input_string >> year >> rating >> votes;
Movie movie (name, year, votes, rating);
ml.Add(movie);
}
ml.PrintAll();
// CHANGE
return ml;
}
Your MovieList class has a fundamental problem. This comes to light on this line:
MovieList ml = MovieList(movies.size());
Your MovieList class has a member that is a pointer to dynamically allocated memory. Once you have this, you have to manage copying and assignment by creating a user-defined copy constructor and assignment operator.
The easiest fix for this is to use std::vector<Movie> instead of Movie * as a member variable of MovieList. Then copy-assignment comes for free and you don't need to implement further functions.
However, if you can't use std::vector for some reason, the following functions can be added:
class MovieList {
public:
//...
MovieList(const MovieList& m);
MovieList& operator=(MovieList m);
//...
};
#include <algorithm>
//...
// copy constructor
MovieList::MovieList(const MoveList& m) {
movies_size = m.size;
movie_count = m.movie.count;
last_movie_index = m.last_movie_index;
movies = new Movie[movies_size];
for (int i = 0; i < movies_size; ++i)
movies[i] = m.movies[i];
}
//...
// assignment operator
MovieList& MovieList::operator=(MoveList m) {
std::swap(m.movie_size, movie_size);
std::swap(m.last_movie_index, last_movie_index);
std::swap(m.movies, movies);
std::swap(m.movie_count, moviE_count);
return *this;
}
The easiest way to describe this to you is not to describe what these do. The best thing for you is to use your debugger and put a breakpoint in any of these functions and step through the code. When you hit the line I mentioned above, you will more than likely see that the copy constructor function is called -- then you can see it in action as to what it is doing.
The assignment operator is the function that's called when you assign an existing MovieList to another MovieList. It's implemented via the copy/swap idiom. This relies on a working copy constructor (provided above), and a destructor (which you already provided in your code). It works by creating a temporary MovieList, and swapping out the internals of the current MovieList with the temporary MovieList. There are many threads on SO as to how this works.
As to the reason why you need these functions above is that without the above functions, the line:
MovieList ml = MovieList(movies.size());
will create two MovieList objects, one temporary and one non-temporary, however the movies pointer for both will be pointing to the same memory. When the temporary is destroyed, the destructor is called, thus deleting the memory pointed to by movies. Now you have m1 pointing to memory that has gone up in smoke. Bad news when you try to use m1.
The user-defined copy and assignment functions above properly copy the object so that you get two distinct memory allocations for movies, so that when the destructor is called, the memory deleted will be unique to that object.
Again, all of this would be alleviated if you used std::vector and forego having to write copy ctor/assignment operators.