C++ const的详解

概述

const 是一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器哪些值是保持不变的,如果我们的代码试图去修改,编译器会给出错误提示。const可以使用在很多地方,下面是我对const进行的梳理。

1、const修饰变量

修饰变量时,表示此变量的值是只读的,不能被修改,因为不能被修改所以必须初始化。如:

const int abcd = 1234;
const int ival;	//编译出错

2、const修饰指针或引用

主要看const在* 的左边还是右边。如果const在*的左边,则表示指针指向的内容为常量,如

const int *ptr;  	//*ptr为常量;
int const *ptr;  	//*ptr为常量;

cosnt 在*的右边,则表示指针本身为常量,如

int* const ptr;	//ptr为常量

两者的结合就是既不能改指针,又不能改内容:

cosnt int* const ptr;	//两者都不能修改

引用和指针,两者差不多,唯一的区别是引用的定义就不能对引用进行重新绑定,所以只有不能修改引用绑定值的逻辑

int a = 123;
const int& b = a;	//不能通过b进行修改

3、const修饰函数

因为非成员函数上不允许修饰符,所以对于非成员函数,只有修饰参数和返回值。如:

int Add(int x, int y) const	//编译器会报错,error C2270: “Add”: 非成员函数上不允许修饰符
{
	return x + y;
}

const修饰函数参数,则函数内部不能进行改变,函数外部并不关心,与修饰变量和指针规则的一样。如

int func( const int x)
{
	return ++x;		//编译时报错
}

const修饰函数返回值,则返回值不能进行修改,与上面一样。

4、const修饰类对象

const修饰类对象时与const修饰变量并无实质不同,只是在于类对象的“改变”定义。
类对象的“改变”定义:改变任何成员变量的值,调用任何非const成员函数

class myclass
{
public:
  void func1();
  void func2() const;
	
	int m_iVal;
};

const myclass temp;
temp.m_iVal = 10;		//编译出错:不能修改成员变量值

temp.func1(); 			//编译出错:不能调用非const成员函数
temp.func2(); 			//正确

5、const修饰类成员变量

const修饰的成员变量不能被修改,所以只能在初始化列表中被初始化,和类中的引用成员变量一样。

class myclass
{
public:
	myclass(int val) : m_iVal(val) { };
	
	const int m_iVal;
};

如果是静态 cosnt 成员变量,因为属于整个类,而不是对象,所以只能去类外部单独定义并初始化:如

class myclass
{    	
	static const int m_iVal;
};
const int myclass::m_iVal = 10;

6、const修饰类成员函数

const修饰成员函数表示此函数不能对任何成员变量进行修改。一般const写在函数的后面,形如:int func() const;

class myclass
{
public:
	int func() const { return ++m_iVal; };	//编译错误,不能进行修改
	
	int m_iVal;
};

在成员函数调用的过程中,都有一个this指针被当做参数隐形的传递给成员函数。这个this指针,指向调用这个函数的对象,并且是const指针,不能修改其指向。

传递给const成员函数的this指针,指向一个const对象。也就是说,在const成员函数内部,这个this指针是一个指向const对象的const指针。所以就明白为啥const成员函数不能修改任何成员变量了。

因为静态成员函数没有this指针,所以静态成员函数不能声明称const的。

两个成员函数,如果只是常量性不同,可以被重载。如果两者有着实质等价的实现时,令非const版本调用const版本,避免代码的重复

7、const修饰类成员函数与非成员函数构成重载

重载的定义:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载

初一看 成员函数 与 const 成员函数不应该构成重载,但下面的类书写时合法的:

class D
{
public:
	void funcA();				//1
	void funcA() const;			//2
	void funcB(int a);			//3
	void funcB(const int a);	//4
};

其中 funcA 的两个函数构成了函数重载,而funcB 则编译错误。

const 发生重载的本质是:由于隐含的this形参的存在,const版本的function函数使得作为形参的this指针的类型变为指向const对象的指针,而非const版本的使得作为形参的this指针就是正常版本的指针。

调用规则:

7.1、const函数与非const函数的调用规则

const对象默认调用const成员函数,非const对象默认调用非const成员函数;
在同时存在const函数和非const重载函数的前提下,若非const对象想调用const成员函数,则需要显示的转化,例如(const Student&)obj.getAge();
若const对象想调用非const成员函数,同理进行强制类型转换const_cast < Student&>(constObj).getAge();(注意constObj一定要加括号)

7.2、当类中只有一种函数存在的情况

非const对象可以调用const成员函数或者非const成员函数
const对象只能调用const成员函数,若直接调用非const成员函数编译器会报错

而funcB 编译错误原因在于:对于非引用传参,形参是否const是等价的

8、mutable关键字

在C++中为了突破const的限制而提供了关键字mutable。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,或类对象中。如:

class myclass
{
public:
	int func() const { return ++m_iVal; };	//正确
	
	mutable int m_iVal;
	mutable int m_iVal2;
};

const myclass temp;
temp.m_iVal2++;

mutable只能修饰非静态数据成员

9、const与define宏定义的区别

  1. 处理阶段不同:define是在预处理阶段。const常量在编译阶段使用。
  2. define仅做替换,没有类型检查。const有明确的类型,在编译阶段会进行类型检查。
  3. 存储方式不同:define只替换不会分配空间。const常量需要根据情况来定是否需要分配内存。有时只将它们保存在符号表中,有时进行分配。
  4. 集成化的调试工具中,define没法调试,const可以

10、const_cast

用于修改类型的const属性,只能改变运算对象的底层const
语法:const_cast<type_id> (expression)

对于将常量对象转换成非常量对象的行为,我们称为“去掉const性质”,但是其内容是不变的。一旦我们去除了某个对象的const性质,编译器就不在阻止我们对该对象进行写操作了。

感谢大家,我是假装很努力的YoungYangD(小羊)。

参考资料:
https://www.jb51.net/article/118141.htm
https://www.cnblogs.com/icemoon1987/p/3320326.html
https://blog.csdn.net/xiazhiyiyun/article/details/71969618
https://blog.csdn.net/u014630623/article/details/51290954

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值