在 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 &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 实际上是传入的地图的副本。
当控制权传递给调用函数时,这个副本被销毁,传递给它的地图仍然是空的。
要通过引用传递映射,您需要将&
添加到函数声明中的参数类型。
即在头文件中,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你忘了增加迭代器:
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++ 问题的主要内容,如果未能解决你的问题,请参考以下文章