2.4 Modular Programming

Over the years, the emphasis in the design of programs has shifted from the design of procedures
and toward the organization of data. Among other things, this reflects an increase in program size.
A set of related procedures with the data they manipulate is often called a module. The programming
paradigm becomes:
                Decide which modules you want;
                partition the program so that data is hidden within modules.
This paradigm is also known as the  data-hiding principle. Where there is no grouping of procedures
with related data, the procedural programming style suffices. Also, the techniques for designing
‘‘good procedures’’ are now applied for each procedure in a module. The most common example
of a module is the definition of a stack. The main problems that have to be solved are:
[1] Provide a user interface for the stack (e.g., functions p u s h () and p o p ()).
[2] Ensure that the representation of the stack (e.g., an array of elements) can be accessed only
through this user interface.
[3] Ensure that the stack is initialized before its first use.
    C++ provides a mechanism for grouping related data, functions, etc., into separate  namespaces. For
example, the user interface of a S t a c k module could be declared and used like this:

namespace Stack { //  interface
void push (char);
char pop();
}
void f()
{
    Stack::push('c');
    if(Stack::pop() != 'c')  e r r o r ("i m p o s s i b l e ");
}

The Stack:: qualification indicates that the push() and pop() are those from the Stack namespace.
Other uses of those names will not interfere or cause confusion.
    The definition of the Stack could be provided in a separately-compiled part of the program:
namespace Stack { //  implementation
    const in max_size = 2 0 0 ;
    char v[max_size] ;
    int top=0;
     void push (char)  { /* check for overflow and push c */ }
     char pop()  { /* check for underflow and pop */ }
}
    The key point about this Stack module is that the user code is insulated from the data representation
of Stack by the code implementing  Stack ::  push  () and  Stack :: pop (). The user doesn’t need to
know that the Stack is implemented using an array, and the implementation can be changed without
affecting user code.
    Because data is only one of the things one might want to ‘‘hide,’’ the notion of data hiding is
trivially extended to the notion of information hiding; that is, the names of functions, types, etc.,
can also be made local to a module. Consequently, C++ allows any declaration to be placed in a
namespace.
    This S t a c k module is one way of representing a stack. The following sections use a variety of
stacks to illustrate different programming styles.
2.4.1 Separate Compilation [tour.comp]
C++ supports C’s notion of separate compilation. This can be used to organize a program into a set
of semi-independent  fragments.
    Typically, we place the declarations that specify the interface to a module in a file with a name
indicating its intended use. Thus,

namespace Stack { //  interface
void push (char);
char pop();
}

would be placed in a file stack.h , and users will include that file, called a header file, like this:
头文件中是用户可以访问的接口,一般是类的定义和其他的申明。但是没有具体实现。

在其它编译单元中要使用这个模块时,要包含头文件获得接口
#include "stack.h" // get the interface
void f()
{
    Stack::push('c');
    if(Stack::pop() != 'c')  e r r o r ("i m p o s s i b l e ");
}

To help the compiler ensure consistency, the file providing the implementation of the S t a c k module
will also include the interface:

#include "stack.h" // get the interface
namespace Stack { //  implementation
    const in max_size = 2 0 0 ;
    char v[max_size] ;
    int top=0;    
}
 void Stack::push (char)  { /* check for overflow and push c */ }
 char Stack::pop()  { /* check for underflow and pop */ }

The user code goes in a third file, say user.c . The code in user.c and stack.c shares the stack
interface information presented in stack.h , but the two files are otherwise independent and can be
separately compiled. Graphically, the program fragments can be represented like this:


Separate compilation is an issue in all real programs. It is not simply a concern in programs that
present facilities, such as a S t a c k , as modules. Strictly speaking, using separate compilation isn’t a
language issue; it is an issue of how best to take advantage of a particular language implementation.
However, it is of great practical importance. The best approach is to maximize modularity, represent
that modularity logically through language features, and then exploit the modularity physically
through files for effective separate compilation
2.4.2 Exception Handling [tour.except]
When a program is designed as a set of modules, error handling must be considered in light of these
modules. Which module is responsible for handling what errors? Often, the module that detects an
error doesn’t know what action to take. The recovery action depends on the module that invoked
the operation rather than on the module that found the error while trying to perform the operation.
As programs grow, and especially when libraries are used extensively, standards for handling errors
(or, more generally, ‘‘exceptional circumstances’’) become important.
    Consider again the Stack example. What ought to be done when we try to p u s h () one too
many characters? The writer of the  Stack  module doesn’t know what the user would like to be
done in this case, and the user cannot consistently detect the problem (if the user could, the overflow
wouldn’t happen in the first place). The solution is for the  Stack  implementer to detect the
overflow and then tell the (unknown) user. The user can then take appropriate action. For example:

对模块的实现者来说,他并不知道这个模块中出现了异常,模块的使用者想怎么处理,对应同样的异常,不一样的用户可能有不一样的处理方式,有的可能直接忽略,有的可能直接终止。
对于模块的使用者来说,他并不能知道什么时候出现异常,我们不能将希望全部寄托在模块的使用者上,认为他们一定能检测出异常是不保险的。
所以,在实际实现中,模块的实现中应该负责异常的检测,并且将检测出的异常告诉模块的用户。模块的用户在接收到报告后,做出相应的处理。

namespace Stack { // interface
void push (char);
char pop();
class overflow { }; // type representing overflow exceptions
}

When detecting an overflow, Stack::push() can invoke the exception-handling code; that is,
"throw an overflow exception:"

void Stack::push (char)
{
    i f (top == max_size ) throw overflow();
    // push c
}

The  throw transfers control to a handler for exceptions of type Stack::overflow in some function
that directly or indirectly called Stack::push(). To do that,  the implementation will unwind the
function call stack as needed to get back to the context of that caller. Thus, the t h r o w acts as a multilevel return . For example:
throw语句就相当于模块将异常报告发送给模块用户,同时也将异常的处理权限交给了用户。

void f()
{
    / / ...
    t r y { // exceptions here are handled by the handler defined below
        w h i l e (t r u e ) S t a c k :: p u s h (´c ´);
    }
    c a t c h (S t a c k :: O v e r f l o w ) {
        / / oops: stack overflow; take appropriate action
    }
    / / ...
}

try...catch语句是模块用户对接收到的异常做出相应的处理。
The w h i l e loop will try to loop forever. Therefore, the c a t c h clause providing a handler for
S t a c k :: O v e r f l o w will be entered after some call of S t a c k :: p u s h () causes a t h r o w .
Use of the exception-handling mechanisms can make error handling more regular and readable.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值