C++编程规范:函数与操作符

第25条:正确地通过值、(智能)指针或引用传递参数

  1. 始终用const限制所有指向只输入参数的指针和引用
  2. 优先通过值来取得原始类型和复制开销比较低的值对象(Point, complex)的输入
  3. 优先按const的引用来取得其他用户定义类型的输入
  4. 如果函数需要其他参数的副本,可以考虑通过值传递代替通过引用传递,这在概念上等同于通过const引用传递加上一次复制,能够帮助编译器更好地优化掉临时变量
  5. 对于输出参数或输入/输出参数
    • 如果参数是可选的或者函数需要保存这个指针的副本或者操纵参数的所有权,那么应该优先通过(智能)指针传递
    • 如果参数是必需的,而且函数无需保存指向参数的指针,或者无需操控其所有权,那么应该优先通过引用传递。这表明参数是必需的,而且调用者必须提供有效对象。

      第26条:保持重载操作符的自然语义

  6. 在模糊或违反直觉的情况下,应该使用命名函数
  7. 例外情况:有些专门的程序库为操作符定义了特定于领域的规范,与C++语义迥异,应该为这种不常见的操作符重载寻找替代方案。

    第27条:优先使用算术操作符和赋值操作符的标准形式

  8. 如果要定义非成员函数的operator@,则应该提供成员函数形式的operator@=,并且用后者实现前者,从而减少重复,提高效率。
    T&  T::operator@=(const T&){
     //具体的实现代码
     return *this;
    }
    T operator@(const T& lhs, const T& rhs){
     T temp(this);
     return temp @= rhs;
    }
    
  9. 变形1:让非成员函数operator@通过值接受其第一个参数,这样就可以安排编译器自动隐式地执行复制 ``` c++ T& operator@=(T& lhs,const T& rhs ){ //具体的实现代码 return lhs; }

T operator@(T lhs, const T& rhs){ return lhs@=rhs; }

3. 变形2:让operator@返回一个const值,这样就可以避免a+b = c之类的废话式的代码,但是缺点是禁止了一些可能有用的结构。
4. 例外情况:在某些情况下,操作符可能要显著地改变其左参数,此时用operator*实现operator*=可能会比反过来更有利。
## 第28条:优先使用++和--的标准形式,优先调用前缀版本
1. 如果定义++C,也要定义C++
2. 对于++和--而言,后缀形式返回的是原值,而前缀形式返回的是新值。应该用前缀形式实现后缀形式。
``` c++
T& T::operator++(){    //前缀形式
    //执行递增
    return *this;
}

T T::operator++(int){   //后缀形式
    T old(*this);
    ++*this;
    return old;
}
  1. 在调用代码时,要优先使用前缀形式,除非确实需要后缀形式返回的原值。

    第29条:考虑重载以避免隐含类型转换

    如果创建临时对象的工作并不必要而且不适于优化,那么可以提供签名与常见参数类型精确匹配的重载函数,而且不会导致转换。

这样的代码看似有重复代码,但是实际上只是“签名重复”而已,因为所有的重载通常都使用相同的后端函数。

第30条:避免重载&&,||或 ,(逗号)

  1. 不能重载这三个操作符的原因是,无法在三种情况下实现内置操作符的完整语义,而程序员通常都需要这些语义
  2. 含有重载&&的表达式将出现下面问题:
    • 函数调用将总是在执行之前对所有参数进行求值
    • 函数参数的求值顺序是不确定的
  3. 内置逗号保证其表达式是从左到右求值的,用户定义的逗号操作符无法保证从左到右求值,通常会产生意料之外的结果。
  4. 例外情况:表达式模板库是一个例外,其目的就是用来捕获所有的操作符。

    第31条:不要编写依赖于函数参数求值顺序的代码

  5. 保持求值顺序:函数参数的求值顺序是不确定的,因此不要依赖具体的顺序
  6. 解决方案:使用命名对象控制求值顺序,即先对参数进行求值。
Table of Contents