代码改变世界

白话C++系列(15) -- const重出江湖

2016-04-28 21:47  Keiven_LY  阅读(759)  评论(0编辑  收藏  举报

const再现江湖

之前我们已经学习过const了,但是还是不够深入,这节课我们继续来学习const。下面先来看一个例子。

这里我们定义了一个坐标Coordinate的类,在这个坐标类当中我们定义了两个数据成员,分别表示横坐标和纵坐标(注意:这两个数据成员我们都用了const关键字来修饰),另外我们还定义了一个构造函数,这个构造函数中有两个参数,我们希望将这两个参数传进来后类似初始化两个数据成员。这里如果要想正确的初始化,我们肯定不能像下面这样进行初始化

因为这里的m_iX和m_iY都是用const修饰的,也就是说它们是两个常成员,所以上面的初始化方式肯定是错误的。我们必须要通过初始化列表来初始化这两个常成员,如下:

从这个例子当中,我们也可以看到,作为一个类的数据成员来说,是可以用const来修饰的,只不过我们此前给大家所讲的一系列例子呢,所修饰的数据成员都是一些基本类型的数据成员。那么,如果要是对象作为数据成员,能不能用const去修饰呢?显然,这也是可以的。我们把这种数据成员就称为常对象成员。为了方便大家爱理解,我们还是以线段这个例子为例。

如果有一条线段,当线段的位置一旦确定下来,就不能再更改了。比如上面的这条线段,它的起点是(2, 1)和终点(6, 4)。一旦起点(2, 1)被确定下来后,我们就不能赋其他值了。如果想要达到这个目的,我们必须要将代码写成如下形式:

这是一个线段的类,其中有两个对象成员(一个是A点一个是B点),因为我们要实现一旦这两个点被确定后就不能被修改,要实现这样一个功能呢,我们就给这两个点定义成为const类型,也就是常对象。这两个点定义完成后,我们如果想要通过构造函数去初始化它,怎么办呢?我们就必须要写成如下形式(采用初始化列表的形式)

而在调用的时候,我们就可以像下面这样去实例化一个线段的对象,然后将线段的参数写全,这些参数就会依次的传递进来,传递进来之后,就可以初始化A点和B点

既然const可以修饰数据成员,那么大家把想法可以放的更大胆一些,用const来修饰一个成员函数怎么样呢?这也行!!!???当然行!!!我们把这样的成员函数称为常成员函数。我们来看下面这个例子。

还是Coordinate这个类,这个类中,除了有一个Coordinate构造函数外,还定义了两个成员函数(一个普通的changeX函数和一个常成员函数changeX)。然后,我们来定义changeX这个函数,如下

思考:常成员函数中为什么不能改变数据成员的值呢?结合我们前面已经学习过的this指针的相关知识,我们一起来分析一下。当我们定义changeX这个成员函数的时候,看上去这个成员函数貌似没有任何的参数,而实际上却隐藏着一个参数,这个参数就是我们前面已经学习过的this指针。比如,我们给m_iX赋值20,实际上在编译的时候,就是给this的m_iX赋值20(如下图所示)

当我们的成员函数不是普通的成员函数,而是一个用const修饰过的常成元函数时,又是怎样的情况呢?当我们把它定义成常成员函数的时候,编译器就会编译成如下形式:

从编译结果看,它的参数中仍然有一个隐藏的this指针,但是这个this指针是用const来修饰的,显然,此时的this指针已经变成了一个常指针,通过常指针去改变指针所指向的数据肯定是不被允许的。所以,我们如果在常成员函数当中,去修改数据成员的值,这样的做法就异地过是错误的。

此外,我们还发现,在此前我们定义的Coordinate类当中,有两个同名的函数,都叫做changeX(),只不过一个是常成员函数(用const修饰的),一个是普通的成员函数,这两个函数名字相同,参数也相同(就是都没有参数),那么这两个函数可以被称为重载函数吗?结论:它们互为重载函数。怎么样,是不是很神奇。虽然从语法的角度来说,这个的确可以有。但是如果真这样去定义的话,接下来在使用的时候肯定会感到疑惑,比如下面这样,谁能告诉我,你调用的是哪个changeX函数呢?

给出答案:这里我们所调用的是那个不带const的普通成员函数。那么,要想调用那个带有const的常常成员函数应当怎么来写呢?此时必须写成如下形式:

也就是说,在实例化对象时,必须用const来修饰这个对象,因而,我们也把这样实例化的对象称为常对象。通过常对象调用的成员函数就是常成员函数。