目录
- mutable
-
- 作用
- 注意事项
- 代码示例
-
- 例1
- 例2
- explicit
-
- 作用
- 注意事项
- 代码示例
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构造函数