Effective C++笔记 (4.设计与声明)

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

请记住:
  1. 好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达到这些性质。
  2. “促进正确使用”的方法包括接口的一致性,以及与内置类型的行为兼容。
  3. “阻止误用”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。
  4. trl::shared_ptr支持定制型删除器(custom deleter)。这可防范DLL问题,可被用来自动解除互斥锁(mutexes)等等。

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

如何设计高效的classes呢?
  • 新type的对象应该如何被创建和销毁?这会影响到class的构造函数和析构函数以及内存分配函数和释放函数的设计。
  • 对象的初始化和对象的赋值该有什么样的差别?这个答案决定你的构造函数和赋值(assignment)操作符的行为,以及其间的差异。很重要的是别混淆了“初始化”和“赋值”,因为它们对应于不同的函数调用。
  • 新type的对象如果被passed by value(以值传递),意味着什么?记住,copy构造函数用来定义一个type的pass-by-value该如何实现。
  • 什么是新type的“合法值”?对class的成员变量而言,通常只有某些数值集是有效的。那些数值集决定了你的class必须维护的约束条件(invariants),也就决定了你的成员函数必须进行的错误检查工作。它也影响函数抛出的异常、以及函数异常明细列。
  • 你的新type需要配合某个继承图系(inheritance graph)吗?如果你继承自某些既有的classes,你就受那些classes的设计的束缚,特别是受到“它们的函数是virtual或non-virtual”的影响。如果你允许其它classes继承你的class,那会影响你所声明的函数---尤其是析构函数---是否为virtual。
  • 你的新type需要什么样的转换?如果你希望允许类型T1之物被隐式转换为类型T2之物,就必须在class T1内写一个类型转换函数(operator T2)或在class T2内写一个non-explicit-one-argument(可被单一实参调用)的构造函数。如果你只允许explicit构造函数存在,就得写出专门负责转换的函数,且不得为类型转换操作符(type conversion operators)或non-explicit-one-argument构造函数。
  • 什么样的操作符和函数对此新type而言是合理的?这个问题的答案决定将为你的class声明哪些函数。其中某些该是member函数,某些则否。
  • 什么样的标准函数应该驳回?那些正是你必须声明为private者。
  • 谁该取用新type的成员?这个提问可以帮助你决定哪个成员为public,哪个为protected,哪个为private。它也帮助决定哪一个classes和functions应该是friends,以及将它们嵌套于另一个之内是否合理。
  • 什么是新type的“未声明接口”?它对效率、异常安全性以及资源运用提供何种保证?
  • 你的新type有多么一般化?或许你其实并非定义一个新type,而是定义一整个types家族。果真如此你及不该定义一个新class,而是应该定义一个新的class template。
  • 你真的需要一个新type嘛?如果至少定义新的derived class以便为既有的class添加机能,那么说不定单纯定义一或多个non-member函数或templates,能够达到目标。

请记住:
  1. Class的设计就是type的设计。

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

缺省情况下C++以by value方式(一个继承自C的方式)传递对象至函数。

请记住:
  • 尽量以pass-by-reference-to-const替代pass-by-value。前者通常比较高效,并可避免切割问题(slicing problem)。
  • 以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。

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

函数创建新对象的途径有二:在stack空间或在heap空间创建之。

请记住:
  • 绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。


条款22:将成员变量声明为private

请记住:
  • 切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允许约束条件获得保证,并提供class作者以充分的实现弹性。
  • protected并不比public更具封装性。

条款23:宁以non-member、non-friend替换member函数

请记住:
  • 宁可拿non-member、non-friend函数替换member函数。这样做可以增加封装性、包裹性和机能扩充性。

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

请记住:
  • 如果你需要为某个函数的所有参数进行类型转换,那么这个函数必须是个non-member。

条款25:考虑写出一个不抛异常的swap函数

swap是个有趣的函数。原本它只是个STL的一部分,而后称为异常安全性编程的脊柱,以及用来处理自我赋值可能性的一个常见机制。

首先,如果swap的缺省实现码对你的class或class template提供可接受的效率,你不需要额外做任何事。任何尝试置换(swap)那种对象的人都会取得缺省版本,而那将有良好的运作。

其次,如果swap缺省实现版的效率不足(那几乎总是意味你的class或template使用了某种pimpl手法),试着做以下事情:
  1. 提供一个public swap成员函数,让它高效地置换你的类型的两个对象值。
  2. 在你的class或template所在的命名空间内提供一个non-member swap,并令它调用上述swap成员函数。
  3. 如果你正在编写一个class,为你的class特化std::swap。并令它调用你的swap成员函数。

请记住:
  • 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
  • 如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes(而非templates),也请特化std::swap。
  • 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。
  • 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些std而言全新的东西。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值