* const p32 = &k2; //指向常量的指针常量
}
1.4顶层与底层const
??任意常量对象为顶层const,包括常量指针;指向常量的指针和声明const的引用都为底层const
??顶层const(top-level const)表示指针本身是个常量int* const ptr=&m;
??此时指针不可以发生改变,但是指针所指向的对象值是可以改变的
??底层const(low-level const)表示指针所指的对象是常量const int* ptr=&m;
??此时指针可以发生改变,但是指针所指向的对象值是不可以改变的
??顶层const可以表示任意的对象是常量(指针、引用、int、double都可以)
??于是只有指针和引用等复合类型可以是底层const
??执行对象的拷贝构造时,常量是顶层const还是底层const差别明显
??顶层const并不会有任何影响
进行拷贝操作的时候,仅仅只是从右值(顶层const)拷贝一个值并给自己赋值,虽然右值是一个不可变的量,但是貌似对我自己的拷贝完全没有影响吧
const int m = 10;
int n = m;
int* const ptr2 = &n;
int* ptr3 = ptr2;
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 const int &r = ci;//用于声明引用的const都是底层const
??当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显。 其中,顶层const不受什么影响:
i = ci;//正确:拷贝ci的值,CI是 一个顶层const, 对此操作无影响
p2 = p3;//正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响
??执行拷贝操作并不会改变被拷贝对象的值,因此,拷入和拷出的对象是否是常量都没什么影响。
??另一方面,底层const的限制却不能忽视。当执行对象的拷贝操作时拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。非常量可以转换成常量,反之则不行:
int *p = p3;//错误:p3包含底层const的定义,而p没有
p2 = p3;//正确:p2和p3都是底层const ??????????
p2 = &i;//正确:int*能转换成const int*
int &r = ci;//错误:普通的int&不能绑定到int常量上
const int &r2 = 1;//正确:const int&可以绑定到一个普通int上
??p3既是顶层const也是底层const,拷贝p3时可以不在乎它是一个顶层const,但是必须清楚它指向的对象得是一个常量。因此,不能用p3去初始化p, 因为p指向的是一 个普通的(非常量)整数。 另一方面,p3的值可以赋给p2,是因为这两个指针都是底层const,尽管p3同时也是一个常量指针(顶层const), 仅就这次赋值而言不会有什么影响。
原文链接:https://blog.csdn.net/m0_64860543/article/details/128269607
2.const 函数形参
??我们已经了解了变量中const修饰符的作用,调用函数就会涉及变量参数的问题,那么在形参列表中const形参与非const形参有什么区别呢?
2.1 const 修饰普通形参
同样,先来看看普通变量:
void fun(const int i)
{
i = 0;
cout << i << endl;
}
void fun(int i)
{
i = 0;
cout << i << endl;
}
int main()
{
const int i = 1;
fun(i);
return 0;
}
??形参的顶层 const 在初始化时会被忽略,所以上面定义的两个函数实际上是一个函数。编译时会出现void fun(int) previously defined here
错误。
2.2 const 修饰指针形参
??与 const 指针变量一样,指向常量的指针形参指向的值不能修改;本身就是常量的指针形参不能指向其他变量;指向常量的常量指针形参指向的值不能被修改,也不能指向其他变量。
#include<iostream>
using namespace std;
void fun(const int* i)
{
cout << *i << endl;
}
void fun(int* i)
{
*i = 0;
cout << *i << endl;
}
int main()
{
const int i = 1;
//调用 fun(const int* i),没有 fun(const int* i),则会编译报错,因为没有匹配形参的函数。
fun(&i);
int j = 1;
//调用 fun(int* i),没有 fun(int* i),则会调用 fun(const int* i),此时 j 的值不会被改变
fun(&j);
return 0;
}
此外,形参的底层 const 在初始化时不会被忽略,所以上面的两个函数是不同的函数,即重载函数,上面例子编译并不会报错,若果再加上一个void fun(int *const i)就会报错,因为这个函数定义里面 i 是顶层 const。
2.3 const 修饰引用形参
??与const引用一样,const引用不会改变被引用变量的值。
#include<iostream>
using namespace std;
void fun(const int& i)
{
cout << i << endl;
}
void fun(int& i)
{
i = 0;
cout << i << endl;
}
int main()
{
const int i = 1;
//调用 fun(const int& i),没有 fun(const int& i),则会编译报错,因为没有匹配形参的函数。
fun(i);
int j = 1;
//调用 fun(int& i),没有 fun(int& i),则会调用 fun(const int& i),此时 j 的值不会被改变
fun(j);
return 0;
}
由于 const 引用也是底层 const ,所以上面两个函数是不同的函数,即重载函数,编译并不会报错。
3.类常量成员函数
??面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员。若要修改类对象,应调用公有成员函数来完成。为了保证const对象的常量性,编译器须区分试图修改类对象