当前位置: 代码迷 >> 综合 >> C++关键字系列【二】:mutable、explicit
  详细解决方案

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

热度:88   发布时间:2023-11-24 01:37:44.0

目录

  • 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构造函数

  相关解决方案