发布时间 : 星期三 文章开发管理之代码编码规范更新完毕开始阅读
4.3.1.6.
4.3.1.7.
对于内置类型参数要传值
需要传指针不传引用的情形
a)内部需要用NULL状态(引用没有NULL状态) b)若参数是被new出来的,是将在函数内被释放
4.3.2. 返回值规则
4.3.2.1. 函数的输出值结果用输出参数(一般为指针)获得,状态用return返回
4.3.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什么也得不到,流下了隐患。
4.3.2.3. 尽量保持函数只有唯一出口
4.3.2.4. 若函数返回状态,尝试用枚举作类型
4.3.2.5. 当函数返回引用或指针时,用文字描述其有效性
4.3.2.6. 禁止成员函数返回成员的引用或指针
4.3.3. 函数内部规则
4.3.3.1. 在函数体的“入口处”,对参数的有效性进行检查,应正确使用断言(assert),
断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况
例:
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; }
4.3.3.2. 在函数的“出口处”,应对return语句的正确性和效率进行检查
4.3.3.3. return语句不可返回指向“找内存”(内部变量)的指针或引用,因为该内存在函
数体结束时被自动销毁
4.3.3.4. 要搞清楚返回的究竟是值、指针,还是引用
4.3.3.5. 如果函数返回值是一个对象,要考虑return语句的效率
4.3.4. 通用规则
4.3.4.1. 函数的功能要单一,不要设计多用途函数
4.3.4.2. 函数体规模要小,尽量控制在100行代码以内,不包括注释和空格行
4.3.4.3. 尽量避免函数带有记忆功能,相同的输入应当产生相同的输出(不用static变量)
示例:如下函数,其返回值(即功能)是不可预测的。
unsigned int integer_sum( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意,是static类型的。 // 若改为auto类型,则函数即变为可预测。 for (index = 1; index <= base; index++) {
sum += index; }
return sum; }
4.3.4.4. 用于出错处理的返回值一定要清楚
4.3.4.5. 引用的规则
a)引用被创建时同时被初始化
b)不能有NULL引用,引用必须与合法的存储单元关联 c)一旦引用被初始化,就不能改变引用的关系 d)引用的功能主要是传递参数和返回值
C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 以下是“值传递”的示例程序。由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n, 所以n的值仍然是0。
void Func1(int x)
{
x = x + 10; } ?
int n = 0; Func1(n);
cout << “n = ” << n << endl; // n = 0
以下是“指针传递”的示例程序。由于Func2函数体内的x是指向外部变量n的指针,
改变该指针的内容将导致n的值改变,所以n的值成为10。
void Func2(int *x)
{
(* x) = (* x) + 10; } ?
int n = 0; Func2(&n);
cout << “n = ” << n << endl; // n = 10
以下是“引用传递”的示例程序。由于Func3函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等于改变n,所以n的值成为10。
void Func3(int &x)
{
x = x + 10; } ?
int n = 0; Func3(n);
cout << “n = ” << n << endl; // n = 10
对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?
答案是“用适当的工具做恰如其分的工作”。
指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?
如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。