林锐C语言--高质量C/C++编程(第五六章)

林锐C语言–高质量C/C++编程(第五六章)

第五章 常量

5.1 为什么需要常量

如果不使用常量,直接在程序中填写数字或字符串,将会有什么麻烦?
程序的可读性(可理解性)变差。程序员自己会忘记那些数字或字符什么意思,用户则更加不知它们从何处来、表示什么。
在程序的很多地方输入同样的数字或字符串,难保不发生书写错误。
如果要修改数字或字符串,则会在很多地方改动,既麻烦又容易出错。

#define MAX 100 /* c 语言的宏常量*/
const int MAX =100; // C++语言的 const 常量
const float PI =3.14159;// C ++语言的 const 常量

5.2 const与#define的比较

const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。

  • 【规则 5-2-1】在 C++ 程序中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量

5.3 常量定义规则

  • 【规则 5-3-1】需要对外公开的常量放在头文件中,不需要对外公开的常量在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
  • 【规则 5-3-2】如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。例如
const float RADIUS = 100; 
const float DIAMETER = RADIUS * 2;

5.4 类中的常量

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。
枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如 PI=3.14159)

class A 
{.
 const int SIZE =100;//错误,企图在类声明中初始化 const 数据成员 int array [ SIZE ];
//错误,未知的 SIZE 
};
//const 数据成员的初始化只能在类构造函数的初始化表中进行,例如
 class A 
{//构造函数
 A ( int size );
 const int SIZE ;
};
//构造函数的初始化表
 A :: A ( int size ): SIZE ( size )
-
 A a (100);//对象 a 的 SIZE 值为100
 A b (200);//对象 b 的 SIZE 值为200

第六章 函数设计

C 语言中,函数的参数和返回值的传递方式有两种
值传递(pass by value)
指针传递(pass by pointer)。
C++ 语言中多了引用传递(pass by reference)。由于引用传递的性质象指针传递,而使用方式却象值传递。

6.1 参数的规则

  • 【规则 6-1-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。如果函数没有参数,则用 void 填充
  • 【规则 6-1-2】参数命名要恰当,顺序要合理。
    void StringCopy( char *strDestination,char *strSource);
  • 【规则 6-1-3】如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该指针在函数体内被意外修改
  • 【规则 6-1-4】如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率
    void StringCopy(char *strDestination,const char *strSource);

6.2 返回值的规则

  • 【规则 6-2-1】不要省略返回值的类型
    C语言中,凡不加类型说明的函数,一律自动按整型处理。这样做不会有什么好处,却容易被误解为 void 类型
    C++语言有很严格的类型安全检查,不允许上述情况发生。由于C++程序可以调用C 函数,为了避免混乱,规定任何 C++/ C 函数都必须有类型。
    如果函数没有返回值,那么应声明为void类型
    如果getchar碰到文件结束标志或发生读错误,它必须返回一个标志 EOF。为了区别于正常的字符,只好将 EOF 定义为负数(通常为负 1)。因此函数getchar就成了int类型。

违反这条规则的典型代表是 C 标准库函数 getchar 。

例如:

 char c ;
 c = getchar ();
 if ( c == EOF )
...

按照getchar名字的意思,将变量c声明为 char 类型是很自然的事情。但不幸的是 getchar 的确不是 char 类型,而是 int 类型,其原型如下:
int getchar ( void );
由于 c 是 char 类型,取值范围是[-128,127],如果宏 EOF 的值在 char 的取值范围之外,那么 if 语句将总是失败,这种"危险"人们一般哪里料得到!导致本例错误的责任并不在用户,是函数 getchar 误导了使用者。

6.3 函数内部实现的规则

函数的出口入口处对参数的有效性进行检查

char * Func ( void )
{
 char str []=" hello world "; //str的内存位于栈上
 return str ; //将导致错误
}

编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的花费,提高了效率。
3)如果函数返回值是一个对象,要考虑 return 语句的效率。例如

return String (s1+s2);

这是临时对象的语法,表示"创建一个临时对象并返回它"。不要以为它与"先创建一个部对象temp并返回它的结果"是等价的,如

 String temp (s1+s2);
 return temp ;

实质不然,上述代码将发生三件事。首先, temp对象被创建,同时完成初始化;然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束时被销毁(调用析构函数)。然而"创建一个临时对象并返回它"的过程是不同的,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。

6.4 其他建议

  • 【建议 6-4-1】函数的功能要单一,不要设计多用途的函数。
  • 【建议 6-4-2】函数体的规模要小,尽量控制在 50 行代码之内。
  • 【建议 6-4-3】尽量避免函数带有“记忆”功能。如:static

6.5 使用断言

程序一般分为 Debug 版本和 Release 版本,Debug 版本用于内部调试,Release 版本发行给用户使用。
断言assert是仅在 Debug 版本起作用的宏,它用于检查“不应该”发生的情况。
而是宏。程序员可以把assert 看成一个在任何系统状态下都可以安全使用的无害测试手段。

void * memcpy ( void * pvTo , const void * pvFrom , size _ t size )assert (( pvTo != NULL )&&( pv =1
//防止改变 pvTo 的地址
 y * pbTo =( byte *) pvTo ;
//防止改变 pvFrom 的地址
 byte * pbFrom =( byte *) pvFrom ;
 while ( size =->0)
* pbTo ++ = *pbFrom ++;
 return pvTo ;

示例6-5复制不重叠的内存块

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值