const修饰的成员函数

发布于:2024-05-10 ⋅ 阅读:(20) ⋅ 点赞:(0)

欢迎来到博主的专栏——C++杂谈
博主ID:代码小豪

const修饰的成员函数并非是在返回值类型用const,而是在成员函数的末尾加上const,比如:

const char& operator[](size_t pos) const;

我们先忽略这个函数的定义是什么,只需要知道一个事情,这个operator []函数是一个成员函数,并且是一个const修饰的成员函数(只有成员函数才能末尾加上const修饰)。

如果你是一个刚从c语言学完过来学c++的人,如果看到这么一个函数声明,我想大概都是一脸懵逼的,因为这个设定有点反直觉了,在C语言当中,const总是放在函数名或变量名的前面起到修饰作用。

那么为什么c++会有这么一个设计呢?这就不得不提到c++的封装这一特性了。

const修饰了什么

const修饰成员函数到底修饰了什么?它的后面并不存在任何标识符。所以不熟悉c++的人初见这个const当然是一头雾水(博主也是)。

这就得说到c++类的封装特性了。在定义的成员函数当中,该成员函数的实际参数并非我们所声明的那样,比如上例中的operator []成员函数,看起来只声明了一个pos参数。实际上所有的成员函数都有一个隐藏的参数,c++称为this指针。

c++规定,this指针不能显示的声明在成员函数当中。所有的成员函数都会存在一个this指针。虽然this指针不能在参数列表当中声明,但我们可以在函数体内使用this指针。

message& operator =(const message& mes)
{
	this->_text = mes._text;
	return *this;
}

this指针是指向调用此函数成员的对象的指针。其类型为object* const this,this指针存在const修饰,因此在成员函数当中对this的指向进行修改是一个非法的操作。

message& operator =(const message& mes)
	{
		this->_text = mes._text;
		this = nullptr;//error,this指针不能被修改
		return *this;
	}

那么问题来了,虽然我们不能修改this指针,但是可以修改this指针指向的数据(*this)。如果我们声明了一个const类型的对象,那么在这个const对象的this指针就应该是这种类型。const object* const this

在c++中存在权限这一概念,如果一个函数的参数是非const引用或指针类型,那么const的指针或引用参数就不能调用这个函数。我们将其称为c++当中禁止权限的放大

如何理解这个权限的放大呢?我们拿一个例子举例。比如某个函数的参数是非const类型的引用,而实参缺上传了一个const类型,那么这个非const的引用就能对const类型的对象进行修改,这个对象在设计就确定不能被修改,但是通过某个函数缺将该对象修改了,这被称为权限的放大。在c++中是禁止的。

c++允许权限的缩小,禁止权限的放大,一个非const引用不能指向const对象,但是const引用可以指向非const对象

	const int a = 0;
	int& ra = a;//error,权限的放大。
	int b = 10;
	const int& rb = b;//ok,权限的缩小

让我们回到this指针,我们说到非const的对象的this指针的类型是object* const this,而const对象的this指针的类型是const object* const this

而一般的成员函数中的this指针是object* const this,因此一般的成员函数是不能作用与const对象的。因此我们需要将成员函数的隐藏参数this指针的类型修改为const object* const this,但是我们不能再参数列表当中显示的将this指针声明为const object* const this。因此c++允许我们将const放在成员函数的后面,将this指针声明为const object* const this。

	char& operator[](size_t pos)//object*const this

	const char& operator[](size_t pos) const//const object*const this

  • const修饰的成员函数的this指针是const object* const this
  • 非const的成员函数this指针是object* const this

const修饰的成员函数是为什么?

前面已经讲完了const修饰的成员函数,其const修饰的不是函数的返回值,也不是参数列表中的参数。而是成员函数当中的this指针,使其函数能作用于const对象。

那么const修饰成员函数的目的是什么呢?

  • 只有const修饰的成员函数才能作用与const对象身上
  • 能让class接口更加易于理解。函数的使用者可以轻松的得知哪些函数可以改动对象的内容(非const成员函数),哪些函数不会修改对象的内容(const成员函数)。
  • 它们使操作const对象变得灵活(如果一个类定义出来的const对象不能被使用,那么这个类就少了不少的用法)

还有一个很重要的特性。如果两个成员函数是const修饰和非const修饰的,那么这两个函数可以重载。我们看看下面这个类。

class message
{
public:
	message(const char* str)
	{
		_text = str;
	}
	char& operator[](size_t pos)//object*const this
	{
		return _text[pos];
	}
	const char& operator[](size_t pos) const//const object*const this
	{
		return _text[pos];
	}

private:
	std::string _text;
};

message的operator []可以被这么使用。

	message text1("hello world");
	cout << text1[1];//调用的是非const版本的operator []
	
	const message text2("hello world");
	cout << text2[2];//调用的是const版本的operator []

重载operator[]还有一个好处。只要给予operator[]不同的返回类型,就可以让const message对象和非const message对象进行不同的处理。

	message text1("hello world");
	text1[1] = 'j';//ok,修改了一个char类型的引用
	cout << text1[1];//ok,返回的是char类型的引用字符
	
	const message text2("hello world");
	text2[2] = 'j';//error,对const char类型的引用进行修改
	cout << text2[2];//ok,返回的是const char类型的字符

注意,会造成这种差异的原因是operator []的返回类型所致。错误的原因是因为一个对const char&的变量进行赋值操作。

问答环节

问:为什么需要将一个成员函数重载成const版本和非const版本?

答:

  • 这是由于某些成员函数要考虑到const对象和非const对象调用时造成的不同影响。比如非const对象可以被修改,而const对象不可被修改。除外。c++存在这么一个特性:const修饰的成员函数可以接收const类型的对象的this指针和非const类型的对象的this指针,而非const修饰的成员函数不能接收const类型的对象的this指针。(禁止权限的放大)