用类继承完成文字的合纵连横——《C++沉思录》第10章

本文探讨了C++中Picture类存在的内存浪费问题,并通过引入句柄类和类继承的方法优化内存使用,避免了不必要的复制操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题
《C++沉思录:Ruminations on C++》由Picture类(负责文字信息的存储)、frame函数(创建新Picture并加框存储)、'|'运算符重载(创建新Picture,并将两幅文字作横向合并存储)、'&'运算符重载(创建新Picture,并将两幅文字做纵向合并存储)完成的方案,这种方法的缺点是:

  • 复制图像就要复制字符,而且把一副小图像并到大图像的过程中,也要复制其内部字符
  • 可能花费不少内存来存储空格,如果图像中某一行明显比其他各行长得多,那么这一开销就不容忽视了
  • 会丢掉所有图像内部结构信息,如果想写一个去掉边框的函数,我们无法区分真正的边框和字符

作者认为上述这些缺点是因为我们实现了Picture类来存储一个图像的“表象”,却没有存储其结构,本书第10章则利用了类继承和句柄类来解决上面得问题:一则可以将我们使用继承的实现细节隐藏起来,二则可以省去用户处理内存管理的麻烦,而提供给用户的接口不变(class Picture、frame()、|、&)。

解决内存问题
对于内存浪费的举个简单的例子就可以说明: 

1 char * init[] = { " Paris " , " in the " , " Spring " };
2 Picture p1(init, 3 );
3 Picture p2 = frame(p2);

 用第9章的代码,运行起来,在调试器里我们看到(注意p1、p2的data里面的内容)——

"Paris in the Spring"这段文字被复制后保存在两块内存中,在实际运行中,p2没有理由去复制p1的全部内容,看过前几章节的朋友可能会想到一个处理方法,没错,句柄类!
句柄类P_Node具有引用计数功能,并允许Picture类的访问,即:

1 class P_Node{
2 friend class Picture;
3   protected :
4 int use;
5 };

 解决保存内部结构方案

1 class String_Pic: public P_Node{}; // 直接由字符串数组生成的图像
2   class Frame_Pic: public P_Node{}; // 给其他图像加边框而形成的图像
3   class VCat_Pic: public P_Node{}; // 两幅图像横向连接所得的图像
4 class HCat_Pic: public P_Node{}; // 两幅图像纵向连接所得的图像

类继承树的方案也解决占用内存的问题——String_Pic用来存储文字内容,其他三个类只是引用它的内容,在打印的时候做加框和连接处理,如此用来我们再看第一个例子的运行情况:

 

类图

代码(被折叠)

 

class Picture
1 #ifndef PICTURE_HDL_H
2 #define PICTURE_HDL_H
3
4 #include < iostream >
5
6 using namespace std;
7
8 /* ****类定义*********************************************************************** */
9 class P_Node;
10 // Picture类定义
11 class Picture
12 {
13 friend Picture frame( const Picture & ); // 加框
14 friend Picture operator & ( const Picture & , const Picture & ); // 纵向合并
15 friend Picture operator | ( const Picture & , const Picture & ); // 横向合并
16 friend ostream & operator << (ostream & o, const Picture & p); // 输出运算符重载
17 friend static void pad(ostream & os, int x, int y);
18 friend class String_pic;
19 friend class Frame_Pic;
20 friend class Hcat_Pic;
21 friend class Vcat_pic;
22 public :
23 Picture( const char * const * , int );
24 Picture( const Picture & );
25 ~ Picture();
26 Picture & operator = ( const Picture & );
27 private :
28 Picture(P_Node * p_node): p(p_node){};
29 int height() const ;
30 int width() const ;
31 void display(ostream & , int , int ) const ;
32 P_Node * p;
33 };
34
35 // 句柄类
36 class P_Node
37 {
38 friend class Picture;
39 protected :
40 P_Node():use( 1 ){};
41 virtual ~ P_Node(){};
42 virtual int height() const = 0 ;
43 virtual int width() const = 0 ;
44 virtual void display(ostream & , int , int ) const = 0 ;
45 int max( int x, int y) const { return (x > y ? x : y);};
46 private :
47 int use;
48 };
49 // 文字图像句柄类
50 class String_Pic: public P_Node
51 {
52 friend class Picture;
53 String_Pic( const char * const * , int );
54 ~ String_Pic();
55 int height() const ;
56 int width() const ;
57 void display(ostream & , int , int ) const ;
58 char ** data;
59 int size;
60 };
61 // 加框图像句柄类
62 class Frame_Pic: public P_Node
63 {
64 friend Picture frame( const Picture & );
65 Frame_Pic( const Picture & pic):p(pic){};
66 int height() const { return p.height() + 2 ;};
67 int width() const { return p.width() + 2 ;};
68 void display(ostream & , int , int ) const ;
69 Picture p;
70 };
71 // 纵向合并图像句柄类
72 class Vcat_pic: public P_Node
73 {
74 friend Picture operator & ( const Picture & , const Picture & );
75 Vcat_pic( const Picture & t, const Picture & b):top(t),bottom(b){};
76 int height() const { return top.height() + bottom.height();};
77 int width() const { return max(top.width(), bottom.width());};
78 void display(ostream & , int , int ) const ;
79 Picture top, bottom;
80 };
81 // 横向合并图像句柄类
82 class Hcat_Pic: public P_Node
83 {
84 friend Picture operator | ( const Picture & , const Picture & );
85 Hcat_Pic( const Picture & l, const Picture & r):left(l),right(r){};
86 int height() const { return max(left.height(),right.height());};
87 int width() const { return left.width() + right.width();};
88 void display(ostream & , int , int ) const ;
89 Picture left,right;
90 };
91
92 /* ****类成员实现*********************************************************************** */
93 Picture::Picture( const char * const * array, int n):p( new String_Pic(array,n))
94 {
95 }
96
97 Picture::Picture( const Picture & orig):p(orig.p)
98 {
99 orig.p -> use ++ ;
100 }
101
102 Picture:: ~ Picture()
103 {
104 if ( -- p -> use == 0 )
105 delete p;
106 }
107
108 Picture & Picture:: operator = ( const Picture & orig)
109 {
110 orig.p -> use ++ ;
111 if ( -- p -> use == 0 )
112 delete p;
113 p = orig.p;
114 return * this ;
115 }
116
117 int Picture::height() const
118 {
119 return p -> height();
120 }
121
122 int Picture::width() const
123 {
124 return p -> width();
125 }
126
127 void Picture::display(ostream & o, int x, int y) const
128 {
129 p -> display(o, x, y);
130 }
131
132 String_Pic::String_Pic( const char * const * p, int n) : data( new char * [n]),size(n)
133 {
134 for ( int i = 0 ; i < n; i ++ )
135 {
136 data[i] = new char [strlen(p[i]) + 1 ];
137 strcpy(data[i], p[i]);
138 }
139 }
140
141 String_Pic:: ~ String_Pic()
142 {
143 for ( int i = 0 ; i < size; i ++ )
144 delete[] data[i];
145 delete[] data; // 注意此处销毁二维数组的方式
146 }
147
148 int String_Pic::height() const
149 {
150 return size;
151 };
152
153 int String_Pic::width() const
154 {
155 int n = 0 ;
156 for ( int i = 0 ; i < size; i ++ )
157 {
158 n = max(n, strlen(data[i]));
159 }
160 return n;
161 };
162
163 void String_Pic::display(ostream & os, int row, int wid) const
164 {
165 int start = 0 ;
166 if (row >= 0 && row < height())
167 {
168 os << data[row];
169 start = strlen(data[row]);
170 }
171 pad(os, start, wid);
172 }
173
174 void Frame_Pic::display(ostream & os, int row, int wid) const
175 {
176 if (row < 0 || row >= height())
177 {
178 // 越界
179 pad(os, 0 , wid);
180 }
181 else
182 {
183 if (row == 0 || row == height() - 1 )
184 {
185 os << " + " ;
186 int i = p.width();
187 while ( -- i >= 0 )
188 os << " - " ;
189 os << " + " ;
190 }
191 else
192 {
193 os << " | " ;
194 p.display(os, row - 1 , p.width());
195 os << " | " ;
196 }
197 pad(os, width(), wid);
198 }
199 }
200
201 void Vcat_pic::display(ostream & os, int row, int wid) const
202 {
203 if (row >= 0 && row < top.height())
204 top.display(os, row, wid);
205 else if (row < top.height() + bottom.height())
206 bottom.display(os, row - top.height(), wid);
207 else
208 pad(os, 0 , wid);
209 }
210
211 void Hcat_Pic::display(std::ostream & os, int row, int wid) const
212 {
213 left.display(os, row, left.width());
214 right.display(os, row, right.width());
215 pad(os, width(), wid);
216 }
217
218 /* ****友元函数实现*********************************************************************** */
219 // 加框函数
220 Picture frame( const Picture & p)
221 {
222 return new Frame_Pic(p);
223 }
224 // 运算符重载,纵向合并
225 Picture operator & ( const Picture & t, const Picture & b)
226 {
227 return new Vcat_pic(t, b);
228 }
229 // 运算符重载,横向合并
230 Picture operator | ( const Picture & l, const Picture & r)
231 {
232 return new Hcat_Pic(l, r);
233 }
234 // 输出运算符重载
235 ostream &
236 operator << (ostream & o, const Picture & p)
237 {
238 int ht = p.height();
239 for ( int i = 0 ; i < ht; i ++ )
240 {
241 p.display(o, i, 0 ); // 原书勘误:p.display(o,i,o);
242 o << endl;
243 }
244 return o;
245 };
246 // 负责打印y-x个空格
247 static void pad(ostream & os, int x, int y)
248 {
249 for ( int i = x; i < y; i ++ )
250 os << " " ;
251 }
252
253 #endif
 
 

转载于:https://www.cnblogs.com/blueclue/archive/2010/02/08/1665945.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值