Change Object type based on inheritance - c++

I have the class Person:
class Person {
public:
int Age;
int ID;
};
And I have 2 classes: Adult and Child that inherit Person:
class Child : Person{
public:
Child();
void print_ms_child(){
cout << "I'm an child";
}
};
class Adult : Person{
public:
Adult();
void print_ms_Adult(){
cout << "I'm an adult";
}
};
I want to declare a Person Object
int main() {
Person my_Person;
my_Person.Age = 10;
my_Person.ID = 013272;
And then use conditions to set its type.
if(my_Person.Age > 18){
my_Person = new Adult(); // this won't work
}
else{
my_Person = new Child(); // this won't work
}
I suppose it it's posible but I don't know the right syntax for doing it (How do I do that?). (Sorry for posting code that won't even compile)
But also, I want to change from Child to Adult in case the Age changes (I'm not even sure that's possible).
my_Person.Age = 21;
my_Person = new Adult(); // this won't work

I would first define your class Person like this instead:
class Person {
int Age;
int ID;
protected:
Person(int age, int id): Age{age}, ID{id} {}
public:
virtual ~Person() = default;
};
That is, Person's constructor is made protected so that a class outside the hierarchy can't create a Person object, and its destructor is made virtual to make it suitable as a base class for a polymorphic type.
Then, both Child and Adult publicly inherit from Person, as you already have them. Their constructors end up calling Person's constructor with the same parametrization their constructors receive:
class Adult: public Person {
public:
Adult(int age, int id): Person(age, id) {}
// ...
};
class Child: public Person {
public:
Child(int age, int id): Person(age, id) {}
// ...
};
Finally, for creating objects that derived from Person, I would create the factory function below:
std::unique_ptr<Person> create_person(int age, int id) {
if (age < 18)
return std::make_unique<Child>(age, id);
else
return std::make_unique<Adult>(age, id);
}
This factory function creates a Child object if the to-create person's age is under 18. Otherwise, it creates an Adult object. This – determining the type of the object to create – is done at run time.
EDIT: You wondered how a Child object could become an Adult object after its age has reached 18 years old.
I would suggest making the state of a Person object immutable so that a fresh new object has to be created every time the age of a person is increased. This way, creating a new object from an existing one will be the only way to grow a Person's age and, at that moment, you will have the control to decide whether to create a Child or Adult object based on the new age.
For doing this, first, qualify the Age data member in Person as const (you can do the same with ID). Then, define the following Person's member function which always creates a new object (with the same ID) resulting from increasing a Person's object Age by one:
std::unique_ptr<Person> Person::increase_age() const {
return create_person(Age + 1, ID);
}
As you see, it delegates the creation of the object to the factory function, create_person(), already explained above. This member function can be called on both Child and Adult objects since it inherits from Person.

You cannot change the type of a variable after its declaration. C++ is a statically typed language. my_person is a Person. You cannot change that.

Related

How to create objects that has a variable based on the data from another object that is passed in the constructor

The title may be a little confusing but hear me out. I have this two classes , Entity and Human.
Entity is the parent class of human. When I create a human object, it will require an Entity object passed as argument in the constructor so all the human objects that I create has the same Entity object info.
This is my problem: If I change some data from the Entity object I want to update all the data from the Human objects that I created using that Entity Object in the constructor.
I want to implement this in ones of my University projects so I am allowed to use only the standard library.
I wrote this example so it's easier to understand:
#include <iostream>
using namespace std;
class Entity{
private:
//DATA
int life;
public:
//DEFAULT CONSTRUCTOR
Entity() {life = 100;}
//PARAMETRIZED CONSTRUCTOR
Entity(int life) {this -> life = life;}
//GETTER
int get_life(){return life;}
//SETTER
void set_life(int new_life){life = new_life;}
//FUNCTIONS
void print_life() {cout << "This entity has " << life << " life" << endl;}
};
class Human : public Entity{
public:
//DATA
string name;
//DEFAULT CONSTRUCTOR
Human() {name = "N/A";}
//PARAMETRIZED CONSTRUCTOR
Human(string name, Entity object){
Entity::set_life(object.get_life());
this -> name = name;
}
};
int main(){
//DATA
Entity Human_data(50);
Human Hero("Steve", Human_data);
Human Villain("Mike", Human_data);
//BODY
Human_data.set_life(5000);
Hero.print_life();
//END MAIN
return 0;}
As you can see, after I update Human data life from 50 to 5000, it does not also change Hero and Villain life to 5000 and only changes Human data life to 5000.
I'm assuming that when you call Human_data.set_life(5000) you want to effect all Human objects constructed with the Human_data object.
Here's an example of how you might use references for that. But note putting a reference inside a class is not without consequences. You might use a pointer instead, or even better a smart pointer. But I'm just trying to indicate the general idea.
On a technical note, using references means you have to get used to using initializer lists since references cannot be assigned.
#include <iostream>
using namespace std;
class Entity{
private:
int life;
public:
Entity() : life(100) {}
Entity(int life) : life(life) {}
int get_life() {return life;}
void set_life(int new_life){life = new_life;}
void print_life() {cout << "This entity has " << life << " life" << endl;}
};
class Human { // no inheritence
private:
Entity& entity; // store a reference to the entity
public:
string name;
Human(string name, Entity& object) // object is a reference
: entity(object), name(name)
{
}
void print_life() { entity.print_life(); } // forward to entity
};
int main(){
//DATA
Entity Human_data(50);
Human Hero("Steve", Human_data);
Human Villain("Mike", Human_data);
//BODY
Human_data.set_life(5000);
Hero.print_life();
//END MAIN
return 0;
}
The inheritance could be correct, if all "humans" are also entities (remember that inheritance is an "is a" relationship).
But the implementation is flawed.
Instead of passing an Entity object to the Human constructor, and using the separate and distinct Entity object to control the "life", pass the life itself to the Human constructor, and use set_life on the Human object directly.
Something like this:
class Human : public Entity
{
public:
// By default humans start out with 50 in life
Human(std::string const& name, int life = 50)
: Entity(life), name(name) // Initialize the parent class, then the name
{
}
private:
std::string name;
};
int main()
{
Human hero("The Hero", 5000); // The hero will have 5000 life
Human villain("The Villain"); // The villain will only have 50 life
// Some stuff...
// Update the heroes life
hero.set_life(4000); // Uh oh, the hero have lost 1000 life!
// ...
}

I have problem with least privilege principle. incrementing a member when an object is created

I want to keep track of the number of students in my system so, My idea was to make a static datamember in the "StudentController" class called "_numOfStudents" and increment it with the Student's constructor but it didn't work so, I moved it into the "Student" class and made that when a Student object is created the number increment by the help of the constructor. The problem is: isn't it not the Student class's business to know how many students are there thus breaking the principle of least privilege. what can I do better to keep track of the student objects' count.
Student(string firstName, string lastName, int age,vector<float>&subjects)//Student conctructor
{
SetFirstName(firstName);
setLastName(lastName);
SetAge(age);
SetMarks(subjects);
this->m_id++;
StudentController::_numberOfStudents++;
}
class StudentController//this is where i declared the static data member
{
private:
list<Student> _students;
public:
static int _numberOfStudents;
StudentController() {};
StudentController(list<Student>& st) :_students(st) {};
}
}
};
int StudentController::_numberOfStudents = 0;
When you try to do StudentController::_numberOfStudents++; in the constructor, the StudentController class is not yet defined, therefore the compiler doesn't know about that class and its static member.
Maybe keeping track of the students shouldn't be the role of the Student class itself. Instead it should be the role of a separate Classroom object:
struct Student;
struct Classroom {
void add(Student&) {
m_count++;
}
size_t count() const { return m_count; }
private:
size_t m_count{0};
};
struct Student {
Student(Classroom& classroom) {
classroom.add(*this);
}
};
int main()
{
Classroom classroom;
Student alice{classroom};
Student bob{classroom};
assert(classroom.count() == 2);
}

Classes and subclasses - sending data from derived class to primary class

I'm trying to send data (fishname) from the class "Fish" to the primary class "Animal". I made an object, and tried to access the subclass function to send the data from the subclass to the primary class.
My goal: Send whatever I write in the readFish() to Animal 's private name char.
Code:
#include <iostream>
using namespace std;
const int STRLEN = 32;
class Animal {
private:
char name[STRLEN];
public:
Animal() { }
Animal(char animalname) { name[STRLEN] = animalname; }
};
class AnimalInWater : public Animal {
private:
public:
AnimalInWater() { }
};
class Fish : public AnimalInWater {
private:
char fishname[STRLEN];
public:
void readFish() {
cout << "The name of the fish: "; cin.getline(fishname, STRLEN);
Animal(fishname); // Is supposed to use the constructor seen in Animal class
}
};
int main() {
Animal a1;
d1.readFish(); // readFish() is in the Fish class, which is under AnimalInWater which is under Animal.
// In other words; the readFish() function is the granddaughter class of Animal.
return 0;
}
I made an object, and tried to access the subclass function to send the data from the subclass to the primary class.
You're talking about classes as though they were objects. If you have an instance of Fish, then it is an instance of AnimalInWater and also of Animal at the same time.
If you want readFish() to assign some value to name, then just assign it to name in that method in the Fish class.
Looking at this, piece by piece.
The variable name is an array of char and has STRLEN slots.
The slots are numbered from 0 to STRLEN-1.
The expression name[0] represents the first slot.
The expression name[STRLEN] represents one past the end of the array.
The variable animalname is a single character.
The constructor is placing the value inside animalname to the slot name[STRLEN], which is beyond the array.

using pointers with inheritance in C++

I have a problem in understanding one important aspect of the c++. see the example below:
class human
{
protected:
int age;
public:
human(int a){age=a;}
void f(){cout<<"\n the age of human ="<<age;}
};
and the class below is derived from above class:
class student: public human
{
private:
int num;
public:
student(int b, int c):human(b){num=c;}
void g(){cout<<"\n the student with age "<<age<<" has " <<num;}
};
now I want to use these classes in the code below:
int main()
{
human h1(10);
student s1(11,4);
human* p=&h1;
p->f();
p=&s1;
p->g();
}
but the compiler gives me error on the p->g().
what is the problem?
Your base class pointer still points to a base class object human* p=&h1;. Try something like this:
#include <iostream>
class human
{
protected:
int age;
public:
human(int a){ age = a; }
void f(){ std::cout << "\n the age of human =" << age; }
virtual void g(){}
};
// class student code here...
int main()
{
human* p = new student(11, 4);
p->f();
p->g();
delete p;
return 0;
}
or instead use the s1 object and not the h1:
student s1(11, 4);
human* p = &s1;
The function g() is not part of the interface of the class human. p is a human*. If you upcast p to a student*, it will work.
The compiler doesn't know that your pointer is of student, it only sees the declaration as human. To fix this you need to cast it to a student pointer.
((student*)p)->g();
You can announce a virtual g in the Human.
class human {
public:
virtual void g() {}
... ...
};
In your example main() has two objects: human h1 and student s1.
int main()
{
human h1(10);
student s1(11,4);
human* p=&h1;
p->f();
p=&s1;
p->g();
}
Aside from the constructor, the declaration of the parent class human has only the function f() and the child class student, only adds the one function g().
The human pointer can look up only the function contained in it's class. Any information in the child's look up table is unknown to the parent because human was never told anything about the child class.
Passing the child object's reference &s1 to the parent pointer means that the parent can only look up the function it already knows. This is sometimes called object slicing. The child function g() has no place keeper in the parent's class. But, the child has both functions in it's look up table.
In other words, the child knows both itself and it's parent because it is declared class student: public human but the parent class knows only itself because it is merely declared "class human"
As Bauss says, it is possible to "upcast" the human pointer but I would not recommended that style. It will only confuse what type of pointer is being actually used.
This is the way I would rewrite main:
int main()
{
human h1(10);
human* p=&h1;
p->f();
/* prints " the age of human = 10" */
student s1(11,4);
student* q=&s1;
q->f();
/* prints " the age of human = 11" */
q->g();
/* prints " the student with age 10 has 4" */
}

Efficient way to copy data from instance of parent class into child class

I have class scene (from assimp library) and I extended it with some functions and made class sceneChild. Now I need to copy data from instance of scene into sceneChild instance. I know it is possible to write manually something like this: sceneChild.a=scene.a , sceneChild.b=scene.b ... or this.a=scene.a , this.b=scene.b in copy constructor. However something tells me that this can be done in few lines of code. Am I right? If so then how this can be done? Thank you!
I ask about it because I need to perform this copy operation not only with scene class but with many other classes that are full of data and therefore it will take too long to do it manually.
Example:
class Parent
{
public:
string name;
};
class Child: Parent
{
public:
int age;
};
int main()
{
Parent p;
p.name = "some name";
Child c(p); // some magic here so that data (in this case string "name")
//is copied from p to c so that c.name=="some name"
return 0;
}
I'm not famliar with the scene class, but generaly you can use initialization list in the constructor to initialize the parent class.
In your example you can use:
class Parent
{
public:
string name;
};
class Child: public Parent
{
public:
int age;
Child(Parent& p) : Parent(p) {}
};