• 【校招面试 之 C/C++】第18题 C++ 中的隐式转换以及explicit关键字


    1、什么是隐式转换:

    众所周知,C++的基本类型中并非完全的对立,部分数据类型之间是可以进行隐式转换的。

    所谓隐式转换,是指不需要用户干预,编译器私下进行的类型转换行为。很多时候用户可能都不知道进行了哪些转换。

    2、为什么要进行隐式类型转换:

    C++面向对象的多态特性,就是通过父类的类型实现对子类的封装。

    通过隐式转换,你可以直接将一个子类的对象使用父类的类型进行返回。

    在比如,数值和布尔类型的转换,整数和浮点数的转换等。

    某些方面来说,隐式转换给C++程序开发者带来了不小的便捷。

    C++是一门强类型语言,类型的检查是非常严格的。

    如果没有类型的隐式转换,这将给程序开发者带来很多的不便。

    当然,凡事都有两面性,在你享受方便快捷的一面时,你不得不面对太过智能以至完全超出了你的控制。

    风险就在不知不觉间出现。

    3、隐式转换原则:

      • 基本数据类型 基本数据类型的转换以取值范围的作为转换基础(保证精度不丢失)。
        隐式转换发生在从小->大的转换中。比如从char转换为int。
        int -> long。
      •  自定义对象 子类对象可以隐式的转换为父类对象。

    4、隐式转换的风险:

    隐式转换的风险一般存在于自定义的类构造函数中。

     例一
    如下面所示:

    class String
    {
    public:
        String ( const char* p ); // 用C风格的字符串p作为初始化值
        //…
    }
     
    String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(”hello”)
    

    但是有的时候可能会不需要这种隐式转换,如下:

    class String
    {
    public:
        String ( int n ); //本意是预先分配n个字节给字符串
        String ( const char* p ); // 用C风格的字符串p作为初始化值
     
        //…
    }
    

    下面两种写法比较正常:
    String s2 ( 10 );   //OK 分配10个字节的空字符串
    String s3 = String ( 10 ); //OK 分配10个字节的空字符串

    下面两种写法就比较疑惑了:
    String s4 = 10; //编译通过,也是分配10个字节的空字符串
    String s5 = ‘a’; //编译通过,分配int(‘a’)个字节的空字符串
    s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。

    例2:

    class Test
    {
    public:
      Test(int a);
      bool isSame(Test other)
      {
        return m_val == other.m_val;
      }
     
    private:
      int m_val;
    }
    

    如下调用:
    Test a(10);
    If(a.isSame(10)) //该语句将返回true

    本来用于两个Test对象的比较,竟然和int类型相等了。
    这里就是由于发生了隐式转换,实际比较的是一个临时的Test对象。
    这个在程序中是绝对不能允许的。

    例3:

    class CxString  // 没有使用explicit关键字的类声明, 即默认为隐式声明  
    {  
    public:  
        char *_pstr;  
        int _size;  
        CxString(int size)  
        {  
            _size = size;                // string的预设大小  
            _pstr = malloc(size + 1);    // 分配string的内存  
            memset(_pstr, 0, size + 1);  
        }  
        CxString(const char *p)  
        {  
            int size = strlen(p);  
            _pstr = malloc(size + 1);    // 分配string的内存  
            strcpy(_pstr, p);            // 复制字符串  
            _size = strlen(_pstr);  
        }  
        // 析构函数这里不讨论, 省略...  
    };  
      
        // 下面是调用:  
      
        CxString string1(24);     // 这样是OK的, 为CxString预分配24字节的大小的内存  
        CxString string2 = 10;    // 这样是OK的, 为CxString预分配10字节的大小的内存  
        CxString string3;         // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用  
        CxString string4("aaaa"); // 这样是OK的  
        CxString string5 = "bbb"; // 这样也是OK的, 调用的是CxString(const char *p)  
        CxString string6 = 'c';   // 这样也是OK的, 其实调用的是CxString(int size), 且size等于'c'的ascii码  
        string1 = 2;              // 这样也是OK的, 为CxString预分配2字节的大小的内存  
        string2 = 3;              // 这样也是OK的, 为CxString预分配3字节的大小的内存  
        string3 = string1;        // 这样也是OK的, 至少编译是没问题的, 但是如果析构函数里用free释放_pstr内存指针的时候可能会报错, 完整的代码必须重载运算符"=", 并在其中处理内存释放  
    

    上面的代码中, "CxString string2 = 10;" 这句为什么是可以的呢? 在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 "CxString string2 = 10;" 这段代码, 编译器自动将整型转换为CxString类对象, 实际上等同于下面的操作:

    CxString string2(10);  
    或  
    CxString temp(10);  
    CxString string2 = temp; 
    

    但是, 上面的代码中的_size代表的是字符串内存分配的大小, 那么调用的第二句 "CxString string2 = 10;" 和第六句 "CxString string6 = 'c';" 就显得不伦不类, 而且容易让人疑惑. 有什么办法阻止这种用法呢? 答案就是使用explicit关键字. 我们把上面的代码修改一下, 如下:

     
    class CxString  // 使用关键字explicit的类声明, 显示转换  
    {  
    public:  
        char *_pstr;  
        int _size;  
        explicit CxString(int size)  
        {  
            _size = size;  
            // 代码同上, 省略...  
        }  
        CxString(const char *p)  
        {  
            // 代码同上, 省略...  
        }  
    };  
      
        // 下面是调用:  
      
        CxString string1(24);     // 这样是OK的  
        CxString string2 = 10;    // 这样是不行的, 因为explicit关键字取消了隐式转换  
        CxString string3;         // 这样是不行的, 因为没有默认构造函数  
        CxString string4("aaaa"); // 这样是OK的  
        CxString string5 = "bbb"; // 这样也是OK的, 调用的是CxString(const char *p)  
        CxString string6 = 'c';   // 这样是不行的, 其实调用的是CxString(int size), 且size等于'c'的ascii码, 但explicit关键字取消了隐式转换  
        string1 = 2;              // 这样也是不行的, 因为取消了隐式转换  
        string2 = 3;              // 这样也是不行的, 因为取消了隐式转换  
        string3 = string1;        // 这样也是不行的, 因为取消了隐式转换, 除非类实现操作符"="的重载  
    

    explicit关键字的作用就是防止类构造函数的隐式自动转换.

    上面也已经说过了, explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了(除非存在默认参数的情况). 

     
  • 相关阅读:
    C# 实现 Snowflake算法生成唯一性Id
    kafka可视化客户端工具(Kafka Tool)的基本使用(转)
    docker 安装kafka
    Model类代码生成器
    使用docker 部署rabbitmq 镜像
    Vue 增删改查 demo
    git 提交代码到库
    Android ble蓝牙问题
    mac 配置 ssh 到git (Could not resolve hostname github.com, Failed to connect to github.com port 443 Operation timed out)
    okhttp
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/9347113.html
Copyright © 2020-2023  润新知