Creating a counter inside a template class constructor - c++

I'm stuck on a homework question. We are to create a class template called department, and in the constructor, we need to initialize a counter to be used later. I'm having trouble understanding how to use this counter elsewhere in the program. We were provided with a main.cpp file to use, which we aren't allowed to change. These are the specific instructions I'm stuck on:
You are to create a constructor that may take the department name as an argument, and if it’s null it will ask for a department name to be entered from the keyboard and stores it. It also initializes a counter that keeps track of the number of employees in the array and is maintained when you add, remove, or clear.
The only way I've managed to get it to work is by setting the constructor to accept two arguments, one for department name, and one for the counter. But the main.cpp file provided only allows for one argument, name.
Department.h:
template <class Type>
class Department {
private:
std::string name;
...
public:
Department(const std::string & deptName)
{
int counter = 0;
name = deptName;
}
...
};
Main.cpp (provided, and not allowed to change):
int main()
{ Department dept1("CIS"); // a department
...
Is there a way to use the counter initialized in the constructor outside of the constructor without changing the argument requirements for Department?

Is there a way to use the counter initialized in the constructor outside of the constructor without changing the argument requirements for Department?
Sure. Make a counter member variable, and use it in the methods you write for your class.
template <class Type>
class Department {
private:
std::string name;
int counter;
public:
Department(const std::string & deptName)
{
counter = 0; // note `int` not needed, as counter is already declared
name = deptName;
}
int getCounter()
{
return counter;
}
void addEmployee(std::string name)
{
counter++;
// do something with adding employees
}
// other methods
};

Related

Dealing with errors due to different constructors in the code while allocating new memory in object oriented programming

I want to allocate new memory for my class which has some derived classes as well. as I have defined a constructor of type Professor(string name,int age,int publications,int cur_id) memory allocation
per[i] = new Professor; in the main throws error:no matching function for call to 'Professor::Professor().
another error I am getting is candidate: 'Professor::Professor(std::string, int, int, int) expects 4 arguments, 0 provided. please help me how to define a constructor which allocates memory without giving any error, thanks.
ps: I am trying to solve this question
part of my class looks like;
class Person{
protected:
string name;
int age;
public:
Person(string name,int age){
name=name;
age=age;
}
int z=0;
void getdata(){
string m;int n;
cin>>m>>n;
z++;
Person(m,n);
}
void putdata(){
cout<<name<<" "<<age<<endl;
}
};
class Professor: public Person{
public:
int publications;
int cur_id;
Professor(string name,int age,int publications,int cur_id)
:Person(name,age)
{
publications=publications;
cur_id=cur_id;
}
int b=0;
void getdata(){
string a;int b,c;
cin>>a>>b>>c;
b++;
Professor(a,b,c,b);
}
void putdata(){
cout<<name<<" "<<age<<" "<<publications<<" "<<cur_id<<endl;
}
};
class Student:public Person{
public:
int marks[6];
int cur_id;
Student(string name,int age,int arr[6],int cur_id)
:Person(name,age)
{
marks[6]=arr[6];
cur_id=cur_id;
}
int s=0;
void getdata(){
string p;int q;int r[6];
cin>>p>>q;
for(int i=0;i<6;i++){
cin>>r[i];
}
s++;
Student(p,q,r,s);
}
void putdata(){
cout<<name<<" "<<age<<" "<<marks[0]<<" "<<marks[1]<<" "<<marks[2]<<" "<<marks[3]<<" "<<marks[4]<<" "<<marks[5]<<" "<<cur_id<<endl;
}
};
My main function looks like
int main(){
int n, val;
cin>>n; //The number of objects that is going to be created.
Person *per[n];
for(int i = 0;i < n;i++){
cin>>val;
if(val == 1){
// If val is 1 current object is of type Professor
per[i] = new Professor;
}
else per[i] = new Student; // Else the current object is of type Student
per[i]->getdata(); // Get the data from the user.
}
for(int i=0;i<n;i++)
per[i]->putdata(); // Print the required output for each object.
return 0;
}
Your problem has nothing to do with dynamic allocations. You are trying to construct Professors and Students by calling their default constuctor (new Professor / new Student), but they don not have a default constructor.
A default constructor is a constructor that can be called without parameters. You can change existing constructors:
Person(string name = "",int age = 42) : name(name), age(age) {}
And similar for Student. Note that your constructor implementation was wrong. You assigned the parameters to themself but did not initialize the members. The member initialization list is a special place where you can use the same name for member and argument without shadowing.
Alternatively call the constructor with parameters.
Glad to see you are trying to improve your C++ skills. However, there are few things you can do to improve the code quality and style:
per[i]->getdata(); // Get the data from the user. will not work.
You have not defined getdata() with a virtual prefix.
Mark the overloaded methods in your child classes with an override suffix.
The same problem exists for putdata()
Virtual destructor is missing.
Is it valid to create a Person object? I think not. Therefore:
Constructor can be protected.
Person::getdata() can be a pure virtual function.
Person::z is a public member variable.
Member variables in child classes (Student and Professor) are also public.
Mentioned already by #463035818_is_not_a_number that Person *per[n]; is not portable C++.
As suggested by #JulienLopez, generally avoid raw pointers in C++11 and above. Use std::shared_ptr or std::unique_ptr.
I think you're missing the point of a constructor a bit here.
The idea is to have all the data ready to create your instance before hand, and then instanciate your class (either Professor or Student).
So your constructors are good, but you getdata member functions are not.
The easiest way would be for your getdata functions to become free functions (or static member functions, in our case, it's pretty much the same), and they gather the datas needed for construction and instanciate your class. (and a clearer name wouldn't hurt while we're at it)
Professor* createProfessorFromCin()
{
string a;int b,c;
cin>>a>>b>>c;
return new Professor(a,b,c,b+1);
}
and your calling code would just end up as
per[i] = createProfessorFromCin();
for example.
Also, few tips if you plan on improving this piece of code:
use smart pointers, it's not the 90's anymore, your code well be a lot more memory
safe (on the same vein, std::vector or std::array would be nice for the grades too)
If you plan on adding more subclasses of the sort, you should look into the Factory pattern for more OCP-friendly code.
Your Person class needs a virtual destructor (once you will fix your memory issues), otherwise your instances won't get destroyed properly.

having an array as class attribute

sorry i'm new to c++ and i feel a little stupid for asking, but i can't get it to work after hours of googling and i can't find what i'm doing wrong
the task is pretty easy:
i want to have employees that have name, age etc (those work)
and i want these employees to be stored inside an array.
so i have:
class Employee {
public:
Employee(string, string, int, int);
string name;
string firstName;
int birthYear;
int usedVacation;
int getAge();
};
and then the list:
class EmployeeList {
public:
int addEmployee(string, string, int, int);
Employee eList[500];
};
what i want is: an object that holds an array of Employees, with methods to add/edit etc them.
so i define the addEmployee method outside the class as follows:
int EmployeeList::addEmployee(string first, string last, int year, int used) {
int i = 0;
for (i; i < this.eList.length; i++) {
if (this.eList[i].deleted == true) {
this.eList[i] = {
firstName: first,
lastName : last,
birthYear : year,
usedVacation : used,
deleted : false };
} else {
this.eList.push({ firstName: first, lastName : last, birthYear : year, usedVacation : used, deleted : false });
}
return i;
}
};
As you probably instantly see, there's a lot wrong with that.
All of the this throw Expression must have class type in VS2015,
also identifier firstName is undefined and identifier lastName is undefined,
but in my eyes, there's nothing wrong with my code.
i'm pretty sure this is a very basic thing i just didn't get yet, but i just can't find out, where the problem is.
I defined methods outside Employee, and this works there without a problem (although with this->, but i tried that and it doesn't work either)
please forgive my lack of skills, i come from javascript :(
this.eList[i] =
this is a pointer, not a reference, so you access the members via this->eList. But 99.99% of the time, it's completely unnecessary. You're inside a member method, so the compiler knows what this is. All you need is eList[i] =.
this.eList.push({
First a brief tutorial with arrays in C++. Employee eList[500] doesn't make an array that can hold 500 Employees, eList is 500 Employees. Forever and always. They are are instantly constructed with the default constructor (or will be once you give Employee a default constructor. This is required if you want to have objects in an array, except in very advanced cases).
this.eList.length
There are three actions you can do with arrays: you can access an element with the [i] syntax, they magically convert to a pointer to the first item very often, and finally, if and only if they're a member of a struct, they can be copied. That's all you can do with them. There is no push, because as I said, it is 500 Employee. There is no length member. Yours is always 500 Employees.
if (this.eList[i].deleted == true)
You gave each Employee 4 members, deleted was not one of them. Instead of keeping track of which Employee objects are unused, the normal thing is to keep all the employees in the first few slots, and give EmployeeList a count to keep track of how many "live" employees there are. The live ones are in indecies 0-count. The employees at count-500 are "deleted". Unfortunately, this does mean that when one is deleted, you have to shift forward all the ones after that.
this.eList[i] = {
firstName: first,
lastName : last,
Employee does not have a lastName. The right way to construct an Employee is via the constructor you declared: Employee(string, string, int, int). So that line should probably be eList[i] = Employee(first, last, year, used);. This creates a new Employee object, and then copies it into that slot of the array. Alternatively, just assign the members one by one.
How about something like:
#include<string>
class Employee
{
public:
std::string firstname;
std::lastname;
int birthyear;
int usedVacation;
void setFirstname(std::string fname){firstname = fname;}
void setLastname(std::string lname){lastname = lname;}
void setBirthyear(int year){birthyear = year;}
void setVacation(int vac){usedVacation = vac;}
};
class EmployeeList
{
public:
int count;
EmployeeList eList[500];
EmployeeList()
{
count = 0;
}
void addEmployee(std::string fname, std::string lname, int year, int vac);
void removeEmployee(std::string fname, std::string lname, int year, int vac);
};
void EmployeeList::addEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count++;
}
void EmployeeList::removeEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count--;
}
BTW I am not sure where your push, deleted, and length methods came from? You will likely also want to add more methods then what I have written, for example a method to return count. You can also add get methods to the employee class and make your member variables private. I hope this is of some help. Goodluck!

Forward Declaring and Dynamically Allocating an Array of Pointers of that Declared Class?

I have been having this problem all day with my C++ lab. As far as I can tell, I have everything working, except for this one clause that my professor has stipulated in our assignment:
The order of class declarations in your source file is InventorySystem, InventoryItem, Product, eProduct. Since InventorySystem contains InventoryItem array of pointers you must use forward declaration on InventoryItem
So InventoryItem -> Product -> eProduct relate to each other in a derived heriarchy, and our assignment is to write that heirarchy combined with an InventorySystem class to manage an array of pointers to eProduct objects.
Unfortunately, all of my trolling of StackOverflow posts have led me to the conclusion that what is asked of me is impossible. As I understand forward declaration, "it is really only useful to inform the complier that the class exists" and anything contextual concerning the structure or the definition of the code would not work with forward declaration-- something that seems to directly conflict with the other requirements of my lab, as InventorySystem has a method required called BuildInventory that parses a formatted textfile and dynamically allocates an array of pointers to eProduct objects. Does that not require a constructor of the "forward declared" object?
I really, really hope that I'm just being a newbie at C++ and that I'm massively misinformed, since this issue has been driving me nuts all day.
Thanks in advance for your help.
PS: sorry for the weird casing of function names and variable, it is how my professor wrote the casing in the our assignment, and I thought it safer just to roll with what he established rather.
//Format for text file is Name;Quantity;Price;Condition
void BuildInventory()
{
ifstream fin ("in.txt");
string name="";
string Buffer = "";
int quantity = 0;
double price = 0.0;
int temp = 0;
if (!fin) {
cout << "ERROR: Failed to open input file\n";
exit(-1);
}
while ( getline (fin, Buffer, ';') ) {
string condChar = "";
Condition condition = NEW;
name = Buffer;
getline (fin, Buffer, ';');
quantity = atol (Buffer.c_str ( ) );
getline (fin, Buffer, ';');
price = atof (Buffer.c_str( ) );
getline (fin, Buffer, '\n') ;
condChar = Buffer.c_str();
if(condChar.compare("R") == 0)
condition = REFURBISHED;
else if(condChar.compare("U") == 0)
condition = USED;
else if(condChar.compare("D") == 0)
condition = DEFECTIVE;
ep = new eProduct(name, quantity, price , condition);
ItemList[ItemCount] =ep;
++ItemCount;
fin.ignore(1, '\n');
}
fin.close();
Sort();
}
Below are the constructors for the hierarchy of objects that the array of pointers dynamically allocated by InventorySystem have to point to (all point to eProducts)
//Constructor of eProduct
eProduct(string Name, int Quantity, double Price, Condition condition)
:Product(Name, Quantity, Price)
{
this -> condition = condition;
}
//Constructor of Product
Product():ProductID(0), Price(0.0){}
Product(string Name, int Quantity, double Price)
:InventoryItem(Name, Quantity)
{
this -> Price = Price;
this -> ProductID = generateProductID();
}
//Constructor of InventoryItem
InventoryItem(std::string Name, int Quantity)
{
this -> Name = Name;
this -> Quantity = Quantity;
}
The secret is in the instructions your professor gave you, as well as in your own description:
The order of class declarations in your source file is InventorySystem, InventoryItem, Product, eProduct. Since InventorySystem contains InventoryItem array of pointers you must use forward declaration on InventoryItem
our assignment is to write that heirarchy combined with an InventorySystem class to manage an array of pointers to eProduct objects.
So you need something like this:
class InventoryItem; // forward declaration
class InventorySystem
{
...
InventoryItem* ItemList[TheArraySizeHere]; // array of InventoryItem pointers
...
};
class InventoryItem
{
...
};
class Product : public InventoryItem
{
...
};
class eProduct : public Product
{
...
};
Since eProduct derives from InventoryItem, you can store eProduct pointers in the array of InventoryItem pointers.
Here is the other piece of the puzzle. You cannot implement BuildInventory() inline inside the InventorySystem class declaration, since the eProduct class has not been declared yet. The implementation of BuildInventory() needs to be separated and implemented after eProduct is defined, eg:
class InventoryItem; // forward declaration
class InventorySystem
{
...
InventoryItem* ItemList[TheArraySizeHere]; // array of InventoryItem pointers
...
void BuildInventory();
};
class InventoryItem
{
...
};
class Product : public InventoryItem
{
...
};
class eProduct : public Product
{
...
};
...
void InventorySystem::BuildInventory()
{
// implementation here ...
}
This is typically done by place all of the declarations in a .h file, and all of the implementations in a .c/.cpp file that #includes the .h file, eg:
Inventory.h:
#ifndef InventoryH
#define InventoryH
class InventoryItem; // forward declaration
class InventorySystem
{
...
InventoryItem* ItemList[TheArraySizeHere]; // array of InventoryItem pointers
...
};
class InventoryItem
{
...
InventoryItem(std::string Name, int Quantity);
...
};
class Product : public InventoryItem
{
...
Product();
Product(string Name, int Quantity, double Price);
...
};
class eProduct : public Product
{
...
eProduct(string Name, int Quantity, double Price, Condition condition);
...
};
#endif
Inventory.cpp:
#include "Inventory.h"
//Constructor of InventoryItem
InventoryItem::InventoryItem(std::string Name, int Quantity)
{
this->Name = Name;
this->Quantity = Quantity;
}
//Constructor of Product
Product::Product() : ProductID(0), Price(0.0) {}
Product::Product(string Name, int Quantity, double Price)
: InventoryItem(Name, Quantity)
{
this->Price = Price;
this->ProductID = generateProductID();
}
//Constructor of eProduct
eProduct::eProduct(string Name, int Quantity, double Price, Condition condition)
: Product(Name, Quantity, Price)
{
this->condition = condition;
}
void InventorySystem::BuildInventory()
{
// implementation here ...
}
Question
InventorySystem has a method required called BuildInventory that parses a formatted textfile and dynamically allocates an array of pointers to eProduct objects. Does that not require a constructor of the "forward declared" object?
Answer
Yes, that would require the full class definition of eProduct and any other leaf level classes derived from InventoryItem. However, that is in the implementation of the class.
The definition of the class can still continue to use pointers to the forwarded declared classes.
A forward-declaration allows you to specify pointers and references of the forward-declared type, and also use the type in function declarations (in the return type and parameter types). It is true that a forward-declaration does not allow you to actually instantiate the type, e.g. as a local variable or class attribute, because it is not a complete type yet (it's called an incomplete type), but provided that that is not what is required, you should be able to make it work.
If you need to work with instantiations of a type inside a function body, that is also perfectly doable, provided you define the actual function body after the dependent type has been fully defined. This is normally done by separating class definitions and function implementations into two separate source files, the header (.h or .hpp) and code (.c or .cpp). The code files need to include all headers that they depend on prior to actually getting into the code.
See When can I use a forward declaration? for an excellent summary of what you can and cannot do with forward-declarations.

I have a class object as a member of another class, how do I initialize as a safe empty state?

I have 2 classes, ISBN, Order. I have an ISBN object as a data member of my Order class and I am having issues with the Order constructor to place the ISBN object in a safe empty state.
My Order.h
#include <iostream>
using namespace std;
class ISBN;
class Order {
int ordered;
int delivered;
ISBN * book;
bool empty;
public:
Order();
Order(const ISBN & isbn);
};
My ISBN.h
#include <iostream>
using namespace std;
class ISBNPrefix;
class ISBN {
char isbnNum[13];
char area[6];
char publisher[8];
char title[7];
char checkDigit[1];
bool emptycheck;
bool registered;
public:
ISBN();
ISBN(const char * str, const ISBNPrefix& list);
}
In my Order constructor I tried this code:
Order::Order() {
ordered = 0;
delivered = 0;
empty = true;
*book->ISBN();
/*
(*book).isbnNum[0] = '\0';
book.area[0] = '\0';
book.publisher[0] = '\0';
book.title[0] = '\0';
book.checkDigit[0] = '\0';
book.emptycheck = true;
book.registered = false; */
}
And variations of it, but I get errors like: "type name is not allowed" "expression must have pointer type" etc...Anyone know what my issue is?
You almost certainly don't want a pointer here, just an ISBN object as a data member:
ISBN book;
This will automatically be initialised using its default constructor; you don't need to do anything. If you want to initialise it using the other constructor (with arguments), then you'll need to do that in the initialiser list:
Order::Order() : book(some_string, some_list)
{
// body of constructor
}
You are having problems because you have declared book as an ISBN*. Therefore your posted line *book->ISBN(); is trying to dereference a null and then call the blank constructor.
If you want to manually allocate book, then you should then use this pattern:
Order::Order() {
ordered = 0;
delivered = 0;
empty = true;
book = new ISBN();
}
Note this will require Order's destructor to call delete on its book member.
You can automatically allocate and delete book as an ISBN by making it a class member, and not a pointer. For that, use this declaration:
class Order {
ISBN book;
... // your other members
}
This will automatically allocate and automatically deallocate an ISBN object member whenever class Order is instatiated and destroyed respectively. No additional steps necessary.

Is it possible to pass a variable out of a class without creating a new object in C++

I have a variable, which is a member of one of my classes, that another is in need of, but I'm not sure how to effectively pass the value between them without using a global variable, which is something I'd like to avoid if at all possible. I know I could create an object, but that would invoke the constructor of the originating class which would execute a number of functions and write the needless results to memory, which would be wasteful of system resources.
Is there an easy way to pass this value between the two functions?
Update: The class that is in need of the variable, called no_of_existing_devices. The purpose of class Initialise is to open up a file and count the number of lines of test it contains, and place that number in the variable int no_of_existing_devices, which is then used by the Device::Device() to create an object for each
class Device
{
public:
void view_attribute_list();
void set_attribute();
Device();
};
Device::Device()
{
for (int count = 0; count < no_of_existing_devices; count ++)
{
// Create an object for each iteration, up to a maximum of no_of_existing_devices
}
}
The class of which this variable is a member
class Initialise
{
public:
int no_of_existing_devices;
bool initialisation;
string existing_device_list[100];
void initialise_existing_devices();
Initialise();
};
Initialise::Initialise()
{
no_of_existing_devices = 0;
}
void Initialise::initialise_existing_devices()
{
string line;
ifstream DeviceList;
DeviceList.open("devices/device_list");
while (true)
{
getline(DeviceList, line, '\n');
if (DeviceList.eof())
{
break;
}
++ no_of_existing_devices;
}
DeviceList.close();
DeviceList.open("devices/device_list");
for (int i = 0; i < no_of_existing_devices; i ++)
{
getline(DeviceList, line, '\n');
existing_device_list[i] = line;
}
Device existing_devices[no_of_existing_devices];
!initialisation; // Existing devices are now initialised
}
Okay, from what I understand:
You don't want to have a global
You don't want to have a static
You don't want to introduce a dependency between Device and Initialise
There is one other option, assuming something owns Device and Initialise, move the no_of_existing_devices up to there, then construct both Device and Initialise with a reference to this variable...
In a similar circumstance I was just passing the pointer to the member --- I had to invoke a member function then, so it was a pointer to the member function, http://www.parashift.com/c++-faq-lite/pointers-to-members.html
It's a bit messy, but it works :-).
If the variable in the originating class can hold a value without an instance of the class I would assume that the variable is static. If not create a public static member of the class. And use it in the target class.
Something like:
// .h file
class A
{
public:
static int a;
}
// .cpp file
int A::a = 123;
// .cpp file of class B
void B::foo()
{
cout << A::a;
}
If it is a class attribute (internal variable), then you can obtain a reference through a get method. Otherwise, you can use the friend keyword on the class you want to access the attribtue from the other For example, if you declare friend class B; on class A, the attributes of the class B will be accessible on the class A.
I suggest you use the first method in order to maintain your code OO pure ;)
Edit: of course, if you access through a reference there are no resources wasted :)
Edit 2: use a static method on Initialise class that returns the no_of_existing_devices and call Initialise::NoOfExistingDevices() on the Device class. If you want to resources use a pointer like this:
public static int* Initialise::NoOfExistingDevices() {
return &no_of_existing_devices;
}
By the way, I advise you to turn the variable private.