C++(五):const

const

1. 指针

const修饰 * 号,则不能把解引用作为左值;
const修饰指针名,则不能把指针名作为左值。

1.1 const修饰指针——常量指针

在 const修饰指针的情况下,指针的指向可以更改,但是不能通过指针去改变它所指向的变量的值。

int a = 10;
int b = 20;
const int *p = &a;//const修饰的指针,常量指针

p = &b;//正确,常量指针可以更改其指向
*p = 30; //错误,无法通过解引用修改其指向的变量的值

1.2 const修饰常量——指针常量

由于const修饰的是常量,因此在这种情况下,指针的指向不可以改,但是指向的值可以改。

int * const p = &a;//const修饰p,是一个指针常量

p = &b;//错误,指针的指向不能修改,即p的值不变
*p = 30;//正确,可以解引用修改a的值

1.3 const同时修饰指针和常量

既不可以修改指针的指向,也不能修改指针的指向的内容。

const int * const p = &a;

p = &b;
*p = 30;//均错误

2. 结构体

struct student
{
	string name;
	int age;
	int score;
}

void PrintStudent(const student *p)
{
	cout << p->name << p->age << p->score << ednl;
}

const 在结构体中的应用基本就是这样一个场景。

如果 PrintStudent 的形参不是指针而是 student p , 由于是值传递的形式,程序会拷贝一份副本,倘若实参很大或者不方便拷贝,则会导致效率低下甚至报错。因此这里选择使用指针,只需传递一份地址即可。

由于指针传递会修改实参的值,我们在编写这个函数的时候并不希望它修改结构体的值,因此使用 const 修饰指针,这样就无法通过解引用或者 -> 运算符修改实参的值了。

3. 类

与类有关的const可出现的位置很多,包括有:

  • 常成员变量
  • 常成员函数
  • 常对象

3.1 常成员变量

class p
{
public:
	const int m_h;
	int m_m;
};

上面p的成员m_h就是一个常成员变量,常成员变量进可以使用构造函数的初始化列表赋值,且不能再以后的使用过程中进行修改。

p::p(int h, int m) : m_h(h),m_m(m){}  //这是合法的,使用参数列表进行初始化

p::p(int h, int m)
{
	this->m_h = h;
	this->m_m = m;
}  //非法,不可以使用赋值的方法初始化常成员变量

3.2 常成员函数

常成员函数指的是在形参列表后用const修饰的成员函数,如下。常成员函数允许读取对象中的成员,但是不允许修改常对象的值。

常函数的const意在表明这个函数不会修改任何一个成员的值。

void p::show() const
{
	cout << this->m_h << endl;
}

3.3 常对象

当我们将对象定义为常对象的时候,默认在所有的成员中添加了const关键词,这导致了该对象不能修改任何成员变量。

因此,常对象只能调用常成员函数,非常成员函数无法调用,会报错。

int& p::set(int &m)
{
	this->m_m = m;
}

const p p1;
p1.set(m);   //报错,因为常对象无法调用非常成员函数

由于存在了const,就产生了一些访问权限的问题。

3.4 方法对成员的访问规则

成员非const方法const方法
非const成员可引用
可改变值
可引用
可改变值
const成员可引用
不可改变值
可引用
可改变值
const对象的成员不可引用
不可改变值
可引用
不可改变值

3.5 遇到的问题

我遇到过这样的一个问题。我定义了一个类,其中有一个成员函数Getname()作用是获取其隐私权限下的一个成员。如果我像下面这样写的话,就会报错。

class Player
{
public:
	Player(){}
	Player(string name, int age) :m_name(name), m_age(age){}
	string& Getname()
	{
		return this->m_name;
	}
	int& Getage()
	{
		return this->m_age;
	}
	bool operator==(const Player& p)
	{
		return (this->Getage() == p.Getage() && this->Getname() == p.Getname());
	}
private:
	string m_name;
	int m_age;
};

报错信息为:

IntelliSense: 对象包含与成员函数不兼容的类型限定符
对象类型是: const Player

报错行是operator==()函数。这是因为operator==()的形参为常量引用,根据上面的规则,常量引用的形参无法调用非常量函数,因此p.Getname()报错。

出于这个考虑,我尝试将Getname()函数定义成常函数。

	string& Getname() const
	{
		return this->m_name;
	}
	int& Getage() const
	{
		return this->m_age;
	}

但是依然报错,此时的报错信息为:

error C2440: “return”: 无法从“const std::string”转换为“std::string &”
error C2440: “return”: 无法从“const int”转换为“int &”

报错的就是这两个函数,经过考虑后我认为原因是这个函数return的量在const修饰下变成了常量,但是返回值类型string& 是个非常量引用。我们知道,将常量绑定到非常量引用上是非法的,导致了限定符const被丢弃,所以在这里报错了。

解决办法可以是将返回值类型string& 变为 const string&。对于这个函数而言,目的就是获取成员,倘若返回一个常量似乎也是合情合理的。但是我对这个办法总是觉得难以接受,我不知道我是否会遇到一个返回值应当允许改变的常成员函数。,我在想遇到这样的情况的话应该怎么办。所以有点不是很接受这个解决办法。

但是目前就先这样吧。以后在这种不需要修改返回值的情况下不仅形参写成const,可能还需要函数本身被const修饰,以及返回类型也是const。

4. Primer C++上的一小节

下面引用Primer C++上面的一小节内容,讲的是常量引用以及可能出现的问题。

尽量使用常量引用
把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用非常量引用也会极大的限制函数所能接受的实参类型。就像刚刚看到的,我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
这种错误绝不像看起来那么简单,它可能造成出人意料的后果。以6.2.2节的find_char函数为例,那个函数(正确地)将它的string类型的形参定义成常量引用。假如我们把它定义成普通的string&:

//不良设计,第一个形参应该是const string&
string::size_type find_char(string& s,char c,
						string::size_type& occurs)

则只能将find_char函数作用域string对象。类似下面的调用:

find_char("Hello World",'o',ctr);

将在编译时发生错误。
还有一个更难察觉的问题,加入其他函数(正确地)将它们的形参定义成常量引用,那么第二个版本的find_char无法在此类函数中正常使用。举个例子,我们希望在一个判断string对象是否是句子的函数中使用find_char:

bool is_sentence(const string& s)
{
	//如果在s的末尾有且只有一个句号,则s是一个句子
	string::size_type ctr = 0;
	return (find_char(s,'.',ctr) == s.size() - 1 &&
			ctr == 1);
}

如果find_char的第一个形参类型是string&,那面上面这条调用find_char的语句将在编译时发生错误。原因在于s是常量引用,但是find_char被(不正确地)定义成只接受普通引用。
解决该问题的一种思路是修改is_sentence的形参类型,但是这么做只不过转移了错误而已,结果是is_sentence函数的调用者只能接受非常量string对象了。
正确的修改思路是改正find_char函数的形参。如果实在不能修改find_char,就在is_sentence内部定义一个string类型的变量,令其为s的副本,然后办这个string对象传递给find_char。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值