Some Tips On Using MFC and STL and Containers

做计算技术基础课吴老师的作业时候发现的一些问题和解决办法,留做备份。

作业如下:

1.两个输入文件:grade.txt及grade2.txt. grade.txt每行如:"0211111001 张三丰 99"
grade2.txt也有多行,每行形如:"0201121002 周伯通 老玩童 100"
2.用已提供的MyStl.h文件。
3.定义一个输出文件,名为“你的学号+result.txt“。
4.创立一个名为Homework.cpp的文件。
5.在Homework.cpp中用Grade 的CArray, CList, CMap, vector, list, queue, map分别做:
<A>读入grade.txt, grade2.txt于容器(即上述CArray, Clist 等)。
<B>将内容输出至输出文件(见4)。
<C>如果容器可以排序,将内容排序,再将结果输出至输出文件。
<D>删除所有grade==100的记录,再将结果输出至输出文件。

问题一:MFC库的使用

由于平常只是建立空文档,很少使用到MFC库。但当程序中使用到MFC的容器类之后,如果没有选择使用MFC库,会在link时候报出"error LNK2001"错误。
解决办法是在建立Win32 Console Application的时候在向导的第二个对话框里选择"Using MFC as shared dll"复选框。已经建立好工程的,在"工程"->"设置"->"常规"选项卡里找到"Microsoft Fundation Class"下拉框,选择"using MFC as shared dll"即可。

问题二:头文件的包含关系

由于作业涉及到MFC和STL,很多东西老的库和新的都会有冲突,还有命名空间的问题,最好对每个类别建立一个头文件和源程序文件,能尽量的避免出错。
我的方法是,用MFC来处理数据的程序为一个源文件,并在头文件里只包含和MFC有关的东西,其中要包含的系统头文件有:
#include <afx.h> //包含许多基类的声明,例如:CObject,CString
#include <afxtempl.h> //包含模板类的定义
#include <iostream.h> //标准输入输出流
#include <string.h> //包含字符串处理函数
#include <fstream.h> //文件输入输出流
用STL来处理数据的源程序的头文件包括:
#include <fstream> //文件输入输出流,标准C++
#include <iostream> //标准输入输出流,标准C++
#include <string> //字符串,串处理函数,串模板,STL
#include <algorithm> //算法,STL
#include <vector> //向量模板,STL
#include <list> //列表模板,STL
#include <queue> //队列模板,STL
#include <map> //散列表(准确的来说散列表是hash_map,但这里暂且这么称呼)模板,STL
using namespace std; //使用标准名字空间
#pragma warning(disable:4786) //不提示4786号警告,因为VC6.0与标准C++的不全相容性,对长名有警告

问题三:类的定义

由于显然性的两个文件的关系,定义类的时候可以采用继承的方法,注意变量尽量声明成protected类型,这样在派生类里可以直接访问父类的变量。其实在这里并不是完全有必要把数据封装起来,但封装会有很大的好处。比如说,通过重载函数来增加程序的一致性和可读性,重载运算符对算法的支持。下面是在MFC环境下的类定义,相应在标准C++环境下不同的地方主要是CString变为string,BOOL变为:bool,char*也可变为:string。
class cSwordsman
{
public:
cSwordsman(char* Sworder_ID = "",char* Sworder_NAME = "",int Sworder_SCORE = 0);
~cSwordsman();
CString GetId();
CString GetName();
int GetScore();
void SetId(char* Sworder_ID);
void SetName(char* Sworder_NAME);
void SetScore(int Sworder_SCORE);
BOOL operator<(cSwordsman& y);
BOOL operator>(cSwordsman& y);
BOOL operator==(cSwordsman& y);
void FileOut(ofstream& fout);
void ScreenOut();
protected:
CString id;
CString name;
int score;
};
class cLongSwordsman:public cSwordsman
{
public:
cLongSwordsman(char* Sworder_ID = "",char* Sworder_NAME = "",char* Sworder_MONIKER = "",int Sworder_SCORE = 0);
~cLongSwordsman();
CString GetMoniker();
void SetMoniker(char* Sworder_MONIKER);
void FileOut(ofstream& fout);//对父类成员函数的重载
void ScreenOut();
protected:
CString moniker;
};

问题四:文件输入和输出流的参数不同

文件输入输出流其实是一个类,平常所用的:ifstream fin("grade.txt",ios::in|ios::nocreate);其实是对象的初始化。所以在上面可以在FileOut函数的参数里设为:ofstream& fout。这样在函数里就可以直接用fout输出了,通过用ofstream对象的引用做函数参数,避免了频繁的打开文件。
在<fstream.h>和<fstream>中对文件流的定义是相似但不同的,最主要的一个方面表现在打开参数上,<fstream.h>中定义的打开模式是ios::in(输入)形式的参数,而<fstream>中是以ios_base::app(添加)形式的参数。
而且<fstream.h>和<fstream>的成员方法有些使用上差别很大,比如getline。

问题五:函数重载的使用减少工作量

比如由上面两个类的定义,可以使处理相应的类型数据的函数使用相同的名字,而在参数中使用两个不同的类引用作为参数,这样就能通过参数的不同调用不同的函数,而函数名是相同的,减少记忆和出错的可能。

问题六:运算符重载以使用已有算法

在STL的<algorithm>中定义了许多现成算法,比如:sort,就是根据类中"<"的重载来排序的。

问题七:文本文件的读取

文本文件可以用标准输入输出流来读,假如已经定义了ifstream fin("grade.txt",ios::in|ios::nocreate);,就可以像cin一样读取数据,但有时候可能需要更完善的方法,还可以用fin.getline来读入一行,然后再用字符串处理函数对这和串进行处理。

问题八:STL中iterator的使用

iterator实际是一种类型的变量,定义的时候根据模板的不同而不同。在vector,list和queue中,可以把iterator当作指向成员类的指针,用iterator->PublicVar,来使用公有成员变量,用(*iterator).MemFunctions,来使用成员函数。但在map中要注意iterator指向的是一个pair,pair是map模板中定义的一个类,有两个公有成员变量,first和second。其中iterator->first,代表使用的key,iterator->second才是模板成员类的对象。用iterator->second.MemFunctions,来调用成员函数。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注