语言基础(6):关键字explicit
1、explicit含义
C++中 explicit 被用来修饰只有一个参数的构造函数,作用是调用该构造函数必须是显示的(且要求只能放在函数声明处), 跟它相对应的单词是 implicit(隐含的、不言明的), 类构造函数默认情况下即声明为 implicit (因此C++没有此关键字)。
如果不使用该关键字,调用只有一个参数的构造函数允许通过缺省的转换操作完成,过程可能通过如下两步执行(VS2017验证不是):
- 将该构造函数参数对应数据类型的数据转换为该类对象;
- 然后调用复制构造函数来实现构造对象;
通过一个例子来说明 explicit 作用:
class String{
explicit String(int n);
String(const char *p);
};
String s1 = 'a'; // 错误:不能做隐式char->String转换
String s2(10); // 可以:调用explicit String(int n);
String s3 = String(10); // 可以:调用explicit String(int n);再调用默认的复制构造函数
String s4 = "Brian"; // 可以:隐式转换调用String(const char *p);再调用默认的复制构造函数
String s5("Fawlty"); // 可以:正常调用String(const char *p);
void f(String);
String g()
{
f(10); // 错误:不能做隐式int->String转换
f("Arthur"); // 可以:隐式转换,等价于f(String("Arthur"));
return 10; // 错误:不能做隐式int->String转换
}
#2、例外的情况 前面说过,该参数只对有一个参数的构造函数有效,对于无参数和多个参数的构造函数,因为他们只能通过显示调用,所以使用 explicit 没有任何意义,但也存在例外情况,如下代码: ``` // Example.h #pragma once class CExample { public: CExample(void); public: ~CExample(void); public: int m_iFirst; int m_iSecond; public: CExample(int iFirst, int iSecond = 0); };
// Example.cpp
include "Example.h"
CExample::CExample(void): m_iFirst(0) { }
CExample::~CExample(void) { }
CExample::CExample(int iFirst, int iSecond):m_iFirst(iFirst), m_iSecond(iSecond) { }
//TestExplicitKey.cpp
...//其它头文件
include "Example.h"
int main()
{
CExample objOne; // 调用没有参数的构造函数
CExample objTwo(12, 12); // 调用有两个参数的构造函数
CExample objThree(12); // 同上,可以传一个参数是因为该构造函数的第二个参数有默认值
/* 执行了隐式转换,等价于CExample temp(12);objFour(temp);注意这个地方调用了编译器为我们提供的默认复制构造函数 */
CExample objFour = 12;
return 0;
}
如果在构造函数声明中加入关键字explicit,如下:
`explicit CExample(int iFirst, int iSecond = 0);`
那么 CExample objFour = 12; 这条语句将不能通过编译。在vs下的编译错误提示如下:
error C2440: 'initializing' : cannot convert from 'int' to 'CExample'
Constructor for class 'CExample' is declared 'explicit'
<br/>
#3、何时不用explicit
当我们需要隐式转换的时候,比如说String类的一个构造函数:
`String(const char*);`
定义成这样的好处,在需要隐式转化的时候编译器会自动地帮我们转换,标准库里面的String就是一个好的证明。具体来说:
我们可以这样
String str="helloworld"; // 直接调用构造函数
String str="hello"+str+"world";
// 调用重载的+操作符号,此过程相当于:
String temp("hello"); // 调用构造函数
String str=temp+str;
String t("world"); // 调用构造函数
String str=str+t;
<br/>
#总结
<table><tr><td bgcolor = #233333><font color = #DDFFDD>
1. explicit关键字只需用于类内的单参数构造函数前面,由于无参数的构造函数和多参数的构造函数总是显示调用,这种情况在构造函数前加explicit无意义;<br/>
2. google的c++规范中提到explicit的优点是可以避免不合时宜的类型变换,缺点无。所以google约定所有单参数的构造函数都必须是显示的,只有极少数情况下拷贝构造函数可以不声明称explicit (例如作为其他类的透明包装器的类或者需要隐式转换的情况);<br/>
3. effective c++中说:被声明为explicit的构造函数通常比其non-explicit兄弟更受欢迎。因为它们禁止编译器执行非预期(往往也不被期望)的类型转换。除非我有一个好理由允许构造函数被用于隐式类型转换,否则我会把它声明为explicit,鼓励大家遵循相同的政策;<br/>
</font></td></tr></table>