C++ explicit禁止单参构造函数隐式调用
1.单参数构造函数隐式调用
C++中单参数构造函数是可以被隐式调用的,主要有两种情形会隐式调用单参数构造函数:
(1)同类型对象的拷贝构造;即用相同类型的其它对象来初始化当前对象。
(2)不同类型对象的隐式转换。即其它类型对象隐式调用单参数拷贝构造函数初始化当前对象。比如A a=1;
就是隐式转换,而不是显示调用构造函数,即A a(1);
。像A(1)这种涉及类型转换的单参数构造函数,又被称为转换构造函数(Converting Constructor)。
单参数构造函数的隐式调用示例如下:
#include <iostream>
using namespace std;class MyInt
{
public:
MyInt( int num)
{
dNum=num;
}
int getMyInt() const
{
return dNum;
}
private:
int dNum;
};int main()
{
MyInt objMyInt = 10; //不同类型对象的隐式转换
MyInt objMyInt1=objMyInt; //同类型对象的拷贝构造,编译器默认生成拷贝构造函数
cout<<objMyInt.getMyInt()<<endl;
cout<<objMyInt1.getMyInt()<<endl;
}
程序输出结果:
10
10
单参数的构造函数在上例中如下两行被调用,
MyInt objMyInt = 10;
MyInt objMyInt1=objMyInt;
这种单参数构造函数被隐式调用在C++中是被默许的,但是这种写法很明显会影响代码的可读性,有时甚至会导致程序出现意外的错误。
2.单参数构造函数隐式调用的危害
单参数构造函数隐式调用不仅仅会给代码可读性造成影响,有时会带来意外的结果。
#include <iostream>
using namespace std;class MyInt
{
public:MyInt(int* pdNum){cout<<"in MyInt(int*)"<<endl;m_pdNum=pdNum;}int getMyInt() const{return *m_pdNum;}~MyInt(){cout<<"in ~MyInt()"<<endl;if(m_pdNum){delete m_pdNum;}}
private:int* m_pdNum;
};void print(MyInt objMyInt)
{cout<<"in print_MyInt"<<endl;cout<<objMyInt.getMyInt()<<endl;
}int main()
{int* pdNum=new int(666);print(pdNum); //意外的被隐式转换为MyInt对象int* pdNewNum=new int(888);*pdNum=16;cout<<*pdNewNum<<endl; //应该输出888,结果为16
}
程序输出结果:
in MyInt(int*)
in print_MyInt
666
in ~MyInt()
16
程序的本意是想打印输出int指针指向的内容,在没有合适的打印函数被调用时,应该由编译器在编译环节终止编译,报告错误。但是由于编译器“自作主张”的将int指针变量pdNum隐式转换为MyInt对象,调用了函数print(MyInt objMyInt)
。objMyInt在函数调用结束后,其生命周期也随之结束,于是其析构函数被调用,导致int指针变量pdNum指向的内容空间被释放。当再次申请int指针变量pdNewNum时,导致pdNewNum与pdNum指向同一块内存空间,于是对pdNum的改写直接影响到pdNewNum,于是出现了上面诡异的结果。
3.禁止单参构造函数隐式调用
在没有合适理由必须使用隐式转换的前提下,为了提高代码可读性以及避免单参数构造函数的隐式调用带来的潜在风险,建议使用explicit关键字阻止单参数构造函数的隐式调用。具体做法是在单参数构造函数申明时加上explicit。
class MyInt
{
public:explicit MyInt(int num){dNum = num;}explicit MyInt(const MyInt& objMyInt){dNum = objMyInt.getMyInt();}int getMyInt() const{return dNum;}
private:int dNum;
};int main()
{MyInt objMyInt = 11; //编译报错MyInt objMyInt1 = objMyInt; //编译报错
}
当然,多形参构造函数是没有构造函数的隐式转换,所以没必要声明explicit。
参考文献
[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008:1.17explicit的用法
[2]改善C++程序的150个建议[M].李健:提防隐式转换带来的麻烦
[3]深入理解C++中的explicitkeyword
C++ explicit禁止单参构造函数隐式调用相关推荐
- 演练 构造函数的显示调用与隐式调用 c# 1613715344
演练 构造函数的显示调用与隐式调用 c# 1613715344 父类 子类 入口 效果 自动调用了父类的带参构造方法 调用父类带参构造方法 调用了父类的带参构造 无参构造则不会被调用 代码改造分析原因 ...
- QT隐式调用VC开发的DLL
首先用vs开发一个简单的dll 头文件testdll.h #include <Windows.h> extern "C" int WINAPI Add(int x, i ...
- C++---显示实例化与隐式实例化,显示调用与隐式调用
出现场景:C++模板中 template<class T> T Add(T left,T right)return left+right; 上述代码只有经过实例化之后才会形成真正的函数,没 ...
- 没有lib文件的情况下,怎么隐式调用dll
有时候,总会遇到只有一个dll没有头文件,也没有lib文件的情况下,怎么隐式调用呢? 1.首先要生成lib文件,方法如下: Visual C++ 开发工具提供了两个命令行工具,一个是dumpbin.e ...
- QT调用dl(隐式调用)l
Dll文件调用方式分为显式调用和隐式调用,在这里选择隐式调用的方式. 连接地址:http://www.360doc.com/content/18/0911/17/59608061_785737751. ...
- C++ 类析构函数的显示调用和隐式调用
类的析构函数调用方式 堆和栈 结论 系统在什么情况下不会自动调用析构函数呢? 举例 参考与致谢 堆和栈 为了理解这个问题,我们必须首先弄明白"堆"和"栈"的概念 ...
- linux下动态链接库(.so)的显式调用和隐式调用
进入主题前,先看看两点预备知识. 一.显式调用和隐式调用的区别 我们知道,动态库相比静态库的区别是:静态库是编译时就加载到可执行文件中的,而动态库是在程序运行时完成加载的,所以使用动态库的程序的体积要 ...
- c#有参构造函数中怎么调用其无参构造函数
class A { public A() { } public A(int A) :this() { } } 当A a=new A(1); 时 会先调用无参数的构造函数.在调用有参数那个
- 隐式调用 Intent 大全
//调用浏览器 Uri uri = Uri.parse(""); Intent it = new Intent(Intent.ACTION_VIEW,uri); startActi ...
最新文章
- python待遇如何-Python薪资待遇到底是多少?老男孩python学习
- python获取数据库的存储过程_python远程调用sqlserver存储过程记录
- 移动测试中游戏和应用的不同之处
- 一个程序员的时间管理
- [云炬创业基础笔记]第四章测试22
- 通向架构师的道路(第十二天)之Axis2 Web Service(三)
- arm 指令1(转)
- 第3章 Python 数字图像处理(DIP) - 灰度变换与空间滤波5 - 分段线性变换 - 灰度级分层
- 阿里巴巴指东打西,PC之后卖盒饭?
- V4L2Gstreamer媒体控制工具(五)
- 用波尔理论推导里德伯公式
- JSP——编写一个简单的JSP页面,显示英文字母表
- 一文搞懂什么是神经网络Neural Network【详细介绍】
- 笔记本电脑怎么打不开计算机,笔记本电脑打不开了怎么办
- 阿里云安全送您六道平安符,恭贺新春! 1
- LaTeX中的特殊符号,数学公式符号的相关写法
- jmeter性能测试并监控服务器
- 离散控制 discrete control
- 晁盖与吴用 3gp转换软件
- 屡陷丑闻的 Facebook,试图靠 AI Bot 管住员工的嘴,可能吗?
热门文章
- ES6 中 class 和 extends 的es5实现
- AGC018C Coins (set)
- Android SQLite数据库升级的问题
- 越知道自己要什么,越知道自己是什么
- javascript中的undefined,null,,0和false的云集
- JS小游戏-极速快跑
- 使用 MIDP 底层用户接口 API
- [CTO札记]电纸书,将成为教学、阅读潮流
- [Java] 蓝桥杯ADV-135 算法提高 三角形面积
- 蓝桥杯竞赛C/C++组不支持C++11特性