C++ const关键字的总结

  const关键字是c++中一个很重要又很有迷惑性的知识点,这里对其进行一次总结。

const修饰非成员变量

 const全局/局部变量

        const全局变量

在文件a.cpp中定义了一个全局变量a

int a = 1;

 在文件test.cpp中使用全局变量a

#include <iostream>
using namespace std;

extern int a;
int main()
{
	//const volatile int a = 7;
	int *p = (int *)(&a);
	*p = 8;
	cout << "a=" << a << endl;
	cout << "*p=" << *p;
	system("pause");
	return 0;
}

结果为:

如果将全局变量a定义为const

const int a = 1;
#include <iostream>
using namespace std;

extern const int a;
int main()
{
	//const volatile int a = 7;
	int *p = (int *)(&a);
	*p = 8;
	cout << "a=" << a << endl;
	cout << "*p=" << *p;
	system("pause");
	return 0;
}

这里可以看出const在修饰全局变量时第一个作用,会限定全局变量的作用范围到其定义时所在的编译单元

const全局变量使得我们指定了一个语义约束,即被修饰的全局变量不允许被修改,而编译器会强制实施这个约束。

#include <iostream>
using namespace std;

const int a = 7;
int main()
{
	//const volatile int a = 7;
	int *p = (int *)(&a);
	*p = 8;
	cout << "a=" << a << endl;
	cout << "*p=" << *p;
	system("pause");
	return 0;
}

运行这段代码,会发现编译器报异常。编译器不允许对const全局变量的改动

       const局部变量

对于const局部变量,有个有趣的地方:

#include <iostream>
using namespace std;

int main()
{
	//const volatile int a = 7;
	const int a = 7;
	int *p = (int *)(&a);
	*p = 8;
	cout << "a=" << a << endl;
	cout << "*p=" << *p;
	system("pause");
	return 0;
}

运行结果显示const局部变量被修改了,但是在使用变量名输出时,编译器会出现一种类似宏定义的功能一样的行为,将变量名替换为初始值。可见,const局部变量并不能做到真正的不变,而是编译器对其进行了一些优化行为,这导致了const局部变量与真实值产生了不一致。那么,如果想获取修改后的const局部变量真实值,该怎么办呢?答案是使用volatile关键字。

#include <iostream>
using namespace std;

int main()
{
	const volatile int a = 7;
	//const int a = 7;
	int *p = (int *)(&a);
	*p = 8;
	cout << "a=" << a << endl;
	cout << "*p=" << *p;
	system("pause");
	return 0;
}

volatile关键字使得程序每次直接去内存中读取变量值而不是读寄存器值,这个作用在解决一些不是程序而是由于别的原因修改了变量值时非常有用。

cosnt修饰指针

const修饰指针,涉及到两个很重要的概念,顶层const底层cosnt

指针自身是一个对象,它的值为一个整数,表明指向对象的内存地址。因此指针长度所指向对象类型无关,在32位系统下为4字节,64位系统下为8字节。进而,指针本身是否是常量以及所指向的对象是否是常量就是两个独立的问题。

顶层const(top-level const): 指针本身是个常量

底层const(low-level const): 指针指向对象是一个常量

int a = 1;
int b = 2;
const int* p1 = &a;
int* const p2 = &a;

根据从内到外,由近到远读符号的规则

p1依次解读为:p1是个指针(*),指向一个int型对象(int),该对象是个常量(const)。 因此这是一个底层cosnt

p2依次解读为:p2是个常量(const),p2是个指针(*),指向一个int对象(int)。 因此这是一个顶层const

const修饰函数参数

const修饰参数是为了防止函数体内可能会修改参数原始对象。因此,有三种情况可讨论:

  1. 函数参数为值传递:值传递(pass-by-value)是传递一份参数的拷贝给函数,因此不论函数体代码如何运行,也只会修改拷贝而无法修改原始对象,这种情况不需要将参数声明为const。
  2. 函数参数为指针:指针传递(pass-by-pointer)只会进行浅拷贝,拷贝一份指针给函数,而不会拷贝一份原始对象。因此,给指针参数加上顶层const可以防止指针指向被篡改,加上底层const可以防止指向对象被篡改。
  3. 函数参数为引用:引用传递(pass-by-reference)有一个很重要的作用,由于引用就是对象的一个别名,因此不需要拷贝对象,减小了开销。这同时也导致可以通过修改引用直接修改原始对象(毕竟引用和原始对象其实是同一个东西),因此,大多数时候,推荐函数参数设置为pass-by-reference-to-const。给引用加上底层const,既可以减小拷贝开销,又可以防止修改底层所引用的对象。

const修饰函数返回值

令函数返回一个常量,可以有效防止因用户错误造成的意外。

比如

if (a*b = c)

如果a,b,c都是如同int的内置类型,编译器会直接报错

因为对于内置类型的*操作返回的不是一个左值,因此不能放在=的左边。为什么会出现这种情况呢?可能用户只是想比较是否相等,却打字打漏了一个等号(ORZ)。因此,对于很多自定义类型的函数,应该尽量与内置类型兼容,在应该返回右值的函数返回那里应该加上const。

if (a*b == c)

const成员函数

将const实施与成员函数上,只要是防止成员函数修改类对象的内容。良好的类接口设计应该确保如果一个成员函数功能上不需要修改对象的内容,该成员函数应该加上const修饰。

const_reference operator[]( size_type pos ) const;

上图是STL string的成员函数,可以看出,在函数返回值,函数参数,函数是否可以修改类对象三个地方都做出了const限定。

如果const成员函数想修改成员变量值,可以用mutable修饰目标成员变量。

如果一个类对象为const 对象,语义上说明该对象的值不可改变,因此该const对象只能调用const成员函数,因为非const成员函数不能保证不修改对象值,编译器会禁止这种会造成隐患的行为。

 

以上就是对于const的简单总结。

  • 69
    点赞
  • 254
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值