怎样实现图形多态显示?
比如说我有一个基类shape,它有两个虚函数draw()和move()功能,后来又从shape派生出来很多的其他的的基本图形类,比如说square,circle,ellipse等等,每一个类都重写了draw()和move(),我最终的目的是要在MFC程序的文档中存储绘制的图形列表,然后在视图中遍历图形列表,并显示出来,我应该怎么做?我所知道的就是,多态的显示可以这么做
//图形拖放
void dragAndDrop(shape& s) throw()//<-----一个多态函数
{
s.move(42,24);//<------动态绑定调用"合适的代码",绑定合适的派生图形
s.draw();//<只要是从shape派生来的类,都可以调用,就像可以预测未来一样
}当然了,这样简单的例子还行,一旦结合文档就麻烦了,为了记录每一个绘制的图形的数据,有必要建立一个列表.然后在视图中绘制的时候,遍历图形列表,使用图形多态显示.
多态的简单例子,可以参看 http://community.csdn.net/Expert/TopicView1.asp?id=3984730  中的部分内容.显示到还好说,基于前面的多态应用的例子就可以了,关键是在文档中存储每一种图形数据的时候,很麻烦.
你可以想象,每一个派生的基本图形需要记录的数据是不一样的(这个和它所拥有的成员变量有关),怎么样在DOC中记录这个图形列表也是一个难事,既然C++支持多态,我想的就是用多态的功能来实现,却不知道具体怎么实现多态序列化,又怎么在视图中调用多态显示.// shape.h: interface for the shape class.
//
//////////////////////////////////////////////////////////////////////#if !defined(AFX_SHAPE_H__79AFD738_5912_4037_91F0_32F03E4EBEDB__INCLUDED_)
#define AFX_SHAPE_H__79AFD738_5912_4037_91F0_32F03E4EBEDB__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000class shape
{
public:
shape(int x,int y) throw();
shape(const shape& s) throw();
virtual ~shape()   throw();
virtual void draw()const throw()=0;
virtual void move(int x,int y)  throw()=0;
protected:
int x_,y_;
void operator =(const shape& s) throw();

};=============================================
继承类,依稀记得代码来自C++ primier
class square : public shape  
{
public:
virtual void move(int x,int y) throw();
virtual void draw() const throw();
square(int x,int y,int width) throw();
virtual ~square();
protected:
int width_;};
class circle : public shape  
{
public:
virtual void move(int x,int y) throw();
virtual void draw() const throw();
circle(int x,int y,int radius);
virtual ~circle();protected:
int radius_;
};#endif // !defined(AFX_SHAPE_H__79AFD738_5912_4037_91F0_32F03E4EBEDB__INCLUDED_)/////////////很多代码就不列在这里了以前看过别人写的EastDraw,VCKBase有的下载,没有看过的人先去 
http://www.vckbase.com/code/viewcode.asp?id=1627 下载一个回来自己看看先.作者实现的方法我感觉很笨拙,虽然管理起来也很方便,添加新的图形类也很方便,但是文档记录和图形显示没有利用多态性,使用了很多switch,添加了一个图形类,就要改动很多地方.他的代码这里分析一下:
基类CUnit,派生于CObject,实现了添加了Serialize支持,和自绘制(当然,都是虚函数)
派生类:
CDLine,CCurve,CRectangle,CRoundRectangle,CEllipse,CRound,CPolygon,CLiEllipse,CRoundArc,CEllipseArc,CPolyBezier,CText,都有自己的Serialize,和自己重新实现DrawXXX()函数这些都无可厚非,如果是我,我也会这么做的.
问题的关键在后面,看看他是怎么实现图形数据的记录和保存的:1,文档数据成员,使用了一个很长的,各种图形类的列表,如下
class CEastDrawDoc : public CDocument
{
protected: // create from serialization only
  CEastDrawDoc();
  DECLARE_DYNCREATE(CEastDrawDoc)// Attributes
public:
  CObArray m_DLineArray;
  CObArray m_CurveArray;
  CObArray m_RectangleArray;
  CObArray m_RoundRectangleArray;
  CObArray m_EllipseArray;
  CObArray m_RoundArray;
  CObArray m_PolygonArray;
  CObArray m_LiEllipseArray;
  CObArray m_RoundArcArray;
  CObArray m_EllipseArcArray;
  CObArray m_PolyBezierArray;
  CObArray m_TextArray;
  省略......
}
2.序列化了,由上面的数据成员的定义,可以知道他只有下面的路走了:我下载的源代码中,没有序列化的代码,但是我猜想肯定是这样的:
void CEastDrawDoc::Serialize(CArchive& ar)
{
  if (ar.IsStoring())
  {    
    // TODO: add storing code here     
  }
  else
  {
    // TODO: add loading code here
  }
//////我添加的
  m_DLineArray.Serialize(ar);
  m_CurveArray.Serialize(ar);
  m_RectangleArray.Serialize(ar);
  省略......
}
3.CEastDrawView::OnDraw(),代码中也没有实现,但是我想肯定是这样的:void CEastDrawView::OnDraw(CDC* pDC)
{
    CEastDrawDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
    ///我添加的
//   一个循环
    pDoc->m_DLineArray[i].DrawXXX(CDC*...);
    pDoc->m_CurveArray[i].DrawXXX(CDC*...);
    pDoc->m_RectangleArray[i].DrawXXX(CDC*...);
    省略......
}诸位看官,看到这里你是否觉得作者这样的做法是否很好呢?我是不以为好的,但是不会说他差,只是觉得还有改进的地方.怎么改进,我这里也是和大家一起探讨,来实现图形的多态显示.所以就希望高人出来解决了!!解决只需要点拨关键的地方就可以了.我的做法是(全当作是抛砖引玉了):
文档数据存储
1.数据成员
// Attributes
public:
CTypedPtrList <CObList,CUnit *> m_UnitList;2.序列化
void CEastDrawDoc::Serialize(CArchive& ar)
{
  if (ar.IsStoring())
  {
    // TODO: add storing code here
  }
  else
  {
    // TODO: add loading code here
  }
  //保存图形列表
  m_UnitList.Serialize(ar);
}
图形显示
3.视图显示
void CEastDrawView::OnDraw(CDC* pDC)
{
    CEastDrawDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
    ///我添加的
//   一个循环
    pDoc->m_UnitList[i].DrawXXX(CDC*...);
}/////////////////////////////////////
但是我不知道这里会不会有问题出现,首先是序列化,我不能够肯定它能否实现图形多态序列化(自动调用派生类的Serialize()函数),然后是视图显示,我也不能够保证它能否实现图形多态显示(自动调用派生类的DrawXXX()函数).到此,我的问题我才觉得讲清楚了,到底应该怎样实现图形多态序列化和图形多态显示,大侠们,显显身手吧!!!!!!!!!!!!!!
强烈召唤你们!!!!

解决方案 »

  1.   

    VCKBase的程序看了,不能说好。
    我觉得还是增加一个中间的类比如CShapeManager,用来管理,序列化你的图形对象链表为好。
    关于多态,深入浅出MFC中关于虚函数的章节有清晰的解释和用法
    你的用法,m_UnitList.Serialize(ar);序列化可能没有问题,但是读出来就有问题了。
    解决方法是在每次序列化一个对象前写入图形的标识,读取的时候由CShapsManager根据读出的标识,动态创建对象,再把ar传给这个对象完成读取。
      

  2.   

    楼上的,你说的很有道理,我也是想在保存的说话添加一个对象唯一标志的,只是没有想到读取的时候要动态创建对象完成读取;同时也没有搞过什么Manager之类的C++类,你有过这个方面的经验吗?