再也不怕面试官问const关键字了

const关键字

const即constan的缩写,即不变的,被const修饰之后,相当于程序员告诉编译器:这个值是不变的,你处理的时候注意着点儿。

const可以修饰内置类型变量、自定义的类对象、类的成员函数、函数的返回值、函数的参数。

const修饰普通变量

const int a = 8;
int b = a; //Correct
a = 9; //Fault

编译器会把a认定为常量,其值不可被改变,所以对它赋值是错误的。

const修饰指针变量

const修饰指针变量有三种情况:

  1. const修饰指针指向的内容,即指针所指地址中的内容不可变。
  2. const修饰指针本身,即指针所指向的地址不可变。
  3. const同时修饰指针和指针指向的内容,即二者皆不可变。
const int *p = 8;

由内向外看,*表示p是一个指针;const int表示p指针指向一个int型常量,综合起来就是p是一个常量指针。即p指针指向的这个地址存的是个8,这个值不能变,但是p可以指向其他的地址。

int a = 8;
int* const p = &a;

由内而外看,const表示p是一个常量,这已经说明它自身的值是不变的;int*表示p是一个指向int型变量的指针;所以,综合起来,p是一个指向int型变量的指针常量。即p指向的地址是不可变的,但这段地址存放的内容是可以变的。

int a = 8;
const int* const p = &a;

依旧由内而外看,const表示p是一个常量;const int*表示p是一个指向int型常量的指针;综合起来,p是一个指向int型常量的指针常量。即p指向的地址以及它指向的地址中存放的内容均为不可变的。

总结:*左边的const表示内容不变;*右边的const表示地址不变。

常量指针:指向常量的指针,即其指向的地址中的内容不变。

指针常量:指针本身是常量,即其指向地址不变。

const修饰函数参数

const修饰函数参数也分三种情况:

  1. 对于值传递的函数,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。

    include <iostream>
    void ValueTransfer(const int a){
            ++a;
            std::cout << a << std::endl;
    }
    int main(){
            ValueTransfer(8);
            return 0;
    }
    

    上边这段程序用g++编译时报错:

    const_test.cpp: In function ‘void ValueTransfer(int)’:
    const_test.cpp:3:4: error: increment of read-only parameter ‘a’
      ++a;
    

    对只读的参数a进行自增操作是非法的,编译直接不通过。

  2. 当 const 参数为指针时,可以防止指针被意外篡改。

    #include<iostream> 
    void Test(int *const a){
        std::cout << *a << std::endl; //a为8
        *a = 9;
    } 
    int main(void){
        int a = 8;
        Test(&a);
        std::cout << a << std::endl; // a为9
        return 0;
    }
    
  3. 自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们采取 const 外加引用传递的方法。并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。

    按值传递对象需要复制所有对象成员的副本,这可能会减慢程序的执行时间,如果对象有很多成员,则更是如此。另一方面,当按引用传递对象时,由于该函数可以访问原始对象,而不必进行任何复制,所以它比通过值传递更快,正因为如此,一般更愿意按引用传递对象。

    但是,按引用传递对象有一个缺点,因为该函数可以访问原始对象,所以它可以调用其成员函数更改对象成员数据。这就是为什么当程序员想要保护对象的内容时,通常不会按引用传递变量。

    幸运的是这个问题有解决办法。为了保护对象让它作为实参传递,而又不必复制副本,可以将它作为常量引用进行传递,这意味着原始对象作为引用被传递给了函数,但是它不能调用没有const修饰的成员函数或更改对象的成员数据,它只能调用自己被指定为const函数的成员函数。

const修饰函数返回值

const修饰函数返回值也分三种情况:

  1. const 修饰内置类型的返回值,修饰与不修饰返回值没什么区别。
  2. const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
  3. const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。

const修饰类的成员函数

const 修饰类成员函数,其目的是防止成员函数修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。

const 关键字不能与 static 关键字同时使用,因为 static 关键字修饰静态成员函数,静态成员函数不含有 this 指针,即不能实例化,const 成员函数必须具体到某一实例。

#include<iostream> 
class Test{
public:
    Test(){}
    Test(int _m):_cm(_m){}
    int get_cm() const{
       return _cm;
    } 
private:
    int _cm;
};
void Cmf(const Test& _tt){
    std::cout << _tt.get_cm(); //输出8
}
int main(void){
    Test t(8);
    Cmf(t);
    return 0;
}

如果 get_cm() 去掉 const 修饰,g++编译时提示以下错误:

const_test.cpp: In function ‘void Cmf(const Test&)’:
const_test.cpp:13:29: error: passing ‘const Test’ as ‘this’ argument of ‘int Test::get_cm()’ discards qualifiers [-fpermissive]
     std::cout << _tt.get_cm();

Cmf 传递的 const _tt 即使没有改变对象的值,编译器也认为函数会改变对象的值,进而编译不通过。所以我们尽量按照要求将所有的不需要改变对象内容的函数都作为 const 成员函数。

如果有个成员函数想修改对象中的某一个成员怎么办?这时我们可以使用 mutable 关键字修饰这个成员,mutable 的意思也是易变的,容易改变的意思,被 mutable 关键字修饰的成员可以处于不断变化中。

全部评论

相关推荐

不愿透露姓名的神秘牛友
10-05 10:13
已编辑
HHHHaos:让这些老登来现在秋招一下,简历都过不去
点赞 评论 收藏
分享
已老实求offer😫:有点像徐坤(没有冒犯的意思哈)
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务