zoukankan      html  css  js  c++  java
  • const的一些总结

    const的一些总结

    采用const符号常量写出来的代码更容易维护,有些函数只读不写:

    1 常变量: const 类型说明符 变量名

    2 常引用: const 类型说明符 &变量名

    3 常成员函数: 类名::fun(形参) const

    4 常量和指针

    用法一:常量

    const变量取代了C中宏常量的方式

    const char ch;

    char const ch;

    上面二者是等同的,如果没有初始化编译会报错。也就是说一般情况下const常量在定义时必须初始化

    不过在C++类中就不一定,例如

    你定义了

        class A
        {
             char const ch;
        };

    而没有使用类A定义任何对象和指针是不会报错的

    但你一旦定义了构造函数

        class A
        {
        public:
            A(){};
             char const ch;
        };

    就必须初始化,一般是在成员初始化列表或者构造函数里面初始化。记住const 常量要初始化。外部变量在其它文件申明不需要重新初始化

    用法二:指针和常量

    使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用const“预先固定”将使那个对象而不是使这个指针成为常量。要将指针本身而不是被指对象声明为常量,必须使用声明运算符*const。

    (1) char *const cp; //到char的const指针,指针指向的对象不能变,但是可以修改对象的内容

    int a = 9;
    int * const p = &a;
    *p = 10;   //可以
    int c = 0;
    p = &c;  //编译报错


     


    (2)char const *pc1; //到const char的指针,这种看起来有点晦涩,一般使用下面一种方法申明
    (3)const char *pc2; //到const char的指针(跟上一个声明是等同的),不能改变所指向对象的内容,但是可以重新指向其他对象

    int a = 9;
    const int *pNum = &a;
    *pNum = 10; //编译无法通过

    还有另外一种方式能够进行修改

        void func(const int *pi)

        {

        //这里相当于重新构建了一个指针,指向相同的内存区域。当然就可以通过该指针修改内存中的值了。

       int* pp = (int*)pi;

        *pp = 100;

        }

    int a = 9;
    const int *pNum = &a;
    a = 25;
    int *p = &a;
    *p = 10;

    通过其他指针操作a的地址还是可以改变a的内容的

    int a = 9;
    const int *pNum = &a;
    //*pNum = 10;
    const int b = 0;
    pNum = &b;

    这样是可以的,上面例子可以看到并不一定要指向const类型的变量哦,允许把非 const 对象的地址赋给指向 const 对象的指针,不允许把一个 const 对象的地址赋给一个普通的、非 const 对象的指针。

    (4)const char * const pc2;//指向const chat的const指针,也就是指向常量的常指针,听这个名字你就知道了,只能乖乖的用,别想做啥了

    上面内容可以有如下从右向左读的记忆方式记忆方式:
    cp is a const pointer to char.

    pc2 is a pointer to const char.

    用法三:修饰函数返回值
        可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。

    class A
    {
       
    public:
        const char* fun()
        {
            char *p = new char('a');
            return p;
        }
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
        A obj;
        const char *p = obj.fun();

        char *p2 = obj.fun();//报错

    }

    用法四:const修饰函数传入参数
        将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。
        通常修饰指针参数和引用参数:
    void Fun( const A *in); //修饰指针型传入参数
    void Fun(const A &in); //修饰引用型传入参数

    记起来一个有趣的问题

    class A
    {
       
    public:
        A(){};
        A(const A a){};
        const char* fun()
        {
            char *p = new char('a');
            return p;
        }
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
        A obj1;
        A obj2(obj1);
        cout<<"Initialize obj2 success ."<<endl;
       
        getchar();
       
        return 0;
    }

    在vc2008上编译直接通不过,error C2652: “A”: 非法的复制构造函数: 第一个参数不应是“A”

    可能与编译器有关。不过就算通过也是有问题的,因为类A的复制构造函数使用的值传递,使用复制构造时就会不停递归的调用复制构造

    所以一般都会使用引用类型传参,大型对象还可以节省空间提高运行效率

    下面还有个例子可以详解下:

    class A
    {
    private:
    int value;
    public:
    A(int n)
    {
    value = n;
    }
    A(A other)
    {
    value = other.value;
    }

    void Print()
    {
    cout<<value<<endl;
    }
    };


    int _tmain(int argc, _TCHAR* argv[])
    {
    A a = 10;
    A b = a;
    b.Print();

    getchar();
    return 0;
    }

    上述代码中,赋值构造函数A(A other)传入的参数是A的一个实例。由于是传值参数,我们把形参复制到实参会调用复制构造函数,

    就会形成永无休止的递归调用从而导致栈溢出。因此  C++标准  不允许复制构造函数传值参数,在VS和GCC中都将编译报错。

    用法五:const修饰成员函数(c++特性)
    const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
    const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;
    const成员函数不可以修改对象的数据,不管对象是否具有const性质。编译时以是否修改成员数据为依据进行检查。

    class A
    {
       
    public:
        A(){};
        void fun1()const{};
        void fun2(){};
    };
    int _tmain(int argc, _TCHAR* argv[])
    {   
        A obj;
        obj.fun1();//正确,非常量对象可以调用常量函数。
        obj.fun2() // 正确,非常量对象也允许修改调用非常量成员函数修改数据成员。

        const A constObj;
        constObj.fun1(); //正确,常量对象只能调用常量函数。因为不希望修改对象状态。
        constObj.fun2(); //错误!常量对象的状态不能被修改,而非常量函数存在修改对象状态的可能,因此会产生语法错误

    }

    实际上,我们知道每个成员函数都有一个隐含的指向对象本身的this指针。而常量函数则包含一个this的常量指针。如下:

        void fun1(const A* this) const;

        void fun2(A* this);

         也就是说对于常量函数,我们不能通过this指针去修改对象对应的内存块。但是,在上面我们已经知道,这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。看下面的代码:

    void A::fun1() const

        {

        //给类A添加一个value成员

        cout << "Value = "<< value << endl;

        // 这里,我们根据this指针重新定义了一个指向同一块内存地址的指针。

        // 通过这个新定义的指针,我们仍然可以修改对象的状态。

        A* pA = (A*)this;

        pA->value = 50;

        cout << "A::fun1() called. value = "<< value << endl;

        }

        int main()

        {

        A a;

        a.fun1();

        return 0;

        }

    上面是可行的,不过一般不会这么做.有些编译器支持mutable变量,这样可以直接修改这个变量或者类成员

    const 在c和c++中的区别

    1. C++中的const正常情况下是看成编译期的常量,编译器并不为const分配空间,只是在编译的时候将期值保存在名字表中,并在适当的时候折合在代码中。在C中就不能使用const变量作为数组定义的长度,因为在C中在C中,const是一个不能被改变的普通变量,既然是变量,就要占用存储空间

    2. 在C语言中: const int size; 这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间.但在C++中这样写是不正确的.C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字.即C++中,const默认使用内部连接.而C中使用外部连接.
    (1) 内连接:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定.
    (2) 外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过

    3. C++中,是否为const分配空间要看具体情况.如果加上关键字extern或者取const变量地址,则编译器就要为const分配存储空间.

    4. C++中定义常量的时候不再采用define,因为define只做简单的宏替换,并不提供类型检查

    生命在于折腾,生活就是如此的丰富多彩
  • 相关阅读:
    Web开发人员需知的Web缓存知识
    SQLServer常见性能问题
    C#面试常见题
    SQL Server数据库大型应用解决方案总结
    asp.net 缓存
    asp.net 的页面几种传值方式
    C# 连接SQL数据库以及操作数据库
    变量命名规则
    C# 委托
    删除文件
  • 原文地址:https://www.cnblogs.com/Mr-Zhong/p/4071035.html
Copyright © 2011-2022 走看看