C++ 只有explicit关键字,没有implicit关键字。编译器允许解析函数的参数时,对参数作隐式转换。也就是说,编译器能够使用带单个参数的构造函数,将函数参数转换为正确的类型。
带有单个形参的构造函数定义了一种由实参类型到类类型的隐式转换。
下面的类带有可用于隐式转换的构造函数:
class Foo
{
public:
Foo (int foo) : m_foo (foo) { } //带单个参数的构造函数,可以用于隐式转换
int GetFoo () { return m_foo; }
private:
int m_foo;
};
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
int main ()
{
DoBar (42);
}
DoBar的形参为Foo对象,现在却传了一个int给它,然而,Foo类存在一个带有 int 类型的构造函数,所以编译器就用这个构造函数将参数隐式转换为Foo对象类型。
如果在构造函数前面加explicit关键字,就能阻止编译器使用这个构造函数做隐式转换,而是必须明确地写出:DoBar (Foo (42))
由于无意中的隐式转换会隐藏bug, 这就是构造函数前面加explicit的原因。
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
class MyString {
public:
// 如果要阻止编译器隐式调用,前面必须加 explicit 关键字
MyString(size_t size) : m_size(size) {
m_str = string(size, 'x');
}
MyString(const char* p) : m_str(p) { }
void printContent() const {
cout << m_str << endl;
}
private:
int m_size;
string m_str;
};
void print(const MyString& mystr) {
mystr.printContent();
}
int _tmain(int argc, _TCHAR* argv[])
{
print(3);
system("pause");
return 0;
}
上面的例子,本意是要写成print("3"),但是不小心写成了print(3), 编译器自行隐式使用构造函数MyString(3), 将临时对象传递给print,输出“xxx”,但是如果在构造函数MyString(int size)前加“explicit", 写成print(3)编译时就会报错,显式写成print(Mystring(3))就不会。
再如:
class Sales_item {
public:
// book 的默认参数是空的字符串
// 定义了一种隐式转换
Sales_item(const std::string &book = ""): isbn(book), units_sold(0), revenue(0.0) { }
Sales_item(std::istream &is); // 定义了一种隐式转换
bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; }
// default constructor needed to initialize members of built-in type
Sales_item(): units_sold(0), revenue(0.0) { }
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
string null_book = "9-999-99999-9";
// ok: builds a Sales_item with 0 units_sold and revenue from
// and isbn equal to null_book
item.same_isbn(null_book);
由于same_isbn函数要求传入Sales_item对象的参数,现在传入的是string,于是编译器自动使用带string参数的构造函数隐式转换,生成一个Sales_item临时对象,传给same_isbn函数。代码虽然能执行,但也可能是用户写错了。
一个使用了explicit关键字的类:
#ifndef REFPTR_H__INCLUDED_
#define REFPTR_H__INCLUDED_
#undef free //this class redifines free
template <class TYPE>
class RefPtr
{
private:
TYPE *m_pPtr;
int *m_pCount;
void free() throw()
{
if (--*m_pCount == 0)
{
delete m_pCount;
delete m_pPtr;
}
}
void copy(const RefPtr<TYPE> & p) throw()
{
m_pPtr = p.m_pPtr;
m_pCount = p.m_pCount;
++*m_pCount;
}
public:
explicit RefPtr(TYPE *p = NULL) throw() : m_pPtr(p), m_pCount(new int(1))
{
}
RefPtr(const RefPtr<TYPE> & obj) throw()
{
copy(obj);
}
~RefPtr() throw()
{
free();
}
RefPtr<TYPE> & operator=(const RefPtr<TYPE> & rhs) throw()
{
if (this != &rhs)
{
free();
copy(rhs);
}
return *this;
}
TYPE & operator*() const throw()
{
return *m_pPtr;
}
TYPE * operator->() const throw()
{
return m_pPtr;
}
virtual bool operator<( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr<rhs.m_pPtr;
}
virtual bool operator>( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr>rhs.m_pPtr;
}
virtual bool operator<=( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr<=rhs.m_pPtr;
}
virtual bool operator>=( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr>=rhs.m_pPtr;
}
virtual bool operator!=( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr!=rhs.m_pPtr;
}
virtual bool operator==( const RefPtr<TYPE>& rhs ) const
{
return m_pPtr==rhs.m_pPtr;
}
bool isNull() const throw()
{
return m_pPtr == NULL;
}
TYPE* GetRawPtr() const throw()
{
return m_pPtr;
}
};
#endif
[1] C++ Primer 12.4.4
[2]https://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-mean/121163#121163
[3]https://stackoverflow.com/questions/7099957/implicit-vs-explicit-conversion