- 浏览: 3450620 次
- 性别:
- 来自: 苏州
文章分类
最新评论
-
sonichy:
Qt5改动很多,要改改了。
基于QItemDelegate的例子1 SpinBoxDelegate -
我的主页6:
楼主,2.2子查询的分页方式:SELECT * FROM ar ...
Mysql 分页语句Limit用法 -
liguoqi:
非常感谢楼主的用心指导,工具以及图片例子都提供了 赞!
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
问下这个图片怎么解压损坏呀
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
楼主讲解的非常详细,还附带工具和图片例子,非常感谢
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载
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即为方式三的汇编代码。细心的你可能发现2和6行有所不同,一个是-4(%ebp)而另一个一个是-8(%ebp),这分别为类变量P1和P3的地址。
对于不可随意进行类型转换的强类型语言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.cpp:23: 错误:请求从 ‘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为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。
注意:只是用于一个参数的构造函数( 如:1、constructor(typename value); 2、construcor(typename value1,typename value2=defaultvalue,typename value3=defaultvalue,...) ),因为两个参数的构造函数几乎没办法隐式的转换,即无法出现classtype classname = value;的情况(因为这样只能赋给一个值)。
发表评论
-
C++ 实现把非静态成员函数作为回调函数(非static)
2012-11-16 10:10 14604众所周知,C++的类成员函数不能像普通函数那样用于回调,因 ... -
【转】windows多线程CreateThread与_beginthreadex本质区别
2012-11-15 14:40 13825本文将带领你与多线程作第一次亲密接触,并深入分析Crea ... -
windows下C语言多线程编程
2012-11-15 14:37 10032_beginThreadex创建多线程解读 一、需 ... -
ostringstream 的清空数据方法
2012-09-27 15:28 19408关于C++中 <sstream>中ostrings ... -
输入参数和输出参数
2012-09-26 10:43 36711. 输入参数,是函数的调用者,传递给函数的实现者,对函 ... -
ofstream、ifstream、fstream
2012-09-23 19:23 2927学习一下C++的文件流: ofstream是从内 ... -
将ostream 对象转换成字符串流输出
2012-09-22 10:47 8605将ostream 对象转换成字符串流输出 因为项目中需 ... -
sstream和strstream以及fstream
2012-09-22 10:38 4463在C++有两种字符串流,也称为数组I/O流,一种在sstre ... -
字符串流 ostrstream 和 istrstream
2012-09-22 10:28 7932ostrstream类流 /*ostrst ... -
将有格式的int解析成float
2012-09-20 18:10 2314因项目中有一个需求:将采集到所有short int 转换成fl ... -
回调函数测试例子
2012-09-29 14:15 4331回调函数就是一个通过函数指针调用的函数。如果你把函数的指 ... -
fatal error C1083: Cannot open include file: 'iostream.h': No such file or dire
2012-09-20 17:19 6453错误: fatal error C1083: Cannot o ... -
C++中类的声明
2012-08-29 16:28 6949可以声明一个类而不定义它 class Sc ... -
only static const integral data members can be initialized within a class
2012-06-28 11:40 2751翻译一下:只有静态整型常量数据成员能在类中初始化 ... -
fstream与 C 风格(例如fread 和 fwrite )两种读写文件方法的效率比较
2012-06-27 17:21 20998我觉得作者写的挺好,评论也写的很对。 目前我的项目 ... -
C/C++快速写入类结构对象进入文件
2012-06-19 10:38 0void QObject::deleteLater() { ... -
C语言文件操作函数大全
2012-06-18 13:54 2387C语言文件操作函数大 ... -
C++ 动态申请数组
2012-06-09 00:05 6713方法1、C++使用vector方便 ... -
VC++动态链接库(DLL)编程深入浅出(ZZ)
2012-06-08 10:06 24541.概论 先来阐述一下DLL(Dynamic Link ... -
C++中的头文件和源文件
2012-05-09 14:54 6628一、C++编译模式 通常 ...
相关推荐
总的来说,`explicit`关键字是C++中控制类型转换的一个关键工具,它可以帮助开发者更好地控制类的构造行为,避免隐式转换可能导致的意外问题,从而提高程序的稳定性和安全性。在编写类库或设计复杂类型时,合理使用`...
在C++程序中很少有人去使用explicit关键字,不可否认,在平时的实践中确实很少能用的上。再说C++的功能强大,往往一个问题可以利用好几种C++特性去解决。但稍微留心一下会发现现有的MFC库或者C++标准库中的相关类...
在C++中,关键字是预定义的保留词汇,它们具有特殊含义,不能用作变量、函数或其他标识符的名字。了解并掌握C++的关键字对于任何初学者来说都至关重要,因为这些关键字构成了语言的基础框架,直接影响到代码的编写和...
总的来说,`explicit`关键字是C++中用于增强类型安全性的工具,它可以防止无意的隐式类型转换,提高代码的可读性和可维护性。在编写类时,尤其是在设计具有特定语义的构造函数时,合理使用`explicit`关键字是非常...
在C++编程语言中,`explicit`关键字是一个非常重要的特性,它主要用于控制构造函数的隐式类型转换。本文将深入探讨`explicit`的作用,并通过示例代码帮助理解其工作原理。 `explicit`关键字用于声明单参数构造函数...
在C++程序中很少有人去使用explicit关键字,不可否认,在平时的实践中确实很少能用的上。再说C++的功能强大,往往一个问题可以利用好几种C++特性去解决。但稍微留心一下就会发现现有的MFC库或者C++标准库中的相关类...
C++编程语言中有很多比较重要的关键字在实际编程中起着非常重要的作用。我们今天为大家介绍的C++ explicit关键字就是其中一个应用比较频繁的关键字。下面就让我们一起来看看这方面的知识吧。 C++ explicit关键字用来...
- **示例**:如果需要直接控制硬件或者优化性能,可以在C++中插入汇编代码。 ```cpp asm("nop"); // 插入一条空操作指令 ``` #### auto(自动) - **描述**:存储类型标识符,表明变量“自动”具有局部作用域。 ...
C++ explicit关键字在刚学的时候就接触到了,也从各处了解了一些,但始终云里雾里,在闲来无事的时候再仔细研究了一下,自己消化理解了一下,自认为比其他网友总结的精简而不失准确。
本文将深入探讨几个重要的C++关键字:volatile、mutable、explicit以及dynamic_cast。 首先,volatile关键字是用于处理多线程环境中的共享变量的。在多线程编程中,当多个线程需要访问并修改同一个变量时,为了确保...
21. `export`: `export`关键字在C++中主要用于模板的导出,使得模板实例化可以在一个编译单元中定义,在其他编译单元中使用。然而,由于实现复杂性和效率问题,C++标准库通常不使用`export`。 这些关键字是C++编程...
20. `export`:C++标准库中关键字,用于导出模板定义,让其他编译单元能直接使用,但实际大多数编译器并未实现此功能。 21. `extern`:声明变量或函数具有外部链接性,使其在其他源文件中可见。外部变量是静态分配...
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++ 关键字大全 C++ 作为一门高效、灵活的编程语言,具有丰富的关键字集合,了解这些关键字的作用和用法是每个 C++ 开发者的必备技能。下面,我们将对 C++ 的关键字进行分类和解释,从基本数据类型到控制流语句,从...
在C++编程语言中,关键字具有特定的语法意义,它们由编译器识别并赋予特殊含义。下面将详细介绍一系列C++的关键字及其用途。 #### asm(汇编指令) - **功能**:`asm`关键字用于嵌入汇编代码到C++程序中。 - **...
在C++中,如果一个类有一个接受单个参数的构造函数,那么这个类的对象可以被隐式地从该参数的类型创建。这可能导致意外的结果,特别是在赋值或函数调用时。 2. **`explicit`关键字的作用**: 使用`explicit`关键字...
- `const`:声明常量,C++中还可以用于方法修饰,限制修改对象状态。 - `const_cast`:用于改变对象或指针的const或volatile属性。 - `continue`:与C语言相同。 - `default`:与C语言相同。 - `delete`:释放动态...