[Basic]面向对象之信息隐藏(二)

来源:互联网 发布:日系男装店 知乎 编辑:程序博客网 时间:2024/05/02 02:56

信息隐藏

在前一篇文章中,谈到了防止数据被直接访问与修改的处理方法。Hmm...让我们多聊聊访问控制与信息隐藏吧。让我们假设你现在编写了一个类库,没有采取任何措施,典型的传统C库。嗯,就是一些struct加上对struct的操作方法。团队的其它的一些程序员(client programmers)依赖着你这个类库而做其它的软件设计。突然有一天,你回过头再看你设计的这个类库,你发现里面的一个设计可以更棒,可以让算法更加高效。Wow...真是太美妙的事情了。然而,你想改变,client programmers却想要保持不变。因为他们不想改变他们的代码,因为你这一改变,可能伤筋动骨,涉及到他们使用的类库成员。Hoho...传统的C库就是这么的“赤裸与暴力”,对于client programmers而言,他们可以看见一切,你也没有任何办法阻止他们。而这种“赤裸与暴力”也不是你这个类库设计者想要看见的,你只想要他们知道如何使用类库即可,不必了解是如何实现的;你也只想要他们使用你提供的方法,而不是去对方法有什么改变,去触碰到他们不该触碰到的东西;更重要的是当你想要改变内部实现设计时,你发现你不能!这是一件多么痛苦的事情,你是这么迫切的想要设立访问控制以及信息隐藏!Ho...让我们看看C++和Java怎么做的吧。

C++提供的访问控制与信息隐藏支持

C++提供了三个关键字(Keywords)来支持访问控制:public,protected,private。关于这三个关键字的意义与使用,正如这三个关键字的本身含义一般。

public: 意味着公共的,everyone都能够访问。Hmm...作为类库设计者,你想要client programmers访问到的方法,我想你应该放到这。

private:与public相反,意味着除了你之外,别人都不可以访问。如果你想要你的数据成员隐藏,不被直接访问到,我想你应该放到这。

protected:这个关键字,大部分时候和private工作一样,只是在继承中,子类可以访问到父类中的protected成员,让我们谈到继承的时候再细说。

那么让我们写一下上文中谈到的数据保护吧,Ho...

//naked datastruct stuff {int mI ;};//data hiddenstruct mumble {public:int getI() const;void setI(int i);private:int mI;};int mumble :: getI() const {return mI;}void mumble :: setI(int i) {mI = i;}int main(int argc,char* argv[]) {struct stuff test1;struct mumble test2;test1.mI = 30; //OK!test2.mI = 40; //Error!test2.setI(40); //OK!std::cout<<test1.mI<<std::endl; // OK!std::cout<<test2.getI()<<std::endl; // OK!return 0;} 

Hmm...我想C++中关于访问控制与信息隐藏基本上讲完了,让我们讲讲别的吧。你是说还有友元没有讲吗?不,我不打算讲那个。我好不容易才进行好信息隐藏,我可不想用友元来破坏。如果感兴趣,可以查询一下有关资料。而对于友元的使用,建议:三思而行。

看见这种struct,也许C++程序员比较正常,而C程序员则也许会有点疑惑,这还是struct吗?(Hmm...我的思维又跳跃了,突然想起了一位同学去应聘一家公司,公司的面试官问他,在纯C的环境中,struct可以加入函数吗?读者自己想想答案吧)。嗯,我很“悲情”的看着C程序员,是的,这真的是struct,只是和C有点不一样而已。

在C++中的世界中,struct和C的struct有点不同。至于这个不同,OMG,网上太多了,请自行Google吧。

Hmm...那么我们看见C++中的struct可以申明public,private,可以加入函数进行数据控制等,也许有读者会说那要class干嘛?除了那千篇一律的struct里面的成员默认访问权限是public,class里面的成员默认访问权限是private,struct默认继承是public继承,而class是private继承,亦或是是否可以使用大括号进行初始化等语法枝叶,他们到底还有什么区别?其实,class能干的事情,struct几乎都能干。是的,是几乎。什么时候不能干?

template <struct T> // Error!template <class T>  // OK!void function (T argv) {std::cout << argv << std::endl;}
Ho...好吧,我承认这依然是语法枝叶,读者试着去解答一下为什么这里struct不行吧......

那么,既然如此,为什么要C++要引进class呢?这真的一定要引入class才行吗?答案是否定的,这依然是一种哲学。不过,class的引入足够让人满意,因为它超越了一个关键字了,它背后隐藏的支持封装与继承的哲学更让人着迷。Ah...让我们想象一下,一个virtual base struct,里面再含义多个struct...与之相比,再使用class......

那么,我们又问,我们在C++中,又什么时候使用struct呢?(这里的struct指的C式的,只是数据封装,而非有函数的,如果你要问何时使用C++的含有函数的struct,我想我会给你一个美丽的答案:你们团队觉得舒服的时候。前文已经分析的很清楚,因为他们其实没有多少差别,只是哲学上的美丽以及蕴含的ADT思想不同)在揭晓我的答案前,让我们看看一段代码吧(From "Inside the C++ Object Model")

//C programer's trick...struct mumble { /* stuff */ char pc[ 1 ]; }; // grab a string from file or standard input // allocate memory both for struct & string struct mumble *pmumb1 = ( struct mumble* ) malloc(sizeof(struct mumble)+strlen(string)+1); strcpy( &mumble.pc, string ); 
上述代码是C程序员的trick,这样可以让每一个struct的object都拥有可变大小的数组。

Ho...让我们用class来改变一下,这个class有public,protected,private访问限制符。

class stumble { public: // operations ... protected: // protected stuff private: /* private stuff */ char pc[ 1 ]; }; 

而C程序员的这个trick在C++中也许会奏效,也许不奏效。需要看protected的成员在private前面还是后面才行。Hmm...我想我不会告诉你protected的成员需要在private前面才能奏效。所以,在写访问控制符顺序时,也许有时候你也应该有所策略。

让我们再想象一下,如果这个class继承于一个class,那么基类与子类也没有规定成员的分布先后,很不幸,如果还有virtual  function,可能只有泪流满面了......那么反过来,你这样把数据再反过去传给C程序员,让他来做struct。他也许也会泪流满面。于是,得出的结论就是:让我们忘记那些trick,并且注意保持C与C++中兼容的空间布局。

所以,如果C++中使用C式的struct的一个合理用途是:当你要传递一个复杂的class object里面的全部数据或者部分时,尤其是给C的函数使用时,你最好把数据抽象成struct,封装起来,并且保证C与C++兼容的空间布局。如何保证,Hmm...我想我不会告诉你只有在组合而非继承的情况下才能保证。至于缘由,读者可以自行Google查查资料。

Next:

面向对象之信息隐藏(三)


Main References:

Inside the C++ Object Model Chapter 01, Chapter 03; Author: Stanley B. Lippman

Thinking in C++ 2nd Edition Chapter 05; Author: Bruce Eckel

The Design and Evolution of C++ Chapter 01, Chapter 02, Chapter 03;Author:Bjarne Stroustrup