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

从二叉搜索树(Binary Search Tree)入手,学习C++中类的构建 --(三)public\private\protected、封装\继承\多态、提供更丰富的函数接口

2015-03-13 14:22 399 查看
面向对象(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、[b]对象不可访问。[/b]

二、public-protected-private的继承

[1] 继承的使用:公用继承、受保护继承、私有继承

class Child : public/protected/private Father {

};

Child是Father的派生类。
这里主要讨论Child后面的 ”public“ 处,继承中访问标号的使用。

1 - 继承什么? 就是基类把自己分化,以适应各种不同的环境。
因为派生类没有基类的private成员访问权限。首先基类的private是自己的秘密,不会给任何派生类!
————因此也就是说:我们所继承的东西仅仅是pubic和protected!
————我们所关心的是,把基类成员,加上一个权限符号,他会变成什么权限?
————简单理解,就是只能把基类的成员变得更隐秘更难查看,更少的人能看到,而不能把它公开给更多人。

现在假设基类是一个公司的总裁。总裁手里有三种信息:
private——总裁经常挪用一些公款,这是秘密,谁也不能知道、
protected——公司的目前研发的一个项目,公司的职员可以知道、
public——公司的基本信息,我们公开给任何使用我们产品的用户

2 - private继承。
总裁现在需要向竞争对手处卧底一个职员,从事秘密工作。希望他保密公司的任何信息,可以提供一些维护服务,但是不能透露有关公司的任何信息。

————基类的protected和public,经过私有继承后,全部作为派生类的private成员。

3 - protected继承。
总裁现在要找一个下属,去开一个子公司拓展业务。子公司的员工知道他们是个分公司,但是子公司对外不公开自己的母公司。
————基类的public成员被继承为protected,显然protected继承后还是protected

4 - public继承。
总裁的业务做大了,在杭州建立了一个研究院,和总公司属于平行关系,相关研发都可以在新的研究院开展。
————protected仍然是protected,public依旧是public。

[2]多态,继承后的变化

尽管派生类继承自基类,但是他们都是一个独立的个体——
1 -- [b]private继承的卧底也需要有个合理的身份去伪装,因此可以发展出自己的public属性;[/b]
当然卧底有时候也需要发展自己的下线,所以卧底发展出了protected成员;
有时候卧底也会想要反水,这属于隐秘信息private。
————当然,毕竟是可控的派生类,再怎么发展卧底也不会把继承来的private说出去。

2 -- protected继承的子公司在发展中,[b]建立了自己的公众形象——产生public;[/b]
子公司随着业务的拓展,开了许多分部——产生protected;
子公司管理层腐败,[b]做了一些假账——发展出了自己的private;[/b]
[b] ————当然,继承的protected成员只能被子公司用来自己使用或者来开分部[/b]

3 -- public继承的杭州研究院,自然不必多说,和子公司有同样的权限。
不过因为吸纳了更多的员工,派生的类虽然和基类很像,但是也有自己的特色——public、private、protected

[3] 基类行为:总裁并不那么负责任

1——虽然private是派去卧底的,但是总裁把卧底派去后就忘记曾经派过这个人了;
2——虽然开个子公司是个大事,但是总裁记性实在太差,子公司开了一堆分部,但是总裁表示不记得这个子公司不是我开的;
3——虽然盖研究院的大楼花了10个亿,但是总裁钱多任性,某一天研究院终于解决了某个核心技术的研发,总裁却一点也没有高兴,因为他忘了……

总裁很健忘——基类并不能知道派生类里面有什么,假如基类不去看源代码,根本不知道自己竟然有这么个派生类、更不用说取得对派生类内部成员的权限了!
因此:
1 - 派生类完全可看做是一个独立的类!
2 - 基类是静态的,不会访问派生类内部,更不会动态获取派生类的变化!(不过提供一块共享区域倒是可以让基类获得派生类的变化,比如传递引用之类的)
3 - 因为基类是静态的,所以经过继承的派生类是多态的!这也是面向对象的重要特性!
4 - 相反,派生类的记性倒是很好,它能够记得自己的上一代基类是谁(调用构造函数时,对于未定义构造函数的成员会去基类中寻找)
5 - 尽管派生类的记性比总裁好那么一点,但是让它记住自己的爷爷是谁也还是太难了!(不关心基类的基类)
(注意:这里并没有考虑virtual继承的情况,如果是virtual继承,则是共享模式,即base能够获得派生类带来的变化)

三、提供更丰富的函数接口

回到我们的二叉搜索树。

1、实现调用构造函数的同时,维护vector<Node*> sortedNode,使之存放排序后的节点

首先内置一个操作vector的函数:
void* getAllNode(Node *a) {sortedTree.push_back(a);return NULL;}
作为维护vector的函数。
然后,定义我们的inorderBST()函数,使之能够中序遍历。
/* 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中有分析。

现在,我们的中序遍历已经完成了,把它放在拷贝构造函数中调用,即可保证生成类对象的时候,不仅产生一个二叉搜索树,而且维护一个有序的vector
copy constructor——
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函数

在框架中我们提到:希望提供中序遍历函数。在应用中,往往需要中序遍历二叉树做各种操作,譬如输出、拷贝、修改……等等。如果为这些行为每一个都写一个中序遍历函数,那么就太麻烦了!
因此,我希望用下面的方式,提供一个inorderBST的函数借口:
void inorderBST(void*(*)(Node*,void*),void*);

(想想qsort,一样的使用方式。)
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函数的实现。
因为中序遍历中,需要不断移动操作的位置,因此我们在这里重载了inorderBST()函数。
第一个作为public的接口,提供给对象使用。

第二个函数作为内置调用的函数,标记为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;
}


其中,printNode用来打印节点,输出排序后的队列。
getNodeVal则演示了传入一个结构体用来获得参数/传入参数的使用方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐