- 在线时间
- 0 小时
- 最后登录
- 2007-9-23
- 注册时间
- 2004-9-10
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 9975 点
- 威望
- 7 点
- 阅读权限
- 150
- 积分
- 4048
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1893
- 主题
- 823
- 精华
- 2
- 分享
- 0
- 好友
- 0

我的地盘我做主
该用户从未签到
 |
<TABLE width="100%" border=0>5 o/ K# C. A* V C! P) Y. L2 e
; ~5 d7 g! o* r G+ D; l4 g: [# s! y' g<TR>
" z$ F) o1 t, u' w* l<TD width="7%"> </TD>0 B# ^1 I% X& n5 F4 ]; }
<TD width="85%">
" d0 u0 U, X6 [- l" {( }<DIV class=Section1 style="LAYOUT-GRID: 15.6pt none">
( L3 c/ j1 `9 @" V- A< align=center></P>
) z6 `+ v" M7 t< ><B>5 </B><B>大规模程序设计</B>(Large-scale Programming)<B></B></P>
2 Y! z% T; `1 {; T& K< ><B> </B></P>" m( z1 I; @/ U J7 D; A
< >本文的主要内容涵盖的是C++所支持的主要程序设计风格。然而namespace和异常处理机制虽然是重要的语言特性,但却并不属于这种讨论范畴,因为它们在所有的程序设计风格中都能支持大规模程序设计。在将分散的部件组合成完整程序的过程中,namespace和异常处理机制被用来缓减整个过程的难度和复杂度;随着程序规模的增大,它们也随之起到越来越重要的作用。</P>$ d6 Z- Q" ?4 N* F. m
< > </P>
: W9 y2 x) U' F3 Q< ><B>5</B><B>.</B><B>1 </B><B>异常和错误处理</B>(Exceptions and Error Handling)</P>
7 M( b6 q1 A- _7 \< >当在某个位置检测到一个错误的时候,我们使用<I>异常</I>来把程序的控制权转交到能够处理这种错误的调用者(caller)那里。显然,这种机制只用来处理那些在当前区域内无法处理的错误。</P>& H4 c& q0 P9 d
< >有人可能会问:“如果一个位置所发生的事情最终能够被正确的处理并使得程序如期正常运行,那么这件事情又怎么能被认为是一个错误呢?”事实上,我们把这类异常事件(或简称异常)以及用来处理这类事件的语言机制一起成为异常处理(exception handling)。</P>+ a, n4 a% [8 R* Z
< >我们可以像这样报告Stack中发生的上溢(overflow)和下溢(underflow)错误:</P>
& y+ i+ t! b7 I* p( D1 i< >template<class T> class Stack {</P>: U2 H- _$ `5 a4 x* _5 m5 ]5 w0 l3 o
< >T* v;</P>
k7 j: o' o+ u< >Int max_size;</P>9 z$ T1 r, v8 [+ P1 N7 o
< >Int top;</P>; ?* I! I. I Y% O. F. w! g8 @/ h- z
< > ublic:</P>/ W. B8 }3 R( W! ?
< >class Underflow { }; // 用来报告下溢情况的型别</P>- K1 D4 c& O" Y# v: _8 z
< >class Overflow { }; // 用来报告上溢情况的型别</P>
; H1 _1 x, {5 {: D< >Stack(int s); // 构造函数</P>
7 R9 d+ P% O4 |" L! y< >~Stack(); // 析构函数</P>
1 q+ B! p) c8 n) G< >void push(T c)</P>
- L8 z d; x: g1 z< >{</P>
, ~6 m6 E# l0 f< > if (top == max_size) throw Overflow(); // 检查是否发生错误</P> r4 [( ]3 t* V) P
< > v[top++] = c; // 增加top并将c存放在top位置</P>
$ G- f9 L8 S$ D, O* g< >}</P>
$ b, b/ k5 w; [< >T pop()</P>
0 z* h: y1 g# c8 R< >{</P>! R3 p9 ^6 w( {
< > if (top == 0) throw Underflow(); // 检查是否发生错误</P>
1 @# W5 Z6 g' ^1 R* M* c; i< > return v[--top]; //减少top</P>
& s" K9 Z* _1 f* U' L ]" B* B< >}</P>
9 I1 a. |$ p& g! O< >};</P>% i' R+ O+ T: a |. }' w, y
<P>一个被抛出的对象能够被抛出该对象的那个函数中的相关调用者(caller)捕获。例如:</P>& T% ^ Y/ o; H: a# B' Z0 A- R
<P>void f()</P>0 ~- v; y* |% L% `
<P>{</P>& c( A' I1 g( l
<P >Stack<string> ss (10);</P>
5 B4 ?/ A) u( G6 n( s; s6 {( h<P >Try {</P>
1 \- r. j, z/ o7 ^: {% T<P > ss.push(“Quiz”);</P>7 ~8 B: Y+ J, ^3 b# Q0 v
<P > string s = ss.pop(); // 用pop操作弹出”Quiz”</P>( t8 w. t+ k. ~. ^9 r
<P > ss.pop(); // 这一句企图对一个空的string施以pop操作,将会抛出Underflow</P>: r1 n' T5 [* n) a" N
<P >}</P>
. K7 F4 Z1 j) v) |<P >catch (Stack<string>::Underflow) { // 异常处理器</P>$ z f& G9 k) `. t& Y& z' A
<P > cerr << “error: stack underflow”;</P>
2 e7 ?( j5 z4 w<P > return;</P>. c* m$ g4 T1 F" O
<P >}</P>
/ P# `, {+ N( B8 A1 L: }<P >catch (Stack<string>::Overflow) { // 异常处理器</P>( m3 K @' z- g! c( a0 G
<P > cerr << “error: stack overflow”;</P>
' [( W- y) ^! L; H<P > return;</P>
" o, O- g* o' H( e+ G- O+ E2 f<P >}</P>) d2 ^( p' w1 B' ]& D7 C
<P >// …</P>
) W$ X3 }3 V8 I, c3 f7 }; q" L* h<P>}</P>% X+ Z+ n6 V* X9 f- k6 W6 t
<P>在一个try{…}代码段中抛出的异常,或者在try{…}代码段所调用的一段代码中抛出的异常会被其对应型别的catch语句捕获。</P>' C# \" e; B0 p% K6 \2 K
<P >我们可以使用异常来把错误处理变得更具独特性且更自然。特别是异常类的层次结构,它可以用来将异常进行分组,从而使一段处理代码能够只在适当的细节层级上处理错误。例如,我们假设open_file_error类派生自io_error:</P>
2 I: J/ B& b' M8 N' a g) N<P>void use_file(const char* fn)</P>
$ l- ^* Z( `* G3 S7 D& W# E6 X<P>{</P>
9 L- R: ?: Y+ v0 l) h2 n7 d<P >File_ptr f(fn, “r”); // 打开fn以便读取;</P>6 |8 z7 U. M4 w1 ?6 ?9 `
<P >// 如果在这个过程中出现错误,则抛出open_file_error</P>
. n1 a& |+ C7 |9 C+ Y/ |<P >// 这里的代码使用fn</P>' X" m3 k# B1 a O
<P>}</P>
0 i0 P+ Y5 t" G' g9 a6 `5 t. g! ?<P>void some_fct()</P>
! X. Z( U3 l- a: G- N1 C* o<P>{</P>$ {: o% w! y& s7 a( ?* h# l0 \$ t
<P >try {</P># w' H- S( A5 b. X1 ?" q
<P > // …</P>/ J Z2 q- D& n; V
<P > use_file(“homedir/magic”);</P>
/ w# o. v' S0 G<P > // …</P>
3 m. m4 r: E1 J2 P' G<P >}</P>
0 _2 c& H3 m/ `<P >catch (io_error) {</P>4 L+ m7 K' g9 q1 P) f
<P > // …</P>0 w; q/ O2 Y# d v& M3 B
<P >}</P>
* {, P5 H- n4 P; z* b {" N<P>}</P>1 { x+ l) d! ]+ o
<P>代码中的some_fct并不需要了解具体是什么出错了;这即是说,它并不需要认识open_file_error。some_fct只处理io_error这个抽象层级上的错误。</P>
3 N( K* V! p4 F: q9 N8 n! ?. t<P >可以注意到,use_file本身根本不处理异常。其任务只是打开一个文件并予以使用。一旦没有可以打开的文件,程序控制权立即被转交给了some_fct。同样,一旦在使用文件的过程中发生了读取方面的错误,该文件将会在控制权转到some_fct之前经由File_ptr的析构函数而被正常关闭。</P>! B: H8 v5 o1 l" C* a
<P> </P>
% |8 W' t1 Z/ j5 I- Q j" I! I<P><B>5</B><B>.</B><B>2 namespaces</B><B>(名字空间)</B><B></B></P>% s0 q/ @8 o9 M
<P >一个namespace是指一个具名的范围(named scope)。namespace被用来将相关的声明划归在一起,将不相关的代码部分隔开。例如,两个各自独立开发的程序库使用相同的名称来代表不同的东西,但用户还是能够同时使用它们:</P>. E2 b+ e3 j9 _: Z
<P>namespace Mylib {</P>; A d5 `1 x- k: V' R
<P >template<class T> class Stack { /* … */ };</P>
7 Z0 W. c" Z" i5 J$ a' }; y<P >// …</P>0 \6 o9 X9 t4 v1 C5 q- H5 A! X5 @
<P>}</P>
R1 H" C* h: t$ e6 T& E<P>namespace Yourlib {</P>3 \) n* \" E0 h# L: @: v9 ^
<P >class Stack { /* … */ };</P>1 ^9 W5 L5 L2 s! ?
<P >// …</P>
- g8 \8 ~8 J: J M; N1 N7 O) i<P>}</P>! }/ v2 Y7 V0 r1 K! @9 h" a
<P>void f(int max)</P>/ ]- I# s- a1 I. f
<P>{</P>
( J7 @" R: ~5 u1 A<P >Mylib::Stack<int> s1(max); // 使用我的那个Stack</P>0 O# F$ \2 V' _9 R
<P >Yourlib::Stack s2(max); // 使用你的那个Stack</P>
: |( Q9 C1 Z( E k1 k- R<P >// …</P> v0 s" k( Q; ~/ |7 |
<P>}</P>
- Z2 N* j4 t* ^% m) f |7 }<P>然而,让一个namespace名称在同一处不断的重复出现,反而会影响和迷惑阅读者和编写者自己。因此,C++规定:在一个特定的namespace内部,即使不做显式的限定,也默认为该namespace中所包含的对象名称是有效的。例如:</P>( z3 p- ]2 P; b6 `5 I0 X
<P>void f(int max)</P>
& e2 u' r; f9 X8 L<P>{</P>" \1 q0 h9 p+ N
<P >using namespace Mylib; // 使Mylib()中的各种名称成为有效可用的</P>
; T0 s5 O" P, U/ N<P >Stack<int> s1(max); // 使用我的Stack</P>
7 c8 C- s% L. f% B8 P% Y<P >Yourlib::Stack s2(max); // 使用你的Stack</P>
6 P% Z) f# s$ c# e<P >// …</P>
0 _+ [6 T% T6 s' J<P>}</P>5 A4 `& T# }7 Y6 d# M, B) H) i$ H
<P>namespace为管理不同的程序库和不同版本的代码提供了一个强有力的工具;特别是,它能让程序员自己控制一个non-local名称之引用的能见度。</P>
5 M3 m% |( x: `- l- k/ r" G<P></P></DIV></TD>* b* l" f2 D9 f" s
<TD width="8%"> </TD></TR></TABLE> |
zan
|