第3课 进化后的 const分析

1.  C语言中的const变量(const修饰变量)

(1)const修饰的变量只读的,使得变量具有只读属性,但本质还是变量

(2)const定义的并不是真正的常量,它只是告诉编译器该变量不能出现在赋值符号的左边

(3)const修饰的局部变量上分配空间全局变量只读存储区分配空间

(4)const只在编译期有用,在运行期无用

 

【编程实验】C/C++中的const  3-1.cpp

#include <stdio.h>

        

int main()

{

    const int c = 0;   //C语言中会为变量c分配内存

    int* p = (int*)&c; //C++中遇到&才为c分配内存

 

    printf("Begin...\n");

 

    *p = 5;  //内存中的值己被改为5.

 

    printf("c = %d\n", c);//C语言会输出内存中的5.

                          //C++中会从符号表(而不是内存)中取值,所以为0
 

    printf("End...\n");

    return 0;

}

运行结果:

  

 

 

2. C++中的const变量(const修饰变量)

(1)C++C语言的基础上对const进行了优先处理,当碰见const声明时在符号表中放入常量

(2)编译过程若发现使用常量,则直接以符号表中的值替换

        

(3)C++译过程中若发现对const常量使用了extern&操作符,则会给对应的常量分配存储空间

(4)注意C++编译器虽然可能const常量分配空间,但不会使用存储空间中的值

 

3. 对比C/C++中的const

 

C语言

C++

本质

只读变量

常量

分配内存

会分配

可能分配存储空间

  • 当使用&操作符const常量取地址会分配内存

  • const常量全局,并且需要在其它文件中使用会分配内存(extern)

 

4. C++中的const区别(const常量类似于宏定义

 

C++中的const

定义

const int c = 5;

#define c 5

处理方式

由编译器处理编译器会进行类型检查作用域检查

预处理器处理,只是简单的文本替换

 

【编程实验】const常量宏定义  3-2.cpp

#include <stdio.h>

 

void f()

{

    //宏由预编译处理,其后面的宏起作用

    #define a 3

 

    const int b = 4;//作用域仅限于f函数

}

 

void g()

{

    printf("a = %d\n", a); //合法,只要是宏定义之后都可以使用

   

    //printf("b = %d\n", b); //非法b的作用域仅限于f函数

}

 

int main()

{

    const int A = 1;

    const int B = 2;

    int array[A + B] = {0};//C++中合法,因为它认为A、B都是常量。

                           //而C语言的const本质还是变量,数组大小只能是常量

 

    int i = 0;

 

    for(i=0;i<(A + B); i++)

    {

        printf("array[%d] = %d\n", i, array[i]);

    }

 

    f();

    g();

 

    return 0;

}

运行结果:

  

 

5. 小结

(1)与C语言不同C++中的const不是只读变量

(2)C++中的const是一个真正意义上的常量

(3)C++编译器可能会为const常量分配空间

(4)C++完全兼容C语言中的const常量语法特性

 

补充:

6.const指针(const修饰指针)

(1)几种情况

  ①const int* p;   //p可变,p指向的内容不可变

  ②int const* p;   //p可变,p指向的内容不可变

  ③int* const p;   //p不可变,p指向的内容可变

  ④const int* const p; //p不可变,p指向的内容不可变

(2)口诀:左数右指

  ①当const出现在*号左边时,指针指向的数据为常量

  ②当const出现在*号右边时,指针本身为常量

 

【实例分析】常量与指针

#include <stdio.h>

 

int main()

{

    int i = 0;

   

    const int* p1 = &i;

    int const* p2 = &i;

    int* const p3 = &i;

    const int* const p4 = &i;

   

    *p1 = 1;    // compile error

    p1 = NULL;  // ok

   

    *p2 = 2;    // compile error

    p2 = NULL;  // ok

   

    *p3 = 3;    // ok

    p3 = NULL;  // compile error

   

    *p4 = 4;    // compile error

    p4 = NULL;  // compile error

   

    return 0;

}

 

 

7. const对象(const修饰对象)

(1)const关键字能够修饰对象,使之能为只读对象,即成员变量不允许被改变

(2)只读对象编译阶段的概念,在运行时无效

 

8. const成员函数

(1)const成员函数不能直接改写成员变量的值

(2)const对象只能调用const成员函数。如拷贝构造函数里只能调用const成员函数

(3)const成员函数只能调用const成员函数

(4const成员函数的定义

    Type ClassName::func(Type p) const{};    //声明与定义都必须带const关键字

 

【编程实验】类的const函数初探

#include <stdio.h>

 

class Test

{

private:

    int mi;

 

public:

    Test(int i);

    Test(const Test& t);//t是const对象(的引用),函数内只能调用const成员函数

    int getMI() const; //const成员函数

 

    void print()      //非const成员函数

    {

        printf("mi = %d\n", mi);

    }

 

    void show() const

    {

        //print(); //错误,const成员函数不能调用非const的成员函数

       printf("mi = %d\n", mi);

    }

 

};

 

Test::Test(int i)

{

   mi = i;

}

 

Test::Test(const Test& t)

{

 

}

 

int Test::getMI() const

{

    //mi = 2; //const成员函数不能改变成员变量的值!

    return mi;

}

 

 

int main()

{

    const Test t(1); //const对象

    t.getMI();       //正确,const对象只能调用const成员函数

    t.show();        //正确,const对象只能调用const成员函数

    //t.print();     //错误,const对象不能调用非const成员函数

   

    return 0;

}

 

9. const函数重载

函数重载

函数重载的定义是:在相同的作用域中,如果函数具有相同名字而仅仅是形参表不同,此时成为函数重载。注意函数重载不能基于不同的返回值类型进行重载。

注意函数重载中的“形参表”不同,是指本质不同,不要被一些表象迷惑。main函数不能被重载。

下面三组定义本质是相同的,不是重载:

1)int sum (int &a); 和 int sum (int &);

2)  int sum (int a) 和 int sum (const int a);

3)typedef int DD;

     int sum(int a); 和 int sum (DD a);

其中第二个例子对于非引用传参,形参是否const是等价的。但是当使用引用传参时,有无const是不同的使用指针传参时,指向const对象的指针和指向非const对象的指针做形参的函数是不同的

 

*下面谈论一个比较恶心的问题,基于const的重载。

在类中会有这样一种重载,它是合法的。

Class A {

int function ();

int function () const;

};

可以看到在A类中,function函数是发生重载了,而且是合法的。而且在调用时,只用A类的const对象才能调用const版本的function函数,而非const对象可以调用任意一种通常非const对象调用不是const版本的function函数

原因是:按照函数重载的定义,函数名相同而形参表有本质不同的函数称为重载。在类中,由于隐含的this形参的存在,const版本的function函数使得作为形参的this指针的类型变为指向const对象的指针,而非const版本的使得作为形参的this指针就是正常版本的指针。此处是发生重载的本质。重载函数在最佳匹配过程中,对于const对象调用的就选取const版本的成员函数,而普通的对象调用就选取非const版本的成员函数。

(注:this指针是一个const指针,地址不能改,但能改变其指向的对象或者变量)

 

 

我们从一个例子说起,来看一下String类, 我们为它提供一个下标操作符([ ])以读写指定位置的字符(char)。

只要了解过C++的操作符重载的语法,很快就可以写出下面这个[]操作符重载函数:
char& operator[](int posion)    // function_1
 {
      return data[posion];
 };
注意,这里该函数的返回值为一个引用,否则str[0] = 'c'这样的语句就会不合法,因为str[0]将是一个左值。

那么,是否提供这样一个function_1就足够了呢?看下面这段代码:
const String str= "She";
char c = str[0];    // 错误!编译提示:error C2678: 二进制“[” : 没有找到接受“const String”类型的左操作数的运算符(或没有可接受的转换)

很显然,我们必须还要为const String提供一个const版本的opeartor[]。如下:
char& operator[](int posion) const
 {
      return data[posion];
 }
这样,当使用const的String对象使用[]操作符时,便会调用该const的重载版本。
但是,这样就OK了嘛?虽然上面的那段代码没有问题了,但是其中却隐藏了一个陷阱,看如下代码:
const String str = "She";
str[0] = 'T';
上面这段代码可以编译,运行通过,str变为了"The"!而str声明为const的!!

现在,你应该知道了,对于const的该操作符重载函数其返回值也应该是const的,否则就会出现可以通过其修改const对象的漏洞。修改如下:
const char& operator[](int posion) const
 {
      return data[posion];
 }
好了,现在没有问题了!

我们再回过头来看一下,为了给String提供一个[]操作符来读写指定位置的字符,需要提供如下两个函数,以分别对非const String对象和const String对象提供支持:
char& operator[](int posion)
 {
     return data[posion];
 };

 const char& operator[](int posion) const
 {
     return data[posion];
 }

posted @ 2018-12-08 15:11  梦心之魂  阅读(173)  评论(0编辑  收藏  举报