摘要载入中…    请稍等…












内容载入中…    请稍等…

如长时间看不到内容,请关闭浏览器,重新打开此页!

芯友首页 应用软件 编程开发 网络硬件 资源下载 动漫音乐 精美图库 芯友论坛 视频教程 电脑技术QQ群:73422782
 ★★photoshop学友-史上最强播放器★★
 位置:{$compro$}>{$comcity$}>高质量C++/C编程
◎→ 本类最新
函数设计
常量
命名规则
程序的版式
文件结构
前言
◎→相关资源
操作系统优化
操作系统优化
◎→ 热门资源
在DELPHI文本和图形的打印
在Delphi里播放Flash
在Delphi程序中应用IE浏览器控件
在Delphi程序中实现时间限制和加
在DELPHI程序中拨号上网
Delphi编程中创建一个启动闪现窗
在DELPHI2.0/3.0中直接操作端口

函数设计


日期:2008-10-20 21:41:46    来源:互联网
   

l         【规则6-2-3】不要将正常值和错误标志混在一起返回正常值用输出参数获得,而错误标志用return语句返回。

回顾上例,C标准库函数的设计者为什么要将getchar声明为令人迷糊的int类型呢?他会那么傻吗?

在正常情况下,getchar的确返回单个字符。但如果getchar碰到文件结束标志或发生读错误,它必须返回一个标志EOF。为了区别于正常的字符,只好将EOF定义为负数(通常为负1)。因此函数getchar就成了int类型。

我们在实际工作中,经常会碰到上述令人为难的问题。为了避免出现误解,我们应该将正常值和错误标志分开。即:正常值用输出参数获得,而错误标志用return语句返回。

函数getchar可以改写成 BOOL GetChar(char *c);

虽然gechar比GetChar灵活,例如 putchar(getchar()); 但是如果getchar用错了,它的灵活性又有什么用呢?

 

²        【建议6-2-1】有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。

例如字符串拷贝函数strcpy的原型:

char *strcpy(char *strDest,const char *strSrc);

strcpy函数将strSrc拷贝至输出参数strDest中,同时函数的返回值又是strDest。这样做并非多此一举,可以获得如下灵活性:

    char str[20];

    int  length = strlen( strcpy(str, “Hello World”) ); 

 

²        【建议6-2-2】如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。

例如:

class String

{…

    // 赋值函数

    String & operate=(const String &other);    

// 相加函数,如果没有friend修饰则只许有一个右侧参数

friend    String   operate+( const String &s1, const String &s2); 

private:

    char *m_data; 

}

 

       String的赋值函数operate = 的实现如下:

String & String::operate=(const String &other)

{

    if (this == &other)

        return *this;

    delete m_data;

    m_data = new char[strlen(other.data)+1];

    strcpy(m_data, other.data);

    return *this;    // 返回的是 *this的引用,无需拷贝过程


对于赋值函数,应当用“引用传递”的方式返回String对象。如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。例如:

  String a,b,c;

  …

  a = b;     // 如果用“值传递”,将产生一次 *this 拷贝

  a = b = c;   // 如果用“值传递”,将产生两次 *this 拷贝

 

       String的相加函数operate + 的实现如下:

String  operate+(const String &s1, const String &s2)  

{

    String temp;

    delete temp.data;    // temp.data是仅含‘\0’的字符串

        temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];

        strcpy(temp.data, s1.data);

        strcat(temp.data, s2.data);

        return temp;

    }

 

对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。例如:

    c = a + b; 

此时 a + b 并不返回期望值,c什么也得不到,流下了隐患。

6.3 函数内部实现的规则
不同功能的函数其内部实现各不相同,看起来似乎无法就“内部实现”达成一致的观点。但根据经验,我们可以在函数体的“入口处”和“出口处”从严把关,从而提高函数的质量。

 

l         【规则6-3-1】在函数体的“入口处”,对参数的有效性进行检查。

很多程序错误是由非法参数引起的,我们应该充分理解并正确使用“断言”(assert)来防止此类错误。详见6.5节“使用断言”。

 

l         【规则6-3-2】在函数体的“出口处”,对return语句的正确性和效率进行检查。

     如果函数有返回值,那么函数的“出口处”是return语句。我们不要轻视return语句。如果return语句写得不好,函数要么出错,要么效率低下。

注意事项如下:

(1)return语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。例如

    char * Func(void)

    {

        char str[] = “hello world”;    // str的内存位于栈上

        …

        return str;     // 将导致错误

    }

(2)要搞清楚返回的究竟是“值”、“指针”还是“引用”。 高质量C++/C编程指南 -- 第4章 表达式和基本语句 
本文出自: 作者: 林锐 博士 (2002-07-16 06:02:01) 
第4章 表达式和基本语句
读者可能怀疑:连if、for、while、goto、switch这样简单的东西也要探讨编程风格,是不是小题大做?

我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似的错误。

表达式和语句都属于C++/C的短语结构语法。它们看似简单,但使用时隐患比较多。本章归纳了正确使用表达式和语句的一些规则与建议。

4.1 运算符的优先级
       C++/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示。注意一元运算符 +  -  * 的优先级高于对应的二元运算符。 

表4-1 运算符的优先级与结合律 

l         【规则4-1-1】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。

由于将表4-1熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。例如:

word = (high << 8) | low

if ((a | b) && (a & c))   

4.2 复合表达式
如 a = b = c = 0这样的表达式称为复合表达式。允许复合表达式存在的理由是:(1)书写简洁;(2)可以提高编译效率。但要防止滥用复合表达式。

 

l         【规则4-2-1】不要编写太复杂的复合表达式。

例如:

      i = a >= b && c < d && c + f <= g + h ;   // 复合表达式过于复杂

 

l         【规则4-2-2】不要有多用途的复合表达式。

例如:

d = (a = b + c) + r ; 

该表达式既求a值又求d值。应该拆分为两个独立的语句:

a = b + c;

d = a + r;

 

l         【规则4-2-3】不要把程序中的复合表达式与“真正的数学表达式”混淆。

例如:  

if (a < b < c)            // a < b < c是数学表达式而不是程序表达式

并不表示       

if ((a<b) && (b<c))

而是成了令人费解的

if ( (a<b)<c )

4.3 if 语句
    if语句是C++/C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if语句。本节以“与零值比较”为例,展开讨论。

 

4.3.1 布尔变量与零值比较

l         【规则4-3-1】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。

根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE定义为1,而Visual Basic则将TRUE定义为-1。

假设布尔变量名字为flag,它与零值比较的标准if语句如下:

if (flag)    // 表示flag为真

if (!flag)    // 表示flag为假

其它的用法都属于不良风格,例如:

    if (flag == TRUE)   

    if (flag == 1 )     

    if (flag == FALSE)  

    if (flag == 0)      

 

4.3.2 整型变量与零值比较

l         【规则4-3-2】应当将整型变量用“==”或“!=”直接与0比较。

    假设整型变量的名字为value,它与零值比较的标准if语句如下:

if (value == 0)  

if (value != 0)

不可模仿布尔变量的风格而写成

if (value)    // 会让人误解 value是布尔变量

if (!value) 

 

4.3.3 浮点变量与零值比较

l         【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。

    千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。

    假设浮点变量的名字为x,应当将   

if (x == 0.0)     // 隐含错误的比较

转化为 

if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON是允许的误差(即精度)。

 

4.3.4 指针变量与零值比较

l         【规则4-3-4】应当将指针变量用“==”或“!=”与NULL比较。

    指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if语句如下:

        if (p == NULL)    // p与NULL显式比较,强调p是指针变量

        if (p != NULL) 

不要写成

        if (p == 0)   // 容易让人误解p是整型变量

        if (p != 0)    

    或者

if (p)            // 容易让人误解p是布尔变量

    if (!p)            

 

4.3.5 对if语句的补充说明

有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把p和NULL颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。

程序中有时会遇到if/else/return的组合,应该将如下不良风格的程序  [1]
Tags: 
{$enumber$}
芯友网版权所有 1999-2006 | 著作权与商标声明 | 法律声明 | 服务条款 | 隐私声明 | 联系我们