〖轻松一刻〗:BMW和一块帆布
天要下雨了,车场上一辆BMW,工作人员拿一块帆布将它盖得严严实实。丁小明和南老师经过时看到了这一幕。
丁小明:“看,那块帆布就是一种封装,它保护了底下的BMW不受风吹雨打。”
南郁:“不,真正的封装,是BMW自身,这辆车才是完美的封装艺术”。
“封装”有这么多内容吗?有些教材,不就是把“public、protected、private”一提,就结束了有关“封装”的内容吗?让我们回顾一下本章的历程。
Ø 不变式是类的灵魂
要懂得“封装”,语法与语义上第一件要理解的是“this”指针,知道它是被编译器偷会为成员函数的偷偷加一个入参,就是它。然后要能理解就算是一个看起来一个空空的类,它也会有一些“默认的行为”。
但是,关于封装的第一件有“灵魂”的事,是“类的不变式”。让我们来复习一下这些没有灵魂的封装:
说这就是一个坐标?这是裸奔。封装都谈不上,哪来的灵魂。
代码阅读者的心理负担因为这个结构的存在,而减轻了很多,但它也就如此了, 不存在更多信息的封装。
这是冗余保护,struct穿上了小马甲摇身变成class,可是我们还是认得出它。
面向对象技术本来就是用来对付足够复杂的问题的。如果面对的是一个简单的程序中的某个简单的事物(此处的坐标点),那么struct版本的Point就是最佳选择。如果面对的是一个复杂的程序中的一个简单的对象,冗余保护的那个版本值得借鉴。
一个类型,可能有十数个成员数据,但如果这些成员数据之间彼此没有什么关系,那么很可能它就只适合做一个结构;或者,它只有一个内部数据,但它在和外部打交道时,需要在多种状态之间按照某种规律切换,那么这个类也需要我们好好去封装,以维护它的 “不变式”。
Ø 需要拷贝构造和赋值吗?
当一个类在构造对象时,需要分配额外资源,通常就需要一个配套的析构函数,用以在对象消逝前释放资源。当然,并不是说资源只能在析构时释放,但通常会是个好时机可以确保资源被安全回收。
如果仅仅是这样,那倒也简单,一对一地写构造和析构函数就是了;但是想到对象还会以另一个对象为模子创建出来,这就需要一个拷贝构造函数了。再想到对象还可以彼此间赋值,于是要重载赋值操作符,这下子要放下自己原来已有的,然后再接受对方已经有的……这些过程,都要考虑“深拷贝”或“浅拷贝”问题。
是时候做一个提示了:并不是所有类(的对象)都需要“拷贝”或“赋值”的。比如写一个窗口(Windows)程序,会有许多和窗口类,这些类通过就在需要时构造出一个实际窗口,很少需要进行“拷贝”,或者让A窗口赋值给B窗口。再比如,我们自己定制的某些容器类(比如前述的IntegerList),在实际应用中,有时候没有复制整条链表的需求。
当某个类不需要 “拷贝构造”或“相互赋值”的能力时,可以直接明了的将这两个函数声明为私有的:
注意:我们把FontDialog的拷贝构造和赋值操作符声明为私有的,这就代表了外部再也不能调用这两个函数,既然外部不会调用这两个函数,自然我们也就不需去真正实现它们。
下面的代码通过不过编译:
〖小提示〗:STL 容器里的元素,必须“可复制”
STL的容器,默认采用“值”存储。并且由于要支持添加,排序等,所以元素之间 经常需要复制,所以想要放入STL标准容器的对象,都必须能拷贝及赋值。不过,如果一个对象在实际逻辑上确实不能复制,这时如果又需要放在容器里,最正确的做法就是只在容器里保存它们的指针(相应的,通过需要将元素在堆里分配)。
-------------------------------------------------------------------------------
如果您想与我交流,请点击如下链接成为我的好友:
http://student.csdn.net/invite.php?u=112600&c=f635b3cf130f350c