2

我正在尝试打印地图的内容,这是我的代码失败的地方。我已经测试了我所有的方法,我没有问题从文件中读取,过滤单词,将它放入地图,甚至打印功能都可以工作。但是,当我从 main 调用打印机函数时,它不会打印地图。我是多态的新手,我认为我的错误在于我如何将映射传递给 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

我将不胜感激您的任何帮助。谢谢,阿德里安娜

4

3 回答 3

4

您的代码中有很多潜在的问题,但最明显的可能导致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显式调用。

于 2010-01-15T21:57:46.013 回答
0

你忘了增加迭代器:

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)
于 2010-01-15T21:56:42.197 回答
0

如果可能的话,我建议将逻辑分解成更小的单元,并将更多的逻辑推入类中——现在,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;
};

然后我们需要一些 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 可以将几乎所有实际工作委托给 Map(它又使用过滤器):

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;

}

还有其他一些零碎的东西,例如对计数类型进行类型定义并为其定义插入器,但它们都是非常小的细节。

于 2010-01-16T06:04:22.760 回答