Compilation error on diamond problem(inheritance) - c++

Why does the code below throw the following compilation error on msvc ?
No default constructor exists for class 'Person'
If I don't create a default constructor in the class Person, then should I delete the constructor of Employee and Student that takes only one argument and call Employee(name,age,grade) and Student(name,age,cl) from ctor of Manager although the Base class constructor is never going to get called from Employee and Student class?
#include<iostream>
#define endl '\n'
using std::cout;
class Person {
std::string mname;
int mage;
public:
//Person() = default;
Person(const std::string& name, int age) :mname{ name }, mage{ age }{}
};
class Employee :virtual public Person {
int msalary;
public:
Employee(const std::string& name, int age, int sal) :Person{ name,age }, msalary{ sal }{}
Employee(int sal): msalary { sal }{}
};
class Student : virtual public Person {
int mclass;
public:
Student(const std::string& name, int age, int cls) :Person{ name,age }, mclass{ cls }{}
Student(int cls) :mclass{ cls } {}
};
class Manager :public Student, Employee {
private:
public:
Manager(const std::string& name,int age, int salary,int cl) :Person{name,age}, Student{cl}, Employee{salary} {}
};
int main() {
Manager manager{ "lorem",40,10000 ,10};
return 0;
}

Why does the above code throws compilation error
The problem is that before entering the body of the ctor Employee::Employee(int), the default ctor Person::Person() of Person will be used but the compiler will not synthesize the default ctor Person::Person() as the parameterized ctor Person::Person(const std::string&, int) is present.
The error can be reproduced by just the following example:
class Person {
std::string mname;
int mage;
public:
//Person() = default;
Person(const std::string& name, int age) :mname{ name }, mage{ age }{}
};
class Employee :virtual public Person {
int msalary;
public:
Employee(const std::string& name, int age, int sal) :Person{ name,age }, msalary{ sal }{}
//--------------------v--------------------->here Person::Person() is called implicitly
Employee(int sal): msalary { sal }{}
};
Demo error

The cause of the problem:
Both Employee and Student have constructors that do not call the only available constructor of the base class Person (requireing name and age parameters). Therefore the compiler will attempt to use a default constuctor fo the base class which is not available.
You can handle it the following way:
Decide what will be the default name and age for a Person derived class like Employee, Student.
Use the defaults to construct the base, e.g.:
static inline const std::string DEFAULT_NAME = "<default_name>";
static inline const int DEFAULT_AGE = 20;
Employee(int sal) : Person(DEFAULT_NAME, DEFAULT_AGE), msalary{ sal } {}

Related

C++ - Unable to inherit from base class | Gives empty values

I guess I am doing something wrong here in the below code. I want to inherit the methods of class Person in class Employee.
#include<bits/stdc++.h>
using namespace std;
class Person{
private:
string name;
int age;
public:
Person(string name, int age){ //Base parameterized constructor
name = name;
age = age;
}
void getName(){
cout<<"Name: "<<name<<endl;
}
void getAge(){
cout<<"Age: "<<age<<endl;
}
};
class Employee: public Person{ //Default inheritance type is private
private:
int employeeID;
public:
Employee(string name, int age, int id) : Person(name, age){ //Derived parameterized constructor
employeeID = id;
}
void getEmployeeDetails(){
getName();
getAge();
cout<<"Employee ID: "<<employeeID<<endl;
}
};
int main(){
Employee* e = new Employee("John", 24, 14298);
e->getEmployeeDetails();
return 0;
}
I am getting the below output:
Name:
Age: 0
Employee ID: 14298
Please let me know what am I missing here. Any help would be appreciated!
The issue is not with inheritance, but with the fact that Person never initializes any of its fields.
This code:
Person(string name, int age){ //Base parameterized constructor
name = name;
age = age;
}
assigns local variable name to itself and same with age, because parameters shadow class member names. Member objects with the same name are never initialized.
Three solutions possible (listed in subjective order in which I prefer them):
Use member initializer list to initialize your members:
Person(string name, int age) : name{name}, age{age}
{
}
Use different names
Person(string providedName, int providedAge)
{
name = providedName;
age = providedAge;
}
Use this to disambiguate objects
Person(string name, int age){ //Base parameterized constructor
this->name = name;
this->age = age;
}
Nothing to do with inheritance, the code would have been wrong anyway.
This
Person(string name, int age){ //Base parameterized constructor
name = name;
age = age;
}
should be this
Person(string n, int a){
name = n;
age = a;
}
Because your constructor parameter declaration names are the same as your member variables, the member variables are hidden and you were just assigning the parameters to themselves.
A better way to write the same code is to use an initialiser list
Person(string name, int age) : name(name), age(age)
}
Initialiser lists have a couple of advantages, one of them is that there's no ambiguity, you can have the parameter names the same as the member variable names. The other (more important) is that in general, initialisation is more efficient than assignment.

How to change a class to another class which derive from the same base class?

I have a base class called Animal:
class Animal {
protected:
std::string name;
int age;
int weight;
public:
Animal(const std::string& _name, int _age, int _weight):name(_name),age(_age),weight(_weight) {};
virtual void animal_cange(Animal*) = 0;
};
and from the Animal class derives two sublasses
class Dog : public Animal {
public:
Dog(const std::string& _name, int _age, int _weight) :Animal(_name, _age, _weight) {};
void animal_cange(Animal* poot) override {
this = new Cat(poot->name,poot->age,poot->weight);
}
};
and this
class Cat : public Animal {
public:
Cat(const std::string& _name, int _age, int _weight) :Animal(_name, _age, _weight) {};
void animal_cange(Animal* poot) override {
this = new Dog(name, age, weight);
}
};
I made a virtual funcion in the base class caled virtual void animal_cange(Animal*) = 0; which should change a Dog object to a Cat object if it is called with the object's already existing name, age and weight value and visa versa but it always gives me error like:
assignment to 'this' (anachronism)
a value of type "Cat *" cannot be assigned to an entity of type "Dog *"
protected member "Animal::name" (declared at line 12) is not accessible through a pointer or object
I also tried without animal_change being a virtual function like this:
class Animal {
protected:
std::string name;
int age;
int weight;
public:
Animal(const std::string& _name, int _age, int _weight):name(_name),age(_age),weight(_weight) {};
};
class Dog : public Animal {
public:
Dog(const std::string& _name, int _age, int _weight) :Animal(_name, _age, _weight) {};
void animal_cange() {
this = new Cat(this->name,this->age,this->weight);
}
};
class Cat : public Animal {
public:
Cat(const std::string& _name, int _age, int _weight) :Animal(_name, _age, _weight) {};
void animal_cange() {
*this = new Dog(name, age, weight);
}
};
And the erorrs i get:
this = new Cat(this->name,this->age,this->weight); : "assignment to 'this' (anachronism)" and the entity error
"no operator matches these operands operand types are: Cat = Dog *"
In general, you cannot assign an object to one of a different class - that's what static type system is about. To "change" the dynamic type of a polymorphic object the client code can create another one like this:
Animal* animal = new Dog{}; // actually you should use smart pointers
if (want_to_change) {
delete animal; // prevents a memory leak; smart pointers perform it automatically
animal = new Cat{};
}
If you would like actual animal type choice to happen during initialization, consider using a factory:
class Factory {
public:
// may be static if uses no state, than you can just write a free function
Animal* produce(/* some parameters */) const;
};
Animal* Factory::produce(/* some parameters */) const {
if (should_be_cat(/* depending on the parameters */)) {
return new Cat{};
} else {
return new Dog{};
}
}

How do I create a sub class in C++

I am new to C++. I am trying to create a subclass of a base class, but it does not seem to work.
I have the base class Person and a sub class Student, and the Person class is complete. Now I want to create a sub class Student. I have the following code:
namespace Uni
{
class Student : public Person
{
private:
int enrollNumber_;
string majorCourse_;
int averageGrade_;
Student(const Student &);
public:
Student(int enrolled_number, string major_course, int average_grade);
~Student();
const int getenrollNumber() const { return enrollNumber_; }
const int getAverageGrade() const { return averageGrade_; }
const string getMajorCourse() const { return majorCourse_;
};
}
The problem is that the IDE says that on the line class Student : Person that Person [is] not a class or struct name.
How can I create the subclass Student?

c++ inheritance/multi-inheritance ambiguous call

Beginner C++ question:
I have a class Person that has protected variables FirstName and LastName:
class Person
{
protected:
int Id;
std::string FirstName;
std::string LastName;
public:
Person();
Person(std::string firstName, std::string lastName);
~Person();
std::string GetPersonInfo() const;
std::string GetFirstName() const;
std::string GetLastName() const;
};
inline std::string Person::GetPersonInfo() const {
return FirstName + " " + LastName;
}
inline std::string Person::GetFirstName() const {
return FirstName;
}
inline std::string Person::GetLastName() const {
return LastName;
}
I have a Teacher class that inherits from Person (and an Adult Class)
class Teacher :
public Person, public Adult
{
private:
int ClassroomID;
public:
Teacher() = default;
~Teacher() = default;
Teacher(std::string firstName, std::string lastName, std::string emailAddress, std::string phoneNumber,
std::vector<Address> teacherAddress, int classroomID);
};
In my main() I have the following:
vector<Teacher> teachers = TeacherRepository.RetrieveTeachers();
for (Teacher teacher : teachers) {
cout << teacher.GetFirstName(); }
When I start to type "teacher." I see "GetFirstName" appear as an option, however; it is throws a compiler error that "Teacher::GetFirstName is ambiguous"
What have I done wrong?
EDIT: Definition for Adult
class Adult :
public Person
{
protected:
std::string Email;
std::string PhoneNumber;
std::vector<Address> address;
public:
Adult() = default;
~Adult() = default;
Adult(std::string emailAddress, std::string phoneNumber, std::vector<Address> address);
Adult(std::string emailAddress, std::string phoneNumber);
};
You have incorrect hierarchy. Teacher inherit from Person and Adult at the same time and Adult inherit Person too. What do you want compiler call when you write Teacher::GetFirstName? Maybe Person::GetFirstName or Adult::Person::GetFirstName. Moreover you will have two exemplars of Person's variables.
Decisions:
virtual inheritance:
class Adult : virtual public Person {...};
class Teacher : virtual public Person, public Adult {...};
more here
teacher's basic class must be Adult only:
class Teacher : public Adult {...};
As bad option: you can to indicate explicit which certainly method you want to call:
`Teacher t = ...;
t.Adult::GetFirstName();`
Bonus: Don't pass arguments by value, in your case will be better pass arguments as constant reference.
`Person(const std::string& firstName, const std::string& lastName);`
Instead
`Person(std::string firstName, std::string lastName);`
There are multiple issues in your previous design. The direct issue that causes the compilation error is that your Teacher is inherited from both Adult and Person class. Since Adult is also a subclass of Person, both of those classes have GetLastName() method, so the compiler cannot tell which one to call.
More importantly, you should manage your hierarchy in a more correct and clear way. Semantically, an adult should also be a person and a teacher is supposed to be an adult. So why not just inherit Teacher from Adult only and make your hierarchy a linear list in your design?
Moreover, if you want complete information for your base classes, you should initialize your base class objects first in your derived class object. Use C++ initialization list to complete this.
class Person
{
protected:
int Id;
std::string FirstName;
std::string LastName;
public:
Person() = default;
Person(std::string firstName, std::string lastName) : FirstName(firstName), LastName(lastName) {}
~Person() = default;
std::string GetPersonInfo() const;
std::string GetFirstName() const;
std::string GetLastName() const;
};
inline std::string Person::GetPersonInfo() const {
return FirstName + " " + LastName;
}
inline std::string Person::GetFirstName() const {
return FirstName;
}
inline std::string Person::GetLastName() const {
return LastName;
}
class Adult : public Person
{
protected:
std::string Email;
std::string PhoneNumber;
std::vector<std::string> Address;
public:
Adult() = default;
~Adult() = default;
Adult(std::string firstName, std::string lastName, std::string emailAddress, std::string phoneNumber, std::vector<std::string> address)
: Person(firstName, lastName), Email(emailAddress), PhoneNumber(phoneNumber), Address(address) {};
Adult(std::string firstName, std::string lastName, std::string emailAddress, std::string phoneNumber)
: Person(firstName, lastName), Email(emailAddress), PhoneNumber(phoneNumber) {};
};
class Teacher : public Adult
{
private:
int ClassroomID;
public:
Teacher() = default;
~Teacher() = default;
Teacher(std::string firstName, std::string lastName, std::string emailAddress, std::string phoneNumber,
std::vector<std::string> teacherAddress, int classroomID)
: Adult(firstName, lastName, emailAddress, phoneNumber, teacherAddress), ClassroomID(classroomID) {}
};
int main(void) {
Teacher teacher("ExampleFirstName", "ExampleLastName", "example#email.com", "100-1001000", {"example address"}, 1);
cout << teacher.GetLastName() << endl;
return 0;
}

Calling superclass function inheritance c++

In my C++ file, when I run it visual studio, my output is not what I thought it was be an I don't know where I messed up. Basically I have a Person and a Student class, and the student class inherits from the Person class, and when the student obj is created, it calls the Person class to initialize common variables.
class Person {
public:
Person() {
}
Person(string _name, int _age) {
name = _name;
age = _age;
}
void say_stuff() {
cout << "I am a person. " << name << age << endl;
}
private:
string name;
int age;
};
class Student : public Person {
public:
Student(string _name, int _age, int _id, string _school) {
Person(_name, _age);
id = _id;
school = _school;
}
private:
string name;
int age;
int id;
string school;
};
int main() {
Student s1("john", 20, 123, "AAAA");
s1.say_stuff();
system("pause");
return 0;
}
My output is I am a person. -858993460
Why is this?
The way you invoke the constructor of the super class is wrong. This is how you should do it:
Student(string _name, int _age, int _id, string _school) : Person(_name, _age) {
id = _id;
school = _school;
}
Note that, When you put Person(_name, _age); inside the body, it has no effect but to construct a temporary Person object. On the other hand, the correct way above references the "embedded" Person to be constructed with these parameters.
Your Student constructor's syntax is wrong, for constructing it's superclass. It should be:
Student(string _name, int _age, int _id, string _school)
: Person(_name, _age) {