AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > C++

C++开发中数据结构和算法的分离

51自学网 http://www.wanshiok.com

  那么如何实现高度的抽象和良好的接口呢?我们现场请来OO(object orient),请它来讲一下它的实现。设计如下派生关系:
  


//================================================================
class FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) PURE ;
} ;
//================================================================
class FCPixelInvert : public FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
} ;
void FCPixelInvert::ProcessPixel (int x, int y, BYTE * pPixel)
{
 pPixel[0] = ~pPixel[0] ; pPixel[1] = ~pPixel[1] ; pPixel[2] = ~pPixel[2] ;
}
//================================================================
class FCPixelAdjustRGB : public FCSinglePixelProcessBase
{
 public :
  FCPixelAdjustRGB (int DeltaR, int DeltaG, int DeltaB) ;
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
 protected :
  int m_iDeltaR, m_iDeltaG, m_iDeltaB ;
} ;
void FCPixelAdjustRGB::ProcessPixel (int x, int y, BYTE * pPixel)
{
 pPixel[0] = FClamp0255 (pPixel[0] + m_iDeltaB) ;
 pPixel[1] = FClamp0255 (pPixel[1] + m_iDeltaG) ;
 pPixel[2] = FClamp0255 (pPixel[2] + m_iDeltaR) ;

}
//================================================================

  然后我们修改image类如下:

//================================================================
#include "PixelProcessor.h"
class FCObjImage
{
 public :
  void PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress = NULL) ;
} ;
//================================================================
void FCObjImage::PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress)
{
 if (GetHandle() == NULL)
  return ;

 int nSpan = ColorBits() / 8 ; // 每象素字节数3, 4
 for (int y=0 ; y < Height() ; y++)
 {
  BYTE * pPixel = GetBits (y) ;
  for (int x=0 ; x < Width() ; x++, pPixel += nSpan)
  {
   PixelProcessor.ProcessPixel (x, y, pPixel) ;
  }
  if (progress != NULL)
   progress->SetProgress (y * 100 / Height()) ;
 }
}
//================================================================
void FCObjImage::Invert (FCObjProgress * progress)
{
 PixelHandler (FCPixelInvert(), progress) ;
}
void FCObjImage::AdjustRGB (int R, int G, int B, FCObjProgress * progress)
{
 PixelHandler (FCPixelAdjustRGB (R,G,B), progress) ;
}
//================================================================

  (以上只是一个基本框架,你可以很轻易的把区域处理的参数添加进去-通过构造时传递一个RECT参数。)

  对象真的是一个很奇妙的东西,它可以对外提供一个简单的接口,而自身又可以封装上很多附加信息。

  好,现在让我们来检验一下刚才的成果:添加一个给图像奇数行置黑,给偶数行置白的操作。

//================================================================class FCPixelTest : public FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
} ;
void FCPixelTest::ProcessPixel (int x, int y, BYTE * pPixel)
{
 if (y % 2) pPixel[0]=pPixel[1]=pPixel[2] = 0 ;
 // 奇数行
 else
  pPixel[0]=pPixel[1]=pPixel[2] = 0xFF ;
// 偶数行
}

  然后进行如下调用:

PixelHandler (FCPixelTest(), progress) ;
//================================================================

  多么的和谐美妙,设计算法的人员只需写出自己的算法,而不用去考虑怎么让它支持进度条和区域这些问题。感觉这就象一把设计优良的AK,你可以不断的往里添加子弹(对象)^-^

  至此,我们应该已经大功告成了。还有问题吗?

  等等,别忙,有些地方不太对,我添加这个算法后,怎么编译这么久啊。

  问题就出在那个不起眼的:

  #include "PixelProcessor.h"

  image是图像处理的最底层对象,工程中的所有文件都直接或间接地包含它,因此,任何对image.h本身及它所包含的.h的修改都会引起几乎整个工程的build,这当然是无法忍受的,解决的办法是使用“前置声明”,因为在PixelHandler接口中我们只需要它的引用(也即是说:我(接口)并不需要知道传给我的类的内部结构,给我一个32(64)的内存地址就OK了)。

  因此我们把

#include "PixelProcessor.h"

  替换成:

class FCSinglePixelProcessBase ; // external class 前置声明

  然后在.cpp文件中再包含PixelProcessor.h,这样,对PixelProcessor.h的改变仅仅会导致.cpp文件的重新编译,大大节约了编译时间。

  总结:

  1)可能的话,在编程中永远也别去想“拷贝代码”这个字眼。毕竟,OO就是为了抽象和代码重用才诞生的。

  2)除非必要,否则类的成员变量和函数的参数尽量用指针或引用代替,这样做可以在.h中尽可能地少包含其他.h文件,而用前置声明来替代,以此来减少编译时间和以后可能会产生的交叉包含。

  3)最后说一下效率问题:有些朋友可能会说每个像素都调用虚函数会影响性能,这的确,但实际的损失远没有想象的大。我实测了一下:对1024*768的图片进行反片处理,速度只有5%左右的损失,进行复杂处理(亮度/对比度/gamma)时损失可完全忽略,毕竟多出来的那部分代码只是进出栈和查表,而不是浮点除这样耗时的指令。

 
 

上一篇:深入剖析C++重载函数的应用  下一篇:异步消息的传递之回调机制