implicit/explicit conversion (转载)

原文: http://www.cnblogs.com/visayafan/archive/2011/11/27/2265349.html

转http://blog.csdn.net/callmeback/article/details/4039304

 

不翻译了,比较简单。

 

这个 《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 关键字的作用
在 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。

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

不相信? 耳听为虚,眼见为实,让我们看看底层的实现方式。

为了更容易比较方式一和方式三的实现方式,我们对上面的代码作一点修改,去除方式二:

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’

////第二个转载

 

explicit的意思是明显的,和它相对应的一个词是implicit意思是隐藏的。

我参考了MSDN和《c++标准程序库》对这个关键字的描述,并参考了网络上对这个关键字的解释。现将它的使用方法和总结记录如下:

首先这个关键字只能用在类构造函数。它的作用是不能进行隐式转换。

class gxgExplicit  //没有关键字explicit的类

{

public:

   int _size;

   gxgExplicit(int size)

   {

      _size = size;

   }

};

下面是调用

   gxgExplicit gE1(24);     //这样是没有问题的

   gxgExplicit gE2 = 1;     //这样也是没有问题的

   gxgExplicit gE3;         //这样是不行的,没有默认构造函数

   gE1 = 2;                 //这样也是没有问题的

   gE2 = 3;                 //这样也是没有问题的

   gE2 = gE1;               //这样也是没有问题的

 

但是假如gxgExplicit修改为Stack,我们的_size代表的是堆栈的大小,那么调用的第二句就显得不伦不类,而且容易让人疑惑。这并不是可以让代码阅读者明白和接受的形式,虽然它是合法的(编译器可以通过编译)。这是因为编译器默认情况下有隐式转换的功能,你输入gE2 = 1就编译成同第一句相同的结果。所以,explicit就派上了用场。修改代码为:

class gxgExplicit

{

public:

   int _size;

   explicit gxgExplicit(int size)

   {

      _size = size;

   }

};

继续上面的调用:

   gxgExplicit gE1(24);     //这样是没有问题的

   gxgExplicit gE2 = 1;     //这样是不行的,关键字取消了隐式转换

   gxgExplicit gE3;         //这样是不行的,没有默认构造函数

   gE1 = 2;                 //这样是不行的,关键字取消了隐式转换

   gE2 = 3;                 //这样是不行的,关键字取消了隐式转换

   gE2 = gE1;               //这样是不行的,关键字取消了隐式转换,除非类实现操作符“=”的重载。

这是编译器(vs2005)显示:cannot convert from 'int' to 'gxgExplicit'。

从这里也就看出这个关键字的作用是将编译器隐式转换的功能给屏蔽掉。

 

MSDN上有一个注意点描述了下面的事实,当构造函数参数超过两个时自动取消隐式转换。例如

class gxgExplicit

{

private:

   int _size;

   int _age;

public:

   explicit gxgExplicit(int age, int size)

   {

      _age = age;

      _size = size;

   }

};

这是有没有关键字效果是一样的。那就是相当于有这个关键字。

但是另外一种情况例外:其中只有一个必须输入的参数,其余的为有默认值的参数。

class gxgExplicit

{

private:

   int _size;

   int _age;

public:

   explicit gxgExplicit(int age, int size = 0)

   {

      _age = age;

      _size = size;

   }

};

class gxgExplicit

{

private:

   int _size;

   int _age;

int _hight;

public:

   explicit gxgExplicit(int age, int size = 0)

   {

      _age = age;

      _size = size;

      _hight = hight;

   }

};

 

 

posted @ 2015-07-07 17:19  飞在风口的猪  阅读(476)  评论(0编辑  收藏  举报