一道考验你设计能力的C++编程题 (一)

2014-11-24 11:58:28 · 作者: · 浏览: 5
\

看到这道题,我们就开始设计这个图像类了,按照面向对象“依赖倒置”的设计原则,我们站在客户的立场,来考虑我们这个类该提供哪些接口,很快我们设计了如下一个类
class CSimplePicture
{
public:
CSimplePicture(char* init[], int nCount);
CSimplePicture(CSimplePicture& p1, CSimplePicture& p2, bool bVerCat);

void Frame();
void Print(std::ostream& os) const;
protected:
std::vector m_arData;
};

CSimplePicture(char* init[], int nCount);
根据字符串数组构造一幅图像.

CSimplePicture(CSimplePicture& p1, CSimplePicture& p2, bool bVerCat);
根据两幅图像构造一幅图像,bVerCat表明是纵联接还是横联接.

void Frame();
给图像对象加框

void Print(std::ostream& os) const;
打印输出图像

std::vector m_arData;
存储图像数据的字符串数组

下面来考虑具体实现,这个对于有一定开发的经验的人来说还是很容易的,就不具体写了,
CSimplePicture(char* init[], int nCount)无非是数据的拷贝,CSimplePicture(CSimplePicture& p1, CSimplePicture& p2, bool bVerCat)就是把2幅图片的数据连接,合在一起,void Frame()修改里面的数据加上边框,void Print(std::ostream& os) const遍历字符串数组输出。

根据上面的设计和实现,应该已经满足我们这个题目的要求了。
但是客户的需求是多变的,现在客户又有一个新的需求,要求把一幅图片去掉边框。
另外客户觉得我们这个图片类的性能太差了,每次加框或是合成图片都要大量的内存拷贝。

这时我们傻眼了,该死的客户,根据我们上面的设计,根本不支持这些新功能,因为我们存储的是图像的内部的字符串数据,根本不知道它是不是加框过的,另外我们的图像数据本身就是不支持共享的。

接下来我们就要重新考虑设计了,如何让我们的图像对象支持UnFrame(去边框)操作,关键是要建立我们的图像类型层次,这样就可以判断是否是加框的类对象,于是有了如下的类层次:
//图象接口基类
class CPic_Base
{};

//字符串图像类
class CPic_String: public CPic_Base
{};

//加框图像类
class CPic_Frame: public CPic_Base
{}

//纵联接图像类
class CPic_VCat: public CPic_Base
{};

//横联接图像类
class CPic_HCat: public CPic_Base
{};

然后我们考虑如何共享图像数据,这就要用到智能指针了,智能指针在C++里一般有2种实现,一种是STL 里的auto_ptr,还有一种就是基于引用计数。auto_ptr的本质是拥有关系,也就是你拥有了这对象后,别人就不能拥有了,所以这里不符合我们的要求。引用计数是个好东西,对于共享对象特别有用,COM里的IUnknow接口就是基于这个技术的,还有很多脚本语言里变量自动销毁,实际上都是基于引用计数的技术。这里分享一个基于引用计数的智能指针类。
class CRefCountBase
{
public:
CRefCountBase()
{
m_nRefCount = 0;
}

int GetRefCount() const
{
return m_nRefCount;
}

int AddRefCount()
{
return ++m_nRefCount;
}

int SubRefCount()
{
return --m_nRefCount;
}

void ResetRefCount()
{
m_nRefCount = 0;
}

private:
int m_nRefCount;
};

template
class CRefPtr
{
public:
T* operator->() const
{
return m_pRawObj;
}

T& operator()() const
{
return *m_pRawObj;
}

T& operator*() const
{
return *m_pRawObj;
}

T* GetPtr() const
{
return m_pRawObj;
}

bool IsNull() const
{
return m_pRawObj == NULL;
}

CRefPtr()
{
m_pRawObj = NULL;
}

CRefPtr(T* p)
{
m_pRawObj = p;
if(p != NULL)
{
p->AddRefCount();
}
}

CRefPtr(const CRefPtr& ref)
{
m_pRawObj = ref.m_pRawObj;
if(m_pRawObj != NULL)
{
m_pRawObj->AddRefCount();
}
}

~CRefPtr()
{
if(m_pRawObj != NULL && m_pRawObj->SubRefCount() == 0)
{
delete m_pRawObj;
}
}

CRefPtr& operator = (const CRefPtr& ref)
{
if(this != &ref)
{
if(m_pRawObj != NULL
&& m_pRawObj->SubRefCount() == 0)
{
delete m_pRawObj;
}

m_pRawObj = ref.m_pRawObj;

if(m_pRawObj != NULL)
{
m_pRawObj->AddRefCount();
}
}

return *this;
}

bool operator == (const CRefPtr& ref) const
{
return m_pRawObj == ref.m_pRawObj;
}

CRefPtr Copy()
{
if(m_pRawObj != NULL)
{
T* p = new T(*m_pRawObj);
p->ResetRefCount();

return p;
}
else
{
return NULL;
}
}

private:
T* m_pRawObj;
};

这样使用这个类
class A: public CRefCountBase
{
Public:
Void fun1();
};

CRefPtr p = new A;
p->fun1();

重新设计我们的CPic_Base,
class CPic_Base: public CRefCountBase
{
public:
virtual ~CPic_Base() {}

//打印输出图像
void Print(std::ostream& os) const;

//返回图像宽度
virtual int GetWidth() const = 0;

//返回图像高度
virtual int GetHeight() const = 0;

//返回某行的图像字符串数据
virtual std::string GetLineData(int nLineIndex) const = 0;

//返回去掉边框的对象
virtual CRefPtr GetUnFrame() const { return NULL; }
};

这里Print方法实现就很简单了:
voi