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方式实现

24.若所有参数类型皆需要转换,请为此采用non-member函数

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);
    const Rational operator* (const Rational& rhs) const
};

如果使用成员函数形式,则以下代码无法通过编译。

Rational result, rational;
result = 2 * rational;

如果采用non-member函数形式则可以

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);
};
const Rational operator*(const Rational& lhs, const Rational& rhs);
Rational result, rational;
result = 2 * rational;

2先通过隐式转换为Rational后调用operator*

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值