zoukankan      html  css  js  c++  java
  • C++关键字explict的详解和使用

    在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。

    explicit使用注意事项:
        explicit 关键字只能用于类内部的构造函数声明上。  

        explicit 关键字作用于单个参数的构造函数。

        在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换。


    在C++中,如果一个类有只有一个参数的构造函数,C++允许一种特殊的声明类变量的方式。在这种情况下,可以直接将一个对应于构造函数参数类型的数据直接赋值给类变量,
    编译器在编译时会自动进行类型转换,将对应于构造函数参数类型的数据转换为类的对象。如果在构造函数前加上explicit修饰词,则会禁止这种自动转换,在这种情况下,即使将
    对应于构造函数参数类型的数据直接赋值给类变量,编译器也会报错。
    下面以具体实例来说明。
      建立people.cpp 文件,然后输入下列内容: 
    class People 

    public: 
    int age; People (int a)  {    age=a;  } 
    };

    void foo ( void ) 
    {   
    People p1(10);  //方式一  
    People* p_p2=new People(10); //方式二  
    People p3=10; //方式三 
    }

    这段C++程序定义了一个类people,包含一个构造函数,这个构造函数只包含一个整形参数a,可用于在构造类时初始化age变量。
    然后定义了一个函数foo,在这个函数中我们用三种方式分别创建了三个10岁的“人”。
    第一种是最一般的类变量声明方式。
    第二种方式其实是声明了一个people类的指针变量,然后在堆中动态创建了一个people实例,并把这个实例的地址赋值给了p_p2.
    第三种方式就是我们所说的特殊方式,为什么说特殊呢?我们都知道,C/C++是一种强类型语言,不同的数据类型是不能随意转换的,
    如果要进行类型转换,必须进行显式强制类型转换,而这里,没有进行任何显式的转换,直接将一个整型数据赋值给了类变量p3. 

    因此,可以说,这里进行了一次隐式类型转换,编译器自动将对应于构造函数参数类型的数据转换为了该类的对象,
    因此方式三经编译器自动转换后和方式一最终的实现方式是一样的。

    explicit关键字到底是什么作用呢?它的作用就是禁止这个特性。如文章一开始而言,凡是用explicit关键字修饰的构造函数,
    编译时就不会进行自动转换,而会报错。 


    以下再以几个例子来加深印象:
     
    例子一: 未加explicit时的隐式类型转换 
    class Circle  
    {  
    public:  
    Circle(double r) : R(r) {} 
    Circle(int x, int y = 0) : X(x), Y(y) {}  
    Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {} 

    private:  
    double R;  
    int X;  
    int Y;  
    };  
      
    int _tmain(int argc, _TCHAR* argv[])  
    {  
    //发生隐式类型转换  
    //编译器会将它变成如下代码  
    //tmp = Circle(1.23)  
    //Circle A(tmp);  
    //tmp.~Circle();  
    Circle A = 1.23;  
    //注意是int型的,调用的是Circle(int x, int y = 0)  
    //它虽然有2个参数,但后一个有默认值,仍然能发生隐式转换
    Circle B = 123;  
    //这个算隐式调用了拷贝构造函数  
    Circle C = A; 
    return 0;  
    }  

    加了explicit关键字后,可防止以上隐式类型转换发生 
    class Circle  
    {  
    public:  
    explicit Circle(double r) : R(r) {}  
    explicit Circle(int x, int y = 0) : X(x), Y(y) {}  
    explicit Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}  
    private:  
    double R;  
    int X;  
    int Y;  
    };  
     
    int _tmain(int argc, _TCHAR* argv[])  
    {  
    //一下3句,都会报错  
    //Circle A = 1.23;  
    //Circle B = 123;  
    //Circle C = A;  

    //只能用显示的方式调用了  
    //未给拷贝构造函数加explicit之前可以这样  
    Circle A = Circle(1.23);  
    Circle B = Circle(123);  
    Circle C = A;  

    //给拷贝构造函数加了explicit后只能这样了  
    Circle A(1.23);  
    Circle B(123);  

    Circle C(A);  

    return 0;  
    }


    例子二:
    class   A   
    {    
    public:                
    A(int); 
    private:    
    int   num;      
    };  
      
    int   Test(const   A&)   //   一个应用函数 
    {
    ...      

       
    Test(2);   //   正确    
    过程是这样的:   编译器知道传的值是int而函数需要的是A类型,但它也同时知道调用A的构造函数将int转换成一个合适的A,
    所以才有上面成功的调用.换句话说,编译器处理这个调用时的情形类似下面这样:    
    const  A  temp(2);   //   从2产生一个临时A对象 
    Test(temp);          //   调用函数       
     
    如果代码写成如下样子:  
    class  A    
    {   
    public:    
    explicit   A(int); 
    private:    
    int   num;    
    };    


    int   Test(const   A&)   //   一个应用函数     
    {    
            ...    
    }    

    Test(2);   //   失败,不能通过隐式类型转换将int类型变量构造成成A类型变量.


    例子三:
    按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示: 
    class String 

    String ( const char* p ); // 用C风格的字符串p作为初始化值      
    //…


    String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(“hello”);  
     
    但是有的时候可能会不需要这种隐式转换,
    如下: class String
     { 
            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型,
    隐式转换成了分配若干字节的空字符串,容易令人误解。为了避免这种错误的发生,我们可以声明显示的转换,使用explicit 关键字: 
    class String
    {        
    explicit String ( int n ); //本意是预先分配n个字节给字符串
            String ( const char* p ); // 用C风格的字符串p作为初始化值        
    //… 
    }  

    加上explicit,就抑制了String ( int n )的隐式转换,
    下面两种写法仍然正确: 
    String s2 ( 10 );   //OK 分配10个字节的空字符串
    String s3 = String ( 10 ); //OK 分配10个字节的空字符串   

    下面两种写法就不允许了:  
    String s4 = 10; //编译不通过,不允许隐式的转换 
    String s5 = ‘a’; //编译不通过,不允许隐式的转换  
    因此,某些时候,explicit 可以有效得防止构造函数的隐式转换带来的错误或者误解。
    ====================================================================================================
    explicit只对构造函数起作用,用来抑制隐式转换。如:     
    class   A
    {               
    A(int   a);     
    };  
         
    int   Function(A   a);      
    当调用Function(2)的时候,2会隐式转换为A类型。这种情况常常不是程序员想要的结果,所以,要避免之,就可以这样写:     
    class   A
    {               
    explicit   A(int   a);      
    }; 
          
    int   Function(A   a);          
    这样,当调用Function(2)的时候,编译器会给出错误信息(除非Function有个以int为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。
     注意:只是用于一个参数的构造函数,如:
    1、constructor(typename value); 
    2、construcor(typename value1,typename value2=defaultvalue,typename value3=defaultvalue,...) ),
    因为两个参数的构造函数几乎没办法隐式的转换,即无法出现classtype classname = value;的情况(因为这样只能赋给一个值)。

  • 相关阅读:
    AntvF2 踩坑及解决办法总结
    Swiper 禁止手动控制滑动
    cnpm run dev 报错 : Error: getaddrinfo ENOTFOUND localhost 解决
    vue项目px自动转rem
    用Vue简单实现自动全选
    使用node搭建静态资源服务器(1)
    node的模块机制
    什么是node
    女程序员的困惑
    Linux系统下node安装(mac命令行安装)
  • 原文地址:https://www.cnblogs.com/duguochao/p/5036268.html
Copyright © 2011-2022 走看看