const是衡量一个程序员是否老道的一个标准,除了修饰变量之外,还可以修饰函数,主要有以下几种形式
const int& fun(int& a); //修饰返回值
int& fun(const int& a); //修饰形参
int& fun(int& a) const{} //const成员函数
const返回值
这种多是修饰返回值是引用类型的情况下,为了避免返回值被修改的情况。
解释下:返回值是引用的函数, 可以肯定的是这个引用必然不是临时对象的引用, 因此一定是成员变量或者是函数参数, 所以在返回的时候为了避免其成为左值被修改,就需要加上const关键字来修饰。
举个例子:
#include<iostream>
using namespace std;
class A
{
private:
int data;
public:
A(int num):data(num){}
~A(){};
int& get_data()
{
return data;
}
};
int main()
{
A a(1);
a.get_data()=3;
cout<<a.get_data()<<endl; //data=3
return 0;
}
那么这个时候为了避免成员函数被修改就需要加上const关键字,这个时候如果试图改变返回值是不允许的:
error: cannot assign to return value because function 'get_data' returns a const value
需要指出的是,如果函数的返回类型是内置类型,比如 int char 等,修改返回值本身就是不合法的!所以 const 返回值是处理返回类型为用户定义类型的情况。
const 修饰实参
多数情况下,我们都会选择 pass by reference,这样可以节省内存,并且可以起到改变实参的目的。不过有的时候我们并不希望改变实参的值,就要加上const关键字。
这个不仔细说了,很容易理解。不过在构造接口之前一定要思考函数是否会修改参数,如果不会修改的话一定要加上const,这个是代码写的是否大气的一个标准(侯捷大师原话。。)。
const成员函数
这种情况多数情形下很容易被忽视,其实这个是非常重要的一个内容。
设想这样一种场景:
const String str("hello world");
str.print();
假设在string类中有一个成员函数叫做print, 如果这个函数在定义的时候没有加上const 关键字,上述的代码是无法通过编译的,下面举个具体的例子:
#include<iostream>
using namespace std;
class A
{
private:
int data;
public:
A(int num):data(num){}
~A(){};
int& get_data()
{
return data;
}
};
int main()
{
const A a(1);
a.get_data();
return 0;
}
毫不意外的出错了:
error: ‘this’ argument to member function ‘get_data’ has type ‘const A’, but function is not marked const。
我们敏锐的发现了一个“this"指针,这个从何说起?
其实任何成员函数的参数都是含有this 指针的,好比py中的 self ,只不过c++中规定全部不写这个参数, 其实这个参数就是对象本身, 即谁调用成员函数, 这个 this 就是谁!
我们的例子中 a 是一个const 对象, 它调用了 get_data 方法,所以函数签名应该是:
get_data(a){}
而 a是一个 const 对象, 我们默认的 this 指针并不是 const 类型,所以参数类型不匹配, 编译无法通过!
这下我们终于清楚了, const 修饰成员函数, 根本上是修饰了 this 指针。
下面摘取侯捷PPT中的一张图片,介绍了string源码中的一些片段:
需要指出的是, 这俩函数确实是不同的函数, 加了这个const 之后函数签名是不一样的。
有如下的规则:
补充一点,如果成员函数同时具有 const 和 non-const 两个版本的话, const 对象只能调用const成员函数, non-const 对象只能调用 non-const 成员函数。