How to create member objects by class constructor? - c++

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();
}

Related

C++ code -calling a constructor of one class within a constructor of same class

I am trying to compile this C++ code unfortunately, I failed to compile the code below. Can you help me by explaining why I am getting this error ?
#include <iostream>
#include <cstring>
using namespace std;
class student
{
private:
char name[10];
int id;
int fee;
public:
student(char name[10],int id)
{
strcpy(this->name,name); //string copy
this->id=id;
fee=0;
}
student(char name[10],int id,int fee)
{
student::student(name,id); //calling a constructor of one class
// within a constructor of same class
this->fee=fee;
}
void show() //print function
{
cout<<"Name:"<<name<<endl;
cout<<"id:"<<id<<endl;
cout<<"fee:"<<fee<<endl<<endl;
}
};
int main()
{
student s1("DAVID",123);
student s2("WILLIAM",124,5000);
s1.show();
s2.show();
return 0;
}
Below is the error GCC is complaining about.:
main.cpp|20|error: cannot call constructor 'student::student' directly [-fpermissive]|
As rightly suggested by #sweenish, You must do the constructor delegation in the initialization section. Something like this:
#include <iostream>
#include <cstring>
using namespace std;
class student
{
private:
char name[10];
int id;
int fee;
public:
student(char name[10],int id)
{
strcpy(this->name,name); //string copy
this->id=id;
fee=0;
}
student(char name[10],int id,int fee): student(name, id) // constructor delegation
{
this->fee=fee;
}
void show() //print function
{
cout<<"Name:"<<name<<endl;
cout<<"id:"<<id<<endl;
cout<<"fee:"<<fee<<endl<<endl;
}
};
int main()
{
student s1("DAVID",123);
student s2("WILLIAM",124,5000);
s1.show();
s2.show();
return 0;
}
Also, an advice for you. You may define the more specialized constructor and delegate the responsibility of less specialized constructors to more specialized ones.
#include <iostream>
#include <cstring>
using namespace std;
class student
{
private:
char name[10];
int id;
int fee;
public:
student(char name[10],int id, int fee): id{id}, fee{fee} // more specialized
{
strcpy(this->name,name); //string copy
}
student(char name[10],int id): student(name, id, 0) { } // less specialized calling the more specialized constructor
void show() //print function
{
cout<<"Name:"<<name<<endl;
cout<<"id:"<<id<<endl;
cout<<"fee:"<<fee<<endl<<endl;
}
};
int main()
{
student s1("DAVID",123);
student s2("WILLIAM",124,5000);
s1.show();
s2.show();
return 0;
}
Here's your code with changes made to get it compiling; I marked the changes with comments.
#include <iostream>
// #include <cstring> // CHANGED: Prefer C++ ways when writing C++
#include <string> // CHANGED: The C++ way
// using namespace std; // CHANGED: Bad practice
class student {
private:
std::string name{}; // CHANGED: Move from C-string to std::string
int id = 0; // CHANGED: Default member initialization
int fee = 0;
public:
// CHANGED: Move from C-string to std::string
student(std::string name, int id)
: name(name),
id(id)
// CHANGED: ^^ Utilize initialization section
{
// CHANGED: Not needed anymore
// strcpy(this->name,name); //string copy
// this->id=id;
// fee=0;
}
student(std::string name, int id, int fee) : student(name, id) {
// CHANGED: Not needed anymore
// student::student(name,id); //calling a constructor of one class
// // within a constructor of same class
// NOTE: Inconsistency with other ctor w.r.t. this->
this->fee = fee;
}
void show() // print function
{
std::cout << "Name:" << name << '\n';
std::cout << "id:" << id << '\n';
std::cout << "fee:" << fee << "\n\n";
}
};
int main() {
student s1("DAVID", 123);
student s2("WILLIAM", 124, 5000);
s1.show();
s2.show();
return 0;
}
Here is the same code, but with the stuff I commented out removed to make it easier to read.
#include <iostream>
#include <string>
class student {
private:
std::string name{};
int id = 0;
int fee = 0;
public:
student(std::string name, int id) : name(name), id(id) {}
student(std::string name, int id, int fee) : student(name, id) {
this->fee = fee;
}
void show()
{
std::cout << "Name:" << name << '\n';
std::cout << "id:" << id << '\n';
std::cout << "fee:" << fee << "\n\n";
}
};
int main() {
student s1("DAVID", 123);
student s2("WILLIAM", 124, 5000);
s1.show();
s2.show();
return 0;
}
The initialization section follows the parameter list and is marked with :. You then initialize each member, in the order they are declared.
In the case of the constructor doing the delegating, you are unable to initialize fee in the initialization section. The error I receive is a delegating constructor cannot have other mem-initializers.
I don't like splitting my initialization like that, and if you insist on delegating constructor calls for this class, implement the most specific constructor and delegate to it with your less specific constructors. I prefer default member initialization as I think it leads to less confusion and written code overall.
The code then compiles and you get the expected output:
Name:DAVID
id:123
fee:0
Name:WILLIAM
id:124
fee:5000

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

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.

How to implement a class that has one of either two types for an arg

if I have a c++ class like:
class Student
{
public:
string name;
int assigned_number;
};
and I want to use either name or number but not both for each instance, is there a way to make this an Or type where only one of them is required?
If you are using C++17 or above, you can use std::variant from <variant>:
#include <iostream>
#include <variant> // For 'std::variant'
class Student
{
public:
std::variant<std::string, int> name_and_id;
};
int main() {
Student stud; // Create an instance of student
// Pass a string and print to the console...
stud.name_and_id = "Hello world!";
std::cout << std::get<std::string>(stud.name_and_id) << std::endl;
// Pass an integer and print to the console...
stud.name_and_id = 20;
std::cout << std::get<int>(stud.name_and_id) << std::endl;
}
std::variant is a new addition to C++17 and is intended to replace the unions from C and has exceptions in case of errors...
You can use union.
#include <string>
class Student
{
// Access specifier
public:
Student()
{
}
// Data Members
union
{
std::string name;
int assigned_number;
};
~Student()
{
}
};
int main()
{
Student test;
test.assigned_number = 10;
test.name = "10";
return 0;
}

How can I initialize struct data members in Constructor?

I am using data.h file which have the following code
#ifndef __DATA_h_INCLUDED__
#define __DATA_h_INCLUDED__
#include "string"
struct data {
std::string location="";
int year = 0, month = 0;
data();
data(std::string location, int year, int month);
};
#endif
and the data.cpp file looks like this
#include "data.h"
#include "string"
using namespace std;
data::data() {
//initialize the data members (location,year,month)
}
data::data(std::string loc, int year, int month) {
//initialize the data members (location,year,month)
}
in some other .cpp file how can i get these values and initialize these values.
node.h
struct Node {
data d;
Node(std::string id, int year, int month);
};
node.cpp
Node::Node(string id, int year, int month){
// here i want to initialize 'data'
}
print.cpp
Node* node;
cout<<node->data->location;
They are already initialized for the default coinstructor (which shoudl proably be =default instead).
Then just use the initialization list:
data::data(std::string loc, int year, int month):loc(std::move(loc)), year(year), month(month) {
}
Include string properly as well:
#include <string>
in your "data.cpp", you can initialize the members like this:
#include "data.h"
#include "string"
using namespace std;
data::data() : year(0), month(0) {
//initialize the data members (location,year,month)
//in fact, 'location' donot need initialization,
//because the member will be constructed first as
//a empty string before give control to user-defined constructor.
location = "";
}
data::data(std::string loc, int _year, int _month)
year(_year), month(_month) {
//initialize the data members (location,year,month)
location = loc; // or location.assign(loc);
}
when you use the structure in other cpp file, you may use is like this:
#include "data.h"
data x; //call default constructor: data();
//since struct 's member is implicitly public,
//you can access them from outside of its defination.
x.location = "your location";
x.location.assign("some other place");
x.location.append("etc");
x.year = 2018;
x.month = 11;
Initializing data in constructor is done like this:
data::data() :
location(""), year(0), month(0)
{
}
data::data(std::string loc, int year, int month) :
location(loc), year(year), month(month)
{
}
In some other cpp file, for example, main.cpp, you can use this like that:
#include <iostream>
#include "data.h"
int main()
{
// initializing
data obj("NY", 2018, 11);
// using
std::cout << "Year: " << obj.year << std::endl;
std::cout << "Month: " << obj.month << std::endl;
std::cout << "Loc: " << obj.location << std::endl;
// setting properties
obj.year = 2100;
obj.month = 1;
std::cout << "Year: " << obj.year << std::endl;
std::cout << "Month: " << obj.month << std::endl;
// initializing by default values
data obj2();
}
Don't overcomplicate this. If you start coding, don't split your code up in too many files.
Other than that, learn about struct member initialization. Either on this site or at your favorite documentation page.
For member access you use the arrow-operator when you have a pointer to an object, vs. the dot-operator that you use when you have the object directly. For further reading:
What is the difference between the dot (.) operator and -> in C++?
https://en.cppreference.com/w/cpp/language/operator_member_access#Built-in_member_access_operators
Here's example code:
#include <iostream>
struct data {
int x_;
// use member initialization list to init the data value directly
data(int x) : x_(x) {}
};
struct node {
data data_;
// use member initialization list to init the data value directly
node(int x) : data_(x) {}
};
int main() {
// create object
node n(42);
// acquire pointer to object
node *p = &n;
// use arrow to access member with pointer, use dot to access with object
std::cout << p->data_.x_ << '\n';
}
The output is:
$ g++ test.cc && ./a.out
42
And because it seems you might be into implementing some sort of data structure, you might also want to learn about object lifetime and ownership issues with manual memory management. So some references for further education:
What is a smart pointer and when should I use one?
CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

Static Functions and class variables?

I have broken down my issue into a small simple program.
I have a class myclass I have created in a separate .cpp file "classes.cpp" and declared in the header file "classes.h". myclass contains a variable a of which is initialized when instantiated. This makes variable a = 5.
My overall goal is to create a class in a separate .cpp file declared in a .h file which I can create multiple instances of in my main() program. The problem I am having is this.
In my main() function I create an instance of myclass called first.
my main program shows the variable a is set to the number 5.
If I want to change that number using a static function (and it has to be a static function as this relates to something much bigger in another program I am writing). I call the static function directly and in that static_function I create an instance of myclass and call the non_static_function because static functions have no implicit 'this' connecting them to an object.
In my non_static_function I change the value to the number 8. The problem is that the value of variable 'a' in 'first' remains at 5 when I want it to be 8. I need to change the value using first->static_function(8) and not by first->a = 8. . How can I do this?
Code below:
**main.cpp**
#include <iostream>
#include "classes.h"
using namespace std;
int main()
{
myclass *first = new myclass();
cout << "Myclass variable a is = " << first->a << endl;
first->static_function(8); // trying to change myclass variable 'a' to 8.
cout << "But" << endl;
cout << "the actual value of a is still: " << first->a << endl;
}
**classes.h**
#ifndef CLASSES_H_INCLUDED
#define CLASSES_H_INCLUDED
class myclass
{
public:
int a;
myclass();
void non_static_function(int x);
static void static_function(int x);
};
#endif // CLASSES_H_INCLUDED
**classes.cpp**
#include <iostream>
#include <cstdlib>
#include "classes.h"
using namespace std;
myclass::myclass()
{
a = 5;
}
void myclass::non_static_function(int x)
{
a = x;
cout << "The value for variable 'a' was 5 but is now: " << a << endl;
}
void myclass::static_function(int x)
{
myclass *p = new myclass();
p->non_static_function(x);
}
If you want every instance of myclass to have its own a and you want to call a static function to change it then you need to pass the instance you want changed to the static function. A static function can only modify static members of a class or the members of an instance that is inside its scope. Non static member functions can change any variable that is a member of the class.
class Foo
{
private:
int bar;
public:
static void static_function(int value, Foo & foo) { foo.bar = value; }
void non_static_function(int value) { bar = value; }
};
int main()
{
Foo foo;
Foo::static_function(8, foo);
// now bar will have the value of 8
foo.non_static_function(20);
// now bar will have the value of 20
}
I have finally found a way to deal with this small problem. Above the 'myclass' definition in classes.cpp I declare a 'myclass' variable
myclass *tgt; . Then in my constructor for 'myclass' I just allocate the instantiated object to a my global myclass variable of which I can access from the myclass definition tgt = this; Now I can use tgt in my static function to call the non_static_function in my 'myclass' definition and it all works perfectly.
NathanOliver, you are correct in saying that I need a class instance but the way I have done it here suits my needs. Passing the instance of myclass is certainly another way of doing this but it would require a global function above my 'myclass' definition.
Thanks for the help.
**main.cpp**
#include <iostream>
#include "classes.h"
using namespace std;
int main()
{
myclass *first = new myclass();
cout << "Myclass variable a is = " << first->a << endl;
first->non_static_function(8); // trying to change myclass variable 'a' to 8.
cout << "But" << endl;
cout << "The actual value of a is still: " << first->a << endl;
myclass *second = new myclass();
cout << "For the 'second' class the variable a is: " << second->a << endl;
second->non_static_function(23);
cout << "After calling the static function from 'second' the value of a is: " << second->a << endl;
cout << "And first->a is still: " << first->a << endl;
}
**classes.h**
#ifndef CLASSES_H_INCLUDED
#define CLASSES_H_INCLUDED
class myclass
{
public:
int a;
myclass();
void non_static_function(int x);
static void static_function(int x);
};
#endif // CLASSES_H_INCLUDED
**classes.cpp**
#include <iostream>
#include <cstdlib>
#include "classes.h"
using namespace std;
myclass *tgt; // *Add a global myclass variable above the myclass
definition*
myclass::myclass()
{
tgt = this; // *In the constructor allocate the instantiated class
//from main() to "tgt" .*
a = 5;
}
void myclass::non_static_function(int x)
{
a = x;
// Now see that the value of a is changed.
cout << "The value for variable 'a' was 5 but is now: "<< this->a << endl;
}
void myclass::static_function(int x)
{
tgt->non_static_function(x);
}