zoukankan      html  css  js  c++  java
  • c++ const关键字

    通常,不用const来区别数据的类型,在声明过程中,const关键字起到的是修饰限定的作用。

    1 符号常量(通常,在赋初值后,其值不能改变;而常量在定义时必须赋初值)

    int i;
    const int k = 3; //标识符k代表的内存单元中存放的int型的数据,关键字const限定,不能通过该标识符k来修改所代表的内存单元中的int值

    i和k代表的内存单元存放的都是int型数据,只是const限定的k为常量;符号常量,在声明时必须赋初值,赋初值后其值不能改变

    下面尝试通过声明一个指针变量t引用k所代表的内存单元,希望通过指针t来改变常量k的值,但结果很奇怪,t指向的内存单元就是常量k的内存单元,但值却是2个不同的值

    int main()
    {
        // const限定声明的量k为常量,通过k不能改变其所表示的值
        const int k = 3;
    
        int *t;
        t = (int *) &k;
        *t = 7;
    
        cout<<&k<<endl; //0018FF14
        cout<<t<<endl;    //0018FF14
    
        cout<<k<<endl;  //3
        cout<<*t<<endl;    //7
    
        return 0;
    };

    2. 数组可以被声明为常量(指的是数组中的每个元素都被当作都被当作常量对待)

    const char cc[] = "hello"; //在声明时必须赋初值,初始化后,数组元素的值不能改变

    3 常成员函数、常数据成员

    使用const修饰的成员函数 -- 常成员函数

    使用const限定的成员函数,为常成员函数,在常成员函数中,不能修改目的对象的数据成员值(通常说的,不能改变对象的状态,mutable关键字修饰的数据成员,不算对象的状态)

    在常成员函数中,不能调用非const成员函数(普通成员函数),因为普通成员函数存在的目的就是修改对象的状态

    使用const修饰的数据成员 -- 常数据成员(对象实例中的常量部分 -- 每个对象中都拥有各自的副本,与static限定的静态数据成员相区分,静态数据成员 -- 在整个类中只有一个数据副本,为该类的所有对象共同使用、维护)

    在数据成员的类型前加const关键字来表征,const限制了对该数据成员的修改,其意思是:该数据成员在构造函数初始化列表中赋初值后,在任何地方都不能对其值进行修改,以后只需做读取该常数据成员的的值即可 --- 此处正契合常量在类中的使用习惯

    类的数据成员也可以定义为常量

    #include "stdafx.h"
    #include <string>
    #include <iostream>
    using namespace std;
    
    class ConstTest
    {
    private:
        int a;
        const int b; //常数据成员
        mutable int c;
    
    public:
        ConstTest(int a, int b, int c) : b(b) //常数据成员的初始化,只能在构造函数的初始化列表中进行
        {
            this->a = a;
            //this->b = b; //错误,error C2166: l-value specifies const object, 等号左边的值是常对象,而常对象在赋初值后其值不能改变
            this->c = c;
        }
    
        void incre()
        {
            a++;
        }
    
        //在常成员函数中,不能修改目的对象的数据成员
        void fun() const
        {
            c++;
            cout<<"a="<<a<<", b="<<b<<", c="<<c<<endl;
        }
    };
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        ConstTest c_test(3, 5, 9);
        c_test.fun();    //a=3, b=5, c=10
        c_test.incre();
        c_test.fun();    //a=4, b=5, c=11
    
        return 0;
    }

    4 常引用

    在引用声明时,使用const修饰,则被声明的引用就是常引用;通过声明的常引用不能对所引用的对象进行修改。

        int a = 3;
        // const限定声明的引用k为常引用,通过引用k不能改变其所引用的对象的值
        const int &k = a;
    
        //k = 9;  //error C2166: l-value specifies const object
    
    
        const int b = 5;
        const int &t = b;
        //t = 9;  //error C2166: l-value specifies const object

    常引用,可以绑定到普通对象,也可以绑定到常对象,但常引用把绑定到的对象都当作常对象来使用。这意味着,对于引用基本数据类型的常引用,不能对其赋值,也不能修改其值;对于引用类类型的常引用,不能修改所引用对象的数据成员的值(也包括不能调用它的普通成员函数)

    即,通过常引用,只能调用被绑定对象的常成员函数,不能调用它的普通成员函数,当然更不能修改被绑定对象的数据成员  -- 一个目的就是,常引用不能改变其引用对象的状态,因为常引用已经把其所引用的对象当作常对象来看待了。

    常对象,是这样的对象:它的数据成员值在对象的整个生存期间内不能被改变,通过常对象不能调用普通成员函数,常对象对外可以的接口就是 --- 常成员函数

    普通引用,只能引用(绑定到)普通对象,要引用常对象需强制类型转换

    如:

        const Point p_const_obj1(7,7);
        //Point &p_infer2 = p_const_obj1; //'initializing' : cannot convert from 'const class Point' to 'class Point &'
        Point &p_infer2 = (Point &) p_const_obj1;

    常对象,其数据成员的值在初始化后不能被改变(应该是通过声明时的常对象名无法改变其数据成员的值,因为const对该对象名对对象的访问操作做了限制(而这种限制只是间接性的限制,通过这个对象名是没法达到改变对象数据成员值这个目的),而不是直接限制该对象名所对应的那块内存单元中的内容不能改变)

    #include <iostream>
    #include <cmath>
    #include <typeinfo>
    #include <string>
    
    using namespace std;
    
    template<typename T>
    string getTypeInfo(const T t)
    {
        const type_info &info1 = typeid(t);    
        return info1.name();
    }
    
    class Point
    {
    public:
        Point(int x=0, int y=0); //构造函数
        Point(const Point &p); //复制构造函数
    
        int getX() { return x;}
        int getY() { return y;}
    
        void move();
    
        friend float dist(const Point &p1, const Point &p2);
    
    public:
        int x;
        int y;
    };
    
    //构造函数实现
    Point::Point(int x, int y) : x(x), y(y) 
    {
        cout<<"构造函数Point::Point(int x, int y)被调用: ";
        cout<<"("<<x<<", "<<y<<")"<<endl;
    }
    
    //复制构造函数实现
    Point::Point(const Point &p)
    {
        x = p.x;
        y = p.y;
        cout<<"复制构造函数Point::Point(const Point &p)被调用: ";
        cout<<"("<<x<<", "<<y<<")"<<endl;
    }
    
    //移动点,此处仅给横纵坐标加1
    void Point::move()
    {    
        cout<<"this pointer's typeinfo: "<<getTypeInfo(this)<<endl; //this指针的类型为: class Point *
        x++;
        y++;
    }
    
    //求2点之间距离函数实现
    //dist函数是类Point的友元,在其函数体中,可以通过"对象名.属性"的方式访问类的私有和保护成员
    float dist(const Point &p1, const Point &p2)
    {
        int xx = p1.x - p2.x;
        int yy = p1.y - p2.y;
    
        return static_cast<float> (sqrt(xx*xx + yy*yy));
    }
    
    
    int main()
    {
    
        /* 调用复制构造函数的特定是,从一个Point对象拷贝构造出另一个Point对象(用源对象的数据成员初始化目的对象的数据成员),都是Point类型,不存在类型的转换
    
        而,通常的调用函数是存在类型上的转换的,其目的是使用调用构造函数传入的参数为新构建的对象的数据成员初始化 */
    
        Point p_obj1(1, 1), p_obj2(4, 5); //调用构造函数
    
        Point pb1 = p_obj1; //调用复制构造函数
        Point pb2(p_obj2);  //调用复制构造函数
    
        Point p_obj3 = 3;  //调用构造函数
    
        cout<<dist(p_obj1, p_obj2)<<endl;
    
    
        //常引用p_infer1绑定到普通对象p_obj1, 从常引用p_infer1的角度, 操作p_obj1要遵循常对象的限制规则
        //其效果等价于,const Point p_const_obj = p_obj1, 只是此处要调用复制构造函数,构造一个新的常对象,在之后的使用中,都遵守常对象的限制规则
        const Point &p_const_infer1 = p_obj1;
        /*
        * 在move()成员函数中,存在一个this指针,其类型为class Point &, 即this指针引用的类Point对象
        * 此处调用该方法的目的对象的类型为const class Point
        **/
        //p_const_infer1.move(); //cannot convert 'this' pointer from 'const class Point' to 'class Point &'
    
        
        /* pc1的类型为 class Point */
        cout<<"p_const_infer1 typeinfo: "<<getTypeInfo(p_const_infer1)<<endl; //class Point
    
        p_obj1.move();
    
    
    
        //初始化出错,普通引用不能绑定到常对象
        //转换: class Point(普通对象)     --> const Point &(常引用)     可以进行, 常引用可以绑定到普通对象
        //转换: const class Point(常对象) --> Point & (普通引用)        不可以进行, 普通引用不能绑定到常对象
        //Point &p_infer1 = p_const_infer1; //'initializing' : cannot convert from 'const class Point' to 'class Point &'
    
        Point &p_infer1 = (Point &) p_const_infer1;
        p_infer1.move(); //p_obj1对象的值被改变了, (3, 3)
        
    
        const Point &p_const_infer2 = p_const_infer1; //可以,这样p_const_infer2和p_const_infer1都引用p_obj1对象,并把其当作常对象使用
    
    
        //测试常对象
        const Point p_const_obj1(7,7);
        //Point &p_infer2 = p_const_obj1; //'initializing' : cannot convert from 'const class Point' to 'class Point &'
        Point &p_infer2 = (Point &) p_const_obj1;
        p_infer2.move(); //p_const_obj1对象的值被改变了, (8, 8)
    
    
    
        return 0;
    };
  • 相关阅读:
    微信公众号开发授权和分享模块脚本封装
    给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案
    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组
    项目启动报错:关于modals以及node版本相关
    假设业务需要,在页面一屏中一次性展示大量图片(100张),导致img组件同时发起大量的请求,导致浏览器性能被消耗殆尽,页面卡死。怎么优化?
    假设页面左侧有一个列表,点击列表某一项时,将根据当前id发起一个请求,并将响应结果展示在右侧。如果快速多次点击不同列表项,当网络不稳定时,请求返回的顺序与我点击顺序不符,导致展示的结果不是我最后一次点击的对应结果,怎么办?
    有一个按钮,点击后就发起一次请求,我现在要限制每2S只能发起一次请求,怎么办?
    微信小程序引入外部字体(字体图标过大,引入外链)
    Android项目打包遇com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
    解决reverse改变原数组
  • 原文地址:https://www.cnblogs.com/asnjudy/p/4508267.html
Copyright © 2011-2022 走看看