C++ Passing map from object - c++

I am building a simple program to 'learn as I go', this program takes a couple of text files and processors them, each line of the first text file is information about a person, and for each line an Object (of type Student (see below)) is created and added to a vector.
Within the Student objects is a map that stores that students marks.
I have a function within the student class that returns the map when called (or at least thats what im trying to do).
Currently this function is:
marksType Student::printMarks(){
return marks;
}
(Where marksType = std::map<string, float>)
and marks is the map of type marksType.
Then in my main function I have:
Student a = *qw;
studmarks = a.printMarks();
for (std::map<string, float>::iterator iter = studmarks.begin(); iter != studmarks.end(); iter++){
cout << "TEST" << endl;
}
Where qw is a pointer to a student object and studmarks is of type map<string, float>
The issue is that the cout doesn't get called so the iterator seems to skip (but the student object does have items in the marks map).
Heres the complete Student class
#include "Student.h"
using namespace std;
typedef std::map<string, float> marksType;
Student::Student(const string &name, int regNo) : Person(name){
marksType marks;
this->regNo = regNo;
}
int Student::getRegNo() const{
return regNo;
}
void Student::addMark(const string& module, float mark){
pair<marksType::iterator,bool> check;
check = marks.insert (pair<string,float>(module,mark));
if (check.second==false){
marks[module]=mark;
}
}
float Student::getMark(const string &module) const throw (NoMarkException){
if (marks.find(module) != marks.end()){
return marks.find(module)->second;
}
else throw NoMarkException();
}
float Student::getAverageMark() const throw (NoMarkException){
if (!marks.empty()){
float avgmark = 0;
for (marksType::const_iterator avgit=marks.begin(); avgit!=marks.end(); ++avgit){
avgmark = avgmark + avgit->second;
}
avgmark = avgmark/marks.size();
return avgmark;
}
else throw NoMarkException();
}
marksType Student::printMarks(){
return marks;
}
Oh and below is the part of the main function that adds marks to the students,
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
Student b = *it;
if (regno == b.getRegNo()){
found = true;
b.addMark(module, mark);
}
}
I know this works because when I use the getMark function it does work.

You are "adding marks" to copies of the students stored in vector students. Each of these copies only lives during one iteration of the loop and the result is that you are not modifying the vector's elements at all:
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
Student b = *it; // b IS A COPY
if (regno == b.getRegNo()){
found = true;
b.addMark(module, mark); // modify local copy of Student
}
}
To add them to the elements of the vector, use
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
if (regno == it->getRegNo()){
found = true;
it->addMark(module, mark);
}
}

Related

LinkedList sorting a-z

Requirements for this function: If the full name (both the first and last name) is not equal to any full name currently in the list then add it and return true. Elements should be added according to their last name. Elements with the same last name should be added according to
their first names. Otherwise, make no change to the list and return false (indicating that the name is already in the list).
//this function add nodes to the list
//return true if fullname isn't in the list. Else return false if fullname is in the list.
//should be added according to last name.
bool OnlineDating::makeMatch(const std::string& firstName, const std::string& lastName, const OnlineType& value)
{
Node* p = head;
//are these nodes already set to firstName and lastName in this function
Node first;
Node last;
Node* temp = nullptr;
//if the list is empty just insert the fullname and value to the list
if (p == nullptr) {
//add values to the empty list
insertToRear(firstName, lastName, value);
return true;
}
else {
// so this loop is to check if fullname is in the list but first sort in alphebetial order
//sure its added in alphebetical order
//traverse the list after knowing where head is
while (p != nullptr) {
//checking to make sure theres at least another node in the list
if (p->next != nullptr) {
//its not going through ig loop?
//these are used to check and alphebetically selected names
if (p->last > p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->next->last > p->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
//check if full name is already in the list
if (p->last == p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->first > p->next->first) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else {
//returns false if it passes through these checks
return false;
}
}
p = p->next;
}
}
}
Here is my main.cpp
int main()
{
OnlineDating clippersGonnaClip;
clippersGonnaClip.makeMatch("Kawhi", "Leonard", 2);
clippersGonnaClip.makeMatch("Paul", "George", 13);
clippersGonnaClip.makeMatch("Ivica", "Zubac", 40);
clippersGonnaClip.makeMatch("Reggie", "Jackson", 1);
clippersGonnaClip.makeMatch("Patrick", "Beverley", 21);
for (int n = 0; n < clippersGonnaClip.howManyMatches(); n++) {
string first;
string last;
int val;
clippersGonnaClip.confirmMatch(n, first, last, val);
cout << first << " " << last << " " << val << endl;
}
return 0;
}
honestly, I just want to create a ptr that will point to each node. Check as long as that node isn't pointing to nullptr, and sort the LinkedList in alphabetical order. finally link the nodes together using my *temp. Why won't it let me go through the if statements every time I compile I get a negative number? Every other function works for this program except this makeMatch(...).
Nothing in the requirements says anything about the data types you need to use. In this case using std::set makes most sense.
By providing a comparison operation on persons and a set you can guarantee uniqueness. Like this :
#include <set>
#include <string>
struct person_t
{
std::string name;
std::string last_name;
unsigned int age;
};
bool operator<(const person_t& lhs, const person_t& rhs)
{
if (lhs.last_name == rhs.last_name)
return lhs.name < rhs.name;
return lhs.last_name < rhs.last_name;
}
int main()
{
// set will ensure all persons are unique
std::set<person_t> persons;
persons.insert({ "Kawhi", "Leonard", 2 });
persons.insert({ "Paul", "George", 13 });
persons.insert({ "Ivica", "Zubac", 40 });
persons.insert({ "Reggie", "Jackson", 1 });
persons.insert({ "Patrick", "Beverley", 21 });
}

Print a string from a pointer to its member class

So I'm trying to print a string, but I get no output. However the other values in the class prints just fine.
In main I have a for loop that prints the the values for the Skill class. In Skill I have a pointer to the Ability class.
class Skill {
private:
Ability* ability;
public:
Ability* GetAbility() {
return ability;
};
}
It gets assigned in the constructor like this:
Skill::Skill(Ability* ability){
this->ability = ability;
}
The Ability class contains just a Name and a score.
class Ability {
private:
string name;
float score;
public:
Ability(string name, float score) {
this->name = name;
this->score = score;
};
string Name() { return name; }
float GetScore() { return score; }
};
Now in main I create a few skills and assign an ability to it. as is a container class that initializes a few ablities in a vector and I can get an ability based on its name.
Skill s* = new Skill[2]
s[0] = Skill(&as.GetAbility("Strength"));
s[1] = Skill(&as.GetAbility("Charisma"));
And then we print
cout << s[i].GetAbility()->Name() << " " << s[i].GetAbility()->GetScore();
However the only output I get is the score. No name what so ever and I can't figure it out. I've tried a few things, but still noting is printing. I'm sure I'm missing something simple that will make me facepalm, but in my defense I haven't written C++ in over 10 years. Thanks in advance.
EDIT: as.GetAbility looks like this:
Ability AbilityScores::GetAbility(string abilityName) {
for (int i = 0; i < abilityScores.size(); i++) {
if (abilityScores[i].Name() == abilityName) {
return abilityScores[i];
}
}
return Ability();
}
abilityScores is a vector
Your AbilityScores::GetAbility() method is returning an Ability object by value, which means it returns a copy of the source Ability, and so your Skill objects will end up holding dangling pointers to temporary Ability objects that have been destroyed immediately after the Skill constructor exits. So your code has undefined behavior.
AbilityScores::GetAbility() needs to return the Ability object by reference instead:
Ability& AbilityScores::GetAbility(string abilityName) {
for (int i = 0; i < abilityScores.size(); i++) {
if (abilityScores[i].Name() == abilityName) {
return abilityScores[i];
}
}
throw ...; // there is nothing to return!
}
...
Skill s* = new Skill[2];
s[0] = Skill(&as.GetAbility("Strength"));
s[1] = Skill(&as.GetAbility("Charisma"));
...
If you want to return a default Ability when the abilityName is not found, consider using std::map instead of std::vector:
private:
std::map<std::string, Ability> abilityScores;
AbilityScores::AbilityScores() {
abilityScores["Strength"] = Ability("Strength", ...);
abilityScores["Charisma"] = Ability("Charisma", ...);
...
}
Ability& AbilityScores::GetAbility(string abilityName) {
// if you don't mind Name() returning "" for unknown abilities...
return abilityScores[abilityName];
// otherwise...
auto iter = abilityScores.find(abilityName);
if (iter == abilityScores.end()) {
iter = abilityScores.emplace(abilityName, 0.0f).first;
}
return iter->second;
}
...
Skill s* = new Skill[2];
s[0] = Skill(&as.GetAbility("Strength"));
s[1] = Skill(&as.GetAbility("Charisma"));
...
Otherwise, return the Ability object by pointer instead:
Ability* AbilityScores::GetAbility(string abilityName) {
for (int i = 0; i < abilityScores.size(); i++) {
if (abilityScores[i].Name() == abilityName) {
return &abilityScores[i];
}
}
return nullptr;
// or:
abilityScores.emplace_back(abilityName, 0.0f);
return &(abilityScores.back());
}
...
Skill s* = new Skill[2];
s[0] = Skill(as.GetAbility("Strength"));
s[1] = Skill(as.GetAbility("Charisma"));
...

looking for specific Design Pattern in C++ that solve this problem

I am looking for specific Design Pattern in C++ that solve this problem.
I want to design a Storyboard. Our version of the Storyboard
contains arbitrary many notes (imagine it like putting sticky notes on a board).
Every note has a title, a text and a set of tags. E.g.
- title: "Test Traceplayer"
- text: "Implement a unit test for the class Traceplayer of the spark core framework."
- tags: {"unit test", "traceplayer", "testing", "spark core"}
Our Storyboard should enable us to search for notes by title, text and tags.
E.g.:
searchByTitle( "Test Traceplayer" )
searchByTag({"testing", "unit test"})
searchByText("Implement a unit test for the class Traceplayer of the spark core framework.")
For the sake of simplicity we don't want to do any similiarity or prefix matching when
searching for a title, tag or text. Only an exact match should give results.
I have number of solution that solve this problem O(1) search complexity But can any one suggest any "Design Pattern" that solve this problem.
Solve that issue with three STL map and get constant time search complexity
Looking for a specific Design Pattern that solves this problem.
I have solved this problem using 3 STL Map and solution get O(1) search complexity
#include <iostream>
#include <vector>
#include <map>
#define INPUT 8
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){
Tital = "\0";
Text = "\0";
}
};
class storyBoard{
public:
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
storyBoard(){}
private:
std::map<string,Note *> TitalMap;
std::map<string,Note *> TextMap;
std::map<string,std::vector<Note *> > TagsMap;
};
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
void feed_data_for_testing(storyBoard &Sb);
void TestCase(storyBoard &Sb);
int main() {
storyBoard Sb;
feed_data_for_testing(Sb);
Sb.PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase(Sb);
return 0;
}
void TestCase(storyBoard &Sb){
Note* obj = Sb.searchByTitle("Tital-3");
if(obj != NULL){
Sb.printSlip(obj);
}
obj = Sb.searchByText("Text-4");
if(obj != NULL){
Sb.printSlip(obj);
}
vector<Note *> vec = Sb.searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb.printSlip(*it);
}
}
}
void feed_data_for_testing(storyBoard &Sb){
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb.AddNote(tital,text,tags);
}
}
I am looking for a design pattern that solves this issue.
I update your code at the following point:-
convert the class into singleton so that data maintain a single map for each type
change map to unorder_map
#define INPUT 8
using namespace std;
/*use class to store single slip data*/
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){ //constructor to initialize data zero
Tital = "\0";
Text = "\0";
}
};
/*create a singalton pattern class so that create only one data storage*/
class storyBoard{
public:
static storyBoard* getInstance(){
storyBoard* Instance= instance.load();
if ( !Instance ){
std::lock_guard<std::mutex> myLock(lock);
Instance= instance.load();
if( !Instance ){
Instance= new storyBoard();
instance.store(Instance);
}
}
return Instance;
}
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
private:
storyBoard()= default;
~storyBoard()= default;
storyBoard(const storyBoard&)= delete;
storyBoard& operator=(const storyBoard&)= delete;
static std::atomic<storyBoard*> instance;
static std::mutex lock;
std::unordered_map<string,Note *> TitalMap;
std::unordered_map<string,Note *> TextMap;
std::unordered_map<string,std::vector<Note *> > TagsMap;
};
std::atomic<storyBoard*> storyBoard::instance;
std::mutex storyBoard::lock;
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
/**temporary function only use for testing*/
void TestCase(){
storyBoard *Sb = storyBoard::getInstance();
Note* obj = Sb->searchByTitle("Tital-3");
if(obj != NULL){
Sb->printSlip(obj);
}
obj = Sb->searchByText("Text-4");
if(obj != NULL){
Sb->printSlip(obj);
}
vector<Note *> vec = Sb->searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb->printSlip(*it);
}
}
}
/**temporary function only use for testing*/
void feed_data_for_testing(){
storyBoard *Sb = storyBoard::getInstance();
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb->AddNote(tital,text,tags);
}
}
int main() {
storyBoard *Sb = storyBoard::getInstance();
feed_data_for_testing();
Sb->PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase();
return 0;
}
I think the term design pattern was mistakenly used by your interviewer in place of the term idiom.
A major issue with your code (and could be the cause of rejection) is memory handling using classical c++ idioms:
use of smart pointers to manage the lifetime of your notes
rule of 3 (or 5) to handle copy/assignment(/move) of your StoryBoard object
Note that using smart pointers is only one way of managing the memory. You could as well use other idioms:
arena of notes and referencing notes from the arena inside your hashmaps
...
Once this issue is solved you have minor issues:
returning pointers instead of references, the StoryBoard is the owner of the memory, you should not return a pointer that a caller could inadvertently free.
no const accessors (returning const references)
repetitive code that could be factored
If I am not mistaken in interpreting what the interviewer said, this question should be moved to codereview.stackexhange.com

Receiving the "vector iterators incompatible" error

I am creating an Uno game and I am stuck trying to search through an a vector of objects by specific attributes. It crashes when the program reaches the Game::player_selection() method. Other than the , everything is working.
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
class Card {
public:
string get_colour();
string get_type();
};
class Player {
public:
vector<Card*> get_hand(); //Should this be a reference?
private:
vector<Card*>current_cards;
};
int main() {
srand(time(NULL)); //Makes shuffle more random (different every time)
Game my_game;
Player new_player("Hank", "human");
Player jack_player("Jack", "human");
my_game.add_player(new_player); //Must create players before doing other tasks
my_game.add_player(jack_player); //Must create players before doing other tasks
my_game.setup_game();
my_game.display_players();
cout << "enter colour" << endl;
cin >> colour;
cout << "enter type" << endl;
cin >> type;
my_game.play_card_to_pile(my_game.player_selection("Jack", colour, type));
my_game.display_players();
Game.cpp
Card* Game::player_selection(string p_name, string colour, string type){
vector<Player>::iterator p_iter;
vector<Card*>::iterator c_iter;
p_iter = find_if (game_players.begin(), game_players.end(), [&] (Player& p) -> bool{return p.get_name() == p_name;}); //Finds correct player
c_iter = find_if(p_iter->get_hand().begin(), p_iter->get_hand().end(), [&] (Card*& c) -> bool{return c->get_colour() == colour && c->get_type() == type;}); //Finds correct card
return (*c_iter);//Should return correct card
}
Error given
Edit
Just posting here for future reference regarding find_if checks and multiple copies of vectors. So the solution was:
Card* Game::player_selection(string p_name, string colour, string type){
vector<Player>::iterator p_iter;
vector<Card*>::iterator c_iter;
p_iter = find_if (game_players.begin(), game_players.end(), [&] (Player& p) -> bool{return p.get_name() == p_name;});
if (p_iter != game_players.end()){
vector<Card*> hand = p_iter->get_hand();//Otherwise have multiple copies of the vector stored in different places in memory
c_iter = find_if(hand.begin(), hand.end(), [&] (Card*& c) -> bool{return c->get_colour() == colour && c->get_type() == type;});
if (c_iter != hand.end()){
return (*c_iter); //If found return found Card
}
}
return (get_pile_card()); //Else return a default
}
The problem is that get_hand is returning a vector by value, so the 2 calls to get_hand are creating different vectors. You could return a reference to the vector, or just call get_hand once:
vector<Card*> hand = p_iter->get_hand();
c_iter = find_if(hand.begin(), hand.end(), ...
You should also check the result of both calls to find_if to make sure they actually found an item satisfying the predicate.

How do I return a Null Pointer in a function C++

I am currently working on a bit of code that will search within a vector of type Person (which I have defined in the code and will show if needed). If it finds the person, it returns their name. This is currently working, but if it does not find the person, it is supposed to return a Null pointer. The problem is, I cannot figure out how to make it return a Null pointer! It just keeps either crashing the program every time.
Code:
Person* lookForName(vector<Person*> names, string input)
{
string searchName = input;
string foundName;
for (int i = 0; i < names.size(); i++) {
Person* p = names[i];
if (p->getName() == input) {
p->getName();
return p; //This works fine. No problems here
break;
} else {
//Not working Person* p = NULL; <---Here is where the error is happening
return p;
}
}
}
You could use std::find_if algorithm:
Person * lookForName(vector<Person*> &names, const std::string& input)
{
auto it = std::find_if(names.begin(), names.end(),
[&input](Person* p){ return p->getName() == input; });
return it != names.end() ? *it : nullptr; // if iterator reaches names.end(), it's not found
}
For C++03 version:
struct isSameName
{
explicit isSameName(const std::string& name)
: name_(name)
{
}
bool operator()(Person* p)
{
return p->getName() == name_;
}
std::string name_;
};
Person * lookForName(vector<Person*> &names, const std::string& input)
{
vector<Person*>::iterator it = std::find_if(names.begin(), names.end(),
isSameName(input));
return it != names.end() ? *it : NULL;
}
If the name you are searching for is not at the first element, then you are not searching in the rest of the elements.
You need to do something like -
for (int i = 0; i<names.size(); i++){
Person* p = names[i];
if (p->getName() == input) {
return p;
// Placing break statement here has no meaning as it won't be executed.
}
}
// Flow reaches here if the name is not found in the vector. So, just return NULL
return NULL;
As Chris suggested, try using std::find_if algorithm.
Looks like you just have to return Null, nullptr, or 0.
codeproject
Just use following code:
return NULL;