C++ Destructor: cannot access private member declared in class - c++

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.

Related

How to create member objects by class constructor?

In my example I created a class Person which has a member object: struct data. This object contains data about person. Each time a Person-Object is created, also the data-object shall be initialized.
Observation: When adding object initializer to code (1) at class constructor I get failure message:
incomplete type is not allowedC/C++(70)
class person {
public:
struct data;
person() { /* (1) */
person::data myPersonData;
}
private:
};
So here is how I practice it now:
No struct object initialization myPersonData in class person constructor (class_person.hpp)
Create person object in main.cpp
Create myPersonData in main.cpp (I would like to save this
initialization and put it to class contructor)
The whole example looks like this:
// class_person.hpp
#include <iostream>
#include <string>
class person {
public:
struct data;
private:
};
struct person::data {
std::string name = "John";
int age = 42;
int weight = 75;
};
_
// main.cpp
#include <iostream>
#include "class_person.hpp"
void outputPersonData(person::data myPerson) {
std::cout << myPerson.name << "\n";
std::cout << myPerson.age << "years\n";
std::cout << myPerson.weight << "kg\n";
};
int main() {
person John;
person::data myPersonData;
outputPersonData(myPersonData);
getchar();
return 0;
}
You should put the definition of data inside the definition of person if you want a member of it. Something like this.
#include <string>
#include <iostream>
class person {
public:
struct data {
std::string name = "John";
int age = 42;
int weight = 75;
};
// This is just the definition the the class. We need the class
// to have a definition if we want to use a value of it in person
person() = default;
// Default constructors, give us a default constructed personData
// that uses the default values from the definition
person(data d) : personData(std::move(d)) {}
// Constructor that takes a personData object and uses it to
// initialize our member. std::move is to avoid uneccesary copying
void outputPersonData() const {
std::cout << personData.name << "\n";
std::cout << personData.age << "years\n";
std::cout << personData.weight << "kg\n";
}
data personData;
// This is the actual data member, now person contains
// a member named personData of type person::data
};
int main() {
person john;
person mike({"Mike", 47, 82});
john.outputPersonData();
mike.outputPersonData();
}

what is the difference between two types of defining constructors? first one using ":" and the second one defining it in curly braces like functions

I have problem in defining constructors for classes and the way I have defined them earlier, doesn't work here properly.
This is for edx c++ intermediate course. when I try to define a class that inherits from some other class, referring to my base class with a constructor doesn't work properly and so my new class won't be defined correctly. What's the difference between the first use of constructors in the first code (using ":" notation that works properly), and the second one in second code (defining like function, that I have used before properly and doesn't work here)?
I have a base class called Person and a Student class that inherits from the base class. When I try to initialize a Student object that calls one of Person class constructors, it gives wrong answers. I think this is because of the way I define constructors. I make them like functions and initialize variables in curl braces. I have used this method earlier and it was working properly, but it doesn't work here. But the standard method using ":" before curly braces works properly here. I want to know what is the difference between these two?
Person.h:
#pragma once
#include <string>
class Person
{
private:
std::string name;
protected:
int age;
public:
Person();
Person(const std::string & name, int age);
void displayNameAge() const;
};
Person.cpp:
#include "pch.h"
#include "Person.h"
#include <iostream>
//Person::Person()
// : name("[unknown name]"), age(0)
//{
// std::cout << "Hello from Person::Person()" << std::endl;
//}
Person::Person()
{
name = "[unknown name]";
age = 0;
std::cout << "Hello from Person::Person()" << std::endl;
}
Person::Person(const std::string & name, int age)
{
this->name = name;
this->age = age;
std::cout << "Hello from Person::Person(string, int)" << std::endl;
}
//Person::Person(const std::string & name, int age)
// : name(name), age(age)
//{
// std::cout << "Hello from Person::Person(string, int)" << std::endl;
//}
void Person::displayNameAge() const
{
std::cout << name << ", " << age << std::endl;
}
Student.h:
#pragma once
#include "Person.h"
class Student : public Person
{
private:
std::string course;
public:
Student();
Student(const std::string & name, int age, const std::string & course);
void displayCourse() const;
};
Student.cpp:
#include "pch.h"
#include "Student.h"
#include <iostream>
Student::Student()
{
course = "[unassigned course]";
std::cout << "Hello from Student::Student()" << std::endl;
}
// first method: the right one
//Student::Student(const std::string & name, int age, const std::string & course)
// : Person(name, age), course(course)
//{
// std::cout << "Hello from Student::Student(string, int, string)" << std::endl;
//}
// second method: the wrong one
Student::Student(const std::string & name, int age, const std::string & course)
{
Person(name, age);
this->course = course;
std::cout << "Hello from Student::Student(string, int, string)" << std::endl;
}
void Student::displayCourse() const
{
std::cout << course << std::endl;
}
Main.cpp:
#include "pch.h"
#include "Student.h"
int main()
{
// Create a Student object using the no-argument constructor.
Student Student1;
Student1.displayNameAge();
Student1.displayCourse();
// Create a Student object using the parameterized constructor.
Student Student2("Jane Smith", 25, "Physics");
Student2.displayNameAge();
Student2.displayCourse();
return 0;
}
expected result:
Hello from Person::Person()
Hello from Student::Student()
[unknown name], 0
[unassigned course]
Hello from Person::Person(string, int)
Hello from Student::Student(string, int, string)
Jane Smith, 25
Physics
actual result:
Hello from Person::Person()
Hello from Student::Student()
[unknown name], 0
[unassigned course]
Hello from Person::Person()
Hello from Person::Person(string, int)
Hello from Student::Student(string, int, string)
[unknown name], 0
Physics
Initializer List
What you are missing is the initializer list.
Type::Type(Parameters)
: member1(init) // This is the initializer list
, member2(init)
{
Your code
}
If you do not explicitly provide one then the compiler will do it for you using the default constructor for the parent class then a call to the default constructor for each member.
So lets look at your class.
Student::Student(const std::string & name, int age, const std::string & course)
{
// Code
}
That is what you wrote. But this is what the compiler implemented.
Student::Student(const std::string & name, int age, const std::string & course)
: Person()
, course()
{
// Code
}
So because you did not do anything the compiler added it calls to the Person default constructor and course (std::string) default constructor.
Now the problem comes if your base class does not have a default constructor. Then the compiler can not add the appropriate calls and will generate a compiler error.
But there is also the issue that the way you are writing this is very inefficient as you are basically initializing all the members twice. Your calling the default constructor then in the Code section you are re-initializing the members with another value.
Student::Student(const std::string & name, int age, const std::string & course)
: Person(name, age)
, course() // Initialize it to empty here.
{
course = "[unassigned course]"; // Re-Initialize it with info.
}
You could simply do it once:
Student::Student()
: Person() // Note person must have a default constructor for this to work.
, course("[unassigned course]")
{}
Temporary Object
Student::Student(const std::string & name, int age, const std::string & course)
{
Person(name, age);
// CODE.
}
This is not doing what you think.
Lets add the initializer list.
Student::Student(const std::string & name, int age, const std::string & course)
: Person()
, course()
{
Person(name, age); // This is creating a person object localy.
// the person object has no name so is a
// temporary variable and goes out of scope
// at the ';'. So it is created and destroyed
// in place before other code is executed.
//
// This does not help initialize the class.
// It is creating a completely different
// and independent object.
// CODE.
}
You can see the execution here:
Hello from Person::Person() // Init parent
Hello from Person::Person(string, int) // Init the temp object.
// If you put a print in the destructor
// You will also see that executed
// Before the Student
// This is also why the object has "unknown name" and 0 age.
Hello from Student::Student(string, int, string) // Now we init the student
[unknown name], 0
Physics
Advice
Is there a valid scenario where you want initialized versions of the object? Personally I think not (if there are then ignore this) so get rid of the default constructors of Person and Student. Then you can not create uninitialized Students or `People.

Subclass member variable as argument for main-class constructor in initialization list = crash?

Very new to programming so forgive me for maybe not seeing something obvious.
Basically I just want to know why all three codes do compile, but the resulting executables CRASH in cases TWO and THREE
(I marked the differences with comments)
ONE - compiles
#include <iostream>
#include <string>
using namespace std;
string testrace = "dog"; //defining it only globally
class Attributes {
public:
Attributes (string race){
if (race == "human"){
intelligence = 10;}
else if (race == "dog"){
intelligence = 4;}
}
int intelligence;
};
class Dalmatian: public Attributes{
public:
// but NOT locally
Dalmatian (): Attributes{testrace} { //using it as an argument
cout << "do i even arrive here" << endl;
}
};
int main() {
Dalmatian bob;
cout << bob.intelligence << endl;
}
TWO - crashes
#include <iostream>
#include <string>
using namespace std;
class Attributes {
public:
Attributes (string race){
if (race == "human"){
intelligence = 10;}
else if (race == "dog"){
intelligence = 4;}
}
int intelligence;
};
class Dalmatian: public Attributes{
public:
string testrace = "dog"; //only defining it locally
Dalmatian (): Attributes{testrace} { //using it as argument
cout << "do i even arrive here" << endl;
}
};
int main() {
Dalmatian bob;
cout << bob.intelligence << endl;
}
THREE - crashes
#include <iostream>
#include <string>
using namespace std;
string testrace = "dog"; //defining it globally
class Attributes {
public:
Attributes (string race){
if (race == "human"){
intelligence = 10;}
else if (race == "dog"){
intelligence = 4;}
}
int intelligence;
};
class Dalmatian: public Attributes{
public:
string testrace = "dog"; // AND locally
Dalmatian (): Attributes{testrace} { //using it as argument
cout << "do i even arrive here" << endl;
}
};
int main() {
Dalmatian bob;
cout << bob.intelligence << endl;
}
What I am looking for, of course, is a working alternative to example TWO.
However I am also interested in an explanation why all of the three pieces of code will compile fine, but executables resulting from 2 and 3 will crash.
EDIT: I do know that examples ONE and THREE don't make sense, I used them for demonstrational purposes. (Also fixed my wording, compiler did fine, executable crashed);
EDIT2: I do, of course, realize that I could just replace "testrace" with ""dog"", but for easier transferability to other subclasses, I would prefer a solution that lets me use a variable argument for Attributes(), that I can vary depending on the subclass, that is invoking the main class.
First off when you have
...
string testrace = "dog";
Dalmatian (): Attributes{testrace}
...
testrace will hide the global testrace as class members supersede global variables when you are in class scope. This means both example two and three use the same variable, the class member variable.
The reason two and three crash because you are trying to use a variable that has no been constructed before. When you get to
Dalmatian (): Attributes{testrace}
testrace has not yet been constructed. Even though you have string testrace = "dog"; in the class body that initialization doesn't happen until after Attributes{testrace} is called. So Attributes (string race) gets a uninitialized string and using it is undefined behavior and also causes your crash.
The problem is that at the time that Attributes is constructed the testrace variable is yet uninitialized, and accessing it gives you undefined behavior.
You can simply fix this by writing
Dalmatian (): Attributes{"dog"} { //using it as argument
cout << "do i even arrive here" << endl;
}
See the working code here.

const int Employee::number is protected

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.

Overriding virtual function not working, header files and c++ files

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.