explicit
关键字在C ++中是什么意思?
#1楼
允许编译器进行一次隐式转换,以将参数解析为函数。 这意味着编译器可以使用可通过单个参数调用的构造函数从一种类型转换为另一种类型,以便获得参数的正确类型。
这是带有可用于隐式转换的构造函数的示例类:
class Foo
{
public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo)
{
}
int GetFoo () { return m_foo; }
private:
int m_foo;
};
这是一个带有Foo
对象的简单函数:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
这就是DoBar
函数的调用位置。
int main ()
{
DoBar (42);
}
该参数不是Foo
对象,而是int
。 但是, Foo
有一个采用int
构造函数,因此可以使用该构造函数将参数转换为正确的类型。
允许编译器对每个参数执行一次此操作。
在explicit
关键字前面加上构造函数,可以防止编译器将该构造函数用于隐式转换。 将其添加到上述类中将在函数调用DoBar (42)
创建编译器错误。 现在必须使用DoBar (Foo (42))
明确调用转换DoBar (Foo (42))
您可能要执行此操作的原因是为了避免可能隐藏错误的意外构造。 人为的例子:
- 您有一个
MyString(int size)
类,该类的构造函数构造了给定大小的字符串。 您有一个函数print(const MyString&)
,并且您调用了print(3)
(当您实际上打算调用print("3")
)。 您希望它打印“ 3”,但是它将打印一个长度为3的空字符串。
#2楼
假设您有一个String
类:
class String {
public:
String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
现在,如果您尝试:
String mystring = 'x';
字符'x'
将隐式转换为int
,然后将调用String(int)
构造函数。 但是,这不是用户可能想要的。 因此,为防止出现这种情况,我们将构造函数定义为explicit
:
class String {
public:
explicit String (int n); //allocate n bytes
String(const char *p); // initialize sobject with string p
};
#3楼
在C ++中,仅具有一个必需参数的构造函数被视为隐式转换函数。 它将参数类型转换为类类型。 这是否是一件好事,取决于构造函数的语义。
例如,如果您有一个带有构造函数String(const char* s)
的字符串类,则可能正是您想要的。 您可以将const char*
传递给需要String
的函数,编译器将自动为您构造一个临时String
对象。
另一方面,如果您有一个缓冲区类,其构造函数Buffer(int size)
占用缓冲区的大小(以字节为单位),则您可能不希望编译器将int
悄悄地转换为Buffer
。 为防止这种情况,请使用explicit
关键字声明构造函数:
class Buffer { explicit Buffer(int size); ... }
那样,
void useBuffer(Buffer& buf);
useBuffer(4);
成为编译时错误。 如果要传递一个临时的Buffer
对象,则必须明确地这样做:
useBuffer(Buffer(4));
总而言之,如果您的单参数构造函数将参数转换为类的对象,则您可能不想使用explicit
关键字。 但是,如果您有一个恰好采用单个参数的构造函数,则应将其声明为explicit
以防止编译器因意外的转换而使您感到惊讶。
#4楼
explicit
关键字使转换构造函数变为非转换构造函数。 结果,该代码不易出错。
#5楼
这已经讨论过了( 什么是显式构造函数 )。 但我必须说,它缺少此处的详细描述。
此外,如前所述,使您的一个参数构造器(包括那些具有arg2,arg3,...的默认值的构造器)始终是一种好的编码实践。 像往常一样使用C ++:如果您不这样做-希望您这样做...
类的另一个好的做法是将副本构造和赋值设置为私有(也称为禁用它),除非您确实需要实现它。 这样可以避免使用默认情况下C ++将为您创建的方法时获得最终的指针副本。 另一种方法是从boost :: noncopyable派生。