Проблема C ++ в вызове функции main, которая печатает карту - PullRequest
2 голосов
/ 16 января 2010

Я пытаюсь распечатать содержимое карты, и здесь мой код не работает. Я проверил все свои методы, и у меня нет проблем, чтобы прочитать из файла, заполнить слово, поместить его в карту, и даже функция печати работает. Однако когда я вызываю функцию принтера из основного, она не печатает карту. Я новичок в полиморфизме, и я думаю, что моя ошибка в том, как я передаю карту функции в main.

Вот мой основной класс:

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;

}

И мой MapWorks.cpp, который содержит карты и функции, связанные с картами:

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

Мой 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

И мои ReadPunctWords (ReadNumWords и ReadCapWords совершенно одинаковые, просто проверяют, есть ли в слове цифры или заглавные буквы вместо знаков препинания, как здесь):

#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

Буду признателен за любую помощь от вас. Спасибо, Адриана

Ответы [ 3 ]

4 голосов
/ 16 января 2010

Есть ряд вещей, которые могут быть потенциальными проблемами в вашем коде, но наиболее очевидная вещь, которая может заставить printMap работать не так, как ожидалось, это цикл while.

map<string, int>::iterator it = myMap.begin();
cout<<"test"<<endl;
while(it!=myMap.end()){
cout<<(*it).first<<" ==> "<<(*it).second<<endl;
}

Нигде вы не увеличиваете итератор, поэтому либо ничего не будет напечатано (если карта пуста), либо первый элемент будет напечатан снова и снова, и цикл не прекратится.

Идиоматический способ написать этот цикл - использовать цикл for.

for (std::map<string, int>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
    std::cout << it->first << " ==> " << it->second << '\n';
}

Другая проблема заключается в том, что ваша функция addToMap, вероятно, не работает должным образом, поскольку вы передаете карту в функцию по значению , и это означает, что карта, к которой функция добавляет элемент на самом деле это копия карты, которая была передана.

Когда управление передается вызывающей функции, эта копия уничтожается, а переданная карта остается пустой.

Чтобы передать карту по ссылке, необходимо добавить & к типу параметра в объявлении функции.

т.е. в заголовочном файле определение класса MapWorks:

void addToMap(string myword, map<string, int>& myMap);

и в исходном файле:

void MapWorks::addToMap(string myword, map<string, int>& myMap)
{
    // definition...
}

Использование ссылок для динамически размещаемых объектов необычно, если не сказать больше. Для ваших целей я не вижу смысла делать:

ReadWords &rnw = *new ReadNumWords();

при удалении объекта в конце той же функции, в которой он был создан. Вы можете просто сделать это (точно так же, как и с MapWorks mw;).

ReadNumWords rnw;

Если вам нужно использовать динамически распределяемые объекты, просто использование указателей, а не ссылок, является гораздо более обычным, но настоятельно рекомендуется использовать какой-то умный указатель, чтобы вам не приходилось вызывать delete явно .

0 голосов
/ 16 января 2010

Если возможно, я бы предложил разбить логику на несколько меньшие единицы и добавить больше логики в классы - прямо сейчас, main делает гораздо больше, чем мне хотелось бы видеть там, и (в частности) знает больше о внутренностях классов, чем мне бы хотелось увидеть.

Если бы я делал это, я бы начал с карты, которая знала, как отфильтровать слова, чтобы она могла принимать только то, что должна:

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

Тогда нам понадобятся некоторые производные фильтра, чтобы выполнить настоящую фильтрацию. Это, вероятно, не работает так, как вы действительно хотите; они действительно просто заполнители:

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

Тогда MapWorks может делегировать почти всю реальную работу на карту (которая в свою очередь использует фильтр):

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

Имея их, main становится довольно простым: (хотя на данный момент я только что прочитал весь файл вместо того, чтобы искать "BEGIN" и "FINIS"):

int main() { 
    MapWorks m;

    std::string temp;
    while (std::cin >> temp)
        m.push_back(temp);

    std::cout << m;
    return 0;

}

Есть несколько других кусочков, таких как typedef, определяющий тип счетчика и определяющий для него вставку, но это довольно незначительные детали.

0 голосов
/ 16 января 2010

Вы забыли увеличить итератор:

while(it!=myMap.end()){
    cout<<(*it).first<<" ==> "<<(*it).second<<endl;
    // you forgot this:
    it++;
}

И, что более важно, рассмотрите несколько модификаций вашего кода:

// 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)
...