函数是组成C/C++程序的基本元素,是将一段执行某项功能的代码进行了封装的代码段。为了实现设计的功能,函数的功能正确性是首要的前提,但是仅仅是正确还不够,其设计的科学性和合理性也是影响函数使用的重要因素。本文简要讨论C/C++函数设计和实现的一些基本规则。
每一个完整的C/C++函数都至少包含三个部分:返回值、函数名和参数。函数参数和返回值承担了调用者与被调用函数之间数据传递的功能,主要方式有三种:值传递、指针传递和引用传递,前两者为C标准,引用传递为C++标准。其中引用传递的性质类似于指针传递,但是使用方式类似值传递。
声明函数主要考虑函数的参数和返回值的问题。当然,函数名也是重要的一部分,在设计函数名时,应当注意函数名体现函数的功能,且不产生歧义。通常使用一句英文表示的一个动作或特性来作为函数名,单词之间通过大小写转换来区分。
函数的参数应当书写完整,不应只写类型而省略参数名称。当函数没有参数时,使用void作为参数。
void Function1(int , int);//不良风格 void Function2(int nParam1, int nParam2);//良好风格 void Funciton3();//不良风格 void Function4(void);//良好风格
int GetRectArea(int a, int b);//不良风格 int GetRectArea(int nWidth, int nHeight);//良好风格
void StringCopy(char *strDst, char *strSrc); char str[20]; StringCopy(str, “Hello World”);
void GetRectArea(const int &nParam1, const int &nParam2); void StringCopy(const char *strDst, char *strSrc);
一些简单的函数,由于几乎不可能出现错误,且一般只有一个计算结果,那么可以将计算结果通过返回值传递给调用者。对于复杂函数,返回值通常用于返回错误标识,而计算结果通过输出参数获得。如果需要返回多个参数,除了定义多个输出参数外,还可以定义一个输出结构体,在输出之前将输出值封装在一个结构体对象中整体返回。
函数返回值的类型有“返回值”和“返回引用”两种设计。其区别是,“返回值”会产生一个临时变量作为函数返回值的副本,而“返回引用”时不会产生值的副本。有些时候选择“返回引用”可以提高效率,而有些时候必须“返回值”否则可能出错。
首先需要注意到一个问题就是,绝对不要返回局部对象的引用,因为局部对象在函数调用结束之后就会被释放,引用指向的对象会是一片未知区域。比如以下代码是不允许出现的:
int& GetRectArea(const int &nWidth, const int &nHeight) { int area = nWidth * nHeight; return area; }
String& String::operator =(const String &str) { if (this == &str) { return *this; } delete [] m_string; int len = strlen(str.m_string); m_string = new char[len+1]; strcpy(m_string,str.m_string); return *this; }在通过返回this指针的对象到引用时,不会把本地对象拷贝到上层函数的对象内存,而是直接返回当前对象本身,降低了不必要的开销,提高了效率。
函数的实现方法千差万别,然而在函数体的一开始以及返回时可以对参数和返回值进行检查以防止不必要的错误发生。
可以使用assert防止非法参数,这样可以预防许多程序的错误。assert是仅在debug模式下生效的宏定义,用于检查不应该发生的情况。如以下函数中,采用assert防止输入参数为空:
void * memcpy(void *pvTo, const void *pvFrom, size_t size) { assert((pvTo != NULL) && (pvFrom != NULL)); // 使用断言 byte *pbTo = (byte *)pvTo; // 防止改变 pvTo 的地址 byte *pbFrom = (byte *)pvFrom; // 防止改变 pvFrom 的地址 while (size-- > 0) *pbTo++ = *pbFrom++; return pvTo; }
对于提高返回对象的效率而言,临时对象是否在栈中创建和销毁是不同的。如以下代码:
String temp(s1 + s2); return temp;此时,程序的流程如下:
而使用以下方式:
return String(s1 + s2);此时,临时对象将直接创建在外部存储单元中,不再调用拷贝构造函数和析构函数,效率更高。