C++沉思录上提到的一道练习题及其源码实现
题目大意(具体问题可参考C++沉思录第九章):
现实中,一张图片,可以给予添加一层又一层片框,也可以和其他的图片组合在一块,或横,或竖,……,
如下图所示:
普通的图片:
wjj lqm lqmwjjszb
加框的图片:
+******+|wjj ||lqm ||lqmwjj||szb |+******+
前两个图片横向连接,再纵向连接,再加框的图片
+**************+|wjj +******+||lqm |wjj |||lqmwjj|lqm |||szb |lqmwjj||| |szb ||| +******+||+******+ |||wjj | |||lqm | |||lqmwjj| |||szb | ||+******+ |+**************+
默认情况下:横向连接顶端对齐,纵向连接左边对齐。
以下为自己看书后写的源代码:
picture.h
#ifndef _PICTURE_H#define _PICTURE_H#include <cstdlib>#include <vector>#include <string>#include "pnode.h"#include "string_pic.h"//图片句柄类,又称作智能指针类class Picture{//重载输出运算符friend std::ostream & operator<<(std::ostream &os, const Picture &p);friend class FramePic;friend class VcatPic;friend class HcatPic;public:Picture() : _p(NULL){}Picture(const Picture & p) : _p(p._p){++p._p->use;}Picture & operator=(const Picture &p){if(_p != p._p){if(--_p->use == 0){delete _p;}_p = p._p;++p._p->use;}return *this;}//默认构造StringPic类型的图片Picture(const std::vector<std::string> &lines) : _p(new StringPic(lines)){}~Picture(){if(--_p->use == 0)delete _p;}//每次输出图片的一行,其实是调用的对应图像累的display函数void display(std::ostream &os, int row) const;//四个实用操作,原书采用的全局函数,这里用的类的静态成员函数static const Picture Frame(const Picture &p, char corner_ch, char side_ch, char top_ch);static void Reframe(const Picture &p, char corner_ch, char side_ch, char top_ch);static const Picture Vcat(const Picture &top, const Picture &bottom);static const Picture Hcat(const Picture &left, const Picture &right);private:int GetWidth() const{return _p->width;}int GetHeight() const{return _p->height;}//私有的构造函数,实现由实际图片类型的指针到Picture的隐式转换Picture(Pnode *p) : _p(p){++p->use;}//指向实际的图片类型Pnode *_p;};#endifpicture.cpp
#include "picture.h"#include "frame_pic.h"#include "vcat_pic.h"#include "hcat_pic.h"void Picture::display(std::ostream &os, int row) const{_p->display(os,row);}std::ostream & operator<<(std::ostream &os, const Picture &p){for(int i = 0; i < p.GetHeight() - 1; ++i){p.display(os,i);os << std::endl;}p.display(os,p.GetHeight() - 1);return os;}const Picture Picture::Frame(const Picture &p, char corner_ch, char side_ch, char top_ch){return new FramePic(p, corner_ch, side_ch, top_ch);}const Picture Picture::Vcat(const Picture &top, const Picture &bottom){return new VcatPic(top, bottom);}const Picture Picture::Hcat(const Picture &left, const Picture &right){return new HcatPic(left, right);}void Picture::Reframe(const Picture &p, char corner_ch, char side_ch, char top_ch){static_cast<FramePic*>(p._p)->Reframe(corner_ch, side_ch, top_ch);}
pnode.h
#ifndef _PNODE_H#define _PNODE_H#include <iosfwd>//输出流的前置声明#include <string>//图片类的基类,实现动态绑定class Pnode{friend class Picture;protected:Pnode(int w, int h) : width(w), height(h), use(1){}//重要的虚函数,每次显示一行图片内容virtual void display(std::ostream &os, int row) const = 0;//用于填充空格符void pad(std::ostream &os, int num, char ch) const{for(int i = 0; i < num; ++i){os << ch;}}int width;int height;int use;//引用计数};#endifstring_pic.h
#ifndef _STRING_PIC_H#define _STRING_PIC_H#include <vector>#include <string>#include <algorithm>#include <functional>#include "pnode.h"class StringPic : public Pnode{friend class Picture;struct StrShorter : std::binary_function<std::string, std::string, bool>{bool operator()(const std::string& str1, const std::string& str2) const{return str1.size() < str2.size();}};StringPic(const std::vector<std::string> &lines);virtual void display(std::ostream &os, int row) const;std::vector<std::string> _lines;};#endifstring_pic.cpp
#include "string_pic.h"StringPic::StringPic(const std::vector<std::string> &lines) : Pnode(std::max_element(lines.begin(),lines.end(),StrShorter())->length(), lines.size()), _lines(lines){++use;}void StringPic::display(std::ostream &os, int row) const{os << _lines[row];pad(os,width - _lines[row].size(),' ');}frame_pic.h
#ifndef _FRAME_PIC_H#define _FRAME_PIC_H#include "picture.h"#include "pnode.h"class FramePic : public Pnode{friend class Picture;FramePic(const Picture &p, char corner_ch, char side_ch, char top_ch);virtual void display(std::ostream &os, int row) const;void Reframe(char corner_ch, char side_ch, char top_ch);Picture _p;char _corner_ch;char _side_ch;char _top_ch;};#endifframe_pic.cpp
#include "frame_pic.h"FramePic::FramePic(const Picture &p, char corner_ch, char side_ch, char top_ch) :Pnode(p.GetWidth()+2, p.GetHeight()+2),_p(p), _corner_ch(corner_ch), _side_ch(side_ch), _top_ch(top_ch){}void FramePic::display(std::ostream &os, int row) const{if(row == 0 || row == height - 1){os << _corner_ch;for(int i = 0; i < width-2; ++i){os << _top_ch;}os << _corner_ch;}if(row > 0 && row < height - 1){os << _side_ch;_p.display(os,row - 1);os << _side_ch;}}void FramePic::Reframe(char corner_ch, char side_ch, char top_ch){_corner_ch = corner_ch;_side_ch = side_ch;_top_ch = top_ch;}vcat_pic.h
#ifndef _VCAT_PIC_H#define _VCAT_PIC_H#include "picture.h"#include "pnode.h"class VcatPic : public Pnode{friend class Picture;VcatPic(const Picture &top_p, const Picture &bottom_p);virtual void display(std::ostream &os, int row) const;Picture _top_p;Picture _bottom_p;};#endif
vcat_pic.cpp
#include "vcat_pic.h"VcatPic::VcatPic(const Picture &top_p, const Picture &bottom_p) :Pnode(std::max(top_p.GetWidth(), bottom_p.GetWidth()), top_p.GetHeight() + bottom_p.GetHeight()),_top_p(top_p), _bottom_p(bottom_p){}void VcatPic::display(std::ostream &os, int row) const{if(row < _top_p.GetHeight()){_top_p.display(os,row);pad(os,width - _top_p.GetWidth(),' ');}else{_bottom_p.display(os,row - _top_p.GetHeight());pad(os,width - _bottom_p.GetWidth(),' ');}}
hcat_pic.h
#ifndef _HCAT_PIC_H#define _HCAT_PIC_H#include "picture.h"#include "pnode.h"class HcatPic : public Pnode{friend class Picture;HcatPic(const Picture &lpic,const Picture &rpic);virtual void display(std::ostream &os, int row) const;Picture _lpic;Picture _rpic;};#endif
hcat_pic.cpp
#include "hcat_pic.h"HcatPic::HcatPic(const Picture &lpic,const Picture &rpic) :Pnode(lpic.GetWidth() + rpic.GetWidth(), std::max(lpic.GetHeight(), rpic.GetHeight())),_lpic(lpic), _rpic(rpic){}void HcatPic::display(std::ostream &os, int row) const{if(row < _lpic.GetHeight())_lpic.display(os,row);elsepad(os,_lpic.GetWidth(),' ');if(row < _rpic.GetHeight())_rpic.display(os,row);elsepad(os,_rpic.GetWidth(),' ');}
main.cpp
#include <iostream>#include <fstream>#include <vector>#include <string>#include "picture.h"using namespace std;int main(int argc, char *argv[]){ofstream outfile("d:/hello.txt");//图像信息保存在vector中,原书用的字符数组//目前写c++程序建议用vector代替数组vector<string> pic;pic.push_back("wjj");pic.push_back("lqm");pic.push_back("lqmwjj");pic.push_back("szb");//创建一个图片Picture p(pic);outfile << p << endl;//创建一个加边框的图片Picture frame_pic = Picture::Frame(pic,'+','|','*');outfile << frame_pic << endl;//综合使用横向连接与纵向连接Picture all_p = Picture::Frame(Picture::Vcat(Picture::Hcat(pic,frame_pic), frame_pic),'+','|','*');outfile << all_p << endl;//改变图像的边框样式Picture::Reframe(frame_pic, '*', '|', '-');outfile << frame_pic << endl;//再连接一次outfile << Picture::Hcat(all_p,frame_pic) << endl;return 0;}
测试输出结果:hello.txt
wjj lqm lqmwjjszb +******+|wjj ||lqm ||lqmwjj||szb |+******++**************+|wjj +******+||lqm |wjj |||lqmwjj|lqm |||szb |lqmwjj||| |szb ||| +******+||+******+ |||wjj | |||lqm | |||lqmwjj| |||szb | ||+******+ |+**************+*------*|wjj ||lqm ||lqmwjj||szb |*------*+**************+*------*|wjj *------*||wjj ||lqm |wjj |||lqm ||lqmwjj|lqm |||lqmwjj||szb |lqmwjj|||szb || |szb ||*------*| *------*| |*------* | ||wjj | | ||lqm | | ||lqmwjj| | ||szb | | |*------* | +**************+