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.
Related
I'm still learning C++ so go easy on me.
Is there a way I can pass an object to a method without specifying an object? I'm probably butchering the terms so ill show code.
class Student
{private:
std::string Name;
float GPA;
char Sex;
int Absentee;
int *Data ;
public:
std::string GetName();
float GetGPA();
char GetSex();
int GetAbsentee();
void SetData(int);
int GetData();
int *GetDataAddr();
//Methods
void DisplayStudent(Student);
void Student::DisplayStudent(Student Stud)
{
std::cout << "___________________________________" << std::endl;
std::cout << "Name :" << Stud.GetName() << std::endl;
std::cout << "GPA :" << Stud.GetGPA() << std::endl;
std::cout << "Sex :" << Stud.GetSex() << std::endl;
std::cout << "Absentee :" << Stud.GetAbsentee() << std::endl;
std::cout << "Data :" << Stud.GetData() << std::endl;
std::cout << "Data Add :" << Stud.GetDataAddr() << std::endl;
std::cout << "___________________________________" << std::endl;
}
int main() {
Student Spike("Spike", 3.9f, 'M', 43,55);
* Compiles fine: Spike.DisplayStudent(Spike);
* DOSNT Compile: Student DisplayStudent(Spike);
* C++ a nonstatic member reference must be relative to a specific object*
return 0;
}
So the question I have is at least with this method, why do I need to specify or rather, what is the purpose of "Spike" in "Spike.DisplayStudent(.....)"? Student::Display(.....) makes far more sense to me.
If your Student::DisplayStudent is designed to display information for the student who is represented by that class instance, you don't need to pass Student Stud at all, just use member variables.
If however it is designed to display info for ANY student - you can make it a static member, or a free-standing function.
If you want the member function DisplayStudent to display the information for the very instance of Student on which the function is called, you do not need to pass a Student as an argument.
class Student {
public:
// The getter methods should be `const`, since calling them does not change
// the `Student`:
const std::string& GetName() const; // return a `const&` to avoid unecessary copying
void DisplayStudent() const; // No `Student` argument, like in `GetName()`
// ...
};
void Student::DisplayStudent() const {
std::cout << "___________________________________\n"
"Name :" << GetName() << "\n"
"GPA :" << GetGPA() << "\n"
"Sex :" << GetSex() << "\n"
"Absentee :" << GetAbsentee() << "\n"
"Data :" << GetData() << "\n"
"Data Add :" << GetDataAddr() << "\n"
"___________________________________\n";
}
You also do not need to call getter methods in DisplayStudent() since you have access to the private member variables and do not need to do any calculations before returning the result.
Usage example (if the appropriate constructor exists as you've indicated):
int main() {
Student Spike("Spike", 3.9f, 'M', 43,55);
Spike.DisplayStudent(); // no instance passed as an argument
}
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
So far I have defined a simple class...
class person {
public:
string firstname;
string lastname;
string age;
string pstcode;
};
...then added some members and values to an object named "bill"...
int main() {
person bill;
bill.firstname = "Bill";
bill.lastname = "Smith";
bill.age = "24";
bill.pstcode = "OX29 8DJ";
}
But how would you simply output all those values? Would you use a for loop to iterate over each member?
I typically override operator <<, so that my objects are as easy to print as any built-in object.
Here is one way to override operator <<:
std::ostream& operator<<(std::ostream& os, const person& p)
{
return os << "("
<< p.lastname << ", "
<< p.firstname << ": "
<< p.age << ", "
<< p.pstcode
<< ")";
}
And then to use it:
std::cout << "Meet my friend, " << bill << "\n";
Here is a complete program using this technique:
#include <iostream>
#include <string>
class person {
public:
std::string firstname;
std::string lastname;
std::string age;
std::string pstcode;
friend std::ostream& operator<<(std::ostream& os, const person& p)
{
return os << "("
<< p.lastname << ", "
<< p.firstname << ": "
<< p.age << ", "
<< p.pstcode
<< ")";
}
};
int main() {
person bill;
bill.firstname = "Bill";
bill.lastname = "Smith";
bill.age = "24";
bill.pstcode = "OX29 8DJ";
std::cout << "Meet my friend, " << bill << "\n";
}
Simplistically, you output each element using an ostream:
class Person
{
public:
void Print_As_CSV(std::ostream& output)
{
output << firstname << ",";
output << lastname << ",";
output << age << ",";
output << pstcode << "\n";
}
string firstname;
string lastname;
string age;
string pstcode;
};
There may be different methods of printing, which is why I didn't overload operator <<. For example, one data member per line would be another popular scenario.
Edit 1: Why not looping?
The class has separate fields, which is why you can't iterate over the members.
If you want to iterator or loop over the members, you either have to have an iterator for your class or use a container, such as std::vector, that provides iteration.
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.
I'm lost with this code, what do I need to do for
Couple *pcouple1 = new Couple(mary, *pjohn);
Couple couple2(*pjane, mark);
to work? I get a red squigle between Couple(mary and couple2(*pjane with the folliwing info:
*error C2664: 'Couple::Couple(const Couple &)' : cannot convert argument 1 from 'Person' to 'const char *'
*IntelliSense: no instance of constructor "Couple::Couple" matches the argument list
argument types are: (Person, Person)
Code is:
Class Couple & Person
class Person {
char* name;
public:
friend class Couple;
friend ostream& operator<<(ostream& str, const Person& os)
{
str << os.name;
return str;
};
Person(const char* n)
{
name = (char*)n;
cout << "char constructor " << name << endl;
};
Person(const Person& os)
{
name = os.name;
cout << "Person constructor " << name << endl;
};
Person& operator=(const Person& os)
{
this->name = os.name;
return *this;
};
~Person()
{
free(name);
cout << "Class Person Destructor" << endl;
};
char* getName(){
return name;
};
};
class Couple {
Person *wife, *husband;
public:
friend ostream& operator<<(ostream& str, const Couple& p)
{
str << "He " << *p.husband << " She " << *p.wife;
};
Couple::Couple(const char* m, const char* z)
{
husband = new Person(m);
wife = new Person(z);
cout << "Couple1: " << endl;
};
Couple::Couple(const Couple& other)
{
husband = new Person(*other.husband);
wife = new Person(*other.wife);
cout << "Couple2: " << endl;
}
Couple& operator=(const Couple& other)
{
this->husband = new Person(*other.husband);
this->wife = new Person(*other.wife);
return *this;
};
~Couple()
{
free(husband->name);
free(husband);
free(wife->name);
free(wife);
cout << "Class Couple Destructor" << endl;
};
};
Main function:
int main(void) {
Person *pjohn = new Person("John"),
*pjane = new Person("Jane");
Person mary("Mary"), mark("Mark");
Couple *pcouple1 = new Couple(mary, *pjohn);
Couple couple2(*pjane, mark);
delete pjohn;
delete pjane;
cout << *pcouple1 << endl;
cout << couple2 << endl;
couple2 = *pcouple1;
delete pcouple1;
cout << couple2 << endl;
return 0;
}
Could somebody recommend me a good source/site/book/exercise to understand better function pointers similar to this exercise, thanks in advance I'm still learning.
Obviously your Couple constructor expects two char* arguments, but you are trying to construct Couple object with two Person objects. Either introduce constructor, that will accept Person as argument, or convert given Person instances to char*.