C++ Creating Array of Objects - c++

I'm having an issue with making one array with 3 objects. I'm trying to display the information of employees and my issue is that I don't know where I would assign the strings to the appropriate members and how to keep one variable for name. My code and an example are below:
class employee
{
private:
string name;
string idNumber;
public:
void setName(string);
void setIDnumber(string);
string getName();
string getIDnumber();
};
const int numEmployees = 3;
int main()
{
employee employeeInfo [numEmployees];
}
I want to create a constructor for 3 employees and input(name, idNumber). for example, in employeeInfo[0], the first employee, I want to input "Tom Jones", "123456"
and in employee[1], the second employee, "James Blyth", "QE123"

You can simply call the setter methods on the individual employee objects, eg:
int main()
{
employee employeeInfo [numEmployees];
employeeInfo[0].setName("Tom Jones");
employeeInfo[0].setIDNumber("123456");
employeeInfo[1].setName("James Blyth");
employeeInfo[1].setIDNumber("QE123");
// and so on...
...
}
If you really want to add a constructor, then it would look like this:
class employee
{
private:
string name;
string idNumber;
public:
employee() = default;
employee(string name, string idNumber);
...
};
employee::employee(string name, string idNumber) {
setName(name);
setIDnumber(idNumber);
};
...
int main()
{
employee employeeInfo [numEmployees];
employeeInfo[0] = employee("Tom Jones", "123456");
employeeInfo[1] = employee("James Blyth", "QE123");
// and so on...
}
Just note that this will default-construct the array elements first, and then construct temporary objects to overwrite the array elements.
If you want to avoid the temporaries, but still want a fixed array of objects stored consecutively, you can use placement-new to construct the array members directly using your non-default constructor, eg:
#include <type_traits>
int main()
{
std::aligned_storage_t<sizeof(employee), alignas(employee)> buffer[numEmployees];
employee* employeeInfo = reinterpret_cast<employee*>(buffer);
new(&employeeInfo[0]) employee("Tom Jones", "123456");
new(&employeeInfo[1]) employee("James Blyth", "QE123");
// and so on...
...
for(int i = 0; i < numEmployees; ++i) {
employeeInfo[i].~employee();
}
}
Or, you can use a std::vector (if you don't mind the array being allocated in dynamic memory), eg:
#include <vector>
int main()
{
std::vector<employee> employeeInfo;
employeeInfo.reserve(numEmployees);
employeeInfo.emplace("Tom Jones", "123456");
employeeInfo.emplace("James Blyth", "QE123");
// and so on...
...
}
Otherwise, create an array of pointers to objects instead, and then you can create the individual objects dynamically using new with your non-default constructor. The objects just won't be stored consecutively in memory, eg:
int main()
{
employee* employeeInfo[numEmployees];
employeeInfo[0] = new employee("Tom Jones", "123456");
employeeInfo[1] = new employee("James Blyth", "QE123");
// and so on...
...
for(int i = 0; i < numEmployees; ++i) {
delete employeeInfo[i];
}
}
Which would be safer handled with std::unique_ptr, eg:
#include <memory>
int main()
{
std::unique_ptr<employee> employeeInfo[numEmployees];
employeeInfo[0] = std::make_unique<employee>("Tom Jones", "123456");
employeeInfo[1] = std::make_unique<employee>("James Blyth", "QE123");
// and so on...
...
}

Related

Segfault when trying to printf pointer-to-object stored in member std::array

I have this program where I have a Person object that has a std::array<Person*, x> in it.
I add pointers in each Person to other Persons who are their friends.
Now inside an instance of Person, I can examine the pointers-to-Persons in its own array and see the names of the friends. But if I examine the whole array, from my main() function for instance, and try to access the friends from there, I get a segfault.
Here is my Person.h
#ifndef TOO2_PERSON_H
#define TOO2_PERSON_H
#include <string>
#include <array>
class Person {
std::string name;
int id_code;
std::array<Person*, 10> friends;
int friends_count;
public:
const std::string &getName();
void setName(const std::string& name);
int getId_code();
void setId_code(int id_code);
std::array<Person*, 10>* getFriends();
void addFriend(Person* person);
Person();
Person(const Person &obj);
~Person();
};
#endif //TOO2_PERSON_H
And here are the relevant methods in Person.cpp:
#include "Person.h"
const std::string& Person::getName() {
return Person::name;
}
void Person::setName(const std::string& name) {
Person::name = name;
}
int Person::getId_code() {
return Person::id_code;
}
void Person::setId_code(int id_code) {
Person::id_code = id_code;
}
void Person::addFriend(Person* person) {
Person::friends[friends_count] = person;
printf("\nNAme: %s", friends[friends_count]->getName().c_str());
Person::friends_count++;
}
std::array<Person*, 10>* Person::getFriends() {
return &friends;
}
Person::Person(const Person &obj) {
Person::name = obj.name;
Person::id_code = obj.id_code;
Person::friends = obj.friends;
}
Person::~Person() {
//printf("%s deleted", Person::name.c_str());
}
Person::Person() {
Person::friends_count = 0;
}
Here is the code in main() that throws an error:
Persons p = Persons();
Person taavi;
taavi.setName("Taavi");
taavi.setId_code(121421);
p.addPerson(taavi);
Person teele;
teele.setName("Teele");
teele.setId_code(22131);
p.addPerson(teele);
taavi.addFriend(&teele);
teele.addFriend(&taavi);
printf("\n%s", p.getPersons()[0].getFriends()->at(0)->getName().c_str());
Persons.h and Persons.cpp
#ifndef TOO2_PERSONS_H
#define TOO2_PERSONS_H
#include <array>
#include "Person.h"
class Persons {
int persons_count;
std::array<Person, 10> persons;
public:
int getPersons_count();
void setPersons_count(int persons_count);
std::array<Person, 10> getPersons();
void addPerson(Person person);
const Person findPersonByName(std::string& name);
Persons();
};
#endif //TOO2_PERSON_H
#include "Persons.h"
std::array<Person, 10> Persons::getPersons() {
return Persons::persons;
}
void Persons::setPersons_count(int persons_count) {
Persons::persons_count = persons_count;
}
int Persons::getPersons_count() {
return Persons::persons_count;
}
void Persons::addPerson(Person person) {
Persons::persons[persons_count] = person;
Persons::persons_count++;
}
Persons::Persons() {
Persons::persons_count = 0;
}
const Person Persons::findPersonByName(std::string &name) {
Person person = Person();
for(int i = 0; i < Persons::persons_count; i++) {
if(Persons::persons[i].getName() == name) {
person = Persons::persons[i];
}
}
return person;
}
So the last printf is throwing an error. The program finishes with code 139.
What am I doing wrong here?
EDIT: The printf is throwing error when I ask the name of the friend from the array. I can fetch the array it self fine.
This adds a copy of taavi to the Persons collection:
p.addPerson(taavi);
Then this creates a relationship between teele and the original taavi object, not the copy you added to p:
taavi.addFriend(&teele);
Also, your getPersons() function returns a copy of the array, so it makes copies of the copies in the Persons collection!
So this accesses a copy of the copy of taavi:
printf("\n%s", p.getPersons()[0].getFriends()->at(0)->getName().c_str());
The copy has no friends, so getFriends()->at(0) returns an uninitialized pointer, because you didn't zero the array in the Person constructor:
Persons::Persons() {
Persons::persons_count = 0;
}
The code is a really fragile design, stop using a fixed-size array that contains uninitialized pointers. Stop adding copies to the Persons collection. Either define a correct copy constructor or prevent copies from being made.
The API of your class is extremely error-prone. The std::array should be an implementation detail, not part of the API. That would allow you to replace it with a different data structure without rewriting all the code that uses the Person class. Instead of getFriends() returning the raw array you could have a getFriend(int) function, which can do some error-checking:
Person* Person::getFriend(int n) {
if (n < friend_count)
return friends[n];
else
throw std::range_error("No such friend");
}
And similarly for getPersons, so you would do:
printf("\n%s", p.getPerson(0).getFriend(0)->getName().c_str());
^^^^^^^^^^^^ ^^^^^^^^^^^^
and it would throw an exception instead of dereferencing a garbage pointer.

Using unordered_map with dyanmically-allocated user-defined class

So I've got a class, "Room", which has the following code:
class Room
{
public:
Room(string name, string desc): name(name), desc(desc) {}
void operator=(const Room room)
{
name = room.name;
desc = room.desc;
}
string getName(); //returns this.name
string getDesc(); //returns this.desc
private:
string name; //name of the room
string desc; //description of the room
};
I've got a global variable in my main.cpp of type unordered_map, like this:
unordered_map<string, *Room> rooms; //Room's name is the key
And I want to allocate Rooms on the fly in a function and add them to my map. I attempted to do it like this:
void builder()
{
Room* room = new Room("Name", "Desc");
rooms[room->getName()] = room;
}
...But I'm getting all kinds of compiler warnings. I figured it would be something to do with iterators or hashing, or I'm not using pointers correctly (which are probably all true), but mostly it seems unordered_map doesn't like being parametrized with Room or *Room. Am I missing something?
There are some syntax errors as *Room. I have some tips
#include <string>
#include <memory>
#include <unordered_map>
using namespace std;
class Room
{
public:
Room(string name, string desc)
: name(name) // This syntax is called initializer list
, desc(desc)
{
}
void operator = (const Room room)
{
name = room.name;
desc = room.desc;
}
string getName() { return name; }
string getDesc() { return desc; }
private:
string name; //name of the room
string desc; //description of the room
};
// Without using unique_ptr you have a memory leak
// because the is not explicitly called the delete operator on pointers
unordered_map<string, std::unique_ptr<Room> > rooms;
void builder()
{
Room* room = new Room("Name", "Desc");
rooms[room->getName()].reset (room);
}

Copying vector and remaining the memory adress of the members

I try to read in some data of teams and players and change the data.
The Player class members have pointers to the teams from the Team class, while the Teams have a playerlist filled with pointers to the players.
The problem comes with the readin()-function which reads in the team and player data from a txt.file and returns a vector of teams. After the readin-process the pointers created in the readin-process seem to direct to wrong addresses.
So reading out data from the vector of teams returned by the read-in function is not the same anymore than reading out the data from the pointers created in the read-in function.
Here is the general structur of the classes:
#include <iostream>
#include <vector>
#include <string>
class Player;
class Team
{
public:
std::vector<Player*> getplayerlist(){
return playerlist;
}
void setteamname(std::string x){
teamname = x;
}
std::string getteamname(){
return teamname;
}
void Team::addPlayer(Player* x){
playerlist.emplace_back(x);
};
private:
std::vector<Player*> playerlist;
std::string teamname;
};
class Player
{
public:
Team* getpteam(){
return pteam;
}
void setpteam(Team* x){
pteam = x;
}
void setinformation(std::string x, int y){
name= x;
id = y;
}
private:
int id;
std::string name;
Team* pteam;
};
And here is the structure of the read-in function
vector<Team> readin(){
//some readin stuff
//if the line contains the teamname
Team team;
team.setteamID_(a);
team.setteamname_(word2);
Teamvector.emplace_back(team);
//if the line contains player data
//add the player to the team created before
Player* player = new Player();
player->setinformation_(b, word1, lname, str, &Teamvector[a-1]);
Teamvector[a-1].addPlayer(player);
return Teamvector;
}
But if i compare the addresses of &Teamvektor[0] and
vector <Team> team = readin();
&team[0]
they are different. How can i "remain" the adress of the stored teams ?
You are returning std::vector by value: this means container is being copied, thus elements have same values but different addresses in memory.
There are few ways how to handle this. For example:
Create container and fill it in your method:
std::vector<Team> v;
void readin_fillVector( vector<Team>& v) {
//.. v.push_back( team);
}
Pass iterator
void readin_fillVector( std::back_insert_iterator< std::vector<Team> > it) {
//.. (*it)++ = team;
}

How can I create multiple items with one class in C++?

I have the class Furniture with:
Furniture.h:
#include <iostream>
#include <string>
using namespace std;
class Furniture {
public:
Furniture();
~Furniture();
void setname(string name);
void setprice(double price);
double getprice();
string getname();
virtual void printSpecs();
private:
string name;
double price;
protected:
static int NumberOfItems;
int Id;
};
furniture.cpp:
#include "furniture.h"
Furniture::Furniture() {
}
Furniture::~Furniture() {
}
void Furniture::setname(string name) {
this->name = name;
}
string Furniture::getname()
{
return this->name;
}
void Furniture::setprice(double price) {
this->price = price;
}
double Furniture::getprice() {
return this->price;
}
void Furniture::printSpecs() {
cout<<"Price: "<<this->price<<endl;
cout<<"Name: "<<this->name<<endl;
}
int main() {
Furniture *model = new Furniture();
model->setname("FinalDestiny");
model->setprice(149.99);
model->printSpecs();
delete model;
}
Everything works fine but I want to add multiple furniture items with the same class and just update the NumberOfItems. Is there any way to do that?
Also, is my code ok? I mean, how can I improve it? I'm quite new to OOP and I'd like to learn some good practices.
Thanks.
The idea is conceptually broken. You cannot do that; you really need different objects.
Alternatively, if you really want to have multiple identical items, you can create one item and create multiple pointers to it, and maintain a separate count for the number of active items. A shared_ptr does that for instance.
That said, your code shouldn’t use pointers at all, this is a common anti-pattern in C++ code. Furthermore, your code probably shouldn’t have setters, provide a proper constructor instead:
int main() {
Furniture model("FinalDestiny", 149.99);
model.printSpecs();
}
Much shorter, simpler, and no possiblity of leaking memory.
To keep track of the number of items, you can update the number of items in the constructor:
Furniture::Furniture() {
Id = NumberOfItems++;
}
and decrement in the destructor if you want:
Furniture::~Furniture() {
NumberOfItems--;
}
To access the item by Id, you need to have an extra manager class or use a map:
std::map<int,Furniture*> items;
which you can pass as parameter to the constructor and update it there:
Furniture::Furniture(std::map& items) {
Id = NumberOfItems++;
items[Id] = this;
}
And, outside, you can simply retrieve items with:
Furniture* f = items[3];
I would write in this way
#include <iostream>
#include <string>
using namespace std;
class Furniture {
public:
Furniture(string name = "", double price = 0)
: name(name), price(price), Id(NumberOfItems++)
{}
Furniture(const Furniture &f)
: name(f.getname()), price(f.getprice()), Id(NumberOfItems++)
{}
void setname(string name) { this->name = name; }
void setprice(double price) { this->price = price; }
double getprice() const { return price; }
string getname() const { return name; }
virtual void printSpecs() {}
private:
string name;
double price;
protected:
static int NumberOfItems;
int Id;
};
int Furniture::NumberOfItems;
int main_furniture(int, char **)
{
Furniture a("product 1", 100);
Furniture x(a), y(a), z(a);
}
I've inlined just to simplify. What's interesting to you should be the copy constructor implementation, and (OT) you forget the const on getter...
Just increment NumberOfItems in the constructor, and decrement it in the destructor.
Store the furniture instances in an array or better in a vector. You can access them with an index or iterator. The NumberOfItems field doesn't belong in the furniture class, an instance of furniture shouldn't know about how many furniture items there are in the system. Use the size () method from vector to get the furniture item count.

dynamic memory allocation

how allocate memory dynamically for the array of stucture....
eg:
class students
{
struct stud
{
int r_no;
char name[20];
}*s;
}
how to allocate memory dynamically for *s...
First of all, this is not the way of doing it, as you could use a vector of stud, for instance. With the code as you have it, it would be something like:
class students
{
public:
struct stud ... *s;
students() // ctor
{
s = new stud[100]; // say you have 100 students
// from now on you can use s[0], s[1], etc. in the class
}
};
However, what you should be using is kind of an STL vector or list:
class students
{
public:
struct stud ... ;
std::vector<stud> my_students;
students() // ctor
{
stud aStudent = {0, "Student Name"};
my_students.push_back(aStudent); // add a new student.
}
};
Why the extra wrapping of the struct in a class with nothing but a pointer?
Anyway, in C you'd do something like this:
struct stud
{
int r_no;
char name[20];
} *s;
size_t num_students = 4711;
s = malloc(num_students * sizeof *s);
Then it'd be prudent to go through and make sure all those individial structs are initialized, of course.
If you mean this to be C++, you should write constructors that take care of that, and use a new[] to allocate an array of structures.
You should make use of standard components when you can. Here std::string and std::vector would help you.
struct Student
{
int r_no;
std::string name;
};
typedef std::vector<Student> StudentList;
With such an approach, there is no point in wondering how to dynamically allocate memory. Everything's taken care of !
EDIT:
I simply typedef'ed StudentList because to me, adding more functionality to it would have been unrelated to the question.
Clearly, the last line can be replaced with a true class definition:
class StudentList
{
public:
// Add your own functionalities here
private:
std::vector<Student> m_students;
};
class students
{
public:
students(size_t noOfStudents):m_noOfStudents(noOfStudents)
{
s = new stud[m_noOfStudents];
}
~students()
{
delete s;
}
private:
struct stud
{
int r_no;
char name[20];
}*s;
size_t m_noOfStudents;
}