31 转换构造函数,类型转换函数

1 再论类型转换

  • 标准数据类型之间会进行隐式的类型安全转换

  • 转换规则

    char  -> 
      ↓      int -> unsigned int -> long -> unsigned long -> float -> double
    short -> 
    
  • 示例1:基本数据类型之间的隐式类型转换

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      int main()
      {   
          short s = 'a';           // 隐式类型转换:char->short
          unsigned int ui = 1000;  // 隐式类型转换:int->unsigned int
          int i = -2000;
          double d = i;            // 隐式类型转换:int->double
          
          cout << "d = " << d << endl;            // -2000
          cout << "ui = " << ui << endl;          // 1000
          cout << "ui + i = " << ui + i << endl;  // 4294966296 = unsigned int + (unsigned int)(int) => 1000 + (unsgined int)(-2000) => 1000 +  
          
          // unsigned int + int => unsigned int + (unsigned int)int => unsigned int > 0
          if( (ui + i) > 0 ) {
              cout << "Positive" << endl;
          }
          else {
              cout << "Negative" << endl;
          }
          
          // short + char => (int)short + (int)char => int
          cout << "sizeof(s + 'b') = " << sizeof(s + 'b') << endl;  // 4
          
          return 0;
      }
      
    • 编译运行

      -2000
      1000
      
      Positive
      4
      
  • 【问题】普通类型与类类型之间能否进行类型转换?类类型之间能否进行类型转换?

    • 普通类型 => 类类型:转换构造函数
    • 类类型 => 普通类型:类型转换函数
    • 类类型 => 类类型:类型转换函数/转换构造函数

2 普通类型 ---> 类类型?

  • 示例2:普通类型 => 类类型

    • Demo1:直接尝试 int => Test

      #include <iostream>
      
      using namespace std;
      
      class Test
      {
      };
      
      int main()
      {   
          Test t;
          
          t = 5;  // int => Test
          
          return 0;
      }
      
    • 编译

      test.cpp: In function ‘int main()’:
      test.cpp:13:7: error: no match for ‘operator=’ (operand types are ‘Test’ and ‘int’)
           t = 5;
             ^
      test.cpp:5:7: note: candidate: Test& Test::operator=(const Test&)
       class Test
             ^
      test.cpp:5:7: note:   no known conversion for argument 1 from ‘int’ to ‘const Test&’
      
    • Demo2:利用强制类型转换

      #include <iostream>
      
      using namespace std;
      
      class Test
      {
      };
      
      int main()
      {   
          Test t;
          
          t = (Test)5;  // int => Test
          
          return 0;
      }
      
    • 编译

      test.cpp: In function ‘int main()’:
      test.cpp:14:15: error: no matching function for call to ‘Test::Test(int)’
           t = (Test)5;
                     ^
      test.cpp:5:7: note: candidate: Test::Test()
       class Test
             ^
      test.cpp:5:7: note:   candidate expects 0 arguments, 1 provided
      test.cpp:5:7: note: candidate: Test::Test(const Test&)
      test.cpp:5:7: note:   no known conversion for argument 1 from ‘int’ to ‘const Test&’
      
    • Demo3:利用构造函数 => 编译通过

      #include <iostream>
      
      using namespace std;
      
      class Test
      {
      public:
          Test() {
              
          }
          // 带一个参数的构造函数
          Test(int i) {
              
          }
      };
      
      int main()
      {   
          Test t;
          
          t = Test(5);  // 利用生成的临时对象赋值
          
          return 0;
      }
      
    • Demo4:再次尝试 int ---> Test => 编译成功 => 为什么这里成功?

      #include <ios tream>
      
      using namespace std;
      
      class Test
      {
      public:
          Test() {
              
          }
          Test(int i) {
              
          }
      };
      
      int main()
      {   
          Test t;
          
          t = 5;  // int -> Test 成功
          
          return 0;
      }
      
  • 【分析】再论构造函数

    • 构造函数可以定义不同类型参数

    • 参数满足下列条件时成为转换构造函数

      • 有且仅有一个参数
      • 参数是基本类型或其它类类型(只要不是自己当前的类型即可)
    • 另一个视角

      • 旧式的 C 方式强制类型转换
      int i;
      Test t;
      
      i = int(1.5);  // C 方式的强制类型转换
      t = Test(100);  // C 方式的强制类型转换:本质是调用类的构造函数
      
    • 编译器会尽力尝试让源码通过编译

      Test t;
      t = 100;
      
      • 100 这个立即数默认为 int 类型,常规上是不能赋值给 Test 类的 t 对象的,但当遇到转换构造函数时会发生变化!
      • 如果 Test 这个类中定义了转换构造函数 Test(int i); ,就可以进行隐式类型转换(普通类型 ---> 类类型),默认等价于:t = Test(100);
  • 编译器尽力尝试的结果是隐式类型转换

    • 隐式类型转换会让程序以意想不到的方式进行工作
    • 隐式类型转换是工程中 bug 的重要来源
  • 示例3:隐式类型转换的危害

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Test
      {
          int mValue;
      public:
          Test() {
              mValue = 0;
          }
          
          // 转换构造函数
          Test(int i) {
              mValue = i;
          }
          
          // +操作符重载函数
          Test operator + (const Test& p) {
              Test ret(mValue + p.mValue);
              
              return ret;
          }
          
          int value() {
              return mValue;
          }
      };
      
      int main()
      {   
          Test t;
          
          t = 5;  // <=> t = Test(5);
          
          Test r;
          
          r = t + 10;  // 这里编译通过:Test + int  => r = t + Test(10);
          
          cout << r.value() << endl;  // 15
          
          return 0;
      }
      
    • 编译运行

      15
      
    • 分析:代码行 r = t + 10; 编译通过,其原因是转换构造函数(Test(int i);)的存在,从而进行了隐式类型转换,等价于:r = t + 10; => r = t + Test(10); 。而这段代码很可能是手误,极有可能造成 bug

  • 工程中通过 explicit 关键字杜绝编译器的转换尝试

    • 转换构造函数被 explicit 修饰时只能进行显式转换

    • 转换方式

      • static_cast<ClassName>(value); : C++
      • ClassName(value); : C
      • (ClassName)value; : C,不推荐
    • 示例4:转换方式

      • Demo1

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Test
        {
            int mValue;
        public:
            Test()
            {
                mValue = 0;
            }
            
            explicit Test(int i)
            {
                mValue = i;
            }
            
            Test operator + (const Test& p)
            {
                Test ret(mValue + p.mValue);
                
                return ret;
            }
            
            int value()
            {
                return mValue;
            }
        };
        
        int main()
        {   
            Test t;
               
            t = 5;
            
            Test r;
            
            r = t + 10;
            
            cout << r.value() << endl;
            
            return 0;
        }
        
      • 编译

        test.cpp: In function ‘int main()’:
        test.cpp:37:7: error: no match for ‘operator=’ (operand types are ‘Test’ and ‘int’)
             t = 5;
               ^
        test.cpp:6:7: note: candidate: Test& Test::operator=(const Test&)
         class Test
               ^
        test.cpp:6:7: note:   no known conversion for argument 1 from ‘int’ to ‘const Test&’
        test.cpp:41:11: error: no match for ‘operator+’ (operand types are ‘Test’ and ‘int’)
             r = t + 10;
                   ^
        test.cpp:20:10: note: candidate: Test Test::operator+(const Test&)
             Test operator + (const Test& p)
        
      • Demo2

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Test
        {
            int mValue;
        public:
            Test()
            {
                mValue = 0;
            }
            
            explicit Test(int i)
            {
                mValue = i;
            }
            
            Test operator + (const Test& p)
            {
                Test ret(mValue + p.mValue);
                
                return ret;
            }
            
            int value()
            {
                return mValue;
            }
        };
        
        int main()
        {   
            Test t;
            
            t = static_cast<Test>(5);    // t = Test(5);
        
            Test r;
            
            r = t + static_cast<Test>(10);   // r = t + Test(10);
            
            cout << r.value() << endl;  //15
            
            return 0;
        }
        

4 类类型 ---> 普通类型?

  • 【问题】类类型是否能够类型转换到普通类型?

  • 可以,利用类型转换函数

  • 类型转换函数

    • C++ 类中可以定义类型转换函数

    • 类型转换函数用于将类对象转换为其他类型

    • 语法规则

      operator Type()
      {
          Type ret;
          //...
          return ret;
      }
      
  • 示例5:类型转换函数

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Test
      {
          int mValue;
      public:
          Test(int i = 0) {
              mValue = i;
          }
          int value() {
              return mValue;
          }
          // 类型转换函数
          operator int () {
              return mValue;
          }
      };
      
      int main()
      {   
          Test t(100);
          int i = t;  // <=> int i = t.operator int ();
          
          cout << "t.value() = " << t.value() << endl;  // 100
          cout << "i = " << i << endl;  // 100
          
          return 0;
      }
      
  • 类型转换函数

    • 转换构造函数具有同等的地位
    • 使得编译器有能力将对象转换为其他类型
    • 编译器能够隐式的使用类型转换函数
  • 编译器会尽力尝试让源码通过编译

    Test t(1);
    int i = t;
    
    • t 对象为 Test 类型,为什么可以用于初始化 int 类型的变量:因为 Test 类中定义了类型转换函数 operator int(); ,可以进行转换 (相当于这个对象隐式地调用类型转换函数)

5 类类型之间的转换

  • 【问题】类类型之间可以相互转换么?

  • 可以,类型转换函数和转换构造函数

  • 示例6:

    • Demo1:利用类型转换函数

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Value
      {
      public:
          Value() {
          }
      };
      
      class Test
      {
          int mValue;
      public:
          Test(int i = 0) {
              mValue = i;
          }
          int value() {
              return mValue;
          }
          // 类型转换函数
          operator Value() {
              Value ret;
              cout << "operator Value()" << endl;
              return ret;
          }
      };
      
      int main()
      {   
          Test t(100);
          Value v = t;  // <=> Value v = t.operator Value ();
          
          return 0;
      }
      
    • 编译运行

      operator Value()
      
    • Demo2:利用转换构造函数

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Value
      {
      public:
          Value() {
          }
          // 转换构造函数
          Value(Test& t) {
              cout << "Value(Test& t)" << endl;
          }
      };
      
      class Test
      {
          int mValue;
      public:
          Test(int i = 0) {
              mValue = i;
          }
          int value() {
              return mValue;
          }
      };
      
      int main()
      {   
          Test t(100);
          Value v = t;  // <=> Value v = Value(t)
          
          return 0;
      }
      
    • 编译运行

      Value(Test& t)
      
  • 类型转换函数 VS 转换构造函数

    • 类型转换函数和转换构造函数同时存在时,编译器会混淆选择

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Test;
      
      class Value
      {
      public:
          Value() {
          }
          // 转换构造函数
          Value(Test& t) {
          }
      };
      
      class Test
      {
          int mValue;
      public:
          Test(int i = 0) {
              mValue = i;
          }
          int value() {
              return mValue;
          }
          // 类型转换函数
          operator Value()
          {
              Value ret;
              cout << "operator Value()" << endl;
              return ret;
          }
      };
      
      int main()
      {   
          Test t(100);
          Value v = t;
          
          return 0;
      }
      
    • 编译

      test.cpp: In function ‘int main()’:
      test.cpp:40:35: error: conversion from ‘Test’ to ‘Value’ is ambiguous
           Value v = static_cast<Test&>(t);
                                         ^
      test.cpp:27:9: note: candidate: Test::operator Value()
               operator Value(){
               ^
      test.cpp:14:9: note: candidate: Value::Value(Test&)
               Value(Test& t){
               ^
      
    • 改进:给 Test 类添加 exlpicit 关键字

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Test;
      
      class Value
      {
      public:
          Value() {
          }
          // 转换构造函数加以限制
          explicit Value(Test& t) {
          }
      };
      
      class Test
      {
          int mValue;
      public:
          Test(int i = 0) {
              mValue = i;
          }
          int value() {
              return mValue;
          }
          // 类型转换函数
          operator Value() {
              Value ret;
              cout << "operator Value()" << endl;
              return ret;
          }
      };
      
      int main()
      {   
          Test t(100);
          Value v = t
          
          return 0;
      }
      
    • 编译运行

      operator Value()
      
  • 无法抑制隐式的类型转换函数调用

  • 类型转换函数可能与转换构造函数冲突

  • 工程中以 Type toType() 的公有成员代替类型转换函数

  • 示例:Qt 文件

    • Demo

      #include <QDebug>
      #include <QString>
      
      int main()
      {
          QString str = "";
          int i = 0;
          double d = 0;
          short s = 0;
      
          str = "-255";
          //使用Type toType()公有成员函数
          i = str.toInt();
          d = str.toDouble();
          s = str.toShort();
      
          qDebug() << "i = " << i << endl;  //-255
          qDebug() << "d = " << d << endl;  //-255
          qDebug() << "s = " << s << endl;  //-255
      
          return 0;
      }
      
posted @ 2020-10-30 19:34  nxgy  阅读(123)  评论(0编辑  收藏  举报