高质量C/C++编程之函数设计

函数接口的两大要素是参数和返回值。
C语言中,函数的参数和返回值的传递方式有两种值传递pass by value和指针传递pass by pointer。
C++中,更多采用的是引用传递pass by reference。引用传递性质像指针传递,但使用方式更像值传递。

1.参数的规则

规则1: 参数的书写要完整,不能贪图省事只写参数类型而忽略参数名字,如果函数没有参数,用void填充。
规则2: 参数命名要恰当,顺序要合理:一般地将目的参数放在前面,源参数放在后面
规则3: 如果参数是指针,且仅做输入使用,则应在类型前面加const,防止该指针在函数体中被意外修改
规则4: 如果输入参数以值传递的方式传递对象,则宜改用“const&”方式来传递,这样可以省去临时对象的构造和析构,从而提高效率

建议1: 避免函数有过多的参数,参数个数应该尽量控制在5个以内,
建议2: 尽量不要使用类型和数目不确定的参数
        库函数int printf(const char *format[,arguments]...)就是这样的典型代表
        这种风格的函数在编译时丧失了严格的类型安全检查。

2.返回值的规则

规则1: 不要省略返回值的类型
        C语言中,凡不加类型说明的函数,一律自动按照整型处理。
        为了避免混乱,规定C/C++函数必须有类型,如果函数没有返回值,应该声明为void类型。
规则2: 函数名与返回值类型在语义上不能有冲突
        如:int getChar(void);  
规则3: 不要把正常值和错误标识混在一起返回
        正常值用输入参数获得,而错误标识用return语句返回。
        bool getChar(char *c);
建议1: 有些函数本来没有返回值,但是为了增加灵活性如支持链式表达式,可以附加返回值。
        如字符串拷贝函数strcpy的原型:
        char* strcpy(char *strDest,char *strSrc);
        链式表达式:int len = strlen(strcpy(pStrDest,pStrSrc));
建议2: 如果函数的返回值是一个对象,有些场合可以用引用传递替代值传递,可以提高效率,前提是不能出错。
        class String{
            String& operator=(const String &other);                    //引用传递
            friend String operator+(const String &str1,const String &str2);    //值传递
            private:
                char* m_data;
        }
        String& String::operator=(const String &other){
            if(this == &other) return *this;
            delete m_data;
            m_data = new char[strlen(other_data)+1];
            str_cpy(m_data,other_data);
            return *this;        //返回的是*this的引用,无需拷贝过程
        }
        String String::operator+(const String &str1,const String &str2){
            String temp;
            delete temp.m_data;

            temp = new char[strlen(str1.m_data)+strlen[str2.m_data]+1];
            strcpy(temp,str1);
            strcat(temp,str2);

            return temp;        //return会对对象进行复制构造,有拷贝过程
        }

3.函数内部实现规则

规则1: 在函数的入口处,对参数的有效性进行检查
        充分理解并使用断言assert
规则2:在函数的出口处,对return语句的有效性和效率进行检查
    # 不可返回“栈内存”中的指针或引用,因为该内存在函数结束时将自动销毁
    char* func(void){
        char str[] = "";
        ........
        return str;
    }
    # 要搞清楚返回的是值、指针还是引用
    # 如果函数返回的是一个对象,需要考虑return语句的效率
    return String(s1 + s2);        //创建一个临时对象,并返回它,
                        //直接在外部内存的单元初始化临时对象,省去了拷贝和析构,提高了效率
    解读一下代码:
    String temp(str1 + s2);        //temp对象被完成,并完成初始化
    return temp;                //拷贝构造函数将tenp拷贝到保存返回值的外部内存单元,然后调用析构函数销毁temp对象

4.其他建议

建议1: 函数功能单一,不要涉及多用途的函数
建议2: 函数体规模要小,控制在50行代码以内
建议3: 尽量避免函数带有记忆功能,相同的输入应该有相同的输出。
        这样的函数不易于测试和维护
        在C/C++语言中,函数的static局部变量是函数记忆存储器,应该尽量少用static局部变量,除非必须。
建议4: 不仅要检查参数的有效性,还要检查通过其他途径进入函数体内的变量的有效性,比如全局变量和文件句柄等
建议5: 用于出错处理的返回值一定要明确,使使用者不容易忽视或误解错误情况

5.使用断言

程序一般分为debug版本和release版本,debug版本用于内部调试,release版本发行给用户使用。

断言assert是只在debug版本中起作用的宏,它用于检查不应该发生的情况。
assert是宏,为了不引起在debug版本和release版本的差异,将assert定义为宏。
程序员可以把assert看成是任何系统状态下都可以安全使用的无害测试手段。

规则1: 使用断言捕捉不应该发生的非法情况
        不要混淆非法情况和错误情况,对于错误情况是要一定要做处理的
规则2: 在函数的入口,使用断言检查参数的有效性

建议1: 确定假定,使用断言对假定进行检查

6.引用与指针的比较

引用的一些规则:
1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)
2)不能有NULL引用,引用必须和合法的存储单元关联(指针可以指向NULL)
3)一旦被初始化,不能改变引用关系(指针则可以随时改变所指的对象)

引用的主要功能是传递函数的参数和返回值。
C++中,函数的参数和返回值的传递方式有三种:值传递,指针传递,引用传递
引用传递的性质象指针传递,但是书写方式象值传递。

指针毫无约束地使用内存单元的任何东西,功能强大,但十分危险。
如果只需要借用下某对象的别名,使用引用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值