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
Related
I am trying to implement trie data structure in C++ using class. In TrieNode class I have a TrieNode *children[26]; array and an isEndOfWord boolean value to determine if it is the end word. In that same class I have other functions appropriate to function like getters and setters and additionally insert and search.
Whenever I try to add a new word it is also setting the bool value as true at the end of each word by setting true to isEndOfWord. But in searching function it is not determining the end of the word. Please guide me as I am new to this data structure, and please comment on the way i write the code and what is the appropriate way to write it(in a Professional way, if interested). Thanks!
#include<cstdio>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
class TrieNode{
private:
TrieNode *children[26];
bool isEndOfWord;
public:
TrieNode(){
for(int i = 0; i < 26; i++){
children[i] = NULL;
}
isEndOfWord = false;
}
bool checkNull(char temp){
cout<<"\nIncheckNULL "<<temp<<" "<<(temp - 'a')<<" \n";
if(children[temp - 'a'] == NULL){
return true;
}
else{
return false;
}
}
void setNode(char temp){
cout<<"Setting node \n";
children[temp - 'a'] = new TrieNode();
}
TrieNode *getNode(char temp){
return children[temp - 'a'];
}
void setEndWord(){
this->isEndOfWord = true;
}
bool getEndWord(){
return this->isEndOfWord;
}
void insert(TrieNode*, string);
bool search(TrieNode*, string);
};
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
//cout<<"key is "<<key<<endl;
int length = sizeof(key)/sizeof(key[0]);
//cout<<"find length\n";
for(int i = 0; key[i] != '\0'; i++){
cout<<"TEST null check key is "<<key[i]<<endl;
if(crawl->checkNull(key[i])){
cout<<"null check key is "<<key[i]<<endl;
crawl->setNode(key[i]);
crawl = crawl->getNode(key[i]);
if(key[i + 1] == '\0'){
cout<<"In setting end word\n";
if(crawl->getEndWord()){
cout<<"Word already exists";
}
else{
crawl->setEndWord();
cout<<"End word setted "<<crawl->getEndWord()<<endl;
}
}
}
else{
if(key[i + 1] == '\0'){
cout<<"In setting end word\n";
if(crawl->getEndWord()){
cout<<"Word already exists";
}
else{
crawl->setEndWord();
cout<<"End word setted\n";
}
}
else{
crawl = crawl->getNode(key[i]);
}
}
}
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
cout<<"key is "<<key<<endl;
cout<<"\n In search\n";
int length = sizeof(key)/sizeof(key[0]);
for(int i = 0; key[i] != '\0'; i++){
if(crawl->checkNull(key[i])){
cout<<"INside search checknull"<<endl;
cout<<"Word does not exists"<<"sorry"<<endl;
break;
}
else{
cout<<"IN each character getting getEndWord "<<crawl->getEndWord()<<endl;
if(key[i + 1] == '\0'){
if(crawl->getEndWord()){
cout<<"Word Exists";
}
else{
cout<<"Word does not exists"<<"sorry"<<endl;
break;
}
}
else{
crawl = crawl->getNode(key[i]);
}
}
}
}
int main(){
TrieNode *root = new TrieNode();
cout<<"starting"<<endl;
root->insert(root, "hello");
cout<<"first added"<<endl;
root->insert(root, "anna");
root->insert(root, "anni");
cout<<"words added"<<endl;
root->search(root, "hello");
root->search(root, "anny");
}
Your insert and search functions can be simplified a bit.
Consider this. (Read the comments in the below code, they illustrate what the code does)
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
if (!crawl) {
crawl = new TrieNode();
}
cout << "Adding " << key << " to the trie" << endl;
for (int index = 0, auto str_iterator = str.begin(); str_iterator < str.end(); ++str_iterator, ++index) {
char key_char = *str_iterator;
if(crawl -> checkNull(key_char)){
// If a node representing the char does not exist then make it
crawl -> setNode(key_char);
}
crawl = crawl -> getNode(key_char);
if (index == key.length() - 1) {
// We are at the last character, time to mark an end of word
crawl -> setEndWord();
}
}
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
if (!crawl) {
cout << "Trie is empty!" << endl;
return false;
}
cout << "Searching for " << key << " in the trie" << endl;
for (int index = 0, auto str_iterator = str.begin(); str_iterator < str.end(); ++str_iterator, ++index) {
char key_char = *str_iterator;
if(crawl -> checkNull(key_char)){
cout << "Key is not in the trie" << endl;
return false;
}
crawl = crawl -> getNode(key_char);
if (index == key.length() - 1) {
if (!(crawl -> getEndWord())) {
cout << "Word is physically present in trie, but not present as a distinct word" << endl;
return false;
} else {
return true;
}
}
}
cout << "Code should not reach here" << endl; // IMO throw an exception I guess
return false;
}
Take advantage of the power of C++ std::string
Also your whole temp - 'a' logic is a bit iffy to me. I wouldn't much around with ASCII values unless I needed to
Why are you including a whole bunch of C headers? Just iostream should suffice to do what cstdio does.
if(!ptr) is a much more natural way to check for NULL.
In production don't use using namespace std; Instead just preface stuff like cout and endl with std::. The reason for this is to avoid polluting the standard namespace.
Read a good CPP OOP book :). It will help you a lot.
Also I lol'd at anna and anni. Your anna and anni must be proud to be in your trie :D
There are many things I'd give you feedback on, but this isn't a code review site, it's for specific questions. I'll point out briefly a few things I notice though:
1) don't include C headers; use c++ ones instead.
2) what type is string?
3) you compute length (incorrectly, assuming answer to question 2 is "the standard c++ string class"), but you don't use it.
4) search() returns a bool but you don't return anything. When you find the end of a word, you should return from the function.
5) search() calls checkNull() at the top of the for loop without ensuring that it's not null. After this: crawl = crawl->getNode(key[i]); it could be null, but then you loop and go through the pointer without testing it.
6) setNode is a public function, and unconditionally overwrites whatever is in the slot for the given variable. You can clobber an existing child if someone calls it with the same character twice and leak (and probably lose data in your tree.
7) search doesn't need to be a member of TrieNode. In fact, it doesn't access any data through "this". You probably don't want the TrieNode to be public at all, but an internal implenetation detail of Trie, which is where the search function should live, where the root should be stored and managed.
8) in c++ use nullptr instead of NULL
9) Looks like you need to debug search(), because it is not on the last letter when you check for end of word.
10) you need a destructor and need to deallocate your nodes. Or store them in unique_ptr<> for automatic deletion when your object goes out of scope.
11) don't "using namespace std;" in headers. It makes your headers toxic to include in my code.
The insert and search functions are a mess.
They use rather contrived ways to check the end of the string, duplicated unnecessarily and with a bug in one of the branches.
Here are simpler versions.
They use string size for the loop bounds, and the actions needed at the end of the loop are made after the loop, which is more natural.
void TrieNode::insert(TrieNode *root, string key){
TrieNode *crawl = root;
for(int i = 0; i < (int) (key.size()); i++){
if(crawl->checkNull(key[i])){
crawl->setNode(key[i]);
}
crawl = crawl->getNode(key[i]);
}
crawl->setEndWord();
}
bool TrieNode::search(TrieNode *root, string key){
TrieNode *crawl = root;
for(int i = 0; i < (int) (key.size()); i++){
if(crawl->checkNull(key[i])){
return false;
}
crawl = crawl->getNode(key[i]);
}
return crawl->getEndWord();
}
I used the same style, but omitted the debug outputs for readability.
Also, the code did not actually use search as a function, it didn't return a value.
Instead, it relied on debug output to show the result.
This is now corrected.
A main function complementing these is as follows.
int main(){
TrieNode *root = new TrieNode();
cout<<"starting"<<endl;
root->insert(root, "hello");
cout<<"first added"<<endl;
root->insert(root, "anna");
root->insert(root, "anni");
cout<<"words added"<<endl;
cout << root->search(root, "hello") << endl; // 1
cout << root->search(root, "anny") << endl; // 0
}
I've been trying to read this file for some time now and tried about everything I could think of. I placed the file in my Products folder and In my resource folder and included (ResourcePath + "File.cfg.txt") and neither worked. I'd appreciate it if someone could tell me what I'm missing and where to put this file to read it in. Again I'm using Xcode and the SFML library with it.
Keys.cfg.txt
Window_close 0:0
Fullscreen_toggle 5:89
Move 9:0 24:38
/////////////////////////////////////////
.CPP
#include "EventManager.h"
using namespace std;
EventManager::EventManager(): m_hasFocus(true){ LoadBindings(); }
EventManager::~EventManager(){
for (auto &itr : m_bindings){
delete itr.second;
itr.second = nullptr;
}
}
bool EventManager::AddBinding(Binding *l_binding){
if (m_bindings.find(l_binding->m_name) != m_bindings.end())
return false;
return m_bindings.emplace(l_binding->m_name, l_binding).second;
}
bool EventManager::RemoveBinding(std::string l_name){
auto itr = m_bindings.find(l_name);
if (itr == m_bindings.end()){ return false; }
delete itr->second;
m_bindings.erase(itr);
return true;
}
void EventManager::SetFocus(const bool& l_focus){ m_hasFocus = l_focus; }
void EventManager::HandleEvent(sf::Event& l_event){
// Handling SFML events.
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
EventType sfmlEvent = (EventType)l_event.type;
if (e_itr.first != sfmlEvent){ continue; }
if (sfmlEvent == EventType::KeyDown || sfmlEvent == EventType::KeyUp){
if (e_itr.second.m_code == l_event.key.code){
// Matching event/keystroke.
// Increase count.
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else if (sfmlEvent == EventType::MButtonDown || sfmlEvent == EventType::MButtonUp){
if (e_itr.second.m_code == l_event.mouseButton.button){
// Matching event/keystroke.
// Increase count.
bind->m_details.m_mouse.x = l_event.mouseButton.x;
bind->m_details.m_mouse.y = l_event.mouseButton.y;
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else {
// No need for additional checking.
if (sfmlEvent == EventType::MouseWheel){
bind->m_details.m_mouseWheelDelta = l_event.mouseWheel.delta;
} else if (sfmlEvent == EventType::WindowResized){
bind->m_details.m_size.x = l_event.size.width;
bind->m_details.m_size.y = l_event.size.height;
} else if (sfmlEvent == EventType::TextEntered){
bind->m_details.m_textEntered = l_event.text.unicode;
}
++(bind->c);
}
}
}
}
void EventManager::Update(){
if (!m_hasFocus){ return; }
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
switch (e_itr.first){
case(EventType::Keyboard) :
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Mouse) :
if (sf::Mouse::isButtonPressed(sf::Mouse::Button(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Joystick) :
// Up for expansion.
break;
default:
break;
}
}
if (bind->m_events.size() == bind->c){
auto callItr = m_callbacks.find(bind->m_name);
if(callItr != m_callbacks.end()){
callItr->second(&bind->m_details);
}
}
bind->c = 0;
bind->m_details.Clear();
}
}
void EventManager::LoadBindings(){
std::string delimiter = ":";
std::ifstream bindings;
bindings.open("keys.cfg");
if (!bindings.is_open()){ std::cout << "! Failed loading keys.cfg." << std::endl; return; }
std::string line;
while (std::getline(bindings, line)){
std::stringstream keystream(line);
std::string callbackName;
keystream >> callbackName;
Binding* bind = new Binding(callbackName);
while (!keystream.eof()){
std::string keyval;
keystream >> keyval;
int start = 0;
int end = keyval.find(delimiter);
if (end == std::string::npos){ delete bind; bind = nullptr; break; }
EventType type = EventType(stoi(keyval.substr(start, end - start)));
int code = stoi(keyval.substr(end + delimiter.length(),
keyval.find(delimiter, end + delimiter.length())));
EventInfo eventInfo;
eventInfo.m_code = code;
bind->BindEvent(type, eventInfo);
}
if (!AddBinding(bind)){ delete bind; }
bind = nullptr;
}
bindings.close();
}
The problem is that you have to copy the respective files in the bundle, and that only objective-c can provide you the full name of the file on the destination device.
To overcome this, make a .mm-file and place a c++ trampoline function in there, which gives you the full path (see code below).
One pitfall can be that you have to make sure that the config- and text files like "keys.cfg" are actually copied into the bundle. Select the respective file in the project and open the property inspector; make sure that - the respective target in "Target Membership" is checked.
// File: myFileNameProvider.mm
#import <Foundation/Foundation.h>
#include <iostream>
std::string GetTextureFilename(const char *name)
{
NSString *nameAsNSString = [NSString stringWithUTF8String:name];
NSString *fullName = [[NSBundle mainBundle]
pathForResource:nameAsNSString ofType: nil];
if (fullName)
return std::string([fullName UTF8String]);
else
return "";
}
Then, in your CPP-code, declare the signature of std::string GetTextureFilename(const char *name), and before opening the file get the full path by calling it:
// MyCPPFile.cpp
#include <iostream>
#include <fstream>
// declaration:
std::string GetTextureFilename(const char *name);
void myC_Func {
std::string fullPath = GetTextureFilename("keys.cfg");
std::ifstream bindings;
bindings.open(fullPath.c_str());
if (!bindings.is_open()) {
std::cout << "! Failed loading keys.cfg." << std::endl;
}
...
}
I'm having a problem that I haven't found an answer for in a week now. I have a dynamic array class and it has a method to add string values to it. It's supposed to represent an inventory you can add items to. However, I find that the changes made in the method to the private values of the class element aren't "updated" when I try to call for a print-method for the class element "backpack" later in the main(). I think this might be a problem due to referencing issues, but I've seen this work when a class hasn't been in a different module.
My "backpack" module print and add methods:
const int INITIAL_SIZE = 5;
Inventory::Inventory():
array_(new string[INITIAL_SIZE]),
max_space_(INITIAL_SIZE),
used_space_(0) {}
void Inventory::add(string item){
if ( size() == max_space_ ) {
string* new_array = new string[2 * max_space_];
for ( int i = 0; i < size(); ++i ) {
new_array[i] = array_[i];
}
delete [] array_;
array_ = new_array;
max_space_ = 2 * max_space_;
}
array_[used_space_] = item;
++used_space_;
}
void Inventory::print() {
for ( int i = 0; i < size(); ++i ) {
cout << array_[i] << endl;
}
}
The main():
Inventory inv;
string input;
while (cout << "input> "
and getline(cin,input)){
add_to_bag(input,inv);
So the point is you reset the inventory when you give it new contents. The function add_to_bag(); is:
void add_to_bag(string input, Inventory inv){
const string WHITESPACE1_REGEX = "[[:space:]]*";
const string WHITESPACE2_REGEX = "[[:space:]]+";
const string WORD_REGEX = "[[:alpha:]_]+";
const string LINE_REGEX =
WHITESPACE1_REGEX +
WORD_REGEX +
"(" +
WHITESPACE2_REGEX +
WORD_REGEX +
")*" +
WHITESPACE1_REGEX;
regex line_reg(LINE_REGEX);
regex word_regex(WORD_REGEX);
string line = input;
if ( regex_match(line, line_reg) ) {
sregex_iterator iter(line.begin(), line.end(), word_regex);
sregex_iterator end;
while ( iter != end ) {
inv.add(iter->str());
++iter;
}
} else {
cout << "Error: unknown inventory contents." << endl;
}
}
Your problem is:
void add_to_bag(string input, Inventory inv);
You pass a copy of the Inventory object to add_to_bag. You modify that copy ... and then it gets thrown away. The fix is to pass by reference:
void add_to_bag(string input, Inventory &inv);
Incidentally, in real-life code, I would strongly advise the use of std::vector<std::string> rather than "roll your own". There are a number of tricky exception handling issues you have got wrong here - and unless Inventory doesn't have a destructor (implying a memory leak), or does have a correct copy constructor I would have expected you to run into "double free" issues. (Read about "The Rule of Three".)
A simple way to design your class would be as follows:
class Inventory {
private:
std::vector<std::string> items_;
public:
Inventory(){}
~Inventory(){}
void addItem( const std::string& item ) {
items_.push_back( item );
}
void printInventory() const {
int idx = 0;
for (; idx < items_.size(); ++idx ) {
std::cout << items_[idx] << std::endl;
}
}
void clearInventory() {
items_.clear();
}
};
And as for your problem Martin Bonner had already answered it with the modifying of the copy and the removal of it afterwards and the other issues with the memory management.
I am trying to remove a name from this array and then have an empty spot at the last position of the array once removed. How do I do this? Here is what I tried below. It removes it, but doesnt move to the end.
const int array_size = 16;
string restaurants[array_size] = {"Texas Roadhouse","On The Border","Olive Garden","Panda Express","Cracker Barrel","IHOP","Woohoo","Pei Wei","Mcdonalds","Denny's","Mrs. Fields","Subway","Dairy Queen","Burger King","Pizza Hut","Dominos"};
int current_size = 16;
cout << "Please enter the name of the Restaurant you would like to remove: ";
cin.ignore();
getline(cin, remove_restaurant);
remove(restaurants, restaurants_size, remove_restaurant);//function call
bool remove(string restaurants[], int& current_size, string name)//function to remove array
{
for (int i = 0; i < current_size; i++)//look at each name you want to remove
{
if ( restaurants[i] == name)
{
restaurants[i]=restaurants[i+1];
current_size --;
cout << "Restaurant removed successfully." << endl;
return true;
}
}
return false;
}
Use the remove-erase idiom, with std::remove and std::fill:
bool remove(string restaurants[], int& current_size, string name)//function to remove array
{
auto begin = std::begin(restaurants);
auto end = std::next(begin, current_size);
auto new_end = std::remove(begin, end, name);
std::fill(new_end, end, {});
current_size = std::distance(begin, new_end);
if (new_end != end) {
std::cout << "Restaurant removed successfully." << std::endl;
}
return new_end != end;
}
Create an array of the same size as the original
Begin iterating elements of the original array
If the current item in the array does not equal the item to remove, add it to the new array
Here is one possible way to amend your current solution. I agree with ott--, though, you should probably use a list instead.
for (int i = 0; i < current_size; i++)//look at each name you want to remove
{
if (restaurants[i] == name) {
swap(restaurants[i], restaurants[current_size-1]);
current_size --;
cout << "Restaurant removed successfully." << endl;
return true;
}
}
With std::vector, std::remove, and std::vector::erase:
#include <algorithm>
#include <string>
#include <vector>
// ...
vector<string> restaurants { ... };
string remove_restaurant;
getline(cin, remove_restaurant);
restaurants.erase
(remove(restaurants.begin(), restaurants.end(), remove_restaurant));
First of all, I think you'd much rather use a vector or list for this, that's what it has been designed for. But if you want to go this way, you could for example write a moveUp method:
void moveUp(int startIndex, int endIndex, string* array) {
for (int ii = startIndex; ii < endIndex; ++ii) {
array[ii] = array[ii + 1];
}
array[endIndex] = 0;
}
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);
}
}