我所遇见的坑(一)

前言

编程多了,就会遇见坑。这些坑可能有自己的不小心,也有可能第三方工具的不小心。本篇记录自己所遇到的关于const的坑,事情起因源于我拷贝一个函数定义,父类函数没有const修饰,子类有const修饰,当我想new一个对象的时候,始终编译不过去。类似如下代码:

1
2
3
4
5
6
7
8
class father
{
virtual bool HasCapture() = 0;
};
class children
{
virtual bool HasCapture() const { return true;}
};

真正的代码要比这个复杂,实例化是宏来调用的,所以我看了老半天,还是找不到哪里出错了。因为用的codeblock,编译过程中输出的都是英文,没有仔细看。后来看了VS编译,发现有提示哪个虚函数没有实现,这才想到看codeblock编译输出的结果。(这是一个惨痛的教训

const的作用

为了秉持追根到底的思想,特意去查了查const放在函数后面的作用。当然,既然介绍const,就把它的作用介绍全面。

修饰普通类型变量

1
2
3
const int a = 7;
int* p = (int*)(&a);
*p = 8;

这个很容易理解,就是a的值不能被改变。深层次我们调试看下不同编译器内存的结果,首先先看VS编译器。

Windows内存(一)

windows内存(二)

Windows内存(三)

Windows内存四

a的值内存上看到的是8,但是实际打印的时候是7。

下面来看g++编译器:

G++

编译器行为一样,都是内存看到的是8,实际打印的是7。

编译器认为a的值是7,所以千万不要轻易对const变量设法赋值,这会产生意想不到的行为。

如果不想让编译器察觉到上面对const的操作,我们可以在const前面加上volatile关键字。

volatile关键字跟const对应相反,是易变的,容易改变的意思。所以不会被编译器优化,编译器也就不会改变对a变量的操作。

以上引用来自于菜鸟教程

const本身就是不让改变的,遵循这个规范即可。

const 修饰指针变量

总共有三种情况,

  1. 修饰指针所指向的内容的时候
1
2
3
4
const int* p = 8;
*p = 10; // error
int a = 10;
p = &a; // ok

可以看出,*p = 10 是错误的语法,但是将p指针重新指向另一块内存是可以的。这种情况,const修饰的是右侧的值是不可改变的。打个比喻,比如 p 是一个小纸条,小纸条上用铅笔写着明天会下雨,你可以更改小纸条的内容,但是明天会下雨这个是不能改变的。(PS:这个比喻莫名有些恐怖的气氛。。。)

  1. 修饰指针的时候
    1
    2
    int a = 8;
    int* const p = &a;

理解了第一种情况,第二种情况就容易理解了。还是打个比喻,p 是一个小纸条,小纸条上用圆珠笔写着你明天会迟到,小纸条上的内容没有办法修改,但是你可以明天早点起床,这样你就不会迟到了。(PS:说可以修改小纸条的,请出门左走。毕竟这只是个语法规范,超出这个规范,一概不管)

  1. 同时修饰内容和指针‘
    1
    2
    int a = 8;
    const int* const p = &a;
    这种情况是第一种和第二种的结合体,理解第一第二种,这种情况自然理解了。还是依旧打个比喻,p 是一个小纸条,纸条上用圆珠笔写着明天会下雨,纸条上的内容没有办法改变,明天会下雨也没有办法改变。

const 修饰类成员函数

重头戏来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class test
{
public:
int get() const
{
// a = 10;
return a;
}
void set(int a)
{
this->a = a;
}
private:
int a;
};
int main()
{
test t;
t.set(5);
cout<<t.get()<<endl;
}

如果不注释 a = 10;编译会报错:

const 修饰类成员函数

就好比,在 逛街 函数中,你只能看不能买。

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

引用与菜鸟教程

一般情况,我们不会遇到这种需求。真要遇到了,查了查,用一用即可。

评论