Effective C++阅读笔记(二)

目录


条款18-25——设计与声明

条款18:让接口容易被正确使用,不易被误用

好的接口应当被正确使用,不容易被误用。
促进正确使用的办法:接口的一致性、内置类型行为兼容。
阻止误用的办法:建立新类型(如在日期类中加入年月日类封装数据以避免非法数据)、限制类型上的操作(见条款03),束缚对象值,消除客户的资源管理责任(见条款13)。

条款19:设计class 犹如设计type

设计一个高效的classes 要当做设计type ,遵循以下规范:
1.为新type 设计合理的构造、析构、内存分配与释放函数。
2.不要混淆构造函数与赋值操作符的行为(初始化与赋值)
3.copy 构造函数用来定义一个typepass-by-value该如何实现。
4.成员函数要进行输入错误检查工作,避免非法数据。
5.考虑到class 是否继承自其他class ,以及是否会被其他class 继承。
6.设计所需要的类型转换函数。
7.设计合理的成员函数和操作符。
8.若不想使用编译器自动生成的函数,就该明确拒绝(见条款06)。
9.为成员函数确定属性:public、protected、private。
10.见条款29。
11.若新type 足够一般化,也许需要定义新的class template
12.若只是为已有的class 添加功能,也许单纯定义non-member 函数或templates更合适。

条款20:宁以pass-by-reference-to-const 替换pass-by-value

class Person {…};
class Student: public Person {…};

bool validateStudent(Student s);  //函数以by value方式接受学生
Student plato;
bool platoIsOk = validateStudent(plato);

以上代码执行时,会调用Studentcopy构造函数、Personcopy构造函数、以及构造他们各自的私有成员对象和析构函数,可见成本很高。

bool validateStudent(const Student& s);

用引用传值则高效得多,不用创建任何对象,不调用任何构造或析构函数。const确保函数不会对传入的Student做改变。引用传值也可以避免对象切割问题:即将派生类对象以传值方式传递给基类对象时,基类的构造函数被调用,会将除了基类对象所需成员之外的其他类成员切割掉,而引用传值则不会出现这种问题。

条款21:必须返回对象时,别妄想返回其reference

尽管在条款20中讲到引用比传值开销更低,但是要避免在使用引用时,传递的是一个reference指向并不存在的对象。

class Rational
 {
 private:
     int n,d;
    friend 
     const Rational operator* (const Rational& r1, const Rational& r2){…}
 public:
     Rational(int n = 0, int d = 1);  
 };

Rational a(1,2);
Rational b(3,5);
Rational c = a * b;

在调用operator*时返回的是一个两个Rational对象的乘积的引用,但是期望这个含有乘积(a * b)的Rational对象本来就存在是不合理的,因此必须要创建这个Rational对象。
下面三种创建对象的方法都是不可取的:
1.创建在栈空间。

const Rational& operator* (const Rational& r1, const Rational& r2)
{
    Rational result(r1.n * r2.n, r1.d * r2.d);
    return result; //result是个local对象,函数退出前就销毁了
}

2.创建在堆上。

const Rational& operator* (const Rational& r1, const Rational& r2)
{
    Rational* result = new Rational(r1.n * r2.n, r1.d * r2.d);
    return *result; //无人来delete以释放result的内存,导致资源泄露
}

3.创建local static 对象。

const Rational& operator* (const Rational& r1, const Rational& r2)
{
    static Rational result;
    result = … ;
    return result; 
}

bool operator==(const Rational& r1, const Rational& r2);
Rational a,b,c,d;
…
if((a * b) == (c * d)) {
    …
} else {
    …
}

在上述代码中,(a * b) == (c * d) 总是为true,原因是(a * b) == (c * d) 等价于 operator==(operator*(a, b), operator*(c, d)) ,在operator== 被调用前,已经有两个operator* 调用式起作用了,每一个都返回reference指向operator* 内部定义的static Rational 对象,虽然两次operator* 各自改变了static Rational 对象值,但是由于返回的是引用,所以调用端看到的永远是static Rational 对象的“现值”,所以两次是相同的。


条款26-31——实现


条款32-40——继承与面向对象设计

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值