从二叉搜索树(Binary Search Tree)着手,学习C++中类的构建 -(三)public\private\protected、封装\继承\多态、提供更丰富的函数接口
面向对象(objected-oriented,或者说理解为面向物体\实体 更合适一些),重要特点就是:封装、抽象、多态、继承
在本篇中,主要提及的是封装(Encapsulation)。而封装提供给我们的不只是更加结构化、模块化的程序设计,还有一个最重要的特性:数据隐藏。
一、访问标号:public-private-protected
简单的来说,访问标号的用处就相当于为各个类成员添加一个权限。好比linux系统下的文件权限一样,可以对不同的访问者、用户组给予不同权限。
类成员的访问者有三个:自身成员访问、类的对象访问、派生类访问。很好理解——
自身成员访问:
因为是自己的,所以相当于root权限。不会对自己隐藏自己的东西,哪怕是private。类比: 不管你怎么隐藏硬盘中的文件,硬盘自己都知道它在哪
类的对象访问:
——什么是对象?你不会做cpu,不会写操作系统,但这并不妨碍你能使用电脑。电脑就是一个对象,你能够使用该对象提供的接口-interface。
对象可以看做是用class封装后生产出的产品。生产的成品会使用自己内部的构造,而你只是负责使用的,所以你不必知道它是怎么使用的。
另一方面生产者往往也并不想给你公开这个东西的内部结构,一是怕你随便拆拆坏了--[数据保护],二是为了保护自己的专利--[数据隐藏]。
所以,类把源码和成品分开,并添加权限private\pubic做为区分。
派生类访问:
虽然同样是LINUX操作系统,但是我不仅可以在PC上使用它,我加一个外壳,把它做成Android,还可以在手机上使用它。
这里,Android就是Linux的一个派生类,而作为操作系统,Android显然不能不关心Linux是怎么工作的,所以对于Android用到的基于Linux的功能,Android必须能够有权限看到这部分的工作方式——protected。(当然作为用产品的人,我们还是不必知道这个东西里面究竟是怎么回事,对于对象来说protected也是加密的)。
而之所以开辟出这么一个级别,是因为面向对象的特性:多态、继承。Linux能够做桌面操作系统Ubuntu,还能做嵌入式操作系统Raspbian,更能来做手机操作系统Android。(其实WIndows也很厉害的,还能做ATM。。。)他们属于linux的分支(继承),但又各自拥有不同的新特性(多态)
说了这么过,访问标号的问题反而好理解了:
public——公开展示,任何人都可以访问
private——隐私内容,只有类成员函数可以访问。(不过friend友元函数也是可以的,可以理解为至交好友之类的。这里先不讨论)
protected——类成员函数可以访问、派生类可以访问基类的protected、对象不可访问。
二、public-protected-private的继承
[1] 继承的使用:公用继承、受保护继承、私有继承
class Child : public/protected/private Father { };
[2]多态,继承后的变化
[3] 基类行为:总裁并不那么负责任
三、提供更丰富的函数接口
1、实现调用构造函数的同时,维护vector<Node*> sortedNode,使之存放排序后的节点
void* getAllNode(Node *a) {sortedTree.push_back(a);return NULL;}作为维护vector的函数。
/* inorder visit BST */ void BST::inorderBST() { if(head==NULL) return; inorderBST(head->left); this->getAllNode(head); inorderBST(head->right); std::cout<<std::endl; } void BST::inorderBST(Node* head) { if(head==NULL) return; inorderBST(head->left); this->getAllNode(head); inorderBST(head->right); }这里重载了inroderBST,原因在2中有分析。
BST::BST(const vector<int>&num) { if(num.size()==0){ std::cout<<"Create Tree Failed, NULL Input!\n"; return; } Node *put = new Node(num[0]); //fun = &getAllNode; sortedTree.push_back(put); head = put; for(unsigned int i=1;i<num.size();i++) { put = new Node(num[i]); insertNode(*head,*put); //printNode(put); } inorderBST(); } /* ----- */
2、提供拥有函数接口的inorderBST函数
void inorderBST(void*(*)(Node*,void*),void*);
void BST::inorderBST(void*(*)(Node*,void*) fun,void* argument) { if(head==NULL) return; inorderBST(head->left,fun,argument); fun(head,argument); inorderBST(head->right,fun,argument); } void BST::inorderBST(Node* head,<span style="font-family: Arial, Helvetica, sans-serif;">void*(*)(Node*,void*)</span> fun,void* argument) { if(head==NULL) return; inorderBST(head->left,fun,argument); fun(head,argument); inorderBST(head->right,fun,argument); }
上面就是我们对于inorderBST函数的实现。
第二个函数作为内置调用的函数,标记为private。只希望内部调用,而希望对object隐藏。
至此,我们的class变成了这样——
class BST { private: inline void insertNode(Node&,Node&); void inorderBST(Node* head); void _deleteBST(Node* head); //BSTFUN fun; void inorderBST(); void inorderBST(Node*,<span style="font-family: Arial, Helvetica, sans-serif;">void*(*)(Node*,void*)</span>,void*); void* getAllNode(Node *a) {sortedTree.push_back(a);return NULL;} public: Node* head; vector<Node*> sortedTree; void inorderBST(<span style="font-family: Arial, Helvetica, sans-serif;">void*(*)(Node*,void*)</span>,void*); BST():head(NULL){}; BST(const vector<int>&); };
3、演示带有函数参数的inorderBST接口的使用:
void* printfnode(Node* a,void* arg) { std::cout<<a->val<<"=="; return NULL; }
struct get { vector<int> left; }; void* getNodeVal(Node*a, void* arg) { ((get*)arg)->left.push_back(a->val); return NULL; } /* == make sure destructor work == */ int main() { int a[7] = {3,1,8,2,6,7,5}; vector<int> num(a,a+7); BST tree1(num); tree1.inorderBST(printfnode,NULL); get tmp; tree1.inorderBST(getNodeVal,(void*)&tmp); return 0; }