在 main 中调用打印地图的函数时的 C++ 问题

Posted

技术标签:

【中文标题】在 main 中调用打印地图的函数时的 C++ 问题【英文标题】:C++ problem in calling a function in main that prints the map 【发布时间】:2011-01-05 17:02:29 【问题描述】:

我正在尝试打印地图的内容,这就是我的代码失败的地方。我已经测试了我所有的方法,我没有问题从文件中读取,过滤单词,将它放入地图,甚至打印功能都可以工作。 但是,当我从 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

如果您能提供任何帮助,我将不胜感激。 谢谢,阿德里安娜

【问题讨论】:

(希望我们有更好的方式来格式化 cmets!) 谢谢,我会尝试解决这个问题,看看是怎么回事。 您好,您能再看一下吗?我更新了问题并实施了这些更改。谢谢 您尚未更改 addToMap 函数以通过引用获取传递的地图。这是否反映了您的代码?您还删除了 delete &amp;obj 但没有将 obj 更改为堆栈对象,例如ReadPunctWords obj(name); 老实说,我完全迷失了 :-) 无论如何感谢您的帮助。好吧,修改后我得到了各种各样的错误。如果我的 addToMap 函数通过引用获取传递的地图,那么我想我需要更改头文件中的函数以获取对地图的引用?我不确定它的语法是什么。我要让 obj 成为一个堆栈对象并在一分钟内更新它。感谢您的帮助,但我是编程的初学者 :-) 希望帮助我不会花费太多时间。 【参考方案1】:

您的代码中有许多潜在问题,但最明显的可能导致 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 函数可能无法按预期工作,因为您将地图传递给函数按值,这意味着该函数正在添加项目的地图to 实际上是传入的地图的副本。

当控制权传递给调用函数时,这个副本被销毁,传递给它的地图仍然是空的。

要通过引用传递映射,您需要将&amp; 添加到函数声明中的参数类型。

即在头文件中,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 .

【讨论】:

谢谢,我在尝试注释掉的功能时已经这样做了,但仍然无法正常工作。你还能看出什么问题吗? 被注释掉的函数增加了一个不同于它用来打印的迭代器,所以它也是错误的。如果不查看其他源代码(例如 MapWorks.h 和 ReadWords..),很难知道还有什么问题。 MapWorks.h : #ifndef MAPWORKS_H #define MAPWORKS_H #include #include using namespace std; /** * MapWorks 类构建地图并进行地图处理和打印 */ class MapWorks public: map mapPunct; //(单词, 出现次数) map mapNum; //(单词, 出现次数) map mapCap; //(单词,出现次数) MapWorks(); void addToMap (string, map); //将单词添加到地图中 void printMap (map); //打印地图 void clearMap(map); //清除地图 ; #endif 我很抱歉这种错误的格式。我怎样才能将我的其他文件发送给您? 我很抱歉,但这作为评论完全不可读。为什么不更新您原来的问题?【参考方案2】:

你忘了增加迭代器:

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)

【讨论】:

我认为您的意思是:ReadPunctWords obj(name);ReadPunctWords* obj = new PunctWords(name);。从 RAII 的描述中我推测是前者。 我已经做到了,谢谢!但它仍然无法正常工作......你还能发现其他东西吗?【参考方案3】:

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


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

【讨论】:

以上是关于在 main 中调用打印地图的函数时的 C++ 问题的主要内容,如果未能解决你的问题,请参考以下文章

所有地图变体的通用打印功能 C++ STL

node源码详解 —— 在main函数之前 —— js和C++的边界,process.binding

main函数可否进行递归调用

在继承 C++ 中调用 main 中的参数化构造函数

C++在main中调用类的静态函数

c++ 错误:没有匹配的函数用于从函数内调用“getline”,但在 main 中有效