C++关键字系列【二】:mutable、explicit

本文详细介绍了C++中mutable和explicit关键字的用途、注意事项及代码示例。mutable允许在const成员函数中修改特定数据成员,而explicit则阻止隐式类型转换,提高代码安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mutable

mutable的中文意思是可变的,刚好是const的反义词。被mutable修饰的变量永远都是可变的,即使在可以const成员函数内,其依然可变。

作用

  • 保持常量对象大部分数据成员只读的情况下,对个别数据成员进行修改
  • 使类的const成员函数能够修改对象的mutable数据成员
  • 可变lambada,默认情况下,对于一个值拷贝的变量,lambda不会改变其值,但如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上mutable

注意事项

  • mutable只能用来修饰类的非静态和非常量数据成员,即非static和非const

mutable不能修饰const数据成员,这非常容易理解,毕竟mutable是其反义词,同时修饰那自相矛盾了
mutable不能修饰static数据成员,是因为static数据成员不属于某个类对象,而是被类中所有对象共享,即属于类,const类对象和const函数都可以对其修改,并不需要加上mutable

  • 除非十分必要,在一个类中尽量不要使用mutable,大量使用mutable说明该类设计上有缺陷

代码示例

例1

class A {
public:
	static int num;
	void GetCallTimes()const {
		cout << "count:" << count++ << endl;
	}
	void GetNum()const {
		num = 15;
		cout << "num:" << num << endl;
	}
private:
	//int count = 0;错误,不能在const函数中修改变量count
	//mutable const int count = 0;编译出错
	//mutable static int count = 0;编译出错
	mutable int count=0;
};
int A::num = 0;
int main(void)
{
	A a;
	const A b;
	for (int i = 0; i < 5; i++) {
		a.GetCallTimes();
	}
	b.num = 10;//常量对象可以修改类的静态数据成员
	cout << "num:" << A::num << endl;
	b.GetNum();
}

输出结果:
在这里插入图片描述

例2

int main(void)
{
	int num = 10;
	//f可以改变它所捕获的变量的值
	auto f = [num]()mutable {return ++num; };//如果不加mutable,则++num会出现编译错误
	num = 5;
	auto j = f();//j为11
}

lambda的值捕获类似参数传递,不过其捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝,所以在num=5后,并不会影响到lambda内对应的值,即j为11,而不是6

explicit

作用

在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数,不能发生相应的隐式类类型转换,只能以显式的方式进行类类型转换。即被explicit修饰的构造函数不能再通过该构造函数隐式的创建类对象

原则上应该在所有的构造函数前加explicit关键字,除非明确需要用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。

注意事项

  • 只能用于类内声明的构造函数
//错误:explicit关键字只允许出现在类内的构造函数声明处
explicit A::A(double d){
	```
}
  • 只对一个实参的构造函数有效,或者有n个参数,但是其中 n-1 个参数是带有默认值的构造函数也有效
  • explicit构造函数只能用于直接初始化

代码示例

未加explicit的隐式类类型转换

class A {
public:
	A() = default;
	A(double d) {
		this->d = d;
		cout << "A(double d)" << " d:" << d << endl;
	}
	A(int x, double d=2.5) {
		this->x = x;
		this->d = d;
		cout << "A(int x, double d=2.5)" << " x:" << x << " d:" << d << endl;
	}
	A(const A&a) {
		d = a.d;
		x = a.x;
		cout << "A(const A&a)" << endl;
	}
	void func(const A& a) {
		cout << "void func(const A& a)" << " x:" << a.x << " d:" << a.d << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
private:
	double d = 0;
	int x = 0;
};

int main(void)
{
	//隐式转换过程如下
	//tmp = A()  
	//A a1(tmp);  
	//tmp.~A(); 
	A a1 = 1.5;
	//隐式调用A(int x, double d=2.5)构造函数,虽然有2个参数,但后一个有默认值,仍然能发生隐式转换, 
	A a2 = 1;
	//隐式调用A(const A&a)拷贝构造函数
	A a3 = a1;
	//隐式调用A(double d)构造函数
	a1.func(2.0);
}

输出结果:
在这里插入图片描述

加了explicit关键字后,可防止以上隐式类类型转换发生

class A {
public:
	A() = default;
	explicit A(double d) {
		//其他代码同上
	}
	explicit A(int x, double d=2.5) {
		//其他代码同上
	}
	explicit A(const A&a) {
		//其他代码同上
	}
	/*
	其他代码同上
	*/
};
int main(void)
{
	/*
	下面四句代码都会报错
	A a1 = 1.5;
	A a2 = 1;
	A a3 = a1;
	a1.func(2.0);
	*/
	//通过直接初始化调用explicit构造函数,而不能使用拷贝初始化
	A a1(1.5);
	A a2(1);
	A a3(a1);
	//尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用以下方式进行显示转换
	a1.func(A(2.0));
}

发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时,我们只能使用直接初始化而不能使用explicit构造函数

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倒地不起的土豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值