I am confused as to why casting a derived class to a pointer of base class calls upon the derived class method when I haven't used the virtual keyword. Is this normal behavior? Doesn't the pointer hold a Person object in memory, therefore casting it to a Student should not have any affect to its contents?
class Person {
public:
Person()
{
cout << "Creating Person Class" << endl;
}
void about_me()
{
cout << "I am a person" << endl;
}
};
class Student : protected Person {
public:
Student()
{
cout << "Creating Student Class" << endl;
}
void about_me()
{
cout << " I am a student " << endl;
}
};
int main()
{
Person* pperson = new Person();
Student* pstudent = new Student();
pperson->about_me();
pstudent->about_me();
pperson-> about_me();
((Student*)pperson)-> about_me(); // this is the line where I do the cast
return 0;
}
The output of the code is the following
Creating Person Class
Creating Person Class
Creating Student Class
I am a person
I am a student
I am a person
I am a student
Your code "works" because neither of your about_me() methods access their respective this pointers for anything.
Technically speaking, what you are doing is undefined behavior, because pperson does not point at a valid Student object, but you are telling the compiler to treat it as if it were. So literally anything can happen.
In many common compiler implementations, a class method call like pperson->about_me() is actually calling more like about_me(pperson), where about_me() gets implemented as a standalone function with a this input parameter. So code you have shown might be implementation by the compiler more like this under the hood (not exactly, but you should get the idea):
struct Person
{
};
void Person_Person(Person *this)
{
cout << "Creating Person Class" << endl;
}
void Person_about_me(Person *this)
{
cout << "I am a person" << endl;
}
struct Student
{
};
void Student_Student(Student *this)
{
Person_Person(this);
cout << "Creating Student Class" << endl;
}
void Student_about_me(Student *this)
{
cout << " I am a student " << endl;
}
int main()
{
//Person* pperson = new Person();
byte *buf1 = new byte[sizeof(Person)];
Person* pperson = (Person*) buf1;
Person_Person(pperson);
//Student* pstudent = new Student();
byte *buf2 = new byte[sizeof(Student)];
Student* pstudent = (Student*) buf2;
Student_Student(pstudent);
//pperson->about_me();
Person_about_me(pperson);
//pstudent->about_me();
Student_about_me(pstudent);
//pperson-> about_me();
Person_about_me(pperson);
//((Student*)pperson)-> about_me();
Student_about_me((Student*)pperson);
return 0;
}
So, in the 4th call to about_me(), you are instructing the compiler to call Student::about_me() instead of letting it call Person::about_me() normally, with its this parameter set to a Person* pointer that is type-casted to Student*. Since this is not dereferenced by about_me(), the call is "successful" in that you see the "expected" output. It doesn't matter what this points to in this case, because it is not being used.
Now, try adding some data members to your classes, and then output those members in about_me(), and you will see very different, very unexpected/random results, due to the undefined behavior you are invoking. For example:
class Person
{
protected:
string m_name;
public:
Person(const string &name)
: m_name(name)
{
cout << "Creating Person Class" << endl;
}
void about_me()
{
cout << "I am a person, my name is " << m_name << endl;
}
};
class Student : protected Person
{
private:
int m_id;
string m_school;
public:
Student(const string &name, int id, const string &school)
: Person(name), m_id(id), m_school(school)
{
cout << "Creating Student Class" << endl;
}
void about_me()
{
cout << "I am a student, my name is " << m_name << ", my id is " << m_id << " at " << m_school << endl;
}
};
int main()
{
Person* pperson = new Person("John Doe");
Student* pstudent = new Student("Jane Doe", 12345, "Some School");
pperson->about_me(); // "I am a person, my name is John Doe"
pstudent->about_me(); // "I am a student, my name is Jane Doe, my id is 12345 at Some School"
pperson->about_me(); // "I am a person, my name is John Doe"
((Student*)pperson)->about_me(); // runtime error!
delete pstudent;
delete pperson;
return 0;
}
Live Demo
Related
So I am coding in c++ and am having a problem with inheritance and pointers. I am trying to access how many notches are in the gun slinger object (gun) but it is saying person has no such function. gun is a child class of person so I am a bit confused. here is the code
side note the error is p->getnotches() in main and it says (class "Person" has no member "getnotches")
int main()
{
Person *p;
Gunslinger gun("Doc Holiday", 5);
//PokerPlayer poker("Billy Jack");
//BadDude bad("Billy Kid", 4);
Person p1("James Cagney");
cout << "Persons name is: " << p1.getName() << endl;
Person p2 = p1;
Person p3, p4;
p4 = p3 = p1;
p = new Person("Jack Benny");
cout << "Person name is: " << p->getName() << endl;
cout << "Person 1 name is: " << p1.getName() << endl;
cout << "Person 2 name is: " << p2.getName() << endl;
cout << "Person 3 name is: " << p3.getName() << endl;
cout << "Person 4 name is: " << p4.getName() << endl;
delete p;
p = &gun;
cout << "Gunslinger name: " << p->getName() << " and number of notched on gun is: " <<
p->getnotches() << endl;
return 0;
}
#ifndef PERSON_H
#define PERSON_H
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Person
{
private:
char *name;
public:
Person();
Person(string name);
virtual string getName();
virtual ~Person();
};
#endif
// no default constructor
Person::Person()
{
}
// this function assigns a name to person object
Person::Person(string n)
{
name = new char[n.length() + 1];
strcpy(name, n.c_str());
}
// this functions grabs the name of the person object
string Person::getName()
{
return name;
}
Person::~Person()
{
delete[] name;
}
#ifndef GUNSLINGER_H
#define GUNSLINGER_H
#pragma once
#include "Person.hpp"
#include <cstring>
#include <string>
class Gunslinger : public Person
{
private:
char *name;
int notches;
double drawTime;
public:
Gunslinger(string n, int cylinders);
int getnotches();
virtual string getName();
~Gunslinger();
};
#endif
#include "Gunslinger.hpp"
Gunslinger::Gunslinger(string n, int clinders)
{
name = new char[n.length() + 1];
strcpy(name, n.c_str());
notches = clinders;
}
string Gunslinger::getName()
{
return name;
}
int Gunslinger::getnotches()
{
return notches;
}
Gunslinger::~Gunslinger()
{
delete[] name;
}
In this expression
p->getnotches()
the static type of the pointer p is Person * due to this declaration
Person *p;
So the compiler searches in the class Person the function getnotches and does not find it because the class does not have such a function.
class Person
{
private:
char *name;
public:
Person();
Person(string name);
virtual string getName();
virtual ~Person();
};
You need to use dynamic_cast (if the compiler supports it) or reinterpret_cast with the pointer.
Although you have a Gunslinger object, you're treating it as a Person object and the Person class does not have a getnotches method. I'm not sure why you want to have a Person* to a Gunslinger object. Just use the Gunslinger 'gun' instead of the Person* 'p'.
cout << "Gunslinger name: " << gun.getName() << " and number of notched on gun is: " <<
gun.getnotches() << endl;
I am currently learning C++, coming from C#/Java background, using visual studio 2017.
I have a question in regards to creating objects on heap and referencing them properly down the road. So far I came across multiple tutorials and ways of doing things. Some recommend using smart pointers as much as possible, others swear its the devils tool.
My current main looks like this:
//Main
Person *makePerson()
{
string name;
int age;
cout << "Input name: ";
cin >> name;
cout << "Input age: ";
cin >> age;
return new Person(name, age);
}
Child *makeChild(Person &parent)
{
return new Child(*makePerson(), &parent);;
}
int main()
{
cout << "---Input parent data---" << endl;
Person *person = makePerson();
cout << "printing: " << *person << endl;
cout << "---Input child data---" << endl;
Child *child = makeChild(*person);
cout << "printing: " << *child << endl;
cout << "---end of main---" << endl;
delete person;
delete child;
return 0;
}
A function handles input of personal data and returns a pointer to new Person object. Then I have a function that handles creation of child object by taking a parent reference and asking makePerson for remaining data.
Can this be considered good C++? How can I make it better? I would really appreciate some code examples.
As some have already suggested, I could replace raw pointers with either shared_ptr<Person> person (heavy) or unique_ptr<Person> (better than shared).
This is code for Person and child classes. Note that Child has a raw pointer of type Person *parent.
//header
class Person
{
protected:
std::string name;
int age;
public:
Person();
Person(const Person& other);
Person(std::string inName, int inAge);
~Person();
virtual void print() const;
std::string getName() const;
int getAge() const;
Person &operator=(const Person &other);
//overload print functionality, act as if it was toString
friend std::ostream &operator<<(std::ostream &out, const Person &p);
};
//cpp
Person::Person() : name(""), age(0) {
std::cout << "Person empty constructor" << std::endl;
}
Person::Person(std::string inName, int inAge) : name(inName), age(inAge) {
std::cout << "Person (" << name << ") default constructor" << std::endl;
}
Person::Person(const Person & other) : name(other.name), age(other.age) {
std::cout << "Person (" << name << ") copy constructor" << std::endl;
}
Person::~Person() {
std::cout << "Person (" << name << ") destructor" << std::endl;
}
void Person::print() const {
std::cout << name << ", " << age << std::endl;
}
std::string Person::getName() const
{
return name;
}
int Person::getAge() const
{
return age;
}
Person & Person::operator=(const Person & other) {
std::cout << "Person (" << other.name << ") assignment constructor" << std::endl;
name = other.name;
age = other.age;
return *this;
}
std::ostream &operator<<(std::ostream &out, const Person &p) {
return out << p.name << ", " << p.age;
}
A child is a person and it makes sense for a child to have knowledge of who childs parent is. However, am uncertain how to handle this "knowledge". Here is code I am using for child class:
//Header
class Child : public Person
{
private:
const Person *parent;
public:
Child();
Child(std::string name, int age);
Child(std::string name, int age, const Person *parent);
Child(const Child &child, const Person *parent);
Child(const Person &person);
~Child();
Child &operator=(const Child &other);
void print() const;
friend std::ostream &operator<<(std::ostream &out, const Child &c);
};
//cpp
Child::Child() {
std::cout << "Child empty constructor" << std::endl;
}
Child::Child(std::string name, int age) : Person(name, age), parent(nullptr) {
std::cout << "Orphan (" << name << ") constructor" << std::endl;
}
Child::Child(std::string name, int age, const Person *parent) :
Person(name, age), parent(parent) {
std::cout << "Child (" << name << ") default constructor" << std::endl;
}
Child::Child(const Child &child, const Person *parent) :
Person(child.name, child.age), parent(parent) {
std::cout << "Child (" << child.name << ") copy constructor" << std::endl;
}
Child::Child(const Person &person) : Person(person), parent(nullptr) {
std::cout << "Child from person (" << name << ") constructor" << std::endl;
}
Child::~Child() {
std::cout << "Child (" << name << ") destructor" << std::endl;
}
Child & Child::operator=(const Child & other) {
name = other.name;
age = other.age;
parent = other.parent;
std::cout << "Child (" << name << ") assignment constructor" << std::endl;
return *this;
}
void Child::print() const {
if(parent)
std::cout << *this << " is child of " << *parent << std::endl;
else
std::cout << *this << " is orphan" << std::endl;
}
std::ostream &operator<<(std::ostream &out, const Child &c) {
return out << c.name << ", " << c.age << " is " <<
(c.parent ? ("child of " + c.parent->getName() + ", " + std::to_string(c.parent->getAge())) : "orphan");
}
This is the output I get:
I suppose my question still remains, could anyone give me an example of how it should look like to be considered good C++?
#user4581301 if you take a look at the updated main, do you mean I should return a std::unique_ptrinstead of a * (raw pointer)? In which case my function will look like this:
std::unique_ptr<Person> makePerson2()
{
string name;
int age;
cout << "Input name: ";
cin >> name;
cout << "Input age: ";
cin >> age;
return std::unique_ptr<Person>(new Person(name, age));
}
And variable declaration as:
std::unique_ptr<Person> upParent = makePerson2();
cout << "printing: " << *upParent << endl;
Would this be considered "better" C++ than what I have so far?
I think that one of the most important thing to know about C++ especially if you come from Java/C# background is:
Objects are value-types by default, not reference type!
you entire code could have been written as simply:
int main()
{
//this work
Person person("John Doe", 22);
//this work
Child child("Johnny Doe", 2, person);
cout << "---end of main---" << endl;
return 0;
}
See how the code turned into nothing? you don't have to worry about allocations, deleting unused object etc. because objects are not reference type to begin with!
My personal hierarchy of rules go:
use objects as value types as much as possible. pass them by reference to avoid copies. make sure a valid move constructor is implemented for relevant classes. objects as value types + references should be the default way of programming C++.
if you can't use a reference, because you want to specify a missing object, use C-pointer. by any way, strict the C-pointers to minimum and never let them manage anything. C-pointers are basically "viewers" to something and they can view "nothing" (or null). always think if you can replace C-pointer by C++ reference. if you can do it, do it.
use std::unique_ptr if for some reason you do need dynamic memory allocation, such as dynamic polymorphism. keep in mind that C++ works mostly on templates as static polymorphism, instead of the Java style inherit+override technique.
use std::shared_ptr rarely,and only when you're sure there are many owners, like an object which is referenced on different threads. std::shared_ptr should be used in extreme cases. always copy the shared pointer. never pass the shared pointer by reference.
Anyway, new , new[] , delete and delete[] are pretty much deprecated. only library writers should use them in very extreme cases. std::make_ should be the only way allocating object on the heap, alongside the STL containers such as std::vector and std::list.
So I have these classes:
In main I wrote an array of pointers:
student *arry[10];
How can I make each cell point to an object of a different class?
For example :
I want the cell 0 , 2 , 4
point to an object of class medstudent
using ( new statement )
thank you
here is class medStudent
#include<iostream>
#include"student.cpp"
using namespace std;
class medStudent:public student {
public :int clinicH;
public:
medStudent(int ch, string n , int i ):student(n,i){
setClinicH(ch);
cout << "New Medecine Student" << endl;
}
~medStudent(){
cout << "Medecine Student Deleted" << endl;
}
medStudent(medStudent & ms):student(ms){
cout << "New Copy Medecined Student" << endl;
}
medstudent(){
}
void setClinicH(int ch){
clinicH = ch;
}
int getClinicH()const{
return clinicH;
}
void print()const{
student::print();
cout << "Clinical Hours: " << getClinicH() << endl;
}
};
Here is class student:
#include <iostream>
//#include"medstudent.cpp"
using namespace std;
class student//:public medstudent
{
public :
static int numberOfSaeeds;
const int id;
string name;
public:
~student(){
cout << "Delete Student: " << getName() << " " << endl ;
}
student(string n, int i):id(i){
setName(n);
cout << "Student with args" << endl ;
}
void setName(string n){
name = n;
}
string getName()const{
return name;
}
void print()const{
cout << "My name is: " << name << endl;
cout << "My ID is: " << id << endl;
}
void setNOS(int nos){
numberOfSaeeds = nos;
}
int getNOS(){
return numberOfSaeeds;
}
void printAddress()const{
cout << "My address is " << this << endl;
}
student * getAddress(){
return this;
}
student(student & sc):id(sc.id){
name = sc.name;
setName(sc.getName());
cout << "New Object using the copy constructor" << endl;
}
};
Here is main code:
#include<iostream>
using namespace std;
#include"time.cpp"
#include "student.cpp"
//#include"medstudent.cpp"
int main(){
student a1("asa" , 2);
student * a[10];
a[3]= new student("jj", 22 );
a[0] = new medStudent();
}
Since you explicitly declare a medStudent constructor the compiler will not create a default constructor for your class. And when you do new medStudent(); you are (explicitly) trying to invoke the default constructor, which doesn't exist.
That will give you a build error, one that should have been very easy to diagnose if you read it and most importantly shown it to us (when asking questions about build errors, always include the complete and unedited error output, including any informational output, in the body of the question, together with the code causing the error).
The solution? Call the existing parameterized constructor. E.g. new medStudent("foo", 123).
By the way, if you want inheritance to work okay, and the base-class destructor to be called when deleting an object of a child-class, then you need to make the destructors virtual.
My code consists of a class animal and 2 sub classes inheriting animal charactistics - amphibian, and fish. The code compiles but the oorder of the deconstructors kills them from bottom to top but i want them to be killed from top to bottom as the display function order suggests-
Here is my code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Animal{
public:
Animal(string name, string diet, bool queue)
: name_species(name), regime(diet), queue(queue){
if(name == ""){cout << "Error, name can't be empty!" << endl; }
else { cout << "A new animal" << endl; }}
virtual ~Animal(){ cout << "End of animal" << endl; }
virtual void display()const;
protected:
string name_species;
string regime;
bool queue;
};
void Animal::display() const{
cout << "I present the species " << name_species << endl;
}
class Fish : public Animal {
public:
Fish(string name, unsigned int scales, string diet = "Carnivore", bool queue = true)
: Animal(name, diet, queue), scales(scales){ cout << "Add a fish" << endl; }
~Fish(){ cout << "Fish species " << name_species << " is turned off. " << endl; }
void display() const override;
private:
unsigned int scales;
};
void Fish::display() const {
Animal::display();
if(queue == true){cout << "I have a tail "; }
else { cout << "I don't have a tail "; } cout << "and i follow " << regime << endl;
cout << "I have " << scales << " scales" << endl;
}
class Amphibian : public Animal {
public: Amphibian(string name, string diet, unsigned int claws = 4, bool queue = false)
: Animal(name, diet, queue), claws(claws){ cout << "Add an amphibian" << endl; }
void affiche() const;
~Amphibian(){ cout << "Amphibian species " << name_species << " is turned off. " << endl; }
private:
unsigned int claws;
};
void Amphibian::display() const {
Animal::display();
if(queue == true){ cout << "I have a tail "; }
else { cout << "I don't have a tail " ; } cout << "and i follow " << regime << endl;
cout << "I have " << scales << " scales " << endl;
}
class Species {
public:
virtual void display()const = 0;
virtual ~Species();
private:
vector<Animal*> animals;
};
void Species::display()const{
for(size_t i(0); i < animals.size(); ++i){
animals[i]->display(); }
}
Species::~Species(){
for(size_t i(0); i < animals.size(); ++i){
delete animals[i];
}
}
int main()
{
Amphibian kermit("lizard", "insects");
Fish nemo("dolphin", 6);
Fish sala("salamander", 4);
Animal* animal(&kermit);
animal->display();
animal = &nemo;
animal->display();
return 0;
}
Nothing inherits from class Species so conversion not possible using assignment technique.
There are no methods in Species to convert an Animal into a Species, so no luck there. Compiler running out of ideas on how to convert Animal to Species.
Looks like Species is a container of Animals. You will need to supply a method that adds an Animal to the container inside Species or make the container public.
You want something like this:
Species s;
s.animals.push_back(&kermit);
The following code suggests that you see Species as something more general than Animal:
Species* animal(&kermit);
animal->display();
animal = &nemo;
In this case you shall make sure the inheritance is declared, by changing the class definition to :
class Animal : Species {
...
};
Some remarks nevertheless:
as all species have a name, whether vegetal or animal, you could consider to move member Animal::name_species to class Species;
the fact that the name of the class Species is in plural, that you have a vector as member, and that every element of this member is displayed or deleted when the corresponding operation is called for the member tends however to suggest that Species is an aggregation of Animals. It is then not clear how you add elements to your vector.
Rather than using raw pointers in the aggregation, you could use shared_ptr instead. You then don't need to delete the aniamls yourself.
I'm trying to figure out basic inheritance. But I seem unable to get the enum to take. It just responds with a smiley face and nothing.
I honestly don't know what I'm doing wrong so I'm afraid I'll have to just throw all my code at you.
#include <iostream>
#include <string>
using namespace std;
enum Discipline {
COMPUTER_SCIENCE, Computer_SCIENCE_AND_INNOVATION
};
const string DISCIPLINE_STRINGS[2] = { "Computer Science",
"Computer Science and Innovation",
};
class Person {
public:
Person(){cout << "Person object created using the default Person constructor\n";};
Person(const string& name, Discipline type){pName = name; pType = type;};
~Person(){cout << "Person object destroyed\n";};
string pName;
string pType;
};
class Faculty: public Person {
public:
Faculty();
Faculty(const string& name, Discipline type) {pName = name; pType = type; cout << "Faculty object created using the alternative Faculty constructor\n";};
~Faculty(){cout << "Faculty object destroyed!";};
string getName(){return pName;};
string getDepartment(){return pType;};
};
class Student: public Person {
public:
Student();
Student(const string& name, Discipline type) {pName = name; pType = type; cout << "Student object created using the alternative Student constructor\n";};
~Student(){cout << "Student object destroyed!";};
string getMajor(){return pType;};
string getName(){return pName;};
};
int main()
{
Faculty prof("Name1", COMPUTER_SCIENCE);
Student stu(" Name2", Computer_SCIENCE_AND_INNOVATION);
cout << endl << "I, " << stu.getName() << ", am majoring in " << stu.getMajor() << "." << endl;
cout << "I am taking CSI 240 with Prof. " << prof.getName() << ", who teaches "
<< prof.getDepartment() << " courses." << endl << endl;
system ("pause");
return 0;
}
You are printing out the enum instead of the actual string.
You should use the enum to index into DISCIPLINE_STRINGS.
When you set the type string do this:
pType = DISCIPLINE_STRINGS[type]