C++: const用法

  const限定词的用法一直是我学习、使用C/C++时遇到的困扰之一,想着在这里慢慢记录每一次遇到的问题吧。
  主要是const在各个位置所代表的含义与区别。

在本文中参考到的文章链接:
  https://blog.csdn.net/liyuan123zhouhui/article/details/51513382
  https://bbs.csdn.net/topics/70273340
  https://blog.csdn.net/jisuanji198509/article/details/80557333


1.声明中的const

Example 1:

int n = 5;
int* pint = &n;
const int* cpint = &n;
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
n = 6;			// 正确:没有限定n
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
*pint = 7;		// 正确:没有限定pint
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
// *cpint = 8;	// 错误:cpint被限定为const,无法修改指向内容的值

Out (Example 1):

n = 5, *pint = 5, *cpint = 5, pint = 0000002A39AFDFC4
n = 6, *pint = 6, *cpint = 6, pint = 0000002A39AFDFC4
n = 7, *pint = 7, *cpint = 7, pint = 0000002A39AFDFC4
// 若加上注释行,则报错为
// C3892	“cpint”: 不能给常量赋值

Example 2:

int m = 4;
int n = 5;
int* pint = &n;
int* const cpint = &n;
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
n = 6;			// 正确:没有限定n
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
*pint = 7;		// 正确:没有限定pint
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
*cpint = 8;		// 正确:cpint的指向被限定为const,但当前指向的值可修改
cout << "n = " << n
	<< ", *pint = " << *pint
	<< ", *cpint = " << *cpint
	<< ", pint = " << pint << endl;
//cpint = &m;	// 错误:cpint的指向被限定为const,无法更改指向

Out (Example 2)【总结】:

n = 5, *pint = 5, *cpint = 5, pint = 0000000F651EDD44
n = 6, *pint = 6, *cpint = 6, pint = 0000000F651EDD44
n = 7, *pint = 7, *cpint = 7, pint = 0000000F651EDD44
n = 8, *pint = 8, *cpint = 8, pint = 0000000F651EDD44
// 若加上注释行,则报错为
// C3892	“cpint”: 不能给常量赋值

TIP:
  对于上述两个用法实际区分的是const在*的左边还是右边,因此有如下三种使用使用方法都是正确的:

int n = 5, m = 3;
const int* cpint1 = &n;			// const数据,非const指针
int const* cpint2 = &n;			// const数据,非const指针
int* const cpint3 = &n;			// 非const数据,const指针
const int* const cpint4 = &n;	// const数据,const指针
// *cpint1 = 3;					// 报错
// cpint3 = &m;					// 报错

2.函数中的const

Example 1:

int func(const int& val) {
	int n = val + 1;	// 正确:val作为右值可用于计算
	val = 3;			// 错误:val被const限定,无法修改值
	return 1;
}

TIP:
  在此处使用const的目的是,让编程人员清楚的知道,此处使用引用传递,保证该参数在函数中不允许被修改。相应地,当编程人员想要修改引用的变量时,一般使用指针进行传值。

Example 2:

const int func(int val) {
	return 1;
}

  这里使用const,是保证函数的返回值不被修改,也许你会质疑这种可能性,但是这种可能性确实存在,
详细情形如下: (摘自effective c++)

const rational operator*(const rational& lhs,
                         const rational& rhs);

  很多程序员第一眼看到它会纳闷:为什么operator*的返回结果是一个const对象?因为如果不是这样,用户就可以做下面这样的坏事:

rational a, b, c;
...
(a * b) = c;      // 对a*b的结果赋值

  我不知道为什么有些程序员会想到对两个数的运算结果直接赋值,但我却知道:如果a,b和c是固定类型,这样做显然是不合法的。一个好的用户自定义类型的特征是,它会避免那种没道理的与固定类型不兼容的行为。对我来说,对两个数的运算结果赋值是非常没道理的。声明operator*的返回值为const可以防止这种情况,所以这样做才是正确的。

Example 3.1:

class foo{
private:
	int m_member;
public:
	int func() const;	// 在函数形参的括号外面有一个const
};

 我最不明白的就是这个const的限定,网上的介绍如下:

(1)const对象的访问只能靠const成员函数
(2)const成员函数不被允许修改它所在对象的任何一个数据成员。

  最初我的理解就是,这个const只用于类成员函数的后面,用来限定:不能在函数中修改类成员变量的值(因为正常访问修改类成员变量,只能通过类成员函数)。
  后来遇到一个问题,发现这个const在用const(数据)修饰的类指针中有特定的用处:

Example 3.2:

说明:
  我最初想写一个类test(其功能可以是存储一些特定的数据类型),在func.h和func.cpp文件中使用test类的数组来显示数据(总之就是不会更改类成员),因此在使用该类的数组/指针时应该在前面加上数据const的限定来告知程序员这一点。但是在实际操作时发现,这样使用时,在foo子程序中无法调用类成员函数。伪代码如下:

// test.h
class test{
private:
	int i;
public:
	test(int i);
	void print();
};
// func.h
// func.cpp
#include "test.h"
void foo(const test* p_test,const int n){
	for(int i = 0; i < n; i++)
		p_test[i].print();	// 这里无法调用 print() 函数
}

  其原因是,使用了const(数据)修饰指针,使得编译器认为你的类成员变量是无法修改的,即使是类成员函数也不应该修改类成员变量,但是编译器默认每一个类成员函数是有资格修改类成员变量的!除非你使用const限定来告知编译器,而这个const就加在类成员函数的最后。伪代码如下:

// test.h
class test{
private:
	int i;
public:
	test(int i);
	void print() const;		// 这个const告诉编译器,这个成员函数是不修改成员变量的
							// 因而可以被const数据保护的类变量调用
};
// func.h
// func.cpp
#include "test.h"
void foo(const test* p_test,const int n){
	for(int i = 0; i < n; i++)
		p_test[i].print();	// 编译器已被告知 print() 函数不会修改成员变量,符合const数据的限定
							// 因此这里可以通过编译
}

总结:
  对于const限定的类变量,只能调用const限定的类成员函数(该const加在函数声明的末尾);而const限定的类成员函数则可以被任意类变量所调用。(代码示意如下)
(关于这个const,若有其他用处和注意点,欢迎留言。)

// test.h
class test{
private:
	int i;
public:
	test(int i);
	void print1();
	void print2() const;
};
// main.cpp
#include "test.h"
int main(){
	test obj1(123);
	const test obj2(1234);
	obj1.print1();	// 正确,非const数据保护的类对象任意调用类成员函数
	obj1.print2();	// 正确,非const数据保护的类对象任意调用类成员函数
	obj2.print1();	// 错误!const数据保护的类对象 不能 调用 非const限定 的类成员函数
	obj2.print2();	// 正确,const数据保护的类对象 只能 调用   const限定 的类成员函数
	return 1;
}

3.使用const代替#define

const double pi = 3.1415926;	// 推荐
#define pi 3.1415926			// 不推荐

  这也是网络上很多人推崇的做法,原因是:使用define(宏)实际上是在编译时,将所有的对应字符进行替换,从而会导致产生一些意料外的错误。我印象中老师给我们举过这样一个例子:

#define foo 3 + 4

int n;
int ans;
n = 2;
ans = n * foo;

  对于大部分人的直观理解来说,或许编程人员想要的结果是ans = n * (3 + 4);即输出结果为ans = 14。
  但实际上,编译器处理时会替换成:

ans = n * 3 + 4;	// 处理结果为 ans = 10

  而这一点在复杂的程序中一般能通过编译,却不易发现错误之处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值