从二叉搜索树(Binary Search Tree)入手,学习C++中类的构建 --(二)class与struct, 构造函数,重载函数

来源:互联网 发布:paxos一致性算法 编辑:程序博客网 时间:2024/06/06 13:20

一、Class与Struct

class是C++的特性,而struct则是沿用自C。通常意义上来说,我们认为struct仅仅是用来存放一些数据的集合,而不包含成员函数。这个理解是简单的,也建议初学class的人这样理解。但是记得最开始在LeetCode上练习链表的时候,会出现如下的struct定义:

strcut Node {int val;Node *next;Node(int x): val(x), next(NULL) {};};

可见:struct允许添加成员函数!是不是很新鲜?

其实这一点也不新鲜,如果你知道struct还可以用private\public\protected、还能实现继承、实现多态的话……

这么看来,class和struct根本就是一个东西嘛!其实,还是有点区别的。struct中的成员默认为public,而class的成员则默认为public。

从历史原因来看,C++为了保证对C的兼容,逐渐对struct添加了更多的新特性使得它和class并没有太大区别!(对没错就是没什么太大区别)

而且从编译器的角度来说,编译时对于class和struct的行为也是接近的!

说了这么长,结论呢……就是还是把struct看做简单数据的集合,不包含成员函数这样的理解比较合适

二、构造函数

回到我们的BST类。我们希望在构造的时候就创建一个二叉搜索树。即希望有这么一个构造函数存在:
BST::BST(vector<int>&);
这就涉及这么几个知识点:

默认构造函数 default constructor

默认构造函数就是在创建一个对象时自动调用的初始化函数

[1]未定义默认构造函数时,编译器行为

BST没有默认构造函数,于是编译器会为这样的class自动生成一个隐式的默认构造函数 implicit default constructor。这个默认构造函数由成员的默认构造函数组成。

C++的STL都是有默认构造函数的,比如:
vector 调用默认构造函数创建一个空的vector
string 调用默认构造函数创建一个空的string (string在C++中的存储也是很有意思的,sizeof(string)在VS下得到28,无关其内容,有机会总结一下)
int a[10] 这样的则是没有构造函数的,仅仅是在内存中申请一片区域存放该数组。

因此,我们的类:
class BST {public:Node* head;vector<Node*> sortedTree;};
——Node* head没有默认构造函数。
——vector<Node*> sortedTree 有默认构造函数
所以声明一个新的BST类的时候: 
BST tree1;
head的值未初始化,sortedTree调用了vector的默认构造函数,为空。

[2] 另一个例子

看下面的代码:
class A {private:int x;int y;vector<int> vec1;public:A(): x(0),y(0){};A(int val): x(val){};};
无论我们调用 A() 还是 A(10) , class A中的成员vector<int> vec1, 都会调用一次自己的默认构造函数。
这正说明了:对于构造函数中未提及的成员,都会默认调用它的default constructor

注意区分:——没有默认构造函数的成员是不会被初始化的,即使某个重载的构造函数中定义了该成员的初始化方式.
A class_1(1);cout<<x<<y;
输出结果是x=1,y未知。
因为虽然在A()中有y(0)的初始化式,但是很显然:一个class不会调用自身的构造函数两次(为什么提到这个似乎没什么意义的例子?因为在类的继承中,派生类会去基类中寻找成员的默认构造函数)

[3] 添加一个不传递参数的默认构造函数。

在BST类中,因为head是一个指针,而在BST的default constructor(由编译器生成的)中没有初始化这个指针,这就造成了对于未初始化head的访问是不安全的。
因此在这里决定定义一个显式的的默认构造函数,主要目的是为head赋值NULL以避免不安全访问
BST():head(NULL){};

[4] 复制构造函数 copy constructor

根据定义,copy constructor的形参应该是一个引用&。(const则未限定)
我们期望创建BST的方式可以是多样化的,这里首先定义一个从vector构建的constructor
BST(const vector<int>&);
根据上一篇中我们所写的伪代码,可以比较容易的获得简单版本的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]);head = put;for(unsigned int i=1;i<num.size();i++){put = new Node(num[i]);<strong>insertNode(*head,*put);</strong>//printNode(put);}inorderBST();}
因为在constructor中我们要对每个数值做插入操作,所以还需要定义插入节点的函数 insertNode。其实现如下:
/* insert new node */void BST::insertNode(<strong>Node& root,Node& put</strong>){if(put.val<=root.val){if(root.left==NULL)<strong>root.left = &put;</strong>//I can't add const because thereelseinsertNode(*root.left,put);}else{if(root.right==NULL)root.right = &put;elseinsertNode(*root.right,put);}}/* ----- */
在insertNode()中,我们采用的是引用传参的方式。
注意一个问题:虽然C++中对于不希望改变的元素,最好添加const限定符。但是这里因为我们要把put的地址赋值给root.left,因此不能限定为const,虽然我们并不希望改变put节点
(当然也可以声明为const。那样就要用到const_cast类型转换消除const属性)

重载函数(重载构造函数) overloaded functions

截止到目前,我们的类变成了这样:
struct Node {int val;Node* left;Node* right;Node(int x): val(x),left(NULL),right(NULL) {};};/*- - - class BST - - -*/class BST {private:void insertNode(Node&,Node&);public:Node* head;vector<Node*> sortedTree;BST():head(NULL){};BST(const vector<int>&);};
其中BST(*)有两个同名函数,但是他们的形参表并不相同。
这就是重载函数。(在这里他们还是构造函数,因此可以叫overloaded constructors

小结

截止到这里,我们的BST类还有一些显而易见的问题:
1 - sortedNode的值还未处理
2 - 构造函数调用了new,析构函数却没有调用delete。显然会造成内存泄露

从二叉搜索树(Binary Search Tree)入手,学习C++中类的构建 --(一)BST与基本框架
0 0
原创粉丝点击