C++成员函数修饰词的意义和使用(&, &&, const, override)

1. 引言

今天逛知乎,发现有人问在C++成员函数后加& 有什么作用?因此就想着回答一下,就顺带研究了一下C++相关的几个成员函数修饰词,并总结了一下相关的用法和意义

2. C++成员函数修饰词

C++11中的成员函数修饰词主要有&、&&、const;在这里我把override也看作成员函数修饰词,这个是因为override也可以向&、&&、const用在成员函数后, 但override的作用范围有限,只能用于虚函数且虚函数需要改写的时候。下面从override开始介绍C++11的几个成员函数修饰词。由下面的类说起

class A {
public:
    A() {
        std::cout << "A constructor" << std::endl;
    }

    A(const A&) {
        std::cout << "A copy constructor" << std::endl;
    }

    A(A&&) {
        std::cout << "A move constructor" << std::endl;
    }

    virtual void m1() && {
        std::cout << "A m1()" << std::endl;
    }

    virtual ~A() {}
};

class B : public A {
public:
    virtual void m1() & {
        std::cout << "B m1()" << std::endl;
    }
};

上述,将A的成员函数m1用&&成员函数修饰词修饰,并将其声明为虚函数,在B中想要改写虚函数m1() 但是不小心将其成员函数修饰词写成了&,此时编译器不会报错,会默认B中的m1()是一个新的虚函数,这与我们的意图不符,因此怎样能让编译器报错并提示我们呢?答案便是在虚函数中成员函数修饰词后加 override

加上override的版本如下:

class A {
public:
    A() {
        std::cout << "A constructor" << std::endl;
    }

    A(const A&) {
        std::cout << "A copy constructor" << std::endl;
    }

    A(A&&) {
        std::cout << "A move constructor" << std::endl;
    }

    virtual void m1() && {
        std::cout << "A m1()" << std::endl;
    }

    virtual ~A() {}
};

class B : public A {
public:
    virtual void m1() & override {
        std::cout << "B m1()" << std::endl;
    }
};

此时运行,编译会报如下错误:

error: 'virtual void B::m1() &' marked 'override', but does not override
   27 |     virtual void m1() & override {

故可以帮我们尽快的纠正意图,所以在C++11中,如果想要改写基类中的某个虚函数,优先选择加上override。

下面我们看看真正的成员函数修饰词的作用与使用,为了讲解修饰词&、&&的作用,先从const成员函数修饰词讲起,毕竟这个大家用的多,也较为熟悉。

const成员函数修饰词:放在类的成员函数声明的最后,作为成员函数声明的一部分,主要是用来修饰成员函数的this指针,使得*this可以指向一个常量对象。

继续利用类A讲解,此时在类A的设计如下:

class A {
public:
    A() {
        std::cout << "A constructor" << std::endl;
    }

    void m1() {
        std::cout << "A m1()" << std::endl;
    }

    virtual ~A() {}
};

首先类A的成员函数void m1() 会被编译器翻译成void m1(A* const this); 因此可知传递给m1的对象不能是常量,只能是变量或者右值,即不加const修饰词会限制成员函数m1()的使用范围。下面是测试m1不能用于常量。

int main() {
    const A a = A();
    a.m1();

    return EXIT_SUCCESS;
}

编译上述代码,编译器会报如下错误:

error: passing 'const A' as 'this' argument discards qualifiers [-fpermissive]
   24 |     a.m1();

当我们对成员函数加上const修饰词后,在运行上述代码,会得到想要的结果。

理解了const成员函数修饰词的作用和使用,那么对于成员函数修饰词&、&&的理解就很简单了,可以类比const就行。继续利用类A来讲解成员函数修饰词&、&&。此时类A声明如下:

class A {
public:
    A() {
        std::cout << "A constructor" << std::endl;
    }

    void m1() & {
        std::cout << "A lvalue m1()" << std::endl;
    }

    void m1() && {
        std::cout << "A rvalue m1()" << std::endl;
    }

    virtual ~A() {}
};

利用如下代码进行测试:

A f() {
    return A();
}

int main() {
     A a = A();
     a.m1();
     f().m1();

    return EXIT_SUCCESS;
}

输出结果如下:

A constructor
A lvalue m1()
A constructor
A rvalue m1()

由此我们可以得出: 成员函数修饰词&、&&可以用来重载成员函数,且&标识的是传入成员函数m1()的是一个左值,&&标识传入成员函数的this指针指向的是一个右值。

类比const成员函数,此时编译器会将void m1()& 翻译成 void m1(A& *const this), 将void m1() && 翻译成 void (A&& *const this)。

因此我们也可以将成员函数修饰词进行组合,譬如const 与 &, const 与 &&。

下面简要介绍下const 与 & 的组合,利用类A进行测试,如下:

class A {
public:
    A() {
        std::cout << "A constructor" << std::endl;
    }

    void m1() const & {
        std::cout << "A lvalue m1()" << std::endl;
    }
/*
    void m1() const && {
        std::cout << "A rvalue m1()" << std::endl;
    }*/

    virtual ~A() {}
};
 A f() {
    return A();
}

int main() {
     A a = A();
     a.m1();
     f().m1();

    return EXIT_SUCCESS;
}

输出结果如下:

A constructor
A lvalue m1()
A constructor
A lvalue m1()

之所以如上述结果,此时可以将编译器将m1() const & 翻译成void m1(const A& * const this) 因此传入临时变量依然会调用该函数,如果不加const 上述测试便会报错。

3. 总结

总结起来,就是C++对于成员函数,编译器会默认传入一个this指针,如果需要对这个this指针参数进行某种修饰,则需要在成员函数后面加上成员函数修饰词。

成员函数修饰词的作用和其在正常函数中修饰形参的作用其实是一样的。

泛型编程与STL读书笔记

增加

后期我计划把相关文章全部转移到公众号,欢迎大家关注交流

 

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qls315

感觉好可打赏几毛钱增强更新动力

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

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

打赏作者

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

抵扣说明:

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

余额充值