优良的函数编程习惯:
1.使用断言检测函数参数的合法性
2.使用const提高函数的健壮性
3.全面思考提高代码的鲁棒性
4.return的良好习惯
1.1assert的使用:
assert只有在Debug版本才有效,作用是在函数的入口处检测参数的有效性;
使用assert目的是捕捉在运行时不应该出现的非法情况。
非法情况:一般是由用户输入错误导致的
错误情况:一般是程序运行过程中自然出现的,这时一定要做相关的处理,
不能使用assert;
实例:
malloc 动态申请内才能失败是错误情况不能使用断言处理
//错误示例
int *pBuf=(int*)malloc(sizeof(int)*1000);
assert(pBuf!=NULL);
//正确示例
int *pBuf=(int*)malloc(sizeof(int)*1000);
if(NULL==pBuf)//把常量放在前面,避免一不小心将等号==写成赋值=
{
}
else
{}
2.1用const修饰函数的参数
1.如果参数用于输出,不论它是什么数据类型,是采用指针传递还是引用传递,
都不能加const,否则会失去作用
2.如果输入参数采用“指针传递“,那么const修饰可以防止意外的修改该指针所
指向的内存空间,起到保护作用。如:
void strcpy(char *strDest,const char *strSrc);
3.如果传入参数采用“值传递”,由于函数将自动用实参的拷贝初始化形参,因此
即便在函数内修改了该参数,改变的也只是堆栈上的拷贝而不是实参,所以一般
不使用const修饰;
4.对于ADT/UDT的输入参数,一般将值传递改为const &传递,目的是提高效率,
例:将 void Fun(A a)改为 void Fun(const A &a);对于基本类型参数一般不这
样做,因为引用的内部实现是指针,基本数据类型大小较小,也无拷贝构造所以
达不到提高效率的目的
2.2用const修饰函数的返回值
1.如果给“指针传递”的函数返回值加上const修饰符,那么函数返回值是一种契约
性常量,不能被直接修改,并且该返回值只能被赋给加const修饰的同类型指针。
例如;
const char *GetString(void)
如下会出现编译错误
char *str=GetString();
正确用法:
const char *str=GetString();
2.如果函数返回值采用“值传递”的方式,在一般情况下函数会把返回值复制到外部
的临时的储存单元中去,所以加const没有什么意义
4.1return的良好习惯
1.return不要返回栈空间的“指针”或者“引用”,因为该内存单元在函数体结束时会自
动释放,例:
char *Fun(void)//如果函数不传参最好加上void,防止在c语言中出现二义性
{
char *str[]="change world";//str数组在函数堆栈上,并用字符串常量初始化
//在末尾自动加'\0'
cout<<sizeof(str)<<endl; //13
cout<<streln(str)<<endl; //12
return str //该语句存在隐患,str指向的内存单元将被释放
}
但是下列程序是正确的:
const char *Fun(void)//如果函数不传参最好加上void,防止在c语言中出现二义性
{
const char *p="change world";//字符串常量放在程序的静态数据区
//在末尾自动加'\0'
cout<<sizeof(p)<<endl; //4,指针大小
cout<<streln(p)<<endl; //12
return str //返回字符串常量的地址
}
2.如果函数返回值是一个对象,要考虑return语句的效率,例如
return String(s1+s2);这是临时对象的构造语法,表示创建一个临时对象并返回它
效率明显优于
String res(s1+s2);
return res;
上述代码将发成三件事情:
1.res对象被创建,同时调用相应的构造函数完成初始化
2.调用拷贝构造函数,把res复制保存到返回值的外部储存单元中;
3.res在函数结束时被销毁(调用析构函数);
而第一种返回临时变量的做法,编译器直接把临时对象创建并初始化在外部单元中,
省去了拷贝和析构的开销
类似不要把 return(x+y);写成 int temp=x+y; return temp;代码会不易读;
更详细内容见高质量c/c++编程
函数编程时的优良习惯
最新推荐文章于 2021-03-04 11:55:24 发布