Adding data inside unordered_map inside a class - c++

I have a class Citydata, defined in .hh file
struct City_details {
string name;
int taxrate;
};
class Citydata {
public:
bool add_data(string id, string name, int taxrate)
//other member functions...
private:
unordered_map<id, City_details> info_map;
I have trouble implementing the add_data function in .cc file. Here is my try.
bool Citydata::add_data(string id, string name, int taxes) {
if ( info_map.find(id) == info_map.end()) {
City_details dataload;
dataload.name = name;
dataload.taxrate = taxes;
info_map[id] = dataload;
return true;
}
else return false;
}
When I test this, I cannot see any data added the way I wanted. Instead I see one completely empty entry (default empty values), and one entry with right id (the key) but no data added to the parameters. Each time I call add_data, it creates similar pair of one completely empty key-data member, then one with right id and otherwise empty data.
The original program is much longer than this, so problem might persist there too, but I wonder if my approach is flawed by design.

I have made some modifications that makes your program work.
First you should replace unordered_map<id, City_details> info_map; with unordered_map<std::string, City_details> info_map;
Second you had a missing semicolon ; for you add_data member function declaration inside the class which you might have missed while copy pasting the code here on Stackoverflow.
Third i have avoided the use of using namespace std;.
main.cpp
#include <iostream>
#include "file.h"
int main()
{
Citydata c1;
c1.add_data("id1","Georgia", 34);
c1.add_data("id2", "California", 32);
c1.add_data("id3","Texas", 23);
//lets print out the element of info_map
for(auto &it:c1.info_map)
std::cout << it.first <<" "<<it.second.name<<" "<<it.second.taxrate<<std::endl;
return 0;
}
file.h
#pragma once
#include <unordered_map>
#include <string>
struct City_details {
std::string name;
int taxrate;
};
class Citydata {
public:
bool add_data(std::string id, std::string name, int taxrate);
//other member functions...
//private:
std::unordered_map<std::string, City_details> info_map;//the first tempate argument should be int and not id
};
file.cpp
#include "file.h"
bool Citydata::add_data(std::string id, std::string name, int taxes) {
if ( info_map.find(id) == info_map.end()) {
City_details dataload;
dataload.name = name;
dataload.taxrate = taxes;
info_map[id] = dataload;
return true;
}
else return false;
}

Related

Why is my class constructor being called twice?

I've got a base class Container with a derived class Player_Inventory. There can only be one Player_Inventory so my code throws an exception if for some reason a second one is created.
The problem I'm having is that my code is failing my test as it throws the exception even on what is supposed to be the very first construction of the Player_Inventory class. I've debugged the code and two things are happening which I don't quite understand - the number attribute is not tracked by the debugger (at least not in the GUI on VSC), and it seems that right after hitting the first REQUIRE statement, the constructor is called again, thus triggering the exception.
Can anyone help?
After rewriting my constructor method, I'm still getting a similar error.
My revised code is as follows:
containers.h
#include<iostream>
#include<string>
#include<vector>
class Item { // Placeholder class for items
public:
std::string name;
Item(std::string n) : name{n} {};
};
class Container {
protected:
std::string name;
std::string description;
std::vector<Item> contents;
public:
Container(std::string, std::string);
std::string get_name() {return name;}
std::string get_description() {return description;}
std::vector<Item> get_contents() {return contents;}
};
containers.cpp (there are more methods defined in this file which aren't used here)
#include<iostream>
#include<string>
#include "containers.h"
Container::Container(std::string n, std::string desc) : name{n}, description{desc} {};
player_inventory.h
#include "containers.h"
class Player_Inventory : public Container {
public:
static int number;
Player_Inventory(std::string, std::string);
};
player_inventory.cpp
#include<iostream>
#include<stdexcept>
#include "player_inventory.h"
Player_Inventory::Player_Inventory(std::string n, std::string desc): Container(n, desc) {
number += 1;
if (number > 1){
throw std::invalid_argument("You can only have one inventory!");
}
};
int Player_Inventory::number = 0;
test_file.cpp
#include "../lib/Catch2/catch.hpp"
#include "player_inventory.h"
#include<iostream>
#include<string>
#include<vector>
SCENARIO("A player can have an inventory.") {
WHEN("A player inventory is created.") {
Player_Inventory myInventory("My Inventory", "Inventory for the player");
THEN("The created inventory has the correct attribute values.") {
REQUIRE(myInventory.get_name() == "My Inventory");
REQUIRE(myInventory.get_description() == "Inventory for the player");
REQUIRE(myInventory.get_contents().empty());
} // The code works fine when only up to here is included
AND_THEN("Only one player inventory can exist.") { // as soon as this line is included it tries to create another player_inventory object, causing the fail
REQUIRE_THROWS((Player_Inventory myOtherInventory("Second Inventory", "Testing for another one"))); // These two lines were not included but I've included them here as this is the test I wanted to run
REQUIRE(myInventory.get_number() == 1);
}
}
}
Not sure if related, but that's how you should call the Base constructor:
Player_Inventory(std::string n, std::string desc) : Container(n, desc) {
}

Problem constructing a base class from within a subclass constructor

I have 2 classes. Since Doctor will be considered as Employee, I should be using Employee class functions in Doctor class. Only extra thing that Doctor class has is TITLE. Basically, What I tried is I wanted to send value to Doctor's constructor,set title then send remained value to Employee's class ;however, I could not. This is what I have done so far,
employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
class Employee {
private:
int ID;
char *firstname;
char *lastname;
int telno;
char *adress;
char *mail;
int salary;
public:
Employee();
Employee(int,char *,char*,int,char*,char*,int);
char* getfmame();
char* getlname();
char* getadress();
char* getmail();
int getID();
int gettel();
int getsalary();
void printall();
};
#endif
Employee.cpp
#include <iostream>
#include "employee.h"
using namespace std;
Employee::Employee() {
firstname = "Empty";
ID=0;
firstname="Empty";
lastname="Empty";
telno=0;
adress="Empty";
mail="Empty";
salary=0;
}
Employee::Employee(int id,char * first,char* last,int tell,char* adres,char* email,int salar){
ID=id;
firstname=first;
lastname=last;
telno=tell;
adress=adres;
mail=email;
salary=salar;
}
char* Employee::getfmame(){ return firstname; }
char* Employee::getlname(){ return lastname; }
char* Employee::getadress(){ return adress; }
char* Employee::getmail(){ return mail; }
int Employee::getID(){ return ID; }
int Employee::gettel(){ return telno; }
int Employee::getsalary(){ return salary; }
void Employee::printall(){
cout<<endl<<"EMLOYEE INFORMATION"<<endl<<"------------------"<<endl;
cout<<endl<<"ID :"<<ID<<endl<<"FIRST NAME: "<< firstname <<endl<<"LAST NAME: "<< lastname << endl << "TELEPHONE NUMBER: "<<telno<<endl<<"ADRESS: "<<adress<<endl<<"MAIL: "<<mail<<endl<<"SALARY: "<<salary<<endl;
}
Doctor.h
#ifndef DOCTOR_H
#define DOCTOR_H
#include "Employee.h"
using namespace std;
class Doctor :Employee {
public:
enum title {Intern=0,Practitioner=1,Assistant=2,Specialist=3,Docent=4,Professor=5,None=6};
Doctor();
Doctor(title a,int id,char * first,char* last,int tell,char* adres,char* email,int salar);
};
#endif
Doctor.cpp
#include <iostream>
#include "Doctor.h"
#include "Employee.h"
using namespace std;
Doctor::Doctor() {
title tit = None ;
}
Doctor::Doctor(title a,int id,char * first,char* last,int tell,char* adres,char* email,int salar) {
title tit=a;
Employee(id,first,last, tell,adres,email,salar);
printall();
cout<<"typed";
}
Main.cpp
#include <iostream>
#include "employee.h"
#include "doctor.h"
using namespace std;
int main(){
Doctor a=Doctor(Doctor::None,12,"a","b",0550550505,"8424 str nu:5","#hotmail",5000);
return 0;
}
Subclass construction in C++ works so that the base class object must be constructed when the subclass' constructor body is executed:
class A {
/* etc. etc. */
public:
void do_stuff();
};
class B : public A {
B() {
// at this point, an A has already been constructed!
A::do_stuff();
}
};
Note that in this example, since we haven't chosen an explicit constructor for the A instance, the default constructor, A::A(), will be used; and if that constructor is unavailable - we get a compilation error. The fact that a constructor for A has been called is what allows us to then use methods of class A - like A::do_stuff() in the example above.
But - how can we specify a different constructor before the body of the B constructor? Or in your case, how can we use the appropriate constructor for Employee before the body of the Doctor constructor?
The answer was suggested by #user4581301: You need to use an member initializer list. Initializations/constructions on this list are performed before the body, and may include the underlying class. I'll demonstrate with a simplified example. Let's suppose an Employee only has an id and a Doctor only has an additional title.
class Employee {
protected:
int id_;
public:
Employee(int id) : id_(id) { };
int id() const { return id_; }
};
class Doctor : public Employee {
protected:
std::string title_;
public:
Doctor(int id, std::string title) : Employee(id), title_(title) { };
const std::string& title() const { return title_; }
};
So, when a Doctor is being constructed, it constructs its underlying Employee instance using the id it got. The constructor body is used for more complex code beyond simple member initializations.
PS:
You might want to initialize the title_ member with std::move(title) rather than just title, see this question for details.
It's confusing when a constructor has more than two or three parameters with compatible types - users are likely to confuse them with each other. You might consider default values for most fields and setting them after construction, or alternatively, using a builder pattern.
address, with two d's, not adress.
Unless you plan on editing char* fields in-place, use const char *.
They way you've written your classes, Doctor methods would not have write acesss to Employee methods; make sure that's what you intended.
I have some other nitpicks but I'll stop now...

How to access protected structure variables from other structures

So I am writing a C++ program that will mimic a library card catalog. I defined a struct for card and all the info on each card, as well as a working vector and iterator to access/print all variables on specified card using a global void function.
Now, I want to move that void function within a newly defined struct, Catalog that handles all the methods for dealing with library cards, like insert/push_back, search or remove/erase/pop_back. I also want my variables under card to be protected, as I'm constantly told it's good coding practice to have your class/struct variables to be private (I did protected for other classes inherited).
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
struct Card
{
public:
Card(string title, string name)
{
this->title = title;
this->name = name;
}
//protected:
string title = "Unknown";
string name = "Unknown";
};
vector<Card> test;
vector<Card>::iterator it;
void showCard(vector<Card> test)
{
for (it = test.begin(); it != test.end(); it++)
{
if (it->title != "Unknown")
{
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 /
T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
test.push_back(book1);
test.push_back(book2);
test.push_back(book3);
showCard(test);
getchar();
return 0;
}
I guess my question is, how could I call Catalog struct from main to then access the protected variables under Card in order to print the protected variables?
Couldn't be as simple as listing friend struct Card in catalog would it?
Edit: I played around and found that friend struct Catalog under Card was able to get rid of errors in the void function for the protected variables it tried accessing. I still am working to have main pass through catalog, though I had all objects in main defined as Card.
I suppose I could try a setCard() called in main, defined in Catalog where it uses vector to refer to the variables protected.
There are multiple ways to do it, and the right way depends on the context. Here are some possible solutions, from the easiest/hackiest to the most verbose/hardest (not an exhaustive listing):
1. Just make everything public
...
struct Card{
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
string title = "Unknown";
string name = "Unknown";
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->title != "Unknown"){
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
While that does solve the problem, it isn't a good solution. If you would ever want to change the name of member title to main_title you will have quite a pain in doing so, because you will have do edit every single occurrence of title and that can get messy quickly.
2. Make void showCard(vector<Card> test) a friend of struct Card
If void showCard(vector<Card> test) is friend of Card then it will have access to all protected and private members of Card as if they were public. This is a nice solution because only void showCard(vector<Card> test) would have access to these protected members.
Because you can only be friend of previously declared functions, you would need to forward declare the function void showCard(vector<Card> test) before the declaration of Card.
However, because void showCard(vector<Card> test) takes a vector<Card> argument, the class Card needs to be forward declared before the forward declaration of the function.
...
struct Card;
void showCard(vector<Card> test);
struct Card{
public:
friend void showCard(vector<Card> test);
Card(string title, string name){
this->title = title;
this->name = name;
}
protected:
string title = "Unknown";
string name = "Unknown";
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->title != "Unknown"){
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
3. Create getters and setters for Card
This is one of the canonical implementations. Every time you make a member private/protected you provide a get_member and a set_member methods for it.
That way everyone can access the member, however, they only can access it if they use those methods. You can even create getters/setters for members that don't exist (i.e. you compute them when you need them).
Since the code speaks more than words, here is an implementation:
...
struct Card{
protected:
string title = "Unknown";
string name = "Unknown";
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
string get_title(){
return this->title;
}
void set_title(string new_title){
this->title = new_title;
}
string get_name(){
return this->name;
}
void set_name(string new_name){
this->name = new_name;
}
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->get_title() != "Unknown"){
printf("%s\n", it->get_title().c_str());
printf("%s\n", it->get_name().c_str());
}
}
}
If you ever would want to change the name of the member title to main_title, you would only have to edit get_title and set_title and all your code would keep working as if you didn't change it at all. You could even delete that member or do anything else (like fetching it from a database) because the only place where it's existence and name matters is inside get_title and set_title. Without getters and setters, you would need to edit every single occurrence of title in order to do that.
Getters and setters are also wonderful places to improve the const correctness of your code, making it more robust and efficient. A const-correct get/set pair would look something like this:
const string& get_title() const {
return this->title;
}
void set_title(const string& new_title){
this->title = new_title;
}
And a pair for a non-existent member would look like this:
#include <string>
#include <algorithm>
#include <iterator>
string get_title_and_name(){
// Concatenates the title and name
return this->title + " / " + this->name;
}
void set_title_and_name(string new_string){
// Splits the string between a title and a name
std::size_t split_point = 0;
split_point = new_string.find('/');
this->title = new_string.substr(0, split_point);
// We don't want to include the char '/' of
// the new_string in this->name, so use
// (split_point + 1) instead of split_point
this->name = new_string.substr(split_point + 1, new_string.size() - (split_point + 1));
}
While this solution may be more verbose than others, it is also more flexible.
A suggested solution
We could modify solution 3 by creating a new struct Catalog and putting void showCard(vector<Card> test) inside it. This isn't a usual solution, put it open the possibility for we to get rid of some global variables (global variables are almost always evil) and hide the fact that we are using a vector<Card> to keep Cards (we could use a hashmap instead of a vector and that would work as well, so that other code don't need to know which one we chose of the two).
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
// As in solution 3
struct Card {
protected:
string title = "Unknown";
string name = "Unknown";
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
// Right now we only need getters,
// but we could have setters as well
// (the names are in camelCase to follow
// showCard() naming convention)
string getTitle(){
return this->title;
}
string getName(){
return this->name;
}
};
struct Catalog {
protected:
// This one was a global variable previously
// Also we don't specify a default value
// for it here, we will do that in the constructor
vector<Card> test;
public:
Catalog(){
// The start value of test will be a empty vector
this->test = vector<Card>();
}
// We moved void showCard(vector<Card> test) to here
void showCard(){
// This is a local variable now
vector<Card>::iterator it;
// For loop as in solution 3
for (it = this->test.begin(); it != this->test.end(); it++){
if (it->getTitle() != "Unknown"){
printf("%s\n", it->getTitle().c_str());
printf("%s\n", it->getName().c_str());
}
}
}
// A new method for adding cards,
// because external code shouldn't care
// about how we add or remove card or even
// if we store cards in this machine or in a web server
void addCard(Card card){
this->test.push_back(card);
}
};
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 / T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
Catalog catalog;
catalog.addCard(book1);
catalog.addCard(book2);
catalog.addCard(book3);
// We could even do something like
// catalog.addCard({ "My Horse and wally", "Jason Weber" });
// thankfully to the new addCard method.
// We wouldn't even need to declare book1, book2 and book3
// if we used it that way
catalog.showCard();
getchar();
return 0;
}
After you end writing your program, you may be interested in showing it on Code Review in order to receive insights about how other people would approach the same code and learn how people more experienced or with a different background would write such code.
#obidyne, welcome to StackOverflow. If your goal is to keep the members protected but still be able to show them off (as a formatted string), you could implement a public method showCard, rename your other function showCards and call the public method for each object of the vector.
Just an example (using your own code):
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
struct Card
{
public:
Card(string title, string name)
{
this->title = title;
this->name = name;
}
void showCard()
{
if (this->title != "Unknown")
{
printf("%s\n", this->title.c_str());
printf("%s\n", this->name.c_str());
}
}
protected:
string title = "Unknown";
string name = "Unknown";
};
vector<Card> test;
vector<Card>::iterator it;
void showCards(vector<Card> test)
{
for (it = test.begin(); it != test.end(); it++)
{
it->showCard();
}
}
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 /
T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
test.push_back(book1);
test.push_back(book2);
test.push_back(book3);
showCards(test);
getchar();
return 0;
}

Using a string before .getName(); for c++

I would like to use a string before .getName()
Enemy Troll(Troll, 250, 30);
string enemyName;
enemyName = Troll;
enemyName.getName(); //this is causing the error... "No member named 'setHP' in 'std::__1::basic_string<char>'"
I want to be able to get the same results as using Troll.getName(); but instead use a string.
There is no built in way in C++ to use a string to look up a variable of the same name in C++.
What you have to do is create the appropriate data structure for yourself. One way to do that would be to use a map.
#include <map>
std::map<std::string, Enemy> my_map;
...
Enemy trump("Trump", 250, 30);
my_map["Trump"] = trump;
...
std::string name = ...;
Enemy some_enemy = my_map[name];
You can store all the instances by name as a static data member in a map for example. But there should be better ways to do it depending on the complete design of your program. But since you didn't provide all the context I'm just going to show a generic example. With some issues like non thread safety, missing null checks, not dealing with duplicates, etc.
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Enemy{
public:
std::string m_name;
int m_hp;
int m_dmg;
static std::map<std::string,Enemy*> s_instances;
Enemy(const std::string& name, int hp, int dmg)
: m_name(name),
m_hp(hp),
m_dmg(dmg)
{
s_instances[name] = this;
}
~Enemy()
{
s_instances.erase(m_name);
}
const std::string& getName() const
{
return m_name;
}
static Enemy* getInstanceByName(const std::string& name)
{
Enemy* result = nullptr;
auto iter = s_instances.find(name);
if(iter!=s_instances.end()) result = iter->second;
return result;
}
};
std::map<std::string,Enemy*> Enemy::s_instances;
void doStuff()
{
Enemy* instance = Enemy::getInstanceByName("Ork");
std::cout << instance->getName();
}
int main() {
Enemy ork("Ork",300,20);
doStuff();
return 0;
}

C++ Cannot use push_back on list containing custom structs

We are making a list that hold info on boardgames (name, year, score). We scan the info out of a .csv file, make a struct based on that info and then add the struct to a list. We keep doing this untill the document is done reading. Problem is that the push_back method of the list doesn't work. Here's the header of the list class:
NOTE BoardGame is the custom struct. BoardGame(wstring name, int year, float score).
#pragma once
#include "GameEngine.h"
#include "BoardGame.h"
#include <list>
class BoardGameList
{
public:
BoardGameList() {}
virtual ~BoardGameList() {}
// Methods
void Load(const tstring& fileName);
// Members
private:
std::list<BoardGame> m_Games;
};
The cpp file. Maybe I made the list the wrong way?
#include "BoardGameList.h"
#include <fstream>
void BoardGameList::Load(const tstring& fileName)
{
tifstream file(fileName);
tstring line;
if(!file)
{
GAME_ENGINE->MessageBox(_T("Error: The file could not be found!"));
}
else
{
tstring name;
tstring year;
tstring score;
while(!(file.eof()))
{
getline(file,line);
year = line.substr(0,4);
score = line.substr(5,5);
name = line.substr(11,line.find(_T("\n")));
float numberScore = std::stof(score);
int numberYear = std::stoi(year);
m_Games.push_back(BoardGame(name,numberYear,numberScore));
}
}
}
Running the program triggers an error (unhandled exception) that leads me to the following code in the "list" class itself I think.
_Unchecked_iterator _Unchecked_end()
{ // return unchecked iterator for end of mutable sequence
return (_Unchecked_iterator(this->_Myhead, this));
}
Any ideas why I can't add stuff to my list? I tried adding something in the constructor to check if it maybe needed an element before I could add more but even then, using a breakpoint showed me that the memory could not be read.
Many thanks in advance.
EDIT: Header of BoardGame
#pragma once
#include "GameEngine.h"
struct BoardGame
{
BoardGame(tstring name, int year, float score);
//Methods
tstring operator<<(BoardGame rhs);
//Members
tstring m_Name;
int m_Year;
float m_Score;
};
What exception is being thrown? This is vital to debugging your problem.
Without that information my best guess is this line:
name = line.substr(11,line.find(_T("\n")));
Will throw an exception on any line without a trailing newline, or any line less than 11 characters long.