找回密码
 立即注册

C++老鸟日记038 运算符重载之二三问

累计签到:40 天
连续签到:1 天
发表于 2018-10-10 17:33:35 | 显示全部楼层 |阅读模式
本帖最后由 baizy77 于 2018-10-10 17:52 编辑

版权声明
-------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
-------------------------------------------------------------------------------
本套课程属于:《C++跨平台开发干货》系列课程。
-----------------------------------------------------------------------------

引言:
----------------------------------------------------------------------------
       在前面的章节中,我们已经讨论过构造函数的作用与意义,但是我们对于构造函数仍然存在一些疑问,比如
1,  类A的构造函数的参数一定是类A吗?
2,  构造函数自动类型转换指什么?怎样防止构造函数自动类型转换?
3,  如果想让二元运算符转换任一个参数,需要怎么做?
下面我们依次解答。

正文:
----------------------------------------------------------------------------
       闲话少说,直接看题。
问题1,  类A的构造函数的参数一定是类A吗?
代码清单:
  1. ----------------------------------------------------------------------------
  2. // a.h
  3. class A {
  4. public:
  5.        A();
  6.        A(const A& a);
  7. };
  8. ----------------------------------------------------------------------------
复制代码

在上述代码中,类A的构造函数是一个A类型的const引用,这同我们之前看到的构造函数一样,但是也不总是这样。请看下面的代码:
代码清单:
  1. ----------------------------------------------------------------------------
  2. // f.h
  3. class F {
  4. public:
  5.        F();
  6. };
  7. // a2.h
  8. class A2 {
  9. public:
  10.        A2();
  11.        A2(const F&  f);
  12. };
  13. ----------------------------------------------------------------------------
复制代码

从上述代码可以看出,类A2提供了一个不一样的构造函数,它传入一个F类型的const引用,这可以用来将F类型的对象转换为A类型的对象。在前面的章节中,我们也提到过这种编程方法。在这个构造函数中,只要把F类型对象的成员数据根据需要对应拷贝到A的对象上就可以了。

问题 2,  构造函数自动类型转换指什么?怎样防止构造函数自动类型转换?
       其实这个问题同前面的问题性质一样。
代码清单:
  1. ----------------------------------------------------------------------------
  2. void func(A);
  3. ……
  4. int main(int argc, char*argv[]) {
  5.        F f;
  6.        func(f);
  7. }
  8. ----------------------------------------------------------------------------
复制代码

       从上述代码可以看出,函数func()的入口参数类型为A,但是main()函数中调用func()函数时传入的参数的类型为F,因为上一个问题中类型A提供了拷贝构造函数:
       A(const F& f);
       所以,对于func(f)这行代码,编译器检查到这个函数需要一个类型为A的对象,编译器会检查是否有从类型F转换为类型A的方法,结果编译器找到了构造函数A(const F&),因此编译器悄悄执行隐式类型转换,将f转换为类型A。这称作构造函数的自动类型转换。
       有时候通过构造函数自动类型转换可能会出现问题,那么,怎样防止这种情况呢?通过声明显示构造函数来解决:

代码清单:
  1. ----------------------------------------------------------------------------
  2. // a2.h
  3. class A2 {
  4. public:
  5.        A2();
  6.        explicit  A2(const F&  f);
  7. };
  8. ----------------------------------------------------------------------------
复制代码

       关键字explicit通知编译器,必须使用显示类型转换,也就是说下面的代码不起作用了:   
代码清单:
  1. ----------------------------------------------------------------------------
  2. void func(A);
  3. ……
  4. int main(int argc, char*argv[]) {
  5.        F f;
  6.        func(f); // 编译错误:不允许自动类型转换
  7. }
  8. ----------------------------------------------------------------------------
复制代码

       上面的代码会导致编译错误:不允许自动(隐式)类型转换。如果仍然希望调用func,代码应做如下改动:
代码清单:
  1. ----------------------------------------------------------------------------
  2. void func(A);
  3. ……
  4. int main(int argc, char*argv[]) {
  5.        F f;
  6.        func(A(f));
  7. }
  8. ----------------------------------------------------------------------------
复制代码


场景3,  如果想让二元运算符转换任一个参数,需要怎么做?
这个问题是啥意思呢?我们都知道使用成员函数方式重载+-*/这些操作符时,仅仅提供一个入口参数就够了,也就是说,编译器只能尝试把第二个参数进行自动类型转换,那么如果希望编译器自动将任意一个参数(不管第一个还是第二个)进行隐式类型转换,该怎么做呢?

代码清单:
  1. ----------------------------------------------------------------------------
  2. // a.h
  3. class A {
  4. public:
  5.        A(int i) {m_i = i;}
  6.        const A operator+(const A&) const;
  7.        int getI(){return m_i;}
  8.        friend const A operator-(const A&, const A&);
  9. private:
  10.        m_i;
  11. };

  12. // a.cpp
  13. const A  A:: operator+(const A& right)  const {
  14. returnA(m_i + right.getI());
  15. }
  16. const A operator-(constA& a1, const A& a2) {
  17.        return A(a1.m_i – a2.m_i);
  18. }

  19. // main.cpp
  20. void  func() {
  21.        A a(3);
  22.        a + 5;
  23.        2 – a;
  24. }
  25. ----------------------------------------------------------------------------
复制代码

在上述代码中,类A拥有重载的成员运算操作符operator+()和友元操作符operator-()。因为A有一个int参数的构造函数,所以int可以隐式(自动)转换为类型A。所以,当编译器看到 a+5时,编译器会查看A的重载操作符中有没有operator+()接口,当它找到后会自动把第二个操作数5转换为A类型对象并调用a的operator+()接口。但是当编译器看到2-a时就不同了,编译器会把第一个操作数2转换为类型A,因为A有友元操作符operator-()接口。所以对于二元运算符来说,如果希望编译器转换任意一个参数(第一个或第二个),那么就要把操作符定义为友元。
结语:
----------------------------------------------------------------------------
       今天针对运算符重载自问自答了几个问题,其实前两个问题的本质是一样的。我们提这些问题的目的是希望能够加深对运算符重载的理解,希望大家可以在编程中熟练运用这些技能。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

baizy77

    主题

    帖子

    181

    积分

    Qter2级会员

    Rank: 2

    积分
    181

Qter2级会员

发私信

Qt开源社区——开源 共享 自由

微信扫一扫
查看精品教程!