`
qimo601
  • 浏览: 3415534 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

【转】认识 C++ 中的 explicit 关键字

阅读更多

C++ explicit 关键字的作用

 

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.

 

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

 

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

 

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

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即为方式三的汇编代码。细心的你可能发现26行有所不同,一个是-4%ebp)而另一个一个是-8%ebp),这分别为类变量P1P3的地址。

 

对于不可随意进行类型转换的强类型语言C/C++来说,这可以说是C++的一个特性。哦,今天好像不是要说C++的特性,而是要知道explicit关键字的作用?

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

让我们看看吧!修改代码:

 

classPeople

{

public:

int age;

explicit People (int a)

 {

  age=a;

 }

};

 

然后再编译:

$ gcc -S people.cpp

编译器立马报错:

people.cpp In function void foo()’:

people.cpp23 错误:请求从 int’转换到非标量类型‘People

 

以下再以几个例子来加深印象:

例子一:

未加explicit时的隐式类型转换

1. class Circle

2. {

3. public:

4. Circle(double r) : R(r) {}

5. Circle(int x, int y = 0) : X(x), Y(y) {}

6. Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}

7. private:

8. double R;

9. int X;

10. int Y;

11. };

12.

13. int _tmain(int argc, _TCHAR* argv[])

14. {

15. //发生隐式类型转换

16. //编译器会将它变成如下代码

17. //tmp = Circle(1.23)

18. //Circle A(tmp);

19. //tmp.~Circle();

20. Circle A = 1.23;

21. //注意是int型的,调用的是Circle(int x, int y = 0)

22. //它虽然有2个参数,但后一个有默认值,仍然能发生隐式转换

23. Circle B = 123;

24. //这个算隐式调用了拷贝构造函数

25. Circle C = A;

26. return 0;

27. }

 

加了explicit关键字后,可防止以上隐式类型转换发生

1. class Circle

2. {

3. public:

4. explicit Circle(double r) : R(r) {}

5. explicit Circle(int x, int y = 0) : X(x), Y(y) {}

6. explicit Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}

7. private:

8. double R;

9. int X;

10. int Y;

11. };

12.

13. int _tmain(int argc, _TCHAR* argv[])

14. {

15. //一下3句,都会报错

16. //Circle A = 1.23;

17. //Circle B = 123;

18. //Circle C = A;

19.

20. //只能用显示的方式调用了

21. //未给拷贝构造函数加explicit之前可以这样

22. Circle A = Circle(1.23);

23. Circle B = Circle(123);

24. Circle C = A;

25.

26. //给拷贝构造函数加了explicit后只能这样了

27. Circle A(1.23);

28. Circle B(123);

29. Circle C(A);

30. return 0;

31. }

 

例子二:

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为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。

注意:只是用于一个参数的构造函数( 如:1constructor(typename value); 2construcor(typename value1,typename value2=defaultvalue,typename value3=defaultvalue,...) ),因为两个参数的构造函数几乎没办法隐式的转换,即无法出现classtype classname = value;的情况(因为这样只能赋给一个值)。

分享到:
评论

相关推荐

    C++ Explicit关键字详细解析

    以下是对C++中Explicit关键字的用法进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助

    C++中的explicit关键字实例浅析

    在C++程序中很少有人去使用explicit关键字,不可否认,在平时的实践中确实很少能用的上,再说C++的功能强大,往往一个问题可以利用好几种C++特性去解决...接下来给大家介绍 C++中的explicit关键字,需要的朋友可以参考下

    C++中的Explicit关键字

    在C++程序中很少有人去使用explicit关键字,不可否认,在平时的实践中确实很少能用的上。再说C++的功能强大,往往一个问题可以利用好几种C++特性去解决。但稍微留心一下会发现现有的MFC库或者C++标准库中的相关类...

    C++中explicit作用示例代码

    C++入门学习——explicit关键字的作用,相关教程链接如下:http://blog.csdn.net/tennysonsky/article/details/49096977

    认识 C++ 中的explicit 关键字

    带单一参数的构造函数在缺省情况下隐含一个转换操作符,请看下面的代码:   class MyClass{ public: MyClass(int nParam);// 带一个参数的构造函数 ... //////////////////////////////

    C++ explicit关键字的应用方法详细讲解

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

    C++ explicit通俗解释

    C++ explicit关键字在刚学的时候就接触到了,也从各处了解了一些,但始终云里雾里,在闲来无事的时候再仔细研究了一下,自己消化理解了一下,自认为比其他网友总结的精简而不失准确。

    C++关键字大全(67个).txt

    C++关键字大全(67个) asm auto bad_cast bad_typeid bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum except explicit extern false finally float ...

    C++中explict关键字用法

    主要介绍了C++中explict关键字用法的相关资料,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧

    C++关键字详细使用

    except explicit extern false finally float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static...

    C++中volatile和mutable关键字用法详解

    C/C++中的volatile关键字和const对应,用来修饰变量,用于告诉编译器该变量值是不稳定的,可能被更改。使用volatile注意事项: (1). 编译器会对带有volatile关键字的变量禁用优化(A volatile specifier is a hint to...

    c/c++拷贝构造函数和关键字explicit详解

    主要介绍了c/c++拷贝构造函数和关键字explicit的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

    C++思维导图Xmind文件和.png文件(持续更新)

    初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类思维导图xmind文件和.png文件 内存管理(new/delete使用详解) 模板——初识 STL——string类 STL——vector STL适配器——stack && ...

    C++ explicit构造函数实例解析

    主要介绍了C++ explicit构造函数,需要的朋友可以参考下

    新手学习C++入门资料

    C++中的try/catch/throw异常处理机制取代了标准C中的setjmp()和longjmp()函数。 二、关键字和变量 C++相对与C增加了一些关键字,如下: typename bool dynamic_cast mutable namespace static_cast using ...

Global site tag (gtag.js) - Google Analytics