I'm confused on how to put numerous objects into a class.
So we are required to read in a file that contains a timestamp, employee ID, location number, event code. An example of the input is:
10039865 WHITE99 1 OP
10039876 WHITE99 1 EN
10047500 PINK01 1 EN
10047624 SMITH01 3 EX
10047701 TAN07 2 EN
10048567 DIITZ01 2 OP
10048577 DIITZ01 2 OP
10048587 DIITZ01 2 OP
how do I set those information into objects in a class?
here is what I got so far, and stuck from here. We are required to write a program using an array of pointers to objects.
class Employee {
long timestamp;
string staffID;
int locNum;
string eventCode;
public:
void setValues (long, string, int, string);
};
void Employee::setValues(long timestamp, string staffID, int locNum, string eventCode) {
this->timestamp = timestamp;
this->staffID = staffID;
this->locNum = locNum;
this->eventCode = eventCode;
}
I'm going to leave out a few things, since this looks like homework, but this will get you far.
A couple of things to watch out for:
You aren't using a constructor. Sure a default constructor is nice, but it can help to make your own, especially when starting out.
You should probably use vector instead of an array.
For example:
// Note that I'm making the members public - this is only for demonstration so I don't have to write getters and setters.
class Employee {
public:
Employee(long, std::string, int, std::string);
long timestamp;
std::string staffID;
int locNum;
std::string eventCode;
};
// Here is the constructor.
Employee::Employee(long l, std::string s, int n, std::string s2): timestamp(l), staffID(s), locNum(n), eventCode(s2){}
As for an array - it may be wiser to stick to using a vector of Employee pointers. Such as:
typedef Employee * EmployeePointer;
EmployeePointer employeePtr;
std::vector<EmployeePointer> employeeVec;
Then .push_back() new Employees using your fancy new constructor with a pointer.
employeePtr = new Employee(181213, "Bob", 22, "OP");
employeeVec.push_back(employeePtr);
And simply re-use the employeePtr for new employees.
employeePtr = new Employee(666732, "Sue", 21, "MA");
employeeVec.push_back(employeePtr);
And you will see that a vector (which most other languages refer to as an Array anyway) of pointers to employee objects is created:
for(auto it = employeeVec.begin(); it != employeeVec.end(); it++){
std::cout << (*it)->timestamp << " " << (*it)->staffID << " " << (*it)->locNum << " " << (*it)->eventCode << std::endl;
}
Which displays:
181213 Bob 22 OP
666732 Sue 21 MA
If you can't use vector for whatever reason then implementing this with an array isn't that different, except.
EmployeePointer empPtrArr;
// You'll need to know the size of your array of pointers or else dynamically allocate it, which I don't think you want to do.
empPtrArr = * new EmployeePointer[2];
And You'll have to use a basic for-loop, and not that fancy for(auto ... ) one I used.
Final comments:
For every 'new' there is a 'delete', or you'll have a memory leak
There is at least one #include I left out for you to figure out, which shouldn't be difficult to find
Related
This is my first question on here, so excuse me if I've formatted everything in a wrong way.
So, to get to the problem - this is s university assignment of mine. The goal is to create a class called Student, which has a few fields, and store the instances in an array of Student objects. One of the tasks is to have a static variable inside the class that keeps track of how many Student instances have been created. To clarify, I have a getData() method that asks the user for the values, and then sets the current object's values to those entered (basically just like a constructor, which makes the constructors obsolete, but they want us to do it that way for some reason). In the getData() function, the static count variable gets raised by 1, as well as in the constructors, and gets decremented in the destructor.
The issue is that for some reason, Student::amount variable sets itself to 100. Every time that i try to access it from the main() function, its 100 plus the amount of students created, so if we have created 2 students, Student::amount will be equal to 102. I've nowhere explicitly set it to that number, which also matches the size of the array, by the way.
I'm still rather new to C++, I've read and watched some material on how OOP works here, but I've barely even scratched the surface.
Sorry again if my question is badly formulated or/and badly formatted, and I hope you can help me with this!
#include <iostream>
#include <string>
using namespace std;
class Date {
...
};
class Student {
// Fields
private:
string name = "";
string PID = "";
int marks[5]{ 0 };
short course = 0;
public:
// Count of all students
static int amount;
// Getters
...
// Setters
...
// Constructors
Student(); // Default one, Student::amount gets raised by 1 there
Student(string name, string PID, int marks[5], short course);
// Destructor
~Student();
// Methods
void getData();
void display(); // Display the information of a student
};
// Array of students
Student students[100];
// Student::Count
int Student::amount; // Initializes with 0 if not explicitly initialized
void Student::getData() {
cin.ignore();
cout << "\nName: "; getline(cin, name);
cout << "PID: "; getline(cin, PID);
cout << "Marks:\n";
for (int i = 0; i < 5; i++)
{
cin >> marks[i];
}
cout << "Course: "; cin >> course;
Student::amount++;
}
Student::Student(string name, string PID, int marks[5], short course) {
this->setName(name);
this->setPID(PID);
this->setMarks(marks);
this->setCourse(course);
Student::amount++;
}
The global declaration Student students[100]; calls the default Student constructor 100 times, before main is reached. According to your comment (you don't supply the constructor implementation), that constructor increases amount by 1.
A solution here is to remove Student::amount and instead use
std::vector<Student> students;
students.size() will give you the number of students in that container. Use push_back to put students into the vector.
A very crude alternative that at least is broadly compliant with the question constraints is to remove the amount increment from the default constructor, and all other places apart from the four argument constructor.
Context:
New to C++ here. I have a larger project where I have a classes A, B, and C.
Class A has a field with type unordered_map<int, B>.
Class B also has fields of class C which have fields of type set.
I want to mutate the B objects in the map because I don't want the overhead associated with immutability. I have tried doing this with map.find() and map.at(), but with both methods, the mapped objects are not mutated as evidenced by the behavior of subsequent calls. I didn't try indexing with [] because class B does not have a default constructor.
According to the VSCode C++ documentation (but oddly not the online docs), the pair returned by find has a copy of the value object, which is wrong for obvious reasons.
I tried using at(), which supposedly returns a reference to the value object. This results in the same issue with find(), unfortunately.
I then tried making my map with values of *B, but later, these objects would go out of scope and I assume deallocated resulting in a segmentation fault.
So I even tried changing my map to be of type <int, int> where the value is an index into a vector, which is where I found the problem to be general to containers as opposed to just maps.
I know I can do something like map.at(i) = map.at(i).funcWithSideEffects(); but I'm not ready to accept that this is the only way to do this. For a procedural language with a concept of state (i.e. not-a-fundamentally-functional language), it seems bizarre that there would be no way to mutate a value in a map or container-type field.
Long story short and minimum example:
How can I mutate objects in a container field such as a vector?
Example:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class Person
{
private:
string first;
string last;
vector<Person> children;
public:
Person(string first, string last) {
this->first = first;
this->last = last;
}
string getFirstName() {
return this->first;
}
void setFirstName(string first) {
this->first = first;
}
string getLastName() {
return this->last;
}
vector<Person> getChildren() {
return this->children;
}
void addChild(Person child) {
return children.push_back(child);
}
};
int main() {
Person p("John", "Doe");
Person child("Johnny", "Appleseed");
p.addChild(child);
Person grandchild("one", "two");
p.getChildren().at(0).addChild(grandchild);
p.getChildren().at(0).setFirstName("Mark");
cout << "Name: " << p.getFirstName() << " " << p.getLastName() << "\n";
cout << "No. Children: " << p.getChildren().size() << "\n";
cout << "Child Name: " << p.getChildren().at(0).getFirstName() << "\n";
cout << "No. Grandchildren: " << p.getChildren().at(0).getChildren().size() << "\n";
return 0;
}
Desired Output:
Name: John Doe
No. Children: 1
Child Name: Mark
No. Grandchildren: 1
Actual Output:
Name: John Doe
No. Children: 1
Child Name: Johnny
No. Grandchildren: 0
Edit:
Unfortunately, the example I created is decoupled from my original problem. Yes, the behavior that I want is that of references, which is why I was scratching my head when I used map.at(), which says it returns a reference. As described above, this is not the behavior I am observing. Leaving this solved, since I did a subpar job asking my question, and will construct a better example in a different post.
Edit 2:
Thank you to everyone who responded! I put two and two together and figured out what I was doing wrong in my map problem.
I was accessing values in my map with
B b = map.at(i);
instead of
B& b = map.at(i);
I guess the original version makes a copy instead of retaining the reference that map returns. Will make a solution post if anyone else is confused about the same thing in the future.
You returning a copy of your child with calling
vector<Person> getChildren() {
return this->children;
}
With this, your changes are done at the returned copy only. Not in the original stored inctance.
Use a reference to it and you get the
vector<Person>& getChildren() {
return this->children;
}
with this you do the changes in the stored instance.
I you want to avoid useless data copy work, you could change
void addChild(Person& child) {
return children.push_back(child);
}
That avoids creation and deletion of instances to/from the callstack.
So, I've been exploring on how to create a dynamical array with a custom template class that I made.
#include <iostream>
#include <vector>
//HOW TO SET CLASS INTO DYNAMICAL ARRAY WITH VECTOR
//CREATE A CLASS
class User{
std::string name;
public:
User(){
}
User(std::string name){
this->name = name;
}
void set_name(std::string name){
this->name = name;
}
std::string get_name(){
return name;
}
};
int main(){
//SET A NORMAL ARRAY THAT CAN CONTAIN AN OBJECT
User user[1];
//DO WHATEVER WITH THE USER[0] TO SET EVERYTHING THAT LATER WILL BE PUT IN VECTOR
user[0].set_name("Meilianto");
std::cout << "user[0]: " << user[0].get_name() << std::endl;
//CREATE A DYNAMICAL ARRAY WHICH IS VECTOR
std::vector<User> vuser;
//PUSHBACK TO THE VECTOR AS "FIRST ELEMENT" BY PUTTING "USER[0]" AS AN ARGUMENT
vuser.push_back(user[0]);
std::cout << "vuser[0]: " << vuser[0].get_name() << std::endl;
//YOU CAN "MODIFIED" THE "USER[0]" AND ADD AGAIN AS THE "SECOND ELEMENT" OF VECTOR
user[0].set_name("Meilianto1");
vuser.push_back(user[0]);
std::cout << "vuser[1]: " << vuser[1].get_name() << std::endl;
//YOU CAN EVEN "MODIFIED" THE "FIRST ELEMENT" BY CALLING THE "METHOD" OF IT
vuser[0].set_name("Hantu");
std::cout << "vuser[0]: " << vuser[0].get_name() << std::endl;
//THE QUESTION HERE, CAN I DECLARE ARRAY TOGETHER WITH THE CONSTRUCTOR?
User user1[1]("Bebek");
//AND AFTER THAT I CAN ADD THAT OBJECT STRAIGHT AWAY TO VECTOR WITHOUT ASSIGNING ALL THE
//MEMBERS ONE BY ONE
return 0;
}
If you have read my comments in my code, what I am trying to do is maybe it will be faster if I just construct right away when I create the object instead of assigning all the members one by one that will cost more code. I imagine if in the future there will be an object with a lot of members and need to assign it one by one. It won't be efficient.
EDIT: I edit the User user[0] into User user[1], Thanks
If you're using a modern standard of C++, then you can do this
std::vector<User> vuser {
{"Meilianto1"},
{"Hantu"},
{"Bebek"}
};
Where each pair of inner brackets with a string calls User constructor, and outer pair of brackets calls std::vector<User> constructor with a sequence of Users
THE QUESTION HERE, CAN I DECLARE ARRAY TOGETHER WITH THE CONSTRUCTOR ?
User user1[1]("Bebek");
You can use list initialization for that, for arrays as well as for vectors:
User users[] { std::string("Herbert"), std::string("Anton") };
std::vector<User> vusers { std::string("Herbert"), std::string("Anton") };
CAN I ADD THAT OBJECT STRAIGHT AWAY TO VECTOR WITHOUT ASSIGNING ALL THE MEMBERS ONE BY ONE
You can initialize a vector with the elements of an previously defined array like this:
std::vector<User> v2users(std::cbegin(users), std::cend(users));
BTW: note that User user[0]; in your code defines an array without elements, i.e. of size 0, which rarely makes sense. Accessing user[0] leads to undefined behaviour
Yes, you can!
User users[]{ User{ "one" }, User{ "two" } };
// Construct vector from iterator-pair:
std::vector<User> users_vector{ std::cbegin(users), std::cend(users) };
You can use emplace_back method or push_back with temporary object to add the vector. For example;
vuser.emplace_back("Meilianto1"); // 1
vuser.push_back(User{"Meilianto1"}); // 2
The first one passes the arguments to the constructor. The second one will move the variable to vector. To be honest, there will be copy elision that's why there will be no move or copy. Just it will construct the variable.
I'm doing some testing on an idea that I have. However, I am at loss.
I have declared a class as such :
class Course {
private:
char *name;
int ID;
public:
void printCourses();
}
Where printCourses() is defined as
void Course::printCourses() {
cout << name;
}
This makes sense. Each class has a data member called name. So, say that I have declared an array of 10 class objects, that means 10 potential names will correspond with to a course...OK.
My problem lies here.
What if object 6 and 9 have those values filled at some point during runtime, and I want to KNOW that. For instance, say course 6's name is "Psychology" and course 9's name is "History". I want to be able to print these values in a sort of "Course List" function... Some class objects are not yet filled here but I want to be able to parse through and find the objects that do have the name variable filled.
I have tried a for loop for testing this but no logic is helping... I tried this.
for (int i=0; i<10; i++) {
course[i]->printCourses();
}
In my head, this should still iterate through all 10 objects (I have already allocated memory for all 10 of them), but it doesnt. However, if I just call the one I know is filled like this:
course[6]->printCourses();
It returns properly "Psychology".
TLDR: Help, How can I iterate through class objects to find certain variables that are filled ?
disclaimer
I am using -> operators for classes because I declared the array as an array of pointers to the class objects.
Course *course[10];
Like so..
Before I answer, a side note. You say: "I have already allocated memory for all 10 of them." This is bad. You've wasted space on names that will never be used. Instead let's use a string to allocate as necessary, and to allocate any number of characters as desired.
Now, you need to use optional to solve this. Specifically your name shouldn't be definied as char* name but a optional<string> name. To use optional you'll just need to change your printCourses method:
void Course::printCourses() const { cout << name.value_or(""); }
Live Example
You could make a additional method that returns your name. If the name is a nullptr, you ignore it. Your method could look like:
char* Course::GetName()
{
return name;
}
And then you iterate with:
for (int i = 0; i < 10; i++) {
if (course[i]->GetName())
course[i]->printCourses();
}
An alternative way is to combine the functions:
void Course::printCourses() {
if (name)
std::cout << name;
}
Iterate with the following and it will only print the name if it exist:
for (int i = 0; i < 10; i++) {
course[i]->printCourses();
}
It would be good to initialize the name in the constructor:
Course::Course() : name(nullptr), ID (0) {}
Little side note: Use std::string instead of char*. I provided you a solution for your case but you can do pretty much the same with a solid c++ string.
I have 2 classes:
class CCandidate {
public:
float score;
BitSet documents;
std::vector<std::vector<int> > phrases;
int cardinality;
/** For merging. */
CCandidate()
: score(0.0), documents(1), phrases(1), cardinality(0) {}
/** */
CCandidate(
std::vector<int>& phraseIndices, BitSet& documents,
int cardinality, float score) {
this->phrases.reserve(1);
this->phrases.push_back(phraseIndices);
this->documents = documents;
this->score = score;
this->cardinality = cardinality;
}
};
class PCandidate {
public:
CCandidate * topics;
float coverage;
bool selected;
bool mostGeneral;
bool mostSpecific;
PCandidate(CCandidate * c, float coverage)
: topics(c), coverage(coverage),
selected(true), mostGeneral(true), mostSpecific(true) {}
};
In another class where these classes are used I have something like this:
// ...
std::vector<std::shared_ptr<PCandidate> > phrases(mergeList.size());
for (size_t i = 0; i < mergeList.size(); i++) {
CCandidate * cc = baseTopics.at(mergeList.get(i));
std::wcout << cc->toString() << std::endl;
float coverage = cc->cardinality / result->cardinality;
std::wcout << "coverage=" << coverage << std::endl;
phrases.push_back(std::make_shared<PCandidate>(PCandidate(cc, coverage)));
std::for_each(phrases.begin(), phrases.end(),
[&](const std::shared_ptr<PCandidate>& pc) {
std::wcout << pc->toString() << " "; }); // error
}
}
anotherMethod(phrases);
// ...
Everything is fine with the CCandidate cc (for now in this verson it is a raw pointer), I can print its contents (method toString() not copied in here) and all is fine. Then I construct the PCandidate Object with the make_shared, push it into the phrases vector and when try to access that `phrases' vector to show me the contents of Pcandidate, the topics cluster I get an segmentation fault.
I could not do something like
std::wcout << ptr->topics->phrases.size() << std::endl
where ptr is a pointer to PCandidate. topics is a pointer to CCandidate containing the phrases vector.
It will give me
==10013== Invalid read of size 8
to see the size of the phrases vector in CCandidate.
I'm a little lost as I do not now where to track down the problem, sitting at this since yesterday. It might be a bloody beginners mistake. Is the lack of missing copy constructors / assignment operators? If yes how should they look like? For example copy the entire phrases vector, like a deep copy? I thought the default copy/assignment should be OK so far.
Would be great if somebody can show me the error or how to fix this! Thanks in advance for your time!
You're populating your phrases vector initially with mergeList.size() NULL shared-pointers, then pushing the real ones in after those.
std::vector<std::shared_ptr<PCandidate> > phrases(mergeList.size());
So the first mergeList.size() pointers in your vector are NULL. Lose the initial sizing.
std::vector<std::shared_ptr<PCandidate> > phrases;
If you want to reserve capacity, you can, but ultimately the shared pointers are still going to have to go through their reference counting algorithm either way. I'd skip it and just do the above.