Loading

《Effective C++》设计与声明章节

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


Item19:设计class犹如设计type
如何设计一个高效的class?首先你必须了解你将面临的一些问题:
1.新的type的对象应该如何被创建和销毁?这会影响到你的class构造函数和析构函数以及内存分配函数和释放函数(operator new,operator new[],operator delete,operator delete[]})。
2.对象的初始化和对象的赋值该有怎么样的差别?这个答案决定了你的构造函数和赋值(assignment)操作符的行为,以及中间的差异。很重要的是别混淆了“初始化”和“赋值”,因为它们对应了不同的函数调用。
3.新的type的对象如果被passed by value,意味着什么?记住,copy构造函数用来定义一个type的pass-by-value该如何实现。
4.什么是新type的“合法值”?对于class的成员变量而言,通常只有某些数值集是有效的,那些数值决定了你的class必须维护的约束条件,也就决定了你的成员函数(特别是构造函数、赋值操作、和所谓的“setter函数”)必须进行的错误检查工作。
5.你的type需要配合某个继承图系吗?如果继承自某些已经有的classes,那么该type就会受到那些classes的设计的束缚,特别是受到它们的函数是virtual或者是non-virtual的(尤其是析构函数)。
6.你的新type需要类型转换吗
7.什么样的操作符和函数对此type是合理的?这个问题的答案将决定你将为你的class声明什么member函数。
8.什么样的标准函数应该禁止被使用?那些正是你要设置为private的函数。
9.谁该取用新的type成员?这个提问将帮助你决定哪个成员是public,那个是private或者protected。它也帮助你决定哪一个class或function是friend。
10.什么是新type的“未声明接口”?它对效率、异常安全性以及资源运用提供何种保证?在这方面提供的保证将为你的class实现代码加上约束条件。
11.你的新type有多么一般化?或许你定义的并非是一个新的type,而是一整个type家族,那么你将需要使用template。
12.你真的需要一个新的type吗?如果只是定义新的derived class以便为现有的class添加机能,那么说不定单纯定义一个或者多个non-member函数或者tmeplates,更加能够达到目标。
总结:请记住,class的设计就是type的设计,在定义一个新的type之前,请确定你已经考虑过本条提议所覆盖的主题。


Item20:宁以pass-by-reference-to-const替换pass-by-value
以pass-by-reference方式传递参数可以避免slicing(对象切割问题)。当一个derived 对象以by-value的方式作为参数传递的时候,会被认为是一个base class对象,base class的copy构造函数会被调用,而“造成此对象的行为像个derived class 对象”的那些特化性质全都被切割掉了,仅仅剩下了一个base class对象。

void show(father a)//以值传递
{
//do something...
}
int main()
{
 son s;
 show(s);//如果你用子类的对象传进来的话,会被构造出一个父类对象,而子类对象的那些特性全都被“切割”掉了!!!
}

当然,如果你的参数是一个内置类型的话,用pass-by-value的方式往往比pass-by-reference的效率要高(引用的底层还是使用指针来实现的)。
总结:

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

Item21:必须返回对象时,别妄想返回其reference
总结:

  • 绝对不要返回一个reference或者pointer指向一个local stack对象,或者返回一个reference指向一个heap-allocated对象,或者返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。条款4已经为“在单线程环境下合理返回reference指向一个local static对象”提供了一种解决方案。
  • -

Item22:将成员变量声明为private
总结:

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

Item23:宁以non-member、non-friend替换member函数
总结:

  • 宁可拿non-member、non-friend替换member函数。这样做可以增加封装性、包裹弹性(packaging flexibility)和机能扩充性。
  • -

Item24:若所有参数皆需参数类型转换,请为此采用non-member函数
总结:

  • 如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻的参数)进行类型转换,那么这个函数必须是个non-member。

Item25:考虑写一个不抛出异常的swap函数
总结:

  • 当std::swap对你的类型效率不高的时候,提供一个swap函数,并确定这个函数不会抛出异常。
  • 如果你提供一个member swap,也应该提供一个non-member swap函数用来调用前者。对于classes(而非templates),也请特化std::swap。
  • 调用swap时应针对std::swap使用using声明式,然后调用swap并不带任何“命名空间资格修饰”。
  • 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入对std而言全新的东西。
posted @ 2017-03-12 23:44  李正浩  阅读(95)  评论(0编辑  收藏  举报