关于const函数重载。修饰(成员)函数的const算作函数签名的一部分
const修饰函数参数一般不作为函数签名的一部分。除非修饰的参数是指针或引用。并且需要是修饰的是底层。
const修饰的函数参数是值的时候不可以进行重载。如下代码编译错误。
class myobj{ public: myobj(int x):cal(x){} int cal;};void func1(myobj a){ cout << "non const" << endl; }void func1(const myobj a){ cout <<"const" << endl;}-
const修饰的函数参数是指针或引用的时候,并且修饰的是底层的时候可以进行重载。 -
volatile也适用于此规则。 -
以上例外
const必须修饰的是指针指向的内容, 即ponter-to-const;- 如果是指针本身是
const的,则不能重载
- 如果是指针本身是
-
原因是函数签名不包括形参列表参数的顶层CV限定
-
顶层
const表示指针本身是个常量。顶层修饰的是对象本身。myobj* const b = new myobj(20);const指针b指向myobj
-
底层
const表示指针所指向的对象是个常量。const myobj* b = new myobj(20);指针b指向const myobj
-
//指针void func(myobj* a){ cout << "non const" << endl; }void func(const myobj* a){ cout <<"const" << endl;}//引用void func1(myobj& a){ cout << "non const" << endl; }void func1(const myobj& a){ cout <<"const" << endl;}
int main(){ myobj* a = new myobj(10); const myobj* b = new myobj(20); func(a); //输出non const func(b); //输出const
myobj obj(10); myobj& refa = obj; const myobj& refb = obj;
func1(refa); //输出 non const func1(refb); //输出 const
return 0;
}- const引用可以绑定到非const对象,但是非const引用不可绑定const对象。因为不能保证其不被修改。
string t = "234"; //非const对象const string& y = t; //可以 const引用
const string s = "234"; //const对象string& b = s; //不可以 非const引用- 所以函数入参为引用的时候,非const对象可以传入const引用参数。也就是const引用绑定到非const对象。比如作用是防止修改。但是const对象不可传入非const引用参数。也就是非const引用不可以绑定到const对象。
void test(const string& a){ cout << a << endl;}
void test2(string& a){ cout << a << endl;}int main(){ const string str1 = "234"; string str2 = "234"; test(str1); test2(str1); //不可以。const对象不能传入非const参数 test(str2); test2(str2);
}- 值传递不受影响
const如果放在函数后面(修饰成员函数)只能放在类成员函数后面
先看代码
class myclass{public: int vala; int valb; myclass(int x, int y):vala(x), valb(y){}
void changethis(myclass& obj)const { cout <<"const" << endl; } // void changethis(const myclass* const this, myclass& obj) const{ // cout <<"non const" << endl; // } void changethis(myclass& obj) { cout <<"non const" << endl; } // void changethis(myclass* const this, myclass& obj) { // cout <<"non const" << endl; // }};
int main(){ myclass obj(10,20); const myclass obj1(10,20); obj.changethis(obj); //调用不带const的 obj1.changethis(obj); //调用带const的 return 0;}const放在类成员函数后面的意义是,不可以修改当前调用对象的数据成员的值。切记注意,入参是否可以被修改是不归函数管的。函数加const只是负责禁止修改调用方。在这里也就是禁止修改调用方obj1的值。
const修饰成员函数修饰的是什么?是this指针
我们在上面代码段看到了两段被注释掉的函数,里面写出了如果显式this指针会是什么样。
-
注意,由于
this指针不能改变指向,所以this指针是一个常量指针。也就是myclass* const this。 -
如果我们不想修改一个参数的值,办法就是让这个参数被
const修饰。在这里就是把this指针也用const修饰。则会变成const myclass* const this。这也说明了为什么const只能修饰成员函数,因为非成员函数没有this指针。 -
又由于
this指针会被编译器根据对象来隐式传入。所以const对象的this指针必然是也是常量指针。所以自然const对象无法调用非const函数。因为this指针的不匹配。const可以被隐式(函数调用)加上但是不能被去除。也就是权限的缩紧是可以的,放松是不可以的。 -
简单理解就是因为
const成员无法进行修改。如果const成员变量调用了非const函数,函数不能保证不修改const成员,如果修改了会报错,所以直接加以限制。当然了非const成员因为可以修改也可以不修改,都不会报错。所以自然可以调用const函数也可以调用非const函数。 -
const成员函数不能调用非const成员函数。因为在const成员函数里面的this指针被const修饰,在const成员函数里面调用非const成员函数时,相当于将const修饰的this指针传给非const成员函数。- 也可以理解为权限的扩张是禁止的。不可以修改(整体)-> 可以修改(局部)是不可以的。
-
非
const成员函数可以调用const成员函数的原因是:权限的缩紧是可以的。- 也就是:可以修改(整体)->不可以修改(局部)这样是可以的
无意间发生的const与非const冲突
class myclass{public: int vala; int valb; myclass(int x, int y):vala(x), valb(y){} void func(){ //一些内容 } void changethis(const myclass& obj){ obj.func(); //错误。const对象(引用)不能调用非const函数 }};- 我们看到了。经常性的我们会使用常量左值引用做为函数入参签名,一是防止拷贝二是防止修改。但是我们可能需要通过这个对象调用其内部的一些函数。此时就不可以通过
const引用调用非const函数
注意 this指针并不是对象本身的一部分。
注意 this指针并不是对象本身的一部分。也不会影响sizeof的结果。它的作用域是在类的内部。this指针只不过会在调用非静态函数的时候被编译器隐式加入。

关于const函数重载。const算作函数签名的一部分
我们在上面代码可以看到,我们有一个函数重载。因为const算作函数签名的一部分。
但是编译器如何判断我们调用的是哪个函数呢?虽然表面参数一样,但其实隐藏的this形参是不同的。
- 所以如果我们调用方是
const对象,则会调用const版本的函数。如果是非const对象就调用非const版本的函数。
.png)
为什么返回值类型不能做为函数重载的依据?也就是为什么仅返回值不同的函数不可重载?
- 理论上来说就是函数的返回值不是普通函数签名的一部分。
- 实际上来说是有二义性。比如
int func();string func();
int main(){ func(); //调用哪个func? return 0;}如上面代码,调用func的时候编译器无法确定到底调用哪个func。
虽然返回值不是普通函数签名一部分,但是在链接期,匹配的不仅是函数的签名,还包括其类型。所以返回值依旧要匹配(个人理解)
函数签名总结
- 普通函数的函数签名包括:
- 函数名(非限定名)或者 生成该函数的函数模板名称
- 参数类型和个数(参数列表)。如果函数是从函数模板中生成的,那么指的是替换前的模板参数
- 函数名称所属的类或命名空间作用域;如果函数名称拥有内部链接,还包括该名称声明所在的编译单元。
- 如果是类成员函数则还包括函数的:
const限定符volatile限定符ref限定符
- 函数模板的签名除了上面的以外还包括:
- 返回值类型
- 模板参数和模板实参
- 格外注意,函数模板的签名包括返回值类型导致的表面说“函数模板的重载“其实是一种重载决议。被不同类型实例化的函数模板是不同的函数。
- 函数参数的const一般不算做函数签名。除非该参数为引用或指针
- 普通参数的const不算做函数签名是为了兼容C。
- C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
普通函数的函数签名不包括返回值类型。也就是无法仅通过返回值类型的不同进行函数重载
关键字 final
- final关键字的意义是阻止子类重写自身(父类)的虚函数或禁止继承。
- 一般不用在父类,因为没有意义。
- 一般用在某个子类防止子类的子类重写子类的虚函数 或者是 禁止继承。