We have a parent class called Student. We have a child class: StudentCS.
Student.h:
#include <iostream.h>
#include<string.h>
#include<vector.h>
#include "Course.h"
class Course;
class Student {
public:
Student();
Student(int id, std::string dep, std::string image,int elective);
virtual ~Student();
virtual void Study(Course &c) const; // this is the function we have a problem with
void setFailed(bool f);
[...]
};
Student.cpp:
#include "Student.h"
[...]
void Student::Study(Course &c) const {
}
And we have StudentCS.h:
#include "Student.h"
class StudentCS : public Student {
public:
StudentCS();
virtual ~StudentCS();
StudentCS (int id, std::string dep, std::string image,int elective);
void Study(Course &c) const;
void Print();
};
And StudentCS.cpp:
void StudentCS:: Study (Course &c) const{
//25% to not handle the pressure!
int r = rand()% 100 + 1;
cout << r << endl;
if (r<25) {
cout << student_id << " quits course " << c.getName() << endl;
}
}
We create student in the main:
Student *s;
vector <Student> uniStudent;
[...]
if(dep == "CS")
s = new StudentCS(student_id,dep,img,elective_cs);
else
s = new StudentPG(student_id,dep,img,elective_pg);
uniStudent.push_back(*s);
Then we call to study, but we get the parent study, and not the child!
Please help!
The code compiles but when run and called on the uniStudent.Study() it uses the parent and not the child
EDIT: after your edit the problem is clear.
The problem is that you are storing base concrete objects in a STL container. This creates a problem called object slicing.
When you add a student to a vector<Student>, since the allocator of the vector is built on the Student class, every additional information on derived classes is just discarded. Once you insert the elements in the vector they become of base type.
To solve your problem you should use a vector<Student*> and store directly references to students in it. So the allocator is just related to the pointer and doesn't slice your objects.
vector<Student*> uniStudent;
...
uniStudent.push_back(s);
uniStudent[0]->study();
Mind that you may want to use a smart pointer to manage everything in a more robust way.
Related
I am trying to create a simple program in C++ that creates lists of movies using 2 classes: Movie, which contains details of one movie, and Movies, which contains a name and a vector of Movie objects. The whole point is that the user should only interact with the class Movies, therefore I have chosen to make all the members (both data and methods) of the Movie class private.
I keep getting the following error if I let Movie::~Movie() as it is, but if I comment it in both .h and .cpp, it works just fine.
I have explicitly made Movies class a friend of Movie class, so it can access all its members.
error from Visual Studio Community 2019
Movie.h:
#pragma once
#include<iostream>
#include<string>
class Movie
{
friend class Movies;
private:
std::string name;
std::string rating;
int watchedCounter;
int userRating;
std::string userOpinion;
// methods also private because the user should only interact with the Movies class, which is a friend of class Movie
std::string get_name() const;
Movie(std::string nameVal = "Default Name", std::string ratingVal = "Default Rating", int watchedCounterVal = 0, int userRatingVal = 0, std::string userOpinionVal = "");
~Movie();
};
Movie.cpp:
#include "Movie.h"
// Name getter
std::string Movie::get_name() const
{
return this->name;
}
///*
// Destructor
Movie::~Movie()
{
std::cout << "Movie destructor called for: " << this->name << std::endl;
}
//*/
// Constructor
Movie::Movie(std::string nameVal, std::string ratingVal, int watchedCounterVal, int userRatingVal, std::string userOpinionVal) : name{ nameVal }, rating{ ratingVal }, watchedCounter{ watchedCounterVal }, userRating{ userRatingVal }, userOpinion{userOpinionVal}
{
std::cout << "Movie constructor called for: " << name << std::endl;
}
Movies.h:
#pragma once
#include<iostream>
#include<vector>
#include "Movie.h"
class Movies
{
private:
std::string listName;
std::vector<Movie> vectOfMovies;
static int listNumber;
public:
Movies(std::string listNameVal = "Default User's Movie List ");
~Movies();
std::string get_listName() const;
void addMovie(Movie m);
};
Movies.cpp:
#include<string>
#include "Movies.h"
int Movies::listNumber = 0;
// Constructor
Movies::Movies(std::string listNameVal) : listName{listNameVal}
{
++listNumber;
listName += std::to_string(listNumber);
std::cout << "MovieS constructor called for: " << listName << std::endl;
}
// Destructor
Movies::~Movies()
{
std::cout << "MovieS destructor called for: " << listName << std::endl;
}
std::string Movies::get_listName() const
{
return this->listName;
}
//Add movie to list of movies
void Movies::addMovie(Movie m)
{
this->vectOfMovies.push_back(m);
}
And the main .cpp file:
#include <iostream>
// #include "Movie.h"
#include "Movies.h"
int main()
{
Movies moviesObj;
moviesObj.get_listName();
return 0;
}
If the constructor/destructor is declared as private, then the class cannot be instantiated. If the destructor is private, then the object can only be deleted from inside the class as well. Also, it prevents the class from being inherited (or at least, prevent the inherited class from being instantiated/destroyed at all).
The use of having destructor as private:
Any time you want some other class to be responsible for the life cycle of your class' objects, or you have reason to prevent the destruction of an object, you can make the destructor private.
For instance, if you're doing some sort of reference counting thing, you can have the object (or manager that has been "friend"ed) responsible for counting the number of references to itself and delete it when the number hits zero. A private dtor would prevent anybody else from deleting it when there were still references to it.
For another instance, what if you have an object that has a manager (or itself) that may destroy it or may decline to destroy it depending on other conditions in the program, such as a database connection being open or a file being written. You could have a "request_delete" method in the class or the manager that will check that condition and it will either delete or decline, and return a status telling you what it did. That's far more flexible that just calling "delete".
So, I suggest that you could declare ~Movie(); as public. Then, the problem will be solved.
A little weird prob;em i came across when working on something that I got stuck with and I have no idea why this happens.
So i have 2 files (actually way more but those arent important) called Employee and Keeper. Employee is the base class while Keeper is the derived class.
The employee has several attributes and a method called saveFile and the keep inherits these.
Employee.h:
protected:
const int number;
const std::string name;
int age;
// All ordinary employees
Employee *boss = nullptr; // works for ...
public:
void saveFile(std::ostream&) const;
Keeper.cc
void Keeper::saveFile(ostream& out) const
{
out << "3\t3\t"
<< number << "\t" << name << "\t" << age
// The error happen here on boss->number
<< "\t" << cage->getKind() << "\t" << boss->number << endl;
}
Keeper.h (full code)
#ifndef UNTITLED1_KEEPER_H
#define UNTITLED1_KEEPER_H
#include "Employee.h"
// tell compiler Cage is a class
class Cage;
#include <string> // voor: std::string
#include <vector> // voor: std::vector<T>
#include <iostream> // voor: std::ostream
class Keeper : public Employee {
friend std::ostream& operator<<(std::ostream&, const Keeper&);
public:
Keeper(int number, const std::string& name, int age);
~Keeper();
/// Assign a cage to this animalkeeper
void setCage(Cage*);
/// Calculate the salary of this employee
float getSalary() const;
/// Print this employee to the ostream
void print(std::ostream&) const;
// =====================================
/// Save this employee to a file
void saveFile(std::ostream&) const;
protected:
private:
// Keepers only
Cage *cage = nullptr; // feeds animals in ...
};
Now i get the error on the const int number from the employee.h when i call boss->number in the saveFile method.
The error is on this line:
<< "\t" << cage->getKind() << "\t" << boss->number << endl;
because of boss->number
I have no idea why this happens and everywhere I read it said it should compile just fine but it doesnt.
Can anyone help?
Thank you~
The number member of the boss object is protected from direct access by functions outside of the object itself, even when owned by an object of the same type. The exceptions would be friend classes and methods, and copy constuctors.
In response to a comment: Inheritance is not your problem. The data in the object itself is protected from outside access. Your Keeper object inherits its own number member that it can access, as well as a pointer to a boss Employee. To fix your problem you can either make number public, or add an access method to return the value.
This question already has answers here:
Updating vector of class objects using push_back in various functions
(2 answers)
Closed 7 years ago.
I am attempting to make a text adventure sort of game, and I would like to avoid a bunch of conditionals, so I am trying to learn about the classes stuff and all that. I have created several classes, but the only ones that pertain to this problem are the Options class and the Items class. My problem is that I am trying to push_back() a object into a vector of the type of that object's class and it apparently doesn't happen yet runs until the vector is attempted to be accessed. This line is in main.cpp. I have researched on this, but I have not been able to find a direct answer, probably because I'm not experienced enough to not know the answer in the first place.
The program is separated into 3 files, main.cpp, class.h, and dec.cpp.
dec.cpp declares class objects and defines their attributes and all that.
main.cpp:
#include <iostream>
#include "class.h"
using namespace std;
#include <vector>
void Option::setinvent(string a, vector<Item> Inventory, Item d)
{
if (a == op1)
{
Inventory.push_back(d);
}
else {
cout << "blank";
}
return;
}
int main()
{
vector<Item> Inventory;
#include "dec.cpp"
Option hi;
hi.op1 = "K";
hi.op2 = "C";
hi.op3 = "L";
hi.mes1 = "Knife";
hi.mes2 = "Clock";
hi.mes3 = "Leopard!!";
string input1;
while (input1 != "quit")
{
cout << "Enter 'quit' at anytime to exit.";
cout << "You are in a world. It is weird. You see that there is a bed in the room you're in." << endl;
cout << "There is a [K]nife, [C]lock, and [L]eopard on the bed. Which will you take?" << endl;
cout << "What will you take: ";
cin >> input1;
hi.setinvent(input1, Inventory, Knife);
cout << Inventory[0].name;
cout << "test";
}
}
dec.cpp just declares the Item "Knife" and its attributes, I've tried pushing directly and it works, and the name displays.
class.h
#ifndef INVENTORY_H
#define INVENTORY_H
#include <vector>
class Item
{
public:
double damage;
double siz;
double speed;
std::string name;
};
class Player
{
public:
std::string name;
double health;
double damage;
double defense;
double mana;
};
class Monster
{
public:
double health;
double speed;
double damage;
std::string name;
};
class Room
{
public:
int x;
int y;
std::string item;
std::string type;
};
class Option
{
public:
std::string op1;
std::string op2;
std::string op3;
std::string mes1;
std::string mes2;
std::string mes3;
void setinvent(std::string a, std::vector<Item> c, Item d);
};
#endif
Any help would be greatly appreciated! I realize that the whole structure may need to be changed, but I think that this answer will help even if that may be the case.
My problem is that I am trying to push_back() a object into a vector of the type of that object's class and it apparently doesn't happen yet runs until the vector is attempted to be accessed.
it happen but only inside your setinvent method:
void Option::setinvent(string a, vector<Item> Inventory, Item d)
^^^^^^^^^^^^ - passed by value
Inventory is passed by value which means it is a local vector variable in setinvent function. If you want to modify vector from main function, make it a reference:
void Option::setinvent(string a, vector<Item>& Inventory, Item d)
^^^^^^^^^^^^ - passed by reference, modifies vector from main
now Inventory is local reference variable. Also dont forget to change setinvent declaration in header file.
I got two classes, one named Person that I checked is working (I can create objects of that class so the problem should not be here).
I then have another class called Family with composition from Person:
Family.h
#include "Person.h"
class Family
{
public:
Family();
void printFamily();
private:
Person dad_();
Person mum_();
Person son_();
Person daughter_();
};
Family.cpp
#include "Family.h"
Family::Family()
{
}
void printFamily()
{
dad_.printAll();
mum_.printAll();
son_.printAll();
daughter_.printAll();
//printAll() is a function in the Person class that worked when
//I tested it earlier with only the person class
}
But when i try to compile this I get an error:
left of '.printAll' must have class/struct/union
'son_' : undeclared identifier
This error goes for all the .printAll() calls in family.cpp.
I can't see why this wouldn't work, so I hope you can.
Edit1:
Ok i changed
void printFamily()
to
void Family::printFamily()
That removes one error, but i still get
left of '.printAll' must have class/struct/union
Edit2
Ah my bad with the Person calls i changed them to
Person dad_;
and the same with the rest.
Seems like their might be an error with my Person class so i will post that also
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person( const string & = "000000-0000", const string & = "N", const string & = "",const string & = "N");
~Person();
void setFirstName(const string &);
void setMiddleName(const string &);
void setLastName(const string &);
void getData(string &,string &,string &,string &);
static int getNumberOfPersons();
void printPartially() const;
void printAll() const;
bool checkForSameName(const Person &);
private:
string firstName_;
string middleName_;
string lastName_;
string socialSecNumber_;
static int numberOfPersons_;
};
Person.cpp
#include "Person.h"
#include <iostream>
int Person::numberOfPersons_ = 0;
Person::Person( const string &sNumber, const string &firstName, const string &middleName,const string &lastName )
:firstName_(firstName),middleName_(middleName),lastName_(lastName),socialSecNumber_(sNumber)
{
numberOfPersons_ ++;
}
Person::~Person()
{
numberOfPersons_--;
}
void Person::setFirstName(const string &firstName)
{ firstName_ = firstName; }
void Person::setMiddleName(const string &middleName)
{ middleName_ = middleName; }
void Person::setLastName(const string &lastName)
{lastName_ = lastName;}
void Person::getData(string &fName,string &mName,string &lName,string &sNumber)
{
fName = firstName_;
mName = middleName_;
lName = lastName_;
sNumber = socialSecNumber_;
}
int Person::getNumberOfPersons()
{
return numberOfPersons_;
}
void Person::printPartially() const
{
cout <<"Navn: "<<firstName_<<" "<<middleName_<<" "<<lastName_<<endl;
cout <<"Født: ";
for (int i = 0;i<6;i++)
{
cout <<socialSecNumber_.at(i);
}
}
void Person::printAll() const
{
cout <<"Navn: "<<firstName_<<" "<<middleName_<<" "<<lastName_<<endl;
cout <<"Personnr: "<<socialSecNumber_<<endl;
}
bool Person::checkForSameName(const Person &p)
{
if (p.firstName_ == firstName_ && p.middleName_ ==middleName_ && p.lastName_ == lastName_)
return true;
else
return false;
}
Now i am getting some new errors:
error C2011: 'Person' : 'class' type redefinition
see declaration of 'Person'
'Family::dad_' uses undefined class 'Person'
The "dad" error applies to the whole family
You have a few syntax issues.
First, you're declaring each of what are supposed to be member variables as functions which return Person. They should look like (note, no parens):
Person dad_;
Person mum_;
Person son_;
Person daughter_;
You're also missing the scoping on your definition of printFamily:
void Family::printFamily() {
...
}
Without the preceding Family::, C++ thinks you're defining a free function, and doesn't know to look inside the Family class for the declarations of dad_, mum_, etc.
Additionally, at least with the code you've shown, there's no way to initialize the people in your class. The Family constructor should take arguments to define the people, or you should have setters which allow defining them later. Right now, you'll get 4 identical people, set up however the default person constructor builds them.
I would normally prefer the constructor method, but I have other design reservations about your code to begin with (e.g. Does a family always contain mum, dad, brother, sister?) and that's not really what this question is about.
The line:
Person dad_();
says that dad_ is a function that returns a Person, not an object. Did you mean that? Similarly for others.
Try
Person dad_;
Family.h
#include "Person.h"
class Family
{
public:
Family();
void printFamily();
private:
Person dad_;
Person mum_;
Person son_;
Person daughter_;
};
Family.cpp
#include "Family.h"
Family::Family()
{
}
void Family::printFamily()
{
dad_.printAll();
mum_.printAll();
son_.printAll();
daughter_.printAll();
//printAll() is a function in the Person class that worked when
//I tested it earlier with only the person class
}
The out of line definition of a member function needs to include the class name:
void Family::printFamily()
{
//...
Surprisingly, you already got this right for the constructor but then immediately forgot...
Second, your private class members are functions, not data members (which is odd), but if that's deliberate, you need to call them:
dad_().printAll();
// ^^^
guys, I have the following code, but I'm getting error at compiling time... the error is at the end. Thanks
1st Class is "Person"
#ifndef PERSON_H//protecting .h files
#define PERSON_H//protecting .h files
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person();
Person(string first, string last)
{
firstName = first;
lastName = last;
}
virtual void setName(string first, string last)
{
firstName = first;
lastName = last;
}
virtual void setWeightAge(int w, int a)
{
weight = w;
age = a;
}
virtual string getFirstName()
{
return firstName;
}
virtual string getLastName()
{
return lastName;
}
virtual int getWeight()
{
return weight;
}
virtual int getAge()
{
return age;
}
virtual void printPerson()
{
cout << "Name: " << firstName << " " << lastName << endl;
cout << "Age: " << age << endl;
cout << "Weight: " << weight << endl;
}
protected:
string firstName, lastName;
int weight, age;
};
#endif
2nd Class is "Student"
#ifndef STUDENT_H//protecting .h files
#define STUDENT_H//protecting .h files
#include <iostream>
#include <string>
#include "Person.h"
using namespace std;
class Student : public Person
{
public:
Student();
Student(Person s)
{
sStudent = s;
}
virtual void setGPA(double g)
{
gpa = g;
}
virtual void setSchedule(string c, string t, string d)
{
stClass = c;
time = time;
days = d;
}
virtual void setGrade(char g)
{
grade = g;
}
virtual double getGPA()
{
if (grade == 'a') { gpa = 4.0; }
if (grade == 'b') { gpa = 3.0; }
if (grade == 'c') { gpa = 2.0; }
else gpa = 0;
return gpa;
}
virtual char getGrade()
{
return grade;
}
virtual void printSchedule()
{
cout << "Class | Days | Time " << endl;
cout << stClass << " | " << days << " | " << time << endl;
}
protected:
string stClass, time, days;
char grade;
double gpa;
Person sStudent;
};
#endif
and Main()
#include <iostream>
#include "Student.h"
using namespace std;
int main()
{
//creating a person
Person john("John", "Smith");
john.setWeightAge(180, 39);
john.printPerson();
//making john a student
Student johnStdntMath(john);
johnStdntMath.setSchedule("Math", "7:45", "M, W");
johnStdntMath.setGrade('b');
johnStdntMath.printPerson();
johnStdntMath.printSchedule();
system("pause");
return 0;
errors:
1>------ Build started: Project: Person, Configuration: Debug Win32 ------
1> main.cpp
1>main.obj : error LNK2019: unresolved external symbol "public: __thiscall Person::Person(void)" (??0Person##QAE#XZ) referenced in function "public: __thiscall Student::Student(class Person)" (??0Student##QAE#VPerson###Z)
1>c:\users\jorge\documents\visual studio 2010\Projects\Person\Debug\Person.exe : fatal error LNK1120: 1 unresolved externals
}
I suggest you double check your is-A and has-A relationships.
Writing Student : public Person says that a Student is-A Person. But later, you have a member variable sStudent of type Person, which says a Student has-A Person, and I'm guessing is not what you really want.
Check out the answers to this question: Inheritance vs. Aggregation for better explanations.
Listen to your linker, it's just as it says: In constructor Student::Student(Person) you're referring to constructor Person::Person(), but you didn't define Person::Person(), not in a way the linker can see when it does its thing with the Student constructor.
Technically, because you are filling in sStudent in the Student constructor's body the compiler first default-initializes the Person object sStudent, and then assigns to it s, the Person parameter of the constructor. If you'd use the initializer list then the Person member wouldn't be default-initialized and then assigned to but rather copy-constructed right away:
Student(const Person& s) : sStudent(s) { }
But the question remains: Why are you publicly declaring a default constructor in Person and not define it?
Also, you have a leak in Student. The string and Person members of Student won't clean up because when a Student object destructs its destructor won't be called. The Person destructor will be called, but not the Student destructor, and the reason being that the destructor of Person is non-virtual.
One more thing: It's a bad idea in object-oriented design in general and C++ in particular to use inheritance for reuse. The reason is that this very often leads to a violation of the LSP. It can also bear a (not major but nonetheless) performance overhead for introducing a virtual table. But it's the correctness that suffers that matters when you pick inheritance when you should really be using delegation.
You are accessing the no argument constructor for Person when you create the johnStdntMath instance. You need to either
Implement Person::Person() or ...
Change Student::Student(Person s) to Student::Student(Person const& s)
There are some other problems in your code as well. Student is-a Person so there is no need for the Student class to have a Person member variable - it shares the instance variables of its base class by virtue of inheritance. In other words, Student extends Person so your program could be written:
int main() {
Student johnStdntMath;
johnStdntMath.setName("John", "Smith")
johnStdntMath.setWeightAge(180, 39);
johnStdntMath.setSchedule("Math", "7:45", "M, W");
johnStdntMath.setGrade('b');
johnStdntMath.printPerson();
johnStdntMath.printSchedule();
return 0;
}
I would also avoid the using namespace std; statement anywhere and especially in a header file. For example, your Student class contains a member variable named time. This will conflict with the std::time_t std::time(std::time_t*) function defined in <ctime> if that header is included before "student.h".
You haven't implement the default constructor Person(),you can write it like this:
Person():weight(0)
,age(0){};
If you just want to complier it, this is enough;
Here are some tips below:
1.Check your mind, does class Student really need a Person member. If you really need it, Student(Person) may add an explicit symbol:
explicit Student(const Person& s) : sStudent(s) {...};
2.In Person.h
protected:
string firstName, lastName;
int weight, age;
the protected may be private?