C++ const总结
C++ const在成员函数中的用法总结
首先把这学明白是看了C++ primer中对这里的讲解,学懂这部分一定要理解this指针的用法。
那么这期笔记分几点来讲:
一.根据C++ primer内容讲this指针,然后讲const在成员函数中的概念。
二.理解了概念之后,通过网上的一个博客案例([c/c++: c++函数返回类型什么情况带const - A_zhu - 博客园 (cnblogs.com)](https://www.cnblogs.com/Azhu/p/4352613.html#:~:text=const 成员函数的返回类型是引用时候,需要加const 约束 int fun (),const%3B int %26 fun () const%3B))来巩固一下。
三.巩固完之后呢,通过Effctive C++中的一个例子,详细说明一下,const成员函数以及它的返回类型什么时候返回const ,什么时候返回const&,通过排列组合来说明。
一、this指针
我们借用C++ primer的基础程序来举例说明:
1 | Sales_data total; |
我们的疑问是,在调用isbn的函数的时候,函数怎么会准确的返回total这个对象的bookId成员呢?
原因是,类里边的成员函数其实是有一个隐形的参数的,他叫做this指针(注意,this指针出现的位置是类的成员函数)this指针是一个常量指针(这个指针的值是不变的,指针值指向的变量是可以变化的)。
那么this指针这个隐形的成员函数参数是做什么的呢?
它的作用就是当一个类对象调用这个类的成员函数的时候,类对象的地址就会赋给成员函数的this指针。
1 | //伪代码,用于说明调用成员函数的实际执行过程 |
二、const成员函数
1.const类对象与const成员函数
上文提到,成员函数中的this指针是一个常量指针,但是指针指向的类对象他是可以变化的。
那么问题来了?
假设我们定义了一个const的类对象,如下:
1 | const Sales_data sd; |
现在我们想调用sd的成员函数isbn:
1 | sd.isbn(); |
这么做可以么?
答案是不可以,我们借助this指针的知识仔细分析一下,sd是const变量,无论如何是不允许改变的,
我们调用isbn函数,isbn里边的this指针指向的变量可是可变的啊,也就是说会有一个如下的过程:
1 | Sales_data* const this; |
合理么?
不合理!
因为你可以试图通过this指针改变一个const对象。这是不合法的。
那可怎么办?总不能我设计一个类,这个类永远不能定义const类对象吧?
这个时候C++有一种方法,引入const成员函数,具体做法:
1 | void isbn()const |
在函数体之前加一个const,就叫做const成员函数。
那么这么做有什么用呢?
答案是当有对象想调用这个成员函数时,这个成员函数的this指针是一个指向常变量的常量指针,即:
1 | const Sales_data sd; |
这么做就非常合理了,我调用成员函数的类对象是个const变量,我调用的成员函数里边的this指针是一个指向常变量的常量指针。非常合理!
2.普通类对象和const成员函数
上文说到,const类对象能调用的成员函数必须是const成员函数。
那么,普通类对象可以调用const成员函数么?我们来剖析一下:
1 | Sales_data sd; |
这么做是否可行,完全看成员函数的this指针是否可以接纳类对象的地址:
1 | const Sales_data* const this=&sd; |
这么做合理么?
合理!也就是说我们通过this指针不可以改变sd这个对象,但是sd对象依旧是可以通过其他的途径改变。
所以说普通类对象是可以调用const成员函数的!
3.const成员函数有哪些作用?
或者说,我们什么时候需要定义const成员函数?
拍脑袋一想,为了能够定义const对象,肯定每一个成员函数都需要定义一个对应的const成员函数啊,啊不对不对。。。const对象是不可以改变的,很多成员函数是会改变类对象的,
重新说:那当一个成员函数没有改变类对象时,我就需要把他定义为const成员函数,这个是硬性要求!因为你不这么做,你定义的const类对象一个成员函数也调用不了!
那么,只有这一个作用么?
其实不是,我们上一点说过,普通类对象也可以调用const成员函数,当我们设计一个成员函数时,如果这个成员函数不会改变类对象,我们最好也把他设计成const成员函数,反正你也不改变类对象,你设计成const编译器就会为你把关,用来提高程序的健壮性!
综上,两点作用:
1.为了const类对象能有成员函数调用(const类对象能调用的成员函数只能是不改变类对象的,所以为了照顾他,应该把类内的所有不会使类对象改变的成员函数都有一个对应的const成员函数版本,理论上是这样0.0)
2.为了提升程序的健壮性,只要这个成员函数不想让类对象改变,我们就要将他设计为const成员函数
4.容易让我们忽视的点
1 | void isbn() |
注意,这两个函数是可以重载的!
有时候我们会定义诸如此类的两个版本,后边那个版本自然是方便const对象去调用咯。
还有一个需要注意的点,以isbn()函数举例子
如果类中只有这一个成员函数:
1 | void isbn()const |
执行下列语句:
1 | Sales_data sd; |
这么做是合理的,上文已经讲过。
如果类中有两个isbn成员函数:
1 | void isbn() |
执行下列语句:
1 | Sales_data sd; |
注意哦,现在调用的成员函数是这个版本:
1 | void isbn() |
原因很简单,因为函数重载0.0调用这个版本最合理。
5.浅浅的总结
1.const成员函数存在的意义有两点,1为了const类对象,2为了程序的健壮性
2.同函数名字的成员函数和const成员函数是重载的。
三、简单的实例来说明const成员函数以及返回类型
1 | class A { |
我们来看b.setnum();他错在哪里呢?
很简单,b是一个const对象,如果想调用setnum函数:
1 | A* const this=&b; |
this指针企图改变一个const值。显然不可以!
关于const成员函数的返回类型
1.返回类型为void
没啥可说的
2.返回类型为引用类型
如果是引用类型,必须是const引用!
因为你这个是引用类型,你不加const约束
我们来看个例子就知道了
1 | class Test |
先来明确一个点吧,首先 定义了一个普通的类对象:
1 | Test t(3); |
然后调用const成员函数,注意这个时候成员函数的this指针:
1 | const int* const this=&t; |
注意这个时候是有一个类型转换的!value这个成员不在是int类型,而是转换成了 const int,究其原因就是因为this指针指向的是一个常量了!
所以当你要返回成员变量时,你肯定不能返回int&了吧,那样不合法,所以你只能返回const int&
所以,小小的总结一下,既是一个普通的类对象在调用const成员函数时,在成员函数里边,所有类对象的成员都变成了const类型,那么你在返回他们的引用的时候,必然要加const。
3.返回类型为非引用类型
这个随意了:
1 | const int a=10; |
四、Effective C++中的例子:
1 |
|
首先这个例子是想说明重载的,也就是说tb对象调用的是非const成员函数
ctb对象调用的是const成员函数。
然后我们借用这个例子说明一下成员函数返回值应该注意什么(尤其还是这种重载函数)
1.上文所说的const成员函数如果返回引用必须是const类型
原因我已经讲过,我们县在让他是普通引用看看有啥问题就行:
当然针对const成员函数你要是返回类型为char自然是没问题
2.关于运算符重载成员函数有什么讲究?
先说非const成员函数,其实这个需要具体问题具体分析,我们直接拿上述代码举例子分析就行了。
重载运算符[],你可以返回char么?
当然可以编译通过。
但是你想一下这个操作:
1 | tb[0] = 'b'; |
能合法么?
不能!因为你这个函数返回了一个值!一个右值!
右值怎么可能出现在等号左边?
所以,需要的返回类型是char&
当然针对const成员函数,返回char是可以的
五、总结
本篇文章,先是1.讲述了this指针的来源(成员函数的隐藏参数)
然后2.引出了const成员函数(其this指针指向的对象是常对象,所以成员函数内部关于常对象的成员都是const的)
紧接着,我们3.讲述了const类对象只能调用const成员函数,但是普通类对象一样可以调用const成员函数。
后续说明4.const成员函数存在的意义:为了照顾const变量;为了提高程序的健壮性。
然后我们找了几个例子说明了const成员函数。
同时介绍了5.const成员函数返回值需要注意的事项,最需要注意就是返回引用的时候,因为const成员函数使调用对象变成了const,所以返回的引用必须是const&
接着通过Effctive C++说明了这一点
又6.引申了一下重载运算符成员函数返回类型的要求。