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关键字阻止单参数构造函数的隐式调用。具体做法是在单参数构造函数申明时加上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