在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;的情况(因为这样只能赋给一个值)。

C++explicit关键字的更多相关文章

  1. C++ explicit关键字详解

    本文系转载,原文链接:http://www.cnblogs.com/ymy124/p/3632634.html 首先, C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用 ...

  2. C++中explicit关键字的使用

    看书看到了explicit关键字,就来做个笔记,讲得比较明白,比较浅. 在C++中,我们有时可以将构造函数用作自动类型转换函数.但这种自动特性并非总是合乎要求的,有时会导致意外的类型转换,因此,C++ ...

  3. C++中的explicit关键字

    http://www.cnblogs.com/winnersun/archive/2011/07/16/2108440.html 上面链接中的博主写的很好,我也不多说了.举得例子也很好,应该也是看了E ...

  4. C++中explicit关键字的作用

    转载自:http://www.cnblogs.com/winnersun/archive/2011/07/16/2108440.html explicit用来防止由构造函数定义的隐式转换. 要明白它的 ...

  5. C++ explicit关键字应用方法详解

    C++编程语言中有很多比较重要的关键字在实际编程中起着非常重要的作用.我们今天为大家介绍的C++ explicit关键字就是其中一个应用比较频繁的关键字.下面就让我们一起来看看这方面的知识吧. C++ ...

  6. explicit关键字

    C++中,explicit关键字用来修饰类只有一个参数的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换. explicit使用注意事项: explicit 关 ...

  7. C++ 构造函数放置默认转换explicit关键字(2)

    按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示: class String { String ( const char* p );  ...

  8. explicit 关键字

    C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢? 如果c++类 ...

  9. 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字

    一.构造函数.默认构造函数 (1).构造函数 构造函数是特殊的成员函数 创建类类型的新对象,系统自动会调用构造函数 构造函数是为了保证对象的每个数据成员都被正确初始化 函数名和类名完全相同 不能定义构 ...

随机推荐

  1. UVa11613 Acme Corporation(最小费用流)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=33452 [思路] 最小费用流. 构图: 1 每个月建立2个点,建立 ...

  2. web前端开发中Nodejs、Grunt、npm等的介绍、使用

    一.Nodejs的安装: Grunt和所有grunt插件都是基于nodejs来运行的,如果你的电脑上没有nodejs,就去安装吧.去 https://nodejs.org/ 上,点击页面中那个绿色.大 ...

  3. 《University Calculus》-chape4-导数的应用-微分中值定理

    罗尔定理:如果函数f(x)在[a,b]上连续并且在(a,b)处处可微,并且有f(a) = f(b),则我们必然何以找到一个c∈(a,b),使得f’(c) = 0. 证明:我们从函数f(x)的最大值和最 ...

  4. 《Euclidea3》-Eta-07

    Q: 分析:考虑到充分利用三等分和角度的信息,这里我们只需做出一个36°的角即可. 考虑一个顶角是36°的等腰三角形.如下图. 设AD=a1,CD=a2,根据相似,易得a1:a2=(√5-1)/2. ...

  5. WCF 项目应用连载[3] - 双向通信 实例管理与服务端监控

    WCF 项目应用连载[1] - 索引 - 轻量级的Log系统 - Lig Sample -序 第二节我们已经创建了Lig项目,并且能稳定工作了.现在我们来改进ILigAgent接口,实现WCF的双向通 ...

  6. Jinja2模版语言自定义filter的使用

    Jinja2模版语言,自带有一些filter,能够在前端的模版中控制数据按照相应的方式显示.比如以下两种filter,分别能在前端控制数字的近似精度显示和根据字符串长度补齐: round(value, ...

  7. PHP CodeBase: 判断用户是否手机访问(转)

    随着移动设备的普及,网站也会迎来越来越多移动设备的访问.用适应PC的页面,很多时候对手机用户不友好,那么有些时候,我们需要判断用户是否用手机访问,如果是手机的话,就跳转到指定的手机友好页面.这里就介绍 ...

  8. android中的数据库操作(转)

    android中的数据库操作 android中的应用开发很难避免不去使用数据库,这次就和大家聊聊android中的数据库操作. 一.android内的数据库的基础知识介绍 1.用了什么数据库   an ...

  9. Linux access

    1.access函数 功能描述:检查调用进程是否可以对指定的文件执行某种操作. 用法: #include <unistd.h> #include <fcntl.h>int ac ...

  10. SPOJ 3943 - Nested Dolls 最长不下降子序列LIS(二分写法)

    现在n(<=20000)个俄罗斯套娃,每个都有宽度wi和高度hi(均小于10000),要求w1<w2并且h1<h2的时候才可以合并,问最少能剩几个. [LIS]乍一看跟[这题]类似, ...