C++ problem in calling a function in main that prints the map - c++

I am trying to print the contents of the map and this is where my code fails. I have tested all my methods and I have no problem to read from file, filer the word, put it into map, and even the print function is working.
However, when I am calling the printer function from main it does not print the map.
I am new to polymorphism and I think that my error is in how I am passing the map to the function in main.
Here is my main class:
using namespace std;
#include <iostream>
#include "ReadWords.h"
#include "ReadPunctWords.h"
#include "ReadNumWords.h"
#include "ReadCapWords.h"
#include "MapWorks.h"
#include <fstream>
#include <string>
#include <map>
#include <iterator>
/**
* This main function uses all other classes.
*/
int main() {
char* name = "RomeoJuliet.txt";
//ReadPunctWords &obj = *new ReadPunctWords(name);
ReadPunctWords obj(name);
string startSearch="BEGIN";
string endSearch="FINIS";
ReadPunctWords rpw;
ReadCapWords rcw;
ReadNumWords rnw;
MapWorks mw;
while(rpw.isNextWord()){
string tempword = obj.getNextWord();
if(tempword == startSearch){
break;
}
}
while(rpw.isNextWord()){
string tempword = obj.getNextWord();
if(tempword == endSearch){
break;
}
else{
if(rpw.filter(tempword)){
mw.addToMap(tempword, mw.mapPunct);
}
if(rcw.filter(tempword)){
mw.addToMap(tempword, mw.mapCap);
}
if(rnw.filter(tempword)){
mw.addToMap(tempword, mw.mapNum);
}
}
}
mw.printMap(mw.mapPunct);
mw.printMap(mw.mapCap);
mw.printMap(mw.mapNum);
//clear map
mw.clearMap(mw.mapPunct);
mw.clearMap(mw.mapCap);
mw.clearMap(mw.mapNum);
//close the file
//obj.close();
//delete &obj;
//exit(0); // normal exit
return 0;
}
And my MapWorks.cpp which contains the maps and the functions related to maps:
using namespace std;
#include <iostream>
#include <string>
#include <map>
#include <iterator>
#include "MapWorks.h"
/**
* MapWorks class builds the maps and does the map processing and printing
*/
MapWorks::MapWorks() {}
void MapWorks::addToMap(string myword, map<string, int> & myMap){
int n = myMap[myword];
myMap[myword]= n+1;
}
void MapWorks::printMap (map<string, int> &myMap){
for (map<string, int>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
cout << it->first << " ==> " << it->second << '\n'<<endl;
}
}
//delete entries in map
void MapWorks::clearMap(map<string, int>myMap) {
myMap.clear();
}
MapWorks.h :
#ifndef MAPWORKS_H
#define MAPWORKS_H
#include <string>
#include <map>
using namespace std;
/**
* MapWorks class builds the maps and does the map processing and printing
*/
class MapWorks {
public:
map<string, int> mapPunct; //(word, number of occurences)
map<string, int> mapNum; //(word, number of occurences)
map<string, int> mapCap; //(word, number of occurences)
MapWorks();
void addToMap(string myword, map<string, int> & myMap); //adds words to a map
void printMap (map<string, int> &myMap); //prints the map
void clearMap(map<string, int>); //clear map
};
#endif
My ReadWords.h :
/**
* ReadWords class, the base class for ReadNumWords, ReadPunctWords, ReadCapWords
*/
#ifndef READWORDS_H
#define READWORDS_H
using namespace std;
#include <string>
#include <fstream>
#include<iostream>
class ReadWords
{
private:
string nextword;
ifstream wordfile;
bool eoffound;
public:
/**
* Constructor. Opens the file with the default name "text.txt".
* Program exits with an error message if the file does not exist.
*/
ReadWords();
/**
* Constructor. Opens the file with the given filename.
* Program exits with an error message if the file does not exist.
* #param filename - a C string naming the file to read.
*/
ReadWords(char *filename);
/**
* Closes the file.
*/
void close();
/**
* Returns a string, being the next word in the file.
* #return - string - next word.
*/
string getNextWord();
/**
* Returns true if there is a further word in the file, false if we have reached the
* end of file.
* #return - bool - !eof
*/
bool isNextWord();
//pure virtual function for filter
virtual bool filter(string word)=0;
/**
* Fix the word by the definition of "word"
* end of file.
* #return - string
*/
string fix(string word);
};
#endif
And my ReadPunctWords (ReadNumWords and ReadCapWords are quite the same, just checking if the word has digits or capital letters instead of punctuations like in here):
#ifndef READPUNCTWORDS_H
#define READPUNCTWORDS_H
using namespace std;
#include <string>
#include "ReadWords.h"
/**
* ReadPunctWords inherits ReadWords, so MUST define the function filter.
* It chooses to override the default constructor.
*/
class ReadPunctWords: public ReadWords {
public:
ReadPunctWords();
ReadPunctWords(char *filename): ReadWords(filename){};
virtual bool filter(string word);
};
#endif
I would appreciate any help from you.
Thanks, Adriana

There are a number of things that are potential issues in your code, but the most obvious thing that may be causing the printMap not to work as expected is this while loop.
map<string, int>::iterator it = myMap.begin();
cout<<"test"<<endl;
while(it!=myMap.end()){
cout<<(*it).first<<" ==> "<<(*it).second<<endl;
}
Nowhere do you increment the iterator so either nothing will be printed (if the map is empty) or else the first item will printed over and over again and the loop won't terminate.
The idiomatic way to write this loop would be as a for loop.
for (std::map<string, int>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " ==> " << it->second << '\n';
}
The other issue is that your addToMap function probably isn't working as intended because you pass the map to the function by value and this means that the map that the function is adding an item to is actually a copy of the map that was passed in.
When control is passed to the calling function this copy is destroyed and the map that was passed it is still empty.
To pass a map by reference you need to add & to the type of the parameter in the function declaration.
i.e. in the headfile, the the MapWorks class definition:
void addToMap(string myword, map<string, int>& myMap);
and in the source file:
void MapWorks::addToMap(string myword, map<string, int>& myMap)
{
// definition...
}
Your use of references for dynamically allocated objects is unusual, to say the least. For your purposes, I don't see any point to doing:
ReadWords &rnw = *new ReadNumWords();
when you delete the object at the end of the same function in which it is created. You can just do this (exactly as you do with MapWorks mw;).
ReadNumWords rnw;
If you have to use dynamically allocated objects, just using pointers rather than references is much more usual but it is highly recommended to use some sort of a smart pointer so that you don't have to remember to call delete explicitly.

You forgot to increment iterator:
while(it!=myMap.end()){
cout<<(*it).first<<" ==> "<<(*it).second<<endl;
// you forgot this:
it++;
}
And, more importantly, consider few modifications to your code:
// ReadPunctWords &obj = *new ReadPunctWords(name);
// should likely be:
ReadPunctWords obj(name);
// same applies to other 'newed' 'references'
// and then there's no need to do
// delete &obj;
// exit(0); // normal exit
// should probably be just a
return 0;
// obj.close();
// can be called in the destructor of ReadPunctWords class
// and RAII will help you get your file closed correctly when needed
// void MapWorks::printMap (map<string, int>myMap)
// should better be:
void MapWorks::printMap (const std::map<string, int> &myMap)
// same applies to other functions in your code
// here's how your commented-out function could look like
void MapWorks::printMap(const std::map<string, int> &myMap) {
typedef std::map<string, int>::iterator mapsi;
for (mapsi m = myMap.begin(); m != myMap.end(); ++m) {
std::cout << (*m).first << " ==> " << (*m).second << "\n";
}
}
// void MapWorks::addToMap(string myword, map<string, int>myMap)
// should be:
void MapWorks::addToMap(std::string myword, std::map<string, int> &myMap)

If possible, I'd suggest breaking the logic up into slightly smaller units, and pushing more of the logic into the classes -- right now, main does quite a lot more than I'd like to see there, and (particularly) knows more about the internals of the classes than I'd like to see either.
If I were doing it, I'd start with a map that knew how to filter out words, so it can only accept what it's supposed to:
class Map {
std::map<std::string, int> counts;
public:
struct Filter {
virtual bool operator()(std::string const &) const = 0;
};
Map(Filter const &f) : filter(f) {}
bool InsertWord(std::string const &word) {
return filter(word) && (++counts[word] != 0);
}
friend std::ostream &operator<<(std::ostream &os, Map const &m) {
std::copy(m.counts.begin(),
m.counts.end(),
std::ostream_iterator<count>(std::cout, "\n"));
return os;
}
private:
Filter const &filter;
};
Then we'd need some derivatives of Filter to do the real filtering. These probably don't work the way you really want; they're really just placeholders:
struct Num : Map::Filter {
bool operator()(std::string const &w) const {
return isdigit(w[0]) != 0;
}
};
struct Punct : Map::Filter {
bool operator()(std::string const &w) const {
return ispunct(w[0]) != 0;
}
};
struct Letter : Map::Filter {
bool operator()(std::string const &w) const {
return isalpha(w[0]) != 0;
}
};
Then MapWorks can delegate almost all the real work to the Map (which in turn uses a Filter):
class MapWorks {
Map num;
Map punct;
Map letter;
public:
// For the moment, these allocations just leak.
// As long as we only create one MapWorks object,
// they're probably not worth fixing.
MapWorks()
: num(Map(*new Num())),
punct(Map(*new Punct())),
letter(Map(*new Letter()))
{}
// Try adding the word until we find a Map
// that accepts it.
bool push_back(std::string const &word) {
return num.InsertWord(word)
|| punct.InsertWord(word)
|| letter.InsertWord(word);
}
// Write out by writing out the individual Map's:
friend std::ostream &operator<<(std::ostream &os, MapWorks const &m) {
return os << m.num << "\n" << m.punct << "\n" << m.letter << "\n";
}
};
With these in place, main becomes pretty simple: (though for the moment, I've just had it read a whole file instead of looking for "BEGIN" and "FINIS"):
int main() {
MapWorks m;
std::string temp;
while (std::cin >> temp)
m.push_back(temp);
std::cout << m;
return 0;
}
There are a few other bits and pieces, such as typedef'ing the count type and defining an inserter for it, but they're pretty minor details.

Related

How to make a C++ map with class as value with a constructor

I have a class that has a constructor. I now need to make a map with it as a value how do I do this? Right now without a constructor I do.
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
};
int main()
{
map<int,testclass> thismap;
testclass &x = thismap[2];
}
If I added a constructor with arguments how would I add them to the map? I basically need to do
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
testclass(int arg) {
x = arg;
}
};
int main()
{
map<int,testclass> thismap;
testclass &x = thismap[2];
}
This obviously wouldn't work since it requires an argument but I can't figure a way of doing this.
This is how you can add items of your own class to your map.
Note : I used a string in testclass to better show difference
between key and value/class.
#include <iostream>
#include <string>
#include <map>
class testclass
{
public:
explicit testclass(const std::string& name) :
m_name{ name }
{
};
const std::string& name() const
{
return m_name;
}
private:
std::string m_name;
};
int main()
{
std::map<int, testclass> mymap;
// emplace will call constructor of testclass with "one", and "two"
// and efficiently place the newly constructed object in the map
mymap.emplace(1, "one");
mymap.emplace(2, "two");
std::cout << mymap.at(1).name() << std::endl;
std::cout << mymap.at(2).name() << std::endl;
}
Using std::map::operator[] requires that the mapped type is default-constructible, since it must be able to construct an element if one doesn't already exist.
If your mapped type is not default-constructible, you can add elements with std::map::emplace, but you still can't use std::map::operator[] to search, you will need to use std::map::find() or so.
That's a rather obvious feature of std::map (and very similar other std containers). Some of their operations require specific type requirements for good reasons.
There is no problem to create such a map as you suggest in the first place, however, you are restricted to method calls that do not require potential default construction. The operator[] is such a method, since in the case the element is not found, it is created. That is what does not work in your example. Just use other methods with little impact on the map usage and you can still succeed:
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
testclass(int arg) {
x = arg;
}
};
int main()
{
map<int,testclass> thismap;
thismap.insert( {2, testclass(5)} );
auto element2 = thismap.find(2);
if (element2 != thismap.end()) {
testclass& thiselement = element2->second;
cout << "element 2 found in map, value=" << thiselement.x << endl;
}
auto element5 = thismap.find(5);
if (element5 == thismap.end()) {
cout << "no element with key 5 in thismap. Error handling." << endl;
}
}
Main issue: avoid operator[].
Note:
Looking at the other very good answers, there are a lot of methods that can be used without default construction. There is not "right" or "wrong" since this simply depends on your application. at and emplace are prime examples that are highly advisable.

C++: How to make function return only strings which are a part of a list?

I want my function to return a string, but only strings which are a member of a specific list/set of strings. How can I go about doing this?
You do not want to return a string, you want to return a string that has an additional restriction (being part of some predefined set).
For that you'd need a new type:
class BusinessStringWrapper {
public:
BusinessStringWrapper(std::string arg): value{arg} {
if (/* arg is not ok */) {
throw;
}
}
// you can replace that with factory method
// can also return std::optional instead of throwing if the condition is not met
// that depends on your application
std::string value() const { return value; }
private:
const std::string value;
};
And in your application you'd operate on this type, accessing value if needed.
Hoe about using a std::set<std::string>?
#include <iostream>
#include <set>
#include <string>
std::string helper(const std::string & str,
const std::set<std::string> & lst)
{
return lst.find(str) == lst.end() ? "" : str;
}
int main()
{
std::set<std::string> lst = {"alpha", "beta", "gamma"};
std::cout << "return " << helper("alpha", lst) << "\n";
std::cout << "return " << helper("zeta", lst) << "\n";
return 0;
}
Output
return alpha
return
Of course, it really depends on what your definition of does not return is.
If it means an empty string, then use the above solution. Keep your life simple.
If it means an error and the program should terminate, you may #include <cassert> and just
assert(lst.find(str) != lst.end());
If it means an exception to handle, you may try throw and catch.
If it means returning a std::string if str is in a predefined list, but a void if it's not, then you may need some tricks as described in <type_traits>.
You can do this std::map<CardType, std::string> in the example below, or use std::map<int, std::string> to associate a string with any integer. For example mp[123]="abcd"
#include <iostream>
#include <string>
#include <map>
enum CardType {
SPADE,
HEART,
CLUBS,
DIAMD
};
std::map<CardType, std::string> mp{
{CardType::SPADE, "Spade"},
{CardType::HEART, "Heart"},
{CardType::CLUBS, "Clubs"},
{CardType::DIAMD, "Diamond"}
};
int main()
{
std::cout << mp[CardType::SPADE] << std::endl;
return 0;
}

C++ return 0 when initial a class fail

i was writing a class like this
class AA{
private:
char* str;
public:
AA(int size){
str = (char*)malloc(size);
}
};
int main(){
AA anAA(1000);
}
here is the problem, when the size is too big it may cause malloc return a 0 pointer, if the str init fail, is there any method to return a 0 pointer to anAA(in the main entry point, i can check anAA isn't init success by if(anAA != NULL)), i don't want to make a function for creating AA class, or make a check function in the class
I'll ignore the atrocities you are committing in favour of answering your question.
The easiest solution, if you don't want to use exceptions, is to define an operator bool():
class AA{
...
public:
operator bool() const {
return this->str != nullptr; // return str; would actually suffice
}
};
int main(){
AA anAA(1000);
if (!anAA) {
std::cerr << "Creating object failed.\n";
return 1;
}
return 0;
}
#include <iostream>
#include <stdexcept> // std::exception
#include <stdlib.h> // EXIT_FAILURE
#include <string> // std::string
#include <vector> // std::vector
using namespace std;
class AA{
private:
string str_;
public:
AA( int const size)
{
str_.reserve( size );
}
};
int main()
{
try
{
AA anAA( 1000 );
// Whatever
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
return EXIT_FAILURE;
}
}
Note 1: with this approach you probably don't need to reserve a size upfront.
Note 2: I chose reserve as probably the closest thing to a presumed intention of making sure that str_ can hold at least that long a string without failure, a preallocation of resources.
Disclaimer: I've not compiled this code.

how to change from a list of strings to a list of pointers to strings

I am trying to get this to return a string, but i am having trouble getting it working. The goal is to have a doubly-linked list that points to strings. I am not allowed to have it contain the string, it must point to it instead. Currently i am having trouble getting my program to use it. For example, it always seems to return what the command was, and its confusing me and hard to explain.
#ifndef DOUBLY_LINKED_LIST_H
#define DOUBLY_LINKED_LIST_H
#include <iostream>
#include <string>
//#include "Playlist.h"
using namespace std;
class DoublyLinkedList
{
public:
DoublyLinkedList();
~DoublyLinkedList();
bool empty();
void append(string& s);
void insertBefore(string& s);
void insertAfter(string& s);
void remove(string& s);
void begin();
void end();
bool next();
bool prev();
bool find(string& s);
const string& getData();
private:
class Node
{
public:
Node (string *data, Node *next, Node *prev)
{m_data = data; m_next = next; m_prev = prev;}
string *m_data;
Node * m_next;
Node * m_prev;
};
Node *m_head;
Node *m_tail;
Node *m_current;
};
#endif // DOUBLYLINKEDLIST_H_INCLUDED
.cpp file>>>>
const string& DoublyLinkedList::getData()
{
string *m_tmp;
m_tmp = m_current->m_data;
cout << m_current->m_data << endl;
//cout << "returning: " << m_current->m_data << endl;
// return m_current->m_data;
return *m_tmp;
}
void DoublyLinkedList::append(string &s)
{
if (!m_head)
{
m_head = new Node(&s, NULL, NULL);
m_tail = m_head;
m_current = m_head;
}
else
{
m_tail->m_next = new Node (&s, NULL, m_tail);
m_tail = m_tail->m_next;
m_current = m_tail;
}
}
Consider the following example:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void store_value(vector<string*>& vec, string& str)
{
vec.push_back(&str);
}
void create_and_store_value(vector<string*>& vec)
{
string str("This string is temporary");
store_value(vec, str);
}
int main(int argc, char** argv)
{
vector<string*> pointers;
create_and_store_value(pointers);
cout << *pointers.back() << endl;
string myPersistingString("Yay");
store_value(pointers, myPersistingString);
cout << *pointers.back() << endl;
return 0;
}
This example contains two function, a function store_value which behaves similar to your append function (except, for the purposes of this example working on a std::vector) and a second function showing the possible danger of taking the address of a reference (this is one of the possible hazards that I believe Manu343726 and Mats Petersson are preluding too).
The reason this is dangerous is because the string declared inside create_and_store_value does not persist after the completion of the function. This means that we are left with a pointer to memory which is probably not what we expect. On the other hand, creating a string inside the main function is fine, since the string there persists until the end of the program.
For us to help you further, I would suggest editing your question to give us an example of how you are calling your function. I would suggest pasting a minimal striped down version of your code including an example of how you are calling append, something like:
#include <blah>
class DoubleLinkedList
{
DoubleLinkedList(void)
{
// Include these inline to make copying and pasting simpler.
}
~DoubleLinkedList(void)
{
...
}
append(...) { ... }
getData(...) { ... }
};
int main(int argc, char** argv)
{
DoubleLinkedList dll;
// Show us how you are using this list
return 0;
}
In the above, replace the comments and dots with the relevant code.

I am stuck with creating an output member function

I am stuck on the output member function of the class. I have no idea how to create it and just simply couting it does not seem to work. also any other advice would be great. thanks in advance
here's the code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class StringSet
{
public:
StringSet(vector<string> str);
void add(string s);
void remove(int i);
void clear();
int length();
void output(ostream& outs);
private:
vector<string> strarr;
};
StringSet::StringSet(vector<string> str)
{
for(int k =0;k<str.size();k++)
{
strarr.push_back(str[k]);
}
}
void StringSet::add(string s)
{
strarr.push_back(s);
}
void StringSet::remove(int i)
{
strarr.erase (strarr.begin()+(i-1));
}
void StringSet::clear()
{
strarr.erase(strarr.begin(),strarr.end());
}
int StringSet::length()
{
return strarr.size();
}
void StringSet::output()
{
}
int main()
{
vector<string> vstr;
string s;
for(int i=0;i<10;i++)
{
cout<<"enter a string: ";
cin>>s;
vstr.push_back(s);
}
StringSet* strset=new StringSet(vstr);
strset.length();
strset.add("hello");
strset.remove(3);
strset.empty();
return 0;
}
Ok, you should begin by solving some errors in your code:
You use a pointer to StringSet and after that you are trying to access the member-functions with the . operator instead of the ->. Anyway, do you really need to allocated your object dynamically ?
StringSet strset(vstr); // No need to allocated dynamically your object
After that, you are calling an empty() method which does not exist...
Also if you stay on dynamic allocation, don't forget to deallocated your memory :
StringSet* strset = new StringSet(vstr);
// ...
delete strset; // <- Important
Finally, I think that your function output should write in the stream the content of your vector, you can do it that way :
#include <algorithm> // For std::copy
#include <iterator> // std::ostream_iterator
void StringSet::output( ostream& outs )
// ^^^^^^^^^^^^^ don't forget the arguments during the definition
{
std::copy(strarr.begin(), strarr.end(), std::ostream_iterator<string>(outs, "\n"));
}
HERE is a live example of your code fixed.
I would suggest you to understan how class works : http://www.cplusplus.com/doc/tutorial/classes/
If your output function is going to print the state of the StringSet object, you may implement is like this:
#include<iterator> //std::ostream_iterator
#include<algorithm> //std::copy
void StringSet::output(ostream& outs)
{
std::copy(starr.begin(), starr.end(), std::ostream_iterator<string>(outs, "\n"));
}