const的巧用

一、const 修饰一级指针
1、int   a=10;
     const int *p=&a;

2、int a=10;
     int *const p=&a;
                          
                     
3、const int a=10;
      int *p=&a;      //不行,因为可以通过*p改变a

在c中:int *p=(int*)&a       //可以将a取地址强转为int *,c++也可以,不过c++封装四种方式。

二、const修饰二级指针
1、int a=10;
     int *p=&a;
     int **q=&p
  
2、int a=10;
     int *p=&a;
     const int **q=&p  //错,可以通过*p修改a
思考: **q=*p=a 为什么不能通过*qq和*p修改a里的值,a却能修改。
解答:因为我们这个const修饰**q限制的是说不能通过间接访问方式修改a,但是对于a来说,a是直接访问,如果要限制a,应该是用const直接修饰a,即const int a=10;表示a不能被修改。

3、权限缩小:int *->const int *
     权限增大:const int * ->int *

4、int a=10;
     int b=20;
     int *p=&a;
     int *const *q=&p;    //const修饰的是*q,则,即p,表示我们不能通过*q修改p的值,但是我们可以通过p修改自身的值
     p=&b  //正确
     *q=&b //错误;
这个其实和1是一样的道理。但是这个时候如果再有一个三级指针,那就有危险。
int ***pl=&q;
那这个当然也是不行的,因为可以通过解两次引用**pl=*q=p从而修改了p的指向
5、int a=10;
     const int *p=&a;
    int **q=&p;     //会报错,因为可以通过**q修改a的值
三、const与引用
1、int a=10;
     const int &b=a; //正确,这一句 等同于 const int *b1=&a; 因为引用的底层实现就是指针,但是用的时候会给它自动解引用。

2、int &b=10; //error 因为这个等同于int *b=&10,对立即数取地址肯定是错的。

3、const int &b2=10;   //ok
这一句相当于编译器做了两件事:
1)int temp=10                            //创建了一个临时变量
2) const int *b2=&temp;             //将b2指向了临时变量的地址
切记: 注意这个是可以的,但是int *b=&10或者int& b=1=都是不可以的。

4、int a=(int&)b;   //     看汇编代码发现这一句其实就是 让a=b,忽视了&
     cout<<typeid(b3).name()<<endl;     //输出的是int

5、int a=10;
     const int &b3=a;
     b3=10    //错误,因为上一句const 修饰的是b,也就是不能通过b修改a的值
     int *p=&b3        //error还可以通过*p修改b3,可以用改成const int *p=&b3;

6、int a=10;
     int &const b=a ;  //这一句中const没起作用只有对&a整体使用才能发挥const的作用。
     b=12;         //ok
     int *p=&a   //ok
     所以原来的 int &const b=a 相当于 int &b=a

7、int *p=&a;
思考怎样写出一级指针p的引用?
解答:可以先用指针转化,因为引用底层就是指针!
     int **q=&p;
-》int *&q=p;

大家可以记住一个转化的小技巧:int &a=b;《 ——》int *a=&b;
四、const、引用、指针与形参变量结合(重载)

1、void func(int a=10){}
     void func(int a){}
这样不行,重定义了,不叫重载,c++生成符号的时候,并没有把默认值部分加入到函数区别中,所以它们两个生成的函数符号一样。我们可以回顾一下,区别两个函数的因素有哪些:
      1)函数名称
      2)形参列表
      3)形参个数
      4)形参顺序
 对实参的值是否有影响也是构成重载的一个条件,const /volatile修饰的值可以构成重载。
2、void func(const int p);
     void func(int p);
不可以,因为传值不可以改变实参,这只是值传递,值传递只是在调用函数的栈帧压入了实参,都是在func中改变副本,退出func时都会被销毁,所以对实参没有影响,编译器觉得区别它们没意义,所以也不区别它们。
 
     void func(int *p);

4、void func(int *cosnt p);
     void func(int *p);
     //不可以,这个只是对指针指向的修饰,表示p的指向不能改,可以这样的话,它们对实参的权限是一样的,都可以通过*p操作a,编译器无法区别它们对实参的操作上有什么不同,所以这两个函数也不能重载,也就是说我们c++在给每个符号做符号修饰的时候,并不区别int *const p和int *p。

注意:如果形参个数一样,我们区别形参类型的时候,注意我们看的是它们对实参是否有影响,如果两个都没有影响,比如形参是int a或者const int a;或者说它们对实参的影响是一样的权限,比如:int *p或者  int *const p,它们都可以修改*p 编译器在函数名和参数个数一样的时候,就看形参对实参的影响能否区别开来。如果是 int *
和 const int *p 那就可以,因为它们对形参一个是只读,一个是可读可修改,所以可以区别开来。


五、const、引用、指针与函数返回值的结合
1、int sum()
{
     int tmp=10;
     return tmp;
}


int main()
{
   1)  int& a=sum               // 错  它的底层实现其实可以等同于int *a=&sum(); sum的值放在了寄存器中,对
                                      //  sum的取址就是对寄存器的取址,寄存器是不能取地址的,它没有地址。对寄存器
                                     // 取地址就是错的。
 
  2)const int &b=sum(); //  正确,他就等同于const int &b=10; ——》 int temp=10; cosnt int *b=&temp;
   
  3)cosnt int *p=&sum();//错,对sum取地址,就是对寄存器取地址,对寄存器取地址,就是错的。

    强调:cosnt int *p=&10// 不可以,不能对立即数取地址   const int a=10;  //可以,相当于指向了临时变量

  4) const int &p=&sum();  // 不可以,&sum()=&eax  只要是对寄存器取地址就不行。
}

2、int * sum()
{
     static int  tmp=10;
     return& tmp;
}

int main()
{
      int *&p=sum(); // 这是错误的,因为我们可以把它看成指针的形式,也就是int **p=∑sum不可取地址所以不行,是错的。


      问:如果左边是不可修改的左值,右边不赋值左值,赋值一个立即数可以吗?
     答:那就要看情况了,如果是这样const int& p=10,当然可以了,这个我们分析过了,这个实现其实分为了两步。如果是  const int *& p=10,这个当然是不行的&不参与类型比较,但是它和变量连接着,表示整体,那&p表示左边修饰的是cosnt int *  右边只是const int ,明显类型不匹配呀。 那如果是
            int a=10;
            int *q=&a;
            const int *&p=q//错误 因为根据转换,const int **p=&q, 那我们还是可以通过*q 改变啊。
           修改:只要在将int *q 改为 const int *q =&a; 就可以了。


            int *cosnt &p=sum(); // 可以。
原因分析:因为虽然我们sum的返回值是寄存器带回来的,但是我么回想一下int cosnt &a=10,是不是可以的,因为,它其实分成了两步,一个是int temp=10; int const *a =&temp;
         那我么这个题呢,int *const &p=sum()是不是也可以想成是
                                           int *const temp=sum();
                                           int *const *p=&temp;
         那为什么我么temp是int*const 类型呢?
         因为我们如果是int *temp类型,我么不用const修饰,就直接可以用了,我们通过测试发现只有这样编译器才能通过,我们可以尝试站在设计者的角度考虑一下这个问题,temp是底层封装的一个过程,我们用户根本就看不到,所以也不需要修改这个temp的指向,我们根本不知道它在哪,它是谁,既然都用不到,那就设置为const更安全,那为什么我们下面的const int *&p=sum();不行呢?
    ??   const int *&p=sum();  //不可以
         原因:我们可以按照上一题的思路站在设计者的角度考虑一下,如果我们也为它创建一个临时变量,那这个临时变量激素int *temp=sum(); 然后const int **q=&temp可以吗,当然是错的,因为我们现在const 修饰的是**q,但是*temp却有修改的风险,虽然我们触碰不到,但是作为严谨的设计者,就要把一切可能的危险扼杀在摇篮里,那我们用const int *temp=sum()可以吗?当然也不合理,我本来在函数中返回的是可以修改的变量,用户也是想达到这样的效果,但是经过你这的时候,你不管用户想法,直接把它所指向的值设置为只读,权限变小了,所以也不能写成const int *temp=sum(); 那就没办法了。
        那const int *const &p=sum();
        这当然也可以,我们分析过了,那个temp指针是int *const 类型,只要你接收的它的权限一样,或者比它还小,都没有问题,它只是不能修改指针指向,这个现在是既不能修改p指针指向,也不能修改**p的值了(p自带了一次解引用);


         int &a  =*sum; //可以,因为指针解引用就是tmp变量 ,转化就是int& a=tmp;
}


3、int &sum(){}
int main()
{
     int &p=sum();//可以  因为返回&,表示有sum()作为右值的地方底层实现自动解引用一次,就相当于int& p=*sum()=tmp;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值