void func(int i) {……..} void func(const int i /* 顶层const */) {……..} |
之所以能根据不同的实参来区分实际该调用哪个函数。(即函数重载)。最主要的根据在传递实参给函数形参的时候,能够有不同的选择,从而区分出不同的调用。
函数swap(int left, int right),当调用时用实参a、b调用时,swap(a, b)传参相当于这样left=a, right=b。
返回到我们的func函数,当用实参调用时func(a),对于第一个函数传参形式为int i=a,对于第二个函数数,形式传递形式为const int i=a。那么好了,毕竟我们实际只会调用其中的一个函数,是该到了选择的时候了。
int i=a; const int i=a; |
我们可以发现不管实参a的定义,是int a还是const int a,上面两个赋值(拷贝)操作合法的,而且并没有什么理由说明进行哪一种拷贝更合适。编译器将不知道该怎么进行选择。将报错,redefinition of ‘void func(int)’,所以在它看来这两个函数是一个面孔。
void func(int &i) {……..} void func(const int &i /* 底层const */) {……..} |
我们将形参改成底层const。
int &i=a; const int &i=a; |
如果const int a,那么int &i=a,是非法的。a是常量,如果int &i=a成功的话,那么将有能力通过i改变常量a。所以只能选这调用第二个函数。
如果int a,上面两种赋值都是可行的,但是int &i=a,token i与a访问a变量内存权限相同,可读可写。而constint &i=a,token i对a变量的那块内存只用于读权限。所以可以认为int &i=a,匹配更精准,所以调用第一个函数。
另外,类的成员函数,可以在函数末尾加上const,来修饰this指针,这是个底层const。
class which { void func() {……..} void func() const {……..} }; |
which A;
A.func();
将调用第一个成员函数。
cosnt which A;
A.func();
将调用第二个成员函数。
top-level const与low-level const
(顶层const与底层const)
指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const表示指针本身是一个常量,而用名词底层const表示指针所指的对象是一个常量。
int i=0;
int *const p1=&i; //不能改变p1的值,这是一个顶层const
const int ci=42; //不能改变ci的值,这是一个顶层const
const int *p2=&ci; //不能改变p2的值,这是一个底层const
const int *const p3=p2;//靠右顶层const,靠左底层const
const int &r=ci; //用于声明引用的const都是底层const
--à>>当执行拷贝操作时,两者有明显区别:
如赋值操作,它是先再申请一个对象的内存,然后将别的对象拷贝过来,即值拷贝。由于顶层const修饰的是对象本身,const对单纯的值拷贝而言是没有影响的。
int i=0;
const int ci=42;
i= ci; //正确,拷贝ci的值,ci是顶层const
int *p2;
int *const p3;
p2 = p3; //正确,指针p3指针本身是const,
//p2、p3指向的对象类型完全相同(底层const),而顶层const不起影响。
而底层const是起影响的,不能被忽视。当执行对象的拷贝操作时,必须具有相同的底层const或者能够转换,一般来说,非常量可以转换成常量,反之则不行:
int *p2;
const int * p3=temp;//同时具有顶层与底层const
p2 = p3; //错误, p3指向只读int,而p2指向可读可写int。此处如何成功只读int将被p2修改。
p3=p2; //正确,int*能转换成const int*
综上,如果指针指向的对象具有相同的属性,那么指针本身是否是const,对于拷贝该指针不产生影响。如果指针指向的对象一个是const属性的,另一个无const属性,那么只有单向拷贝该指针是合法的(由int*到const int*)。