我是靠谱客的博主 敏感蜜粉,这篇文章主要介绍C++类的理解(二):函数重载和多个构造函数,以及析构函数,现在分享给大家,希望可以做个参考。

一、函数的重载
函数重载并不属于类的特性,是众多高级语言都有的一种函数特性,比如我有下面的函数:

复制代码
1
2
3
4
5
//函数1: int add(int a,int b) { return a+b; }

这个函数接受两个整型变量,返回他们的和,但如果我还要一些功能,比如两个double类型的和,一个整型和100的和,并且我也想用add这个函数名怎么办?
函数重载的概念就是用来解决这个问题的,我们把这些函数都写上:

复制代码
1
2
3
4
5
6
7
8
9
10
11
//函数2: double add(double a,double b) { return a+b; } //函数3: int add(int x) { return x+100; }

以上三个函数就是互为重载函数,他们拥有相同的函数名,但拥有不同的形参表。在代码中调用add函数,系统会根据情况智能的选择最恰当的函数来执行,而选择的标准就是参数表的匹配,举个例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
int a=10,b=20; double x=10,y=20; cout<<add(a,b); //由于传入参数是两个int,所以系统自动匹配函数1进行调用 cout<<add(x,y); //由于传入的是两个double,所以系统自动匹配函数2进行调用 cout<<add(a); //由于传入的是一个int,与函数三的参数表匹配,所以调用函数3 cout<<add(x); //由于传入的是一个double,没有与之匹配的参数表, //但函数3也是一个参数,系统就会尝试将double类型转换成int类型,然后调用函数3,但我们现在的这个情况是转换不了的,因为double编程int会丢失精度,所以该条语句会报错。

当然如果是下面这种情况却是没有问题的:

复制代码
1
2
3
char x=10; cout<<add(x); //因为char是8位整型,int是32位整型,系统将char转变成int时不会有问题,所以会返回int型的110;

这就是函数重载,有两个要求:
1、函数名一致
2、参数表不一致(包括参数数量和参数类型)
参数表完全匹配才可以调用,不匹配系统会擅自尝试类型转换以匹配,转换成功还好,不成功就报错。所以写代码时如果需要类型转换的话,最好也由程序员来自己转换,不要太依靠编译器系统。

函数重载有两个易犯的错误,其实都很简单,都是因为歧义而出的问题:
第一个:带默认值的函数,比如:

复制代码
1
2
3
4
5
6
7
8
9
int add(int x,int y=1) //y带默认值 { return x+y; } int add(int x) { return x+100; }

所以当有如下语句则报错:

复制代码
1
cout<<add(10);

系统会说它匹配了第一个函数(相当于没有给y参数,所以y参数用默认值),也匹配了第二个函数,所以他也不晓得你到底要用哪个函数。。。这个错叫:多个匹配的重载函数。

第二个错也是多个匹配的重载函数:

复制代码
1
2
3
4
5
6
7
8
9
10
//函数1: int add(int x,int y) { return x+y; } //函数2: double add(double x, double y) { return x+y }

然后来了个下面的语句:

复制代码
1
2
char x=10,y=20; cout<<add(x,y);

由于传的参数是两个char,没有这样的函数匹配,系统就自己开始类型转换了,他发现可以转成int,然后用函数1,也可以转成double用函数2……于是又晕了,报错:多个匹配的重载函数。

二、类的构造函数(初始化函数)重载

是个函数一般都是可以重载的,类里的函数也不意外,我们来说说构造函数的重载。
举个例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Point { public: int x; int y; public: Point() { this->x=0; this->y=0; } Point(int x,int y) { this->x=x; this->y=y; } };

这两个构造函数互为重载函数,一个不带参数,一个带两个参数。
然后有如下对象定义的话:

复制代码
1
2
Point t; Point tt(2,5);

第一个t,就是自动调用了没有参数的构造函数,被初始化成:t.x==0, t.y==0;
第二个tt,就自动调用了带两个整型的构造函数,有t.x==2, t.y==5;

然后再来看昨天一个问题:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Circle { public: Point center; int r; public: Circle(int x,int y, int r):center(x,y) { this->r=r; } /* 以及: Circle(int x,int y,int r) { this->center.x=x; this->center.y=y; this->r=r; } */ }

这两种写法是有区别的,第一种没有加注释的写法,是在初始化center的时候,被传了值center(x,y),所以是调用了Point 的有参数的构造函数直接初始化的。
第二种加了注释的写法,是构造center的时候,没有给参数,①:所以调用了Point 没有参数的构造函数,将x,y初始化成了0,②:然后再在Circle构造函数中把center的x,y重新赋了值的。
只有Point自己有不带参的构造函数,注释里的写法才是合法的。

构造函数的重载允许我们用各种方法来初始化我们的对象,很是方便的。

三、拷贝构造函数

这是一个特殊的构造函数,用于在一个同类型对象的基础上生成一个新的对象,他的函数形参表有固定的写法,比如以下二叉树节点的类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Node { public: int index; Node* left; Node* right; public: Node(int i) //构造函数,带一个参数 { index=i; left=0; right=0; } Node() //构造函数,不带参数 { index=0; left=0; right=0; } Node(const Node& t) //拷贝构造函数的特点是参数表是一个(const className&),t是自定义的变量名 { this->index=t.index; left=0; right=0; } }

拷贝构造函数以自己的一个同类对象为参数,生成一个新的对象,比如:

复制代码
1
2
3
4
5
6
Node t(10); //调用带一个参数的构造函数 Node x(t); //调用拷贝构造函数 //另外当我们使用赋值号生成新的对象时,系统也是调用拷贝构造函数 Node y=t; //等价于Node y(t);

所以再强调一遍吧,任何用自身同类对象生成新对象时,都会调用拷贝构造函数。

那么有一个问题,我刚刚说用赋值号的时候,系统会调用拷贝构造函数,那我没写拷贝构造函数,我难道不能用赋值号吗?
答:如果没写拷贝构造函数,系统免费送一个默认的。这个默认的构造函数就是简单的把各个属性都对应复制一遍。

这种默认的拷贝构造函数,大部分情况下都没有问题,除非:成员变量里有指针!!!
举个例子:

复制代码
1
2
3
4
5
6
7
8
9
10
class Node { public: int* number public: Node(const Node& t) //默认的拷贝构造函数就这么干的 { this->number=t.number; }

看看它干了啥!!!他把 t 的number里存的地址赋给了新对象的number,这意味着什么? 意味着新对象的numbert.number指着同一个空间,我在新对象里改了number所指空间的值,那么t.number指的空间值也就变了,这一般不是我们想要的,我们一般想要两个对象互不干扰,所以系统默认给的那种只是简单拷贝了指针地址的这种行为,我们叫浅拷贝,解决方法叫深拷贝,如下:

复制代码
1
2
3
4
5
6
7
8
...... Node(const Node& t) { number=new int(); *number=*(t.number); } ......

也就是手动开辟一个新的空间给number,然后把t的number值拷贝进去,这样他们才是互相独立了。

所以当我们的类里存在指针成员变量的时候,又有要用到拷贝构造函数的时候,就要自己动手实现深拷贝。

四、析构函数
析构函数是对象被销毁时,系统最后调用的一个函数,一般用于扫尾工作。析构函数和构造函数一样,没有个话,系统免费送一个,自己定义的话就如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Node { public: int index; Node* left; Node* right; public: Node(int i) //构造函数,带一个参数 { index=i; left=0; right=0; } ~Node() //析构函数 { if (left) delete left; if (right) delete right; }

析构函数没有返回值,且不带参数。一般成员变量里有指针时要写,因为这些指针指的空间一般是用new运算符分配的,不会随着对象的销毁而释放,因为系统只会自己释放那个指针用来存地址的空间,它指的那个空间很可能就孤立了,别人也再也指不过去了,所以要手动释放。

以上就是今天的全部内容啦,有不懂的继续问我吧。

最后

以上就是敏感蜜粉最近收集整理的关于C++类的理解(二):函数重载和多个构造函数,以及析构函数的全部内容,更多相关C++类内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(91)

评论列表共有 0 条评论

立即
投稿
返回
顶部