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

本文探讨C++中class与struct的区别,重点讲解构造函数,包括默认构造函数的编译器行为、复制构造函数以及重载构造函数。通过实例解析,帮助读者深入理解构造函数在二叉搜索树数据结构中的应用。
摘要由CSDN通过智能技术生成

一、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 there
		else
			insertNode(*root.left,put);
	}else{
		if(root.right==NULL)
			root.right = &put;
		else
			insertNode(*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。显然会造成内存泄露

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值