c++中explicit要害字的寄义和用法
副标题#e#
c++中的explicit要害字用来修饰类的结构函数,表白该结构函数是显式的, 既然有"显式"那么一定就有"隐式",那么什么是显示而什 么又是隐式的呢?
假如c++类的结构函数有一个参数,那么在编译的时候 就会有一个缺省的转换操纵:将该结构函数对应数据范例的数据转换为该类工具 ,如下面所示:
class MyClass
{
public:
MyClass( int num );
}
....
MyClass obj = 10; //ok,convert int to MyClass
在上面的代码中编译器自动将整型转换为MyClass类工具,实际上等同于下面的操 作:
MyClass temp(10);
MyClass obj = temp;
上面的 所有的操纵等于所谓的"隐式转换".
假如要制止这种自动转换 的成果,我们该怎么做呢?嘿嘿这就是要害字explicit的浸染了,将类的结构函 数声明为"显示",也就是在声明结构函数的时候 前面添加上explicit 即可,这样就可以防备这种自动的转换操纵,假如我们修改上面的MyClass类的构 造函数为显示的,那么下面的代码就不可以或许编 译通过了,如下所示:
class MyClass
{
public:
explicit MyClass( int num );
}
....
MyClass obj = 10; //err,can't non-explict convert
class isbn_mismatch:public std::logic_error{
public:
explicit isbn_missmatch(const std::string &s):std:logic_error(s){}
isbn_mismatch(const std::string &s,const std::string &lhs,const std::string &rhs):
std::logic_error(s),left(lhs),right(rhs){}
const std::string left,right;
virtual ~isbn_mismatch() throw(){}
};
Sales_item& operator+(const Sales_item &lhs,const Sales_item rhs)
{
if(!lhs.same_isbn(rhs))
throw isbn_mismatch("isbn missmatch",lhs.book(),rhs.book());
Sales_item ret(lhs);
ret+rhs;
return ret;
}
Sales_item item1,item2,sum;
while(cin>>item1>>item2)
{
try{
sun=item1+item2;
}catch(const isbn_mismatch &e)
{
cerr<<e.what()<<"left isbn is:"<<e.left<<"right isbn is:"<<e.right<<endl;
}
}
#p#副标题#e#
用于用户自界说范例的结构函数,指定它是默认的 结构函数,不行用于转换结构函数。因为结构函数有三种:1拷贝结构函数2转换 结构函数3一般的结构函数(我本身的术语^_^)
另:假如一个类或布局存 在多个结构函数时,explicit 修饰的谁人结构函数就是默认的
class isbn_mismatch:public std::logic_error{
public:
explicit isbn_missmatch(const std::string &s):std:logic_error(s){}
isbn_mismatch(const std::string &s,const std::string &lhs,const std::string &rhs):
std::logic_error(s),left(lhs),right(rhs){}
const std::string left,right;
virtual ~isbn_mismatch() throw(){}
};
Sales_item& operator+(const Sales_item &lhs,const Sales_item rhs)
{
if(!lhs.same_isbn(rhs))
throw isbn_mismatch("isbn missmatch",lhs.book(),rhs.book());
Sales_item ret(lhs);
ret+rhs;
return ret;
}
Sales_item item1,item2,sum;
while(cin>>item1>>item2)
{
try{
sun=item1+item2;
}catch(const isbn_mismatch &e)
{
cerr<<e.what()<<"left isbn is:"<<e.left<<"right isbn is:"<<e.right<<endl;
}
}
这个 《ANSI/ISO C++ Professional Programmer’s Handbook 》是这样说的
explicit Constructors
A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to
an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example:
class string
{
private:
int size;
int capacity;
char *buff;
public:
string();
string(int size); // constructor and implicit conversion operator
string(const char *); // constructor and implicit conversion operator
~string();
};
Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that
constructs a string from const char *. The second constructor is used to create an empty string object with an
initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is
dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.
Consider the following:
int main()
{
string s = "hello"; //OK, convert a C-string into a string object
int ns = 0;
s = 1; // 1 oops, programmer intended to write ns = 1,
}
In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally,
the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first
searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int.
Consequently, the compiler interprets the expression s= 1; as if the programmer had written
s = string(1);
You might encounter a similar problem when calling a function that takes a string argument. The following example
can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit
conversion constructor of class string, it will pass unnoticed:
int f(string s);
int main()
{
f(1); // without a an explicit constructor,
//this call is expanded into: f ( string(1) );
//was that intentional or merely a programmer's typo?
}
'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit:
class string
{
//...
public:
explicit string(int size); // block implicit conversion
string(const char *); //implicit conversion
~string();
};
An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the
typographical error this time:
int main()
{
string s = "hello"; //OK, convert a C-string into a string object
int ns = 0;
s = 1; // compile time error ; this time the compiler catches the typo
}
Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is
useful and well behaved. A good example of this is the third constructor of string:
string(const char *);
The implicit type conversion of const char * to a string object enables its users to write the following:
string s;
s = "Hello";
The compiler implicitly transforms this into
string s;
//pseudo C++ code:
s = string ("Hello"); //create a temporary and assign it to s
On the other hand, if you declare this constructor explicit, you have to use explicit type conversion:
class string
{
//...
public:
explicit string(const char *);
};
int main()
{
string s;
s = string("Hello"); //explicit conversion now required
return 0;
}
Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization
committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a
new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion
when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared
explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an
implicit conversion operator.
网上找的讲的最好 的贴:
C++ 中 explicit 要害字的浸染
#p#分页标题#e#
在 C++ 中, 假如一个类 有只有一个参数的结构函数,C++ 答允一种非凡的声明类变量的方法。在这种情 况下,可以直接将一个对应于结构函数参数范例的数据直接赋值给类变量,编译 器在编译时会自动举办范例转换,将对应于结构函数参数范例的数据转换为类的 工具。 假如在结构函数前加上 explicit 修饰词, 则会克制这种自动转换,在 这种环境下, 纵然将对应于结构函数参数范例的数据直接赋值给类变量,编译器 也会报错。
下面以详细实例来说明。
成立people.cpp 文件,然后 输入下列内容:
class People
{
public:
int age;
explicit 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.
#p#分页标题#e#
因此,可以说,这里举办了一次隐式范例转换,编译器自动将对应于结构函数参 数范例的数据转换为了该类的工具,因此方法三经编译器自动转换后和方法一最 终的实现方法是一样的。
不相信? 耳听为虚,目睹为实,让我们看看底 层的实现方法。
为了更容易较量方法一和方法三的实现方法,我们对上面 的代码作一点修改,去除方法二:
void foo ( void )
{
People p1(10); //方法一
People p3=10; //方法三
}
去除方法二的原因是方法 二是在堆上动态建设类实例,因此会有一些特别代码影响阐明。修改完成后,用 下列呼吁编译 people.cpp $ gcc -S people.cpp
"-S"选项是GCC输出汇编代 码。呼吁执行后,默认生成people.s。 要害部门内容如下:
.globl _Z3foov
.type _Z3foov, @function
_Z3foov:
.LFB5:
pushl %ebp
.LCFI2:
movl %esp, %ebp
.LCFI3:
subl $24, %esp
.LCFI4:
movl $10, 4(%esp)
leal -4(%ebp), %eax
movl %eax, (%esp)
call _ZN6PeopleC1Ei
movl $10, 4(%esp)
leal -8(%ebp), %eax
movl %eax, (%esp)
call _ZN6PeopleC1Ei
leave
ret
看 “.LCFI4” 行后头的对象,1-4行和5-8行险些一模一样,1-4行即为 方法一的汇编代码,5-8即为方法三的汇编代码。 细心的你大概发明2和6行有所 差异,一个是 -4(%ebp) 而另一个一个是 -8(%ebp) ,这别离为类变量P1和 P3的地点。
对付不行随意举办范例转换的强范例语言C/C++来说, 这可以 说是C++的一个特性。哦,本日仿佛不是要说C++的特性,而是要知道explicit关 键字的浸染?
explicit要害字到底是什么浸染呢? 它的浸染就是克制这 个特性。如文章一开始而言,每每用explicit要害字修饰的结构函数,编译时就 不会举办自动转换,而会报错。
让我们看看吧! 修改代码:
class People
{
public:
int age;
explicit People (int a)
{
age=a;
}
};
然后再编译:
$ gcc -S people.cpp
编译器立马报错:
people.cpp: In function ‘void foo()’:
people.cpp:23: 错误:请求从 ‘int’ 转换到非标量范例 ‘People’