Effective C++ - 设计与声明

Effective C++ -- 设计与声明

18.让接口容易被正确使用

问题:

  • 接口的参数
  • 接口依赖其他调用
class Date {
public:
    Date(int month,int day, int year;
}

以上代码问题:

  • 容易以错误的顺序传递参数
  • 可能传递无效的月份或天数
    解决方法:
  • 通过导入新类型获得预防
struct Day {
    explicit Day(int):val(d){}
    int val;
};

class Date {
public:
    Data(const Month& m, const Day& d, const Year& y);
};

Date d(Month(3), Day(30), Year(1995));

以上代码无法限制数据范围
解决方法:

  • 利用enum表现月份
  • 预定义有效的Month
 class Month {
 public:
     static Month Jan() {return Month(1);}
     static Month Feb() {return Month(2);}
 private:
     explicit Month(int m);
 };
Date d(Month::Feb(), Day(30), Year(1995)); 

接口设计原则

  • 接口不容易被误用
    • 建立新类型
    • 限制类型上的操作
    • 消除客户的资源管理责任
  • 接口一致性
    • stl容器都有一个size接口
    • 不要有的时length,有的是size
  • 任何接口如果要求客户必须记得做某事,会有不正确使用倾向
// 会导致忘记删除指针
Investment* createInvestment();

// 解决方法,保存到智能指针

// 客户会忘记保存的到智能指针
// 解决方法:函数返回智能指针
auto_ptr<Investment> createInvestment()

19.设计class犹如设计type

需要考虑的问题:

  • 新type对象该如何创建和销毁
    • 构造函数,析构函数
    • operator new,operator delete, operator new[] operator delete[]
  • 对象初始化和对象赋值有什么区别
  • 新type的对象如果被pass by value,意味着什么?
    • copy构造函数如何实现
  • 什么时新type的合法值
  • 新type需要配合某个继承图系吗
    • 如果继承某个既有类,受到该类的影响,特别时virtual,non-virtual的影响
    • 如果容许其他类继承,影响你所声明的函数,尤其时析构函数是否为virtual
  • 你的type需要什么样的转换
    • T1需要转换为T2,T1内写类型转换函数operator T2,或着T2内写non-explicit 构造函数
    • 转换是否需要为explicit的函数
  • 什么样的操作符和函数是合理的
  • 该谁取用新的type的成员
    • public,protected,private,friend
  • 你的新type有多么一般化
    • 是否需要为class template

20.宁以pass-by-reference-to-const替代pass-by-value

使用pass-by-value问题

  • 传递的类对象执行copy构造函数,造成性能低下
  • 类对象内部的成员变量执行copy构造函数,造成性能低下

解决方法:

  • 传递常量引用
  • 传递引用可以避免切割发生(切割后virtual函数调用的是基类的virtual函数)
  • 对于内置对象,pass-by-value更高效
    • 编译器会把内置对象放入缓存器
  • 小型的类对象,pass-by-reference更高效
    • 编译器可能不会对小型类对象优化
    • 类对象大小随着需求增加可能会变化,会变为大的类对象

21.必须返回对象时,别妄想返回其reference

返回的reference在内存模型中存在两种情况,stack空间和heap空间

栈空间

代码实现如下:

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result();
    return result;
}

函数返回后,栈空间的result将会被销毁,使用被销毁的引用会引入未定义的行为。

堆空间

代码实现如下:

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational* result = new Rational();
    return *result;
}

Rational w,x,y,z;
w = x*y*z;

以上代码执行乘法操作时会造成资源泄露。
综上,无法返回对象的引用,返回对象进行数据拷贝造成的性能问题编译器会进行优化。

23.宁以non-member,non-friend替换member函数

将便利函数以non-member,non-friend方式实现