• const关键字用法总结


    • const 修饰普通变量
    • const修饰指针变量
    • const修饰普通函数的值传递参数、引用&指针传递参数、返回值
    • const修饰类成员函数

    一、const的作用

    1.定义const常量

    const int Max=100;

    2.便于检查类型

    const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

    void f(const int i)
    {
            .........
    }
    //对传入的参数进行类型检查,不匹配进行提示
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.保护被从const修饰的变量或对象

    防止意外的修改,增强程序的健壮性。

    void f(const int i)
    {
            i=10;//error!
    }
    //如果在函数体内修改了i,编译器就会报错
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.可以很方便地进行参数的调整和修改

    同宏定义一样,可以做到不变则已,一变都变。

    5.为函数重载提供了一个参考

    class A
    {
    public:
      void f(int i){......} //一个函数
      void f(int i) const {......} //上一个函数的重载
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6.可以节省空间,避免不必要的内存分配

    const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝

    #define Pi 3.14159              //常量宏
    double a=pi;          //编译期间进行宏替换,分配内存
    double b=Pi;          //再进行宏替换,又一次分配内存!
    ......
    const doulbe  Pi=3.14159;  //此时并未将Pi放入ROM中
    double a=Pi;           //此时为Pi分配内存,以后不再分配!
    double b=Pi;          //没有内存分配
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7.提高了效率

    编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

    二、const修饰普通类型的变量

    const修饰普通类型的变量被赋值后,其值不允许被修改。

    const int  a = 7; 
    a = 8;       // 错误,a 被编译器认为是一个常量,其值不允许修改。
    
    • 1
    • 2

    三、const修饰指针变量

    1.如果const位于 * 的左侧,const 修饰指针指向的内容,则内容为不可变量,简称左定值;

    int a = 7;
    int b = 8;
    const int* c = &a;
    *c = b; //错误:指针指向的内容不可修改
    c = &b;//正确:指针可以修改
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.如果const位于*的右侧,const 修饰指针,则指针为不可变量,简称右定向;

    int a = 7;
    int b = 8;
    int* const c = &a;
    *c = b; //正确:指针指向的内容可修改
    c = &b;//错误:指针为不可变量
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。

    int a = 7;
    int b = 8;
    const int* const  c = &a;
    *c = b;//错误:指针指向的内容不可修改   
    c = &b;//错误:指针为不可变量
    
    • 1
    • 2
    • 3
    • 4
    • 5

    “左定值,右定向,const修饰不变量”

    四、const参数传递和函数返回值。

    1、const传递参数

    • 值传递的 const 修饰传递,传递过来的参数在函数内不可以改变。
    void func(const int p)
    {
            cout << "p = " << p << endl;
            ++p;//错误:p值不能修改
    }
    int main()
    {
            int a = 10;
            int b = 20;
            func(a);
            func(b);
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 当 const 参数为指针时,可以防止指针被意外篡改。
    #include
     
    using namespace std;
    int b = 30;
    void func(int* const  p)
    {
        cout << "*p = " << *p << endl;
        ++*p;//正确,p指向的值可以修改
        p = &b;//错误:p不能被修改
    }
    int main()
    {
        int a = 10;
        func(&a);
        cout << "a = " << a << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们采取 const 外加引用传递的方法。并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。同时,传递的对象不能被修改。
    /*定义一个学生类,获取学号*/
    class Student
    {
    public:
        Student(){}//默认构造函数
        Student(int num):number(num){}//初始化列表
        int get_number() const //const修饰成员函数,不可修改被调用对象的值
        {
           return number;
        }
        int set_number(int num);
    private:
        int number;
    };
     //普通函数
    void get_student_number(const Student& stu)//避免了临时对象构造
    {
        cout<<stu.get_number()<<endl;
        stu.set_number(3333);//错误!对象stu不能被修改
    }
     
    int Student::set_number(int num)
    {
        this->number = num;
        return this->number;
    }
     
    int main(void)
    {
        Student RF(1001);
        get_student_number(RF);
        int rf_num = RF.set_number(2222);
        cout << "rf_num = " << rf_num << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2、const 修饰函数的返回值【用的少,有机会用到再补充】
    const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
    const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。

    五、const修饰类成员函数。

    • 面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员。
    • 若要修改类对象,应调用公有成员函数来完成。为了保证const对象的常量性,编译器须区分不安全与安全的成员函数(即区分试图修改类对象与不修改类对象的函数)。例如:
    const Screen blankScreen;
    blankScreen.display();   // 对象的读操作
    blankScreen.set(*);    // 错误:const类对象不允许修改
    
    • 1
    • 2
    • 3
    • 在C++中,只有被声明为const的成员函数才能被一个const类对象调用。
    • 要声明一个const类型的类成员函数,只需要在成员函数参数列表后加上关键字const,例如
    class Screen {
    public:
       char get() const;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 在类体之外定义const成员函数时,还必须加上const关键字,例如
    char Screen::get() const {
       return _screen[_cursor];
    }
    
    • 1
    • 2
    • 3
    • 若将成员成员函数声明为const,则该函数不允许修改类的数据成员。例如
    class Screen {
    public:
        int ok() const {return _cursor; }//读可以
        int error(intival) const { _cursor = ival; }//修改不行!
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 值得注意的是,把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果数据成员是指针,则const成员函数则可以修改指针指向的对象,编译器不会把这种修改检测为错误。例如
    class Name {
    public:
        void setName(const string &s) const;
    private:
        char *m_sName;
    };
     
    void setName(const string &s) const {
        m_sName = s.c_str();      // 错误!不能修改m_sName;
     
        for (int i = 0; i < s.size(); ++i) 
            m_sName[i] = s[i];    // 不好的风格,但不是错误的
        //虽然 m_Name 不能被修改,但 m_sName 是 char * 类型,const 成员函数可以修改其所指向的字符。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • const成员函数可以被具有相同参数列表的非const成员函数重载,例如,
    class Screen {
    public:
        char get(int x,int y);
        char get(int x,int y) const;
    };
    //在这种情况下,类对象的常量性决定调用哪个函数。
    const Screen cs;
    Screen cc2;
    char ch = cs.get(0, 0);  // 调用const成员函数
    ch = cs2.get(0, 0);     // 调用非const成员函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    小结:

    • const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
    • 非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
    • 作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。
  • 相关阅读:
    损失函数的期望值与泛化误差
    防火墙安全区域
    SpringBoot-04-主启动程序分析
    Web 智能代码编辑器 WeBuilder 2022
    深度学习中的黑科技:自监督学习(Self-Supervised Learning)
    设计模式:观察者模式(C++实现)
    golang工程——gRpc 拦截器及原理
    Eclipse插件安装版本不兼容问题解决方案——Papyrus插件为例
    TCP IP网络编程(三) 地址族与数据序列
    在线客服系统源码全端通吃版+完全开源可二开 带完整搭建教程
  • 原文地址:https://blog.csdn.net/yhy_13s/article/details/127415480