您的位置:首页 > 编程语言 > C语言/C++

“Effective C++ Third Edition”学习笔记(三)

2009-12-20 22:04 267 查看
摘录一些要点,时刻提醒自己。

条款 28: Avoid returning "handles" to object internals.

class point{};

class Rectangle{

public:

point& getPoint() const {return data};

private:

point data;

}

在Rectangle的函数 point& getPoint() const中,这个函数本意是不能修改类内任何对象的,但是返回值却返回一个引用指向它想要禁止写操作的成员变量,功亏一篑,前后矛盾。

“引用”,“指针”和“迭代器”都是所谓的“handles”,一个函数如果返回它们,那么就打开了一扇开放的门,用户可以通过它们操作那些你本意是要保护的成员变量,而你的本意可能不是如此的。

对于上面的函数point& getPoint() const解决办法是:让函数返回一个const引用,即const point& getPoint() const, 这样用户就只有读权限,而没有了写权限。

但是const point& getPoint() const函数还是存在一些问题的:可能会存在dangling handle,这个handle指向的对象可能在某些时候,已经被对象给释放了,从而就成为一个“野指针”。一旦你一定需要这样做时,请一定要明白:handle的生命周期,有可能比它指向对象的生命周期还要长很多,而使用一个指向一个也已不存在的对象的handle是错误的,灾难的。

条款 30: Understand the ins and outs of inlining.

1)inline不是免费的,inline函数会在增加目标代码的大小。

2)大部分调试器无法支持inline函数的调试。因为inline函数在目标代码中已经替换为函数本体。

3)平均而言,一个程序往往将80%的执行时间花费在20%的代码上。作为程序开发者,你的目标是找出这可能增进程序整体效率的20%代码,然后将它inline或者竭尽全力去将它瘦身。 要努力抑制自己过早进行优化的才冲动,因为往往你不能预知你程序的性能瓶颈到底在哪里。

4)一开始先不要将任何函数声明为inline,或至少将inlining施行范围局限在那些“一定成为inline”或“十分平淡无奇”的函数身上。

5)inline函数和templates两者通常都被定义在头文件中。因为大多数编译环境在编译期进行inline的替换工作,此时编译器必须知道inline函数长什么样子。

6)templates通常被定义在头文件中,因为一旦使用了某个template,编译器使用某些类型将它具体化,必须知道它长什么样子。

7)template却不一定要求是inline的。

条款31: Minimize compilation dependencies between files.

#include "date.h"

#include "address.h"

class Person{

private:

Date theBirthdate;

Address theAddress;

}

在上面的例子中定义Person的头文件和“date.h”以及“address.h”形成了一种编译依存关系。如果这些头文件中的有任何一个发生变化,或者这些头文件所依赖的其他头文件有任何改变,那么每一个含入Person 类的文件就必须重新编译,任何使用Person类的文件也必须重新编译。

1)对于一个大项目来说,可能包含很多源文件,全部编译一次可能需要数小时。

2)如果你只修改了一个类的private部分,却引起很多源文件需要进行编译则可能有些郁闷。

3)在某种程度上这个条款是建议我们“多用聚合,少用组合”。也就是“多用对象引用或者对象指针,少用对象”。你可以只依靠一个类型声明就定义出指向该类型的引用或者指针。但是如果定义某个类型的对象,则必须要看到这个类型的定义。因此如果这个类型定义发生变化,则使用这个类的对象的那些文件都需要重新编译。

聚合:指的是整体与部分的关系。通常在定义一个整体类后,再去分析这个整体类的组成结构。从而找出一些组成类,该整体类和组成类之间就形成了聚合关系。例如一个电脑包括鼠标、键盘、显示器、内存条,主板、硬盘等。需求描述中“包含”、“组成”、“分为…部分”等词常意味着聚合关系。

组合:也表示类之间整体和部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。部分对象与整体对象之间具有共生死的关系。

聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。

4)当你声明一个函数而它用到某个class时,你并不需要改class的定义:纵使函数以by value方式传递参数或者返回值也是这样的。

class Date;

Date today();

void clearAppointments(Date d);

因此声明这些函数的头文件中可以不包括定义class的头文件,使用这些函数的那些客户文件包含定义class的头文件,是可以的。这样就减少了一些编译依存性。

为了减少编译依存性,以及便于编程,可以分别将统一个类的“类的定义”和“类的声明”分别放在两个头文件中,这样对于不同的用户,各取所需,一定程度上减少编译依存性。

5)C++也提供关键字export,允许将template声明式和template定义式分割于不同的文件内。不幸的是支持这个关键字的编译器目前比较少。

6)降低编译依赖性的一种方法是:Handle class pimpl idiom(pimpl是“pointer to implementation”的缩写), 以及利用多态的interface class定义一个抽象类作为接口。

7)你应该考虑渐进方式使用这些技术。在程序发展过程中使用Handle class和Interface class以求实现代码有所变化时对其客户带来最小的冲击。而当它们导致速度或者大小差异过于重大,以至于类之间的耦合相形之下不再那么重要时,你就可以使用具象替代Handle class和Interface class。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: