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

2014-11-24 11:58:28 · 作者: · 浏览: 6
d CPic_Base::Print(std::ostream& os) const
{
for(int i=0; i {
os << GetLineData(i);
os << "\n";
}
}


然后考虑实现CPic_String
class CPic_String: public CPic_Base
{
public:
CPic_String(char* p[], int nCount);

virtual int GetWidth() const;
virtual int GetHeight() const;
virtual std::string GetLineData(int nLineIndex) const;


protected:
std::vector m_arData;
};
这个类里存储真正的字符串图像数据,里面方法的实现也很简单,和最开始的的第一种实现类似,就不详写了。


再考虑实现CPic_Frame
class CPic_Frame: public CPic_Base
{
public:
CPic_Frame(CRefPtr& pic);

virtual int GetWidth() const;
virtual int GetHeight() const;
virtual std::string GetLineData(int nLineIndex) const;

virtual CRefPtr GetUnFrame() const { return m_pic; }

protected:
CRefPtr m_pic;
};
可以看到这里我们引用了一个其他的图像数据,而不是真正存储这些数据,方法实现也很简单, 主要依赖于m_pic所指向的图像类,同时m_pic是个基于引用计数的智能指针, 所以赋值时也没有内存拷贝, 注意GetUnFrame这个方法只有这里返回非NULL,表示只有这种对象支持去边框。
CPic_Frame::CPic_Frame(CRefPtr& pic)
: m_pic(pic)
{
_ASSERTE(!m_pic.IsNull());
}

int CPic_Frame::GetWidth() const
{
return m_pic->GetWidth() + 2;
}

int CPic_Frame::GetHeight() const
{
return m_pic->GetHeight() + 2;
}

string CPic_Frame::GetLineData(int nLineIndex) const
{
int nWidth = GetWidth();
int nHeight = GetHeight();

_ASSERTE(nLineIndex < nHeight && nLineIndex >= 0);

if(nLineIndex == 0 //first line and last line
|| nLineIndex == nHeight - 1)
{
int nPadding = nWidth - 2;
return string("+") + string(nPadding, '-') + string("+");
}
else
{
return string("|") + m_pic->GetLineData(nLineIndex - 1) + string("|");
}
}

再考虑实现CPic_VCat
class CPic_VCat: public CPic_Base
{
public:
CPic_VCat(CRefPtr& pic1, CRefPtr& pic2);

virtual int GetWidth() const;
virtual int GetHeight() const;
virtual std::string GetLineData(int nLineIndex) const;

protected:
CRefPtr m_pic1;
CRefPtr m_pic2;
};
他里面存储了上下2个图像对象,方法实现是也不复杂,就不具体写了。

另外CPic_HCat也是类似:
class CPic_HCat: public CPic_Base
{
public:
CPic_HCat(CRefPtr& pic1, CRefPtr& pic2);

virtual int GetWidth() const;
virtual int GetHeight() const;
virtual std::string GetLineData(int nLineIndex) const;

protected:
CRefPtr m_pic1;
CRefPtr m_pic2;
};

有了上面的实现,现在我们可以这么实现我们需要的功能了:
Int main()
{
char* init1[] = {"Paris", "in the", "Spring"};
CRefPtr p1 = new CPic_String(init, 3);

CRefPtr p2 = new CPic_Frame(p1);

CRefPtr p3 = new CPic_VCat(p1, p2);

P3->Print(cout);
CRefPtr p4 = p2->GetUnFrame();
}

这时我们发现这样对于客户调用很不友好,因为我们内部实现的类层次都暴露给客户了,而这些信息对客户来说应该都是透明的,我们应该再封装一个更简单的界面类给客户。

于是有了如下的设计,其实接口类似我们的第一种实现。
class CPicture
{
public:
CPicture(char* p[], int nCount);
CPicture(CPicture& p1, CPicture& p2, bool bVerCat);

void Frame();
bool UnFrame();

friend std::ostream& operator << (std::ostream& os, const CPicture& pic);

protected:
CRefPtr m_pic;
};

std::ostream& operator << (std::ostream& os, const CPicture& pic);

这样对客户来说他们只需要和CPicture打交道,根本不用关心内部的实现。
这个类的实现也很简单:
CPicture::CPicture(char* p[], int nCount)
{
m_pic = new CPic_String(p, nCount);
}

CPicture::CPicture(CPicture& pic1, CPicture& pic2, bool bVerCat)
{
if(!bVerCat)
{
m_pic = new CPic_HCat(pic1.m_pic, pic2.m_pic);
}
else
{
m_pic = new CPic_VCat(pic1.m_pic, pic2.m_pic);
}
}

void CPicture::Frame()
{
m_pic = new CPic_Frame(m_pic);
}

bool CPicture::UnFrame()
{
CRefPtr p = m_pic->GetUnFrame();
if(!p.IsNull())
{
m_pic = p;
}

return !p.IsNull();
}

std::ostream& operator << (std::ostream& os, const CPicture& pic)
{
pic.m_pic->Print(os);
return os;
}

下面是我们使用这个类的代码:
char* init1[] = {"Paris", "in the", "Spring"};
char* init2[] = {"Hello world", "every", "thing", "is", "OK!"};

int main(int argc, char* argv[])
{
CPicture p1(init1, 3);
CPicture p2(init2, 5);

//
std::cout << p1;
cout <

//
std::cout << p2;
cout <

//
p2.Frame();
cout << p2;
cout <

//
p1.F