Using unordered_map with dyanmically-allocated user-defined class - c++

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

Related

Pass data from object in class A to class B

New to classes and objects in c++ and trying to learn a few basics
I have the class TStudent in which the Name, Surname and Age of student are stored, also I have the constructor which is accessed in main and inserts in the data.
What I want to do is: having the class TRegistru, I have to add my objects data in it, in a way that I can store it there, then I could save the data in data.bin and free the memory from the data, then I want to put the data back in the class and print it out.
The question is: In what way & what is the best way to add my objects in the second class, so that I could eventually work with them in the way I've described in the comments, so that I won't have to change nothing in main
Here's my code so far:
#include <iostream>
using namespace std;
class TStudent
{
public:
string Name, Surname;
int Age;
TStudent(string name, string surname, int age)
{
Name = name;
Surname = surname;
Age = age;
cout <<"\n";
}
};
class TRegistru : public TStudent
{
public:
Tregistru()
};
int main()
{
TStudent student1("Simion", "Neculae", 21);
TStudent student2("Elena", "Oprea", 21);
TRegistru registru(student1);//initialising the object
registru.add(student2);//adding another one to `registru`
registru.saving("data.bin")//saving the data in a file
registru.deletion();//freeing the TRegistru memory
registru.insertion("data.bin");//inserting the data back it
registru.introduction();//printing it
return 0;
}
Hence the question is about passing data from A to B, I will not comment on the file handling portion.
This can be done in multiple ways, but here is one of the simplest and most generic. By calling TRegistru::toString() you serialize every TStudent added to TRegistru into a single string which then can be easily written to a file.
Demo
class TStudent
{
public:
std::string Name, Surname;
int Age;
std::string toString() const
{
return Name + ";" + Surname + ";" + to_string(Age);
}
};
class TRegistru
{
public:
void add(const TStudent& student)
{
students.push_back(student);
}
void deletion()
{
students.clear();
}
std::string toString() const
{
std::string ret{};
for(const auto& student : students)
{
ret += student.toString() + "\n";
}
return ret;
}
std::vector<TStudent> students;
};

C++: how to make getters and setters work with an empty constructor

First of all, I have only learned a little bit of Java before. It's been only a few days since I started getting friendly with C++ so please don't take this question so basic and please don't degrade my question.
I made a simple source code as follows:
#include <iostream>
using namespace std;
class Car {
public:
void setBrand(string name);
void setPrice(double price);
string getBrand();
double getPrice();
Car();
Car(string name);
Car(string name, double price);
private:
string name;
double price;
};
Car::Car() {
}
Car::Car(string name) {
name = name;
}
Car::Car(string name, double price) {
name = name;
price = price;
}
void Car::setBrand(string name) {
name = name;
}
void Car::setPrice(double price) {
price = price;
}
string Car::getBrand(void) {
return name;
}
double Car::getPrice(void) {
return price;
}
int main() {
Car car;
car.setBrand("Nissan");
car.setPrice(30000);
cout << "Brand: " << car.getBrand() << endl;
cout << "Price: " << car.getPrice() << endl;
return 0;
}
I wanted to make a code that creates an empty instance of a class called Car, set the field values later and print them out on the console.
The code did not make any errors during the compile, but the result I see was totally different from what I expected. It didn't show the brand name and the price was looking even weird, as follows.
Brand:
Price: 6.95322e-310
Somebody help me out! Thank you very much indeed in advance.
The problem you have is that you override the member names with function parameters. You can use this-> to make it explicit or name the member differently.
For example:
void Car::setBrand(string name) {
this->name = name;
}
Or:
void Car::setBrand(string new_name) {
name = new_name;
}
In your constructor and setters, you make no differentiation between the local parameter and the class member.
name = name;
Both the function parameter and the class member are called name. Currently the compiler is assigning the parameter value to itself, and not affecting the class member at all. This is because the function parameter is in a more immediate scope.
Possible solutions:
Specify this when referring to the class member: this->name = name;.
Rename the function parameter: name = _name;.
For the constructor, use initializer lists:
Car::Car(string name, double price)
: name(name)
, price(price)
{ }
There's too much wrong with your code to describe it in prose, so let me present a fixed implementation, and I leave it to you to spot the difference:
#include <string>
class Car
{
private:
static constexpr double kNoPrice = -1.0;
static constexpr const char* kNoName = "[no name]";
public:
// Main constructor: constructs a car with the given name and price.
Car(std::string name, double price)
: name_(std::move(name))
, price_(price)
{}
// Convenience constructors:
Car() : Car(kNoName, kNoPrice) {}
Car(std::string name) : Car(std::move(name), kNoPrice) {}
// Accessors:
const std::string& getBrand() const { return name_; }
void setBrand(std::string name) { name_ = std::move(name); }
double getPrice() const { return price_; }
void setPrice(double price) { price_ = price; }
private:
std::string name;
double price;
};
Some random notes, in no particular order:
Use correct names. It's std::string, not string, mate or buddy. Never ever be abusing namespace std.
Include headers for external names that you need.
Reading uninitialized values is undefined behaviour, so none of your constructors should leave fields uninitialized (like price_).
Give private members consistent names (e.g. foo_ in my example).
Accessors should be const-correct.
Convenience constructors should delegate to one single work-horse constructor.
Pick sensible defaults for initial values of defaulted fields and make them discoverable.
Use move semantics when taking ownership of dynamically managed data (strings, dynamic containers, etc.).

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.

C++ Insert into List of Objects

can someone tell me how can i add data into my list of class with C++
please ?
#include <string>
class Person
{
private:
std::string Name;
public:
Person();
~Person();
std::string GetName()
{
return Name;
}
std::string SetName(std::string name)
{
name = Name;
return name;
}
};
void main()
{
list<Person> lp = new list<Person>();
Person p = new Person();
p.Name = "Smith";
lp.insert(p);
}
this is my attempt. Can someone correct me or give me another answer ? Thanks
If by some chance you are using c++11 or greater, consider using:
list.emplace_back()
or
list.emplace_front()
which handles construction and insertion. Of course this would be even better if you had a Person constructor that took a string argument.
Person::Person(std::string& name) { Name = name; }
In which case you could do:
lp.emplace_back("Smith");
list.insert() takes an iterator, i.e. position at which you want to insert the new element.
You can also do
list.push_back(p);
This inserts 'p' at the end of the list.
If you do
list.push_front(p);
insertion happens at the head of list.
First of all, since you're coding in c++ your main function should return an int:
int main()
Second, you're using the new keyword on an object that isn't a pointer, correct that:
// This would perhaps be better:
list<Person*> lp;
Person *p = new Person; // remember to delete p; later
I don't see that you have included <list> header and using std. You need that in order to use list. Or at least write std::list or using std::list;
You are trying to assign to a private class member, either declare it public, or use a setter function:
p->Name = "Smith"; // remember for pointers, use the '->' or *(p).Name
emplace_back() is maybe what you are looking to use (since c++11):
lp.emplace_back(p);
// otherwise, specify an iterator as first argument for insert:
// lp.insert(lp.end(), p);
Why isn't your SetName only void and needs to return something? If you want to return the new Name, you also should have
Name = name;
return Name;
You have switched it.
Also, you don't need any of the new and every main function should return int value (usually 0 if no error occurred).
FIXED CODE:
#include <string>
#include <list>
class Person
{
private:
std::string Name;
public:
//Person();
//~Person();
std::string GetName()
{
return Name;
}
void SetName(std::string name)
{
Name = name;
}
};
int main()
{
std::list<Person> lp;
Person p;
p.SetName("Smith");
lp.push_back(p);
return 0;
}
You need to include lists in order to use them, and you can't assign a private member directly. Also, your declarations are wrong - for this I would recommend some cpp tutorials and reading the documentation.

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.