C++沉思录上提到的一道练习题及其源码实现

小白 C++ 入门并发疯学习路线(书单) 真人就是我自己,这是我的 C++ 学习之路总结。 具体总结前,想先回答一个问题——要不要先学C,再学C++? 我作为小白时疑惑过这个问题,在此给后来的小白和当年的自己一个答案。 个人看法是,学习C++,就直接学习C++,不用先学C。在微软维护STL的Stephen Thomas Lavavej都说他当年学C++之前学C语言浪费了大量的时间,早知道就直接从C++开始。 因此,我这篇文章基本不提C的学... 阅读详情

题目大意(具体问题可参考C++沉思录第九章):

现实中,一张图片,可以给予添加一层又一层片框,也可以和其他的图片组合在一块,或横,或竖,……,

如下图所示:

普通的图片:

wjj   
lqm   
lqmwjj
szb  

加框的图片:

+******+
|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;
};

#endif

picture.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;//引用计数
};

#endif

string_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;
};

#endif

string_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;
};

#endif

frame_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);
	else
		pad(os,_lpic.GetWidth(),' ');
	if(row < _rpic.GetHeight())
		_rpic.display(os,row);
	else
		pad(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   
lqmwjj
szb   
+******+
|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   |      |        
|*------*      |        
+**************+        


 

小白 C++ 入门到大神发疯学习路线 这篇文章实际上是我自己入门过程的总结,一个尽量少废话的C++入门指南。总结完忽然发现像个读书清单????。 具体总结前,想先回答一个问题——要不要先学C,再学C++? 我作为小白时疑惑过这个问题,在此给后来的小白和当年的自己一个答案。 个人看法是,单就学习C++而言,直接学习C++就可以了,不用先学C。在知乎看到轮子哥在微软维护STL的Stephen Thomas Lavavej都说他当年学C++... 阅读详情

相关推荐

C++ 沉思录随书实例源代码

内容索引:VC/C++源码,随书源码,随书源码  C++ 沉思录随书实例源代码,运行于C++环境,都是些小程序,C++打基础的用户学习学习。

C++沉思录书和源代码

C++沉思录书和源代码. C++沉思录书和源代码.

技术交流 412

Linux系统下奥比中光Astra Pro相机SDK部署与避坑指南

本文详细指导了在Linux系统下部署奥比中光Astra Pro深度相机SDK的全过程,涵盖系统准备、SDK安装、设备权限配置及常见问题排查。重点解析了USB设备规则配置这一关键步骤,并提供了Python与C++项目的集成方法,旨在帮助开发者快速完成相机配置,避免常见陷阱。

weixin_28455801的博客 298

C++ 沉思录源代码

         记得上学时,阅读过这本经典之作。当时没有实际项目经验,全是为了看书而看书,读完后,一本经典之作竟然一点触动都没有。        这次偶尔翻到此书,闲暇时间,再次拜读几个章节,影响极深,对C++以及面向对象又有了新的认识,打动了本来已经沉寂好久的求学欲望。回想这几年工作,全部精力都投入到了项目中。很多时候都处于赶进度的状态,为了快速得到结果,忽略了详细的斟酌过程。结果还是代码

leehs334的专栏 943

Synopsys PCIe IP Loopback功能深度解析与应用实践

本文深入解析了Synopsys PCIe IP的Loopback功能,包括本地数字环回和远程环回两种模式的应用实践。通过详细的寄存器配置步骤和实战案例,帮助开发者快速掌握PCIe芯片验证与调试技术,提升系统开发效率。特别适合PCIe协议相关的硬件工程师和验证工程师参考。

weixin_29198045的博客 530

kettle写doris操作

文件在目录:doris-master\extension\kettle\assemblies\plugin\target\doris-stream-loader-plugins-9.4.0.0-343.zip。官方提供了doris stream load插件,下载源码编译后放到kettle的插件目录即可使用。地址:https://github.com/apache/doris.git。注意:需要doris插件或者kettle转换生成模板的私聊博主。使用PDmaner同步业务库生成建表语句。

weixin_44449054的博客 3028

ruminations on c++ 沉思录 源代码

ruminations on c++ 源代码,for unix、windows、mac os。。。。。

摄心神,致虚极,守静笃 864

OpenPnP/Arduino - 二手西门子电动飞达的通讯协议解析与实战

本文详细解析了二手西门子电动飞达的通讯协议,并通过Arduino控制板实现与开源贴片平台OpenPnP的集成。文章涵盖了硬件接口定义、协议帧结构分析、Arduino控制实战、OpenPnP集成技巧以及二手飞达翻新指南,为开发者提供了全面的技术指导和实战经验。

weixin_30411819的博客 201

C++程序员必看书单 .

C++:       Prata《C++ Primer Plus》:基础,第一本书。(之前的版本也可,不过推荐最新的)       Lippman《Inside C++ Object Model》:初级,加深语言层次上的理解。不过有点小老了。里面的cfront编译器早退出江湖好久了。思想值得学习       侯捷《STL源码剖析》:第三本书,中级,深入STL       Vandevoor

u012368964的专栏 1058

快速了解Spyder

一 面板介绍菜单栏:放置所有功能工具栏:放置快捷菜单,可以通过菜单栏中的View的Toolbars的复选框来勾选工作区:写代码的地方属性页的标题栏:可以显示当前代码的名字和位置查看栏:查看文件、调试时的对象及变量输出栏:查看程序的输出信息,也可以作为shell终端来输入Python语句状态栏:用来显示当前文件权限、编码,光标指向位置和系统内存二 注释功能注释功能为编写代码很常用功能,下面介绍Spy...

实践求真知 1万+

让 ABAP BAPI_COSTACTPLN_POSTPRIMCOST 不再沉默:一套可落地的参数一致性校验与排错打法

SAP BAPI_COSTACTPLN_POSTPRIMCOST 沉默失败分析与解决方案 问题现象 使用 BAPI_COSTACTPLN_POSTPRIMCOST 上传计划数据时,接口执行不报错但数据未写入 COSP 等计划表,呈现"沉默失败"现象。 根本原因 索引关联断裂:INDEXSTRUCTURE 中 OBJECT_INDEX 和 VALUE_INDEX 未正确关联 COOBJECT 和 PERVALUE/TOTVALUE 表的数据 对象定义冲突:COOBJECT 中同一行填写了多

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 107

《十年沉思录》

转自MyBatis 中文官网 www.mybatis.cn 《十年沉思录》 _ 题记: _ 弹指十年,终成此文,有名其曰:沉思录。 本系列仅是一家之言,只供参考,望读者能明鉴,取其用,弃之误。 因个人原因,武断误信,误入歧途,以致劳心伤神,事倍功半,概不负责。最后告知:请不要过来找作者背锅。 联系作者 作者:MyBatis 中文官网,网址:http://www.mybatis.cn/,更新时间:2019 年 5 月 前言:作者的十年经历一、科目的学习方法总结 2007 暑假留校复习高数,一直到 2008

牧xin的博客 604

使用R语言实战:构建Logistic回归模型

Logistic回归是一种基于概率的分类算法,它用于预测离散的结果变量(也称为响应变量或因变量)。与线性回归不同,Logistic回归的输出是一个概率值,通常在0和1之间。这使得它非常适合处理二分类问题。市场营销:预测客户是否会购买某个产品。医学研究:预测疾病的发生与否。信用风险评估:预测个人申请贷款的违约风险。社会科学:预测个人对某个政策的态度。

2301_79331230的博客 1139

C++程序员必看书单

C++:       Prata《C++ Primer Plus》:基础,第一本书。(之前的版本也可,不过推荐最新的)       Lippman《Inside C++ Object Model》:初级,加深语言层次上的理解。不过有点小老了。里面的cfront编译器早退出江湖好久了。思想值得学习       侯捷《STL源码剖析》:第三本书,中级,深入STL       Vandevoor

追风筝的猪 1万+

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1198

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C++书籍推荐

经典c++书籍推荐

shuai1989912gege的专栏 332

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

模板类的友元函数

VS2013 编译《C++沉思录》ch14中的源码时,出现无法解析的外部符号: 错误 1 error LNK2019: 无法解析的外部符号 "class Pointer<int> __cdecl operator+(class Pointer<int> const &,int)" (??H@YA?AV?$Pointer@H@@ABV0@H@Z),该符号...

csenjoy的博客 283

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

如何提升C++水平?

你希望系统地提升自己的 C++ 编程水平,并想知道有哪些适合的书籍可以作为学习参考。提升 C++ 水平是一个循序渐进的过程,需要结合理论学习、大量实践和代码阅读,我会按学习阶段给你梳理清晰的路径和对应的书籍推荐。

人邮异步社区 2608

C++书籍下载

C# COM+编程指南(C#(CSharp)/CSharp) ASP.NET深入编程(C#)(C#(CSharp)/CSharp) Microsoft Visual CSharp .NET 2003 Developers Cookbook(C#(CSharp)/CSharp) Microsoft.Visual.C.Sharp.NET.2003.Kick.Start(C#(CSharp)/CShar

feijj2002_的专栏 4451

我的C++进阶之路

C++ primer,effective c++,C++编程思想,C++设计模式,STL源码剖析,C++对象模型……C++沉思录 数据结构与算法

frogview 1496

计算机读书笔记

应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。当表达式中存在有符号数和无符号数类型时,所有的操作都自动转换为无符号类型。可见无符号数的运算优先级高于有符号数。无符号数转换为有符号数时,对于小的数将保持原值,对于大的数将转换为负数,相当于原值减去2的n次方。有符号数转换为无符号数时,负数转换为大的正数,相当于在原值上加上2的n次方,而正数保持不变。无符号整数和有符号整数之间进行强制类型转换时,位模式不改变。相邻的字符串常量将会自动合并成一个字符串。

最佳损友1020的博客 2518

C++ GOOD BOOK

C# COM+编程指南(C#(CSharp)/CSharp) ASP.NET深入编程(C#)(C#(CSharp)/CSharp) Microsoft Visual CSharp .NET 2003 Developers Cookbook(C#(CSharp)/CSharp) Microsoft.Visual.C.Sharp.NET.2003.Kick.Start(C#(CSharp)/CShar

dankes 2142

C++ Primer Plus 读书总结

看的是英文第6版,分上、下册。 C语言比较熟悉,C++以前也学过一点,看过老谭的C++程序设计,不过由于长时间没怎么用,很多东西都忘了, 而现在在做TopCoder SRM的时候,发现里面大量运用STL算法,越发觉得C++的强大,于是决定好好学习一番。 C++书籍最有名的就要属C++ Primer了,大多数人都推荐这本书,不过我也看到了这本C++ Primer Plus,开始不 知

xuzhezhaozhao的专栏 2694

C++学习路线

C++学习路线推荐 《Accelerated C++》很适合新手,容易通读完,减少挫败感。 《Essential C++》比较适合有其他编程经验的中手迅速学习C++。 《A Tour of C++》,C++ creator扛鼎之作。 看书的同时可以看一些博客,交叉印证 C++ 教程:http://www.runoob.com/cplusplus/cpp-tutorial.html 你好,C++:h...

sk18192449347的博客 557
上一篇: C中的volatile用法
下一篇: C++中使用Lua脚本 和lua中调用c的方法
执迷的信徒
博客等级 码龄16年 245粉丝 60原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值