Effective C++读书笔记(四) 设计与声明

来源:互联网 发布:海迅开料软件 编辑:程序博客网 时间:2024/05/17 19:23

4 设计与声明

•    软件设计,是令软件做出你希望它做的事情的步骤和做法,通常以颇为一般性的构想开始,最终演变成十足的细节,以允许特殊接口(interface)的开发,这些接口而后必须转换为C++声明式。

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

               Makeinterfaces easy to use correctly and hard to use incorrectly.

                 •    好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达成这些性质。

                 •    “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容

                 •    “阻止误用”的办法包括建立新类型、限制类型上的操作、束缚对象值,以及消除客户的资源管理责任。

                 •    tr1::shared_ptr支持定制型删除器(custom deleter)。这可防范DLL问题,可被用来自动解除互斥锁(mutexes)

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

               Treat classdesign as type design.

                 •    设计高效类型(types)(classes)的方法:

                        1.       新type的对象应该如何被创建和销毁

                               •    构造和析构函数以及内存分配和释放

                        2.       对象的初始化和对象的赋值该有什么样的差别

                               •    构造函数和赋值(assignment)操作符的行为

                        3.       新type的对象如果passed by value(以值传递),意味着什么

                               •    Copy函数用来定义一个type的passed by value

                        4.       什么是新type 的“合法值”?

                               •    <异常处理>

                        5.       你的新type需要配合某个继承图系(inheritance graph)

                               •    <是否需要虚函数>

                        6.       你的新type需要什么样的转换?

                               •    explicit

                        7.       什么样的操作符和函数对此新type而言是合理的?

                        8.       什么样的标准函数应该驳回?

                        9.       谁该取用新type的成员?

                               •    Public? Private? Protected?Or friend?

                        10.   什么是新type的“未声明接口”(undeclaredinterface)?

                        11.   你的新type有多么一般化?

                               •    Class template?

                        12.   你真的需要一个新type吗?

                               •    Derived class ?

                •    Class的设计就是type的设计。在定义一个新type之前,请确定你已经考虑过本条款覆盖的所有主题。

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

                Perfer passby reference to const to pass by value.

•    如果你有个对象属于内置类型(例如 int),passby value 往往比pass by reference的效率高些。

•    尽量以pass by reference替换 pass by value 。前者通常比较高效,并可避免切割问题(slicing problem)。

•    不过并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言pass by value 往往比较适当。


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

               Don’t try toreturn a reference when you must return an object

•    当我们领悟条款20中传值的开销后,总是避免于少用传值,然而在返回对象时,要格外小心了,因为你可能:传递一些引用或指针指向其实已经不存在的对象。这可不是件好事。

•     任何时候看到一个reference声明式,你都应该立刻问自己,它的另一个名称是什么?

•    函数创建新对象的途径有二:在栈空间和堆空间

          1.       栈上:即在函数内的局部变量。局部变量在函数返回后就没有存在的意义,若还对它“念念不忘”,将带来灾难性后果。所以传引用在栈上不可行。

          2.       堆上:在堆上构造一个对象,并返回。看似可行,也埋下了资源泄漏的危险。谁该对这对象实施delete呢?别把这种对资源的管理寄托完全寄托于用户。所以传引用                                 在堆上不可行。

          3.       可能还有一种想法:把“让返回的引用指向一个被定义于函数内部的静态对象”。出于我们对多线程安全性的疑虑,以及当线程中两个函数对单份对象的处理也可能带                     来不可测行为。所以静态对象也是不可行的。

          4.       一个“必须返回新对象”的函数的正确写法是:就让那个函数返回一个新对象。
                    编译器实现者实行最优化,用以改善产出码的效率却不改变其观察的行为。所以我们还是老老实实的返回一个对象吧。

•    Reference只是个名称,代表某个既有对象

•    不要返回pointerreferencce指向一个local stack对象或返回reference指向一个heap-allocated对象,或返回pointerreference指向一个local static对象而有可能同时需要多个这样对象。


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

                Declare datamembers private

•    将成员变量隐藏在函数接口的背后,可以为所有可能的实现提供弹性。例如,这可使得成员变量被读或写时轻松通知其它对象、可以验证calss的约束条件以及函数的前提和事后状态、可以在多线程环境中执行同步控制......
不封装意味不可改变!成员变量的封装性与成员变量的内容改变时所坏量的代码数量成反比。

•    切记将成员变量声明为private.这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。

•    Protected并不比public更具封装性


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

               Prefernon-member non friend functions to member functions

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


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

                Declarenon-member functions when type conversions should apply to all parameters.

•    只有当参数被列于参数列内,这个参数才是隐式类型转换的合格参与者

•    member函数的反面是non-member函数,不是friend函数

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


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

                Considersupport for a non-throwing swap.

        •    函数模板不能偏特化,不是语言实现不了,因为可以通过函数重载(overload)实现

        •    客户可以全特化std内的templates,但是不可以添加新的templates(或classes 或functions或其他任何东西)到std里。Std的内容完全由C++标准委员会决定,标准委               员会禁止我们膨胀那些已经声明好的东西。可以置于其他命名空间中。

 

  • 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。

  • 如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于class(而非templates),也请特化std::swap。

  • 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。

  • 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。   

 

原创粉丝点击